Source Code Cross Referenced for BaseContentService.java in  » ERP-CRM-Financial » sakai » org » sakaiproject » content » impl » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » ERP CRM Financial » sakai » org.sakaiproject.content.impl 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /**********************************************************************************
0002:         * $URL: https://source.sakaiproject.org/svn/content/tags/sakai_2-4-1/content-impl/impl/src/java/org/sakaiproject/content/impl/BaseContentService.java $
0003:         * $Id: BaseContentService.java 33702 2007-08-08 20:42:31Z ajpoland@iupui.edu $
0004:         ***********************************************************************************
0005:         *
0006:         * Copyright (c) 2003, 2004, 2005, 2006, 2007 The Sakai Foundation.
0007:         * 
0008:         * Licensed under the Educational Community License, Version 1.0 (the "License"); 
0009:         * you may not use this file except in compliance with the License. 
0010:         * You may obtain a copy of the License at
0011:         * 
0012:         *      http://www.opensource.org/licenses/ecl1.php
0013:         * 
0014:         * Unless required by applicable law or agreed to in writing, software 
0015:         * distributed under the License is distributed on an "AS IS" BASIS, 
0016:         * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
0017:         * See the License for the specific language governing permissions and 
0018:         * limitations under the License.
0019:         *
0020:         **********************************************************************************/package org.sakaiproject.content.impl;
0021:
0022:        import java.io.ByteArrayInputStream;
0023:        import java.io.FileInputStream;
0024:        import java.io.FileOutputStream;
0025:        import java.io.IOException;
0026:        import java.io.InputStream;
0027:        import java.io.OutputStream;
0028:        import java.io.UnsupportedEncodingException;
0029:        import java.util.ArrayList;
0030:        import java.util.Collection;
0031:        import java.util.Comparator;
0032:        import java.util.HashMap;
0033:        import java.util.Hashtable;
0034:        import java.util.Iterator;
0035:        import java.util.List;
0036:        import java.util.Map;
0037:        import java.util.ResourceBundle;
0038:        import java.util.Set;
0039:        import java.util.SortedSet;
0040:        import java.util.Stack;
0041:        import java.util.TreeSet;
0042:        import java.util.Vector;
0043:        import java.util.concurrent.ConcurrentHashMap;
0044:
0045:        import javax.servlet.http.HttpServletRequest;
0046:        import javax.servlet.http.HttpServletResponse;
0047:
0048:        import org.apache.commons.codec.binary.Base64;
0049:        import org.apache.commons.logging.Log;
0050:        import org.apache.commons.logging.LogFactory;
0051:        import org.sakaiproject.alias.api.AliasService;
0052:        import org.sakaiproject.authz.api.AuthzGroup;
0053:        import org.sakaiproject.authz.api.AuthzGroupService;
0054:        import org.sakaiproject.authz.api.AuthzPermissionException;
0055:        import org.sakaiproject.authz.api.GroupNotDefinedException;
0056:        import org.sakaiproject.authz.api.Role;
0057:        import org.sakaiproject.authz.api.RoleAlreadyDefinedException;
0058:        import org.sakaiproject.authz.cover.FunctionManager;
0059:        import org.sakaiproject.authz.cover.SecurityService;
0060:        import org.sakaiproject.component.api.ServerConfigurationService;
0061:        import org.sakaiproject.content.api.ContentCollection;
0062:        import org.sakaiproject.content.api.ContentCollectionEdit;
0063:        import org.sakaiproject.content.api.ContentEntity;
0064:        import org.sakaiproject.content.api.ContentHostingHandler;
0065:        import org.sakaiproject.content.api.ContentHostingService;
0066:        import org.sakaiproject.content.api.ContentResource;
0067:        import org.sakaiproject.content.api.ContentResourceEdit;
0068:        import org.sakaiproject.content.api.GroupAwareEdit;
0069:        import org.sakaiproject.content.api.GroupAwareEntity;
0070:        import org.sakaiproject.content.api.ResourceType;
0071:        import org.sakaiproject.content.api.ResourceTypeRegistry;
0072:        import org.sakaiproject.content.api.GroupAwareEntity.AccessMode;
0073:        import org.sakaiproject.content.cover.ContentTypeImageService;
0074:        import org.sakaiproject.content.types.FileUploadType;
0075:        import org.sakaiproject.content.types.FolderType;
0076:        import org.sakaiproject.content.types.HtmlDocumentType;
0077:        import org.sakaiproject.content.types.TextDocumentType;
0078:        import org.sakaiproject.content.types.UrlResourceType;
0079:        import org.sakaiproject.entity.api.ContextObserver;
0080:        import org.sakaiproject.entity.api.Edit;
0081:        import org.sakaiproject.entity.api.Entity;
0082:        import org.sakaiproject.entity.api.EntityAccessOverloadException;
0083:        import org.sakaiproject.entity.api.EntityCopyrightException;
0084:        import org.sakaiproject.entity.api.EntityManager;
0085:        import org.sakaiproject.entity.api.EntityNotDefinedException;
0086:        import org.sakaiproject.entity.api.EntityPermissionException;
0087:        import org.sakaiproject.entity.api.EntityPropertyNotDefinedException;
0088:        import org.sakaiproject.entity.api.EntityPropertyTypeException;
0089:        import org.sakaiproject.entity.api.EntityTransferrer;
0090:        import org.sakaiproject.entity.api.HttpAccess;
0091:        import org.sakaiproject.entity.api.Reference;
0092:        import org.sakaiproject.entity.api.ResourceProperties;
0093:        import org.sakaiproject.entity.api.ResourcePropertiesEdit;
0094:        import org.sakaiproject.event.api.Event;
0095:        import org.sakaiproject.event.api.NotificationEdit;
0096:        import org.sakaiproject.event.api.NotificationService;
0097:        import org.sakaiproject.event.cover.EventTrackingService;
0098:        import org.sakaiproject.exception.CopyrightException;
0099:        import org.sakaiproject.exception.IdInvalidException;
0100:        import org.sakaiproject.exception.IdLengthException;
0101:        import org.sakaiproject.exception.IdUniquenessException;
0102:        import org.sakaiproject.exception.IdUnusedException;
0103:        import org.sakaiproject.exception.IdUsedException;
0104:        import org.sakaiproject.exception.InUseException;
0105:        import org.sakaiproject.exception.InconsistentException;
0106:        import org.sakaiproject.exception.OverQuotaException;
0107:        import org.sakaiproject.exception.PermissionException;
0108:        import org.sakaiproject.exception.ServerOverloadException;
0109:        import org.sakaiproject.exception.TypeException;
0110:        import org.sakaiproject.id.cover.IdManager;
0111:        import org.sakaiproject.memory.api.Cache;
0112:        import org.sakaiproject.memory.api.CacheRefresher;
0113:        import org.sakaiproject.memory.api.MemoryService;
0114:        import org.sakaiproject.site.api.Group;
0115:        import org.sakaiproject.site.api.Site;
0116:        import org.sakaiproject.site.api.SiteService;
0117:        import org.sakaiproject.thread_local.cover.ThreadLocalManager;
0118:        import org.sakaiproject.time.api.Time;
0119:        import org.sakaiproject.time.cover.TimeService;
0120:        import org.sakaiproject.tool.api.SessionBindingEvent;
0121:        import org.sakaiproject.tool.api.SessionBindingListener;
0122:        import org.sakaiproject.tool.cover.SessionManager;
0123:        import org.sakaiproject.tool.cover.ToolManager;
0124:        import org.sakaiproject.user.api.User;
0125:        import org.sakaiproject.user.api.UserNotDefinedException;
0126:        import org.sakaiproject.user.cover.UserDirectoryService;
0127:        import org.sakaiproject.util.BaseResourcePropertiesEdit;
0128:        import org.sakaiproject.util.Blob;
0129:        import org.sakaiproject.util.StorageUser;
0130:        import org.sakaiproject.util.StringUtil;
0131:        import org.sakaiproject.util.Validator;
0132:        import org.sakaiproject.util.Web;
0133:        import org.sakaiproject.util.Xml;
0134:        import org.w3c.dom.Document;
0135:        import org.w3c.dom.Element;
0136:        import org.w3c.dom.Node;
0137:        import org.w3c.dom.NodeList;
0138:
0139:        /**
0140:         * <p>
0141:         * BaseContentService is an abstract base implementation of the Sakai ContentHostingService.
0142:         * </p>
0143:         */
0144:        public abstract class BaseContentService implements 
0145:                ContentHostingService, CacheRefresher, ContextObserver,
0146:                EntityTransferrer {
0147:            /** Our logger. */
0148:            private static Log M_log = LogFactory
0149:                    .getLog(BaseContentService.class);
0150:
0151:            protected static final long END_OF_TIME = 8000L * 365L * 24L * 60L
0152:                    * 60L * 1000L;
0153:            protected static final long START_OF_TIME = 365L * 24L * 60L * 60L
0154:                    * 1000L;
0155:
0156:            /** The initial portion of a relative access point URL. */
0157:            protected String m_relativeAccessPoint = null;
0158:
0159:            /** A Storage object for persistent storage. */
0160:            protected Storage m_storage = null;
0161:
0162:            /** A Cache for this service - ContentResource and ContentCollection keyed by reference. */
0163:            protected Cache m_cache = null;
0164:
0165:            /**
0166:             * The quota for content resource body bytes (in Kbytes) for any hierarchy in the /user/ or /group/ areas, or 0 if quotas are not enforced.
0167:             */
0168:            protected long m_siteQuota = 0;
0169:
0170:            static {
0171:                ROOT_COLLECTIONS.add(COLLECTION_SITE);
0172:                ROOT_COLLECTIONS.add(COLLECTION_USER);
0173:                ROOT_COLLECTIONS.add(COLLECTION_DROPBOX);
0174:                ROOT_COLLECTIONS.add(COLLECTION_PUBLIC);
0175:                ROOT_COLLECTIONS.add(ATTACHMENTS_COLLECTION);
0176:                ROOT_COLLECTIONS.add(COLLECTION_MELETE_DOCS);
0177:            }
0178:
0179:            /** Optional path to external file system file store for body binary. */
0180:            protected String m_bodyPath = null;
0181:
0182:            /** Optional set of folders just within the m_bodyPath to distribute files among. */
0183:            protected String[] m_bodyVolumes = null;
0184:
0185:            /**********************************************************************************************************************************************************************************************************************************************************
0186:             * Constructors, Dependencies and their setter methods
0187:             *********************************************************************************************************************************************************************************************************************************************************/
0188:
0189:            /** Dependency: MemoryService. */
0190:            protected MemoryService m_memoryService = null;
0191:
0192:            /**
0193:             * Dependency: MemoryService.
0194:             * 
0195:             * @param service
0196:             *        The MemoryService.
0197:             */
0198:            public void setMemoryService(MemoryService service) {
0199:                m_memoryService = service;
0200:            }
0201:
0202:            /** Dependency: AliasService. */
0203:            protected AliasService m_aliasService = null;
0204:
0205:            /**
0206:             * Dependency: AliasService.
0207:             * 
0208:             * @param service
0209:             *        The AliasService.
0210:             */
0211:            public void setAliasService(AliasService service) {
0212:                m_aliasService = service;
0213:            }
0214:
0215:            /** Dependency: SiteService. */
0216:            protected SiteService m_siteService = null;
0217:
0218:            /**
0219:             * Dependency: SiteService.
0220:             * 
0221:             * @param service
0222:             *        The SiteService.
0223:             */
0224:            public void setSiteService(SiteService service) {
0225:                m_siteService = service;
0226:            }
0227:
0228:            /** Dependency: NotificationService. */
0229:            protected NotificationService m_notificationService = null;
0230:
0231:            /**
0232:             * Dependency: NotificationService.
0233:             * 
0234:             * @param service
0235:             *        The NotificationService.
0236:             */
0237:            public void setNotificationService(NotificationService service) {
0238:                m_notificationService = service;
0239:            }
0240:
0241:            /** Dependency: ServerConfigurationService. */
0242:            protected ServerConfigurationService m_serverConfigurationService = null;
0243:
0244:            /**
0245:             * Dependency: ServerConfigurationService.
0246:             * 
0247:             * @param service
0248:             *        The ServerConfigurationService.
0249:             */
0250:            public void setServerConfigurationService(
0251:                    ServerConfigurationService service) {
0252:                m_serverConfigurationService = service;
0253:            }
0254:
0255:            /** Dependency: EntityManager. */
0256:            protected EntityManager m_entityManager = null;
0257:
0258:            /**
0259:             * Dependency: EntityManager.
0260:             * 
0261:             * @param service
0262:             *        The EntityManager.
0263:             */
0264:            public void setEntityManager(EntityManager service) {
0265:                m_entityManager = service;
0266:            }
0267:
0268:            /** Dependency: AuthzGroupService. */
0269:            protected AuthzGroupService m_authzGroupService = null;
0270:
0271:            /**
0272:             * Dependency: AuthzGroupService.
0273:             * 
0274:             * @param service
0275:             *        The AuthzGroupService.
0276:             */
0277:            public void setAuthzGroupService(AuthzGroupService service) {
0278:                m_authzGroupService = service;
0279:            }
0280:
0281:            /** Dependency: SecurityService. */
0282:            protected SecurityService m_securityService = null;
0283:
0284:            /**
0285:             * Dependency: SecurityService.
0286:             * 
0287:             * @param service
0288:             *        The SecurityService.
0289:             */
0290:            public void setSecurityService(SecurityService service) {
0291:                m_securityService = service;
0292:            }
0293:
0294:            /**
0295:             * Set the site quota.
0296:             * 
0297:             * @param quota
0298:             *        The site quota (as a string).
0299:             */
0300:            public void setSiteQuota(String quota) {
0301:                try {
0302:                    m_siteQuota = Long.parseLong(quota);
0303:                } catch (Throwable t) {
0304:                }
0305:            }
0306:
0307:            /** Configuration: cache, or not. */
0308:            protected boolean m_caching = false;
0309:
0310:            /**
0311:             * Configuration: set the locks-in-db
0312:             * 
0313:             * @param path
0314:             *        The storage path.
0315:             */
0316:            public void setCaching(String value) {
0317:                try {
0318:                    m_caching = new Boolean(value).booleanValue();
0319:                } catch (Throwable t) {
0320:                }
0321:            }
0322:
0323:            /**
0324:             * Configuration: set the external file system path for body storage If set, the resource binary database table will not be used.
0325:             * 
0326:             * @param value
0327:             *        The complete path to the root of the external file system storage area for resource body bytes.
0328:             */
0329:            public void setBodyPath(String value) {
0330:                m_bodyPath = value;
0331:            }
0332:
0333:            /**
0334:             * Configuration: set the external file system volume folders (folder just within the bodyPath) as a comma separated list of folder names. If set, files will be distributed over these folders.
0335:             * 
0336:             * @param value
0337:             *        The comma separated list of folder names within body path to distribute files among.
0338:             */
0339:            public void setBodyVolumes(String value) {
0340:                try {
0341:                    m_bodyVolumes = StringUtil.split(value, ",");
0342:                } catch (Throwable t) {
0343:                }
0344:            }
0345:
0346:            /** Configuration: short refs */
0347:            protected boolean m_shortRefs = true;
0348:
0349:            /**
0350:             * Configuration: set the short refs
0351:             * 
0352:             * @param value
0353:             *        The short refs value.
0354:             */
0355:            public void setShortRefs(String value) {
0356:                try {
0357:                    m_shortRefs = new Boolean(value).booleanValue();
0358:                } catch (Throwable t) {
0359:                }
0360:            }
0361:
0362:            /**
0363:             * {@inheritDoc}
0364:             */
0365:            public boolean isShortRefs() {
0366:                return m_shortRefs;
0367:            }
0368:
0369:            /** Configuration: allow use of alias for site id in references. */
0370:            protected boolean m_siteAlias = true;
0371:
0372:            /**
0373:             * Configuration: set the alias for site
0374:             * 
0375:             * @param value
0376:             *        The alias for site value.
0377:             */
0378:            public void setSiteAlias(String value) {
0379:                try {
0380:                    m_siteAlias = new Boolean(value).booleanValue();
0381:                } catch (Throwable t) {
0382:                }
0383:            }
0384:
0385:            /** Dependency: allowGroupResources setting */
0386:            protected boolean m_allowGroupResources = true;
0387:
0388:            /**
0389:             * Dependency: allowGroupResources
0390:             * 
0391:             * @param allowGroupResources
0392:             *        the setting
0393:             */
0394:            public void setAllowGroupResources(boolean allowGroupResources) {
0395:                m_allowGroupResources = allowGroupResources;
0396:            }
0397:
0398:            /**
0399:             * Get
0400:             * 
0401:             * @return allowGroupResources
0402:             */
0403:            public boolean getAllowGroupResources() {
0404:                return m_allowGroupResources;
0405:            }
0406:
0407:            /** flag indicating whether entities can be hidden (scheduled or otherwise) */
0408:            protected boolean m_availabilityChecksEnabled = true;
0409:
0410:            /**
0411:             * Configuration: set a flag indicating whether entities can be hidden (scheduled or otherwise)
0412:             * 
0413:             * @param value
0414:             *        The value indicating whether entities can be hidden.
0415:             */
0416:            public void setAvailabilityChecksEnabled(boolean value) {
0417:                m_availabilityChecksEnabled = value;
0418:            }
0419:
0420:            /**
0421:             * Access flag indicating whether entities can be hidden (scheduled or otherwise).
0422:             * @return true if the availability features are enabled, false otherwise.
0423:             */
0424:            public boolean isAvailabilityEnabled() {
0425:                return m_availabilityChecksEnabled;
0426:            }
0427:
0428:            /** flag indicating whether custom sort order based on "priority" is enabled */
0429:            protected boolean m_prioritySortEnabled = true;
0430:
0431:            /**
0432:             * Configuration: set a flag indicating whether custom sort order based on "priority" is enabled
0433:             * 
0434:             * @param value
0435:             *        The value indicating whether custom sort order is enabled.
0436:             */
0437:            public void setPrioritySortEnabled(boolean value) {
0438:                m_prioritySortEnabled = value;
0439:            }
0440:
0441:            /**
0442:             * Configuration: set a flag indicating whether custom sort order based on "priority" is enabled
0443:             * 
0444:             * @param value
0445:             *        The value indicating whether custom sort order is enabled.
0446:             */
0447:            public boolean getPrioritySortEnabled() {
0448:                return m_prioritySortEnabled;
0449:            }
0450:
0451:            /**
0452:             * Access flag indicating whether sorting by "priority" is enabled.
0453:             * @return true if the custom sort by priority is enabled, false otherwise.
0454:             */
0455:            public boolean isSortByPriorityEnabled() {
0456:                return m_prioritySortEnabled;
0457:            }
0458:
0459:            /**
0460:             * Dependency: the ResourceTypeRegistry
0461:             */
0462:            protected ResourceTypeRegistry m_resourceTypeRegistry;
0463:
0464:            /**
0465:             * Dependency: inject the ResourceTypeRegistry
0466:             * @param registry
0467:             */
0468:            public void setResourceTypeRegistry(ResourceTypeRegistry registry) {
0469:                m_resourceTypeRegistry = registry;
0470:            }
0471:
0472:            /**
0473:             * @return the ResourceTypeRegistry
0474:             */
0475:            public ResourceTypeRegistry getResourceTypeRegistry() {
0476:                return m_resourceTypeRegistry;
0477:            }
0478:
0479:            protected boolean useResourceTypeRegistry = true;
0480:
0481:            public void setUseResourceTypeRegistry(boolean useRegistry) {
0482:                useResourceTypeRegistry = useRegistry;
0483:            }
0484:
0485:            public boolean usingResourceTypeRegistry() {
0486:                return useResourceTypeRegistry;
0487:            }
0488:
0489:            /**********************************************************************************************************************************************************************************************************************************************************
0490:             * Init and Destroy
0491:             *********************************************************************************************************************************************************************************************************************************************************/
0492:
0493:            /**
0494:             * Final initialization, once all dependencies are set.
0495:             */
0496:            public void init() {
0497:                try {
0498:                    m_relativeAccessPoint = REFERENCE_ROOT;
0499:
0500:                    // construct a storage helper and read
0501:                    m_storage = newStorage();
0502:                    m_storage.open();
0503:
0504:                    // make the cache
0505:                    if (m_caching) {
0506:                        m_cache = m_memoryService.newCache(this ,
0507:                                getAccessPoint(true));
0508:                    }
0509:
0510:                    // register a transient notification for resources
0511:                    NotificationEdit edit = m_notificationService
0512:                            .addTransientNotification();
0513:
0514:                    // set functions
0515:                    edit.setFunction(EVENT_RESOURCE_ADD);
0516:                    edit.addFunction(EVENT_RESOURCE_WRITE);
0517:
0518:                    // set the filter to any site related resource
0519:                    edit.setResourceFilter(getAccessPoint(true)
0520:                            + Entity.SEPARATOR + "group");
0521:                    // %%% is this the best we can do? -ggolden
0522:
0523:                    // set the action
0524:                    edit.setAction(new SiteEmailNotificationContent());
0525:
0526:                    StringBuffer buf = new StringBuffer();
0527:                    if (m_bodyVolumes != null) {
0528:                        for (int i = 0; i < m_bodyVolumes.length; i++) {
0529:                            buf.append(m_bodyVolumes[i]);
0530:                            buf.append(", ");
0531:                        }
0532:                    }
0533:
0534:                    // register as an entity producer
0535:                    m_entityManager
0536:                            .registerEntityProducer(this , REFERENCE_ROOT);
0537:
0538:                    // register functions
0539:                    FunctionManager.registerFunction(AUTH_RESOURCE_ADD);
0540:                    FunctionManager.registerFunction(AUTH_RESOURCE_READ);
0541:                    FunctionManager.registerFunction(AUTH_RESOURCE_WRITE_ANY);
0542:                    FunctionManager.registerFunction(AUTH_RESOURCE_WRITE_OWN);
0543:                    FunctionManager.registerFunction(AUTH_RESOURCE_REMOVE_ANY);
0544:                    FunctionManager.registerFunction(AUTH_RESOURCE_REMOVE_OWN);
0545:                    FunctionManager.registerFunction(AUTH_RESOURCE_ALL_GROUPS);
0546:                    FunctionManager.registerFunction(AUTH_RESOURCE_HIDDEN);
0547:
0548:                    FunctionManager.registerFunction(AUTH_DROPBOX_OWN);
0549:                    FunctionManager.registerFunction(AUTH_DROPBOX_MAINTAIN);
0550:
0551:                    M_log.info("init(): site quota: " + m_siteQuota
0552:                            + " body path: " + m_bodyPath + " volumes: "
0553:                            + buf.toString());
0554:                } catch (Throwable t) {
0555:                    M_log.warn("init(): ", t);
0556:                }
0557:
0558:                if (usingResourceTypeRegistry()) {
0559:                    this .getResourceTypeRegistry().register(
0560:                            new FileUploadType());
0561:                    this .getResourceTypeRegistry().register(new FolderType());
0562:                    this .getResourceTypeRegistry().register(
0563:                            new TextDocumentType());
0564:                    this .getResourceTypeRegistry().register(
0565:                            new HtmlDocumentType());
0566:                    this .getResourceTypeRegistry().register(
0567:                            new UrlResourceType());
0568:                }
0569:
0570:            } // init
0571:
0572:            /**
0573:             * Returns to uninitialized state.
0574:             */
0575:            public void destroy() {
0576:                m_storage.close();
0577:                m_storage = null;
0578:
0579:                if ((m_caching) && (m_cache != null)) {
0580:                    m_cache.destroy();
0581:                    m_cache = null;
0582:                }
0583:
0584:                M_log.info("destroy()");
0585:
0586:            }
0587:
0588:            /**********************************************************************************************************************************************************************************************************************************************************
0589:             * StorageUser implementation - for collections
0590:             *********************************************************************************************************************************************************************************************************************************************************/
0591:
0592:            /**
0593:             * Storage user for collections - in the resource side, not container
0594:             */
0595:            protected class CollectionStorageUser implements  StorageUser {
0596:                public Entity newContainer(String ref) {
0597:                    return null;
0598:                }
0599:
0600:                public Entity newContainer(Element element) {
0601:                    return null;
0602:                }
0603:
0604:                public Entity newContainer(Entity other) {
0605:                    return null;
0606:                }
0607:
0608:                public Entity newResource(Entity container, String id,
0609:                        Object[] others) {
0610:                    return new BaseCollectionEdit(id);
0611:                }
0612:
0613:                public Entity newResource(Entity container, Element element) {
0614:                    return new BaseCollectionEdit(element);
0615:                }
0616:
0617:                public Entity newResource(Entity container, Entity other) {
0618:                    return new BaseCollectionEdit((ContentCollection) other);
0619:                }
0620:
0621:                public Edit newContainerEdit(String ref) {
0622:                    return null;
0623:                }
0624:
0625:                public Edit newContainerEdit(Element element) {
0626:                    return null;
0627:                }
0628:
0629:                public Edit newContainerEdit(Entity other) {
0630:                    return null;
0631:                }
0632:
0633:                public Edit newResourceEdit(Entity container, String id,
0634:                        Object[] others) {
0635:                    BaseCollectionEdit rv = new BaseCollectionEdit(id);
0636:                    rv.activate();
0637:                    return rv;
0638:                }
0639:
0640:                public Edit newResourceEdit(Entity container, Element element) {
0641:                    BaseCollectionEdit rv = new BaseCollectionEdit(element);
0642:                    rv.activate();
0643:                    return rv;
0644:                }
0645:
0646:                public Edit newResourceEdit(Entity container, Entity other) {
0647:                    BaseCollectionEdit rv = new BaseCollectionEdit(
0648:                            (ContentCollection) other);
0649:                    rv.activate();
0650:                    return rv;
0651:                }
0652:
0653:                /**
0654:                 * Collect the fields that need to be stored outside the XML (for the resource).
0655:                 * 
0656:                 * @return An array of field values to store in the record outside the XML (for the resource).
0657:                 */
0658:                public Object[] storageFields(Entity r) {
0659:                    Object[] rv = new Object[1];
0660:                    rv[0] = StringUtil.referencePath(((ContentCollection) r)
0661:                            .getId());
0662:                    return rv;
0663:                }
0664:
0665:                /**
0666:                 * Check if this resource is in draft mode.
0667:                 * 
0668:                 * @param r
0669:                 *        The resource.
0670:                 * @return true if the resource is in draft mode, false if not.
0671:                 */
0672:                public boolean isDraft(Entity r) {
0673:                    return false;
0674:                }
0675:
0676:                /**
0677:                 * Access the resource owner user id.
0678:                 * 
0679:                 * @param r
0680:                 *        The resource.
0681:                 * @return The resource owner user id.
0682:                 */
0683:                public String getOwnerId(Entity r) {
0684:                    return null;
0685:                }
0686:
0687:                /**
0688:                 * Access the resource date.
0689:                 * 
0690:                 * @param r
0691:                 *        The resource.
0692:                 * @return The resource date.
0693:                 */
0694:                public Time getDate(Entity r) {
0695:                    return null;
0696:                }
0697:
0698:            } // class CollectionStorageUser
0699:
0700:            /**
0701:             * Storage user for resources - in the resource side, not container
0702:             */
0703:            protected class ResourceStorageUser implements  StorageUser {
0704:                public Entity newContainer(String ref) {
0705:                    return null;
0706:                }
0707:
0708:                public Entity newContainer(Element element) {
0709:                    return null;
0710:                }
0711:
0712:                public Entity newContainer(Entity other) {
0713:                    return null;
0714:                }
0715:
0716:                public Entity newResource(Entity container, String id,
0717:                        Object[] others) {
0718:                    return new BaseResourceEdit(id);
0719:                }
0720:
0721:                public Entity newResource(Entity container, Element element) {
0722:                    return new BaseResourceEdit(element);
0723:                }
0724:
0725:                public Entity newResource(Entity container, Entity other) {
0726:                    return new BaseResourceEdit((ContentResource) other);
0727:                }
0728:
0729:                public Edit newContainerEdit(String ref) {
0730:                    return null;
0731:                }
0732:
0733:                public Edit newContainerEdit(Element element) {
0734:                    return null;
0735:                }
0736:
0737:                public Edit newContainerEdit(Entity other) {
0738:                    return null;
0739:                }
0740:
0741:                public Edit newResourceEdit(Entity container, String id,
0742:                        Object[] others) {
0743:                    BaseResourceEdit rv = new BaseResourceEdit(id);
0744:                    rv.activate();
0745:                    return rv;
0746:                }
0747:
0748:                public Edit newResourceEdit(Entity container, Element element) {
0749:                    BaseResourceEdit rv = new BaseResourceEdit(element);
0750:                    rv.activate();
0751:                    return rv;
0752:                }
0753:
0754:                public Edit newResourceEdit(Entity container, Entity other) {
0755:                    BaseResourceEdit rv = new BaseResourceEdit(
0756:                            (ContentResource) other);
0757:                    rv.activate();
0758:                    return rv;
0759:                }
0760:
0761:                /**
0762:                 * Collect the fields that need to be stored outside the XML (for the resource).
0763:                 * 
0764:                 * @return An array of field values to store in the record outside the XML (for the resource).
0765:                 */
0766:                public Object[] storageFields(Entity r) {
0767:                    // include the file path field if we are doing body in the file system
0768:                    if (m_bodyPath != null) {
0769:                        Object[] rv = new Object[2];
0770:                        rv[0] = StringUtil.referencePath(((ContentResource) r)
0771:                                .getId());
0772:                        rv[1] = StringUtil
0773:                                .trimToZero(((BaseResourceEdit) r).m_filePath);
0774:                        return rv;
0775:                    }
0776:
0777:                    // otherwise don't include the file path field
0778:                    else {
0779:                        Object[] rv = new Object[1];
0780:                        rv[0] = StringUtil.referencePath(((ContentResource) r)
0781:                                .getId());
0782:                        return rv;
0783:                    }
0784:                }
0785:
0786:                /**
0787:                 * Check if this resource is in draft mode.
0788:                 * 
0789:                 * @param r
0790:                 *        The resource.
0791:                 * @return true if the resource is in draft mode, false if not.
0792:                 */
0793:                public boolean isDraft(Entity r) {
0794:                    return false;
0795:                }
0796:
0797:                /**
0798:                 * Access the resource owner user id.
0799:                 * 
0800:                 * @param r
0801:                 *        The resource.
0802:                 * @return The resource owner user id.
0803:                 */
0804:                public String getOwnerId(Entity r) {
0805:                    return null;
0806:                }
0807:
0808:                /**
0809:                 * Access the resource date.
0810:                 * 
0811:                 * @param r
0812:                 *        The resource.
0813:                 * @return The resource date.
0814:                 */
0815:                public Time getDate(Entity r) {
0816:                    return null;
0817:                }
0818:
0819:            } // class ResourceStorageUser
0820:
0821:            /**********************************************************************************************************************************************************************************************************************************************************
0822:             * ContentHostingService implementation
0823:             *********************************************************************************************************************************************************************************************************************************************************/
0824:
0825:            /**
0826:             * Construct a Storage object.
0827:             * 
0828:             * @return The new storage object.
0829:             */
0830:            protected abstract Storage newStorage();
0831:
0832:            /**
0833:             * Determine whether the entityId parameter identifies a collection (as opposed to a resource).  
0834:             * This method does not necessarily verify that a ContentEntity with this id exists.  
0835:             * It merely determines whether the id could identify a collection.
0836:             * @param entityId
0837:             * @return true if the entityId could identify a collection, false otherwise.
0838:             */
0839:            public boolean isCollection(String entityId) {
0840:                return (entityId != null && entityId.endsWith(Entity.SEPARATOR));
0841:            }
0842:
0843:            /**
0844:             * @param id
0845:             *        id of the resource to set the UUID for
0846:             * @param uuid
0847:             *        the new UUID of the resource
0848:             */
0849:            protected abstract void setUuidInternal(String id, String uuid);
0850:
0851:            /**
0852:             * Access the partial URL that forms the root of resource URLs.
0853:             * 
0854:             * @param relative
0855:             *        if true, form within the access path only (i.e. starting with /content)
0856:             * @return the partial URL that forms the root of resource URLs.
0857:             */
0858:            protected String getAccessPoint(boolean relative) {
0859:                return (relative ? "" : m_serverConfigurationService
0860:                        .getAccessUrl())
0861:                        + m_relativeAccessPoint;
0862:
0863:            } // getAccessPoint
0864:
0865:            /**
0866:             * If the id is for a resource in a dropbox, change the function to a dropbox check, which is to check for write.<br />
0867:             * You have full or no access to a dropbox.
0868:             * 
0869:             * @param lock
0870:             *        The lock we are checking.
0871:             * @param id
0872:             *        The resource id.
0873:             * @return The lock to check.
0874:             */
0875:            protected String convertLockIfDropbox(String lock, String id) {
0876:                // if this resource is a dropbox, you need dropbox maintain permission
0877:                if (id.startsWith(COLLECTION_DROPBOX)) {
0878:                    // only for /group-user/SITEID/USERID/ refs.
0879:                    String[] parts = StringUtil.split(id, "/");
0880:                    if (parts.length >= 3) {
0881:                        return AUTH_DROPBOX_MAINTAIN;
0882:                    }
0883:                }
0884:
0885:                return lock;
0886:            }
0887:
0888:            /**
0889:             * Check whether an id would identify an entity in a dropbox.  Does not determine existence of the entity, just whether its id indicates it is a dropbox or contained within a dropbox.
0890:             * @return true if the entity is a dropbox or in a dropbox, false otherwise. 
0891:             */
0892:            public boolean isInDropbox(String entityId) {
0893:                return entityId.startsWith("/group-user");
0894:            }
0895:
0896:            /**
0897:             * Check whether the resource is hidden.
0898:             * @param id
0899:             * @return
0900:             * @throws IdUnusedException
0901:             */
0902:            protected boolean availabilityCheck(String id)
0903:                    throws IdUnusedException {
0904:                // item is available if avaialability checks are <b>NOT</b> enabled OR if it's in /attachment
0905:                boolean available = (!m_availabilityChecksEnabled)
0906:                        || isAttachmentResource(id);
0907:
0908:                GroupAwareEntity entity = null;
0909:                //boolean isCollection = id.endsWith(Entity.SEPARATOR);
0910:                while (!available && entity == null && id != null
0911:                        && !id.trim().equals("")) {
0912:                    if (ROOT_COLLECTIONS.contains(id)) {
0913:                        available = true;
0914:                    } else {
0915:                        try {
0916:                            if (isCollection(id)) {
0917:                                entity = findCollection(id);
0918:                            } else {
0919:                                entity = findResource(id);
0920:                            }
0921:                        } catch (TypeException ignore) {
0922:                            if (isCollection(id)) {
0923:                                M_log
0924:                                        .warn("trying to get collection, found resource: "
0925:                                                + id);
0926:                            } else {
0927:                                M_log
0928:                                        .warn("trying to get resource, found collection: "
0929:                                                + id);
0930:                            }
0931:                        }
0932:
0933:                        if (entity == null) {
0934:                            id = isolateContainingId(id);
0935:                            // isCollection = true;
0936:                        }
0937:                    }
0938:                }
0939:
0940:                if (!available && entity != null) {
0941:                    String creator = entity.getProperties().getProperty(
0942:                            ResourceProperties.PROP_CREATOR);
0943:                    String userId = SessionManager.getCurrentSessionUserId();
0944:
0945:                    // available if user is creator
0946:                    available = creator != null && userId != null
0947:                            && creator.equals(userId);
0948:
0949:                    if (!available) {
0950:                        // available if user has permission to view hidden entities
0951:                        String lock = AUTH_RESOURCE_HIDDEN;
0952:                        available = SecurityService.unlock(lock, entity
0953:                                .getReference());
0954:
0955:                        if (!available) {
0956:                            // available if not hidden or in a hidden collection
0957:                            available = entity.isAvailable();
0958:                        }
0959:
0960:                    }
0961:                }
0962:
0963:                return available;
0964:
0965:            }
0966:
0967:            /**
0968:             * Determine whether an entity is available to this user at this time, taking into account whether the item is hidden and the user's 
0969:             * status with respect to viewing hidden entities in this context.
0970:             * @param entityId
0971:             * @return true if the item is not hidden or it's hidden but the user has permissions to view hidden items in this context (site? folder? group?), 
0972:             * and false otherwise. 
0973:             */
0974:            public boolean isAvailable(String entityId) {
0975:                boolean available = true;
0976:                try {
0977:                    available = availabilityCheck(entityId);
0978:                } catch (IdUnusedException e) {
0979:                    available = false;
0980:                }
0981:                return available;
0982:            }
0983:
0984:            /**
0985:             * Check security permission.
0986:             * 
0987:             * @param lock
0988:             *        The lock id string.
0989:             * @param id
0990:             *        The resource id string, or null if no resource is involved.
0991:             * @return true if permitted, false if not.
0992:             */
0993:            protected boolean unlockCheck(String lock, String id) {
0994:                boolean isAllowed = SecurityService.isSuperUser();
0995:                if (!isAllowed) {
0996:                    lock = convertLockIfDropbox(lock, id);
0997:
0998:                    // make a reference from the resource id, if specified
0999:                    String ref = null;
1000:                    if (id != null) {
1001:                        ref = getReference(id);
1002:                    }
1003:
1004:                    isAllowed = ref != null
1005:                            && SecurityService.unlock(lock, ref);
1006:                }
1007:
1008:                if (isAllowed
1009:                        && lock != null
1010:                        && (lock.startsWith("content.") || lock
1011:                                .startsWith("dropbox."))
1012:                        && m_availabilityChecksEnabled) {
1013:                    try {
1014:                        isAllowed = availabilityCheck(id);
1015:                    } catch (IdUnusedException e) {
1016:                        // ignore because we would have caught this earlier.
1017:                    }
1018:                }
1019:
1020:                return isAllowed;
1021:
1022:            } // unlockCheck
1023:
1024:            /**
1025:             * Throws a PermissionException if the resource with the given Id is explicitly locked
1026:             * 
1027:             * @param id
1028:             * @throws PermissionException
1029:             */
1030:            protected void checkExplicitLock(String id)
1031:                    throws PermissionException {
1032:                String uuid = this .getUuid(id);
1033:
1034:                if (uuid != null && this .isLocked(uuid)) {
1035:                    // TODO: WebDAV locks need to be more sophisticated than this
1036:                    throw new PermissionException(SessionManager
1037:                            .getCurrentSessionUserId(), "remove", id);
1038:                }
1039:            }
1040:
1041:            /**
1042:             * Check security permission.
1043:             * 
1044:             * @param lock
1045:             *        The lock id string.
1046:             * @param id
1047:             *        The resource id string, or null if no resource is involved.
1048:             * @exception PermissionException
1049:             *            Thrown if the user does not have access
1050:             */
1051:            protected void unlock(String lock, String id)
1052:                    throws PermissionException {
1053:                if (SecurityService.isSuperUser()) {
1054:                    return;
1055:                }
1056:
1057:                lock = convertLockIfDropbox(lock, id);
1058:
1059:                // make a reference from the resource id, if specified
1060:                String ref = null;
1061:                if (id != null) {
1062:                    ref = getReference(id);
1063:                }
1064:
1065:                if (!SecurityService.unlock(lock, ref)) {
1066:                    throw new PermissionException(SessionManager
1067:                            .getCurrentSessionUserId(), lock, ref);
1068:                }
1069:                boolean available = false;
1070:                try {
1071:                    available = availabilityCheck(id);
1072:                } catch (IdUnusedException e) {
1073:                    // ignore. this was checked earlier in the call
1074:                }
1075:                if (!available) {
1076:                    throw new PermissionException(SessionManager
1077:                            .getCurrentSessionUserId(), lock, ref);
1078:                }
1079:
1080:            } // unlock
1081:
1082:            /**
1083:             * Check security permission for all contained collections of the given collection (if any) (not the collection itself)
1084:             * 
1085:             * @param lock
1086:             *        The lock id string.
1087:             * @param resource
1088:             *        The resource id string, or null if no resource is involved.
1089:             * @exception PermissionException
1090:             *            Thrown if the user does not have access
1091:             */
1092:            /*
1093:             * protected void unlockContained(String lock, ContentCollection collection) throws PermissionException { if (SecurityService == null) return; Iterator it = collection.getMemberResources().iterator(); while (it.hasNext()) { Object mbr = it.next(); if
1094:             * (mbr == null) continue; // for a contained collection, check recursively if (mbr instanceof ContentCollection) { unlockContained(lock, (ContentCollection) mbr); } // for resources, check else if (mbr instanceof ContentResource) { unlock(lock,
1095:             * ((ContentResource) mbr).getId()); } } } // unlockContained
1096:             */
1097:
1098:            /**
1099:             * Create the live properties for a collection.
1100:             * 
1101:             * @param c
1102:             *        The collection.
1103:             */
1104:            protected void addLiveCollectionProperties(ContentCollectionEdit c) {
1105:                ResourcePropertiesEdit p = c.getPropertiesEdit();
1106:                String current = SessionManager.getCurrentSessionUserId();
1107:                p.addProperty(ResourceProperties.PROP_CREATOR, current);
1108:                p.addProperty(ResourceProperties.PROP_MODIFIED_BY, current);
1109:
1110:                String now = TimeService.newTime().toString();
1111:                p.addProperty(ResourceProperties.PROP_CREATION_DATE, now);
1112:                p.addProperty(ResourceProperties.PROP_MODIFIED_DATE, now);
1113:
1114:                p.addProperty(ResourceProperties.PROP_IS_COLLECTION, "true");
1115:
1116:            } // addLiveCollectionProperties
1117:
1118:            /**
1119:             * Create the live properties for a collection.
1120:             * 
1121:             * @param c
1122:             *        The collection.
1123:             */
1124:            protected void addLiveUpdateCollectionProperties(
1125:                    ContentCollectionEdit c) {
1126:                ResourcePropertiesEdit p = c.getPropertiesEdit();
1127:                String current = SessionManager.getCurrentSessionUserId();
1128:                p.addProperty(ResourceProperties.PROP_MODIFIED_BY, current);
1129:
1130:                String now = TimeService.newTime().toString();
1131:                p.addProperty(ResourceProperties.PROP_MODIFIED_DATE, now);
1132:
1133:            } // addLiveUpdateCollectionProperties
1134:
1135:            /**
1136:             * Create the live properties for a resource.
1137:             * 
1138:             * @param r
1139:             *        The resource.
1140:             */
1141:            protected void addLiveResourceProperties(ContentResourceEdit r) {
1142:                ResourcePropertiesEdit p = r.getPropertiesEdit();
1143:
1144:                String current = SessionManager.getCurrentSessionUserId();
1145:                p.addProperty(ResourceProperties.PROP_CREATOR, current);
1146:                p.addProperty(ResourceProperties.PROP_MODIFIED_BY, current);
1147:
1148:                String now = TimeService.newTime().toString();
1149:                p.addProperty(ResourceProperties.PROP_CREATION_DATE, now);
1150:                p.addProperty(ResourceProperties.PROP_MODIFIED_DATE, now);
1151:
1152:                p.addProperty(ResourceProperties.PROP_CONTENT_LENGTH, Long
1153:                        .toString(r.getContentLength()));
1154:                p.addProperty(ResourceProperties.PROP_CONTENT_TYPE, r
1155:                        .getContentType());
1156:
1157:                p.addProperty(ResourceProperties.PROP_IS_COLLECTION, "false");
1158:
1159:            } // addLiveResourceProperties
1160:
1161:            /**
1162:             * Update the live properties for a resource when modified (for a resource).
1163:             * 
1164:             * @param r
1165:             *        The resource.
1166:             */
1167:            protected void addLiveUpdateResourceProperties(ContentResourceEdit r) {
1168:                ResourcePropertiesEdit p = r.getPropertiesEdit();
1169:
1170:                String current = SessionManager.getCurrentSessionUserId();
1171:                p.addProperty(ResourceProperties.PROP_MODIFIED_BY, current);
1172:
1173:                String now = TimeService.newTime().toString();
1174:                p.addProperty(ResourceProperties.PROP_MODIFIED_DATE, now);
1175:
1176:                p.addProperty(ResourceProperties.PROP_CONTENT_LENGTH, Long
1177:                        .toString(r.getContentLength()));
1178:                p.addProperty(ResourceProperties.PROP_CONTENT_TYPE, r
1179:                        .getContentType());
1180:
1181:            } // addLiveUpdateResourceProperties
1182:
1183:            /**
1184:             * Make sure that the entire set of properties are present, adding whatever is needed, replacing nothing that's there already.
1185:             * 
1186:             * @param r
1187:             *        The resource.
1188:             */
1189:            protected void assureResourceProperties(ContentResourceEdit r) {
1190:                ResourcePropertiesEdit p = r.getPropertiesEdit();
1191:
1192:                String current = SessionManager.getCurrentSessionUserId();
1193:                String now = TimeService.newTime().toString();
1194:
1195:                if (p.getProperty(ResourceProperties.PROP_CREATOR) == null) {
1196:                    p.addProperty(ResourceProperties.PROP_CREATOR, current);
1197:                }
1198:
1199:                if (p.getProperty(ResourceProperties.PROP_CREATION_DATE) == null) {
1200:                    p.addProperty(ResourceProperties.PROP_CREATION_DATE, now);
1201:                }
1202:
1203:                if (p.getProperty(ResourceProperties.PROP_MODIFIED_BY) == null) {
1204:                    p.addProperty(ResourceProperties.PROP_MODIFIED_BY, current);
1205:                }
1206:
1207:                if (p.getProperty(ResourceProperties.PROP_MODIFIED_DATE) == null) {
1208:                    p.addProperty(ResourceProperties.PROP_MODIFIED_DATE, now);
1209:                }
1210:
1211:                // these can just be set
1212:                p.addProperty(ResourceProperties.PROP_CONTENT_LENGTH, Long
1213:                        .toString(r.getContentLength()));
1214:                p.addProperty(ResourceProperties.PROP_CONTENT_TYPE, r
1215:                        .getContentType());
1216:                p.addProperty(ResourceProperties.PROP_IS_COLLECTION, "false");
1217:
1218:            } // assureResourceProperties
1219:
1220:            /**
1221:             * Add properties for a resource.
1222:             * 
1223:             * @param r
1224:             *        The resource.
1225:             * @param props
1226:             *        The properties.
1227:             */
1228:            protected void addProperties(ResourcePropertiesEdit p,
1229:                    ResourceProperties props) {
1230:                if (props == null)
1231:                    return;
1232:
1233:                Iterator it = props.getPropertyNames();
1234:                while (it.hasNext()) {
1235:                    String name = (String) it.next();
1236:
1237:                    // skip any live properties
1238:                    if (!props.isLiveProperty(name)) {
1239:                        p.addProperty(name, props.getProperty(name));
1240:                    }
1241:                }
1242:
1243:            } // addProperties
1244:
1245:            /**********************************************************************************************************************************************************************************************************************************************************
1246:             * Collections
1247:             *********************************************************************************************************************************************************************************************************************************************************/
1248:
1249:            /**
1250:             * check permissions for addCollection().
1251:             * 
1252:             * @param id
1253:             *        The id of the new collection.
1254:             * @return true if the user is allowed to addCollection(id), false if not.
1255:             */
1256:            public boolean allowAddCollection(String id) {
1257:                // collection must also end in the separator (we fix it)
1258:                if (!id.endsWith(Entity.SEPARATOR)) {
1259:                    id = id + Entity.SEPARATOR;
1260:                }
1261:
1262:                // check security
1263:                return unlockCheck(AUTH_RESOURCE_ADD, id);
1264:
1265:            } // allowAddCollection
1266:
1267:            /**
1268:             * Create a new collection with the given resource id.
1269:             * 
1270:             * @param id
1271:             *        The id of the collection.
1272:             * @param properties
1273:             *        A java Properties object with the properties to add to the new collection.
1274:             * @exception IdUsedException
1275:             *            if the id is already in use.
1276:             * @exception IdInvalidException
1277:             *            if the id is invalid.
1278:             * @exception PermissionException
1279:             *            if the user does not have permission to add a collection, or add a member to a collection.
1280:             * @exception InconsistentException
1281:             *            if the containing collection does not exist.
1282:             * @return a new ContentCollection object.
1283:             */
1284:            public ContentCollection addCollection(String id,
1285:                    ResourceProperties properties) throws IdUsedException,
1286:                    IdInvalidException, PermissionException,
1287:                    InconsistentException {
1288:                ContentCollectionEdit edit = addCollection(id);
1289:
1290:                // add the provided of properties
1291:                addProperties(edit.getPropertiesEdit(), properties);
1292:
1293:                // commit the change
1294:                commitCollection(edit);
1295:
1296:                return edit;
1297:
1298:            } // addCollection
1299:
1300:            public ContentCollection addCollection(String id,
1301:                    ResourceProperties properties, Collection groups)
1302:                    throws IdUsedException, IdInvalidException,
1303:                    PermissionException, InconsistentException {
1304:                ContentCollectionEdit edit = addCollection(id);
1305:
1306:                // add the provided of properties
1307:                addProperties(edit.getPropertiesEdit(), properties);
1308:                try {
1309:                    if (groups == null || groups.isEmpty()) {
1310:                        ((BasicGroupAwareEdit) edit).clearGroupAccess();
1311:                    } else {
1312:                        ((BasicGroupAwareEdit) edit).setGroupAccess(groups);
1313:                    }
1314:                } catch (InconsistentException e) {
1315:                    // ignore
1316:                }
1317:
1318:                // commit the change
1319:                commitCollection(edit);
1320:
1321:                return edit;
1322:
1323:            }
1324:
1325:            public ContentCollection addCollection(String id,
1326:                    ResourceProperties properties, Collection groups,
1327:                    boolean hidden, Time releaseDate, Time retractDate)
1328:                    throws IdUsedException, IdInvalidException,
1329:                    PermissionException, InconsistentException {
1330:                ContentCollectionEdit edit = addCollection(id);
1331:
1332:                // add the provided of properties
1333:                addProperties(edit.getPropertiesEdit(), properties);
1334:                try {
1335:                    if (groups == null || groups.isEmpty()) {
1336:                        ((BasicGroupAwareEdit) edit).clearGroupAccess();
1337:                    } else {
1338:                        ((BasicGroupAwareEdit) edit).setGroupAccess(groups);
1339:                    }
1340:                } catch (InconsistentException e) {
1341:                    // ignore
1342:                }
1343:                edit.setAvailability(hidden, releaseDate, retractDate);
1344:
1345:                // commit the change
1346:                commitCollection(edit);
1347:
1348:                return edit;
1349:
1350:            }
1351:
1352:            /**
1353:             * Create a new collection with the given resource id, locked for update. Must commitCollection() to make official, or cancelCollection() when done!
1354:             * 
1355:             * @param id
1356:             *        The id of the collection.
1357:             * @exception IdUsedException
1358:             *            if the id is already in use.
1359:             * @exception IdInvalidException
1360:             *            if the id is invalid.
1361:             * @exception PermissionException
1362:             *            if the user does not have permission to add a collection, or add a member to a collection.
1363:             * @exception InconsistentException
1364:             *            if the containing collection does not exist.
1365:             * @return a new ContentCollection object.
1366:             */
1367:            public ContentCollectionEdit addCollection(String id)
1368:                    throws IdUsedException, IdInvalidException,
1369:                    PermissionException, InconsistentException {
1370:                // check the id's validity (this may throw IdInvalidException)
1371:                // use only the "name" portion, separated at the end
1372:                String justName = isolateName(id);
1373:                Validator.checkResourceId(justName);
1374:
1375:                // collection must also end in the separator (we fix it)
1376:                if (!id.endsWith(Entity.SEPARATOR)) {
1377:                    id = id + Entity.SEPARATOR;
1378:                }
1379:
1380:                // check security
1381:                unlock(AUTH_RESOURCE_ADD, id);
1382:
1383:                return addValidPermittedCollection(id);
1384:            }
1385:
1386:            /**
1387:             * @param collectionId
1388:             * @param name
1389:             * @return
1390:             * @exception PermissionException
1391:             *            if the user does not have permission to add a resource to the containing collection.
1392:             * @exception IdUnusedException
1393:             *            if the collectionId does not identify an existing collection. 
1394:             * @exception IdUnusedException
1395:             *            if the collection id for the proposed name already exists in this collection.
1396:             * @exception IdLengthException
1397:             *            if the new collection id exceeds the maximum number of characters for a valid collection id.
1398:             * @exception IdInvalidException
1399:             *            if the resource id is invalid.
1400:             */
1401:            public ContentCollectionEdit addCollection(String collectionId,
1402:                    String name) throws PermissionException, IdUnusedException,
1403:                    IdUsedException, IdLengthException, IdInvalidException,
1404:                    TypeException {
1405:                // check the id's validity (this may throw IdInvalidException)
1406:                // use only the "name" portion, separated at the end
1407:                Validator.checkResourceId(name);
1408:                checkCollection(collectionId);
1409:
1410:                String id = collectionId + name.trim();
1411:                if (id.length() > MAXIMUM_RESOURCE_ID_LENGTH) {
1412:                    throw new IdLengthException(id);
1413:                }
1414:
1415:                // collection must also end in the separator (we fix it)
1416:                if (!id.endsWith(Entity.SEPARATOR)) {
1417:                    id = id + Entity.SEPARATOR;
1418:                }
1419:
1420:                // check security
1421:                unlock(AUTH_RESOURCE_ADD, id);
1422:
1423:                ContentCollectionEdit edit = null;
1424:
1425:                try {
1426:                    edit = addValidPermittedCollection(id);
1427:                } catch (InconsistentException e) {
1428:                    throw new IdUnusedException(collectionId);
1429:                }
1430:
1431:                return edit;
1432:            }
1433:
1434:            /**
1435:             * Create a new collection with the given resource id, locked for update. Must commitCollection() to make official, or cancelCollection() when done!
1436:             * 
1437:             * @param id
1438:             *        The id of the collection.
1439:             * @exception IdUsedException
1440:             *            if the id is already in use.
1441:             * @exception InconsistentException
1442:             *            if the containing collection does not exist.
1443:             * @return a new ContentCollection object.
1444:             */
1445:            protected ContentCollectionEdit addValidPermittedCollection(
1446:                    String id) throws IdUsedException, InconsistentException {
1447:                // make sure the containing collection exists
1448:                String container = isolateContainingId(id);
1449:                ContentCollection containingCollection = m_storage
1450:                        .getCollection(container);
1451:                if (containingCollection == null) {
1452:                    // make any missing collections
1453:                    generateCollections(container);
1454:
1455:                    // try again
1456:                    containingCollection = m_storage.getCollection(container);
1457:                    if (containingCollection == null)
1458:                        throw new InconsistentException(id);
1459:                }
1460:
1461:                // reserve the collection in storage - it will fail if the id is in use
1462:                BaseCollectionEdit edit = (BaseCollectionEdit) m_storage
1463:                        .putCollection(id);
1464:                if (edit == null) {
1465:                    throw new IdUsedException(id);
1466:                }
1467:
1468:                // add live properties
1469:                addLiveCollectionProperties(edit);
1470:
1471:                // track event
1472:                edit.setEvent(EVENT_RESOURCE_ADD);
1473:
1474:                return edit;
1475:            }
1476:
1477:            /**
1478:             * check permissions for getCollection().
1479:             * 
1480:             * @param id
1481:             *        The id of the collection.
1482:             * @return true if the user is allowed to getCollection(id), false if not.
1483:             */
1484:            public boolean allowGetCollection(String id) {
1485:                return unlockCheck(AUTH_RESOURCE_READ, id);
1486:
1487:            } // allowGetCollection
1488:
1489:            /**
1490:             * Check access to the collection with this local resource id.
1491:             * 
1492:             * @param id
1493:             *        The id of the collection.
1494:             * @exception IdUnusedException
1495:             *            if the id does not exist.
1496:             * @exception TypeException
1497:             *            if the resource exists but is not a collection.
1498:             * @exception PermissionException
1499:             *            if the user does not have permissions to see this collection (or read through containing collections).
1500:             */
1501:            public void checkCollection(String id) throws IdUnusedException,
1502:                    TypeException, PermissionException {
1503:                unlock(AUTH_RESOURCE_READ, id);
1504:
1505:                ContentCollection collection = findCollection(id);
1506:                if (collection == null)
1507:                    throw new IdUnusedException(id);
1508:
1509:            } // checkCollection
1510:
1511:            /**
1512:             * Access the collection with this local resource id. The collection internal members and properties are accessible from the returned Colelction object.
1513:             * 
1514:             * @param id
1515:             *        The id of the collection.
1516:             * @exception IdUnusedException
1517:             *            if the id does not exist.
1518:             * @exception TypeException
1519:             *            if the resource exists but is not a collection.
1520:             * @exception PermissionException
1521:             *            if the user does not have permissions to see this collection (or read through containing collections).
1522:             * @return The ContentCollection object found.
1523:             */
1524:            public ContentCollection getCollection(String id)
1525:                    throws IdUnusedException, TypeException,
1526:                    PermissionException {
1527:                unlock(AUTH_RESOURCE_READ, id);
1528:
1529:                ContentCollection collection = findCollection(id);
1530:                if (collection == null)
1531:                    throw new IdUnusedException(id);
1532:
1533:                // track event
1534:                // EventTrackingService.post(EventTrackingService.newEvent(EVENT_RESOURCE_READ, collection.getReference(), false));
1535:
1536:                return collection;
1537:
1538:            } // getCollection
1539:
1540:            /**
1541:             * Access a List of ContentEntity objects (resources and collections) in this path (and below) if the current user has access to the collection.
1542:             * 
1543:             * @param id
1544:             *        A collection id.
1545:             * @return a List of the ContentEntity objects.
1546:             */
1547:            public List getAllEntities(String id) {
1548:                List rv = new Vector();
1549:
1550:                // get the collection members
1551:                try {
1552:                    ContentCollection collection = getCollection(id);
1553:                    if (collection != null) {
1554:                        getAllEntities(collection, rv, true);
1555:                    }
1556:                } catch (TypeException e) {
1557:                    // should result in an empty list
1558:                } catch (IdUnusedException e) {
1559:                    // should result in an empty list
1560:                } catch (PermissionException e) {
1561:                    // should result in an empty list
1562:                }
1563:
1564:                return rv;
1565:
1566:            } // getAllEntities
1567:
1568:            /**
1569:             * Access a List of all the ContentResource objects in this collection (and below).
1570:             * 
1571:             * @param collection
1572:             *        The collection.
1573:             * @param rv
1574:             *        The list in which to accumulate resource objects.
1575:             * @param includeCollections TODO
1576:             */
1577:            protected void getAllEntities(ContentCollection collection,
1578:                    List rv, boolean includeCollections) {
1579:                if (includeCollections) {
1580:                    rv.add(collection);
1581:                }
1582:
1583:                List members = collection.getMemberResources();
1584:
1585:                // process members
1586:                for (Iterator iMbrs = members.iterator(); iMbrs.hasNext();) {
1587:                    ContentEntity next = (ContentEntity) iMbrs.next();
1588:
1589:                    // if resource, add it if permitted
1590:
1591:                    if (next instanceof  ContentResource) {
1592:                        rv.add(next);
1593:                    }
1594:
1595:                    // if collection, again
1596:                    else {
1597:                        getAllEntities((ContentCollection) next, rv,
1598:                                includeCollections);
1599:                    }
1600:                }
1601:
1602:            } // getAllEntities
1603:
1604:            /**
1605:             * Access a List of all the ContentResource objects in this path (and below) which the current user has access.
1606:             * 
1607:             * @param id
1608:             *        A collection id.
1609:             * @return a List of the ContentResource objects.
1610:             */
1611:            public List getAllResources(String id) {
1612:                List rv = new Vector();
1613:
1614:                // get the collection members
1615:                try {
1616:                    ContentCollection collection = findCollection(id);
1617:                    if (collection != null) {
1618:                        getAllResources(collection, rv, false);
1619:                    }
1620:                } catch (TypeException e) {
1621:                }
1622:
1623:                return rv;
1624:
1625:            } // getAllResources
1626:
1627:            /**
1628:             * Access a List of all the ContentResource objects in this collection (and below) which the current user has access.
1629:             * 
1630:             * @param collection
1631:             *        The collection.
1632:             * @param rv
1633:             *        The list in which to accumulate resource objects.
1634:             * @param includeCollections TODO
1635:             */
1636:            protected void getAllResources(ContentCollection collection,
1637:                    List rv, boolean includeCollections) {
1638:                if (includeCollections) {
1639:                    if (unlockCheck(AUTH_RESOURCE_READ, collection.getId())) {
1640:                        rv.add(collection);
1641:                    }
1642:                }
1643:
1644:                List members = collection.getMemberResources();
1645:
1646:                // process members
1647:                for (Iterator iMbrs = members.iterator(); iMbrs.hasNext();) {
1648:                    ContentEntity next = (ContentEntity) iMbrs.next();
1649:
1650:                    // if resource, add it if permitted
1651:
1652:                    if (next instanceof  ContentResource) {
1653:                        if (unlockCheck(AUTH_RESOURCE_READ, next.getId())) {
1654:                            rv.add(next);
1655:                        }
1656:                    }
1657:
1658:                    // if collection, again
1659:                    else {
1660:                        getAllResources((ContentCollection) next, rv,
1661:                                includeCollections);
1662:                    }
1663:                }
1664:
1665:            } // getAllResources
1666:
1667:            /**
1668:             * Access the collection with this local resource id. Internal find does the guts of finding without security or event tracking. The collection internal members and properties are accessible from the returned Colelction object.
1669:             * 
1670:             * @param id
1671:             *        The id of the collection.
1672:             * @exception TypeException
1673:             *            if the resource exists but is not a collection.
1674:             * @return The ContentCollection object found, or null if not.
1675:             */
1676:            protected ContentCollection findCollection(String id)
1677:                    throws TypeException {
1678:                String ref = getReference(id);
1679:
1680:                ContentCollection collection = null;
1681:                try {
1682:                    collection = (ContentCollection) ThreadLocalManager
1683:                            .get("findCollection@" + ref);
1684:                } catch (ClassCastException e) {
1685:                    throw new TypeException(ref);
1686:                }
1687:
1688:                if (collection == null) {
1689:                    collection = m_storage.getCollection(id);
1690:
1691:                    if (collection != null) {
1692:                        ThreadLocalManager.set("findCollection@" + ref,
1693:                                new BaseCollectionEdit(collection));
1694:                    }
1695:                } else {
1696:                    collection = new BaseCollectionEdit(collection);
1697:                }
1698:
1699:                //		// if not caching
1700:                //		if ((!m_caching) || (m_cache == null) || (m_cache.disabled()))
1701:                //		{
1702:                //			// TODO: current service caching
1703:                //			collection = m_storage.getCollection(id);
1704:                //		}
1705:                //		else
1706:                //		{
1707:                //			// if we have it cached, use it (hit or miss)
1708:                //			String key = getReference(id);
1709:                //			if (m_cache.containsKey(key))
1710:                //			{
1711:                //				Object o = m_cache.get(key);
1712:                //				if ((o != null) && (!(o instanceof ContentCollection))) throw new TypeException(id);
1713:                //
1714:                //				collection = (ContentCollection) o;
1715:                //			}
1716:                //
1717:                //			// if not in the cache, see if we have it in our info store
1718:                //			else
1719:                //			{
1720:                //				collection = m_storage.getCollection(id);
1721:                //
1722:                //				// cache it (hit or miss)
1723:                //				m_cache.put(key, collection);
1724:                //			}
1725:                //		}
1726:
1727:                return collection;
1728:
1729:            } // findCollection
1730:
1731:            /**
1732:             * check permissions for editCollection()
1733:             * 
1734:             * @param id
1735:             *        The id of the collection.
1736:             * @return true if the user is allowed to update the collection, false if not.
1737:             */
1738:            public boolean allowUpdateCollection(String id) {
1739:                boolean isAllowed = allowUpdate(id);
1740:
1741:                if (isAllowed) {
1742:                    try {
1743:                        checkExplicitLock(id);
1744:                    } catch (PermissionException e) {
1745:                        isAllowed = false;
1746:                    }
1747:                }
1748:
1749:                return isAllowed;
1750:
1751:            } // allowUpdateCollection
1752:
1753:            /**
1754:             * check permissions for revising collections or resources
1755:             * @param id The id of the collection.
1756:             * @return true if the user is allowed to update the collection, false if not.
1757:             */
1758:            public boolean allowUpdate(String id) {
1759:                String currentUser = SessionManager.getCurrentSessionUserId();
1760:                String owner = "";
1761:
1762:                try {
1763:                    ResourceProperties props = getProperties(id);
1764:                    owner = props.getProperty(ResourceProperties.PROP_CREATOR);
1765:                } catch (Exception e) {
1766:                    // PermissionException can be thrown if not AUTH_RESOURCE_READ
1767:                    return false;
1768:                }
1769:
1770:                // check security to delete any collection
1771:                if (unlockCheck(AUTH_RESOURCE_WRITE_ANY, id))
1772:                    return true;
1773:
1774:                // check security to delete own collection
1775:                else if (unlockCheck(AUTH_RESOURCE_WRITE_OWN, id)
1776:                        && currentUser.equals(owner))
1777:                    return true;
1778:
1779:                // otherwise not authorized
1780:                else
1781:                    return false;
1782:
1783:            } // allowUpdate
1784:
1785:            /**
1786:             * check permissions for removeCollection(). Note: for just this collection, not the members on down.
1787:             * 
1788:             * @param id
1789:             *        The id of the collection.
1790:             * @return true if the user is allowed to removeCollection(id), false if not.
1791:             */
1792:            public boolean allowRemoveCollection(String id) {
1793:                return allowRemove(id);
1794:
1795:            } // allowRemoveCollection
1796:
1797:            /**
1798:             * check permissions for removing collections or resources
1799:             * Note: for just this collection, not the members on down.
1800:             * @param id The id of the collection.
1801:             * @return true if the user is allowed to removeCollection(id), false if not.
1802:             */
1803:            protected boolean allowRemove(String id) {
1804:                String ref = getReference(id);
1805:                String currentUser = SessionManager.getCurrentSessionUserId();
1806:                String owner = "";
1807:
1808:                try {
1809:                    ResourceProperties props = getProperties(id);
1810:                    owner = props.getProperty(ResourceProperties.PROP_CREATOR);
1811:                } catch (Exception e) {
1812:                    // PermissionException can be thrown if not RESOURCE_AUTH_READ
1813:                    return false;
1814:                }
1815:
1816:                // check security to delete any collection
1817:                if (unlockCheck(AUTH_RESOURCE_REMOVE_ANY, id))
1818:                    return true;
1819:
1820:                // check security to delete own collection
1821:                else if (unlockCheck(AUTH_RESOURCE_REMOVE_OWN, id)
1822:                        && currentUser.equals(owner))
1823:                    return true;
1824:
1825:                // otherwise not authorized
1826:                else
1827:                    return false;
1828:
1829:            } // allowRemove
1830:
1831:            /**
1832:             * Remove just a collection. It must be empty.
1833:             * 
1834:             * @param collection
1835:             *        The collection to remove.
1836:             * @exception TypeException
1837:             *            if the resource exists but is not a collection.
1838:             * @exception PermissionException
1839:             *            if the user does not have permissions to remove this collection, read through any containing
1840:             * @exception InconsistentException
1841:             *            if the collection has members, so that the removal would leave things in an inconsistent state.
1842:             * @exception ServerOverloadException
1843:             *            if the server is configured to write the resource body to the filesystem and an attempt to access the resource body of any collection member fails.
1844:             */
1845:            public void removeCollection(ContentCollectionEdit edit)
1846:                    throws TypeException, PermissionException,
1847:                    InconsistentException, ServerOverloadException {
1848:                // check for closed edit
1849:                if (!edit.isActiveEdit()) {
1850:                    Exception e = new Exception();
1851:                    M_log.warn(
1852:                            "removeCollection(): closed ContentCollectionEdit",
1853:                            e);
1854:                    return;
1855:                }
1856:
1857:                // check security 
1858:                if (!allowRemoveCollection(edit.getId()))
1859:                    throw new PermissionException(SessionManager
1860:                            .getCurrentSessionUserId(),
1861:                            AUTH_RESOURCE_REMOVE_ANY, edit.getReference());
1862:
1863:                // check for members
1864:                List members = edit.getMemberResources();
1865:                if (!members.isEmpty())
1866:                    throw new InconsistentException(edit.getId());
1867:
1868:                // complete the edit
1869:                m_storage.removeCollection(edit);
1870:
1871:                // close the edit object
1872:                ((BaseCollectionEdit) edit).closeEdit();
1873:
1874:                ((BaseCollectionEdit) edit).setRemoved();
1875:
1876:                // remove the old version from thread-local cache.
1877:                String ref = edit.getReference();
1878:                ThreadLocalManager.set("findCollection@" + ref, null);
1879:
1880:                // remove any realm defined for this resource
1881:                try {
1882:                    m_authzGroupService.removeAuthzGroup(m_authzGroupService
1883:                            .getAuthzGroup(edit.getReference()));
1884:                } catch (AuthzPermissionException e) {
1885:                    M_log.warn("removeCollection: removing realm for : "
1886:                            + edit.getReference() + " : " + e);
1887:                } catch (GroupNotDefinedException ignore) {
1888:                    M_log.warn("removeCollection: removing realm for : "
1889:                            + edit.getReference() + " : " + ignore);
1890:                }
1891:
1892:                // track it (no notification)
1893:                EventTrackingService.post(EventTrackingService.newEvent(
1894:                        EVENT_RESOURCE_REMOVE, edit.getReference(), true,
1895:                        NotificationService.NOTI_NONE));
1896:
1897:            } // removeCollection
1898:
1899:            /**
1900:             * Remove a collection and all members of the collection, internal or deeper.
1901:             * 
1902:             * @param id
1903:             *        The id of the collection.
1904:             * @exception IdUnusedException
1905:             *            if the id does not exist.
1906:             * @exception TypeException
1907:             *            if the resource exists but is not a collection.
1908:             * @exception PermissionException
1909:             *            if the user does not have permissions to remove this collection, read through any containing
1910:             * @exception InUseException
1911:             *            if the collection or a contained member is locked by someone else. collections, or remove any members of the collection.
1912:             * @exception ServerOverloadException
1913:             *            if the server is configured to write the resource body to the filesystem and an attempt to access the resource body of any collection member fails.
1914:             */
1915:            public void removeCollection(String id) throws IdUnusedException,
1916:                    TypeException, PermissionException, InUseException,
1917:                    ServerOverloadException {
1918:                // check security 
1919:                if (!allowRemoveCollection(id))
1920:                    throw new PermissionException(SessionManager
1921:                            .getCurrentSessionUserId(),
1922:                            AUTH_RESOURCE_REMOVE_ANY, getReference(id));
1923:
1924:                // find the collection
1925:                ContentCollection this Collection = findCollection(id);
1926:                if (this Collection == null)
1927:                    throw new IdUnusedException(id);
1928:
1929:                // check security: can we remove members (if any)
1930:                // Note: this will also be done in clear(), except some might get deleted before one is not allowed.
1931:                // unlockContained(AUTH_RESOURCE_REMOVE, thisCollection);
1932:
1933:                // get an edit
1934:                ContentCollectionEdit edit = editCollection(id);
1935:
1936:                // clear of all members (recursive)
1937:                // Note: may fail if something's in use or not permitted. May result in a partial clear.
1938:                try {
1939:                    ((BaseCollectionEdit) edit).clear();
1940:
1941:                    // remove
1942:                    removeCollection(edit);
1943:                } catch (InconsistentException e) {
1944:                    M_log.warn("removeCollection():", e);
1945:                } finally {
1946:                    // if we don't get the remove done, we need to cancel here
1947:                    if (((BaseCollectionEdit) edit).isActiveEdit()) {
1948:                        cancelCollection(edit);
1949:                    }
1950:                }
1951:
1952:            } // removeCollection
1953:
1954:            /**
1955:             * Commit the changes made, and release the lock. The Object is disabled, and not to be used after this call.
1956:             * 
1957:             * @param edit
1958:             *        The ContentCollectionEdit object to commit.
1959:             * @throws PermissionException 
1960:             */
1961:            public void commitCollection(ContentCollectionEdit edit) {
1962:                // check for closed edit
1963:                if (!edit.isActiveEdit()) {
1964:                    Exception e = new Exception();
1965:                    M_log.warn(
1966:                            "commitCollection(): closed ContentCollectionEdit",
1967:                            e);
1968:                    return;
1969:                }
1970:
1971:                if (AccessMode.GROUPED == edit.getAccess()) {
1972:                    verifyGroups(edit, edit.getGroups());
1973:                }
1974:
1975:                if (this .m_prioritySortEnabled) {
1976:                    ResourcePropertiesEdit props = edit.getPropertiesEdit();
1977:                    String sortBy = props
1978:                            .getProperty(ResourceProperties.PROP_CONTENT_PRIORITY);
1979:                    if (sortBy == null) {
1980:                        // add a default value that sorts new items after existing items, with new folders before new resources
1981:                        ContentCollection container = edit
1982:                                .getContainingCollection();
1983:                        int count = container.getMemberCount();
1984:                        props.addProperty(
1985:                                ResourceProperties.PROP_CONTENT_PRIORITY,
1986:                                Integer.toString(count + 1));
1987:                    }
1988:                }
1989:
1990:                // update the properties for update
1991:                addLiveUpdateCollectionProperties(edit);
1992:
1993:                // complete the edit
1994:                m_storage.commitCollection(edit);
1995:
1996:                // close the edit object
1997:                ((BaseCollectionEdit) edit).closeEdit();
1998:
1999:                // the collection has changed so we must remove the old version from thread-local cache
2000:                String ref = edit.getReference();
2001:                ThreadLocalManager.set("findCollection@" + ref, null);
2002:
2003:                // track it (no notification)
2004:                EventTrackingService.post(EventTrackingService.newEvent(
2005:                        ((BaseCollectionEdit) edit).getEvent(), edit
2006:                                .getReference(), true,
2007:                        NotificationService.NOTI_NONE));
2008:
2009:            } // commitCollection
2010:
2011:            /**
2012:             * Recursively traverse the heirarchy of ContentEntity objects contained within a collection and remove access groups if they 
2013:             * are not included in the set defined for the initial collection.  The branching stops whenever we verify a ContentCollection 
2014:             * with "grouped" access or a ContentResource. 
2015:             * @param collection 
2016:             * @param groups
2017:             */
2018:            protected void verifyGroups(ContentCollection collection,
2019:                    Collection groups) {
2020:                Collection members = collection.getMemberResources();
2021:                if (members == null || members.isEmpty()) {
2022:                    return;
2023:                }
2024:                Iterator memberIt = members.iterator();
2025:                while (memberIt.hasNext()) {
2026:                    ContentEntity member = (ContentEntity) memberIt.next();
2027:                    if (AccessMode.GROUPED == member.getAccess()) {
2028:                        adjustGroups(member, groups);
2029:                    }
2030:
2031:                    if (member instanceof  ContentCollection) {
2032:                        // recursive call
2033:                        verifyGroups((ContentCollectionEdit) member, groups);
2034:                    }
2035:                }
2036:
2037:            }
2038:
2039:            protected void adjustGroups(ContentEntity member, Collection groups) {
2040:                // check groups and then return
2041:                Collection subgroups = member.getGroups();
2042:                if (groups.containsAll(subgroups)) {
2043:                    // this entity's groups are OK, so do nothing
2044:                } else {
2045:                    Collection newgroups = new Vector();
2046:                    Iterator groupIt = subgroups.iterator();
2047:                    while (groupIt.hasNext()) {
2048:                        String groupRef = (String) groupIt.next();
2049:                        if (groups.contains(groupRef)) {
2050:                            newgroups.add(groupRef);
2051:                        }
2052:                    }
2053:                    if (member instanceof  ContentResource) {
2054:                        ContentResourceEdit edit = m_storage
2055:                                .editResource(member.getId());
2056:                        try {
2057:                            if (newgroups.isEmpty()) {
2058:                                edit.clearGroupAccess();
2059:                            } else {
2060:                                edit.setGroupAccess(newgroups);
2061:                            }
2062:                            // addLiveUpdateResourceProperties(edit);
2063:                            m_storage.commitResource(edit);
2064:                            // close the edit object
2065:                            ((BaseResourceEdit) edit).closeEdit();
2066:                        } catch (InconsistentException e) {
2067:                            // If change of groups is consistent in superfolder, this should not occur here
2068:                            m_storage.cancelResource(edit);
2069:                            M_log.warn("verifyGroups(): ", e);
2070:                        } catch (PermissionException e) {
2071:                            // If user has permission to change groups in superfolder, this should not occur here
2072:                            m_storage.cancelResource(edit);
2073:                            M_log.warn("verifyGroups(): ", e);
2074:                        } catch (ServerOverloadException e) {
2075:                            M_log.warn("verifyGroups(): ", e);
2076:                        }
2077:                    } else {
2078:                        ContentCollectionEdit edit = m_storage
2079:                                .editCollection(member.getId());
2080:                        try {
2081:                            if (newgroups.isEmpty()) {
2082:                                edit.clearGroupAccess();
2083:                            } else {
2084:                                edit.setGroupAccess(newgroups);
2085:                            }
2086:                            // addLiveUpdateCollectionProperties(edit);
2087:                            m_storage.commitCollection(edit);
2088:                        } catch (InconsistentException e) {
2089:                            // If change of groups is consistent in superfolder, this should not occur here
2090:                            m_storage.cancelCollection(edit);
2091:                            M_log.warn("verifyGroups(): ", e);
2092:                        } catch (PermissionException e) {
2093:                            // If user has permission to change groups in superfolder, this should not occur here
2094:                            m_storage.cancelCollection(edit);
2095:                            M_log.warn("verifyGroups(): ", e);
2096:                        }
2097:                    }
2098:                }
2099:
2100:            }
2101:
2102:            /**
2103:             * Cancel the changes made object, and release the lock. The Object is disabled, and not to be used after this call.
2104:             * 
2105:             * @param edit
2106:             *        The ContentCollectionEdit object to commit.
2107:             */
2108:            public void cancelCollection(ContentCollectionEdit edit) {
2109:                // check for closed edit
2110:                if (!edit.isActiveEdit()) {
2111:                    Exception e = new Exception();
2112:                    M_log.warn(
2113:                            "cancelCollection(): closed ContentCollectionEdit",
2114:                            e);
2115:                    return;
2116:                }
2117:
2118:                // release the edit lock
2119:                m_storage.cancelCollection(edit);
2120:
2121:                // if the edit is newly created during an add collection process, remove it from the storage
2122:                if (((BaseCollectionEdit) edit).getEvent().equals(
2123:                        EVENT_RESOURCE_ADD)) {
2124:                    removeRecursive(edit);
2125:                    m_storage.removeCollection(edit);
2126:                }
2127:
2128:                // close the edit object
2129:                ((BaseCollectionEdit) edit).closeEdit();
2130:
2131:            } // cancelCollection
2132:
2133:            /**
2134:             * used to remove any members of a collection whoes add was canceled.
2135:             * 
2136:             * @param parent
2137:             */
2138:            protected void removeRecursive(ContentCollection parent) {
2139:                List members = parent.getMemberResources();
2140:
2141:                for (Iterator i = members.iterator(); i.hasNext();) {
2142:                    Object resource = i.next();
2143:                    try {
2144:                        if (resource instanceof  ContentResource) {
2145:                            removeResource(((ContentResource) resource).getId());
2146:                        } else if (resource instanceof  ContentCollection) {
2147:                            ContentCollection collection = (ContentCollection) resource;
2148:                            removeRecursive(collection);
2149:                            removeCollection(collection.getId());
2150:                        }
2151:                    } catch (IdUnusedException e) {
2152:                        M_log.warn(
2153:                                "failed to removed canceled collection child",
2154:                                e);
2155:                    } catch (TypeException e) {
2156:                        M_log.warn(
2157:                                "failed to removed canceled collection child",
2158:                                e);
2159:                    } catch (PermissionException e) {
2160:                        M_log.warn(
2161:                                "failed to removed canceled collection child",
2162:                                e);
2163:                    } catch (InUseException e) {
2164:                        M_log.warn(
2165:                                "failed to removed canceled collection child",
2166:                                e);
2167:                    } catch (ServerOverloadException e) {
2168:                        M_log.warn(
2169:                                "failed to removed canceled collection child",
2170:                                e);
2171:                    }
2172:                }
2173:            }
2174:
2175:            /**********************************************************************************************************************************************************************************************************************************************************
2176:             * Resources
2177:             *********************************************************************************************************************************************************************************************************************************************************/
2178:
2179:            /**
2180:             * check permissions for addResource().
2181:             * 
2182:             * @param id
2183:             *        The id of the new resource.
2184:             * @return true if the user is allowed to addResource(id), false if not.
2185:             */
2186:            public boolean allowAddResource(String id) {
2187:                // resource must also NOT end with a separator characters (we fix it)
2188:                if (id.endsWith(Entity.SEPARATOR)) {
2189:                    id = id.substring(0, id.length() - 1);
2190:                }
2191:
2192:                // check security
2193:                boolean isAllowed = unlockCheck(AUTH_RESOURCE_ADD, id);
2194:
2195:                if (isAllowed) {
2196:                    // check for explicit locks
2197:                    try {
2198:                        checkExplicitLock(id);
2199:                    } catch (PermissionException e) {
2200:                        isAllowed = false;
2201:                    }
2202:                }
2203:
2204:                return isAllowed;
2205:
2206:            } // allowAddResource
2207:
2208:            /**
2209:             * Create a new resource with the given resource id and attributes, including group awareness.
2210:             * 
2211:             * @param id
2212:             *        The id of the new resource.
2213:             * @param type
2214:             *        The mime type string of the resource.
2215:             * @param content
2216:             *        An array containing the bytes of the resource's content.
2217:             * @param properties
2218:             *        A java Properties object with the properties to add to the new resource.
2219:             * @param groups
2220:             *        A collection (String) of references to Group objects representing the site subgroups that should have access to this entity.
2221:             *        May be empty to indicate access is not limited to a group or groups.
2222:             * @param priority
2223:             *        The notification priority for this commit.
2224:             * @exception PermissionException
2225:             *            if the user does not have permission to add a resource to the containing collection.
2226:             * @exception IdUsedException
2227:             *            if the resource id is already in use.
2228:             * @exception IdInvalidException
2229:             *            if the resource id is invalid.
2230:             * @exception InconsistentException
2231:             *            if the containing collection does not exist.
2232:             * @exception OverQuotaException
2233:             *            if this would result in being over quota.
2234:             * @exception ServerOverloadException
2235:             *            if the server is configured to write the resource body to the filesystem and the save fails.
2236:             * @return a new ContentResource object.
2237:             */
2238:            public ContentResource addResource(String id, String type,
2239:                    byte[] content, ResourceProperties properties,
2240:                    Collection groups, int priority)
2241:                    throws PermissionException, IdUsedException,
2242:                    IdInvalidException, InconsistentException,
2243:                    OverQuotaException, ServerOverloadException {
2244:                id = (String) ((Hashtable) fixTypeAndId(id, type)).get("id");
2245:                ContentResourceEdit edit = addResource(id);
2246:                edit.setContentType(type);
2247:                edit.setContent(content);
2248:                addProperties(edit.getPropertiesEdit(), properties);
2249:                // commit the change
2250:                if (groups == null || groups.isEmpty()) {
2251:                    // access is inherited (the default)
2252:                } else {
2253:                    edit.setGroupAccess(groups);
2254:                    // TODO: Need to deal with failure here
2255:                }
2256:
2257:                commitResource(edit, priority);
2258:
2259:                return edit;
2260:
2261:            } // addResource
2262:
2263:            /**
2264:             * Create a new resource with the given resource id and attributes but no group awareness.
2265:             * 
2266:             * @param id
2267:             *        The id of the new resource.
2268:             * @param type
2269:             *        The mime type string of the resource.
2270:             * @param content
2271:             *        An array containing the bytes of the resource's content.
2272:             * @param properties
2273:             *        A java Properties object with the properties to add to the new resource.
2274:             * @param priority
2275:             *        The notification priority for this commit.
2276:             * @exception PermissionException
2277:             *            if the user does not have permission to add a resource to the containing collection.
2278:             * @exception IdUsedException
2279:             *            if the resource id is already in use.
2280:             * @exception IdInvalidException
2281:             *            if the resource id is invalid.
2282:             * @exception InconsistentException
2283:             *            if the containing collection does not exist.
2284:             * @exception OverQuotaException
2285:             *            if this would result in being over quota.
2286:             * @exception ServerOverloadException
2287:             *            if the server is configured to write the resource body to the filesystem and the save fails.
2288:             * @return a new ContentResource object.
2289:             */
2290:            public ContentResource addResource(String id, String type,
2291:                    byte[] content, ResourceProperties properties, int priority)
2292:                    throws PermissionException, IdUsedException,
2293:                    IdInvalidException, InconsistentException,
2294:                    OverQuotaException, ServerOverloadException {
2295:                Collection no_groups = new Vector();
2296:                return addResource(id, type, content, properties, no_groups,
2297:                        priority);
2298:            }
2299:
2300:            /**
2301:             * Create a new resource with the given resource name used as a resource id within the specified collection or (if that id is already in use) with a resource id based on a variation on the name to achieve a unique id, provided a unique id can be found
2302:             * before a limit is reached on the number of attempts to achieve uniqueness.  Used to create a group-aware resource.
2303:             * 
2304:             * @param name
2305:             *        The name of the new resource (such as a filename).
2306:             * @param collectionId
2307:             *        The id of the collection to which the resource should be added.
2308:             * @param limit
2309:             *        The maximum number of attempts at finding a unique id based on the given name.
2310:             * @param type
2311:             *        The mime type string of the resource.
2312:             * @param content
2313:             *        An array containing the bytes of the resource's content.
2314:             * @param properties
2315:             *        A ResourceProperties object with the properties to add to the new resource.
2316:             * @param groups
2317:             *        A collection (String) of references to Group objects representing the site subgroups that should have access to this entity.
2318:             *        May be empty to indicate access is not limited to a group or groups.
2319:             * @param priority
2320:             *        The notification priority for this commit.
2321:             * @exception PermissionException
2322:             *            if the user does not have permission to add a resource to the containing collection.
2323:             * @exception IdUniquenessException
2324:             *            if a unique resource id cannot be found before the limit on the number of attempts is reached.
2325:             * @exception IdLengthException
2326:             *            if the resource id exceeds the maximum number of characters for a valid resource id.
2327:             * @exception IdInvalidException
2328:             *            if the resource id is invalid.
2329:             * @exception InconsistentException
2330:             *            if the containing collection does not exist.
2331:             * @exception OverQuotaException
2332:             *            if this would result in being over quota.
2333:             * @exception ServerOverloadException
2334:             *            if the server is configured to write the resource body to the filesystem and the save fails.
2335:             * @return a new ContentResource object.
2336:             */
2337:            public ContentResource addResource(String name,
2338:                    String collectionId, int limit, String type,
2339:                    byte[] content, ResourceProperties properties,
2340:                    Collection groups, boolean hidden, Time releaseDate,
2341:                    Time retractDate, int priority) throws PermissionException,
2342:                    IdUniquenessException, IdLengthException,
2343:                    IdInvalidException, InconsistentException,
2344:                    OverQuotaException, ServerOverloadException {
2345:                try {
2346:                    collectionId = collectionId.trim();
2347:                    name = Validator.escapeResourceName(name.trim());
2348:                    checkCollection(collectionId);
2349:                } catch (IdUnusedException e) {
2350:                    throw new InconsistentException(collectionId);
2351:                } catch (TypeException e) {
2352:                    throw new InconsistentException(collectionId);
2353:                }
2354:
2355:                String id = collectionId + name;
2356:                id = (String) ((Hashtable) fixTypeAndId(id, type)).get("id");
2357:                if (id.length() > MAXIMUM_RESOURCE_ID_LENGTH) {
2358:                    throw new IdLengthException(id);
2359:                }
2360:
2361:                ContentResourceEdit edit = null;
2362:
2363:                try {
2364:                    edit = addResource(id);
2365:                    edit.setContentType(type);
2366:                    edit.setContent(content);
2367:                    addProperties(edit.getPropertiesEdit(), properties);
2368:                    if (groups == null || groups.isEmpty()) {
2369:                        // access is inherited (the default)
2370:                    } else {
2371:                        edit.setGroupAccess(groups);
2372:                        // TODO: Need to deal with failure here
2373:                    }
2374:                    edit.setAvailability(hidden, releaseDate, retractDate);
2375:
2376:                    // commit the change
2377:                    commitResource(edit, priority);
2378:                } catch (IdUsedException e) {
2379:                    try {
2380:                        checkResource(id);
2381:                    } catch (IdUnusedException inner_e) {
2382:                        // TODO: What does this condition actually represent? What exception should be thrown?
2383:                        throw new IdUniquenessException(id);
2384:                    } catch (TypeException inner_e) {
2385:                        throw new InconsistentException(id);
2386:                    }
2387:
2388:                    SortedSet siblings = new TreeSet();
2389:                    try {
2390:                        ContentCollection collection = findCollection(collectionId);
2391:                        siblings.addAll(collection.getMembers());
2392:                    } catch (TypeException inner_e) {
2393:                        throw new InconsistentException(collectionId);
2394:                    }
2395:
2396:                    int index = name.lastIndexOf(".");
2397:                    String base = name;
2398:                    String ext = "";
2399:                    if (index > 0 && !"Url".equalsIgnoreCase(type)) {
2400:                        base = name.substring(0, index);
2401:                        ext = name.substring(index);
2402:                    }
2403:                    boolean trying = true;
2404:                    int attempts = 1;
2405:                    while (trying) // see end of loop for condition that enforces attempts <= limit)
2406:                    {
2407:                        String new_id = collectionId + base + "-" + attempts
2408:                                + ext;
2409:                        if (new_id.length() > MAXIMUM_RESOURCE_ID_LENGTH) {
2410:                            throw new IdLengthException(new_id);
2411:                        }
2412:                        if (!siblings.contains(new_id)) {
2413:                            try {
2414:                                edit = addResource(new_id);
2415:                                edit.setContentType(type);
2416:                                edit.setContent(content);
2417:                                if (groups == null || groups.isEmpty()) {
2418:                                    // access is inherited (the default)
2419:                                } else {
2420:                                    edit.setGroupAccess(groups);
2421:                                    // TODO: Need to deal with failure here
2422:                                }
2423:
2424:                                addProperties(edit.getPropertiesEdit(),
2425:                                        properties);
2426:                                // commit the change
2427:                                commitResource(edit, priority);
2428:
2429:                                trying = false;
2430:                            } catch (IdUsedException ignore) {
2431:                                // try again
2432:                            }
2433:                        }
2434:                        attempts++;
2435:                        if (attempts > limit) {
2436:                            throw new IdUniquenessException(new_id);
2437:                        }
2438:                    }
2439:                }
2440:                return edit;
2441:
2442:            }
2443:
2444:            /* (non-Javadoc)
2445:             * @see org.sakaiproject.content.api.ContentHostingService#addResource(java.lang.String, java.lang.String, java.lang.String, int)
2446:             */
2447:            public ContentResourceEdit addResource(String collectionId,
2448:                    String basename, String extension, int maximum_tries)
2449:                    throws PermissionException, IdUniquenessException,
2450:                    IdLengthException, IdInvalidException, IdUnusedException,
2451:                    OverQuotaException, ServerOverloadException {
2452:                // check the id's validity (this may throw IdInvalidException)
2453:                // use only the "name" portion, separated at the end
2454:                try {
2455:                    checkCollection(collectionId);
2456:                } catch (TypeException e) {
2457:                    throw new IdUnusedException(collectionId);
2458:                }
2459:
2460:                if (basename == null) {
2461:                    throw new IdInvalidException("");
2462:                }
2463:
2464:                if (extension == null) {
2465:                    extension = "";
2466:                } else {
2467:                    extension = extension.trim();
2468:                    if (extension.equals("") || extension.startsWith(".")) {
2469:                        // do nothing
2470:                    } else {
2471:                        extension = "." + extension;
2472:                    }
2473:                }
2474:
2475:                basename = Validator.escapeResourceName(basename.trim());
2476:                extension = Validator.escapeResourceName(extension);
2477:
2478:                String name = basename + extension;
2479:                String id = collectionId + name;
2480:
2481:                BaseResourceEdit edit = null;
2482:
2483:                int attempts = 0;
2484:                boolean done = false;
2485:                while (!done && attempts < maximum_tries) {
2486:                    try {
2487:                        edit = (BaseResourceEdit) addResource(id);
2488:                        done = true;
2489:
2490:                        // add live properties
2491:                        addLiveResourceProperties(edit);
2492:
2493:                        ResourceProperties props = edit.getPropertiesEdit();
2494:                        props.addProperty(ResourceProperties.PROP_DISPLAY_NAME,
2495:                                name);
2496:
2497:                        // track event
2498:                        edit.setEvent(EVENT_RESOURCE_ADD);
2499:                    } catch (InconsistentException inner_e) {
2500:                        throw new IdInvalidException(id);
2501:                    } catch (IdUsedException e) {
2502:                        SortedSet siblings = new TreeSet();
2503:                        try {
2504:                            ContentCollection collection = findCollection(collectionId);
2505:                            siblings.addAll(collection.getMembers());
2506:                        } catch (TypeException inner_e) {
2507:                            throw new IdUnusedException(collectionId);
2508:                        }
2509:
2510:                        boolean trying = true;
2511:
2512:                        // see end of loop for condition that enforces attempts <= limit)
2513:                        do {
2514:                            attempts++;
2515:                            name = basename + "-" + attempts + extension;
2516:                            id = collectionId + name;
2517:
2518:                            if (attempts > maximum_tries) {
2519:                                throw new IdUniquenessException(id);
2520:                            }
2521:
2522:                            if (id.length() > MAXIMUM_RESOURCE_ID_LENGTH) {
2523:                                throw new IdLengthException(id);
2524:                            }
2525:                        } while (siblings.contains(id));
2526:                    }
2527:
2528:                }
2529:                //		if (edit == null)
2530:                //		{
2531:                //			throw new IdUniquenessException(id);
2532:                //		}
2533:
2534:                return edit;
2535:
2536:            }
2537:
2538:            /**
2539:             * Create a new resource with the given resource name used as a resource id within the specified collection or (if that id is already in use) with a resource id based on a variation on the name to achieve a unique id, provided a unique id can be found
2540:             * before a limit is reached on the number of attempts to achieve uniqueness. Used to create a resource that is not group aware.
2541:             * 
2542:             * @param name
2543:             *        The name of the new resource (such as a filename).
2544:             * @param collectionId
2545:             *        The id of the collection to which the resource should be added.
2546:             * @param limit
2547:             *        The maximum number of attempts at finding a unique id based on the given name.
2548:             * @param type
2549:             *        The mime type string of the resource.
2550:             * @param content
2551:             *        An array containing the bytes of the resource's content.
2552:             * @param properties
2553:             *        A ResourceProperties object with the properties to add to the new resource.
2554:             * @param priority
2555:             *        The notification priority for this commit.
2556:             * @exception PermissionException
2557:             *            if the user does not have permission to add a resource to the containing collection.
2558:             * @exception IdUniquenessException
2559:             *            if a unique resource id cannot be found before the limit on the number of attempts is reached.
2560:             * @exception IdLengthException
2561:             *            if the resource id exceeds the maximum number of characters for a valid resource id.
2562:             * @exception IdInvalidException
2563:             *            if the resource id is invalid.
2564:             * @exception InconsistentException
2565:             *            if the containing collection does not exist.
2566:             * @exception OverQuotaException
2567:             *            if this would result in being over quota.
2568:             * @exception ServerOverloadException
2569:             *            if the server is configured to write the resource body to the filesystem and the save fails.
2570:             * @return a new ContentResource object.
2571:             */
2572:            public ContentResource addResource(String name,
2573:                    String collectionId, int limit, String type,
2574:                    byte[] content, ResourceProperties properties, int priority)
2575:                    throws PermissionException, IdUniquenessException,
2576:                    IdLengthException, IdInvalidException,
2577:                    InconsistentException, OverQuotaException,
2578:                    ServerOverloadException {
2579:                Collection no_groups = new Vector();
2580:                return addResource(name, collectionId, limit, type, content,
2581:                        properties, no_groups, false, null, null, priority);
2582:            }
2583:
2584:            /**
2585:             * Create a new resource with the given resource id, locked for update. Must commitResource() to make official, or cancelResource() when done!
2586:             * 
2587:             * @param id
2588:             *        The id of the new resource.
2589:             * @exception PermissionException
2590:             *            if the user does not have permission to add a resource to the containing collection.
2591:             * @exception IdUsedException
2592:             *            if the resource id is already in use.
2593:             * @exception IdInvalidException
2594:             *            if the resource id is invalid.
2595:             * @exception InconsistentException
2596:             *            if the containing collection does not exist.
2597:             * @return a new ContentResource object.
2598:             */
2599:            public ContentResourceEdit addResource(String id)
2600:                    throws PermissionException, IdUsedException,
2601:                    IdInvalidException, InconsistentException {
2602:                // check the id's validity (this may throw IdInvalidException)
2603:                // use only the "name" portion, separated at the end
2604:                String justName = isolateName(id);
2605:                Validator.checkResourceId(justName);
2606:                // resource must also NOT end with a separator characters (we fix it)
2607:                if (id.endsWith(Entity.SEPARATOR)) {
2608:                    id = id.substring(0, id.length() - 1);
2609:                }
2610:
2611:                // check security
2612:                checkExplicitLock(id);
2613:                unlock(AUTH_RESOURCE_ADD, id);
2614:
2615:                // make sure the containing collection exists
2616:                String container = isolateContainingId(id);
2617:                ContentCollection containingCollection = m_storage
2618:                        .getCollection(container);
2619:                if (containingCollection == null) {
2620:                    // make any missing collections
2621:                    generateCollections(container);
2622:
2623:                    // try again
2624:                    containingCollection = m_storage.getCollection(container);
2625:                    if (containingCollection == null)
2626:                        throw new InconsistentException(id);
2627:                }
2628:
2629:                // reserve the resource in storage - it will fail if the id is in use
2630:                BaseResourceEdit edit = (BaseResourceEdit) m_storage
2631:                        .putResource(id);
2632:                if (edit == null) {
2633:                    throw new IdUsedException(id);
2634:                }
2635:
2636:                // add live properties
2637:                addLiveResourceProperties(edit);
2638:
2639:                // track event
2640:                edit.setEvent(EVENT_RESOURCE_ADD);
2641:
2642:                return edit;
2643:
2644:            } // addResource
2645:
2646:            /**
2647:             * Create a new resource with the given resource name used as a resource id within the specified collection or (if that id is already in use) with a resource id based on a variation on the name to achieve a unique id, provided a unique id can be found
2648:             * before a limit is reached on the number of attempts to achieve uniqueness.  Used to create a group-aware resource.
2649:             * 
2650:             * @param name
2651:             *        The name of the new resource (such as a filename).
2652:             * @param collectionId
2653:             *        The id of the collection to which the resource should be added.
2654:             * @param limit
2655:             *        The maximum number of attempts at finding a unique id based on the given name.
2656:             * @param type
2657:             *        The mime type string of the resource.
2658:             * @param content
2659:             *        An array containing the bytes of the resource's content.
2660:             * @param properties
2661:             *        A ResourceProperties object with the properties to add to the new resource.
2662:             * @param groups
2663:             *        A collection (String) of references to Group objects representing the site subgroups that should have access to this entity.
2664:             *        May be empty to indicate access is not limited to a group or groups.
2665:             * @param priority
2666:             *        The notification priority for this commit.
2667:             * @exception PermissionException
2668:             *            if the user does not have permission to add a resource to the containing collection.
2669:             * @exception IdUniquenessException
2670:             *            if a unique resource id cannot be found before the limit on the number of attempts is reached.
2671:             * @exception IdLengthException
2672:             *            if the resource id exceeds the maximum number of characters for a valid resource id.
2673:             * @exception IdInvalidException
2674:             *            if the resource id is invalid.
2675:             * @exception InconsistentException
2676:             *            if the containing collection does not exist.
2677:             * @exception OverQuotaException
2678:             *            if this would result in being over quota.
2679:             * @exception ServerOverloadException
2680:             *            if the server is configured to write the resource body to the filesystem and the save fails.
2681:             * @return a new ContentResource object.
2682:             */
2683:            public ContentResource addResource(String name,
2684:                    String collectionId, int limit, String type,
2685:                    byte[] content, ResourceProperties properties,
2686:                    Collection groups, int priority)
2687:                    throws PermissionException, IdUniquenessException,
2688:                    IdLengthException, IdInvalidException,
2689:                    InconsistentException, OverQuotaException,
2690:                    ServerOverloadException {
2691:                try {
2692:                    collectionId = collectionId.trim();
2693:                    name = Validator.escapeResourceName(name.trim());
2694:                    checkCollection(collectionId);
2695:                } catch (IdUnusedException e) {
2696:                    throw new InconsistentException(collectionId);
2697:                } catch (TypeException e) {
2698:                    throw new InconsistentException(collectionId);
2699:                }
2700:
2701:                String id = collectionId + name;
2702:                id = (String) ((Hashtable) fixTypeAndId(id, type)).get("id");
2703:                if (id.length() > MAXIMUM_RESOURCE_ID_LENGTH) {
2704:                    throw new IdLengthException(id);
2705:                }
2706:
2707:                ContentResourceEdit edit = null;
2708:
2709:                try {
2710:                    edit = addResource(id);
2711:                    edit.setContentType(type);
2712:                    edit.setContent(content);
2713:                    addProperties(edit.getPropertiesEdit(), properties);
2714:                    if (groups == null || groups.isEmpty()) {
2715:                        // access is inherited (the default)
2716:                    } else {
2717:                        edit.setGroupAccess(groups);
2718:                        // TODO: Need to deal with failure here
2719:                    }
2720:
2721:                    // commit the change
2722:                    commitResource(edit, priority);
2723:                } catch (IdUsedException e) {
2724:                    try {
2725:                        checkResource(id);
2726:                    } catch (IdUnusedException inner_e) {
2727:                        // TODO: What does this condition actually represent? What exception should be thrown?
2728:                        throw new IdUniquenessException(id);
2729:                    } catch (TypeException inner_e) {
2730:                        throw new InconsistentException(id);
2731:                    }
2732:
2733:                    SortedSet siblings = new TreeSet();
2734:                    try {
2735:                        ContentCollection collection = findCollection(collectionId);
2736:                        siblings.addAll(collection.getMembers());
2737:                    } catch (TypeException inner_e) {
2738:                        throw new InconsistentException(collectionId);
2739:                    }
2740:
2741:                    int index = name.lastIndexOf(".");
2742:                    String base = name;
2743:                    String ext = "";
2744:                    if (index > 0 && !"Url".equalsIgnoreCase(type)) {
2745:                        base = name.substring(0, index);
2746:                        ext = name.substring(index);
2747:                    }
2748:                    boolean trying = true;
2749:                    int attempts = 1;
2750:                    while (trying) // see end of loop for condition that enforces attempts <= limit)
2751:                    {
2752:                        String new_id = collectionId + base + "-" + attempts
2753:                                + ext;
2754:                        if (new_id.length() > MAXIMUM_RESOURCE_ID_LENGTH) {
2755:                            throw new IdLengthException(new_id);
2756:                        }
2757:                        if (!siblings.contains(new_id)) {
2758:                            try {
2759:                                edit = addResource(new_id);
2760:                                edit.setContentType(type);
2761:                                edit.setContent(content);
2762:                                if (groups == null || groups.isEmpty()) {
2763:                                    // access is inherited (the default)
2764:                                } else {
2765:                                    edit.setGroupAccess(groups);
2766:                                    // TODO: Need to deal with failure here
2767:                                }
2768:
2769:                                addProperties(edit.getPropertiesEdit(),
2770:                                        properties);
2771:                                // commit the change
2772:                                commitResource(edit, priority);
2773:
2774:                                trying = false;
2775:                            } catch (IdUsedException ignore) {
2776:                                // try again
2777:                            }
2778:                        }
2779:                        attempts++;
2780:                        if (attempts > limit) {
2781:                            throw new IdUniquenessException(new_id);
2782:                        }
2783:                    }
2784:                }
2785:                return edit;
2786:
2787:            }
2788:
2789:            /**
2790:             * check permissions for addAttachmentResource().
2791:             * 
2792:             * @return true if the user is allowed to addAttachmentResource(), false if not.
2793:             */
2794:            public boolean allowAddAttachmentResource() {
2795:                return unlockCheck(AUTH_RESOURCE_ADD, ATTACHMENTS_COLLECTION);
2796:
2797:            } // allowAddAttachmentResource
2798:
2799:            /**
2800:             * Check whether a resource id or collection id references an entity in the attachments collection. This method makes no guarantees that a resource actually exists with this id.
2801:             * 
2802:             * @param id
2803:             *        Assumed to be a valid resource id or collection id.
2804:             * @return true if the id (assuming it is a valid id for an existing resource or collection) references an entity in the hidden attachments area created through one of this class's addAttachmentResource methods.
2805:             */
2806:            public boolean isAttachmentResource(String id) {
2807:                // TODO: Should we check whether this is a valid resource id?
2808:                return id.startsWith(ATTACHMENTS_COLLECTION);
2809:            }
2810:
2811:            /**
2812:             * Create a new resource as an attachment to some other resource in the system. The new resource will be placed into a newly created collecion in the attachment collection, with an auto-generated id, and given the specified resource name within this
2813:             * collection.
2814:             * 
2815:             * @param name
2816:             *        The name of the new resource, i.e. a partial id relative to the collection where it will live.
2817:             * @param type
2818:             *        The mime type string of the resource.
2819:             * @param content
2820:             *        An array containing the bytes of the resource's content.
2821:             * @param properties
2822:             *        A ResourceProperties object with the properties to add to the new resource.
2823:             * @exception IdUsedException
2824:             *            if the resource name is already in use (not likely, as the containing collection is auto-generated!)
2825:             * @exception IdInvalidException
2826:             *            if the resource name is invalid.
2827:             * @exception InconsistentException
2828:             *            if the containing collection (or it's containing collection...) does not exist.
2829:             * @exception PermissionException
2830:             *            if the user does not have permission to add a collection, or add a member to a collection.
2831:             * @exception OverQuotaException
2832:             *            if this would result in being over quota.
2833:             * @exception ServerOverloadException
2834:             *            if the server is configured to write the resource body to the filesystem and the save fails.
2835:             * @return a new ContentResource object.
2836:             */
2837:            public ContentResource addAttachmentResource(String name,
2838:                    String type, byte[] content, ResourceProperties properties)
2839:                    throws IdInvalidException, InconsistentException,
2840:                    IdUsedException, PermissionException, OverQuotaException,
2841:                    ServerOverloadException {
2842:                // make sure the name is valid
2843:                Validator.checkResourceId(name);
2844:
2845:                // resource must also NOT end with a separator characters (we fix it)
2846:                if (name.endsWith(Entity.SEPARATOR)) {
2847:                    name = name.substring(0, name.length() - 1);
2848:                }
2849:
2850:                // form a name based on the attachments collection, a unique folder id, and the given name
2851:                String collection = ATTACHMENTS_COLLECTION
2852:                        + IdManager.createUuid() + Entity.SEPARATOR;
2853:                String id = collection + name;
2854:
2855:                // add this collection
2856:                ContentCollectionEdit edit = addCollection(collection);
2857:                edit.getPropertiesEdit().addProperty(
2858:                        ResourceProperties.PROP_DISPLAY_NAME, name);
2859:                commitCollection(edit);
2860:
2861:                // and add the resource
2862:                return addResource(id, type, content, properties, new Vector(),
2863:                        NotificationService.NOTI_NONE);
2864:
2865:            } // addAttachmentResource
2866:
2867:            /**
2868:             * Create a new resource as an attachment to some other resource in the system. The new resource will be placed into a newly created collecion in the attachment collection, with an auto-generated id, and given the specified resource name within this
2869:             * collection.
2870:             * 
2871:             * @param name
2872:             *        The name of the new resource, i.e. a partial id relative to the collection where it will live.
2873:             * @param site
2874:             *        The string identifier for the site where the attachment is being added.
2875:             * @param tool
2876:             *        The display-name for the tool through which the attachment is being added within the site's attachments collection.
2877:             * @param type
2878:             *        The mime type string of the resource.
2879:             * @param content
2880:             *        An array containing the bytes of the resource's content.
2881:             * @param properties
2882:             *        A ResourceProperties object with the properties to add to the new resource.
2883:             * @exception IdUsedException
2884:             *            if the resource name is already in use (not likely, as the containing collection is auto-generated!)
2885:             * @exception IdInvalidException
2886:             *            if the resource name is invalid.
2887:             * @exception InconsistentException
2888:             *            if the containing collection (or it's containing collection...) does not exist.
2889:             * @exception PermissionException
2890:             *            if the user does not have permission to add a collection, or add a member to a collection.
2891:             * @exception OverQuotaException
2892:             *            if this would result in being over quota.
2893:             * @exception ServerOverloadException
2894:             *            if the server is configured to write the resource body to the filesystem and the save fails.
2895:             * @return a new ContentResource object.
2896:             */
2897:            public ContentResource addAttachmentResource(String name,
2898:                    String site, String tool, String type, byte[] content,
2899:                    ResourceProperties properties) throws IdInvalidException,
2900:                    InconsistentException, IdUsedException,
2901:                    PermissionException, OverQuotaException,
2902:                    ServerOverloadException {
2903:                // ignore site if it is not valid
2904:                if (site == null || site.trim().equals("")) {
2905:                    return addAttachmentResource(name, type, content,
2906:                            properties);
2907:                }
2908:                site = site.trim();
2909:                String siteId = Validator.escapeResourceName(site);
2910:
2911:                // if tool is not valid, use "_anon_"
2912:                if (tool == null || tool.trim().equals("")) {
2913:                    tool = "_anon_";
2914:                }
2915:                tool = tool.trim();
2916:                String toolId = Validator.escapeResourceName(tool);
2917:
2918:                // make sure the name is valid
2919:                Validator.checkResourceId(name);
2920:
2921:                // resource must also NOT end with a separator characters (we fix it)
2922:                if (name.endsWith(Entity.SEPARATOR)) {
2923:                    name = name.substring(0, name.length() - 1);
2924:                }
2925:
2926:                String siteCollection = ATTACHMENTS_COLLECTION + siteId
2927:                        + Entity.SEPARATOR;
2928:                try {
2929:                    checkCollection(siteCollection);
2930:                } catch (Exception e) {
2931:                    // add this collection
2932:                    ContentCollectionEdit siteEdit = addCollection(siteCollection);
2933:                    try {
2934:                        String siteTitle = m_siteService.getSite(site)
2935:                                .getTitle();
2936:                        siteEdit.getPropertiesEdit()
2937:                                .addProperty(
2938:                                        ResourceProperties.PROP_DISPLAY_NAME,
2939:                                        siteTitle);
2940:                    } catch (Exception e1) {
2941:                        siteEdit.getPropertiesEdit().addProperty(
2942:                                ResourceProperties.PROP_DISPLAY_NAME, site);
2943:                    }
2944:                    commitCollection(siteEdit);
2945:                }
2946:
2947:                String toolCollection = siteCollection + toolId
2948:                        + Entity.SEPARATOR;
2949:                try {
2950:                    checkCollection(toolCollection);
2951:                } catch (Exception e) {
2952:                    // add this collection
2953:                    ContentCollectionEdit toolEdit = addCollection(toolCollection);
2954:                    toolEdit.getPropertiesEdit().addProperty(
2955:                            ResourceProperties.PROP_DISPLAY_NAME, tool);
2956:                    commitCollection(toolEdit);
2957:                }
2958:
2959:                // form a name based on the attachments collection, a unique folder id, and the given name
2960:                String collection = toolCollection + IdManager.createUuid()
2961:                        + Entity.SEPARATOR;
2962:                String id = collection + name;
2963:
2964:                // add this collection
2965:                ContentCollectionEdit edit = addCollection(collection);
2966:                edit.getPropertiesEdit().addProperty(
2967:                        ResourceProperties.PROP_DISPLAY_NAME, name);
2968:                commitCollection(edit);
2969:
2970:                // and add the resource
2971:                return addResource(id, type, content, properties, new Vector(),
2972:                        NotificationService.NOTI_NONE);
2973:
2974:            } // addAttachmentResource
2975:
2976:            /**
2977:             * Create a new resource as an attachment to some other resource in the system, locked for update. Must commitResource() to make official, or cancelResource() when done! The new resource will be placed into a newly created collecion in the attachment
2978:             * collection, with an auto-generated id, and given the specified resource name within this collection.
2979:             * 
2980:             * @param name
2981:             *        The name of the new resource, i.e. a partial id relative to the collection where it will live.
2982:             * @exception IdUsedException
2983:             *            if the resource name is already in use (not likely, as the containing collection is auto-generated!)
2984:             * @exception IdInvalidException
2985:             *            if the resource name is invalid.
2986:             * @exception InconsistentException
2987:             *            if the containing collection (or it's containing collection...) does not exist.
2988:             * @exception PermissionException
2989:             *            if the user does not have permission to add a collection, or add a member to a collection.
2990:             * @return a new ContentResource object.
2991:             */
2992:            public ContentResourceEdit addAttachmentResource(String name)
2993:                    throws IdInvalidException, InconsistentException,
2994:                    IdUsedException, PermissionException {
2995:                // make sure the name is valid
2996:                Validator.checkResourceId(name);
2997:
2998:                // resource must also NOT end with a separator characters (we fix it)
2999:                if (name.endsWith(Entity.SEPARATOR)) {
3000:                    name = name.substring(0, name.length() - 1);
3001:                }
3002:
3003:                // form a name based on the attachments collection, a unique folder id, and the given name
3004:                String collection = ATTACHMENTS_COLLECTION
3005:                        + IdManager.createUuid() + Entity.SEPARATOR;
3006:                String id = collection + name;
3007:
3008:                // add this collection
3009:                ContentCollectionEdit edit = addCollection(collection);
3010:                edit.getPropertiesEdit().addProperty(
3011:                        ResourceProperties.PROP_DISPLAY_NAME, name);
3012:                commitCollection(edit);
3013:
3014:                return addResource(id);
3015:
3016:            } // addAttachmentResource
3017:
3018:            /**
3019:             * check permissions for updateResource().
3020:             * 
3021:             * @param id
3022:             *        The id of the new resource.
3023:             * @return true if the user is allowed to updateResource(id), false if not.
3024:             */
3025:            public boolean allowUpdateResource(String id) {
3026:                return allowUpdate(id);
3027:
3028:            } // allowUpdateResource
3029:
3030:            /**
3031:             * Update the body and or content type of an existing resource with the given resource id.
3032:             * 
3033:             * @param id
3034:             *        The id of the resource.
3035:             * @param type
3036:             *        The mime type string of the resource (if null, no change).
3037:             * @param content
3038:             *        An array containing the bytes of the resource's content (if null, no change).
3039:             * @exception PermissionException
3040:             *            if the user does not have permission to add a resource to the containing collection or write the resource.
3041:             * @exception IdUnusedException
3042:             *            if the resource id is not defined.
3043:             * @exception TypeException
3044:             *            if the resource is a collection.
3045:             * @exception InUseException
3046:             *            if the resource is locked by someone else.
3047:             * @exception OverQuotaException
3048:             *            if this would result in being over quota.
3049:             * @exception ServerOverloadException
3050:             *            if the server is configured to write the resource body to the filesystem and the save fails.
3051:             * @return a new ContentResource object.
3052:             */
3053:            public ContentResource updateResource(String id, String type,
3054:                    byte[] content) throws PermissionException,
3055:                    IdUnusedException, TypeException, InUseException,
3056:                    OverQuotaException, ServerOverloadException {
3057:                // find a resource that is this resource
3058:                ContentResourceEdit edit = editResource(id);
3059:
3060:                edit.setContentType(type);
3061:                edit.setContent(content);
3062:
3063:                // commit the change
3064:                commitResource(edit, NotificationService.NOTI_NONE);
3065:
3066:                return edit;
3067:
3068:            } // updateResource
3069:
3070:            /**
3071:             * Access the resource with this resource id, locked for update. For non-collection resources only. Must commitEdit() to make official, or cancelEdit() when done! The resource content and properties are accessible from the returned Resource object.
3072:             * 
3073:             * @param id
3074:             *        The id of the resource.
3075:             * @exception PermissionException
3076:             *            if the user does not have permissions to read the resource or read through any containing collection.
3077:             * @exception IdUnusedException
3078:             *            if the resource id is not found.
3079:             * @exception TypeException
3080:             *            if the resource is a collection.
3081:             * @exception InUseException
3082:             *            if the resource is locked by someone else.
3083:             * @return the ContentResource object found.
3084:             */
3085:            public ContentResourceEdit editResource(String id)
3086:                    throws PermissionException, IdUnusedException,
3087:                    TypeException, InUseException {
3088:                // check security (throws if not permitted)
3089:                checkExplicitLock(id);
3090:
3091:                // check security 
3092:                if (!allowUpdateResource(id))
3093:                    throw new PermissionException(SessionManager
3094:                            .getCurrentSessionUserId(),
3095:                            AUTH_RESOURCE_WRITE_ANY, getReference(id));
3096:
3097:                // check for existance
3098:                if (!m_storage.checkResource(id)) {
3099:                    throw new IdUnusedException(id);
3100:                }
3101:
3102:                // ignore the cache - get the collection with a lock from the info store
3103:                BaseResourceEdit resource = (BaseResourceEdit) m_storage
3104:                        .editResource(id);
3105:                if (resource == null)
3106:                    throw new InUseException(id);
3107:
3108:                resource.setEvent(EVENT_RESOURCE_WRITE);
3109:
3110:                return resource;
3111:
3112:            } // editResource
3113:
3114:            /**
3115:             * Access the resource with this resource id, locked for update. For non-collection resources only. Must commitEdit() to make official, or cancelEdit() when done! The resource content and properties are accessible from the returned Resource object.
3116:             * 
3117:             * @param id
3118:             *        The id of the resource.
3119:             * @exception PermissionException
3120:             *            if the user does not have permissions to read the resource or read through any containing collection.
3121:             * @exception IdUnusedException
3122:             *            if the resource id is not found.
3123:             * @exception TypeException
3124:             *            if the resource is a collection.
3125:             * @exception InUseException
3126:             *            if the resource is locked by someone else.
3127:             * @return the ContentResource object found.
3128:             */
3129:            protected ContentResourceEdit editResourceForDelete(String id)
3130:                    throws PermissionException, IdUnusedException,
3131:                    TypeException, InUseException {
3132:                // check security (throws if not permitted)
3133:                checkExplicitLock(id);
3134:
3135:                // check security 
3136:                if (!allowRemoveResource(id))
3137:                    throw new PermissionException(SessionManager
3138:                            .getCurrentSessionUserId(),
3139:                            AUTH_RESOURCE_REMOVE_ANY, getReference(id));
3140:
3141:                // check for existance
3142:                if (!m_storage.checkResource(id)) {
3143:                    throw new IdUnusedException(id);
3144:                }
3145:
3146:                // ignore the cache - get the collection with a lock from the info store
3147:                BaseResourceEdit resource = (BaseResourceEdit) m_storage
3148:                        .editResource(id);
3149:                if (resource == null)
3150:                    throw new InUseException(id);
3151:
3152:                resource.setEvent(EVENT_RESOURCE_REMOVE);
3153:
3154:                return resource;
3155:
3156:            } // editResourceForDelete
3157:
3158:            /**
3159:             * check permissions for getResource().
3160:             * 
3161:             * @param id
3162:             *        The id of the new resource.
3163:             * @return true if the user is allowed to getResource(id), false if not.
3164:             */
3165:            public boolean allowGetResource(String id) {
3166:                return unlockCheck(AUTH_RESOURCE_READ, id);
3167:
3168:            } // allowGetResource
3169:
3170:            /**
3171:             * Check access to the resource with this local resource id. For non-collection resources only.
3172:             * 
3173:             * @param id
3174:             *        The id of the resource.
3175:             * @exception PermissionException
3176:             *            if the user does not have permissions to read the resource or read through any containing collection.
3177:             * @exception IdUnusedException
3178:             *            if the resource id is not found.
3179:             * @exception TypeException
3180:             *            if the resource is a collection.
3181:             */
3182:            public void checkResource(String id) throws PermissionException,
3183:                    IdUnusedException, TypeException {
3184:                // check security
3185:                unlock(AUTH_RESOURCE_READ, id);
3186:
3187:                ContentResource resource = findResource(id);
3188:                if (resource == null)
3189:                    throw new IdUnusedException(id);
3190:
3191:            } // checkResource
3192:
3193:            /**
3194:             * Access the resource with this resource id. For non-collection resources only. The resource content and properties are accessible from the returned Resource object.
3195:             * 
3196:             * @param id
3197:             *        The resource id.
3198:             * @exception PermissionException
3199:             *            if the user does not have permissions to read the resource or read through any containing collection.
3200:             * @exception IdUnusedException
3201:             *            if the resource id is not found.
3202:             * @exception TypeException
3203:             *            if the resource is a collection.
3204:             * @return the ContentResource object found.
3205:             */
3206:            public ContentResource getResource(String id)
3207:                    throws PermissionException, IdUnusedException,
3208:                    TypeException {
3209:                // check security
3210:                unlock(AUTH_RESOURCE_READ, id);
3211:
3212:                ContentResource resource = findResource(id);
3213:                if (resource == null)
3214:                    throw new IdUnusedException(id);
3215:
3216:                // track event
3217:                // EventTrackingService.post(EventTrackingService.newEvent(EVENT_RESOURCE_READ, resource.getReference(), false));
3218:
3219:                return resource;
3220:
3221:            } // getResource
3222:
3223:            /**
3224:             * Access the collection with this local resource id, locked for update. Must commitCollection() to make official, or cancelCollection() when done! The collection internal members and properties are accessible from the returned Collection object.
3225:             * 
3226:             * @param id
3227:             *        The id of the collection.
3228:             * @exception IdUnusedException
3229:             *            if the id does not exist.
3230:             * @exception TypeException
3231:             *            if the resource exists but is not a collection.
3232:             * @exception PermissionException
3233:             *            if the user does not have permissions to see this collection (or read through containing collections).
3234:             * @exception InUseException
3235:             *            if the Collection is locked by someone else.
3236:             * @return The ContentCollection object found.
3237:             */
3238:            public ContentCollectionEdit editCollection(String id)
3239:                    throws IdUnusedException, TypeException,
3240:                    PermissionException, InUseException {
3241:                checkExplicitLock(id);
3242:
3243:                // check security 
3244:                if (!allowUpdateCollection(id))
3245:                    throw new PermissionException(SessionManager
3246:                            .getCurrentSessionUserId(),
3247:                            AUTH_RESOURCE_WRITE_ANY, getReference(id));
3248:
3249:                // check for existance
3250:                if (!m_storage.checkCollection(id)) {
3251:                    throw new IdUnusedException(id);
3252:                }
3253:
3254:                // ignore the cache - get the collection with a lock from the info store
3255:                BaseCollectionEdit collection = (BaseCollectionEdit) m_storage
3256:                        .editCollection(id);
3257:                if (collection == null)
3258:                    throw new InUseException(id);
3259:
3260:                collection.setEvent(EVENT_RESOURCE_WRITE);
3261:
3262:                return collection;
3263:
3264:            } // editCollection
3265:
3266:            /**
3267:             * Access the resource with this resource id. For non-collection resources only. Internal find that doesn't do security or event tracking The resource content and properties are accessible from the returned Resource object.
3268:             * 
3269:             * @param id
3270:             *        The resource id.
3271:             * @exception TypeException
3272:             *            if the resource is a collection.
3273:             * @return the ContentResource object found, or null if there's a problem.
3274:             */
3275:            protected ContentResource findResource(String id)
3276:                    throws TypeException {
3277:                String ref = getReference(id);
3278:
3279:                ContentResource resource = null;
3280:                try {
3281:                    resource = (ContentResource) ThreadLocalManager
3282:                            .get("findResource@" + ref);
3283:                } catch (ClassCastException e) {
3284:                    throw new TypeException(ref);
3285:                }
3286:
3287:                if (resource == null) {
3288:                    resource = m_storage.getResource(id);
3289:
3290:                    if (resource != null) {
3291:                        ThreadLocalManager.set("findResource@" + ref,
3292:                                new BaseResourceEdit(resource));
3293:                    }
3294:                } else {
3295:                    resource = new BaseResourceEdit(resource);
3296:                }
3297:
3298:                //		// if not caching
3299:                //		if ((!m_caching) || (m_cache == null) || (m_cache.disabled()))
3300:                //		{
3301:                //			// TODO: current service caching
3302:                //			resource = m_storage.getResource(id);
3303:                //		}
3304:                //
3305:                //		else
3306:                //		{
3307:                //			// if we have it cached, use it (hit or miss)
3308:                //			String key = getReference(id);
3309:                //			if (m_cache.containsKey(key))
3310:                //			{
3311:                //				Object o = m_cache.get(key);
3312:                //				if ((o != null) && (!(o instanceof ContentResource))) throw new TypeException(id);
3313:                //
3314:                //				resource = (ContentResource) o;
3315:                //			}
3316:                //
3317:                //			// if not in the cache, see if we have it in our info store
3318:                //			else
3319:                //			{
3320:                //				resource = m_storage.getResource(id);
3321:                //
3322:                //				// cache it (hit or miss)
3323:                //				m_cache.put(key, resource);
3324:                //			}
3325:                //		}
3326:
3327:                return resource;
3328:
3329:            } // findResource
3330:
3331:            /**
3332:             * check permissions for removeResource().
3333:             * 
3334:             * @param id
3335:             *        The id of the new resource.
3336:             * @return true if the user is allowed to removeResource(id), false if not.
3337:             */
3338:            public boolean allowRemoveResource(String id) {
3339:                // check security
3340:                boolean isAllowed = allowRemove(id);
3341:
3342:                if (isAllowed) {
3343:                    try {
3344:                        checkExplicitLock(id);
3345:                    } catch (PermissionException e) {
3346:                        isAllowed = false;
3347:                    }
3348:                }
3349:
3350:                return isAllowed;
3351:
3352:            } // allowRemoveResource
3353:
3354:            /**
3355:             * Remove a resource. For non-collection resources only.
3356:             * 
3357:             * @param id
3358:             *        The resource id.
3359:             * @exception PermissionException
3360:             *            if the user does not have permissions to read a containing collection, or to remove this resource.
3361:             * @exception IdUnusedException
3362:             *            if the resource id is not found.
3363:             * @exception TypeException
3364:             *            if the resource is a collection.
3365:             * @exception InUseException
3366:             *            if the resource is locked by someone else.
3367:             */
3368:            public void removeResource(String id) throws PermissionException,
3369:                    IdUnusedException, TypeException, InUseException {
3370:                BaseResourceEdit edit = (BaseResourceEdit) editResourceForDelete(id);
3371:                removeResource(edit);
3372:
3373:            } // removeResource
3374:
3375:            /**
3376:             * Remove a resource that is locked for update.
3377:             * 
3378:             * @param edit
3379:             *        The ContentResourceEdit object to remove.
3380:             * @exception PermissionException
3381:             *            if the user does not have permissions to read a containing collection, or to remove this resource.
3382:             */
3383:            public void removeResource(ContentResourceEdit edit)
3384:                    throws PermissionException {
3385:                // check for closed edit
3386:                if (!edit.isActiveEdit()) {
3387:                    Exception e = new Exception();
3388:                    M_log.warn("removeResource(): closed ContentResourceEdit",
3389:                            e);
3390:                    return;
3391:                }
3392:
3393:                String id = edit.getId();
3394:
3395:                // check security (throws if not permitted)
3396:                checkExplicitLock(id);
3397:                if (!allowRemoveResource(edit.getId()))
3398:                    throw new PermissionException(SessionManager
3399:                            .getCurrentSessionUserId(),
3400:                            AUTH_RESOURCE_REMOVE_ANY, edit.getReference());
3401:
3402:                // htripath -store the metadata information into a delete table
3403:                // assumed uuid is not null as checkExplicitLock(id) throws exception when null
3404:                String uuid = this .getUuid(id);
3405:                String userId = SessionManager.getCurrentSessionUserId().trim();
3406:                addResourceToDeleteTable(edit, uuid, userId);
3407:
3408:                // complete the edit
3409:                m_storage.removeResource(edit);
3410:
3411:                // close the edit object
3412:                ((BaseResourceEdit) edit).closeEdit();
3413:
3414:                removeSizeCache(edit);
3415:
3416:                ((BaseResourceEdit) edit).setRemoved();
3417:
3418:                // remove old version of this edit from thread-local cache
3419:                String ref = edit.getReference();
3420:                ThreadLocalManager.set("findResource@" + ref, null);
3421:
3422:                // remove any realm defined for this resource
3423:                try {
3424:                    m_authzGroupService.removeAuthzGroup(m_authzGroupService
3425:                            .getAuthzGroup(edit.getReference()));
3426:                } catch (AuthzPermissionException e) {
3427:                    M_log.debug("removeResource: removing realm for : "
3428:                            + edit.getReference() + " : " + e);
3429:                } catch (GroupNotDefinedException ignore) {
3430:                    M_log.debug("removeResource: removing realm for : "
3431:                            + edit.getReference() + " : " + ignore);
3432:                }
3433:
3434:                // track it (no notification)
3435:                EventTrackingService.post(EventTrackingService.newEvent(
3436:                        EVENT_RESOURCE_REMOVE, edit.getReference(), true,
3437:                        NotificationService.NOTI_NONE));
3438:
3439:            } // removeResource
3440:
3441:            /**
3442:             * Store the resource in a separate delete table
3443:             * 
3444:             * @param edit
3445:             * @param uuid
3446:             * @param userId
3447:             * @exception PermissionException
3448:             * @exception ServerOverloadException
3449:             *            if server is configured to save resource body in filesystem and attempt to read from filesystem fails.
3450:             */
3451:            public void addResourceToDeleteTable(ContentResourceEdit edit,
3452:                    String uuid, String userId) throws PermissionException {
3453:                String id = edit.getId();
3454:                String content_type = edit.getContentType();
3455:                byte[] content = null;
3456:                try {
3457:                    content = edit.getContent();
3458:                } catch (ServerOverloadException e) {
3459:                    String this _method = this  + ".addResourceToDeleteTable()";
3460:                    M_log.warn("\n\n" + this _method + "\n" + this _method
3461:                            + ": Unable to access file in server filesystem\n"
3462:                            + this _method + ": May be orphaned file: " + id
3463:                            + "\n" + this _method + "\n\n");
3464:                }
3465:                ResourceProperties properties = edit.getProperties();
3466:
3467:                ContentResource newResource = addDeleteResource(id,
3468:                        content_type, content, properties, uuid, userId,
3469:                        NotificationService.NOTI_OPTIONAL);
3470:            }
3471:
3472:            public ContentResource addDeleteResource(String id, String type,
3473:                    byte[] content, ResourceProperties properties, String uuid,
3474:                    String userId, int priority) throws PermissionException {
3475:                id = (String) ((Hashtable) fixTypeAndId(id, type)).get("id");
3476:                // resource must also NOT end with a separator characters (fix it)
3477:                if (id.endsWith(Entity.SEPARATOR)) {
3478:                    id = id.substring(0, id.length() - 1);
3479:                }
3480:                // check security-unlock to add record
3481:                unlock(AUTH_RESOURCE_ADD, id);
3482:
3483:                // reserve the resource in storage - it will fail if the id is in use
3484:                BaseResourceEdit edit = (BaseResourceEdit) m_storage
3485:                        .putDeleteResource(id, uuid, userId);
3486:                // add live properties-do we need this? - done to have uniformity with main table
3487:                if (edit != null) {
3488:                    addLiveResourceProperties(edit);
3489:                }
3490:                // track event - do we need this? no harm to keep track
3491:                edit.setEvent(EVENT_RESOURCE_ADD);
3492:
3493:                edit.setContentType(type);
3494:                if (content != null) {
3495:                    edit.setContent(content);
3496:                }
3497:                addProperties(edit.getPropertiesEdit(), properties);
3498:
3499:                // complete the edit - update xml which contains properties xml and store the file content
3500:                m_storage.commitDeleteResource(edit, uuid);
3501:
3502:                // close the edit object
3503:                ((BaseResourceEdit) edit).closeEdit();
3504:
3505:                return edit;
3506:
3507:            } // addDeleteResource
3508:
3509:            /**
3510:             * check permissions for rename(). Note: for just this collection, not the members on down.
3511:             * 
3512:             * @param id
3513:             *        The id of the collection.
3514:             * @return true if the user is allowed to rename(id), false if not.
3515:             */
3516:            public boolean allowRename(String id, String new_id) {
3517:                M_log.warn("allowRename(" + id + ") - Rename not implemented");
3518:                return false;
3519:
3520:                // return unlockCheck(AUTH_RESOURCE_ADD, new_id) &&
3521:                // unlockCheck(AUTH_RESOURCE_REMOVE, id);
3522:
3523:            } // allowRename
3524:
3525:            /**
3526:             * Rename a collection or resource.
3527:             * 
3528:             * @param id
3529:             *        The id of the collection.
3530:             * @param new_id
3531:             *        The desired id of the collection.
3532:             * @return The full id of the resource after the rename is completed.
3533:             * @exception IdUnusedException
3534:             *            if the id does not exist.
3535:             * @exception TypeException
3536:             *            if the resource exists but is not a collection or resource.
3537:             * @exception PermissionException
3538:             *            if the user does not have permissions to rename
3539:             * @exception InUseException
3540:             *            if the id or a contained member is locked by someone else. collections, or remove any members of the collection.
3541:             * @exception IdUsedException
3542:             *            if copied item is a collection and the new id is already in use or if the copied item is not a collection and a unique id cannot be found in some arbitrary number of attempts (@see MAXIMUM_ATTEMPTS_FOR_UNIQUENESS).
3543:             * @exception ServerOverloadException
3544:             *            if the server is configured to write the resource body to the filesystem and the save fails.
3545:             */
3546:            public String rename(String id, String new_id)
3547:                    throws IdUnusedException, TypeException,
3548:                    PermissionException, InUseException, OverQuotaException,
3549:                    InconsistentException, IdUsedException,
3550:                    ServerOverloadException {
3551:                // Note - this could be implemented in this base class using a copy and a delete
3552:                // and then overridden in those derived classes which can support
3553:                // a direct rename operation.
3554:
3555:                // check security for remove resource (own or any)
3556:                if (!allowRemove(id))
3557:                    throw new PermissionException(SessionManager
3558:                            .getCurrentSessionUserId(),
3559:                            AUTH_RESOURCE_REMOVE_ANY, getReference(id));
3560:
3561:                // check security for read resource
3562:                unlock(AUTH_RESOURCE_READ, id);
3563:
3564:                // check security for add resource
3565:                unlock(AUTH_RESOURCE_ADD, new_id);
3566:
3567:                boolean isCollection = false;
3568:                boolean isRootCollection = false;
3569:                ContentResourceEdit this Resource = null;
3570:                ContentCollectionEdit this Collection = null;
3571:
3572:                if (M_log.isDebugEnabled())
3573:                    M_log.debug("copy(" + id + "," + new_id + ")");
3574:
3575:                if (m_storage.checkCollection(id)) {
3576:                    isCollection = true;
3577:                    // find the collection
3578:                    this Collection = editCollection(id);
3579:                    if (isRootCollection(id)) {
3580:                        cancelCollection(this Collection);
3581:                        throw new PermissionException(SessionManager
3582:                                .getCurrentSessionUserId(), null, null);
3583:                    }
3584:                } else {
3585:                    this Resource = editResource(id);
3586:                }
3587:
3588:                if (this Resource == null && this Collection == null) {
3589:                    throw new IdUnusedException(id);
3590:                }
3591:
3592:                if (isCollection) {
3593:                    new_id = copyCollection(this Collection, new_id);
3594:                    removeCollection(this Collection);
3595:                } else {
3596:                    new_id = copyResource(this Resource, new_id);
3597:                    removeResource(this Resource);
3598:                }
3599:                return new_id;
3600:
3601:            } // rename
3602:
3603:            /**
3604:             * check permissions for copy().
3605:             * 
3606:             * @param id
3607:             *        The id of the new resource.
3608:             * @param new_id
3609:             *        The desired id of the new resource.
3610:             * @return true if the user is allowed to copy(id,new_id), false if not.
3611:             */
3612:            public boolean allowCopy(String id, String new_id) {
3613:                return unlockCheck(AUTH_RESOURCE_ADD, new_id)
3614:                        && unlockCheck(AUTH_RESOURCE_READ, id);
3615:            }
3616:
3617:            /**
3618:             * Copy a collection or resource from one location to another. Creates a new collection with an id similar to new_folder_id and recursively copies all nested collections and resources within thisCollection to the new collection.
3619:             * 
3620:             * @param id
3621:             *        The id of the resource.
3622:             * @param folder_id
3623:             *        The id of the folder in which the copy should be created.
3624:             * @return The full id of the new copy of the resource.
3625:             * @exception PermissionException
3626:             *            if the user does not have permissions to read a containing collection, or to remove this resource.
3627:             * @exception IdUnusedException
3628:             *            if the resource id is not found.
3629:             * @exception TypeException
3630:             *            if the resource is a collection.
3631:             * @exception InUseException
3632:             *            if the resource is locked by someone else.
3633:             * @exception IdLengthException
3634:             *            if the new id of the copied item (or any nested item) is longer than the maximum length of an id.
3635:             * @exception InconsistentException
3636:             *            if the destination folder (folder_id) is contained within the source folder (id).
3637:             * @exception IdUsedException
3638:             *            if a unique resource id cannot be found after some arbitrary number of attempts (@see MAXIMUM_ATTEMPTS_FOR_UNIQUENESS).
3639:             * @exception ServerOverloadException
3640:             *            if the server is configured to write the resource body to the filesystem and the save fails.
3641:             */
3642:            public String copyIntoFolder(String id, String folder_id)
3643:                    throws PermissionException, IdUnusedException,
3644:                    TypeException, InUseException, IdLengthException,
3645:                    IdUniquenessException, OverQuotaException,
3646:                    InconsistentException, IdUsedException,
3647:                    ServerOverloadException {
3648:                if (folder_id.startsWith(id)) {
3649:                    throw new InconsistentException(id
3650:                            + " is contained within " + folder_id);
3651:                }
3652:                String new_id = newName(id, folder_id);
3653:                if (new_id.length() >= MAXIMUM_RESOURCE_ID_LENGTH) {
3654:                    throw new IdLengthException(new_id);
3655:                }
3656:
3657:                // Should use copyIntoFolder if possible
3658:                boolean isCollection = false;
3659:                boolean isRootCollection = false;
3660:                ContentResource this Resource = null;
3661:
3662:                if (M_log.isDebugEnabled())
3663:                    M_log.debug("copy(" + id + "," + new_id + ")");
3664:
3665:                // find the collection
3666:                ContentCollection this Collection = null;
3667:                try {
3668:                    this Collection = findCollection(id);
3669:                } catch (TypeException e) {
3670:                    this Collection = null;
3671:                }
3672:                if (this Collection == null) {
3673:                    this Resource = findResource(id);
3674:                } else {
3675:                    isCollection = true;
3676:                    if (isRootCollection(id)) {
3677:                        throw new PermissionException(null, null, null);
3678:                    }
3679:                }
3680:
3681:                if (this Resource == null && this Collection == null) {
3682:                    throw new IdUnusedException(id);
3683:                }
3684:
3685:                if (isCollection) {
3686:                    new_id = deepcopyCollection(this Collection, new_id);
3687:                } else {
3688:                    new_id = copyResource(this Resource, new_id);
3689:                }
3690:                return new_id;
3691:            }
3692:
3693:            /**
3694:             * Calculate a candidate for a resource id for a resource being copied/moved into a new folder.
3695:             * 
3696:             * @param id
3697:             * @param folder_id
3698:             * @exception PermissionException
3699:             *            if the user does not have permissions to read the properties for the existing resource.
3700:             * @exception IdUnusedException
3701:             *            if the resource id is not found.
3702:             */
3703:            protected String newName(String id, String folder_id)
3704:                    throws PermissionException, IdUnusedException {
3705:                String filename = isolateName(id);
3706:                if (filename == null || filename.length() == 0) {
3707:                    ResourceProperties props = getProperties(id);
3708:                    filename = props
3709:                            .getProperty(ResourceProperties.PROP_DISPLAY_NAME);
3710:                }
3711:                filename = Validator.escapeResourceName(filename);
3712:                if (!folder_id.endsWith(Entity.SEPARATOR)) {
3713:                    folder_id += Entity.SEPARATOR;
3714:                }
3715:
3716:                return folder_id + filename;
3717:            }
3718:
3719:            /**
3720:             * Move a resource or collection to a (different) folder. This may be accomplished by renaming the resource or by recursively renaming the collection and all enclosed members (no matter how deep) to effectively change their locations. Alternatively,
3721:             * it may be accomplished by copying the resource and recursively copying collections from their existing collection to the new collection and ultimately deleting the original resource(s) and/or collections(s).
3722:             * 
3723:             * @param id
3724:             *        The id of the resource or collection to be moved.
3725:             * @param folder_id
3726:             *        The id of the folder to which the resource should be moved.
3727:             * @return The full id of the resource after the move is completed.
3728:             * @exception PermissionException
3729:             *            if the user does not have permissions to read a containing collection, or to remove this resource.
3730:             * @exception IdUnusedException
3731:             *            if the resource id is not found.
3732:             * @exception TypeException
3733:             *            if the resource is a collection.
3734:             * @exception InUseException
3735:             *            if the resource is locked by someone else.
3736:             * @exception InconsistentException
3737:             *            if the containing collection does not exist.
3738:             * @exception InconsistentException
3739:             *            if the destination folder (folder_id) is contained within the source folder (id).
3740:             * @exception IdUsedException
3741:             *            if a unique resource id cannot be found after some arbitrary number of attempts (@see MAXIMUM_ATTEMPTS_FOR_UNIQUENESS).
3742:             * @exception ServerOverloadException
3743:             *            if the server is configured to write the resource body to the filesystem and the save fails.
3744:             */
3745:            public String moveIntoFolder(String id, String folder_id)
3746:                    throws PermissionException, IdUnusedException,
3747:                    TypeException, InUseException, OverQuotaException,
3748:                    IdUsedException, InconsistentException,
3749:                    ServerOverloadException {
3750:                if (folder_id.startsWith(id)) {
3751:                    throw new InconsistentException(id
3752:                            + " is contained within " + folder_id);
3753:                }
3754:                String new_id = newName(id, folder_id);
3755:
3756:                // check security for delete existing resource (any or own)
3757:                if (!allowRemove(id))
3758:                    throw new PermissionException(SessionManager
3759:                            .getCurrentSessionUserId(),
3760:                            AUTH_RESOURCE_REMOVE_ANY, getReference(id));
3761:
3762:                // check security for read existing resource
3763:                unlock(AUTH_RESOURCE_READ, id);
3764:
3765:                // check security for add new resource
3766:                unlock(AUTH_RESOURCE_ADD, new_id);
3767:
3768:                boolean isCollection = false;
3769:                boolean isRootCollection = false;
3770:                ContentResourceEdit this Resource = null;
3771:                ContentCollectionEdit this Collection = null;
3772:
3773:                if (M_log.isDebugEnabled())
3774:                    M_log.debug("moveIntoFolder(" + id + "," + new_id + ")");
3775:
3776:                if (m_storage.checkCollection(id)) {
3777:                    isCollection = true;
3778:                    // find the collection
3779:                    this Collection = editCollection(id);
3780:                    if (isRootCollection(id)) {
3781:                        cancelCollection(this Collection);
3782:                        throw new PermissionException(SessionManager
3783:                                .getCurrentSessionUserId(), null, null);
3784:                    }
3785:                } else {
3786:                    this Resource = editResource(id);
3787:                }
3788:
3789:                if (this Resource == null && this Collection == null) {
3790:                    throw new IdUnusedException(id);
3791:                }
3792:
3793:                if (isCollection) {
3794:                    new_id = moveCollection(this Collection, new_id);
3795:                } else {
3796:                    new_id = moveResource(this Resource, new_id);
3797:                }
3798:                return new_id;
3799:
3800:            } // moveIntoFolder
3801:
3802:            /**
3803:             * Move a collection to a new folder. Moves the existing collection or creates a new collection with an id similar to the new_folder_id (in which case the original collection is removed) and recursively moves all nested collections and resources
3804:             * within thisCollection to the new collection. When finished, thisCollection no longer exists, but the collection identified by the return value has the same structure and all of the members the original had (or copies of them).
3805:             * 
3806:             * @param thisCollection
3807:             *        The collection to be copied
3808:             * @param new_folder_id
3809:             *        The desired id of the collection after it is moved.
3810:             * @return The full id of the moved collection.
3811:             * @exception PermissionException
3812:             *            if the user does not have permissions to perform the operations
3813:             * @exception IdUnusedException
3814:             *            if the collection id is not found.
3815:             * @exception TypeException
3816:             *            if the resource is not a collection.
3817:             * @exception InUseException
3818:             *            if the collection is locked by someone else.
3819:             * @exception IdUsedException
3820:             *            if a unique resource id cannot be found after some arbitrary number of attempts to find a unique variation of the new_id (@see MAXIMUM_ATTEMPTS_FOR_UNIQUENESS).
3821:             * @exception ServerOverloadException
3822:             *            if the server is configured to save content bodies in the server's filesystem and an error occurs trying to access the filesystem.
3823:             */
3824:            protected String moveCollection(
3825:                    ContentCollectionEdit this Collection, String new_folder_id)
3826:                    throws PermissionException, IdUnusedException,
3827:                    TypeException, InUseException, OverQuotaException,
3828:                    IdUsedException, ServerOverloadException {
3829:                String name = isolateName(new_folder_id);
3830:
3831:                ResourceProperties properties = this Collection.getProperties();
3832:                ResourcePropertiesEdit newProps = duplicateResourceProperties(
3833:                        properties, this Collection.getId());
3834:                // newProps.addProperty(ResourceProperties.PROP_DISPLAY_NAME, name);
3835:                String displayName = newProps
3836:                        .getProperty(ResourceProperties.PROP_DISPLAY_NAME);
3837:
3838:                if (displayName == null && name != null) {
3839:                    newProps.addProperty(ResourceProperties.PROP_DISPLAY_NAME,
3840:                            name);
3841:                    displayName = name;
3842:                }
3843:
3844:                if (M_log.isDebugEnabled())
3845:                    M_log.debug("copyCollection adding colletion="
3846:                            + new_folder_id + " name=" + name);
3847:
3848:                String base_id = new_folder_id + "-";
3849:                boolean still_trying = true;
3850:                int attempt = 0;
3851:                try {
3852:                    while (still_trying
3853:                            && attempt < MAXIMUM_ATTEMPTS_FOR_UNIQUENESS) {
3854:                        try {
3855:                            ContentCollection newCollection = addCollection(
3856:                                    new_folder_id, newProps);
3857:
3858:                            // use the creator and creation-date of the original instead of the copy
3859:                            BaseCollectionEdit collection = (BaseCollectionEdit) m_storage
3860:                                    .editCollection(newCollection.getId());
3861:                            ResourcePropertiesEdit props = collection
3862:                                    .getPropertiesEdit();
3863:                            String creator = properties
3864:                                    .getProperty(ResourceProperties.PROP_CREATOR);
3865:                            if (creator != null && !creator.trim().equals("")) {
3866:                                props.addProperty(
3867:                                        ResourceProperties.PROP_CREATOR,
3868:                                        creator);
3869:                            }
3870:                            String created = properties
3871:                                    .getProperty(ResourceProperties.PROP_CREATION_DATE);
3872:                            if (created != null) {
3873:                                props.addProperty(
3874:                                        ResourceProperties.PROP_CREATION_DATE,
3875:                                        created);
3876:                            }
3877:                            m_storage.commitCollection(collection);
3878:
3879:                            if (M_log.isDebugEnabled())
3880:                                M_log.debug("moveCollection successful");
3881:                            still_trying = false;
3882:                        } catch (IdUsedException e) {
3883:                            try {
3884:                                ContentCollection test_for_exists = getCollection(new_folder_id);
3885:                            } catch (Exception ee) {
3886:                                throw e;
3887:                            }
3888:                            attempt++;
3889:                            if (attempt >= MAXIMUM_ATTEMPTS_FOR_UNIQUENESS) {
3890:                                throw e;
3891:                            }
3892:                            new_folder_id = base_id + attempt;
3893:                            // newProps.addProperty(ResourceProperties.PROP_DISPLAY_NAME, name + "-" + attempt);
3894:                            newProps.addProperty(
3895:                                    ResourceProperties.PROP_DISPLAY_NAME,
3896:                                    displayName + "-" + attempt);
3897:                        }
3898:                    }
3899:
3900:                    List members = this Collection.getMembers();
3901:
3902:                    if (M_log.isDebugEnabled())
3903:                        M_log.debug("moveCollection size=" + members.size());
3904:
3905:                    Iterator memberIt = members.iterator();
3906:                    while (memberIt.hasNext()) {
3907:                        String member_id = (String) memberIt.next();
3908:                        moveIntoFolder(member_id, new_folder_id);
3909:                    }
3910:
3911:                    removeCollection(this Collection);
3912:                } catch (InconsistentException e) {
3913:                    throw new TypeException(new_folder_id);
3914:                } catch (IdInvalidException e) {
3915:                    throw new TypeException(new_folder_id);
3916:                }
3917:
3918:                return new_folder_id;
3919:
3920:            } // moveCollection
3921:
3922:            /**
3923:             * Move a resource to a new folder. Either creates a new resource with an id similar to the new_folder_id and and removes the original resource, or renames the resource with an id similar to the new id, which effectively moves the resource to a new
3924:             * location.
3925:             * 
3926:             * @param thisResource
3927:             *        The resource to be copied
3928:             * @param new_id
3929:             *        The desired id of the resource after it is moved.
3930:             * @return The full id of the moved resource (which may be a variation on the new_id to ensure uniqueness within the new folder.
3931:             * @exception PermissionException
3932:             *            if the user does not have permissions to perform the operations
3933:             * @exception IdUnusedException
3934:             *            if the resource id is not found.
3935:             * @exception TypeException
3936:             *            if the resource is a collection.
3937:             * @exception InUseException
3938:             *            if the resource is locked by someone else.
3939:             * @exception IdUsedException
3940:             *            if a unique resource id cannot be found after some arbitrary number of attempts to find a unique variation of the new_id (@see MAXIMUM_ATTEMPTS_FOR_UNIQUENESS).
3941:             * @exception ServerOverloadException
3942:             *            if the server is configured to save content bodies in the server's filesystem and an error occurs trying to access the filesystem.
3943:             */
3944:            protected String moveResource(ContentResourceEdit this Resource,
3945:                    String new_id) throws PermissionException,
3946:                    IdUnusedException, TypeException, InUseException,
3947:                    OverQuotaException, IdUsedException,
3948:                    ServerOverloadException {
3949:                String fileName = isolateName(new_id);
3950:                String folderId = isolateContainingId(new_id);
3951:
3952:                ResourceProperties properties = this Resource.getProperties();
3953:                String displayName = properties
3954:                        .getProperty(ResourceProperties.PROP_DISPLAY_NAME);
3955:                if (displayName == null && fileName != null) {
3956:                    displayName = fileName;
3957:                }
3958:                String new_displayName = displayName;
3959:
3960:                if (M_log.isDebugEnabled())
3961:                    M_log.debug("moveResource displayname=" + new_displayName
3962:                            + " fileName=" + fileName);
3963:
3964:                String basename = fileName;
3965:                String extension = "";
3966:                int index = fileName.lastIndexOf(".");
3967:                if (index >= 0) {
3968:                    basename = fileName.substring(0, index);
3969:                    extension = fileName.substring(index);
3970:                }
3971:
3972:                boolean still_trying = true;
3973:                int attempt = 0;
3974:
3975:                while (still_trying
3976:                        && attempt < MAXIMUM_ATTEMPTS_FOR_UNIQUENESS) {
3977:                    // copy the resource to the new location
3978:                    try {
3979:                        ContentResourceEdit edit = addResource(new_id);
3980:                        edit.setContentType(this Resource.getContentType());
3981:                        edit.setContent(this Resource.streamContent());
3982:                        edit.setResourceType(this Resource.getResourceType());
3983:                        edit.setAvailability(this Resource.isHidden(),
3984:                                this Resource.getReleaseDate(), this Resource
3985:                                        .getRetractDate());
3986:
3987:                        //((BaseResourceEdit) edit).m_filePath = ((BaseResourceEdit) thisResource).m_filePath;
3988:                        //((BaseResourceEdit) thisResource).m_filePath = null;
3989:                        //				Collection groups = thisResource.getGroups();
3990:                        //				if(groups == null || groups.isEmpty())
3991:                        //				{
3992:                        //					// do nothing
3993:                        //				}
3994:                        //				else
3995:                        //				{
3996:                        //					edit.setGroupAccess(groups);
3997:                        //				}
3998:
3999:                        ResourcePropertiesEdit props = edit.getPropertiesEdit();
4000:                        Iterator<String> nameIt = properties.getPropertyNames();
4001:                        while (nameIt.hasNext()) {
4002:                            String name = nameIt.next();
4003:                            props.addProperty(name, properties
4004:                                    .getProperty(name));
4005:                        }
4006:                        //addProperties(props, properties);
4007:                        //				String creator = properties.getProperty(ResourceProperties.PROP_CREATOR);
4008:                        //				if (creator != null && !creator.trim().equals(""))
4009:                        //				{
4010:                        //					props.addProperty(ResourceProperties.PROP_CREATOR, creator);
4011:                        //				}
4012:                        //				String created = properties.getProperty(ResourceProperties.PROP_CREATION_DATE);
4013:                        //				if (created != null)
4014:                        //				{
4015:                        //					props.addProperty(ResourceProperties.PROP_CREATION_DATE, created);
4016:                        //				}
4017:                        props.addProperty(ResourceProperties.PROP_DISPLAY_NAME,
4018:                                new_displayName);
4019:
4020:                        String oldUuid = getUuid(this Resource.getId());
4021:                        setUuidInternal(new_id, oldUuid);
4022:
4023:                        m_storage.commitResource(edit);
4024:                        // close the edit object
4025:                        ((BaseResourceEdit) edit).closeEdit();
4026:
4027:                        m_storage.removeResource(this Resource);
4028:
4029:                        if (M_log.isDebugEnabled())
4030:                            M_log.debug("moveResource successful");
4031:                        still_trying = false;
4032:                    } catch (InconsistentException e) {
4033:                        throw new TypeException(new_id);
4034:                    } catch (IdInvalidException e) {
4035:                        throw new TypeException(new_id);
4036:                    } catch (IdUsedException e) {
4037:                        try {
4038:                            ContentResource test_for_exists = getResource(new_id);
4039:                        } catch (Exception ee) {
4040:                            throw e;
4041:                        }
4042:                        if (attempt >= MAXIMUM_ATTEMPTS_FOR_UNIQUENESS) {
4043:                            throw e;
4044:                        }
4045:                        attempt++;
4046:                        new_id = folderId + basename + "-" + attempt
4047:                                + extension;
4048:                        new_displayName = displayName + " (" + attempt + ")";
4049:                    }
4050:                }
4051:
4052:                //removeResource(thisResource);
4053:
4054:                return new_id;
4055:
4056:            } // moveResource
4057:
4058:            /**
4059:             * Copy a resource or collection.
4060:             * 
4061:             * @param id
4062:             *        The id of the resource.
4063:             * @param new_id
4064:             *        The desired id of the new resource.
4065:             * @exception PermissionException
4066:             *            if the user does not have permissions to read a containing collection, or to remove this resource.
4067:             * @exception IdUnusedException
4068:             *            if the resource id is not found.
4069:             * @exception TypeException
4070:             *            if the resource is a collection.
4071:             * @exception InUseException
4072:             *            if the resource is locked by someone else.
4073:             * @exception IdUsedException
4074:             *            if copied item is a collection and the new id is already in use or if the copied item is not a collection and a unique id cannot be found in some arbitrary number of attempts (@see MAXIMUM_ATTEMPTS_FOR_UNIQUENESS).
4075:             * @exception ServerOverloadException
4076:             *            if the server is configured to write the resource body to the filesystem and the save fails.
4077:             * @see copyIntoFolder(String, String) method (preferred method for invocation from a tool).
4078:             */
4079:            public String copy(String id, String new_id)
4080:                    throws PermissionException, IdUnusedException,
4081:                    TypeException, InUseException, OverQuotaException,
4082:                    IdUsedException, ServerOverloadException {
4083:                // Should use copyIntoFolder if possible
4084:                boolean isCollection = false;
4085:                boolean isRootCollection = false;
4086:                ContentResource this Resource = null;
4087:
4088:                if (M_log.isDebugEnabled())
4089:                    M_log.debug("copy(" + id + "," + new_id + ")");
4090:
4091:                // find the collection
4092:                ContentCollection this Collection = findCollection(id);
4093:                if (this Collection != null) {
4094:                    isCollection = true;
4095:                    if (isRootCollection(id)) {
4096:                        throw new PermissionException(null, null, null);
4097:                    }
4098:                } else {
4099:                    this Resource = findResource(id);
4100:                }
4101:
4102:                if (this Resource == null && this Collection == null) {
4103:                    throw new IdUnusedException(id);
4104:                }
4105:
4106:                if (isCollection) {
4107:                    new_id = copyCollection(this Collection, new_id);
4108:                } else {
4109:                    new_id = copyResource(this Resource, new_id);
4110:                }
4111:                return new_id;
4112:
4113:            }
4114:
4115:            /**
4116:             * Get a duplicate copy of resource properties This copies everything except for the DISPLAYNAME - DISPLAYNAME is only copied if it is different than the file name as derived from the id (path) Note to Chuck - should the add operations check for empty
4117:             * Display and set it to the file name rather than putting all the code all over the place.
4118:             */
4119:            private ResourcePropertiesEdit duplicateResourceProperties(
4120:                    ResourceProperties properties, String id) {
4121:                ResourcePropertiesEdit resourceProperties = newResourceProperties();
4122:
4123:                if (properties == null)
4124:                    return resourceProperties;
4125:
4126:                // If there is a distinct display name, we keep it
4127:                // If the display name is the "file name" we pitch it and let the name change
4128:                String displayName = properties
4129:                        .getProperty(ResourceProperties.PROP_DISPLAY_NAME);
4130:                String resourceName = isolateName(id);
4131:                if (displayName == null)
4132:                    displayName = resourceName;
4133:                if (displayName.length() == 0)
4134:                    displayName = resourceName;
4135:
4136:                // loop throuh the properties
4137:                Iterator propertyNames = properties.getPropertyNames();
4138:                while (propertyNames.hasNext()) {
4139:                    String propertyName = (String) propertyNames.next();
4140:                    resourceProperties.addProperty(propertyName, properties
4141:                            .getProperty(propertyName));
4142:                    /*
4143:                    if (!properties.isLiveProperty(propertyName))
4144:                    {
4145:                    	if (propertyName.equals(ResourceProperties.PROP_DISPLAY_NAME))
4146:                    	{
4147:                    		if (!displayName.equals(resourceName))
4148:                    		{
4149:                    			resourceProperties.addProperty(propertyName, displayName);
4150:                    		}
4151:                    	}
4152:                    	else
4153:                    	{
4154:                    		resourceProperties.addProperty(propertyName, properties.getProperty(propertyName));
4155:                    	} // if-else
4156:                    } // if
4157:                     */
4158:                } // while
4159:                return resourceProperties;
4160:
4161:            } // duplicateResourceProperties
4162:
4163:            /**
4164:             * Copy a resource.
4165:             * 
4166:             * @param thisResource
4167:             *        The resource to be copied
4168:             * @param new_id
4169:             *        The desired id of the new resource.
4170:             * @return The full id of the new copy of the resource.
4171:             * @exception PermissionException
4172:             *            if the user does not have permissions to read a containing collection, or to remove this resource.
4173:             * @exception IdUnusedException
4174:             *            if the resource id is not found.
4175:             * @exception TypeException
4176:             *            if the resource is a collection.
4177:             * @exception InUseException
4178:             *            if the resource is locked by someone else.
4179:             * @exception OverQuotaException
4180:             *            if copying the resource would exceed the quota.
4181:             * @exception IdUsedException
4182:             *            if a unique id cannot be found in some arbitrary number of attempts (@see MAXIMUM_ATTEMPTS_FOR_UNIQUENESS).
4183:             * @exception ServerOverloadException
4184:             *            if the server is configured to write the resource body to the filesystem and the save fails.
4185:             */
4186:            public String copyResource(ContentResource resource, String new_id)
4187:                    throws PermissionException, IdUnusedException,
4188:                    TypeException, InUseException, OverQuotaException,
4189:                    IdUsedException, ServerOverloadException {
4190:                String fileName = isolateName(new_id);
4191:                fileName = Validator.escapeResourceName(fileName);
4192:                String folderId = isolateContainingId(new_id);
4193:
4194:                ResourceProperties properties = resource.getProperties();
4195:                String displayName = properties
4196:                        .getProperty(ResourceProperties.PROP_DISPLAY_NAME);
4197:                if (displayName == null && fileName != null) {
4198:                    displayName = fileName;
4199:                }
4200:                String new_displayName = displayName;
4201:                if (M_log.isDebugEnabled())
4202:                    M_log.debug("copyResource displayname=" + new_displayName
4203:                            + " fileName=" + fileName);
4204:
4205:                String basename = fileName;
4206:                String extension = "";
4207:                int index = fileName.lastIndexOf(".");
4208:                if (index >= 0) {
4209:                    basename = fileName.substring(0, index);
4210:                    extension = fileName.substring(index);
4211:                }
4212:
4213:                boolean still_trying = true;
4214:                int attempt = 0;
4215:
4216:                while (still_trying
4217:                        && attempt < MAXIMUM_ATTEMPTS_FOR_UNIQUENESS) {
4218:                    // copy the resource to the new location
4219:                    ContentResourceEdit edit = null;
4220:                    try {
4221:                        edit = addResource(new_id);
4222:                        edit.setContentType(resource.getContentType());
4223:                        edit.setContent(resource.getContent());
4224:                        edit.setResourceType(resource.getResourceType());
4225:                        ResourcePropertiesEdit newProps = edit
4226:                                .getPropertiesEdit();
4227:
4228:                        addProperties(newProps, properties);
4229:                        newProps.addProperty(
4230:                                ResourceProperties.PROP_DISPLAY_NAME,
4231:                                new_displayName);
4232:                        //				Collection groups = resource.getGroups();
4233:                        //				if(groups == null || groups.isEmpty())
4234:                        //				{
4235:                        //					// do nothing
4236:                        //				}
4237:                        //				else
4238:                        //				{
4239:                        //					edit.setGroupAccess(groups);
4240:                        //				}
4241:                        edit.setAvailability(resource.isHidden(), resource
4242:                                .getReleaseDate(), resource.getRetractDate());
4243:
4244:                        commitResource(edit, NotificationService.NOTI_NONE);
4245:                        // close the edit object
4246:                        ((BaseResourceEdit) edit).closeEdit();
4247:
4248:                        if (M_log.isDebugEnabled())
4249:                            M_log.debug("copyResource successful");
4250:                        still_trying = false;
4251:                    } catch (InconsistentException e) {
4252:                        throw new TypeException(new_id);
4253:                    } catch (IdInvalidException e) {
4254:                        throw new TypeException(new_id);
4255:                    } catch (IdUsedException e) {
4256:                        try {
4257:                            ContentResource test_for_exists = getResource(new_id);
4258:                        } catch (Exception ee) {
4259:                            throw e;
4260:                        }
4261:                        if (attempt >= MAXIMUM_ATTEMPTS_FOR_UNIQUENESS) {
4262:                            throw e;
4263:                        }
4264:                        attempt++;
4265:                        new_id = folderId + basename + "-" + attempt
4266:                                + extension;
4267:                        new_displayName = displayName + " (" + attempt + ")";
4268:                        // Could come up with a naming convention to add versions here
4269:                    }
4270:                }
4271:                return new_id;
4272:
4273:            } // copyResource
4274:
4275:            /**
4276:             * Copy a collection.
4277:             * 
4278:             * @param thisCollection
4279:             *        The collection to be copied
4280:             * @param new_id
4281:             *        The desired id of the new collection.
4282:             * @return The full id of the new copy of the resource.
4283:             * @exception PermissionException
4284:             *            if the user does not have permissions to perform the operations
4285:             * @exception IdUnusedException
4286:             *            if the collection id is not found.
4287:             * @exception TypeException
4288:             *            if the resource is not a collection.
4289:             * @exception InUseException
4290:             *            if the resource is locked by someone else.
4291:             * @exception IdUsedException
4292:             *            if the new collection id is already in use.
4293:             */
4294:            public String copyCollection(ContentCollection this Collection,
4295:                    String new_id) throws PermissionException,
4296:                    IdUnusedException, TypeException, InUseException,
4297:                    OverQuotaException, IdUsedException {
4298:                List members = this Collection.getMemberResources();
4299:
4300:                if (M_log.isDebugEnabled())
4301:                    M_log.debug("copyCollection size=" + members.size());
4302:
4303:                if (members.size() > 0) {
4304:                    // recurse to copy everything in the folder?
4305:                    throw new PermissionException(null, null, null);
4306:                }
4307:
4308:                String name = isolateName(new_id);
4309:
4310:                ResourceProperties properties = this Collection.getProperties();
4311:                ResourcePropertiesEdit newProps = duplicateResourceProperties(
4312:                        properties, this Collection.getId());
4313:                newProps
4314:                        .addProperty(ResourceProperties.PROP_DISPLAY_NAME, name);
4315:
4316:                if (M_log.isDebugEnabled())
4317:                    M_log.debug("copyCollection adding colletion=" + new_id
4318:                            + " name=" + name);
4319:
4320:                try {
4321:                    ContentCollection newCollection = addCollection(new_id,
4322:                            newProps);
4323:                    if (M_log.isDebugEnabled())
4324:                        M_log.debug("copyCollection successful");
4325:                } catch (InconsistentException e) {
4326:                    throw new TypeException(new_id);
4327:                } catch (IdInvalidException e) {
4328:                    throw new TypeException(new_id);
4329:                }
4330:                /*
4331:                 * catch (IdUsedException e) // Why is this the case?? { throw new PermissionException(null, null); }
4332:                 */
4333:                return new_id;
4334:
4335:            } // copyCollection
4336:
4337:            /**
4338:             * Make a deep copy of a collection. Creates a new collection with an id similar to new_folder_id and recursively copies all nested collections and resources within thisCollection to the new collection.
4339:             * 
4340:             * @param thisCollection
4341:             *        The collection to be copied
4342:             * @param new_folder_id
4343:             *        The desired id of the new collection.
4344:             * @return The full id of the copied collection (which may be a slight variation on the desired id to ensure uniqueness).
4345:             * @exception PermissionException
4346:             *            if the user does not have permissions to perform the operations
4347:             * @exception IdUnusedException
4348:             *            if the collection id is not found. ???
4349:             * @exception TypeException
4350:             *            if the resource is not a collection.
4351:             * @exception InUseException
4352:             *            if the collection is locked by someone else.
4353:             * @exception IdUsedException
4354:             *            if a unique id cannot be found for the new collection after some arbitrary number of attempts to find a unique variation of the new_folder_id (@see MAXIMUM_ATTEMPTS_FOR_UNIQUENESS).
4355:             * @exception ServerOverloadException
4356:             *            if the server is configured to save content bodies in the server's filesystem and an error occurs trying to access the filesystem.
4357:             */
4358:            protected String deepcopyCollection(
4359:                    ContentCollection this Collection, String new_folder_id)
4360:                    throws PermissionException, IdUnusedException,
4361:                    TypeException, InUseException, IdLengthException,
4362:                    IdUniquenessException, OverQuotaException, IdUsedException,
4363:                    ServerOverloadException {
4364:                String name = isolateName(new_folder_id);
4365:                ResourceProperties properties = this Collection.getProperties();
4366:                ResourcePropertiesEdit newProps = duplicateResourceProperties(
4367:                        properties, this Collection.getId());
4368:                if (newProps.getProperty(ResourceProperties.PROP_DISPLAY_NAME) == null) {
4369:                    name = Validator.escapeResourceName(name);
4370:
4371:                    newProps.addProperty(ResourceProperties.PROP_DISPLAY_NAME,
4372:                            name);
4373:                }
4374:
4375:                if (M_log.isDebugEnabled())
4376:                    M_log.debug("copyCollection adding colletion="
4377:                            + new_folder_id + " name=" + name);
4378:
4379:                String base_id = new_folder_id + "-";
4380:                boolean still_trying = true;
4381:                int attempt = 0;
4382:                ContentCollection newCollection = null;
4383:                try {
4384:                    try {
4385:                        newCollection = addCollection(new_folder_id, newProps);
4386:
4387:                        if (M_log.isDebugEnabled())
4388:                            M_log.debug("moveCollection successful");
4389:                        still_trying = false;
4390:                    } catch (IdUsedException e) {
4391:                        try {
4392:                            checkCollection(new_folder_id);
4393:                        } catch (Exception ee) {
4394:                            throw new IdUniquenessException(new_folder_id);
4395:                        }
4396:                    }
4397:                    String containerId = this 
4398:                            .isolateContainingId(new_folder_id);
4399:                    ContentCollection containingCollection = findCollection(containerId);
4400:                    SortedSet siblings = new TreeSet();
4401:                    siblings.addAll(containingCollection.getMembers());
4402:
4403:                    while (still_trying) {
4404:                        attempt++;
4405:                        if (attempt >= MAXIMUM_ATTEMPTS_FOR_UNIQUENESS) {
4406:                            throw new IdUniquenessException(new_folder_id);
4407:                        }
4408:                        new_folder_id = base_id + attempt;
4409:                        if (!siblings.contains(new_folder_id)) {
4410:                            newProps.addProperty(
4411:                                    ResourceProperties.PROP_DISPLAY_NAME, name
4412:                                            + "-" + attempt);
4413:                            try {
4414:                                newCollection = addCollection(new_folder_id,
4415:                                        newProps);
4416:                                still_trying = false;
4417:                            } catch (IdUsedException inner_e) {
4418:                                // try again
4419:                            }
4420:                        }
4421:                    }
4422:
4423:                    List members = this Collection.getMembers();
4424:
4425:                    if (M_log.isDebugEnabled())
4426:                        M_log.debug("moveCollection size=" + members.size());
4427:
4428:                    Iterator memberIt = members.iterator();
4429:                    while (memberIt.hasNext()) {
4430:                        String member_id = (String) memberIt.next();
4431:                        copyIntoFolder(member_id, new_folder_id);
4432:                    }
4433:
4434:                } catch (InconsistentException e) {
4435:                    throw new TypeException(new_folder_id);
4436:                } catch (IdInvalidException e) {
4437:                    throw new TypeException(new_folder_id);
4438:                }
4439:
4440:                return new_folder_id;
4441:
4442:            } // deepcopyCollection
4443:
4444:            /**
4445:             * Commit the changes made, and release the lock. The Object is disabled, and not to be used after this call.
4446:             * 
4447:             * @param edit
4448:             *        The ContentResourceEdit object to commit.
4449:             * @exception OverQuotaException
4450:             *            if this would result in being over quota (the edit is then cancled).
4451:             * @exception ServerOverloadException
4452:             *            if the server is configured to write the resource body to the filesystem and the save fails.
4453:             * @exception PermissionException 
4454:             * 			 if the user is trying to make a change for which they lack permission.
4455:             */
4456:            public void commitResource(ContentResourceEdit edit)
4457:                    throws OverQuotaException, ServerOverloadException {
4458:                commitResource(edit, NotificationService.NOTI_OPTIONAL);
4459:
4460:            } // commitResource
4461:
4462:            /**
4463:             * Commit the changes made, and release the lock. The Object is disabled, and not to be used after this call.
4464:             * 
4465:             * @param edit
4466:             *        The ContentResourceEdit object to commit.
4467:             * @param priority
4468:             *        The notification priority of this commit.
4469:             * @exception OverQuotaException
4470:             *            if this would result in being over quota (the edit is then cancled).
4471:             * @exception ServerOverloadException
4472:             *            if the server is configured to write the resource body to the filesystem and the save fails.
4473:             */
4474:            public void commitResource(ContentResourceEdit edit, int priority)
4475:                    throws OverQuotaException, ServerOverloadException {
4476:
4477:                // check for closed edit
4478:                if (!edit.isActiveEdit()) {
4479:                    Exception e = new Exception();
4480:                    M_log.warn("commitResource(): closed ContentResourceEdit",
4481:                            e);
4482:                    return;
4483:                }
4484:
4485:                // check for over quota.
4486:                if (overQuota(edit)) {
4487:                    cancelResource(edit);
4488:                    throw new OverQuotaException(edit.getReference());
4489:                }
4490:
4491:                commitResourceEdit(edit, priority);
4492:
4493:                addSizeCache(edit);
4494:
4495:            } // commitResource
4496:
4497:            /**
4498:             * Commit the changes made, and release the lock - no quota check. The Object is disabled, and not to be used after this call.
4499:             * 
4500:             * @param edit
4501:             *        The ContentResourceEdit object to commit.
4502:             * @param priority
4503:             *        The notification priority of this commit.
4504:             * @throws PermissionException 
4505:             */
4506:            protected void commitResourceEdit(ContentResourceEdit edit,
4507:                    int priority) throws ServerOverloadException {
4508:                // check for closed edit
4509:                if (!edit.isActiveEdit()) {
4510:                    Exception e = new Exception();
4511:                    M_log.warn(
4512:                            "commitResourceEdit(): closed ContentResourceEdit",
4513:                            e);
4514:                    return;
4515:                }
4516:
4517:                if (this .m_prioritySortEnabled) {
4518:                    // ((BasicGroupAwareEdit) edit).setPriority();
4519:                }
4520:
4521:                // update the properties for update
4522:                addLiveUpdateResourceProperties(edit);
4523:
4524:                // complete the edit
4525:                m_storage.commitResource(edit);
4526:
4527:                // close the edit object
4528:                ((BaseResourceEdit) edit).closeEdit();
4529:
4530:                // must remove old version of this edit from thread-local cache
4531:                // so we get new version if we try to retrieve it in same thread
4532:                String ref = edit.getReference();
4533:                ThreadLocalManager.set("findResource@" + ref, null);
4534:
4535:                // track it
4536:                EventTrackingService.post(EventTrackingService.newEvent(
4537:                        ((BaseResourceEdit) edit).getEvent(), edit
4538:                                .getReference(), true, priority));
4539:
4540:            } // commitResourceEdit
4541:
4542:            /**
4543:             * Test a collection of Group object for the specified group reference
4544:             * @param groups The collection (Group) of groups
4545:             * @param groupRef The string group reference to find.
4546:             * @return true if found, false if not.
4547:             */
4548:            protected boolean groupCollectionContainsRefString(
4549:                    Collection groups, String groupRef) {
4550:                for (Iterator i = groups.iterator(); i.hasNext();) {
4551:                    Group group = (Group) i.next();
4552:                    if (group.getReference().equals(groupRef))
4553:                        return true;
4554:                }
4555:
4556:                return false;
4557:            }
4558:
4559:            /**
4560:             * Cancel the changes made object, and release the lock. The Object is disabled, and not to be used after this call.
4561:             * 
4562:             * @param edit
4563:             *        The ContentResourceEdit object to commit.
4564:             */
4565:            public void cancelResource(ContentResourceEdit edit) {
4566:                // check for closed edit
4567:                if (!edit.isActiveEdit()) {
4568:                    Exception e = new Exception();
4569:                    M_log.warn("cancelResource(): closed ContentResourceEdit",
4570:                            e);
4571:                    return;
4572:                }
4573:
4574:                // release the edit lock
4575:                m_storage.cancelResource(edit);
4576:
4577:                // if the edit is newly created during an add resource process, remove it from the storage
4578:                if (((BaseResourceEdit) edit).getEvent().equals(
4579:                        EVENT_RESOURCE_ADD)) {
4580:                    m_storage.removeResource(edit);
4581:                }
4582:
4583:                // close the edit object
4584:                ((BaseResourceEdit) edit).closeEdit();
4585:
4586:            } // cancelResource
4587:
4588:            /**
4589:             * check permissions for getProperties().
4590:             * 
4591:             * @param id
4592:             *        The id of the new resource.
4593:             * @return true if the user is allowed to getProperties(id), false if not.
4594:             */
4595:            public boolean allowGetProperties(String id) {
4596:                return unlockCheck(AUTH_RESOURCE_READ, id);
4597:
4598:            } // allowGetProperties
4599:
4600:            /**
4601:             * Access the properties of a resource with this resource id, either collection or resource.
4602:             * 
4603:             * @param id
4604:             *        The resource id.
4605:             * @exception PermissionException
4606:             *            if the user does not have permissions to read properties on this object or read through containing collections.
4607:             * @exception IdUnusedException
4608:             *            if the resource id is not found.
4609:             * @return the ResourceProperties object for this resource.
4610:             */
4611:            public ResourceProperties getProperties(String id)
4612:                    throws PermissionException, IdUnusedException {
4613:                unlock(AUTH_RESOURCE_READ, id);
4614:
4615:                boolean collectionHint = id.endsWith(Entity.SEPARATOR);
4616:
4617:                Entity o = null;
4618:
4619:                try {
4620:                    if (collectionHint) {
4621:                        o = findCollection(id);
4622:                    } else {
4623:                        o = findResource(id);
4624:                    }
4625:                } catch (TypeException ignore) {
4626:                }
4627:
4628:                // unlikely, but...
4629:                if (o == null)
4630:                    throw new IdUnusedException(id);
4631:
4632:                // track event - removed for clarity of the event log -ggolden
4633:                // EventTrackingService.post(EventTrackingService.newEvent(EVENT_PROPERTIES_READ, getReference(id)));
4634:
4635:                return o.getProperties();
4636:
4637:            } // getProperties
4638:
4639:            /**
4640:             * check permissions for addProperty().
4641:             * 
4642:             * @param id
4643:             *        The id of the new resource.
4644:             * @return true if the user is allowed to addProperty(id), false if not.
4645:             */
4646:            public boolean allowAddProperty(String id) {
4647:                boolean isAllowed = allowUpdate(id);
4648:                if (isAllowed) {
4649:                    try {
4650:                        checkExplicitLock(id);
4651:                    } catch (PermissionException e) {
4652:                        isAllowed = false;
4653:                    }
4654:                }
4655:
4656:                return isAllowed;
4657:
4658:            } // allowAddProperty
4659:
4660:            /**
4661:             * Add / update a property for a resource, either collection or resource.
4662:             * 
4663:             * @param id
4664:             *        The resource id.
4665:             * @param name
4666:             *        The properties name to add or update
4667:             * @param value
4668:             *        The new value for the property.
4669:             * @exception PermissionException
4670:             *            if the user does not have premissions to write properties on this object or read through containing collections.
4671:             * @exception IdUnusedException
4672:             *            if the resource id is not found.
4673:             * @exception TypeException
4674:             *            if any property requested cannot be set (it may be live).
4675:             * @exception InUseException
4676:             *            if the resource is locked by someone else.
4677:             * @exception ServerOverloadException
4678:             *            if the server is configured to write the resource body to the filesystem and the save fails.
4679:             * @return the ResourceProperties object for this resource.
4680:             */
4681:            public ResourceProperties addProperty(String id, String name,
4682:                    String value) throws PermissionException,
4683:                    IdUnusedException, TypeException, InUseException,
4684:                    ServerOverloadException {
4685:                // check security 
4686:                checkExplicitLock(id);
4687:                if (!allowAddProperty(id))
4688:                    throw new PermissionException(SessionManager
4689:                            .getCurrentSessionUserId(),
4690:                            AUTH_RESOURCE_WRITE_ANY, getReference(id));
4691:
4692:                boolean collectionHint = id.endsWith(Entity.SEPARATOR);
4693:                Edit o = null;
4694:                if (collectionHint) {
4695:                    o = editCollection(id);
4696:                } else {
4697:                    o = editResource(id);
4698:                }
4699:
4700:                // unlikely, but...
4701:                if (o == null)
4702:                    throw new IdUnusedException(id);
4703:
4704:                // get the properties
4705:                ResourcePropertiesEdit props = o.getPropertiesEdit();
4706:
4707:                // check for TypeException updating live properties
4708:                if (props.isLiveProperty(name))
4709:                    throw new TypeException(name);
4710:
4711:                // add the property
4712:                props.addProperty(name, value);
4713:
4714:                // commit the change
4715:                if (o instanceof  ContentResourceEdit) {
4716:                    commitResourceEdit((ContentResourceEdit) o,
4717:                            NotificationService.NOTI_NONE);
4718:                }
4719:                if (o instanceof  ContentCollectionEdit) {
4720:                    commitCollection((ContentCollectionEdit) o);
4721:                }
4722:
4723:                return props;
4724:
4725:            } // addProperty
4726:
4727:            /**
4728:             * check permissions for removeProperty().
4729:             * 
4730:             * @param id
4731:             *        The id of the new resource.
4732:             * @return true if the user is allowed to removeProperty(id), false if not.
4733:             */
4734:            public boolean allowRemoveProperty(String id) {
4735:                boolean isAllowed = allowUpdate(id);
4736:
4737:                if (isAllowed) {
4738:                    try {
4739:                        checkExplicitLock(id);
4740:                    } catch (PermissionException e) {
4741:                        isAllowed = false;
4742:                    }
4743:                }
4744:
4745:                return isAllowed;
4746:
4747:            } // allowRemoveProperty
4748:
4749:            /**
4750:             * Remove a property from a resource, either collection or resource.
4751:             * 
4752:             * @param id
4753:             *        The resource id.
4754:             * @param name
4755:             *        The property name to be removed from the resource.
4756:             * @exception PermissionException
4757:             *            if the user does not have premissions to write properties on this object or read through containing collections.
4758:             * @exception IdUnusedException
4759:             *            if the resource id is not found.
4760:             * @exception TypeException
4761:             *            if the property named cannot be removed.
4762:             * @exception InUseException
4763:             *            if the resource is locked by someone else.
4764:             * @exception ServerOverloadException
4765:             *            if the server is configured to write the resource body to the filesystem and the save fails.
4766:             * @return the ResourceProperties object for this resource.
4767:             */
4768:            public ResourceProperties removeProperty(String id, String name)
4769:                    throws PermissionException, IdUnusedException,
4770:                    TypeException, InUseException, ServerOverloadException {
4771:                // check security 
4772:                checkExplicitLock(id);
4773:                if (!allowRemoveProperty(id))
4774:                    throw new PermissionException(SessionManager
4775:                            .getCurrentSessionUserId(),
4776:                            AUTH_RESOURCE_WRITE_ANY, getReference(id));
4777:
4778:                boolean collectionHint = id.endsWith(Entity.SEPARATOR);
4779:                Edit o = null;
4780:                if (collectionHint) {
4781:                    o = editCollection(id);
4782:                } else {
4783:                    o = editResource(id);
4784:                }
4785:
4786:                // unlikely, but...
4787:                if (o == null)
4788:                    throw new IdUnusedException(id);
4789:
4790:                // get the properties
4791:                ResourcePropertiesEdit props = o.getPropertiesEdit();
4792:
4793:                // check for TypeException updating live properties
4794:                if (props.isLiveProperty(name))
4795:                    throw new TypeException(name);
4796:
4797:                // remove the property
4798:                props.removeProperty(name);
4799:
4800:                // commit the change
4801:                if (o instanceof  ContentResourceEdit) {
4802:                    commitResourceEdit((ContentResourceEdit) o,
4803:                            NotificationService.NOTI_NONE);
4804:                }
4805:                if (o instanceof  ContentCollectionEdit) {
4806:                    commitCollection((ContentCollectionEdit) o);
4807:                }
4808:
4809:                return props;
4810:
4811:            } // removeProperty
4812:
4813:            /**
4814:             * Access the resource URL from a resource id.
4815:             * 
4816:             * @param id
4817:             *        The resource id.
4818:             * @return The resource URL.
4819:             */
4820:            public String getUrl(String id) {
4821:                // escape just the is part, not the access point
4822:                return getUrl(id, PROP_ALTERNATE_REFERENCE); // getAccessPoint(false) + Validator.escapeUrl(id);
4823:
4824:            } // getUrl
4825:
4826:            /**
4827:             * Access the alternate URL which can be used to access the entity.
4828:             * 
4829:             * @param id
4830:             *        The resource id.
4831:             * @param rootProperty
4832:             *        The name of the entity property whose value controls which alternate reference URL is requested. If null, the native 'raw' URL is requested.
4833:             * @return The resource URL.
4834:             */
4835:            public String getUrl(String id, String rootProperty) {
4836:                // escape just the is part, not the access point
4837:                // return getAccessPoint(false) + Validator.escapeUrl(id);
4838:                return m_serverConfigurationService.getAccessUrl()
4839:                        + getAlternateReferenceRoot(id, rootProperty)
4840:                        + m_relativeAccessPoint
4841:                        + Validator.escapeUrl(convertIdToUserEid(id));
4842:
4843:            } // getUrl
4844:
4845:            /**
4846:             * Compute an alternate root for a reference, based on the value of the specified property.
4847:             * 
4848:             * @param rootProperty
4849:             *        The property name.
4850:             * @return The alternate root, or "" if there is none.
4851:             */
4852:            protected String getAlternateReferenceRoot(String id,
4853:                    String rootProperty) {
4854:                // null means don't do this
4855:                if (rootProperty == null)
4856:                    return "";
4857:
4858:                // if id is missing or blank or root, skip as well
4859:                if ((id == null) || id.equals("/") || id.equals(""))
4860:                    return "";
4861:
4862:                // find the property - "" if not found
4863:                String alternateRoot = null;
4864:                try {
4865:                    // TODO: Can this be done without a security check??
4866:                    // findResource(id).getProperties().getProperty(...) ??
4867:                    alternateRoot = StringUtil.trimToNull(getProperties(id)
4868:                            .getProperty(rootProperty));
4869:                } catch (PermissionException e) {
4870:                    // ignore
4871:                } catch (IdUnusedException e) {
4872:                    // ignore
4873:                }
4874:                if (alternateRoot == null)
4875:                    return "";
4876:
4877:                // make sure it start with a separator and does not end with one
4878:                if (!alternateRoot.startsWith(Entity.SEPARATOR))
4879:                    alternateRoot = Entity.SEPARATOR + alternateRoot;
4880:                if (alternateRoot.endsWith(Entity.SEPARATOR))
4881:                    alternateRoot = alternateRoot.substring(0, alternateRoot
4882:                            .length()
4883:                            - Entity.SEPARATOR.length());
4884:
4885:                return alternateRoot;
4886:            }
4887:
4888:            /**
4889:             * Access the internal reference from a resource id.
4890:             * 
4891:             * @param id
4892:             *        The resource id.
4893:             * @return The internal reference from a resource id.
4894:             */
4895:            public String getReference(String id) {
4896:                return getAccessPoint(true) + id;
4897:
4898:            } // getReference
4899:
4900:            /**
4901:             * Access the resource id of the collection which contains this collection or resource.
4902:             * 
4903:             * @param id
4904:             *        The resource id (reference, or URL) of the ContentCollection or ContentResource
4905:             * @return the resource id (reference, or URL, depending on the id parameter) of the collection which contains this resource.
4906:             */
4907:            public String getContainingCollectionId(String id) {
4908:                return isolateContainingId(id);
4909:
4910:            } // getContainingCollectionId
4911:
4912:            /**
4913:             * Get the depth of the resource/collection object in the hireachy based on the given collection id
4914:             * 
4915:             * @param resourceId
4916:             *        The Id of the resource/collection object to be tested
4917:             * @param baseCollectionId
4918:             *        The Id of the collection as the relative root level
4919:             * @return the integer value reflecting the relative hierarchy depth of the test resource/collection object based on the given base collection level
4920:             */
4921:            public int getDepth(String resourceId, String baseCollectionId) {
4922:                if (resourceId.indexOf(baseCollectionId) == -1) {
4923:                    // the resource object is not a member of base collection
4924:                    return -1;
4925:                } else {
4926:                    int i = 1;
4927:                    // the resource object is a member of base collection
4928:                    String s = resourceId.substring(baseCollectionId.length());
4929:                    while (s.indexOf(Entity.SEPARATOR) != -1) {
4930:                        if (s.indexOf(Entity.SEPARATOR) != (s.length() - 1)) {
4931:                            // the resource seperator character is not the last character
4932:                            i++;
4933:                            s = s.substring(s.indexOf(Entity.SEPARATOR) + 1);
4934:                        } else {
4935:                            s = "";
4936:                        }
4937:                    }
4938:                    return i;
4939:                }
4940:
4941:            } // getDepth
4942:
4943:            /**
4944:             * Test if this id (reference, or URL) refers to the root collection.
4945:             * 
4946:             * @param id
4947:             *        The resource id (reference, or URL) of a ContentCollection
4948:             * @return true if this is the root collection
4949:             */
4950:            public boolean isRootCollection(String id) {
4951:                boolean rv = false;
4952:
4953:                // test for the root local id
4954:                if (id.equals("/")) {
4955:                    rv = true;
4956:                }
4957:
4958:                // test for the root reference
4959:                else if (id.equals(getAccessPoint(true) + "/")) {
4960:                    rv = true;
4961:                }
4962:
4963:                // test for the root URL
4964:                else if (id.equals(getAccessPoint(false) + "/")) {
4965:                    rv = true;
4966:                }
4967:
4968:                // if (M_log.isDebugEnabled()) M_log.debug("isRootCollection: id: " + id + " rv: " + rv);
4969:
4970:                return rv;
4971:
4972:            } // isRootCollection
4973:
4974:            /**
4975:             * Construct a stand-alone, not associated with any particular resource, ResourceProperties object.
4976:             * 
4977:             * @return The new ResourceProperties object.
4978:             */
4979:            public ResourcePropertiesEdit newResourceProperties() {
4980:                return new BaseResourcePropertiesEdit();
4981:
4982:            } // newResourceProperties
4983:
4984:            /**
4985:             * @inheritDoc
4986:             */
4987:            public Comparator newContentHostingComparator(String property,
4988:                    boolean ascending) {
4989:                return new ContentHostingComparator(property, ascending);
4990:            }
4991:
4992:            /**
4993:             * Return a map of Worksite collections roots that the user has access to.
4994:             * 
4995:             * @return Map of worksite resource root id (String) to worksite title (String)
4996:             */
4997:            public Map getCollectionMap() {
4998:                // the return map
4999:                Map rv = new HashMap();
5000:
5001:                // get the sites the user has access to
5002:                List mySites = m_siteService
5003:                        .getSites(
5004:                                org.sakaiproject.site.api.SiteService.SelectionType.ACCESS,
5005:                                null,
5006:                                null,
5007:                                null,
5008:                                org.sakaiproject.site.api.SiteService.SortType.TITLE_ASC,
5009:                                null);
5010:
5011:                // add in the user's myworkspace site, if we can find it
5012:                try {
5013:                    mySites.add(m_siteService.getSite(m_siteService
5014:                            .getUserSiteId(SessionManager
5015:                                    .getCurrentSessionUserId())));
5016:                } catch (IdUnusedException e) {
5017:                }
5018:
5019:                // check each one for dropbox and resources
5020:                for (Iterator i = mySites.iterator(); i.hasNext();) {
5021:                    Site site = (Site) i.next();
5022:
5023:                    // test dropbox
5024:                    if (site.getToolForCommonId("sakai.dropbox") != null) {
5025:                        String collectionId = getDropboxCollection(site.getId());
5026:                        String title = site.getTitle() + " "
5027:                                + rb.getString("gen.drop");
5028:                        rv.put(collectionId, title);
5029:                    }
5030:
5031:                    // test resources
5032:                    if (site.getToolForCommonId("sakai.resources") != null) {
5033:
5034:                        String collectionId = getSiteCollection(site.getId());
5035:                        String title = site.getTitle() + " "
5036:                                + rb.getString("gen.reso");
5037:                        rv.put(collectionId, title);
5038:                    }
5039:                }
5040:
5041:                return rv;
5042:            }
5043:
5044:            /**********************************************************************************************************************************************************************************************************************************************************
5045:             * EntityProducer implementation
5046:             *********************************************************************************************************************************************************************************************************************************************************/
5047:
5048:            /**
5049:             * {@inheritDoc}
5050:             */
5051:            public String getLabel() {
5052:                return "content";
5053:            }
5054:
5055:            /**
5056:             * {@inheritDoc}
5057:             */
5058:            public boolean willArchiveMerge() {
5059:                return true;
5060:            }
5061:
5062:            /** stream content requests if true, read all into memory and send if false. */
5063:            protected static final boolean STREAM_CONTENT = true;
5064:
5065:            /** The chunk size used when streaming (100k). */
5066:            protected static final int STREAM_BUFFER_SIZE = 102400;
5067:
5068:            /**
5069:             * Process the access request for a resource.
5070:             * 
5071:             * @param req
5072:             * @param res
5073:             * @param ref
5074:             * @param copyrightAcceptedRefs
5075:             * @throws PermissionException
5076:             * @throws IdUnusedException
5077:             * @throws ServerOverloadException
5078:             * @throws CopyrightException
5079:             */
5080:            protected void handleAccessResource(HttpServletRequest req,
5081:                    HttpServletResponse res, Reference ref,
5082:                    Collection copyrightAcceptedRefs)
5083:                    throws EntityPermissionException,
5084:                    EntityNotDefinedException, EntityAccessOverloadException,
5085:                    EntityCopyrightException {
5086:                // we only access resources, not collections
5087:                if (ref.getId().endsWith(Entity.SEPARATOR))
5088:                    throw new EntityNotDefinedException(ref.getReference());
5089:
5090:                // need read permission
5091:                if (!allowGetResource(ref.getId()))
5092:                    throw new EntityPermissionException(SessionManager
5093:                            .getCurrentSessionUserId(), AUTH_RESOURCE_READ, ref
5094:                            .getReference());
5095:
5096:                BaseResourceEdit resource = null;
5097:                try {
5098:                    resource = (BaseResourceEdit) getResource(ref.getId());
5099:                } catch (IdUnusedException e) {
5100:                    throw new EntityNotDefinedException(e.getId());
5101:                } catch (PermissionException e) {
5102:                    throw new EntityPermissionException(e.getUser(), e
5103:                            .getLock(), e.getResource());
5104:                } catch (TypeException e) {
5105:                    throw new EntityNotDefinedException(ref.getReference());
5106:                }
5107:
5108:                // if this entity requires a copyright agreement, and has not yet been set, get one
5109:                if (resource.requiresCopyrightAgreement()
5110:                        && !copyrightAcceptedRefs.contains(resource
5111:                                .getReference())) {
5112:                    throw new EntityCopyrightException(ref.getReference());
5113:                }
5114:
5115:                try {
5116:                    // changed to int from long because res.setContentLength won't take long param -- JE
5117:                    int len = resource.getContentLength();
5118:                    String contentType = resource.getContentType();
5119:
5120:                    // for url content type, encode a redirect to the body URL
5121:                    if (contentType
5122:                            .equalsIgnoreCase(ResourceProperties.TYPE_URL)) {
5123:                        byte[] content = resource.getContent();
5124:                        if ((content == null) || (content.length == 0)) {
5125:                            throw new IdUnusedException(ref.getReference());
5126:                        }
5127:
5128:                        String one = new String(content);
5129:                        String two = "";
5130:                        for (int i = 0; i < one.length(); i++) {
5131:                            if (one.charAt(i) == '+') {
5132:                                two += "%2b";
5133:                            } else {
5134:                                two += one.charAt(i);
5135:                            }
5136:                        }
5137:                        res.sendRedirect(two);
5138:                    }
5139:
5140:                    else {
5141:                        // use the last part, the file name part of the id, for the download file name
5142:                        String fileName = Validator.getFileName(ref.getId());
5143:                        fileName = Validator.escapeResourceName(fileName);
5144:
5145:                        String disposition = null;
5146:                        if (Validator.letBrowserInline(contentType)) {
5147:                            disposition = "inline; filename=\"" + fileName
5148:                                    + "\"";
5149:                        } else {
5150:                            disposition = "attachment; filename=\"" + fileName
5151:                                    + "\"";
5152:                        }
5153:
5154:                        // NOTE: Only set the encoding on the content we have to.
5155:                        // Files uploaded by the user may have been created with different encodings, such as ISO-8859-1;
5156:                        // rather than (sometimes wrongly) saying its UTF-8, let the browser auto-detect the encoding.
5157:                        // If the content was created through the WYSIWYG editor, the encoding does need to be set (UTF-8).
5158:                        String encoding = resource.getProperties().getProperty(
5159:                                ResourceProperties.PROP_CONTENT_ENCODING);
5160:                        if (encoding != null && encoding.length() > 0) {
5161:                            contentType = contentType + "; charset=" + encoding;
5162:                        }
5163:
5164:                        // stream the content using a small buffer to keep memory managed
5165:                        if (STREAM_CONTENT) {
5166:                            InputStream content = null;
5167:                            OutputStream out = null;
5168:
5169:                            try {
5170:                                content = resource.streamContent();
5171:                                if (content == null) {
5172:                                    throw new IdUnusedException(ref
5173:                                            .getReference());
5174:                                }
5175:
5176:                                res.setContentType(contentType);
5177:                                res.addHeader("Content-Disposition",
5178:                                        disposition);
5179:                                res.setContentLength(len);
5180:
5181:                                // set the buffer of the response to match what we are reading from the request
5182:                                if (len < STREAM_BUFFER_SIZE) {
5183:                                    res.setBufferSize(len);
5184:                                } else {
5185:                                    res.setBufferSize(STREAM_BUFFER_SIZE);
5186:                                }
5187:
5188:                                out = res.getOutputStream();
5189:
5190:                                // chunk
5191:                                byte[] chunk = new byte[STREAM_BUFFER_SIZE];
5192:                                int lenRead;
5193:                                while ((lenRead = content.read(chunk)) != -1) {
5194:                                    out.write(chunk, 0, lenRead);
5195:                                }
5196:                            } catch (ServerOverloadException e) {
5197:                                throw e;
5198:                            } catch (Throwable ignore) {
5199:                            } finally {
5200:                                // be a good little program and close the stream - freeing up valuable system resources
5201:                                if (content != null) {
5202:                                    content.close();
5203:                                }
5204:
5205:                                if (out != null) {
5206:                                    try {
5207:                                        out.close();
5208:                                    } catch (Throwable ignore) {
5209:                                    }
5210:                                }
5211:                            }
5212:                        }
5213:
5214:                        // read the entire content into memory and send it from there
5215:                        else {
5216:                            byte[] content = resource.getContent();
5217:                            if (content == null) {
5218:                                throw new IdUnusedException(ref.getReference());
5219:                            }
5220:
5221:                            res.setContentType(contentType);
5222:                            res.addHeader("Content-Disposition", disposition);
5223:                            res.setContentLength(len);
5224:
5225:                            // Increase the buffer size for more speed. - don't - we don't want a 20 meg buffer size,right? -ggolden
5226:                            // res.setBufferSize(len);
5227:
5228:                            OutputStream out = null;
5229:                            try {
5230:                                out = res.getOutputStream();
5231:                                out.write(content);
5232:                                out.flush();
5233:                                out.close();
5234:                            } catch (Throwable ignore) {
5235:                            } finally {
5236:                                if (out != null) {
5237:                                    try {
5238:                                        out.close();
5239:                                    } catch (Throwable ignore) {
5240:                                    }
5241:                                }
5242:                            }
5243:                        }
5244:                    }
5245:
5246:                    // track event
5247:                    EventTrackingService.post(EventTrackingService
5248:                            .newEvent(EVENT_RESOURCE_READ, resource
5249:                                    .getReference(), false));
5250:                } catch (Throwable t) {
5251:                    throw new EntityNotDefinedException(ref.getReference());
5252:                }
5253:            }
5254:
5255:            /**
5256:             * Process the access request for a collection, producing the "apache" style HTML file directory listing (complete with index.html redirect if found).
5257:             * 
5258:             * @param req
5259:             * @param res
5260:             * @param ref
5261:             * @param copyrightAcceptedRefs
5262:             * @throws PermissionException
5263:             * @throws IdUnusedException
5264:             * @throws ServerOverloadException
5265:             */
5266:            protected void handleAccessCollection(HttpServletRequest req,
5267:                    HttpServletResponse res, Reference ref,
5268:                    Collection copyrightAcceptedRefs)
5269:                    throws EntityPermissionException,
5270:                    EntityNotDefinedException, EntityAccessOverloadException,
5271:                    EntityCopyrightException {
5272:                // we only access resources, not collections
5273:                if (!ref.getId().endsWith(Entity.SEPARATOR))
5274:                    throw new EntityNotDefinedException(ref.getReference());
5275:
5276:                // first, check for an index.html in the collection - redirect here if found
5277:                if (m_storage.checkResource(ref.getId() + "index.html")) {
5278:                    String addr = Web.returnUrl(req, req.getPathInfo())
5279:                            + "index.html";
5280:                    try {
5281:                        res.sendRedirect(addr);
5282:                        return;
5283:                    } catch (IOException e) {
5284:                        M_log.warn("handleAccessCollection: redirecting to "
5285:                                + addr + " : " + e);
5286:                    }
5287:                }
5288:
5289:                // need read permission
5290:                if (!allowGetResource(ref.getId()))
5291:                    throw new EntityPermissionException(SessionManager
5292:                            .getCurrentSessionUserId(), AUTH_RESOURCE_READ, ref
5293:                            .getReference());
5294:
5295:                BaseCollectionEdit collection = null;
5296:                try {
5297:                    collection = (BaseCollectionEdit) getCollection(ref.getId());
5298:                } catch (IdUnusedException e) {
5299:                    throw new EntityNotDefinedException(e.getId());
5300:                } catch (PermissionException e) {
5301:                    throw new EntityPermissionException(e.getUser(), e
5302:                            .getLock(), e.getResource());
5303:                } catch (TypeException e) {
5304:                    throw new EntityNotDefinedException(ref.getReference());
5305:                }
5306:
5307:                try {
5308:                    // use the helper
5309:                    CollectionAccessFormatter.format(collection, ref, req, res,
5310:                            getAccessPoint(true), getAccessPoint(false));
5311:
5312:                    // track event
5313:                    // EventTrackingService.post(EventTrackingService.newEvent(EVENT_RESOURCE_READ, collection.getReference(), false));
5314:                } catch (Throwable t) {
5315:                    throw new EntityNotDefinedException(ref.getReference());
5316:                }
5317:            }
5318:
5319:            /**
5320:             * {@inheritDoc}
5321:             */
5322:            public HttpAccess getHttpAccess() {
5323:                return new HttpAccess() {
5324:                    public void handleAccess(HttpServletRequest req,
5325:                            HttpServletResponse res, Reference ref,
5326:                            Collection copyrightAcceptedRefs)
5327:                            throws EntityPermissionException,
5328:                            EntityNotDefinedException,
5329:                            EntityAccessOverloadException,
5330:                            EntityCopyrightException {
5331:                        // if the id is null, the request was for just ".../content"
5332:                        String refId = ref.getId();
5333:                        if (refId == null)
5334:                            refId = "";
5335:
5336:                        // test if the given reference is a resource
5337:                        if (m_storage.checkResource(refId)) {
5338:                            handleAccessResource(req, res, ref,
5339:                                    copyrightAcceptedRefs);
5340:                            return;
5341:                        }
5342:
5343:                        // test for a collection
5344:                        if (m_storage.checkCollection(refId)) {
5345:                            handleAccessCollection(req, res, ref,
5346:                                    copyrightAcceptedRefs);
5347:                            return;
5348:                        }
5349:
5350:                        // finally, try a collection that was missing it's final separator
5351:                        if (!refId.endsWith(Entity.SEPARATOR)) {
5352:                            // would it be a collection if we added the missing separator?
5353:                            if (m_storage.checkCollection(refId
5354:                                    + Entity.SEPARATOR)) {
5355:                                // redirect to this
5356:                                // Note: if the request had no trailing separator, getPathInfo still returns "/" - avoid ending up with "...//" -ggolden
5357:                                String addr = Web.returnUrl(req, req
5358:                                        .getPathInfo())
5359:                                        + ("/".equals(req.getPathInfo()) ? ""
5360:                                                : Entity.SEPARATOR);
5361:                                try {
5362:                                    res.sendRedirect(addr);
5363:                                    return;
5364:                                } catch (IOException e) {
5365:                                    M_log.warn("handleAccess: redirecting to "
5366:                                            + addr + " : " + e);
5367:                                    throw new EntityNotDefinedException(ref
5368:                                            .getReference());
5369:                                }
5370:                            }
5371:
5372:                            // nothing we know of...
5373:                            throw new EntityNotDefinedException(ref
5374:                                    .getReference());
5375:                        }
5376:                    }
5377:                };
5378:            }
5379:
5380:            /**
5381:             * {@inheritDoc}
5382:             */
5383:            public Entity getEntity(Reference ref) {
5384:                // double check that it's mine
5385:                if (APPLICATION_ID != ref.getType())
5386:                    return null;
5387:
5388:                Entity rv = null;
5389:
5390:                ResourceProperties props = null;
5391:                try {
5392:                    props = getProperties(ref.getId());
5393:                    boolean isCollection = false;
5394:                    try {
5395:                        isCollection = props
5396:                                .getBooleanProperty(ResourceProperties.PROP_IS_COLLECTION);
5397:                    } catch (EntityPropertyNotDefinedException ignore) {
5398:                        // do nothing -- it's not a collection unless PROP_IS_COLLECTION is defined 
5399:                    } catch (EntityPropertyTypeException e) {
5400:                        // Log this and assume it's not a collection
5401:                        M_log
5402:                                .warn("EntityPropertyTypeException: PROP_IS_COLLECTION not boolean for "
5403:                                        + ref.getReference());
5404:                    }
5405:                    if (isCollection) {
5406:                        try {
5407:                            rv = getCollection(ref.getId());
5408:                        } catch (TypeException e) {
5409:                            // in that case try to get it as a resource
5410:                            rv = getResource(ref.getId());
5411:                        }
5412:                    } else {
5413:                        try {
5414:                            rv = getResource(ref.getId());
5415:                        } catch (TypeException e) {
5416:                            // in that case try to get it as a collection
5417:                            rv = getCollection(ref.getId());
5418:                        }
5419:                    }
5420:                } catch (PermissionException e) {
5421:                    M_log.warn("PermissionException " + ref.getReference());
5422:                } catch (IdUnusedException e) {
5423:                    M_log.warn("IdUnusedException " + ref.getReference());
5424:                } catch (TypeException e) {
5425:                    // TODO Auto-generated catch block
5426:                    M_log.warn("TypeException " + ref.getReference());
5427:                }
5428:
5429:                return rv;
5430:            }
5431:
5432:            /**
5433:             * {@inheritDoc}
5434:             */
5435:            public String getEntityUrl(Reference ref) {
5436:                // double check that it's mine
5437:                if (APPLICATION_ID != ref.getType())
5438:                    return null;
5439:
5440:                return getUrl(convertIdToUserEid(ref.getId()));
5441:            }
5442:
5443:            /**
5444:             * {@inheritDoc}
5445:             */
5446:            public Collection getEntityAuthzGroups(Reference ref, String userId) {
5447:                // double check that it's mine
5448:                if (APPLICATION_ID != ref.getType())
5449:                    return null;
5450:
5451:                // form a key for thread-local caching
5452:                String threadLocalKey = "getEntityAuthzGroups@" + userId + "@"
5453:                        + ref.getReference();
5454:                Collection rv = (Collection) ThreadLocalManager
5455:                        .get(threadLocalKey);
5456:                if (rv != null) {
5457:                    return new Vector(rv);
5458:                }
5459:
5460:                // use the resources realm, all container (folder) realms
5461:
5462:                rv = new Vector();
5463:                rv.addAll(getEntityHierarchyAuthzGroups(ref));
5464:
5465:                try {
5466:                    boolean isDropbox = false;
5467:                    // special check for group-user : the grant's in the user's My Workspace site
5468:                    String parts[] = StringUtil.split(ref.getId(),
5469:                            Entity.SEPARATOR);
5470:                    if ((parts.length > 3) && (parts[1].equals("group-user"))) {
5471:                        rv.add(m_siteService.siteReference(m_siteService
5472:                                .getUserSiteId(parts[3])));
5473:                        isDropbox = true;
5474:                    }
5475:
5476:                    ContentEntity entity = null;
5477:                    if (ref.getId().endsWith(Entity.SEPARATOR)) {
5478:                        entity = findCollection(ref.getId());
5479:                    } else {
5480:                        entity = findResource(ref.getId());
5481:                    }
5482:                    if (entity == null) {
5483:                        String refId = ref.getId();
5484:                        while (entity == null && refId != null) {
5485:                            refId = isolateContainingId(refId);
5486:                            entity = findCollection(refId);
5487:                        }
5488:                    }
5489:
5490:                    boolean inherited = false;
5491:                    AccessMode access = entity.getAccess();
5492:
5493:                    if (AccessMode.INHERITED.equals(access)) {
5494:                        inherited = true;
5495:                        access = entity.getInheritedAccess();
5496:                    }
5497:                    if (isDropbox || AccessMode.SITE == access
5498:                            || AccessMode.INHERITED == access) {
5499:                        // site
5500:                        ref.addSiteContextAuthzGroup(rv);
5501:                    } else if (AccessMode.GROUPED.equals(access)) {
5502:                        Site site = m_siteService.getSite(ref.getContext());
5503:                        boolean useSiteAsContext = false;
5504:                        if (site != null && userId != null) {
5505:                            useSiteAsContext = site.isAllowed(userId,
5506:                                    AUTH_RESOURCE_ALL_GROUPS);
5507:                        }
5508:                        if (useSiteAsContext) {
5509:                            ref.addSiteContextAuthzGroup(rv);
5510:                        } else if (inherited) {
5511:                            rv.addAll(entity.getInheritedGroups());
5512:                        } else {
5513:                            rv.addAll(entity.getGroups());
5514:                        }
5515:                    }
5516:                } catch (Throwable e) {
5517:                }
5518:
5519:                // cache in the thread
5520:                ThreadLocalManager.set(threadLocalKey, new Vector(rv));
5521:
5522:                if (M_log.isDebugEnabled()) {
5523:                    M_log.debug("getEntityAuthzGroups for: ref: "
5524:                            + ref.getReference() + " user: " + userId);
5525:                    for (Iterator i = rv.iterator(); i.hasNext();) {
5526:                        M_log.debug("** -- " + i.next());
5527:                    }
5528:                }
5529:                return rv;
5530:            }
5531:
5532:            protected Collection getEntityHierarchyAuthzGroups(Reference ref) {
5533:                Collection rv = new TreeSet();
5534:
5535:                // add the root
5536:                rv.add(getReference("/"));
5537:
5538:                // try the resource, all the folders above it (don't include /)
5539:                String paths[] = StringUtil
5540:                        .split(ref.getId(), Entity.SEPARATOR);
5541:                boolean container = ref.getId().endsWith(Entity.SEPARATOR);
5542:                if (paths.length > 1) {
5543:                    String root = getReference(Entity.SEPARATOR + paths[1]
5544:                            + Entity.SEPARATOR);
5545:                    rv.add(root);
5546:
5547:                    for (int next = 2; next < paths.length; next++) {
5548:                        root += paths[next];
5549:                        if ((next < paths.length - 1) || container) {
5550:                            root += Entity.SEPARATOR;
5551:                        }
5552:                        rv.add(root);
5553:                    }
5554:                }
5555:
5556:                return rv;
5557:            }
5558:
5559:            /**
5560:             * {@inheritDoc}
5561:             */
5562:            public String archive(String siteId, Document doc, Stack stack,
5563:                    String archivePath, List attachments) {
5564:                // prepare the buffer for the results log
5565:                StringBuffer results = new StringBuffer();
5566:
5567:                // start with an element with our very own name
5568:                Element element = doc.createElement(ContentHostingService.class
5569:                        .getName());
5570:                ((Element) stack.peek()).appendChild(element);
5571:                stack.push(element);
5572:
5573:                // the root collection for the site
5574:                String siteCollectionId = getSiteCollection(siteId);
5575:
5576:                try {
5577:                    // get the collection for the site
5578:                    ContentCollection collection = getCollection(siteCollectionId);
5579:
5580:                    archiveCollection(collection, doc, stack, archivePath,
5581:                            siteCollectionId, results);
5582:                } catch (Exception any) {
5583:                    results.append("Error archiving collection from site: "
5584:                            + siteId + " " + any.toString() + "\n");
5585:                }
5586:
5587:                stack.pop();
5588:
5589:                return results.toString();
5590:
5591:            } // archive
5592:
5593:            /**
5594:             * {@inheritDoc}
5595:             */
5596:            public String archiveResources(List attachments, Document doc,
5597:                    Stack stack, String archivePath) {
5598:                // prepare the buffer for the results log
5599:                StringBuffer results = new StringBuffer();
5600:
5601:                // start with an element with our very own name
5602:                Element element = doc.createElement(APPLICATION_ID);
5603:                ((Element) stack.peek()).appendChild(element);
5604:                stack.push(element);
5605:
5606:                for (Iterator i = attachments.iterator(); i.hasNext();) {
5607:                    Reference ref = (Reference) i.next();
5608:                    try {
5609:                        ContentResource resource = (ContentResource) ref
5610:                                .getEntity();
5611:
5612:                        if (resource != null) {
5613:                            results.append(archiveResource(resource, doc,
5614:                                    stack, archivePath, null));
5615:                        }
5616:                    } catch (Exception any) {
5617:                        results.append("Error archiving resource: " + ref + " "
5618:                                + any.toString() + "\n");
5619:                        M_log.warn(
5620:                                "archveResources: exception archiving resource: "
5621:                                        + ref + ": ", any);
5622:                    }
5623:                }
5624:
5625:                stack.pop();
5626:
5627:                return results.toString();
5628:            }
5629:
5630:            /**
5631:             * Replace the WT user id with the new qualified id
5632:             * 
5633:             * @param el
5634:             *        The XML element holding the perproties
5635:             * @param useIdTrans
5636:             *        The HashMap to track old WT id to new CTools id
5637:             */
5638:            protected void WTUserIdTrans(Element el, Map userIdTrans) {
5639:                NodeList children4 = el.getChildNodes();
5640:                int length4 = children4.getLength();
5641:                for (int i4 = 0; i4 < length4; i4++) {
5642:                    Node child4 = children4.item(i4);
5643:                    if (child4.getNodeType() == Node.ELEMENT_NODE) {
5644:                        Element element4 = (Element) child4;
5645:                        if (element4.getTagName().equals("property")) {
5646:                            String creatorId = "";
5647:                            String modifierId = "";
5648:                            if (element4.hasAttribute("CHEF:creator")) {
5649:                                if ("BASE64".equalsIgnoreCase(element4
5650:                                        .getAttribute("enc"))) {
5651:                                    creatorId = Xml.decodeAttribute(element4,
5652:                                            "CHEF:creator");
5653:                                } else {
5654:                                    creatorId = element4
5655:                                            .getAttribute("CHEF:creator");
5656:                                }
5657:                                String newCreatorId = (String) userIdTrans
5658:                                        .get(creatorId);
5659:                                if (newCreatorId != null) {
5660:                                    Xml.encodeAttribute(element4,
5661:                                            "CHEF:creator", newCreatorId);
5662:                                    element4.setAttribute("enc", "BASE64");
5663:                                }
5664:                            } else if (element4.hasAttribute("CHEF:modifiedby")) {
5665:                                if ("BASE64".equalsIgnoreCase(element4
5666:                                        .getAttribute("enc"))) {
5667:                                    modifierId = Xml.decodeAttribute(element4,
5668:                                            "CHEF:modifiedby");
5669:                                } else {
5670:                                    modifierId = element4
5671:                                            .getAttribute("CHEF:modifiedby");
5672:                                }
5673:                                String newModifierId = (String) userIdTrans
5674:                                        .get(modifierId);
5675:                                if (newModifierId != null) {
5676:                                    Xml.encodeAttribute(element4,
5677:                                            "CHEF:creator", newModifierId);
5678:                                    element4.setAttribute("enc", "BASE64");
5679:                                }
5680:                            }
5681:                        }
5682:                    }
5683:                }
5684:
5685:            } // WTUserIdTrans
5686:
5687:            /**
5688:             * Merge the resources from the archive into the given site.
5689:             * 
5690:             * @param siteId
5691:             *        The id of the site getting imported into.
5692:             * @param root
5693:             *        The XML DOM tree of content to merge.
5694:             * @param archviePath
5695:             *        The path to the folder where we are reading auxilary files.
5696:             * @return A log of status messages from the archive.
5697:             */
5698:            public String merge(String siteId, Element root,
5699:                    String archivePath, String mergeId, Map attachmentNames,
5700:                    Map userIdTrans, Set userListAllowImport) {
5701:                // get the system name: FROM_WT, FROM_CT, FROM_SAKAI
5702:                String source = "";
5703:                // root: <service> node
5704:                Node parent = root.getParentNode(); // parent: <archive> node containing "system"
5705:                if (parent.getNodeType() == Node.ELEMENT_NODE) {
5706:                    Element parentEl = (Element) parent;
5707:                    source = parentEl.getAttribute("system");
5708:                }
5709:
5710:                // prepare the buffer for the results log
5711:                StringBuffer results = new StringBuffer();
5712:
5713:                try {
5714:                    NodeList children = root.getChildNodes();
5715:                    final int length = children.getLength();
5716:                    for (int i = 0; i < length; i++) {
5717:                        Node child = children.item(i);
5718:                        if (child.getNodeType() != Node.ELEMENT_NODE)
5719:                            continue;
5720:                        Element element = (Element) child;
5721:
5722:                        // for "collection" kids
5723:                        if (element.getTagName().equals("collection")) {
5724:                            // replace the WT userid when needed
5725:                            if (!userIdTrans.isEmpty()) {
5726:                                // replace the WT user id with new user id
5727:                                WTUserIdTrans(element, userIdTrans);
5728:                            }
5729:
5730:                            // from the relative id form a full id for the target site,
5731:                            // updating the xml element
5732:                            String relId = StringUtil.trimToNull(element
5733:                                    .getAttribute("rel-id"));
5734:                            if (relId == null) {
5735:                                // Note: the site's root collection will have a "" rel-id, which will be null.
5736:                                continue;
5737:                            }
5738:                            String id = getSiteCollection(siteId) + relId;
5739:                            element.setAttribute("id", id);
5740:
5741:                            // collection: add if missing, else merge in
5742:                            ContentCollection c = mergeCollection(element);
5743:                            if (c == null) {
5744:                                results
5745:                                        .append("collection: "
5746:                                                + id
5747:                                                + " already exists and was not replaced.\n");
5748:                            } else {
5749:                                results.append("collection: " + id
5750:                                        + " imported.\n");
5751:                            }
5752:                        }
5753:
5754:                        // for "resource" kids
5755:                        else if (element.getTagName().equals("resource")) {
5756:                            // a flag showing if continuing merging this resource
5757:                            boolean goAhead = true;
5758:
5759:                            // check if the person who last modified this source has the right role
5760:                            // if not, set the goAhead flag to be false when fromSakai or fromCT
5761:                            if (source.equalsIgnoreCase("Sakai 1.0")
5762:                                    || source.equalsIgnoreCase("CT")) {
5763:                                NodeList children2 = element.getChildNodes();
5764:                                int length2 = children2.getLength();
5765:                                for (int i2 = 0; i2 < length2; i2++) {
5766:                                    Node child2 = children2.item(i2);
5767:                                    if (child2.getNodeType() == Node.ELEMENT_NODE) {
5768:                                        Element element2 = (Element) child2;
5769:
5770:                                        // get the "channel" child
5771:                                        if (element2.getTagName().equals(
5772:                                                "properties")) {
5773:                                            NodeList children3 = element2
5774:                                                    .getChildNodes();
5775:                                            final int length3 = children3
5776:                                                    .getLength();
5777:                                            for (int i3 = 0; i3 < length3; i3++) {
5778:                                                Node child3 = children3
5779:                                                        .item(i3);
5780:                                                if (child3.getNodeType() == Node.ELEMENT_NODE) {
5781:                                                    Element element3 = (Element) child3;
5782:
5783:                                                    // for "message" children
5784:                                                    if (element3.getTagName()
5785:                                                            .equals("property")) {
5786:                                                        if (element3
5787:                                                                .getAttribute(
5788:                                                                        "name")
5789:                                                                .equalsIgnoreCase(
5790:                                                                        "CHEF:modifiedby")) {
5791:                                                            if ("BASE64"
5792:                                                                    .equalsIgnoreCase(element3
5793:                                                                            .getAttribute("enc"))) {
5794:                                                                String creatorId = Xml
5795:                                                                        .decodeAttribute(
5796:                                                                                element3,
5797:                                                                                "value");
5798:                                                                if (!userListAllowImport
5799:                                                                        .contains(creatorId))
5800:                                                                    goAhead = false;
5801:                                                            } else {
5802:                                                                String creatorId = element3
5803:                                                                        .getAttribute("value");
5804:                                                                if (!userListAllowImport
5805:                                                                        .contains(creatorId))
5806:                                                                    goAhead = false;
5807:                                                            }
5808:                                                        }
5809:                                                    }
5810:                                                }
5811:                                            }
5812:                                        }
5813:                                    }
5814:                                }
5815:                            } // the end to if fromSakai or fromCT
5816:
5817:                            if (goAhead) {
5818:                                // replace the WT userid when needed
5819:                                if (!userIdTrans.isEmpty()) {
5820:                                    // replace the WT user id with new user id
5821:                                    WTUserIdTrans(element, userIdTrans);
5822:                                }
5823:
5824:                                // from the relative id form a full id for the target site,
5825:                                // updating the xml element
5826:                                String id = StringUtil.trimToNull(element
5827:                                        .getAttribute("id"));
5828:                                String relId = StringUtil.trimToNull(element
5829:                                        .getAttribute("rel-id"));
5830:
5831:                                // escape the invalid characters
5832:                                id = Validator.escapeQuestionMark(id);
5833:                                relId = Validator.escapeQuestionMark(relId);
5834:
5835:                                // if it's attachment, assign a new attachment folder
5836:                                if (id.startsWith(ATTACHMENTS_COLLECTION)) {
5837:                                    String oldRef = getReference(id);
5838:
5839:                                    // take the name from after /attachment/whatever/
5840:                                    id = ATTACHMENTS_COLLECTION
5841:                                            + IdManager.createUuid()
5842:                                            + id.substring(id.indexOf('/',
5843:                                                    ATTACHMENTS_COLLECTION
5844:                                                            .length()));
5845:
5846:                                    // record the rename
5847:                                    attachmentNames.put(oldRef, id);
5848:                                }
5849:
5850:                                // otherwise move it into the site
5851:                                else {
5852:                                    if (relId == null) {
5853:                                        M_log
5854:                                                .warn("mergeContent(): no rel-id attribute in resource");
5855:                                        continue;
5856:                                    }
5857:
5858:                                    id = getSiteCollection(siteId) + relId;
5859:                                }
5860:
5861:                                element.setAttribute("id", id);
5862:
5863:                                ContentResource r = null;
5864:
5865:                                // if the body-location attribute points at another file for the body, get this
5866:                                String bodyLocation = StringUtil
5867:                                        .trimToNull(element
5868:                                                .getAttribute("body-location"));
5869:                                if (bodyLocation != null) {
5870:                                    // the file name is relative to the archive file
5871:                                    String bodyPath = StringUtil.fullReference(
5872:                                            archivePath, bodyLocation);
5873:
5874:                                    // get a stream from the file
5875:                                    FileInputStream in = new FileInputStream(
5876:                                            bodyPath);
5877:
5878:                                    // read the bytes
5879:                                    Blob body = new Blob();
5880:                                    body.read(in);
5881:
5882:                                    // resource: add if missing
5883:                                    r = mergeResource(element, body.getBytes());
5884:                                }
5885:
5886:                                else {
5887:                                    // resource: add if missing
5888:                                    r = mergeResource(element);
5889:                                }
5890:
5891:                                if (r == null) {
5892:                                    results
5893:                                            .append("resource: "
5894:                                                    + id
5895:                                                    + " already exists and was not replaced.\n");
5896:                                } else {
5897:                                    results.append("resource: " + id
5898:                                            + " imported.\n");
5899:                                }
5900:                            }
5901:                        }
5902:                    }
5903:                } catch (Exception any) {
5904:                    results.append("import interrputed: " + any.toString()
5905:                            + "\n");
5906:                    M_log.warn("mergeContent(): exception: ", any);
5907:                }
5908:
5909:                return results.toString();
5910:
5911:            } // merge
5912:
5913:            /**
5914:             * {@inheritDoc}
5915:             */
5916:            public void transferCopyEntities(String fromContext,
5917:                    String toContext, List resourceIds) {
5918:                // default to import all resources
5919:                boolean toBeImported = true;
5920:
5921:                // set up the target collection
5922:                ContentCollection toCollection = null;
5923:                try {
5924:                    toCollection = getCollection(toContext);
5925:                } catch (IdUnusedException e) {
5926:                    // not such collection yet, add one
5927:                    try {
5928:                        toCollection = addCollection(toContext);
5929:                    } catch (IdUsedException ee) {
5930:                        M_log.warn(this  + toContext, ee);
5931:                    } catch (IdInvalidException ee) {
5932:                        M_log.warn(this  + toContext, ee);
5933:                    } catch (PermissionException ee) {
5934:                        M_log.warn(this  + toContext, ee);
5935:                    } catch (InconsistentException ee) {
5936:                        M_log.warn(this  + toContext, ee);
5937:                    }
5938:                } catch (TypeException e) {
5939:                    M_log.warn(this  + toContext, e);
5940:                } catch (PermissionException e) {
5941:                    M_log.warn(this  + toContext, e);
5942:                }
5943:
5944:                if (toCollection != null) {
5945:                    // get the list of all resources for importing
5946:                    try {
5947:                        // get the root collection
5948:                        ContentCollection oCollection = getCollection(fromContext);
5949:
5950:                        // Get the collection members from the 'new' collection
5951:                        List oResources = oCollection.getMemberResources();
5952:                        for (int i = 0; i < oResources.size(); i++) {
5953:                            // get the original resource
5954:                            Entity oResource = (Entity) oResources.get(i);
5955:                            String oId = oResource.getId();
5956:
5957:                            if (resourceIds != null && resourceIds.size() > 0) {
5958:                                // only import those with ids inside the list
5959:                                toBeImported = false;
5960:                                for (int j = 0; j < resourceIds.size()
5961:                                        && !toBeImported; j++) {
5962:                                    if (((String) resourceIds.get(j))
5963:                                            .equals(oId)) {
5964:                                        toBeImported = true;
5965:                                    }
5966:                                }
5967:                            }
5968:
5969:                            if (toBeImported) {
5970:                                String oId2 = oResource.getId();
5971:                                String nId = "";
5972:
5973:                                int ind = oId2.indexOf(fromContext);
5974:                                if (ind != -1) {
5975:                                    String str1 = "";
5976:                                    String str2 = "";
5977:                                    if (ind != 0) {
5978:                                        // the substring before the fromContext string
5979:                                        str1 = oId2.substring(0, ind);
5980:                                    }
5981:                                    if (!((ind + fromContext.length()) > oId2
5982:                                            .length())) {
5983:                                        // the substring after the fromContext string
5984:                                        str2 = oId2.substring(ind
5985:                                                + fromContext.length(), oId2
5986:                                                .length());
5987:                                    }
5988:                                    // get the new resource id; fromContext is replaced with toContext
5989:                                    nId = str1 + toContext + str2;
5990:                                }
5991:
5992:                                ResourceProperties oProperties = oResource
5993:                                        .getProperties();
5994:                                boolean isCollection = false;
5995:                                try {
5996:                                    isCollection = oProperties
5997:                                            .getBooleanProperty(ResourceProperties.PROP_IS_COLLECTION);
5998:                                } catch (Exception e) {
5999:                                }
6000:
6001:                                if (isCollection) {
6002:                                    // add collection
6003:                                    try {
6004:                                        ContentCollectionEdit edit = addCollection(nId);
6005:                                        // import properties
6006:                                        ResourcePropertiesEdit p = edit
6007:                                                .getPropertiesEdit();
6008:                                        p.clear();
6009:                                        p.addAll(oProperties);
6010:                                        // complete the edit
6011:                                        m_storage.commitCollection(edit);
6012:                                        ((BaseCollectionEdit) edit).closeEdit();
6013:                                    } catch (IdUsedException e) {
6014:                                    } catch (IdInvalidException e) {
6015:                                    } catch (PermissionException e) {
6016:                                    } catch (InconsistentException e) {
6017:                                    }
6018:
6019:                                    transferCopyEntities(oResource.getId(),
6020:                                            nId, resourceIds);
6021:                                } else {
6022:                                    try {
6023:                                        // add resource
6024:                                        ContentResourceEdit edit = addResource(nId);
6025:                                        edit
6026:                                                .setContentType(((ContentResource) oResource)
6027:                                                        .getContentType());
6028:                                        edit
6029:                                                .setContent(((ContentResource) oResource)
6030:                                                        .getContent());
6031:                                        // import properties
6032:                                        ResourcePropertiesEdit p = edit
6033:                                                .getPropertiesEdit();
6034:                                        p.clear();
6035:                                        p.addAll(oProperties);
6036:                                        // complete the edit
6037:                                        m_storage.commitResource(edit);
6038:                                        ((BaseResourceEdit) edit).closeEdit();
6039:                                    } catch (PermissionException e) {
6040:                                    } catch (IdUsedException e) {
6041:                                    } catch (IdInvalidException e) {
6042:                                    } catch (InconsistentException e) {
6043:                                    } catch (ServerOverloadException e) {
6044:                                    }
6045:                                } // if
6046:                            } // if
6047:                        } // for
6048:                    } catch (IdUnusedException e) {
6049:                    } catch (TypeException e) {
6050:                    } catch (PermissionException e) {
6051:                    }
6052:                }
6053:
6054:            } // importResources
6055:
6056:            /**
6057:             * {@inheritDoc}
6058:             */
6059:            public boolean parseEntityReference(String reference, Reference ref) {
6060:                String id = null;
6061:                String context = "";
6062:
6063:                // for content hosting resources and collections
6064:                if (reference.startsWith(REFERENCE_ROOT)) {
6065:                    // parse out the local resource id
6066:                    id = reference.substring(REFERENCE_ROOT.length(), reference
6067:                            .length());
6068:                }
6069:
6070:                // not mine
6071:                else {
6072:                    return false;
6073:                }
6074:
6075:                // recognize a short reference
6076:                if (m_shortRefs) {
6077:                    // ignoring the first separator, get the first item separated from the rest
6078:                    String prefix[] = StringUtil.splitFirst(
6079:                            (id.length() > 1) ? id.substring(1) : "",
6080:                            Entity.SEPARATOR);
6081:                    if (prefix.length > 0) {
6082:                        // the following are recognized as full reference prefixe; if seen, the short ref feature is not applied
6083:                        if (!(prefix[0].equals("group")
6084:                                || prefix[0].equals("user")
6085:                                || prefix[0].equals("group-user")
6086:                                || prefix[0].equals("public")
6087:                                || prefix[0].equals("private") || prefix[0]
6088:                                .equals("attachment"))) {
6089:                            String newPrefix = null;
6090:
6091:                            // a "~" starts a /user/ reference
6092:                            if (prefix[0].startsWith("~")) {
6093:                                newPrefix = Entity.SEPARATOR + "user"
6094:                                        + Entity.SEPARATOR
6095:                                        + prefix[0].substring(1);
6096:                            }
6097:
6098:                            // otherwise a /group/ reference
6099:                            else {
6100:                                newPrefix = Entity.SEPARATOR + "group"
6101:                                        + Entity.SEPARATOR + prefix[0];
6102:                            }
6103:
6104:                            // reattach the tail (if any) to get the new id (if no taik, make sure we end with a separator if id started out with one)
6105:                            id = newPrefix
6106:                                    + ((prefix.length > 1) ? (Entity.SEPARATOR + prefix[1])
6107:                                            : (id.endsWith(Entity.SEPARATOR) ? Entity.SEPARATOR
6108:                                                    : ""));
6109:                        }
6110:                    }
6111:                }
6112:
6113:                // parse out the associated site id, with alias checking
6114:                String parts[] = StringUtil.split(id, Entity.SEPARATOR);
6115:                boolean checkForAlias = true;
6116:                boolean checkForUserIdEid = false;
6117:                if (parts.length >= 3) {
6118:                    if (parts[1].equals("group")) {
6119:                        context = parts[2];
6120:                    } else if (parts[1].equals("user")) {
6121:                        context = m_siteService.getUserSiteId(parts[2]);
6122:
6123:                        // for user sites, don't check for alias
6124:                        checkForAlias = false;
6125:
6126:                        // enable user id/eid checking
6127:                        checkForUserIdEid = true;
6128:                    } else if (parts[1].equals("group-user")) {
6129:                        // use just the group context
6130:                        context = parts[2];
6131:                    }
6132:
6133:                    // if a user site, recognize ID or EID
6134:                    if (checkForUserIdEid && (parts[2] != null)
6135:                            && (parts[2].length() > 0)) {
6136:                        try {
6137:                            // if successful, the context is already a valid user id
6138:                            UserDirectoryService.getUser(parts[2]);
6139:                        } catch (UserNotDefinedException tryEid) {
6140:                            try {
6141:                                // try using it as an EID
6142:                                String userId = UserDirectoryService
6143:                                        .getUserId(parts[2]);
6144:
6145:                                // switch to the ID
6146:                                parts[2] = userId;
6147:                                context = m_siteService.getUserSiteId(userId);
6148:                                String newId = StringUtil.unsplit(parts,
6149:                                        Entity.SEPARATOR);
6150:
6151:                                // add the trailing separator if needed
6152:                                if (id.endsWith(Entity.SEPARATOR))
6153:                                    newId += Entity.SEPARATOR;
6154:
6155:                                id = newId;
6156:                            } catch (UserNotDefinedException notEid) {
6157:                                // if context was not a valid EID, leave it alone
6158:                            }
6159:                        }
6160:                    }
6161:
6162:                    // recognize alias for site id - but if a site id exists that matches the requested site id, that's what we will use
6163:                    if (m_siteAlias && checkForAlias && (context != null)
6164:                            && (context.length() > 0)) {
6165:                        if (!m_siteService.siteExists(context)) {
6166:                            try {
6167:                                String target = m_aliasService
6168:                                        .getTarget(context);
6169:
6170:                                // just to stay well clear of infinite looping (the newReference will call us for content references)
6171:                                // ignore any targets that are to the content service -ggolden
6172:                                if (!(target.startsWith(REFERENCE_ROOT) || target
6173:                                        .startsWith(getUrl("")))) {
6174:                                    Reference targetRef = m_entityManager
6175:                                            .newReference(target);
6176:                                    boolean changed = false;
6177:
6178:                                    // for a site reference
6179:                                    if (SiteService.APPLICATION_ID
6180:                                            .equals(targetRef.getType())) {
6181:                                        // use the ref's id, i.e. the site id
6182:                                        context = targetRef.getId();
6183:                                        changed = true;
6184:                                    }
6185:
6186:                                    // for mail archive reference
6187:                                    // TODO: taken from MailArchiveService.APPLICATION_ID to (fake) reduce a dependency -ggolden
6188:                                    else if ("sakai:mailarchive"
6189:                                            .equals(targetRef.getType())) {
6190:                                        // use the ref's context as the site id
6191:                                        context = targetRef.getContext();
6192:                                        changed = true;
6193:                                    }
6194:
6195:                                    // if changed, update the id
6196:                                    if (changed) {
6197:                                        parts[2] = context;
6198:                                        String newId = StringUtil.unsplit(
6199:                                                parts, Entity.SEPARATOR);
6200:
6201:                                        // add the trailing separator if needed
6202:                                        if (id.endsWith(Entity.SEPARATOR))
6203:                                            newId += Entity.SEPARATOR;
6204:
6205:                                        id = newId;
6206:                                    }
6207:                                }
6208:                            } catch (IdUnusedException noAlias) {
6209:                            }
6210:                        }
6211:                    }
6212:                }
6213:
6214:                // if we end up with no id, or blank, use the root
6215:                if ((id == null) || (id.length() == 0))
6216:                    id = "/";
6217:
6218:                ref.set(APPLICATION_ID, null, id, null, context);
6219:
6220:                // because short refs or id/eid or alias processing may recognize a reference that is not the real reference,
6221:                // update the ref's string to reflect the real reference
6222:                ref.updateReference(REFERENCE_ROOT + id);
6223:
6224:                return true;
6225:            }
6226:
6227:            /**
6228:             * {@inheritDoc}
6229:             */
6230:            public String getEntityDescription(Reference ref) {
6231:                // double check that it's mine
6232:                if (APPLICATION_ID != ref.getType())
6233:                    return null;
6234:
6235:                String rv = "Content: " + ref.getId();
6236:
6237:                try {
6238:                    ResourceProperties props = getProperties(ref.getId());
6239:                    if (props
6240:                            .getBooleanProperty(ResourceProperties.PROP_IS_COLLECTION)) {
6241:                        ContentCollection c = getCollection(ref.getId());
6242:                        rv = "Collection: "
6243:                                + c.getProperties().getPropertyFormatted(
6244:                                        ResourceProperties.PROP_DISPLAY_NAME)
6245:                                + " ("
6246:                                + c.getId()
6247:                                + ")\n"
6248:                                + " Created: "
6249:                                + c.getProperties().getPropertyFormatted(
6250:                                        ResourceProperties.PROP_CREATION_DATE)
6251:                                + " by "
6252:                                + c.getProperties().getPropertyFormatted(
6253:                                        ResourceProperties.PROP_CREATOR)
6254:                                + "(User Id:"
6255:                                + c.getProperties().getProperty(
6256:                                        ResourceProperties.PROP_CREATOR)
6257:                                + ")\n"
6258:                                + StringUtil
6259:                                        .limit(
6260:                                                c
6261:                                                        .getProperties()
6262:                                                        .getPropertyFormatted(
6263:                                                                ResourceProperties.PROP_DESCRIPTION),
6264:                                                30);
6265:                    } else {
6266:                        ContentResource r = getResource(ref.getId());
6267:                        rv = "Resource: "
6268:                                + r.getProperties().getPropertyFormatted(
6269:                                        ResourceProperties.PROP_DISPLAY_NAME)
6270:                                + " ("
6271:                                + r.getId()
6272:                                + ")\n"
6273:                                + " Created: "
6274:                                + r.getProperties().getPropertyFormatted(
6275:                                        ResourceProperties.PROP_CREATION_DATE)
6276:                                + " by "
6277:                                + r.getProperties().getPropertyFormatted(
6278:                                        ResourceProperties.PROP_CREATOR)
6279:                                + "(User Id:"
6280:                                + r.getProperties().getProperty(
6281:                                        ResourceProperties.PROP_CREATOR)
6282:                                + ")\n"
6283:                                + StringUtil
6284:                                        .limit(
6285:                                                r
6286:                                                        .getProperties()
6287:                                                        .getPropertyFormatted(
6288:                                                                ResourceProperties.PROP_DESCRIPTION),
6289:                                                30);
6290:                    }
6291:                } catch (Exception e) {
6292:                }
6293:
6294:                return rv;
6295:            }
6296:
6297:            /**
6298:             * {@inheritDoc}
6299:             */
6300:            public ResourceProperties getEntityResourceProperties(Reference ref) {
6301:                // double check that it's mine
6302:                if (APPLICATION_ID != ref.getType())
6303:                    return null;
6304:
6305:                ResourceProperties props = null;
6306:
6307:                try {
6308:                    props = getProperties(ref.getId());
6309:                } catch (PermissionException e) {
6310:                } catch (IdUnusedException e) {
6311:                }
6312:
6313:                return props;
6314:            }
6315:
6316:            /**
6317:             * {@inheritDoc}
6318:             */
6319:            public String[] myToolIds() {
6320:                String[] toolIds = { "sakai.resources" };
6321:                return toolIds;
6322:            }
6323:
6324:            /**
6325:             * {@inheritDoc}
6326:             */
6327:            public void contextCreated(String context, boolean toolPlacement) {
6328:                try {
6329:                    if (toolPlacement) {
6330:                        Site site = m_siteService.getSite(context);
6331:                        if (site.getToolForCommonId("sakai.dropbox") != null) {
6332:                            enableDropbox(context);
6333:                        }
6334:                        if (site.getToolForCommonId("sakai.resources") != null) {
6335:                            enableResources(context);
6336:                        }
6337:                    }
6338:
6339:                } catch (IdUnusedException e) {
6340:                    // ignore -- nothing to enable
6341:                }
6342:            }
6343:
6344:            /**
6345:             * {@inheritDoc}
6346:             */
6347:            public void contextUpdated(String context, boolean toolPlacement) {
6348:                try {
6349:                    if (toolPlacement) {
6350:                        Site site = m_siteService.getSite(context);
6351:                        if (site.getToolForCommonId("sakai.dropbox") != null) {
6352:                            enableDropbox(context);
6353:                        }
6354:                        if (site.getToolForCommonId("sakai.resources") != null) {
6355:                            enableResources(context);
6356:                        }
6357:                    }
6358:
6359:                } catch (IdUnusedException e) {
6360:                    // ignore -- nothing to enable
6361:                }
6362:            }
6363:
6364:            /**
6365:             * {@inheritDoc}
6366:             */
6367:            public void contextDeleted(String context, boolean toolPlacement) {
6368:                // TODO This avoids disabling the collection if the tool still exists, but ...
6369:                // does it catch the case where the tool is being deleted from the site?
6370:                try {
6371:                    if (toolPlacement) {
6372:                        Site site = m_siteService.getSite(context);
6373:                        if (site.getToolForCommonId("sakai.dropbox") == null) {
6374:                            disableDropbox(context);
6375:                        }
6376:                        if (site.getToolForCommonId("sakai.resources") == null) {
6377:                            disableResources(context);
6378:                        }
6379:                    }
6380:
6381:                } catch (IdUnusedException e) {
6382:                    // ignore -- nothing to enable
6383:                }
6384:            }
6385:
6386:            /**
6387:             * Make sure a home in resources exists for the site.
6388:             * 
6389:             * @param site
6390:             *        The site.
6391:             */
6392:            protected void enableResources(String context) {
6393:                unlockCheck(SITE_UPDATE_ACCESS, context);
6394:
6395:                // it would be called
6396:                String id = getSiteCollection(context);
6397:
6398:                // does it exist?
6399:                try {
6400:                    Site site = m_siteService.getSite(context);
6401:                    try {
6402:                        ContentCollection collection = findCollection(id); // getCollection(id);	// 
6403:
6404:                        if (collection == null) {
6405:                            // make it
6406:                            try {
6407:                                ContentCollectionEdit edit = addValidPermittedCollection(id);
6408:                                edit.getPropertiesEdit().addProperty(
6409:                                        ResourceProperties.PROP_DISPLAY_NAME,
6410:                                        site.getTitle());
6411:                                commitCollection(edit);
6412:                                collection = findCollection(id);
6413:                            } catch (IdUsedException e) {
6414:                                M_log.warn("enableResources: " + e);
6415:                                collection = findCollection(id);
6416:                            } catch (InconsistentException e) {
6417:                                // Because the id is coming from getSiteCollection(), this will never occur.
6418:                                // If it does, we better get alerted to it.
6419:                                M_log.warn("enableResources: " + e);
6420:                                throw new RuntimeException(e);
6421:                            }
6422:                        }
6423:
6424:                        // do we need to update the title?
6425:                        if (!site.getTitle().equals(
6426:                                collection.getProperties().getProperty(
6427:                                        ResourceProperties.PROP_DISPLAY_NAME))) {
6428:                            try {
6429:                                ContentCollectionEdit edit = editCollection(id);
6430:                                edit.getPropertiesEdit().addProperty(
6431:                                        ResourceProperties.PROP_DISPLAY_NAME,
6432:                                        site.getTitle());
6433:                                commitCollection(edit);
6434:                            } catch (IdUnusedException e) {
6435:                                M_log.warn("enableResources: " + e);
6436:                                throw new RuntimeException(e);
6437:                            } catch (PermissionException e) {
6438:                                M_log.warn("enableResources: " + e);
6439:                                throw new RuntimeException(e);
6440:                            } catch (InUseException e) {
6441:                                M_log.warn("enableResources: " + e);
6442:                                throw new RuntimeException(e);
6443:                            }
6444:                        }
6445:                    } catch (TypeException e) {
6446:                        M_log.warn("enableResources: " + e);
6447:                        throw new RuntimeException(e);
6448:                    }
6449:                } catch (IdUnusedException e) {
6450:                    // TODO: -ggolden
6451:                    M_log.warn("enableResources: " + e);
6452:                    throw new RuntimeException(e);
6453:                }
6454:            }
6455:
6456:            /**
6457:             * Remove resources area for a site.
6458:             * 
6459:             * @param site
6460:             *        The site.
6461:             */
6462:            protected void disableResources(String context) {
6463:                // TODO: we do nothing now - resources hang around after the tool is removed from the site or the site is deleted -ggolden
6464:            }
6465:
6466:            /**
6467:             * Make sure a home in resources for dropbox exists for the site.
6468:             * 
6469:             * @param site
6470:             *        The site.
6471:             */
6472:            protected void enableDropbox(String context) {
6473:                // create it and the user folders within
6474:                createDropboxCollection(context);
6475:            }
6476:
6477:            /**
6478:             * Remove resources area for a site.
6479:             * 
6480:             * @param site
6481:             *        The site.
6482:             */
6483:            protected void disableDropbox(String context) {
6484:                // TODO: we do nothing now - dropbox resources hang around after the tool is removed from the site or the site is deleted -ggolden
6485:            }
6486:
6487:            /**********************************************************************************************************************************************************************************************************************************************************
6488:             * etc
6489:             *********************************************************************************************************************************************************************************************************************************************************/
6490:
6491:            /**
6492:             * Archive the collection, then the members of the collection - recursively for collection members.
6493:             * 
6494:             * @param collection
6495:             *        The collection whose members are to be archived.
6496:             * @param doc
6497:             *        The document to contain the xml.
6498:             * @param stack
6499:             *        The stack of elements, the top of which will be the containing element of the "collection" or "resource" element.
6500:             * @param storagePath
6501:             *        The path to the folder where we are writing files.
6502:             * @param siteCollectionId
6503:             *        The resource id of the site collection.
6504:             * @param results
6505:             *        A log of messages from the archive.
6506:             */
6507:            protected void archiveCollection(ContentCollection collection,
6508:                    Document doc, Stack stack, String storagePath,
6509:                    String siteCollectionId, StringBuffer results) {
6510:                // first the collection
6511:                Element el = collection.toXml(doc, stack);
6512:
6513:                // store the relative file id in the xml
6514:                el.setAttribute("rel-id", collection.getId().substring(
6515:                        siteCollectionId.length()));
6516:
6517:                results.append("archiving collection: " + collection.getId()
6518:                        + "\n");
6519:
6520:                // now each member
6521:                List members = collection.getMemberResources();
6522:                if ((members == null) || (members.size() == 0))
6523:                    return;
6524:                for (int i = 0; i < members.size(); i++) {
6525:                    Object member = members.get(i);
6526:                    if (member instanceof  ContentCollection) {
6527:                        archiveCollection((ContentCollection) member, doc,
6528:                                stack, storagePath, siteCollectionId, results);
6529:                    } else if (member instanceof  ContentResource) {
6530:                        results.append(archiveResource(
6531:                                (ContentResource) member, doc, stack,
6532:                                storagePath, siteCollectionId));
6533:                    }
6534:                }
6535:
6536:            } // archiveCollection
6537:
6538:            /**
6539:             * Archive a singe resource
6540:             * 
6541:             * @param resource
6542:             *        The content resource to archive
6543:             * @param doc
6544:             *        The XML document.
6545:             * @param stack
6546:             *        The stack of elements.
6547:             * @param storagePath
6548:             *        The path to the folder where we are writing files.
6549:             * @param siteCollectionId
6550:             *        The resource id of the site collection (optional).
6551:             * @return A log of messages from the archive.
6552:             */
6553:            protected String archiveResource(ContentResource resource,
6554:                    Document doc, Stack stack, String storagePath,
6555:                    String siteCollectionId) {
6556:                byte[] content = null;
6557:                try {
6558:                    // get the content bytes
6559:                    content = resource.getContent();
6560:                } catch (ServerOverloadException e) {
6561:                    M_log.warn("archiveResource(): while reading body for: "
6562:                            + resource.getId() + " : " + e);
6563:                    // return "failed to archive resource: " + resource.getId() + " body temporarily unavailable due to server error\n"
6564:                }
6565:
6566:                // form the xml
6567:                Element el = resource.toXml(doc, stack);
6568:
6569:                // remove the content from the xml
6570:                el.removeAttribute("body");
6571:
6572:                // write the content to a file
6573:                String fileName = IdManager.createUuid();
6574:                Blob b = new Blob();
6575:                b.append(content);
6576:                try {
6577:                    FileOutputStream out = new FileOutputStream(storagePath
6578:                            + fileName);
6579:                    b.write(out);
6580:                    out.close();
6581:                } catch (Exception e) {
6582:                    M_log.warn("archiveResource(): while writing body for: "
6583:                            + resource.getId() + " : " + e);
6584:                }
6585:
6586:                // store the file name in the xml
6587:                el.setAttribute("body-location", fileName);
6588:
6589:                // store the relative file id in the xml
6590:                if (siteCollectionId != null) {
6591:                    el.setAttribute("rel-id", resource.getId().substring(
6592:                            siteCollectionId.length()));
6593:                }
6594:
6595:                return "archiving resource: " + resource.getId()
6596:                        + " body in file: " + fileName + "\n";
6597:            }
6598:
6599:            /**
6600:             * Merge in a collection from an XML DOM definition. Take whole if not defined already. Ignore if already here.
6601:             * 
6602:             * @param element
6603:             *        The XML DOM element containing the collection definition.
6604:             * @exception PermissionException
6605:             *            if the user does not have permission to add a collection.
6606:             * @exception InconsistentException
6607:             *            if the containing collection does not exist.
6608:             * @exception IdInvalidException
6609:             *            if the id is not valid.
6610:             * @return a new ContentCollection object, or null if it was not created.
6611:             */
6612:            protected ContentCollection mergeCollection(Element element)
6613:                    throws PermissionException, InconsistentException,
6614:                    IdInvalidException {
6615:                // read the collection object
6616:                BaseCollectionEdit collectionFromXml = new BaseCollectionEdit(
6617:                        element);
6618:                String id = collectionFromXml.getId();
6619:
6620:                // add it
6621:                BaseCollectionEdit edit = null;
6622:                try {
6623:                    edit = (BaseCollectionEdit) addCollection(id);
6624:                } catch (IdUsedException e) {
6625:                    // ignore if it exists
6626:                    return null;
6627:                }
6628:
6629:                // transfer from the XML read object to the edit
6630:                edit.set(collectionFromXml);
6631:
6632:                try {
6633:                    Time createTime = edit.getProperties().getTimeProperty(
6634:                            ResourceProperties.PROP_CREATION_DATE);
6635:                } catch (EntityPropertyNotDefinedException epnde) {
6636:                    String now = TimeService.newTime().toString();
6637:                    edit.getProperties().addProperty(
6638:                            ResourceProperties.PROP_CREATION_DATE, now);
6639:                } catch (EntityPropertyTypeException epte) {
6640:                    M_log.error(epte);
6641:                }
6642:
6643:                // setup the event
6644:                edit.setEvent(EVENT_RESOURCE_ADD);
6645:
6646:                // commit the change
6647:                commitCollection(edit);
6648:
6649:                return edit;
6650:
6651:            } // mergeCollection
6652:
6653:            /**
6654:             * Merge in a resource from an XML DOM definition. Ignore if already defined. Take whole if not.
6655:             * 
6656:             * @param element
6657:             *        The XML DOM element containing the collection definition.
6658:             * @exception PermissionException
6659:             *            if the user does not have permission to add a resource.
6660:             * @exception InconsistentException
6661:             *            if the containing collection does not exist.
6662:             * @exception IdInvalidException
6663:             *            if the id is not valid.
6664:             * @exception OverQuotaException
6665:             *            if this would result in being over quota.
6666:             * @exception ServerOverloadException
6667:             *            if the server is configured to write the resource body to the filesystem and the save fails.
6668:             * @return a new ContentResource object, or null if it was not created.
6669:             */
6670:            protected ContentResource mergeResource(Element element)
6671:                    throws PermissionException, InconsistentException,
6672:                    IdInvalidException, OverQuotaException,
6673:                    ServerOverloadException {
6674:                return mergeResource(element, null);
6675:
6676:            } // mergeResource
6677:
6678:            /**
6679:             * Merge in a resource from an XML DOM definition and a body bytes array. Ignore if already defined. Take whole if not.
6680:             * 
6681:             * @param element
6682:             *        The XML DOM element containing the collection definition.
6683:             * @param body
6684:             *        The body bytes.
6685:             * @exception PermissionException
6686:             *            if the user does not have permission to add a resource.
6687:             * @exception InconsistentException
6688:             *            if the containing collection does not exist.
6689:             * @exception IdInvalidException
6690:             *            if the id is not valid.
6691:             * @exception OverQuotaException
6692:             *            if this would result in being over quota.
6693:             * @return a new ContentResource object, or null if it was not created.
6694:             */
6695:            protected ContentResource mergeResource(Element element, byte[] body)
6696:                    throws PermissionException, InconsistentException,
6697:                    IdInvalidException, OverQuotaException,
6698:                    ServerOverloadException {
6699:                // make the resource object
6700:                BaseResourceEdit resourceFromXml = new BaseResourceEdit(element);
6701:                String id = resourceFromXml.getId();
6702:
6703:                // get it added
6704:                BaseResourceEdit edit = null;
6705:                try {
6706:                    edit = (BaseResourceEdit) addResource(id);
6707:                } catch (IdUsedException e) {
6708:                    // ignore the add if it exists already
6709:                    return null;
6710:                }
6711:
6712:                // transfer the items of interest (content type, properties) from the XML read object to the edit.
6713:                edit.setContentType(resourceFromXml.getContentType());
6714:                ResourcePropertiesEdit p = edit.getPropertiesEdit();
6715:                p.clear();
6716:                p.addAll(resourceFromXml.getProperties());
6717:
6718:                // if body is provided, use it
6719:                if (body != null) {
6720:                    edit.setContent(body);
6721:                }
6722:
6723:                // setup the event
6724:                edit.setEvent(EVENT_RESOURCE_ADD);
6725:
6726:                // commit the change - Note: we do properties differently
6727:                assureResourceProperties(edit);
6728:
6729:                // check for over quota.
6730:                if (overQuota(edit)) {
6731:                    throw new OverQuotaException(edit.getReference());
6732:                }
6733:
6734:                // complete the edit
6735:                m_storage.commitResource(edit);
6736:
6737:                addSizeCache(edit);
6738:
6739:                // track it
6740:                EventTrackingService.post(EventTrackingService.newEvent(
6741:                        ((BaseResourceEdit) edit).getEvent(), edit
6742:                                .getReference(), true,
6743:                        NotificationService.NOTI_NONE));
6744:
6745:                // close the edit object
6746:                ((BaseResourceEdit) edit).closeEdit();
6747:
6748:                return edit;
6749:
6750:            } // mergeResource
6751:
6752:            /**
6753:             * Find the containing collection id of a given resource id.
6754:             * 
6755:             * @param id
6756:             *        The resource id.
6757:             * @return the containing collection id.
6758:             */
6759:            protected String isolateContainingId(String id) {
6760:                // take up to including the last resource path separator, not counting one at the very end if there
6761:                return id
6762:                        .substring(0, id.lastIndexOf('/', id.length() - 2) + 1);
6763:
6764:            } // isolateContainingId
6765:
6766:            /**
6767:             * Find the resource name of a given resource id.
6768:             * 
6769:             * @param id
6770:             *        The resource id.
6771:             * @return the resource name.
6772:             */
6773:            protected String isolateName(String id) {
6774:                if (id == null)
6775:                    return null;
6776:                if (id.length() == 0)
6777:                    return null;
6778:
6779:                // take after the last resource path separator, not counting one at the very end if there
6780:                boolean lastIsSeparator = id.charAt(id.length() - 1) == '/';
6781:                return id.substring(id.lastIndexOf('/', id.length() - 2) + 1,
6782:                        (lastIsSeparator ? id.length() - 1 : id.length()));
6783:
6784:            } // isolateName
6785:
6786:            /**
6787:             * Check the fixed type and id infomation: The same or better content type based on the known type for this id's extension, if any. The same or added extension id based on the know MIME type, if any Only if the type is the unknown type already.
6788:             * 
6789:             * @param id
6790:             *        The resource id with possible file extension to check.
6791:             * @param type
6792:             *        The content type.
6793:             * @return the best guess content type based on this resource's id and resource id with extension based on this resource's MIME type.
6794:             */
6795:            protected Hashtable fixTypeAndId(String id, String type) {
6796:                // the Hashtable holds the id and mime type
6797:                Hashtable extType = new Hashtable();
6798:                extType.put("id", id);
6799:                if (type == null)
6800:                    type = "";
6801:                extType.put("type", type);
6802:                String extension = Validator.getFileExtension(id);
6803:
6804:                if (extension.length() != 0) {
6805:                    // if there's a file extension and a blank, null or unknown(application/binary) mime type,
6806:                    // fix the mime type by doing a lookup based on the extension
6807:                    if (((type == null) || (type.length() == 0) || (ContentTypeImageService
6808:                            .isUnknownType(type)))) {
6809:                        extType.put("type", ContentTypeImageService
6810:                                .getContentType(extension));
6811:                    }
6812:                } else {
6813:                    // if there is no file extension, but a non-null, non-blank mime type, do a lookup based on the mime type and add an extension
6814:                    // if there is no extension, find one according to the MIME type and add it.
6815:                    if ((type != null) && (!type.equals(""))
6816:                            && (!ContentTypeImageService.isUnknownType(type))) {
6817:                        extension = ContentTypeImageService
6818:                                .getContentTypeExtension(type);
6819:                        if (extension.length() > 0) {
6820:                            id = id + "." + extension;
6821:                            extType.put("id", id);
6822:                        }
6823:                    } else {
6824:                        // if mime type is null or mime type is empty or mime and there is no extension
6825:                        if ((type == null) || (type.equals(""))) {
6826:                            extType.put("type", "application/binary");
6827:                        }
6828:                        // htripath- SAK-1811 remove '.bin' extension from binary file without any extension e.g makeFile
6829:                        // id = id + ".bin";
6830:                        extType.put("id", id);
6831:                    }
6832:                }
6833:
6834:                return extType;
6835:
6836:            } // fixTypeAndId
6837:
6838:            /**
6839:             * Test if this resource edit would place the account" over quota.
6840:             * 
6841:             * @param edit
6842:             *        The proposed resource edit.
6843:             * @return true if this change would palce the "account" over quota, false if not.
6844:             */
6845:
6846:            protected boolean overQuota(ContentResourceEdit edit) {
6847:                // Note: This implementation is hard coded to just check for a quota in the "/user/"
6848:                // or "/group/" area. -ggolden
6849:
6850:                // Note: this does NOT count attachments (/attachments/*) nor dropbox (/group-user/<site id>/<user id>/*) -ggolden
6851:
6852:                // quick exits if we are not doing site quotas
6853:                // if (m_siteQuota == 0)
6854:                // return false;
6855:
6856:                // some quick exits, if we are not doing user quota, or if this is not a user or group resource
6857:                // %%% These constants should be from somewhere else -ggolden
6858:                if (!((edit.getId().startsWith("/user/")) || (edit.getId()
6859:                        .startsWith("/group/"))))
6860:                    return false;
6861:
6862:                // expect null, "user" | "group", user/groupid, rest...
6863:                String[] parts = StringUtil.split(edit.getId(),
6864:                        Entity.SEPARATOR);
6865:                if (parts.length <= 2)
6866:                    return false;
6867:
6868:                // get this collection
6869:                String id = Entity.SEPARATOR + parts[1] + Entity.SEPARATOR
6870:                        + parts[2] + Entity.SEPARATOR;
6871:                ContentCollection collection = null;
6872:                try {
6873:                    collection = findCollection(id);
6874:                } catch (TypeException ignore) {
6875:                }
6876:
6877:                if (collection == null)
6878:                    return false;
6879:
6880:                long quota = getQuota(collection);
6881:
6882:                if (quota == 0) {
6883:                    return false;
6884:                }
6885:
6886:                long size = getCachedBodySizeK((BaseCollectionEdit) collection);
6887:
6888:                // find the resource being edited
6889:                ContentResource inThere = null;
6890:                try {
6891:                    inThere = findResource(edit.getId());
6892:                } catch (TypeException ignore) {
6893:                }
6894:
6895:                if (inThere != null) {
6896:                    // reduce the size by the existing size
6897:                    size -= bytes2k(inThere.getContentLength());
6898:                }
6899:
6900:                // add in the new size
6901:                size += bytes2k(edit.getContentLength());
6902:
6903:                return (size >= quota);
6904:
6905:            } // overQuota
6906:
6907:            /**
6908:             * @param collection
6909:             * @return
6910:             */
6911:
6912:            /*
6913:             * Size Cache.
6914:             * This caches the size of the collection and all children for 10 miutes from first 
6915:             * created, keeping a track of addtions and removals to the collection.
6916:             * 
6917:             * It only works where the same collection id is supplied and does not 
6918:             * consider the size under nested collections or update modifcations 
6919:             * on all nested collections.
6920:             * 
6921:             * It is a temporary fix to eliminate GC collection issues with the size calculations
6922:             */
6923:
6924:            protected class SizeHolder {
6925:
6926:                public long ttl = System.currentTimeMillis() + 600000L;
6927:                public long size = 0;
6928:
6929:            }
6930:
6931:            private Map<String, SizeHolder> quotaMap = new ConcurrentHashMap<String, SizeHolder>();
6932:
6933:            private long getCachedBodySizeK(BaseCollectionEdit collection) {
6934:                return getCachedSizeHolder(collection, true).size;
6935:            }
6936:
6937:            private void addCachedBodySizeK(BaseCollectionEdit collection,
6938:                    long increment) {
6939:                SizeHolder sh = getCachedSizeHolder(collection, false);
6940:                if (sh != null) {
6941:                    sh.size += increment;
6942:                }
6943:            }
6944:
6945:            /**
6946:             * @param collection
6947:             * @return
6948:             */
6949:
6950:            private SizeHolder getCachedSizeHolder(
6951:                    BaseCollectionEdit collection, boolean create) {
6952:                String id = collection.getId();
6953:                SizeHolder sh = quotaMap.get(id);
6954:                boolean scan = false;
6955:                long now = System.currentTimeMillis();
6956:                if (sh != null) {
6957:                    M_log.debug("Cache Hit [" + id + "] size=[" + sh.size
6958:                            + "] ttl=[" + (sh.ttl - now) + "]");
6959:                    if (sh.ttl < now) {
6960:                        M_log.debug("Cache Expire [" + id + "]");
6961:                        quotaMap.remove(id);
6962:                        sh = null;
6963:                        scan = true;
6964:                    }
6965:                } else {
6966:                    M_log.debug("Cache Miss [" + id + "]");
6967:
6968:                }
6969:
6970:                if (create && sh == null) {
6971:                    M_log.debug("Cache Create [" + id + "]");
6972:                    // get the content size of all resources in this hierarchy
6973:                    long size = collection.getBodySizeK();
6974:                    // the above can take a long time, just check that annother thread
6975:                    // hasnt just done the same, if it has then sh != null so we should not
6976:                    // add a new one in, and will have waisted our time.
6977:                    sh = quotaMap.get(id);
6978:                    if (sh == null) {
6979:                        sh = new SizeHolder();
6980:                        quotaMap.put(id, sh);
6981:                        sh.size = size;
6982:                        scan = true;
6983:                    }
6984:                }
6985:                if (scan) {
6986:                    // when we remove one, scan for old ones.
6987:                    for (Iterator<String> i = quotaMap.keySet().iterator(); i
6988:                            .hasNext();) {
6989:                        String k = i.next();
6990:                        SizeHolder s = quotaMap.get(k);
6991:                        if (s.ttl < now) {
6992:                            M_log.debug("Cache Scan Expire [" + id + "]");
6993:                            quotaMap.remove(k);
6994:                        }
6995:                    }
6996:
6997:                }
6998:                return sh;
6999:            }
7000:
7001:            protected void removeSizeCache(ContentResourceEdit edit) {
7002:                // Note: This implementation is hard coded to just check for a quota in the "/user/"
7003:                // or "/group/" area. -ggolden
7004:
7005:                // Note: this does NOT count attachments (/attachments/*) nor dropbox (/group-user/<site id>/<user id>/*) -ggolden
7006:
7007:                // quick exits if we are not doing site quotas
7008:                // if (m_siteQuota == 0)
7009:                // return false;
7010:
7011:                // some quick exits, if we are not doing user quota, or if this is not a user or group resource
7012:                // %%% These constants should be from somewhere else -ggolden
7013:                if (!((edit.getId().startsWith("/user/")) || (edit.getId()
7014:                        .startsWith("/group/"))))
7015:                    return;
7016:
7017:                // expect null, "user" | "group", user/groupid, rest...
7018:                String[] parts = StringUtil.split(edit.getId(),
7019:                        Entity.SEPARATOR);
7020:                if (parts.length <= 2)
7021:                    return;
7022:
7023:                // get this collection
7024:                String id = Entity.SEPARATOR + parts[1] + Entity.SEPARATOR
7025:                        + parts[2] + Entity.SEPARATOR;
7026:                ContentCollection collection = null;
7027:                try {
7028:                    collection = findCollection(id);
7029:                } catch (TypeException ignore) {
7030:                }
7031:
7032:                if (collection == null)
7033:                    return;
7034:
7035:                addCachedBodySizeK((BaseCollectionEdit) collection,
7036:                        -bytes2k(edit.getContentLength()));
7037:
7038:            } // updateSizeCache();
7039:
7040:            protected void addSizeCache(ContentResourceEdit edit) {
7041:                // Note: This implementation is hard coded to just check for a quota in the "/user/"
7042:                // or "/group/" area. -ggolden
7043:
7044:                // Note: this does NOT count attachments (/attachments/*) nor dropbox (/group-user/<site id>/<user id>/*) -ggolden
7045:
7046:                // quick exits if we are not doing site quotas
7047:                // if (m_siteQuota == 0)
7048:                // return false;
7049:
7050:                // some quick exits, if we are not doing user quota, or if this is not a user or group resource
7051:                // %%% These constants should be from somewhere else -ggolden
7052:                if (!((edit.getId().startsWith("/user/")) || (edit.getId()
7053:                        .startsWith("/group/"))))
7054:                    return;
7055:
7056:                // expect null, "user" | "group", user/groupid, rest...
7057:                String[] parts = StringUtil.split(edit.getId(),
7058:                        Entity.SEPARATOR);
7059:                if (parts.length <= 2)
7060:                    return;
7061:
7062:                // get this collection
7063:                String id = Entity.SEPARATOR + parts[1] + Entity.SEPARATOR
7064:                        + parts[2] + Entity.SEPARATOR;
7065:                ContentCollection collection = null;
7066:                try {
7067:                    collection = findCollection(id);
7068:                } catch (TypeException ignore) {
7069:                }
7070:
7071:                if (collection == null)
7072:                    return;
7073:
7074:                addCachedBodySizeK((BaseCollectionEdit) collection,
7075:                        bytes2k(edit.getContentLength()));
7076:
7077:            } // updateSizeCache();
7078:
7079:            /**
7080:             * Convert bytes to Kbytes, rounding up, and counting even 0 bytes as 1 k.
7081:             * 
7082:             * @param bytes
7083:             *        The size in bytes.
7084:             * @return The size in Kbytes, rounded up.
7085:             */
7086:            protected long bytes2k(long bytes) {
7087:                return ((bytes - 1) / 1024) + 1;
7088:
7089:            } // bytes2k
7090:
7091:            /**
7092:             * gets the quota for a site collection or for a user's my workspace collection
7093:             *
7094:             * @param collection the collection on which to test for a quota.  this can be the collection for a site
7095:             * or a user's workspace collection
7096:             * @return the quota in kb
7097:             */
7098:            public long getQuota(ContentCollection collection) {
7099:                long quota = 0;
7100:
7101:                // use this quota unless we have one more specific
7102:                quota = m_siteQuota;
7103:
7104:                // see if this collection has a quota property
7105:                try {
7106:                    long siteSpecific = collection
7107:                            .getProperties()
7108:                            .getLongProperty(
7109:                                    ResourceProperties.PROP_COLLECTION_BODY_QUOTA);
7110:
7111:                    quota = siteSpecific;
7112:                } catch (EntityPropertyNotDefinedException ignore) {
7113:                    // don't log or anything, this just means that this site doesn't have this quota property.
7114:                } catch (Exception ignore) {
7115:                    M_log.warn("getQuota: reading quota property of : "
7116:                            + collection.getId() + " : " + ignore);
7117:                }
7118:
7119:                return quota;
7120:            }
7121:
7122:            /**
7123:             * Attempt to create any collections needed so that the parameter collection exists.
7124:             * 
7125:             * @param target
7126:             *        The collection that we want to exist.
7127:             */
7128:            protected void generateCollections(String target) {
7129:                try {
7130:                    // check each collection from the root
7131:                    String[] parts = StringUtil.split(target, "/");
7132:                    String id = "/";
7133:
7134:                    for (int i = 1; i < parts.length; i++) {
7135:                        // grow the id to the next collection
7136:                        id = id + parts[i] + "/";
7137:
7138:                        // does it exist?
7139:                        ContentCollection collection = findCollection(id);
7140:
7141:                        // if not, can we make it
7142:                        if (collection == null) {
7143:                            ContentCollectionEdit edit = addValidPermittedCollection(id);
7144:                            edit.getPropertiesEdit().addProperty(
7145:                                    ResourceProperties.PROP_DISPLAY_NAME,
7146:                                    parts[i]);
7147:                            commitCollection(edit);
7148:                        }
7149:                    }
7150:                }
7151:                // if we cannot, give up
7152:                catch (Exception any) {
7153:                    M_log.warn("generateCollections: " + any.getMessage(), any);
7154:                }
7155:
7156:            } // generateCollections
7157:
7158:            /**
7159:             * {@inheritDoc}
7160:             */
7161:            public String getSiteCollection(String siteId) {
7162:                String rv = null;
7163:
7164:                if (m_siteService.isUserSite(siteId)) {
7165:                    rv = COLLECTION_USER + m_siteService.getSiteUserId(siteId)
7166:                            + "/";
7167:                }
7168:
7169:                else if (!m_siteService.isSpecialSite(siteId)) {
7170:                    rv = COLLECTION_SITE + siteId + "/";
7171:                }
7172:
7173:                else {
7174:                    // ???
7175:                    rv = "/";
7176:                }
7177:
7178:                return rv;
7179:            }
7180:
7181:            /**
7182:             * {@inheritDoc}
7183:             */
7184:            public boolean isPubView(String id) {
7185:                boolean pubView = SecurityService.unlock(UserDirectoryService
7186:                        .getAnonymousUser(), AUTH_RESOURCE_READ,
7187:                        getReference(id));
7188:                return pubView;
7189:            }
7190:
7191:            /**
7192:             * {@inheritDoc}
7193:             */
7194:            public boolean isInheritingPubView(String id) {
7195:                // the root does not inherit... and makes a bad ref if we try to isolateContainingId()
7196:                if (isRootCollection(id))
7197:                    return false;
7198:
7199:                // check for pubview on the container
7200:                String containerId = isolateContainingId(id);
7201:                boolean pubView = SecurityService.unlock(UserDirectoryService
7202:                        .getAnonymousUser(), AUTH_RESOURCE_READ,
7203:                        getReference(containerId));
7204:                return pubView;
7205:            }
7206:
7207:            /**
7208:             * Set this resource or collection to the pubview setting.
7209:             * 
7210:             * @param id
7211:             *        The resource or collection id.
7212:             * @param pubview
7213:             *        The desired public view setting.
7214:             */
7215:            public void setPubView(String id, boolean pubview) {
7216:                // TODO: check efficiency here -ggolden
7217:
7218:                String ref = getReference(id);
7219:
7220:                // edit the realm
7221:                AuthzGroup edit = null;
7222:
7223:                try {
7224:                    edit = m_authzGroupService.getAuthzGroup(ref);
7225:                } catch (GroupNotDefinedException e) {
7226:                    // if no realm yet, and we need one, make one
7227:                    if (pubview) {
7228:                        try {
7229:                            edit = m_authzGroupService.addAuthzGroup(ref);
7230:                        } catch (Exception ee) {
7231:                        }
7232:                    }
7233:                }
7234:
7235:                // if we have no realm and don't need one, we are done
7236:                if ((edit == null) && (!pubview))
7237:                    return;
7238:
7239:                // if we need a realm and didn't get an edit, exception
7240:                if ((edit == null) && pubview)
7241:                    return;
7242:
7243:                boolean changed = false;
7244:                boolean delete = false;
7245:
7246:                // align the realm with our positive setting
7247:                if (pubview) {
7248:                    // make sure the anon role exists and has "content.read" - the only client of pubview
7249:                    Role role = edit.getRole(AuthzGroupService.ANON_ROLE);
7250:                    if (role == null) {
7251:                        try {
7252:                            role = edit.addRole(AuthzGroupService.ANON_ROLE);
7253:                        } catch (RoleAlreadyDefinedException ignore) {
7254:                        }
7255:                    }
7256:
7257:                    if (!role.isAllowed(AUTH_RESOURCE_READ)) {
7258:                        role.allowFunction(AUTH_RESOURCE_READ);
7259:                        changed = true;
7260:                    }
7261:                }
7262:
7263:                // align the realm with our negative setting
7264:                else {
7265:                    // get the role
7266:                    Role role = edit.getRole(AuthzGroupService.ANON_ROLE);
7267:                    if (role != null) {
7268:                        if (role.isAllowed(AUTH_RESOURCE_READ)) {
7269:                            changed = true;
7270:                            role.disallowFunction(AUTH_RESOURCE_READ);
7271:                        }
7272:
7273:                        if (role.allowsNoFunctions()) {
7274:                            edit.removeRole(role.getId());
7275:                            changed = true;
7276:                        }
7277:                    }
7278:
7279:                    // if "empty", we can delete the realm
7280:                    if (edit.isEmpty())
7281:                        delete = true;
7282:                }
7283:
7284:                // if we want the realm deleted
7285:                if (delete) {
7286:                    try {
7287:                        m_authzGroupService.removeAuthzGroup(edit);
7288:                    } catch (AuthzPermissionException e) {
7289:                    }
7290:                }
7291:
7292:                // if we made a change
7293:                else if (changed) {
7294:                    try {
7295:                        m_authzGroupService.save(edit);
7296:                    } catch (GroupNotDefinedException e) {
7297:                        // TODO: IdUnusedException
7298:                    } catch (AuthzPermissionException e) {
7299:                        // TODO: PermissionException
7300:                    }
7301:                }
7302:            }
7303:
7304:            /**
7305:             * {@inheritDoc}
7306:             */
7307:            public List findResources(String type, String primaryMimeType,
7308:                    String subMimeType) {
7309:                List globalList = new ArrayList();
7310:
7311:                Map othersites = getCollectionMap();
7312:                Iterator siteIt = othersites.keySet().iterator();
7313:                while (siteIt.hasNext()) {
7314:                    String collId = (String) siteIt.next();
7315:                    String displayName = (String) othersites.get(collId);
7316:                    List artifacts = getFlatResources(collId);
7317:                    globalList.addAll(filterArtifacts(artifacts, type,
7318:                            primaryMimeType, subMimeType, true));
7319:                }
7320:
7321:                return globalList;
7322:            }
7323:
7324:            /**
7325:             * get all the resources under a given directory.
7326:             * 
7327:             * @param parentId
7328:             * @return List of all the ContentResource objects under this directory.
7329:             */
7330:            protected List getFlatResources(String parentId) {
7331:                return getAllResources(parentId);
7332:            }
7333:
7334:            /**
7335:             * Eliminate from the collection any duplicates as well as any items that are contained within another item whose resource-id is in the collection.
7336:             * 
7337:             * @param resourceIds
7338:             *        A collection of strings (possibly empty) identifying items and/or collections.
7339:             */
7340:            public void eliminateDuplicates(Collection resourceIds) {
7341:                Collection dups = new Vector();
7342:
7343:                // eliminate exact duplicates
7344:                Set others = new TreeSet(resourceIds);
7345:
7346:                // eliminate items contained in other items
7347:                Iterator itemIt = resourceIds.iterator();
7348:                while (itemIt.hasNext()) {
7349:                    String item = (String) itemIt.next();
7350:                    Iterator otherIt = others.iterator();
7351:                    while (otherIt.hasNext()) {
7352:                        String other = (String) otherIt.next();
7353:                        if (other.startsWith(item)) {
7354:                            if (item.equals(other)) {
7355:                                continue;
7356:                            }
7357:
7358:                            // item contains other
7359:                            otherIt.remove();
7360:                        }
7361:                    }
7362:                }
7363:
7364:                // if any items have been removed, update the original collection
7365:                if (resourceIds.size() > others.size()) {
7366:                    resourceIds.clear();
7367:                    resourceIds.addAll(others);
7368:                }
7369:
7370:            } // eliminate duplicates
7371:
7372:            protected List filterArtifacts(List artifacts, String type,
7373:                    String primaryMimeType, String subMimeType) {
7374:                return filterArtifacts(artifacts, type, primaryMimeType,
7375:                        subMimeType, false);
7376:            }
7377:
7378:            protected List filterArtifacts(List artifacts, String type,
7379:                    String primaryMimeType, String subMimeType,
7380:                    boolean checkPerms) {
7381:                for (Iterator i = artifacts.iterator(); i.hasNext();) {
7382:                    ContentResource resource = (ContentResource) i.next();
7383:                    //check for read permissions...
7384:                    if (!checkPerms
7385:                            || unlockCheck(AUTH_RESOURCE_READ, resource.getId())) {
7386:                        String currentType = resource.getProperties()
7387:                                .getProperty(
7388:                                        ResourceProperties.PROP_STRUCTOBJ_TYPE);
7389:                        String mimeType = resource.getProperties().getProperty(
7390:                                ResourceProperties.PROP_CONTENT_TYPE);
7391:
7392:                        if (type != null
7393:                                && !type.equals(ResourceProperties.FILE_TYPE)) {
7394:                            // process StructuredObject type
7395:                            if (currentType == null) {
7396:                                i.remove();
7397:                            } else if (!currentType.equals(type)) {
7398:                                i.remove();
7399:                            }
7400:                        } else if (currentType != null
7401:                                && type.equals(ResourceProperties.FILE_TYPE)) {
7402:                            // this one is a structured object, get rid of it
7403:                            i.remove();
7404:                        } else {
7405:                            String[] parts = mimeType.split("/");
7406:                            String currentPrimaryType = parts[0];
7407:                            String currentSubtype = null;
7408:                            if (parts.length > 1)
7409:                                currentSubtype = parts[1];
7410:
7411:                            // check the mime type match
7412:                            if (primaryMimeType != null
7413:                                    && !primaryMimeType
7414:                                            .equals(currentPrimaryType)) {
7415:                                i.remove();
7416:                            } else if (subMimeType != null
7417:                                    && !subMimeType.equals(currentSubtype)) {
7418:                                i.remove();
7419:                            }
7420:                        }
7421:                    } else {
7422:                        i.remove();
7423:                    }
7424:                }
7425:                return artifacts;
7426:            }
7427:
7428:            /**********************************************************************************************************************************************************************************************************************************************************
7429:             * Dropbox Stuff
7430:             *********************************************************************************************************************************************************************************************************************************************************/
7431:
7432:            private static ResourceBundle rb = ResourceBundle
7433:                    .getBundle("content");
7434:
7435:            protected static final String PROP_MEMBER_DROPBOX_DESCRIPTION = rb
7436:                    .getString("use1");
7437:
7438:            protected static final String PROP_SITE_DROPBOX_DESCRIPTION = rb
7439:                    .getString("use2");
7440:
7441:            protected static final String DROPBOX_ID = " Drop Box";
7442:
7443:            public static final String SITE_UPDATE_ACCESS = "site.upd";
7444:
7445:            protected static final String GROUP_LIST = "sakai:authzGroup";
7446:
7447:            protected static final String GROUP_NAME = "sakai:group_name";
7448:
7449:            public static final String ACCESS_MODE = "sakai:access_mode";
7450:
7451:            public static final String RELEASE_DATE = "sakai:release_date";
7452:
7453:            public static final String RETRACT_DATE = "sakai:retract_date";
7454:
7455:            public static final String HIDDEN = "sakai:hidden";
7456:
7457:            public static final String CUSTOM_ORDER = "sakai:custom_order";
7458:
7459:            public static final String CUSTOM_RANK = "sakai:rank_element";
7460:
7461:            public static final String MEMBER_ID = "sakai:member_id";
7462:
7463:            public static final String RANK = "sakai:rank";
7464:
7465:            /**
7466:             * @inheritDoc
7467:             */
7468:            public String getDropboxCollection() {
7469:                return getDropboxCollection(ToolManager.getCurrentPlacement()
7470:                        .getContext());
7471:            }
7472:
7473:            /**
7474:             * @inheritDoc
7475:             */
7476:            public String getDropboxCollection(String siteId) {
7477:                String rv = null;
7478:
7479:                // make sure we are in a worksite, not a workspace
7480:                if (m_siteService.isUserSite(siteId)
7481:                        || m_siteService.isSpecialSite(siteId)) {
7482:                    return rv;
7483:                }
7484:
7485:                // form the site's dropbox collection
7486:                rv = COLLECTION_DROPBOX + siteId + "/";
7487:
7488:                // for maintainers, use the site level
7489:                if (isDropboxMaintainer(siteId)) {
7490:                    // return the site's dropbox collection
7491:                    return rv;
7492:                }
7493:
7494:                // form the current user's dropbox collection within this site's
7495:                rv += StringUtil.trimToZero(SessionManager
7496:                        .getCurrentSessionUserId())
7497:                        + "/";
7498:                return rv;
7499:            }
7500:
7501:            /**
7502:             * Access the default dropbox collection display name for the current request. If the current user has permission to modify the site's dropbox collection, this is returned. Otherwise, the current user's collection within the site's dropbox is
7503:             * returned.
7504:             * 
7505:             * @return The default dropbox collection display name for the current request.
7506:             */
7507:            public String getDropboxDisplayName() {
7508:                return getDropboxDisplayName(ToolManager.getCurrentPlacement()
7509:                        .getContext());
7510:            }
7511:
7512:            /**
7513:             * Access the default dropbox collection display name for the site. If the current user has permission to modify the site's dropbox collection, this is returned. Otherwise, the current user's collection within the site's dropbox is returned.
7514:             * 
7515:             * @param siteId
7516:             *        the Site id.
7517:             * @return The default dropbox collection display name for the site.
7518:             */
7519:            public String getDropboxDisplayName(String siteId) {
7520:                // make sure we are in a worksite, not a workspace
7521:                if (m_siteService.isUserSite(siteId)
7522:                        || m_siteService.isSpecialSite(siteId)) {
7523:                    return null;
7524:                }
7525:
7526:                // form the site's dropbox collection
7527:                String id = COLLECTION_DROPBOX + siteId + "/";
7528:
7529:                // for maintainers, use the site level dropbox
7530:                if (isDropboxMaintainer(siteId)) {
7531:                    // return the site's dropbox collection
7532:                    return siteId + DROPBOX_ID;
7533:                }
7534:
7535:                // return the current user's sort name
7536:                return UserDirectoryService.getCurrentUser().getSortName();
7537:            }
7538:
7539:            /**
7540:             * Create the site's dropbox collection and one for each qualified user that the current user can make.
7541:             */
7542:            public void createDropboxCollection() {
7543:                createDropboxCollection(ToolManager.getCurrentPlacement()
7544:                        .getContext());
7545:            }
7546:
7547:            /**
7548:             * Create the site's dropbox collection and one for each qualified user that the current user can make.
7549:             * 
7550:             * @param siteId
7551:             *        the Site id.
7552:             */
7553:            public void createDropboxCollection(String siteId) {
7554:                // make sure we are in a worksite, not a workspace
7555:                if (m_siteService.isUserSite(siteId)
7556:                        || m_siteService.isSpecialSite(siteId)) {
7557:                    return;
7558:                }
7559:
7560:                // do our ONE security check to see if the current user can create the
7561:                // dropbox and all inner folders
7562:                if (!isDropboxMaintainer(siteId)) {
7563:                    createIndividualDropbox(siteId);
7564:                    return;
7565:                }
7566:
7567:                // form the site's dropbox collection
7568:                String dropbox = COLLECTION_DROPBOX + siteId + "/";
7569:
7570:                try {
7571:                    // try to create if it doesn't exist
7572:                    if (findCollection(dropbox) == null) {
7573:                        ContentCollectionEdit edit = addValidPermittedCollection(dropbox);
7574:                        ResourcePropertiesEdit props = edit.getPropertiesEdit();
7575:                        try {
7576:                            Site site = m_siteService.getSite(siteId);
7577:                        } catch (IdUnusedException e) {
7578:                            // TODO Auto-generated catch block
7579:                            e.printStackTrace();
7580:                        }
7581:
7582:                        props.addProperty(ResourceProperties.PROP_DISPLAY_NAME,
7583:                                siteId + DROPBOX_ID);
7584:                        props.addProperty(ResourceProperties.PROP_DESCRIPTION,
7585:                                PROP_SITE_DROPBOX_DESCRIPTION);
7586:                        commitCollection(edit);
7587:                    }
7588:                } catch (TypeException e) {
7589:                    M_log.warn("createDropboxCollection: TypeException: "
7590:                            + dropbox);
7591:                    return;
7592:                } catch (IdUsedException e) {
7593:                    M_log.warn("createDropboxCollection: IdUsedException: "
7594:                            + dropbox);
7595:                    return;
7596:                } catch (InconsistentException e) {
7597:                    M_log
7598:                            .warn("createDropboxCollection(): InconsistentException: "
7599:                                    + dropbox);
7600:                    M_log
7601:                            .warn("createDropboxCollection(): InconsistentException: "
7602:                                    + e.getMessage());
7603:                    return;
7604:                }
7605:                //		catch (PermissionException e) 
7606:                //		{
7607:                //			M_log.warn("createDropboxCollection(): PermissionException: " + dropbox);
7608:                //			return;
7609:                //		}
7610:
7611:                // The AUTH_DROPBOX_OWN is granted within the site, so we can ask for all the users who have this ability
7612:                // using just the dropbox collection
7613:                List users = SecurityService.unlockUsers(AUTH_DROPBOX_OWN,
7614:                        getReference(dropbox));
7615:                for (Iterator it = users.iterator(); it.hasNext();) {
7616:                    User user = (User) it.next();
7617:
7618:                    // the folder id for this user's dropbox in this group
7619:                    String userFolder = dropbox + user.getId() + "/";
7620:
7621:                    // see if it exists - add if it doesn't
7622:                    try {
7623:                        if (findCollection(userFolder) == null) {
7624:                            ContentCollectionEdit edit = addValidPermittedCollection(userFolder);
7625:                            ResourcePropertiesEdit props = edit
7626:                                    .getPropertiesEdit();
7627:                            props.addProperty(
7628:                                    ResourceProperties.PROP_DISPLAY_NAME, user
7629:                                            .getSortName());
7630:                            props.addProperty(
7631:                                    ResourceProperties.PROP_DESCRIPTION,
7632:                                    PROP_MEMBER_DROPBOX_DESCRIPTION);
7633:                            commitCollection(edit);
7634:                        }
7635:                    } catch (TypeException e) {
7636:                        M_log
7637:                                .warn("createDropboxCollectionn(): TypeException: "
7638:                                        + userFolder);
7639:                    } catch (IdUsedException e) {
7640:                        M_log
7641:                                .warn("createDropboxCollectionn(): idUsedException: "
7642:                                        + userFolder);
7643:                    } catch (InconsistentException e) {
7644:                        M_log
7645:                                .warn("createDropboxCollection(): InconsistentException: "
7646:                                        + userFolder);
7647:                    }
7648:                    //			catch (PermissionException e) 
7649:                    //			{
7650:                    //				M_log.warn("createDropboxCollection(): PermissionException: " + userFolder);
7651:                    //			}
7652:                }
7653:            }
7654:
7655:            /**
7656:             * Create an individual dropbox collection for the current user if the site-level dropbox exists
7657:             * and the current user has AUTH_DROPBOX_OWN for the site.
7658:             * 
7659:             * @param siteId
7660:             *        the Site id.
7661:             */
7662:            public void createIndividualDropbox(String siteId) {
7663:                String dropbox = COLLECTION_DROPBOX + siteId + "/";
7664:
7665:                try {
7666:                    if (findCollection(dropbox) == null) {
7667:                        try {
7668:                            ContentCollectionEdit edit = addValidPermittedCollection(dropbox);
7669:                            commitCollection(edit);
7670:                        } catch (IdUsedException e) {
7671:                            // hmmmm ... couldn't find it, but it's already in use???  let's bail out.
7672:                            return;
7673:                        } catch (InconsistentException e) {
7674:                            return;
7675:                        }
7676:                    }
7677:
7678:                    User user = UserDirectoryService.getCurrentUser();
7679:
7680:                    // the folder id for this user's dropbox in this group
7681:                    String userFolder = dropbox + user.getId() + "/";
7682:
7683:                    if (SecurityService.unlock(AUTH_DROPBOX_OWN,
7684:                            getReference(dropbox))) {
7685:                        // see if it exists - add if it doesn't
7686:                        try {
7687:                            if (findCollection(userFolder) == null) {
7688:                                ContentCollectionEdit edit = addValidPermittedCollection(userFolder);
7689:                                ResourcePropertiesEdit props = edit
7690:                                        .getPropertiesEdit();
7691:                                props.addProperty(
7692:                                        ResourceProperties.PROP_DISPLAY_NAME,
7693:                                        user.getSortName());
7694:                                props.addProperty(
7695:                                        ResourceProperties.PROP_DESCRIPTION,
7696:                                        PROP_MEMBER_DROPBOX_DESCRIPTION);
7697:                                commitCollection(edit);
7698:                            }
7699:                        } catch (TypeException e) {
7700:                            M_log
7701:                                    .warn("createIndividualDropbox(): TypeException: "
7702:                                            + userFolder);
7703:                        } catch (IdUsedException e) {
7704:                            M_log
7705:                                    .warn("createIndividualDropbox(): idUsedException: "
7706:                                            + userFolder);
7707:                        } catch (InconsistentException e) {
7708:                            M_log
7709:                                    .warn("createIndividualDropbox(): InconsistentException: "
7710:                                            + userFolder);
7711:                        }
7712:                        //				catch (PermissionException e) 
7713:                        //				{
7714:                        //					M_log.warn("createIndividualDropbox(): PermissionException: " + userFolder);
7715:                        //				}
7716:                    }
7717:
7718:                } catch (TypeException e) {
7719:                    M_log.warn("createIndividualDropbox(): TypeException: "
7720:                            + dropbox);
7721:                }
7722:
7723:            }
7724:
7725:            /**
7726:             * Determine whether the default dropbox collection id for this user in this site 
7727:             * is the site's entire dropbox collection or just the current user's collection 
7728:             * within the site's dropbox.	 
7729:             * @return True if user sees all dropboxes in the site, false otherwise.
7730:             */
7731:            public boolean isDropboxMaintainer() {
7732:                return isDropboxMaintainer(ToolManager.getCurrentPlacement()
7733:                        .getContext());
7734:            }
7735:
7736:            /**
7737:             * Determine whether the default dropbox collection id for this user in some site is the site's entire dropbox collection or just the current user's collection within the site's dropbox.
7738:             * 
7739:             * @return True if user sees all dropboxes in the site, false otherwise.
7740:             */
7741:            public boolean isDropboxMaintainer(String siteId) {
7742:                String dropboxId = null;
7743:
7744:                // make sure we are in a worksite, not a workspace
7745:                if (m_siteService.isUserSite(siteId)
7746:                        || m_siteService.isSpecialSite(siteId)) {
7747:                    return false;
7748:                }
7749:
7750:                // if the user has dropbox maintain in the site, they are the dropbox maintainer
7751:                // (dropbox maintain in their myWorkspace just gives them access to their own dropbox)
7752:                return SecurityService.unlock(AUTH_DROPBOX_MAINTAIN,
7753:                        m_siteService.siteReference(siteId));
7754:            }
7755:
7756:            /******************************************************************************************************************************************************************************************************************************************************
7757:             * Group awareness implementation
7758:             *****************************************************************************************************************************************************************************************************************************************************/
7759:
7760:            /**
7761:             * Access a collection (Group) of groups to which this user has access and whose members have "content.read" permission in the collection. 
7762:             * In effect, this method returns a collection that identifies groups that are defined for the collection (locally or inherited) that 
7763:             * this user can access. If access to the collection is determined by group-membership, the return is limited to groups that have 
7764:             * access to the specified collection. If access is not defined by groups (i.e. it is "site" access), the return includes all groups
7765:             * defined in the site for which this user has read permission.
7766:             * 
7767:             * @param collectionId
7768:             *        The id for the collection.
7769:             */
7770:            public Collection getGroupsWithReadAccess(String collectionId) {
7771:                Collection rv = new Vector();
7772:
7773:                String refString = getReference(collectionId);
7774:                Reference ref = m_entityManager.newReference(refString);
7775:                Collection groups = getGroupsAllowFunction(AUTH_RESOURCE_READ,
7776:                        ref.getReference());
7777:                if (groups != null && !groups.isEmpty()) {
7778:                    rv.addAll(groups);
7779:                }
7780:                return rv;
7781:            }
7782:
7783:            /**
7784:             * Access a collection (Group) of groups to which this user has access and whose members have "content.new" permission in the collection. 
7785:             * In effect, this method returns a collection that identifies groups that are defined for the collection (locally or inherited) in which 
7786:             * this user has permission to add content entities. If access to the collection is determined by group-membership, the return is limited 
7787:             * to groups that have "add" permission in the specified collection. If access is not defined by groups (i.e. it is "site" access), the return 
7788:             * includes all groups defined in the site for which this user has add permission in this collection.
7789:             * 
7790:             * @param collectionId
7791:             *        The id for the collection.
7792:             */
7793:            public Collection getGroupsWithAddPermission(String collectionId) {
7794:                Collection rv = new Vector();
7795:
7796:                String refString = getReference(collectionId);
7797:                Reference ref = m_entityManager.newReference(refString);
7798:                Collection groups = getGroupsAllowFunction(AUTH_RESOURCE_ADD,
7799:                        ref.getReference());
7800:                if (groups != null && !groups.isEmpty()) {
7801:                    rv.addAll(groups);
7802:                }
7803:                return rv;
7804:            }
7805:
7806:            /**
7807:             * Access a collection (Group) of groups to which this user has access and whose members have "content.delete" permission in the collection. 
7808:             * In effect, this method returns a collection that identifies groups that are defined for the collection (locally or inherited) in which 
7809:             * this user has permission to remove content entities. If access to the collection is determined by group-membership, the return is limited 
7810:             * to groups that have "remove" permission in the specified collection. If access is not defined by groups (i.e. it is "site" access), the return 
7811:             * includes all groups defined in the site for which this user has remove permission in this collection.
7812:             * 
7813:             * @param collectionId
7814:             *        The id for the collection.
7815:             */
7816:            public Collection getGroupsWithRemovePermission(String collectionId) {
7817:                Collection rv = new Vector();
7818:                String owner = "";
7819:                String currentUser = SessionManager.getCurrentSessionUserId();
7820:
7821:                try {
7822:                    ResourceProperties props = getProperties(collectionId);
7823:                    owner = props.getProperty(ResourceProperties.PROP_CREATOR);
7824:                } catch (Exception e) {
7825:                    // assume user is not owner
7826:                }
7827:
7828:                String refString = getReference(collectionId);
7829:                Reference ref = m_entityManager.newReference(refString);
7830:
7831:                Collection groups = null;
7832:                if (currentUser.equals(owner))
7833:                    groups = getGroupsAllowFunction(AUTH_RESOURCE_REMOVE_OWN,
7834:                            ref.getReference());
7835:                else
7836:                    groups = getGroupsAllowFunction(AUTH_RESOURCE_REMOVE_ANY,
7837:                            ref.getReference());
7838:
7839:                if (groups != null && !groups.isEmpty()) {
7840:                    rv.addAll(groups);
7841:                }
7842:                return rv;
7843:            }
7844:
7845:            /**
7846:             * Get a collection (Group) of groups that are defined in the containing context of a resource and that this user can access 
7847:             * in the way described by a function string.
7848:             * 
7849:             * @param function
7850:             *        The function to check
7851:             * @param refString
7852:             *        The reference for the resource.
7853:             */
7854:            protected Collection getGroupsAllowFunction(String function,
7855:                    String refString) {
7856:                Collection rv = new Vector();
7857:
7858:                Collection groups = new Vector();
7859:                Collection groupRefs = new TreeSet();
7860:                if (this .m_allowGroupResources) {
7861:                    ContentEntity entity;
7862:                    try {
7863:                        Reference ref = m_entityManager.newReference(refString);
7864:                        Site site = m_siteService.getSite(ref.getContext());
7865:
7866:                        if (ref.getId().endsWith(Entity.SEPARATOR)) {
7867:                            entity = findCollection(ref.getId());
7868:                        } else {
7869:                            entity = findResource(ref.getId());
7870:                        }
7871:
7872:                        if (entity != null) {
7873:                            if (AccessMode.INHERITED == entity.getAccess()) {
7874:                                groups
7875:                                        .addAll(entity
7876:                                                .getInheritedGroupObjects());
7877:                                groupRefs.addAll(entity.getInheritedGroups());
7878:                            } else {
7879:                                groups.addAll(entity.getGroupObjects());
7880:                                groupRefs.addAll(entity.getGroups());
7881:                            }
7882:                        }
7883:
7884:                        if (groups.isEmpty()) {
7885:                            // get the channel's site's groups
7886:                            groups.addAll(site.getGroups());
7887:                            for (Iterator i = groups.iterator(); i.hasNext();) {
7888:                                Group group = (Group) i.next();
7889:                                groupRefs.add(group.getReference());
7890:                            }
7891:                        }
7892:
7893:                        if (SecurityService.isSuperUser()) {
7894:                            rv.addAll(groups);
7895:                        } else if (SecurityService.unlock(
7896:                                AUTH_RESOURCE_ALL_GROUPS, site.getReference())
7897:                                && entity != null
7898:                                && unlockCheck(function, entity.getId())) {
7899:                            rv.addAll(groups);
7900:                        } else {
7901:                            Collection hierarchy = getEntityHierarchyAuthzGroups(ref);
7902:                            String userId = SessionManager
7903:                                    .getCurrentSessionUserId();
7904:
7905:                            for (Iterator i = groups.iterator(); i.hasNext();) {
7906:                                Group group = (Group) i.next();
7907:                                Collection azGroups = new Vector(hierarchy);
7908:                                azGroups.add(group.getReference());
7909:
7910:                                // check whether this user can take this action (function) on this resource
7911:                                // based on membership in this group.  If so, add the group.
7912:                                if (m_authzGroupService.isAllowed(userId,
7913:                                        function, azGroups)) {
7914:                                    rv.add(group);
7915:                                }
7916:                            }
7917:                        }
7918:
7919:                    } catch (TypeException e1) {
7920:                        // ignore
7921:                    } catch (IdUnusedException e) {
7922:                        // ignore
7923:                    }
7924:
7925:                }
7926:                return rv;
7927:            }
7928:
7929:            /**
7930:             * If the id is to the /user/ area, make an id that is based on the user EID not ID, if the EID is available.
7931:             * @param id The resource id.
7932:             * @return The modified id.
7933:             */
7934:            protected String convertIdToUserEid(String id) {
7935:                if (id.startsWith("/user/")) {
7936:                    try {
7937:                        int pos = id.indexOf('/', 6);
7938:                        String userId = id.substring(6, pos);
7939:                        String userEid = UserDirectoryService
7940:                                .getUserEid(userId);
7941:                        String rv = "/user/" + userEid + id.substring(pos);
7942:                        return rv;
7943:                    } catch (StringIndexOutOfBoundsException e) {
7944:                    } catch (UserNotDefinedException e) {
7945:                    }
7946:                }
7947:
7948:                return id;
7949:            }
7950:
7951:            /**********************************************************************************************************************************************************************************************************************************************************
7952:             * ContentEntity implementation
7953:             *********************************************************************************************************************************************************************************************************************************************************/
7954:
7955:            public abstract class BasicGroupAwareEdit implements  GroupAwareEdit {
7956:                /** Store the resource id */
7957:                protected String m_id = null;
7958:
7959:                /** The properties. */
7960:                protected ResourcePropertiesEdit m_properties = null;
7961:
7962:                /** The event code for this edit. */
7963:                protected String m_event = null;
7964:
7965:                /** Active flag. */
7966:                protected boolean m_active = false;
7967:
7968:                /** When true, the collection has been removed. */
7969:                protected boolean m_isRemoved = false;
7970:
7971:                /** The access mode for this entity (e.g., "group" vs "site") */
7972:                protected AccessMode m_access = AccessMode.INHERITED;
7973:
7974:                /** The date/time after which the entity should no longer be generally available */
7975:                protected Time m_retractDate = null;
7976:
7977:                /** The date/time before which the entity should not be generally available */
7978:                protected Time m_releaseDate = null;
7979:
7980:                /** The availability of the item */
7981:                protected boolean m_hidden = false;
7982:
7983:                /** The Collection of group-ids for groups with access to this entity. */
7984:                protected Collection m_groups = new Vector();
7985:
7986:                /** The "priority" of this entity in its containing collection, if a custom sort order is defined for that collection */
7987:                protected int m_customOrderRank = 0;
7988:
7989:                /** The "type" in the ResourceTypeRegistry that defines properties of this ContentEntity */
7990:                protected String m_resourceType;
7991:
7992:                /**
7993:                 * @inheritDoc
7994:                 */
7995:                public Collection getGroups() {
7996:                    return new Vector(m_groups);
7997:                }
7998:
7999:                /**
8000:                 * @inheritDoc
8001:                 */
8002:                public void clearGroupAccess() throws InconsistentException,
8003:                        PermissionException {
8004:                    if (this .m_access != AccessMode.GROUPED) {
8005:                        throw new InconsistentException(this .getReference());
8006:                    }
8007:
8008:                    this .m_access = AccessMode.INHERITED;
8009:                    this .m_groups.clear();
8010:
8011:                }
8012:
8013:                /**
8014:                 * @inheritDoc
8015:                 */
8016:                public void clearPublicAccess() throws InconsistentException,
8017:                        PermissionException {
8018:                    setPubView(this .m_id, false);
8019:                    this .m_access = AccessMode.INHERITED;
8020:                    this .m_groups.clear();
8021:
8022:                }
8023:
8024:                public void setPublicAccess() throws PermissionException {
8025:                    setPubView(this .m_id, true);
8026:                    this .m_access = AccessMode.INHERITED;
8027:                    this .m_groups.clear();
8028:                }
8029:
8030:                /**
8031:                 * @inheritDoc
8032:                 */
8033:                public void setGroupAccess(Collection groups)
8034:                        throws InconsistentException, PermissionException {
8035:                    if (groups == null || groups.isEmpty()) {
8036:                        throw new InconsistentException(this .getReference());
8037:                    }
8038:
8039:                    if (isInheritingPubView(this .m_id)) {
8040:                        throw new InconsistentException(this .getReference());
8041:                    }
8042:
8043:                    if (isPubView(this .m_id)) {
8044:                        setPubView(this .m_id, false);
8045:                    }
8046:
8047:                    SortedSet groupRefs = new TreeSet();
8048:                    if (this .getInheritedAccess() == AccessMode.GROUPED) {
8049:                        groupRefs.addAll(this .getInheritedGroups());
8050:                    } else {
8051:                        try {
8052:                            Reference ref = m_entityManager.newReference(this 
8053:                                    .getReference());
8054:                            Site site = m_siteService.getSite(ref.getContext());
8055:                            Iterator iterator = site.getGroups().iterator();
8056:                            while (iterator.hasNext()) {
8057:                                Group group = (Group) iterator.next();
8058:                                groupRefs.add(group.getReference());
8059:                            }
8060:                        } catch (IdUnusedException e) {
8061:
8062:                        }
8063:                    }
8064:
8065:                    Collection newGroups = new Vector();
8066:                    Iterator groupIt = groups.iterator();
8067:                    while (groupIt.hasNext()) {
8068:                        String groupRef = null;
8069:                        Object obj = groupIt.next();
8070:                        if (obj instanceof  String) {
8071:                            groupRef = (String) obj;
8072:                        } else if (obj instanceof  Group) {
8073:                            groupRef = ((Group) obj).getReference();
8074:                        }
8075:                        if (!groupRefs.contains(groupRef)) {
8076:                            throw new InconsistentException(this .getReference());
8077:                        }
8078:                        newGroups.add(groupRef);
8079:                    }
8080:
8081:                    this .m_access = AccessMode.GROUPED;
8082:                    this .m_groups.clear();
8083:                    this .m_groups.addAll(newGroups);
8084:
8085:                }
8086:
8087:                /**
8088:                 * @inheritDoc
8089:                 * @see org.sakaiproject.content.api.GroupAwareEntity#getGroupObjects()
8090:                 */
8091:                public Collection getGroupObjects() {
8092:                    if (m_groups == null) {
8093:                        m_groups = new Vector();
8094:                    }
8095:                    Collection groups = new Vector();
8096:                    Iterator it = m_groups.iterator();
8097:                    while (it.hasNext()) {
8098:                        String ref = (String) it.next();
8099:                        Group group = m_siteService.findGroup(ref);
8100:                        groups.add(group);
8101:                    }
8102:                    return groups;
8103:
8104:                }
8105:
8106:                /**
8107:                 * @inheritDoc
8108:                 */
8109:                public AccessMode getAccess() {
8110:                    return m_access;
8111:                }
8112:
8113:                /**
8114:                 * @inheritDoc
8115:                 * @see org.sakaiproject.content.api.GroupAwareEntity#getInheritedGroups()
8116:                 */
8117:                public Collection getInheritedGroups() {
8118:                    Collection groups = new Vector();
8119:                    ContentEntity next = ((ContentEntity) this )
8120:                            .getContainingCollection();
8121:                    while (next != null
8122:                            && AccessMode.INHERITED.equals(next.getAccess())) {
8123:                        next = next.getContainingCollection();
8124:                    }
8125:                    if (next != null
8126:                            && AccessMode.GROUPED.equals(next.getAccess())) {
8127:                        groups.addAll(next.getGroups());
8128:                    }
8129:                    return groups;
8130:                }
8131:
8132:                /**
8133:                 * @inheritDoc
8134:                 * @see org.sakaiproject.content.api.GroupAwareEntity#getInheritedAccess()
8135:                 */
8136:                public AccessMode getInheritedAccess() {
8137:                    AccessMode access = AccessMode.INHERITED;
8138:                    ContentCollection parent = ((ContentEntity) this )
8139:                            .getContainingCollection();
8140:                    if (parent != null) {
8141:                        access = parent.getAccess();
8142:                    }
8143:                    while (AccessMode.INHERITED == access && parent != null) {
8144:                        access = parent.getAccess();
8145:                        parent = parent.getContainingCollection();
8146:                    }
8147:                    if (AccessMode.INHERITED == access) {
8148:                        access = AccessMode.SITE;
8149:                    }
8150:                    return access;
8151:                }
8152:
8153:                /**
8154:                 * @inheritDoc
8155:                 * @see org.sakaiproject.content.api.GroupAwareEntity#getInheritedGroupObjects()
8156:                 */
8157:                public Collection getInheritedGroupObjects() {
8158:                    Collection groups = new Vector();
8159:                    Collection groupRefs = getInheritedGroups();
8160:                    Iterator it = groupRefs.iterator();
8161:                    while (it.hasNext()) {
8162:                        String groupRef = (String) it.next();
8163:                        Group group = m_siteService.findGroup(groupRef);
8164:                        groups.add(group);
8165:                    }
8166:                    return groups;
8167:                }
8168:
8169:                /** 
8170:                 * Determine whether current user can update the group assignments (add/remove groups) for the current resource.
8171:                 * This is based on whether the user has adequate rights defined for the group (AUTH_RESOURCE_ADD) or for the 
8172:                 * containing collection of the resource (AUTH_RESOURCE_ADD).  
8173:                 * @param group The group ionvolved in the query.
8174:                 * @return true if allowed, false otherwise.
8175:                 */
8176:                protected boolean allowGroupUpdate(Group group) {
8177:                    String resourceRef = getReference();
8178:                    return allowGroupUpdate(group, resourceRef);
8179:                }
8180:
8181:                /** 
8182:                 * Determine whether current user can update the group assignments (add/remove groups) for a specified resource.
8183:                 * This is based on whether the user has adequate rights defined for the group (AUTH_RESOURCE_ADD) or for the 
8184:                 * containing collection of the resource (AUTH_RESOURCE_ADD).  
8185:                 * @param group The group ionvolved in the query.
8186:                 * @param resourceRef A reference string for the resource.
8187:                 * @return true if allowed, false otherwise.
8188:                 */
8189:                protected boolean allowGroupUpdate(Group group,
8190:                        String resourceRef) {
8191:                    String collectionId = getContainingCollectionId(resourceRef);
8192:                    return unlockCheck(AUTH_RESOURCE_ADD, group.getReference())
8193:                            || unlockCheck(AUTH_RESOURCE_ADD, collectionId);
8194:                }
8195:
8196:                public Time getReleaseDate() {
8197:                    return m_releaseDate;
8198:                }
8199:
8200:                public Time getRetractDate() {
8201:                    // TODO Auto-generated method stub
8202:                    return m_retractDate;
8203:                }
8204:
8205:                public boolean isAvailable() {
8206:                    boolean available = !m_hidden;
8207:
8208:                    if (available
8209:                            && (this .m_releaseDate != null || this .m_retractDate != null)) {
8210:                        Time now = TimeService.newTime();
8211:                        if (this .m_releaseDate != null) {
8212:                            available = this .m_releaseDate.before(now);
8213:                        }
8214:                        if (available && this .m_retractDate != null) {
8215:                            available = this .m_retractDate.after(now);
8216:                        }
8217:                    }
8218:                    if (!available) {
8219:                        return available;
8220:                    }
8221:                    ContentCollection parent = ((ContentEntity) this )
8222:                            .getContainingCollection();
8223:                    if (parent == null) {
8224:                        return available;
8225:                    }
8226:                    return parent.isAvailable();
8227:                }
8228:
8229:                public boolean isHidden() {
8230:                    return this .m_hidden;
8231:                }
8232:
8233:                public void setReleaseDate(Time time) {
8234:                    if (time == null) {
8235:                        m_releaseDate = null;
8236:                    } else {
8237:                        m_releaseDate = TimeService.newTime(time.getTime());
8238:                    }
8239:                    m_hidden = false;
8240:                }
8241:
8242:                public void setRetractDate(Time time) {
8243:                    if (time == null) {
8244:                        m_retractDate = null;
8245:                    } else {
8246:                        m_retractDate = TimeService.newTime(time.getTime());
8247:                    }
8248:                    m_hidden = false;
8249:                }
8250:
8251:                public void setAvailability(boolean hidden, Time releaseDate,
8252:                        Time retractDate) {
8253:                    m_hidden = hidden;
8254:                    if (hidden) {
8255:                        this .m_releaseDate = null;
8256:                        this .m_retractDate = null;
8257:                    } else {
8258:                        if (releaseDate == null) {
8259:                            this .m_releaseDate = null;
8260:                        } else {
8261:                            this .m_releaseDate = TimeService
8262:                                    .newTime(releaseDate.getTime());
8263:                        }
8264:                        if (retractDate == null) {
8265:                            this .m_retractDate = null;
8266:                        } else {
8267:                            this .m_retractDate = TimeService
8268:                                    .newTime(retractDate.getTime());
8269:                        }
8270:                    }
8271:
8272:                }
8273:
8274:                public void setHidden() {
8275:                    m_hidden = true;
8276:                    this .m_releaseDate = null;
8277:                    this .m_retractDate = null;
8278:                }
8279:
8280:                /* (non-Javadoc)
8281:                 * @see org.sakaiproject.content.api.ContentEntity#getResourceType()
8282:                 */
8283:                public String getResourceType() {
8284:                    return m_resourceType;
8285:                }
8286:
8287:                public ContentCollection getContainingCollection() {
8288:                    ContentCollection container = null;
8289:                    String containerId = isolateContainingId(this .getId());
8290:                    try {
8291:                        container = findCollection(containerId);
8292:                    } catch (TypeException e) {
8293:                    }
8294:                    return container;
8295:                }
8296:
8297:                public void setPriority() {
8298:                    ResourcePropertiesEdit props = getPropertiesEdit();
8299:                    String sortBy = props
8300:                            .getProperty(ResourceProperties.PROP_CONTENT_PRIORITY);
8301:                    if (sortBy == null) {
8302:                        // add a default value that sorts new items after existing items, with new folders before new resources
8303:                        String containingCollectionId = isolateContainingId(this .m_id);
8304:                        int count = 1;
8305:                        if (containingCollectionId != null) {
8306:                            try {
8307:                                count = getCollectionSize(containingCollectionId) + 1;
8308:                            } catch (IdUnusedException e) {
8309:                                // TODO Auto-generated catch block
8310:                                e.printStackTrace();
8311:                            } catch (TypeException e) {
8312:                                // TODO Auto-generated catch block
8313:                                e.printStackTrace();
8314:                            } catch (PermissionException e) {
8315:                                // TODO Auto-generated catch block
8316:                                e.printStackTrace();
8317:                            }
8318:                        }
8319:                        if (!m_id.endsWith(Entity.SEPARATOR)) {
8320:                            count += ContentHostingService.CONTENT_RESOURCE_PRIORITY_OFFSET;
8321:                        }
8322:                        props.addProperty(
8323:                                ResourceProperties.PROP_CONTENT_PRIORITY,
8324:                                Integer.toString(count));
8325:                    }
8326:                }
8327:
8328:            } // BasicGroupAwareEntity
8329:
8330:            /**********************************************************************************************************************************************************************************************************************************************************
8331:             * ContentCollection implementation
8332:             *********************************************************************************************************************************************************************************************************************************************************/
8333:            public class BaseCollectionEdit extends BasicGroupAwareEdit
8334:                    implements  ContentCollectionEdit, SessionBindingListener {
8335:                /**
8336:                 * Construct with an id.
8337:                 * 
8338:                 * @param id
8339:                 *        The unique channel id.
8340:                 */
8341:                public BaseCollectionEdit(String id) {
8342:                    // set the id
8343:                    m_id = id;
8344:
8345:                    // setup for properties
8346:                    m_properties = new BaseResourcePropertiesEdit();
8347:
8348:                    m_resourceType = ResourceType.TYPE_FOLDER;
8349:
8350:                } // BaseCollectionEdit
8351:
8352:                /**
8353:                 * Construct as a copy of another.
8354:                 * 
8355:                 * @param other
8356:                 *        The other to copy.
8357:                 */
8358:                public BaseCollectionEdit(ContentCollection other) {
8359:                    set(other);
8360:                    m_resourceType = ResourceType.TYPE_FOLDER;
8361:
8362:                } // BaseCollectionEdit
8363:
8364:                /**
8365:                 * Construct from info in XML in a DOM element.
8366:                 * 
8367:                 * @param el
8368:                 *        The XML DOM element.
8369:                 */
8370:                public BaseCollectionEdit(Element el) {
8371:                    // setup for properties
8372:                    m_properties = new BaseResourcePropertiesEdit();
8373:
8374:                    m_id = el.getAttribute("id");
8375:                    m_resourceType = ResourceType.TYPE_FOLDER;
8376:
8377:                    String refStr = getReference(m_id);
8378:                    Reference ref = m_entityManager.newReference(refStr);
8379:                    String context = ref.getContext();
8380:                    Site site = null;
8381:                    try {
8382:                        site = m_siteService.getSite(ref.getContext());
8383:                    } catch (IdUnusedException e) {
8384:
8385:                    }
8386:
8387:                    // the children (properties)
8388:                    NodeList children = el.getChildNodes();
8389:                    final int length = children.getLength();
8390:                    for (int i = 0; i < length; i++) {
8391:                        Node child = children.item(i);
8392:                        if (child.getNodeType() != Node.ELEMENT_NODE)
8393:                            continue;
8394:                        Element element = (Element) child;
8395:
8396:                        // look for properties
8397:                        if (element.getTagName().equals("properties")) {
8398:                            // re-create properties
8399:                            m_properties = new BaseResourcePropertiesEdit(
8400:                                    element);
8401:                            if (m_prioritySortEnabled) {
8402:                                // setPriority();
8403:                            }
8404:                        }
8405:                        // look for groups 
8406:                        else if (element.getTagName().equals(GROUP_LIST)) {
8407:                            String groupRef = element.getAttribute(GROUP_NAME);
8408:                            if (groupRef != null) {
8409:                                m_groups.add(groupRef);
8410:                            }
8411:                        } else if (element.getTagName().equals(
8412:                                "rightsAssignment")) {
8413:
8414:                        }
8415:                    }
8416:
8417:                    // extract access
8418:                    AccessMode access = AccessMode.INHERITED;
8419:                    String access_mode = el.getAttribute(ACCESS_MODE);
8420:                    if (access_mode != null && !access_mode.trim().equals("")) {
8421:                        access = AccessMode.fromString(access_mode);
8422:                    }
8423:
8424:                    m_access = access;
8425:                    if (m_access == null || AccessMode.SITE == m_access) {
8426:                        m_access = AccessMode.INHERITED;
8427:                    }
8428:
8429:                    // extract release date
8430:                    // m_releaseDate = TimeService.newTime(0);
8431:                    String date0 = el.getAttribute(RELEASE_DATE);
8432:                    if (date0 != null && !date0.trim().equals("")) {
8433:                        m_releaseDate = TimeService.newTimeGmt(date0);
8434:                        if (m_releaseDate.getTime() <= START_OF_TIME) {
8435:                            m_releaseDate = null;
8436:                        }
8437:                    }
8438:
8439:                    // extract retract date
8440:                    // m_retractDate = TimeService.newTimeGmt(9999,12, 31, 23, 59, 59, 999);
8441:                    String date1 = el.getAttribute(RETRACT_DATE);
8442:                    if (date1 != null && !date1.trim().equals("")) {
8443:                        m_retractDate = TimeService.newTimeGmt(date1);
8444:                        if (m_retractDate.getTime() >= END_OF_TIME) {
8445:                            m_retractDate = null;
8446:                        }
8447:                    }
8448:
8449:                    String hidden = el.getAttribute(HIDDEN);
8450:                    m_hidden = hidden != null
8451:                            && !hidden.trim().equals("")
8452:                            && !Boolean.FALSE.toString().equalsIgnoreCase(
8453:                                    hidden);
8454:
8455:                } // BaseCollectionEdit
8456:
8457:                /**
8458:                 * Take all values from this object.
8459:                 * 
8460:                 * @param user
8461:                 *        The other object to take values from.
8462:                 */
8463:                protected void set(ContentCollection other) {
8464:                    // set the id
8465:                    m_id = other.getId();
8466:
8467:                    // copy other's access mode and list of groups
8468:                    m_access = other.getAccess();
8469:                    m_groups.clear();
8470:                    m_groups.addAll(other.getGroups());
8471:                    chh = other.getContentHandler();
8472:                    chh_vce = other.getVirtualContentEntity();
8473:
8474:                    // setup for properties
8475:                    m_properties = new BaseResourcePropertiesEdit();
8476:                    m_properties.addAll(other.getProperties());
8477:
8478:                    m_hidden = other.isHidden();
8479:
8480:                    if (m_hidden || other.getReleaseDate() == null) {
8481:                        m_releaseDate = null;
8482:                    } else {
8483:                        m_releaseDate = TimeService.newTime(other
8484:                                .getReleaseDate().getTime());
8485:                    }
8486:                    if (m_hidden || other.getRetractDate() == null) {
8487:                        m_retractDate = null;
8488:                    } else {
8489:                        m_retractDate = TimeService.newTime(other
8490:                                .getRetractDate().getTime());
8491:                    }
8492:
8493:                } // set
8494:
8495:                /**
8496:                 * Clean up.
8497:                 */
8498:                protected void finalize() {
8499:                    // catch the case where an edit was made but never resolved
8500:                    if (m_active) {
8501:                        cancelCollection(this );
8502:                    }
8503:
8504:                } // finalize
8505:
8506:                /* (non-Javadoc)
8507:                 * @see org.sakaiproject.content.api.ContentEntity#getUrl(boolean)
8508:                 */
8509:                public String getUrl(boolean relative) {
8510:                    return getAccessPoint(relative) + convertIdToUserEid(m_id);
8511:                }
8512:
8513:                /**
8514:                 * Access the URL which can be used to access the resource.
8515:                 * 
8516:                 * @return The URL which can be used to access the resource.
8517:                 */
8518:                public String getUrl() {
8519:                    return getUrl(false);
8520:
8521:                } // getUrl
8522:
8523:                /**
8524:                 * Access the internal reference which can be used to access the resource from within the system.
8525:                 * 
8526:                 * @return The the internal reference which can be used to access the resource from within the system.
8527:                 */
8528:                public String getReference() {
8529:                    return getAccessPoint(true) + m_id;
8530:
8531:                } // getReference
8532:
8533:                /**
8534:                 * @inheritDoc
8535:                 */
8536:                public String getReference(String rootProperty) {
8537:                    return getReference();
8538:                }
8539:
8540:                /**
8541:                 * @inheritDoc
8542:                 */
8543:                public String getUrl(String rootProperty) {
8544:                    return getUrl();
8545:                }
8546:
8547:                /**
8548:                 * Access the id of the resource.
8549:                 * 
8550:                 * @return The id.
8551:                 */
8552:                public String getId() {
8553:                    return m_id;
8554:
8555:                } // getId
8556:
8557:                /**
8558:                 * Access a List of the collection's internal members, each a resource id string.
8559:                 * 
8560:                 * @return a List of the collection's internal members, each a resource id string (may be empty).
8561:                 */
8562:                public List getMembers() {
8563:                    // get the objects
8564:                    List memberResources = getMemberResources();
8565:
8566:                    // form the list of just ids
8567:                    List mbrs = new Vector();
8568:                    for (int i = 0; i < memberResources.size(); i++) {
8569:                        Entity res = (Entity) memberResources.get(i);
8570:                        if (res != null) {
8571:                            mbrs.add(res.getId());
8572:                        }
8573:                    }
8574:
8575:                    if (mbrs.size() == 0)
8576:                        return mbrs;
8577:
8578:                    // sort? %%%
8579:                    // Collections.sort(mbrs);
8580:
8581:                    return mbrs;
8582:
8583:                } // getMembers
8584:
8585:                /**
8586:                 * Access the size of all the resource body bytes within this collection in Kbytes.
8587:                 * 
8588:                 * @return The size of all the resource body bytes within this collection in Kbytes.
8589:                 */
8590:                public long getBodySizeK() {
8591:                    long size = 0;
8592:
8593:                    // get the member objects
8594:                    List members = getMemberResources();
8595:
8596:                    // for each member
8597:                    for (Iterator it = members.iterator(); it.hasNext();) {
8598:                        Object obj = it.next();
8599:                        if (obj == null)
8600:                            continue;
8601:
8602:                        // do not count the size of virtual objects
8603:                        if (obj instanceof  BaseCollectionEdit
8604:                                && ((BaseCollectionEdit) obj)
8605:                                        .getVirtualContentEntity() != null)
8606:                            continue;
8607:
8608:                        // if a resource, add the body size
8609:                        if (obj instanceof  ContentResource) {
8610:                            size += bytes2k(((ContentResource) obj)
8611:                                    .getContentLength());
8612:                        }
8613:
8614:                        // if a collection, count it's size
8615:                        else {
8616:                            size += ((BaseCollectionEdit) obj).getBodySizeK();
8617:                        }
8618:                    }
8619:
8620:                    // if (M_log.isDebugEnabled())
8621:                    // M_log.debug("getBodySizeK(): collection: " + getId() + " size: " + size);
8622:
8623:                    return size;
8624:
8625:                } // getBodySizeK
8626:
8627:                /**
8628:                 * Access a List of the collections' internal members as full ContentResource or ContentCollection objects.
8629:                 * 
8630:                 * @return a List of the full objects of the members of the collection.
8631:                 */
8632:                public List getMemberResources() {
8633:                    List mbrs = new Vector();
8634:
8635:                    // if not caching
8636:                    if ((!m_caching) || (m_cache == null)
8637:                            || (m_cache.disabled())) {
8638:                        // TODO: current service caching
8639:                        mbrs = m_storage.getCollections(this );
8640:                        mbrs.addAll(m_storage.getResources(this ));
8641:                    }
8642:
8643:                    else {
8644:                        // if the cache is complete for this collection, use it
8645:                        if (m_cache.isComplete(getReference())) {
8646:                            // get just this collection's members
8647:                            mbrs = m_cache.getAll(getReference());
8648:                        }
8649:
8650:                        // otherwise get all the members from storage
8651:                        else {
8652:                            // Note: while we are getting from storage, storage might change. These can be processed
8653:                            // after we get the storage entries, and put them in the cache, and mark the cache complete.
8654:                            // -ggolden
8655:                            synchronized (m_cache) {
8656:                                // if we were waiting and it's now complete...
8657:                                if (m_cache.isComplete(getReference())) {
8658:                                    // get just this collection's members
8659:                                    mbrs = m_cache.getAll(getReference());
8660:                                } else {
8661:                                    // save up any events to the cache until we get past this load
8662:                                    m_cache.holdEvents();
8663:
8664:                                    // read from storage - resources and collections, but just those
8665:                                    // whose path is this's path (i.e. just mine!)
8666:                                    mbrs = m_storage.getCollections(this );
8667:                                    mbrs.addAll(m_storage.getResources(this ));
8668:
8669:                                    // update the cache, and mark it complete
8670:                                    for (int i = 0; i < mbrs.size(); i++) {
8671:                                        Entity mbr = (Entity) mbrs.get(i);
8672:                                        m_cache.put(mbr.getReference(), mbr);
8673:                                    }
8674:
8675:                                    m_cache.setComplete(getReference());
8676:
8677:                                    // now we are complete, process any cached events
8678:                                    m_cache.processEvents();
8679:                                }
8680:                            }
8681:                        }
8682:                    }
8683:
8684:                    if (mbrs.size() == 0)
8685:                        return mbrs;
8686:
8687:                    // sort %%%
8688:                    // Collections.sort(mbrs);
8689:
8690:                    return mbrs;
8691:
8692:                } // getMemberResources
8693:
8694:                /**
8695:                 * Access the collection's properties.
8696:                 * 
8697:                 * @return The collection's properties.
8698:                 */
8699:                public ResourceProperties getProperties() {
8700:                    return m_properties;
8701:
8702:                } // getProperties
8703:
8704:                /**
8705:                 * Set the collection as removed.
8706:                 */
8707:                protected void setRemoved() {
8708:                    m_isRemoved = true;
8709:
8710:                } // setRemoved
8711:
8712:                /**
8713:                 * Clear all the members of the collection, all the way down. Security has already been checked!
8714:                 */
8715:                protected void clear() throws IdUnusedException,
8716:                        PermissionException, InconsistentException,
8717:                        TypeException, InUseException, ServerOverloadException {
8718:                    // get this collection's members
8719:                    List mbrs = getMemberResources();
8720:                    for (int i = 0; i < mbrs.size(); i++) {
8721:                        Object mbr = mbrs.get(i);
8722:                        if (mbr == null)
8723:                            continue;
8724:
8725:                        // for a contained collection, clear its members first - if any are in use, the show's over
8726:                        if (mbr instanceof  ContentCollection) {
8727:                            ((BaseCollectionEdit) mbr).clear();
8728:                        }
8729:
8730:                        // now remove this member
8731:                        if (mbr instanceof  ContentCollection) {
8732:                            // if this is not allowed or in use, we throw and the show's over.
8733:                            removeCollection(((ContentCollection) mbr).getId());
8734:                        } else if (mbr instanceof  ContentResource) {
8735:                            // if this is not allowed or in use, we throw and the show's over.
8736:                            removeResource(((ContentResource) mbr).getId());
8737:                        }
8738:                    }
8739:
8740:                } // clear
8741:
8742:                /**
8743:                 * Serialize the resource into XML, adding an element to the doc under the top of the stack element.
8744:                 * 
8745:                 * @param doc
8746:                 *        The DOM doc to contain the XML (or null for a string return).
8747:                 * @param stack
8748:                 *        The DOM elements, the top of which is the containing element of the new "resource" element.
8749:                 * @return The newly added element.
8750:                 */
8751:                public Element toXml(Document doc, Stack stack) {
8752:                    Element collection = doc.createElement("collection");
8753:
8754:                    if (stack.isEmpty()) {
8755:                        doc.appendChild(collection);
8756:                    } else {
8757:                        ((Element) stack.peek()).appendChild(collection);
8758:                    }
8759:
8760:                    stack.push(collection);
8761:
8762:                    collection.setAttribute("id", m_id);
8763:                    collection.setAttribute("resource-type",
8764:                            ResourceType.TYPE_FOLDER);
8765:
8766:                    if (m_access == null || AccessMode.SITE == m_access) {
8767:                        m_access = AccessMode.INHERITED;
8768:                    }
8769:                    collection.setAttribute(ACCESS_MODE, m_access.toString());
8770:
8771:                    collection.setAttribute(HIDDEN, Boolean.toString(m_hidden));
8772:                    if (!m_hidden && m_releaseDate != null) {
8773:                        // add release-date 
8774:                        collection.setAttribute(RELEASE_DATE, m_releaseDate
8775:                                .toString());
8776:                    }
8777:                    if (!m_hidden && m_retractDate != null) {
8778:                        // add retract-date
8779:                        collection.setAttribute(RETRACT_DATE, m_retractDate
8780:                                .toString());
8781:                    }
8782:
8783:                    // properties
8784:                    m_properties.toXml(doc, stack);
8785:
8786:                    stack.pop();
8787:
8788:                    // add groups
8789:                    if ((m_groups != null) && (m_groups.size() > 0)) {
8790:                        Iterator groupIt = m_groups.iterator();
8791:                        while (groupIt.hasNext()) {
8792:                            // how does this get to be a Group instead of a groupRef???
8793:                            String groupRef = (String) groupIt.next();
8794:                            Element sect = doc.createElement(GROUP_LIST);
8795:                            sect.setAttribute(GROUP_NAME, groupRef);
8796:                            collection.appendChild(sect);
8797:                        }
8798:                    }
8799:
8800:                    return collection;
8801:
8802:                } // toXml
8803:
8804:                /**
8805:                 * Access the event code for this edit.
8806:                 * 
8807:                 * @return The event code for this edit.
8808:                 */
8809:                protected String getEvent() {
8810:                    return m_event;
8811:                }
8812:
8813:                /**
8814:                 * Set the event code for this edit.
8815:                 * 
8816:                 * @param event
8817:                 *        The event code for this edit.
8818:                 */
8819:                protected void setEvent(String event) {
8820:                    m_event = event;
8821:                }
8822:
8823:                /**
8824:                 * Access the resource's properties for modification
8825:                 * 
8826:                 * @return The resource's properties.
8827:                 */
8828:                public ResourcePropertiesEdit getPropertiesEdit() {
8829:                    return m_properties;
8830:
8831:                } // getPropertiesEdit
8832:
8833:                /**
8834:                 * Enable editing.
8835:                 */
8836:                protected void activate() {
8837:                    m_active = true;
8838:
8839:                } // activate
8840:
8841:                /**
8842:                 * Check to see if the edit is still active, or has already been closed.
8843:                 * 
8844:                 * @return true if the edit is active, false if it's been closed.
8845:                 */
8846:                public boolean isActiveEdit() {
8847:                    return m_active;
8848:
8849:                } // isActiveEdit
8850:
8851:                /**
8852:                 * Close the edit object - it cannot be used after this.
8853:                 */
8854:                protected void closeEdit() {
8855:                    m_active = false;
8856:
8857:                } // closeEdit
8858:
8859:                /******************************************************************************************************************************************************************************************************************************************************
8860:                 * SessionBindingListener implementation
8861:                 *****************************************************************************************************************************************************************************************************************************************************/
8862:
8863:                public void valueBound(SessionBindingEvent event) {
8864:                }
8865:
8866:                public void valueUnbound(SessionBindingEvent event) {
8867:                    if (M_log.isDebugEnabled())
8868:                        M_log.debug("valueUnbound()");
8869:
8870:                    // catch the case where an edit was made but never resolved
8871:                    if (m_active) {
8872:                        cancelCollection(this );
8873:                    }
8874:
8875:                } // valueUnbound
8876:
8877:                /**
8878:                 * @inheritDoc
8879:                 * @see org.sakaiproject.content.api.ContentEntity#isResource()
8880:                 */
8881:                public boolean isResource() {
8882:                    // TODO: this may need a different implementation in the handler
8883:                    return false;
8884:                }
8885:
8886:                /**
8887:                 * @inheritDoc
8888:                 * @see org.sakaiproject.content.api.ContentEntity#isCollection()
8889:                 */
8890:                public boolean isCollection() {
8891:                    // TODO: this may need a different implementation in the handler
8892:                    return true;
8893:                }
8894:
8895:                public void setPriorityMap(Map priorities) {
8896:                    if (m_prioritySortEnabled) {
8897:                        ResourcePropertiesEdit myProps = getPropertiesEdit();
8898:                        myProps.addProperty(
8899:                                ResourceProperties.PROP_HAS_CUSTOM_SORT,
8900:                                Boolean.TRUE.toString());
8901:                        Iterator nameIt = priorities.keySet().iterator();
8902:                        while (nameIt.hasNext()) {
8903:                            String name = (String) nameIt.next();
8904:                            Integer priority = (Integer) priorities.get(name);
8905:
8906:                            try {
8907:                                if (name.endsWith(Entity.SEPARATOR)) {
8908:                                    ContentCollectionEdit entity = editCollection(name);
8909:                                    ResourcePropertiesEdit props = entity
8910:                                            .getPropertiesEdit();
8911:                                    props
8912:                                            .addProperty(
8913:                                                    ResourceProperties.PROP_CONTENT_PRIORITY,
8914:                                                    priority.toString());
8915:                                    commitCollection(entity);
8916:                                } else {
8917:                                    ContentResourceEdit entity = editResource(name);
8918:                                    ResourcePropertiesEdit props = entity
8919:                                            .getPropertiesEdit();
8920:                                    props
8921:                                            .addProperty(
8922:                                                    ResourceProperties.PROP_CONTENT_PRIORITY,
8923:                                                    priority.toString());
8924:
8925:                                    // Soo Il Kim (kimsooil@bu.edu): added parameter to eliminate notifications
8926:                                    commitResource(entity,
8927:                                            NotificationService.NOTI_NONE);
8928:                                    // close the edit object
8929:                                    ((BaseResourceEdit) entity).closeEdit();
8930:                                }
8931:                            } catch (TypeException e) {
8932:                                // TODO Auto-generated catch block
8933:                                e.printStackTrace();
8934:                            } catch (IdUnusedException e) {
8935:                                // TODO Auto-generated catch block
8936:                                e.printStackTrace();
8937:                            } catch (PermissionException e) {
8938:                                // TODO Auto-generated catch block
8939:                                e.printStackTrace();
8940:                            } catch (InUseException e) {
8941:                                // TODO Auto-generated catch block
8942:                                e.printStackTrace();
8943:                            } catch (OverQuotaException e) {
8944:                                // TODO Auto-generated catch block
8945:                                e.printStackTrace();
8946:                            } catch (ServerOverloadException e) {
8947:                                // TODO Auto-generated catch block
8948:                                e.printStackTrace();
8949:                            }
8950:                        }
8951:
8952:                    }
8953:                }
8954:
8955:                public int getMemberCount() {
8956:                    int count = m_storage.getMemberCount(this .m_id);
8957:                    return count;
8958:                }
8959:
8960:                /*************************************************************************************************************************************************************
8961:                 * ContentHostingHandler Support
8962:                 */
8963:
8964:                /**
8965:                 * Real storage does not have handlers
8966:                 */
8967:                private ContentHostingHandler chh = null;
8968:                private ContentEntity chh_vce = null; // the wrapped virtual content entity
8969:
8970:                public ContentHostingHandler getContentHandler() {
8971:                    return chh;
8972:                }
8973:
8974:                public void setContentHandler(ContentHostingHandler chh) {
8975:                    this .chh = chh;
8976:                }
8977:
8978:                public ContentEntity getVirtualContentEntity() {
8979:                    return chh_vce;
8980:                }
8981:
8982:                public void setVirtualContentEntity(ContentEntity ce) {
8983:                    this .chh_vce = ce;
8984:                }
8985:
8986:                public ContentEntity getMember(String nextId) {
8987:                    ContentEntity ce = m_storage.getCollection(nextId);
8988:                    if (ce == null) {
8989:                        ce = m_storage.getResource(nextId);
8990:                    }
8991:                    return ce;
8992:                    /*
8993:                    List l = getMemberResources();
8994:                    for ( Iterator li = l.iterator(); li.hasNext(); ) {
8995:                    	ContentEntity ce = (ContentEntity) li.next();
8996:                    	if ( nextId.equals(ce.getId())) {
8997:                    		return ce;
8998:                    	}
8999:                    }
9000:                    return null;
9001:                     */
9002:                }
9003:
9004:            } // class BaseCollectionEdit
9005:
9006:            /**********************************************************************************************************************************************************************************************************************************************************
9007:             * ContentResource implementation
9008:             *********************************************************************************************************************************************************************************************************************************************************/
9009:            public class BaseResourceEdit extends BasicGroupAwareEdit implements 
9010:                    ContentResourceEdit, SessionBindingListener {
9011:                /** The content type. */
9012:                protected String m_contentType = null;
9013:
9014:                /** The body. May be missing - not yet read (null) */
9015:                protected byte[] m_body = null;
9016:
9017:                /** The content length of the body, consult only if the body is missing (null) */
9018:                protected int m_contentLength = 0;
9019:
9020:                /** When true, someone changed the body content with setContent() */
9021:                protected boolean m_bodyUpdated = false;
9022:
9023:                /** The file system path, post root, for file system stored body binary. */
9024:                protected String m_filePath = null;
9025:
9026:                protected InputStream m_contentStream;
9027:
9028:                /**
9029:                 * Construct.
9030:                 * 
9031:                 * @param id
9032:                 *        The local resource id.
9033:                 */
9034:                public BaseResourceEdit(String id) {
9035:                    m_id = id;
9036:
9037:                    // setup for properties
9038:                    m_properties = new BaseResourcePropertiesEdit();
9039:
9040:                    // allocate a file path if needed
9041:                    if (m_bodyPath != null) {
9042:                        setFilePath(TimeService.newTime());
9043:                    }
9044:                } // BaseResourceEdit
9045:
9046:                /**
9047:                 * Construct as a copy of another
9048:                 * 
9049:                 * @param other
9050:                 *        The other to copy.
9051:                 */
9052:                public BaseResourceEdit(ContentResource other) {
9053:                    set(other);
9054:                } // BaseResourceEdit
9055:
9056:                /**
9057:                 * Set the file path for this resource
9058:                 * 
9059:                 * @param time
9060:                 *        The time on which to based the path.
9061:                 */
9062:                protected void setFilePath(Time time) {
9063:                    // compute file path: use now in ms mod m_bodyVolumes.length to pick a volume from m_bodyVolumes (if defined)
9064:                    // add /yyyy/DDD/HH year / day of year / hour / and a unique id for the final file name.
9065:                    // Don't include the body path.
9066:                    String volume = "/";
9067:                    if ((m_bodyVolumes != null) && (m_bodyVolumes.length > 0)) {
9068:                        volume += m_bodyVolumes[(int) (Math.abs(time.getTime()) % ((long) m_bodyVolumes.length))];
9069:                        volume += "/";
9070:                    }
9071:
9072:                    m_filePath = volume + time.toStringFilePath()
9073:                            + IdManager.createUuid();
9074:                }
9075:
9076:                /**
9077:                 * Take all values from this object.
9078:                 * 
9079:                 * @param user
9080:                 *        The other object to take values from.
9081:                 */
9082:                protected void set(ContentResource other) {
9083:                    m_id = other.getId();
9084:                    m_contentType = other.getContentType();
9085:                    m_contentLength = other.getContentLength();
9086:                    m_resourceType = other.getResourceType();
9087:                    chh = other.getContentHandler();
9088:                    chh_vce = other.getVirtualContentEntity();
9089:
9090:                    this .m_contentStream = ((BaseResourceEdit) other).m_contentStream;
9091:
9092:                    m_filePath = ((BaseResourceEdit) other).m_filePath;
9093:
9094:                    // if there's a body in the other, reference it, else leave this one null
9095:                    // Note: this treats the body byte array as immutable, so to update it one
9096:                    // *must* call setContent() not just getContent and mess with the bytes. -ggolden
9097:                    byte[] content = ((BaseResourceEdit) other).m_body;
9098:                    if (content != null) {
9099:                        m_contentLength = content.length;
9100:                        m_body = content;
9101:                    }
9102:
9103:                    // copy other's access mode and list of groups
9104:                    m_access = other.getAccess();
9105:                    m_groups.clear();
9106:                    m_groups.addAll(other.getGroups());
9107:
9108:                    // setup for properties
9109:                    m_properties = new BaseResourcePropertiesEdit();
9110:                    m_properties.addAll(other.getProperties());
9111:
9112:                    m_hidden = other.isHidden();
9113:
9114:                    if (m_hidden || other.getReleaseDate() == null) {
9115:                        m_releaseDate = null;
9116:                    } else {
9117:                        m_releaseDate = TimeService.newTime(other
9118:                                .getReleaseDate().getTime());
9119:                    }
9120:                    if (m_hidden || other.getRetractDate() == null) {
9121:                        m_retractDate = null;
9122:                    } else {
9123:                        m_retractDate = TimeService.newTime(other
9124:                                .getRetractDate().getTime());
9125:                    }
9126:
9127:                } // set
9128:
9129:                /**
9130:                 * Construct from information in XML in a DOM element.
9131:                 * 
9132:                 * @param el
9133:                 *        The XML DOM element.
9134:                 */
9135:                public BaseResourceEdit(Element el) {
9136:                    m_properties = new BaseResourcePropertiesEdit();
9137:
9138:                    m_id = el.getAttribute("id");
9139:                    String contentType = StringUtil.trimToNull(el
9140:                            .getAttribute("content-type"));
9141:                    setContentType(contentType);
9142:                    m_contentLength = 0;
9143:                    try {
9144:                        m_contentLength = Integer.parseInt(el
9145:                                .getAttribute("content-length"));
9146:                    } catch (Exception ignore) {
9147:                    }
9148:                    ResourceTypeRegistry registry = getResourceTypeRegistry();
9149:                    String typeId = StringUtil.trimToNull(el
9150:                            .getAttribute("resource-type"));
9151:                    if (typeId == null || registry.getType(typeId) == null) {
9152:                        typeId = registry.mimetype2resourcetype(contentType);
9153:                    }
9154:                    setResourceType(typeId);
9155:
9156:                    String enc = StringUtil.trimToNull(el.getAttribute("body"));
9157:                    if (enc != null) {
9158:                        byte[] decoded = null;
9159:                        try {
9160:                            decoded = Base64
9161:                                    .decodeBase64(enc.getBytes("UTF-8"));
9162:                        } catch (UnsupportedEncodingException e) {
9163:                            M_log.error(e);
9164:                        }
9165:                        m_body = new byte[m_contentLength];
9166:                        System
9167:                                .arraycopy(decoded, 0, m_body, 0,
9168:                                        m_contentLength);
9169:                    }
9170:
9171:                    m_filePath = StringUtil.trimToNull(el
9172:                            .getAttribute("filePath"));
9173:
9174:                    // the children (properties)
9175:                    NodeList children = el.getChildNodes();
9176:                    final int length = children.getLength();
9177:                    for (int i = 0; i < length; i++) {
9178:                        Node child = children.item(i);
9179:                        if (child.getNodeType() != Node.ELEMENT_NODE)
9180:                            continue;
9181:                        Element element = (Element) child;
9182:
9183:                        // look for properties
9184:                        if (element.getTagName().equals("properties")) {
9185:                            // re-create properties
9186:                            m_properties = new BaseResourcePropertiesEdit(
9187:                                    element);
9188:                            if (m_prioritySortEnabled) {
9189:                                // setPriority();
9190:                            }
9191:                        }
9192:                        // look for groups
9193:                        else if (element.getTagName().equals(GROUP_LIST)) {
9194:                            m_groups.add(element.getAttribute(GROUP_NAME));
9195:                        }
9196:
9197:                        // extract access
9198:                        AccessMode access = AccessMode.INHERITED;
9199:                        String access_mode = el.getAttribute(ACCESS_MODE);
9200:                        if (access_mode != null
9201:                                && !access_mode.trim().equals("")) {
9202:                            access = AccessMode.fromString(access_mode);
9203:                        }
9204:                        m_access = access;
9205:                        if (m_access == null || AccessMode.SITE == m_access) {
9206:                            m_access = AccessMode.INHERITED;
9207:                        }
9208:
9209:                        String hidden = el.getAttribute(HIDDEN);
9210:                        m_hidden = hidden != null
9211:                                && !hidden.trim().equals("")
9212:                                && !Boolean.FALSE.toString().equalsIgnoreCase(
9213:                                        hidden);
9214:
9215:                        if (m_hidden) {
9216:                            m_releaseDate = null;
9217:                            m_retractDate = null;
9218:                        } else {
9219:                            // extract release date
9220:                            String date0 = el.getAttribute(RELEASE_DATE);
9221:                            if (date0 != null && !date0.trim().equals("")) {
9222:                                m_releaseDate = TimeService.newTimeGmt(date0);
9223:                                if (m_releaseDate.getTime() <= START_OF_TIME) {
9224:                                    m_releaseDate = null;
9225:                                }
9226:                            }
9227:
9228:                            // extract retract date
9229:                            String date1 = el.getAttribute(RETRACT_DATE);
9230:                            if (date1 != null && !date1.trim().equals("")) {
9231:                                m_retractDate = TimeService.newTimeGmt(date1);
9232:                                if (m_retractDate.getTime() >= END_OF_TIME) {
9233:                                    m_retractDate = null;
9234:                                }
9235:                            }
9236:                        }
9237:
9238:                    }
9239:                } // BaseResourceEdit
9240:
9241:                /**
9242:                 * Clean up.
9243:                 */
9244:                protected void finalize() {
9245:                    // catch the case where an edit was made but never resolved
9246:                    if (m_active) {
9247:                        cancelResource(this );
9248:                    }
9249:
9250:                } // finalize
9251:
9252:                /**
9253:                 * @inheritDoc
9254:                 */
9255:                public String getUrl() {
9256:                    return getUrl(false, PROP_ALTERNATE_REFERENCE);
9257:                }
9258:
9259:                /**
9260:                 * @inheritDoc
9261:                 */
9262:                public String getReference() {
9263:                    return getReference(PROP_ALTERNATE_REFERENCE);
9264:                }
9265:
9266:                /* (non-Javadoc)
9267:                 * @see org.sakaiproject.content.api.ContentEntity#getUrl(boolean)
9268:                 */
9269:                public String getUrl(boolean relative) {
9270:                    return getUrl(relative, PROP_ALTERNATE_REFERENCE);
9271:                }
9272:
9273:                /**
9274:                 * @inheritDoc
9275:                 */
9276:                public String getUrl(boolean relative, String rootProperty) {
9277:                    return (relative ? m_serverConfigurationService
9278:                            .getAccessPath() : m_serverConfigurationService
9279:                            .getAccessUrl())
9280:                            + getAlternateReferenceRoot(rootProperty)
9281:                            + m_relativeAccessPoint + convertIdToUserEid(m_id);
9282:                }
9283:
9284:                /**
9285:                 * @inheritDoc
9286:                 */
9287:                public String getUrl(String rootProperty) {
9288:                    return getUrl(false, rootProperty);
9289:                }
9290:
9291:                /**
9292:                 * @inheritDoc
9293:                 */
9294:                public String getReference(String rootProperty) {
9295:                    return getAlternateReferenceRoot(rootProperty)
9296:                            + m_relativeAccessPoint + m_id;
9297:                }
9298:
9299:                /**
9300:                 * Compute an alternate root for a reference, based on the value of the specified property.
9301:                 * 
9302:                 * @param rootProperty
9303:                 *        The property name.
9304:                 * @return The alternate root, or "" if there is none.
9305:                 */
9306:                protected String getAlternateReferenceRoot(String rootProperty) {
9307:                    // null means don't do this
9308:                    if (rootProperty == null)
9309:                        return "";
9310:
9311:                    // find the property - "" if not found
9312:                    String alternateRoot = StringUtil
9313:                            .trimToNull(getProperties().getProperty(
9314:                                    PROP_ALTERNATE_REFERENCE));
9315:                    if (alternateRoot == null)
9316:                        return "";
9317:
9318:                    // make sure it start with a separator and does not end with one
9319:                    if (!alternateRoot.startsWith(SEPARATOR))
9320:                        alternateRoot = SEPARATOR + alternateRoot;
9321:                    if (alternateRoot.endsWith(SEPARATOR))
9322:                        alternateRoot = alternateRoot.substring(0,
9323:                                alternateRoot.length() - SEPARATOR.length());
9324:
9325:                    return alternateRoot;
9326:                }
9327:
9328:                /**
9329:                 * @inheritDoc
9330:                 */
9331:                protected boolean requiresCopyrightAgreement() {
9332:                    // check my properties
9333:                    return m_properties
9334:                            .getProperty(ResourceProperties.PROP_COPYRIGHT_ALERT) != null;
9335:                }
9336:
9337:                /**
9338:                 * Access the id of the resource.
9339:                 * 
9340:                 * @return The id.
9341:                 */
9342:                public String getId() {
9343:                    return m_id;
9344:
9345:                } // getId
9346:
9347:                /**
9348:                 * Access the content byte length.
9349:                 * 
9350:                 * @return The content byte length.
9351:                 */
9352:                public int getContentLength() {
9353:                    // Use the CHH delegate, if there is one.
9354:                    if (chh_vce != null)
9355:                        return ((ContentResource) chh_vce).getContentLength();
9356:
9357:                    // if we have a body, use it's length
9358:                    if (m_body != null)
9359:                        return m_body.length;
9360:
9361:                    // otherwise, use the content length
9362:                    return m_contentLength;
9363:
9364:                } // getContentLength
9365:
9366:                /**
9367:                 * Access the resource MIME type.
9368:                 * 
9369:                 * @return The resource MIME type.
9370:                 */
9371:                public String getContentType() {
9372:                    // Use the CHH delegate, if there is one.
9373:                    if (chh_vce != null)
9374:                        return ((ContentResource) chh_vce).getContentType();
9375:
9376:                    return ((m_contentType == null) ? "" : m_contentType);
9377:                } // getContentType
9378:
9379:                /**
9380:                 * Access the content bytes of the resource.
9381:                 * 
9382:                 * @return An array containing the bytes of the resource's content.
9383:                 * @exception ServerOverloadException
9384:                 *            if server is configured to store resource body in filesystem and error occurs trying to read from filesystem.
9385:                 */
9386:                public byte[] getContent() throws ServerOverloadException {
9387:                    // Use the CHH delegate, if there is one.
9388:                    if (chh_vce != null)
9389:                        return ((ContentResource) chh_vce).getContent();
9390:
9391:                    // return the body bytes
9392:                    byte[] rv = m_body;
9393:
9394:                    if ((rv == null) && (m_contentLength > 0)) {
9395:                        // todo: try to get the body from the stream
9396:
9397:                        // TODO: we do not store the body with the object, so as not to cache the body bytes -ggolden
9398:                        rv = m_storage.getResourceBody(this );
9399:                        // m_body = rv;
9400:                    }
9401:
9402:                    return rv;
9403:
9404:                } // getContent
9405:
9406:                /**
9407:                 * Access the content as a stream. Please close the stream when done as it may be holding valuable system resources.
9408:                 * 
9409:                 * @return an InputStream through which the bytes of the resource can be read.
9410:                 */
9411:                public InputStream streamContent()
9412:                        throws ServerOverloadException {
9413:                    InputStream rv = null;
9414:
9415:                    if (m_body != null) {
9416:                        rv = new ByteArrayInputStream(m_body);
9417:                    } else {
9418:                        rv = m_storage.streamResourceBody(this );
9419:                    }
9420:
9421:                    return rv;
9422:                }
9423:
9424:                /**
9425:                 * Access the resource's properties.
9426:                 * 
9427:                 * @return The resource's properties.
9428:                 */
9429:                public ResourceProperties getProperties() {
9430:                    return m_properties;
9431:
9432:                } // getProperties
9433:
9434:                /**
9435:                 * Set the resource as removed.
9436:                 */
9437:                protected void setRemoved() {
9438:                    m_isRemoved = true;
9439:
9440:                } // setRemoved
9441:
9442:                /**
9443:                 * Set the content byte length.
9444:                 * 
9445:                 * @param length
9446:                 *        The content byte length.
9447:                 */
9448:                public void setContentLength(int length) {
9449:                    m_contentLength = length;
9450:
9451:                } // setContentLength
9452:
9453:                /**
9454:                 * Set the resource MIME type.
9455:                 * 
9456:                 * @param type
9457:                 *        The resource MIME type.
9458:                 */
9459:                public void setContentType(String type) {
9460:                    type = (String) ((Hashtable) fixTypeAndId(getId(), type))
9461:                            .get("type");
9462:                    m_contentType = type;
9463:
9464:                } // setContentType
9465:
9466:                /**
9467:                 * Set the resource content.
9468:                 * 
9469:                 * @param content
9470:                 *        An array containing the bytes of the resource's content.
9471:                 */
9472:                public void setContent(byte[] content) {
9473:                    if (content == null) {
9474:                        M_log.warn("setContent(): null content");
9475:                        return;
9476:                    }
9477:
9478:                    // only if different
9479:                    if (StringUtil.different(content, m_body)) {
9480:                        // take the new body and length
9481:                        m_body = content;
9482:                        m_contentLength = m_body.length;
9483:
9484:                        // mark me as having a changed body
9485:                        m_bodyUpdated = true;
9486:                    }
9487:
9488:                } // setContent
9489:
9490:                /* (non-Javadoc)
9491:                 * @see org.sakaiproject.content.api.ContentResourceEdit#setContent(java.io.OutputStream)
9492:                 */
9493:                public void setContent(InputStream stream) {
9494:                    if (stream == null) {
9495:                        M_log.warn("setContent(): null stream");
9496:                        return;
9497:                    }
9498:
9499:                    m_contentStream = stream;
9500:                    // m_contentLength = 
9501:                }
9502:
9503:                /**
9504:                 * Serialize the resource into XML, adding an element to the doc under the top of the stack element.
9505:                 * 
9506:                 * @param doc
9507:                 *        The DOM doc to contain the XML (or null for a string return).
9508:                 * @param stack
9509:                 *        The DOM elements, the top of which is the containing element of the new "resource" element.
9510:                 * @return The newly added element.
9511:                 */
9512:                public Element toXml(Document doc, Stack stack) {
9513:                    Element resource = doc.createElement("resource");
9514:
9515:                    if (stack.isEmpty()) {
9516:                        doc.appendChild(resource);
9517:                    } else {
9518:                        ((Element) stack.peek()).appendChild(resource);
9519:                    }
9520:
9521:                    stack.push(resource);
9522:
9523:                    resource.setAttribute("id", m_id);
9524:                    resource.setAttribute("content-type", m_contentType);
9525:                    resource.setAttribute("resource-type", m_resourceType);
9526:
9527:                    // body may not be loaded; if not use m_contentLength
9528:                    int contentLength = m_contentLength;
9529:                    if (m_body != null)
9530:                        contentLength = m_body.length;
9531:                    resource.setAttribute("content-length", Integer
9532:                            .toString(contentLength));
9533:
9534:                    if (m_filePath != null)
9535:                        resource.setAttribute("filePath", m_filePath);
9536:
9537:                    // if there's no body bytes (len = 0?) m_body will still be null, so just skip it
9538:                    if (m_body != null) {
9539:                        String enc = null;
9540:                        try {
9541:                            enc = new String(Base64.encodeBase64(m_body),
9542:                                    "UTF-8");
9543:                        } catch (UnsupportedEncodingException e) {
9544:                            M_log.error(e);
9545:                        }
9546:                        resource.setAttribute("body", enc);
9547:                    }
9548:
9549:                    // add access
9550:                    if (m_access == null || AccessMode.SITE == m_access) {
9551:                        m_access = AccessMode.INHERITED;
9552:                    }
9553:                    resource.setAttribute(ACCESS_MODE, m_access.toString());
9554:
9555:                    resource.setAttribute(HIDDEN, Boolean.toString(m_hidden));
9556:                    if (!m_hidden && m_releaseDate != null) {
9557:                        // add release-date 
9558:                        resource.setAttribute(RELEASE_DATE, m_releaseDate
9559:                                .toString());
9560:                    }
9561:                    if (!m_hidden && m_retractDate != null) {
9562:                        // add retract-date
9563:                        resource.setAttribute(RETRACT_DATE, m_retractDate
9564:                                .toString());
9565:                    }
9566:
9567:                    // properties
9568:                    m_properties.toXml(doc, stack);
9569:
9570:                    stack.pop();
9571:
9572:                    // add groups
9573:                    if ((m_groups != null) && (m_groups.size() > 0)) {
9574:                        Iterator groupIt = m_groups.iterator();
9575:                        while (groupIt.hasNext()) {
9576:                            String groupRef = (String) groupIt.next();
9577:                            Element sect = doc.createElement(GROUP_LIST);
9578:                            sect.setAttribute(GROUP_NAME, groupRef);
9579:                            resource.appendChild(sect);
9580:                        }
9581:                    }
9582:
9583:                    return resource;
9584:
9585:                } // toXml
9586:
9587:                /**
9588:                 * Access the event code for this edit.
9589:                 * 
9590:                 * @return The event code for this edit.
9591:                 */
9592:                protected String getEvent() {
9593:                    return m_event;
9594:                }
9595:
9596:                /**
9597:                 * Set the event code for this edit.
9598:                 * 
9599:                 * @param event
9600:                 *        The event code for this edit.
9601:                 */
9602:                protected void setEvent(String event) {
9603:                    m_event = event;
9604:                }
9605:
9606:                /**
9607:                 * Access the resource's properties for modification
9608:                 * 
9609:                 * @return The resource's properties.
9610:                 */
9611:                public ResourcePropertiesEdit getPropertiesEdit() {
9612:                    return m_properties;
9613:
9614:                } // getPropertiesEdit
9615:
9616:                /**
9617:                 * Enable editing.
9618:                 */
9619:                protected void activate() {
9620:                    m_active = true;
9621:
9622:                } // activate
9623:
9624:                /**
9625:                 * Check to see if the edit is still active, or has already been closed.
9626:                 * 
9627:                 * @return true if the edit is active, false if it's been closed.
9628:                 */
9629:                public boolean isActiveEdit() {
9630:                    return m_active;
9631:
9632:                } // isActiveEdit
9633:
9634:                /**
9635:                 * Close the edit object - it cannot be used after this.
9636:                 */
9637:                protected void closeEdit() {
9638:                    m_active = false;
9639:
9640:                } // closeEdit
9641:
9642:                /******************************************************************************************************************************************************************************************************************************************************
9643:                 * SessionBindingListener implementation
9644:                 *****************************************************************************************************************************************************************************************************************************************************/
9645:
9646:                public void valueBound(SessionBindingEvent event) {
9647:                }
9648:
9649:                public void valueUnbound(SessionBindingEvent event) {
9650:                    if (M_log.isDebugEnabled())
9651:                        M_log.debug("valueUnbound()");
9652:
9653:                    // catch the case where an edit was made but never resolved
9654:                    if (m_active) {
9655:                        cancelResource(this );
9656:                    }
9657:
9658:                } // valueUnbound
9659:
9660:                public boolean isResource() {
9661:                    // TODO: this may need a different implementation in the handler
9662:                    return true;
9663:                }
9664:
9665:                public boolean isCollection() {
9666:                    // TODO: this may need a different implementation in the handler
9667:                    return false;
9668:                }
9669:
9670:                /* (non-Javadoc)
9671:                 * @see org.sakaiproject.content.api.ContentResourceEdit#setResourceType(java.lang.String)
9672:                 */
9673:                public void setResourceType(String type) {
9674:                    m_resourceType = type;
9675:                }
9676:
9677:                /**
9678:                 * real content resources dont have handlers
9679:                 */
9680:                private ContentHostingHandler chh = null;
9681:                private ContentEntity chh_vce = null; // the wrapped virtual content entity
9682:
9683:                public ContentHostingHandler getContentHandler() {
9684:                    return chh;
9685:                }
9686:
9687:                public void setContentHandler(ContentHostingHandler chh) {
9688:                    this .chh = chh;
9689:                }
9690:
9691:                public ContentEntity getVirtualContentEntity() {
9692:                    return chh_vce;
9693:                }
9694:
9695:                public void setVirtualContentEntity(ContentEntity ce) {
9696:                    this .chh_vce = ce;
9697:                }
9698:
9699:                /**
9700:                 * ContentResources cant have members, so this always returns null
9701:                 */
9702:                public ContentEntity getMember(String nextId) {
9703:                    return null;
9704:                }
9705:
9706:            } // BaseResourceEdit
9707:
9708:            /**********************************************************************************************************************************************************************************************************************************************************
9709:             * Storage implementation
9710:             *********************************************************************************************************************************************************************************************************************************************************/
9711:
9712:            protected interface Storage {
9713:                /**
9714:                 * Open and be ready to read / write.
9715:                 */
9716:                public void open();
9717:
9718:                /**
9719:                 * Get a count of all members of a collection, where 'member' means the collection
9720:                 * is the immediate parent of the item.  The count is not recursive and it will 
9721:                 * include all resources and collections whose immediate parent is the collection
9722:                 * identified by the parameter.
9723:                 */
9724:                public int getMemberCount(String collectionId);
9725:
9726:                /**
9727:                 * Close.
9728:                 */
9729:                public void close();
9730:
9731:                /**
9732:                 * Return the identified collection, or null if not found.
9733:                 */
9734:                public ContentCollection getCollection(String id);
9735:
9736:                /**
9737:                 * Return true if the identified collection exists.
9738:                 */
9739:                public boolean checkCollection(String id);
9740:
9741:                /**
9742:                 * Get a list of all getCollections within a collection.
9743:                 */
9744:                public List getCollections(ContentCollection collection);
9745:
9746:                /**
9747:                 * Keep a new collection.
9748:                 */
9749:                public ContentCollectionEdit putCollection(String collectionId);
9750:
9751:                /**
9752:                 * Get a collection locked for update
9753:                 */
9754:                public ContentCollectionEdit editCollection(String collectionId);
9755:
9756:                /**
9757:                 * Commit a collection edit.
9758:                 */
9759:                public void commitCollection(ContentCollectionEdit edit);
9760:
9761:                /**
9762:                 * Cancel a collection edit.
9763:                 */
9764:                public void cancelCollection(ContentCollectionEdit edit);
9765:
9766:                /**
9767:                 * Forget about a collection.
9768:                 */
9769:                public void removeCollection(ContentCollectionEdit collection);
9770:
9771:                /**
9772:                 * Return the identified resource, or null if not found.
9773:                 */
9774:                public ContentResource getResource(String id);
9775:
9776:                /**
9777:                 * Return true if the identified resource exists.
9778:                 */
9779:                public boolean checkResource(String id);
9780:
9781:                /**
9782:                 * Get a list of all resources within a collection.
9783:                 */
9784:                public List getResources(ContentCollection collection);
9785:
9786:                /**
9787:                 * 
9788:                 * @param collectionId
9789:                 * @return
9790:                 */
9791:                public List getFlatResources(String collectionId);
9792:
9793:                /**
9794:                 * Keep a new resource.
9795:                 */
9796:                public ContentResourceEdit putResource(String resourceId);
9797:
9798:                /**
9799:                 * Get a resource locked for update
9800:                 */
9801:                public ContentResourceEdit editResource(String resourceId);
9802:
9803:                /**
9804:                 * Commit a resource edit.
9805:                 */
9806:                public void commitResource(ContentResourceEdit edit)
9807:                        throws ServerOverloadException;
9808:
9809:                /**
9810:                 * Cancel a resource edit.
9811:                 */
9812:                public void cancelResource(ContentResourceEdit edit);
9813:
9814:                /**
9815:                 * Forget about a resource.
9816:                 */
9817:                public void removeResource(ContentResourceEdit resource);
9818:
9819:                /**
9820:                 * Read the resource's body.
9821:                 * 
9822:                 * @exception ServerOverloadException
9823:                 *            if server is configured to save resource body in filesystem and an error occurs while trying to access the filesystem.
9824:                 */
9825:                public byte[] getResourceBody(ContentResource resource)
9826:                        throws ServerOverloadException;
9827:
9828:                /**
9829:                 * Stream the resource's body.
9830:                 * 
9831:                 * @exception ServerOverloadException
9832:                 *            if server is configured to save resource body in filesystem and an error occurs while trying to access the filesystem.
9833:                 */
9834:                public InputStream streamResourceBody(ContentResource resource)
9835:                        throws ServerOverloadException;
9836:
9837:                /**
9838:                 * Return a single character representing the access mode of the resource or collection identified by the parameter, or null if not found.
9839:                 * @param id
9840:                 * @return A character identifying the access mode for the content entity, one of 's' for site, 'p' for public or 'g' for group.
9841:                 */
9842:                //public char getAccessMode(String id);
9843:                // htripath-storing into shadow table before deleting the resource
9844:                public void commitDeleteResource(ContentResourceEdit edit,
9845:                        String uuid);
9846:
9847:                public ContentResourceEdit putDeleteResource(String resourceId,
9848:                        String uuid, String userId);
9849:
9850:            } // Storage
9851:
9852:            /**********************************************************************************************************************************************************************************************************************************************************
9853:             * CacheRefresher implementation (no container)
9854:             *********************************************************************************************************************************************************************************************************************************************************/
9855:
9856:            /**
9857:             * Get a new value for this key whose value has already expired in the cache.
9858:             * 
9859:             * @param key
9860:             *        The key whose value has expired and needs to be refreshed.
9861:             * @param oldValue
9862:             *        The old exipred value of the key.
9863:             * @param event
9864:             *        The event which triggered this refresh.
9865:             * @return a new value for use in the cache for this key; if null, the entry will be removed.
9866:             */
9867:            public Object refresh(Object key, Object oldValue, Event event) {
9868:                Object rv = null;
9869:
9870:                // key is a reference
9871:                Reference ref = m_entityManager.newReference((String) key);
9872:                String id = ref.getId();
9873:
9874:                if (M_log.isDebugEnabled())
9875:                    M_log.debug("refresh(): key " + key + " id : "
9876:                            + ref.getId());
9877:
9878:                // get from storage only (not cache!)
9879:                boolean collectionHint = id.endsWith(Entity.SEPARATOR);
9880:                if (collectionHint) {
9881:                    rv = m_storage.getCollection(id);
9882:                } else {
9883:                    rv = m_storage.getResource(id);
9884:                }
9885:
9886:                return rv;
9887:
9888:            } // refresh
9889:
9890:        } // BaseContentService
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.