/** * OLAT - Online Learning and Training<br> * http://www.olat.org * <p> * Licensed under the Apache License, Version 2.0 (the "License"); <br> * you may not use this file except in compliance with the License.<br> * You may obtain a copy of the License at * <p> * http://www.apache.org/licenses/LICENSE-2.0 * <p> * Unless required by applicable law or agreed to in writing,<br> * software distributed under the License is distributed on an "AS IS" BASIS, <br> * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. <br> * See the License for the specific language governing permissions and <br> * limitations under the License. * <p> * Copyright (c) since 2004 at Multimedia- & E-Learning Services (MELS),<br> * University of Zurich, Switzerland. * <hr> * <a href="http://www.openolat.org"> * OpenOLAT - Online Learning and Training</a><br> * This file has been modified by the OpenOLAT community. Changes are licensed * under the Apache 2.0 license as the original file. */ package org.olat.restapi.repository; import static org.olat.restapi.security.RestSecurityHelper.getIdentity; import static org.olat.restapi.security.RestSecurityHelper.getUserRequest; import static org.olat.restapi.security.RestSecurityHelper.isAuthor; import static org.olat.restapi.security.RestSecurityHelper.isAuthorEditor; import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import javax.servlet.http.HttpServletRequest; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; import javax.ws.rs.FormParam; import javax.ws.rs.GET; import javax.ws.rs.POST; import javax.ws.rs.PUT; import javax.ws.rs.Path; import javax.ws.rs.PathParam; import javax.ws.rs.Produces; import javax.ws.rs.core.CacheControl; import javax.ws.rs.core.Context; import javax.ws.rs.core.EntityTag; import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Request; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.Status; import org.olat.admin.securitygroup.gui.IdentitiesAddEvent; import org.olat.basesecurity.BaseSecurity; import org.olat.basesecurity.GroupRoles; import org.olat.core.CoreSpringFactory; import org.olat.core.commons.modules.bc.FolderConfig; import org.olat.core.gui.UserRequest; import org.olat.core.gui.media.MediaResource; import org.olat.core.id.Identity; import org.olat.core.logging.OLog; import org.olat.core.logging.Tracing; import org.olat.core.logging.activity.LearningResourceLoggingAction; import org.olat.core.logging.activity.OlatResourceableType; import org.olat.core.logging.activity.ThreadLocalUserActivityLogger; import org.olat.core.util.FileUtils; import org.olat.core.util.coordinate.LockResult; import org.olat.fileresource.FileResourceManager; import org.olat.fileresource.types.ImsCPFileResource; import org.olat.repository.ErrorList; import org.olat.repository.RepositoryEntry; import org.olat.repository.RepositoryManager; import org.olat.repository.RepositoryService; import org.olat.repository.handlers.RepositoryHandler; import org.olat.repository.handlers.RepositoryHandlerFactory; import org.olat.repository.manager.RepositoryEntryLifecycleDAO; import org.olat.repository.model.RepositoryEntryLifecycle; import org.olat.resource.OLATResource; import org.olat.resource.OLATResourceManager; import org.olat.restapi.security.RestSecurityHelper; import org.olat.restapi.support.MultipartReader; import org.olat.restapi.support.ObjectFactory; import org.olat.restapi.support.vo.RepositoryEntryLifecycleVO; import org.olat.restapi.support.vo.RepositoryEntryVO; import org.olat.user.restapi.UserVO; import org.olat.user.restapi.UserVOFactory; import org.olat.util.logging.activity.LoggingResourceable; /** * Description:<br> * Repository entry resource * * <P> * Initial Date: 19.05.2009 <br> * @author patrickb, srosse, stephane.rosse@frentix.com */ public class RepositoryEntryResource { private static final OLog log = Tracing.createLoggerFor(RepositoryEntryResource.class); public static CacheControl cc = new CacheControl(); static { cc.setMaxAge(-1); } private RepositoryManager repositoryManager; private RepositoryService repositoryService; private BaseSecurity securityManager; public RepositoryEntryResource(RepositoryManager repositoryManager, RepositoryService repositoryService, BaseSecurity securityManager) { this.repositoryManager = repositoryManager; this.repositoryService = repositoryService; this.securityManager = securityManager; } /** * get a resource in the repository * @response.representation.200.qname {http://www.example.com}repositoryEntryVO * @response.representation.200.mediaType application/xml, application/json * @response.representation.200.doc Get the repository resource * @response.representation.200.example {@link org.olat.restapi.support.vo.Examples#SAMPLE_REPOENTRYVO} * @response.representation.404.doc The repository entry not found * @param repoEntryKey The key or soft key of the repository entry * @param request The REST request * @return */ @GET @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getById(@PathParam("repoEntryKey")String repoEntryKey, @Context Request request){ RepositoryEntry re = lookupRepositoryEntry(repoEntryKey); if(re == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } Date lastModified = re.getLastModified(); Response.ResponseBuilder response; if(lastModified == null) { EntityTag eTag = ObjectFactory.computeEtag(re); response = request.evaluatePreconditions(eTag); if(response == null) { RepositoryEntryVO vo = ObjectFactory.get(re); response = Response.ok(vo).tag(eTag).lastModified(lastModified); } } else { EntityTag eTag = ObjectFactory.computeEtag(re); response = request.evaluatePreconditions(lastModified, eTag); if(response == null) { RepositoryEntryVO vo = ObjectFactory.get(re); response = Response.ok(vo).tag(eTag).lastModified(lastModified); } } return response.build(); } //get put/post delete add owner /** * Returns the list of owners of the repository entry specified by the groupKey. * @response.representation.200.qname {http://www.example.com}userVO * @response.representation.200.mediaType application/xml, application/json * @response.representation.200.doc Owners of the repository entry * @response.representation.200.example {@link org.olat.user.restapi.Examples#SAMPLE_USERVOes} * @response.representation.404.doc The repository entry cannot be found * @param repoEntryKey The key of the repository entry * @param request The HTTP Request * @return */ @GET @Path("owners") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getOwners(@PathParam("repoEntryKey") String repoEntryKey, @Context HttpServletRequest request) { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } return getIdentityInSecurityGroup(repoEntry, GroupRoles.owner.name()); } /** * Adds an owner to the repository entry. * @response.representation.200.doc The user is added as owner of the repository entry * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The repository entry or the user cannot be found * @param repoEntryKey The key of the repository entry * @param identityKey The user's id * @param request The HTTP request * @return */ @PUT @Path("owners/{identityKey}") public Response addOwner(@PathParam("repoEntryKey") String repoEntryKey, @PathParam("identityKey") Long identityKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } Identity identityToAdd = securityManager.loadIdentityByKey(identityKey); if(identityToAdd == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } UserRequest ureq = RestSecurityHelper.getUserRequest(request); IdentitiesAddEvent iae = new IdentitiesAddEvent(identityToAdd); repositoryManager.addOwners(ureq.getIdentity(), iae, repoEntry); return Response.ok().build(); } catch (Exception e) { log.error("Trying to add an owner to a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } @PUT @Path("owners") @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response addOwners(UserVO[] owners, @PathParam("repoEntryKey") String repoEntryKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } List<Identity> identityToAdd = loadIdentities(owners); UserRequest ureq = RestSecurityHelper.getUserRequest(request); IdentitiesAddEvent iae = new IdentitiesAddEvent(identityToAdd); repositoryManager.addOwners(ureq.getIdentity(), iae, repoEntry); return Response.ok().build(); } catch (Exception e) { log.error("Trying to add an owner to a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } /** * Removes the owner from the repository entry. * @response.representation.200.doc The user is removed as owner from the repository entry * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The repository entry or the user cannot be found * @param repoEntryKey The key of the repository entry * @param identityKey The user's id * @param request The HTTP request * @return */ @DELETE @Path("owners/{identityKey}") public Response removeOwner(@PathParam("repoEntryKey") String repoEntryKey, @PathParam("identityKey") Long identityKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if (!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } Identity identityToRemove = securityManager.loadIdentityByKey(identityKey); if(identityToRemove == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } final UserRequest ureq = RestSecurityHelper.getUserRequest(request); repositoryManager.removeOwners(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry); return Response.ok().build(); } catch (Exception e) { log.error("Trying to remove an owner to a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } /** * Returns the list of coaches of the repository entry. * @response.representation.200.qname {http://www.example.com}userVO * @response.representation.200.mediaType application/xml, application/json * @response.representation.200.doc Coaches of the repository entry * @response.representation.200.example {@link org.olat.user.restapi.Examples#SAMPLE_USERVOes} * @response.representation.404.doc The repository entry cannot be found * @param repoEntryKey The key of the repository entry * @param request The HTTP Request * @return */ @GET @Path("coaches") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getCoaches(@PathParam("repoEntryKey") String repoEntryKey, @Context HttpServletRequest request) { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } return getIdentityInSecurityGroup(repoEntry, GroupRoles.coach.name()); } /** * Adds a coach to the repository entry. * @response.representation.200.doc The user is added as coach of the repository entry * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The repository entry or the user cannot be found * @param repoEntryKey The key of the repository entry * @param identityKey The user's id * @param request The HTTP request * @return */ @PUT @Path("coaches/{identityKey}") public Response addCoach(@PathParam("repoEntryKey") String repoEntryKey, @PathParam("identityKey") Long identityKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } Identity identityToAdd = securityManager.loadIdentityByKey(identityKey); if(identityToAdd == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } UserRequest ureq = RestSecurityHelper.getUserRequest(request); IdentitiesAddEvent iae = new IdentitiesAddEvent(identityToAdd); repositoryManager.addTutors(ureq.getIdentity(), ureq.getUserSession().getRoles(), iae, repoEntry, null); return Response.ok().build(); } catch (Exception e) { log.error("Trying to add a coach to a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } @PUT @Path("coaches") @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response addCoach(UserVO[] coaches, @PathParam("repoEntryKey") String repoEntryKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } List<Identity> identityToAdd = loadIdentities(coaches); UserRequest ureq = RestSecurityHelper.getUserRequest(request); IdentitiesAddEvent iae = new IdentitiesAddEvent(identityToAdd); repositoryManager.addTutors(ureq.getIdentity(), ureq.getUserSession().getRoles(), iae, repoEntry, null); return Response.ok().build(); } catch (Exception e) { log.error("Trying to add a coach to a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } /** * Removes the coach from the repository entry. * @response.representation.200.doc The user is removed as coach from the repository entry * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The repository entry or the user cannot be found * @param repoEntryKey The key of the repository entry * @param identityKey The user's id * @param request The HTTP request * @return */ @DELETE @Path("coaches/{identityKey}") public Response removeCoach(@PathParam("repoEntryKey") String repoEntryKey, @PathParam("identityKey") Long identityKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if (!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } Identity identityToRemove = securityManager.loadIdentityByKey(identityKey); if(identityToRemove == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } final UserRequest ureq = RestSecurityHelper.getUserRequest(request); repositoryManager.removeTutors(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry); return Response.ok().build(); } catch (Exception e) { log.error("Trying to remove a coach from a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } /** * Returns the list of participants of the repository entry. * @response.representation.200.qname {http://www.example.com}userVO * @response.representation.200.mediaType application/xml, application/json * @response.representation.200.doc Coaches of the repository entry * @response.representation.200.example {@link org.olat.user.restapi.Examples#SAMPLE_USERVOes} * @response.representation.404.doc The repository entry cannot be found * @param repoEntryKey The key of the repository entry * @param request The HTTP Request * @return */ @GET @Path("participants") @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response getParticipants(@PathParam("repoEntryKey") String repoEntryKey, @Context HttpServletRequest request) { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } return getIdentityInSecurityGroup(repoEntry, GroupRoles.participant.name()); } /** * Adds a participant to the repository entry. * @response.representation.200.doc The user is added as participant of the repository entry * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The repository entry or the user cannot be found * @param repoEntryKey The key of the repository entry * @param identityKey The user's id * @param request The HTTP request * @return */ @PUT @Path("participants/{identityKey}") public Response addParticipant(@PathParam("repoEntryKey") String repoEntryKey, @PathParam("identityKey") Long identityKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } Identity identityToAdd = securityManager.loadIdentityByKey(identityKey); if(identityToAdd == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } UserRequest ureq = RestSecurityHelper.getUserRequest(request); IdentitiesAddEvent iae = new IdentitiesAddEvent(identityToAdd); repositoryManager.addParticipants(ureq.getIdentity(), ureq.getUserSession().getRoles(), iae, repoEntry, null); return Response.ok().build(); } catch (Exception e) { log.error("Trying to add a participant to a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } @PUT @Path("participants") @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response addParticipants(UserVO[] participants, @PathParam("repoEntryKey") String repoEntryKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if(!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } List<Identity> participantList = loadIdentities(participants); UserRequest ureq = RestSecurityHelper.getUserRequest(request); IdentitiesAddEvent iae = new IdentitiesAddEvent(participantList); repositoryManager.addParticipants(ureq.getIdentity(), ureq.getUserSession().getRoles(), iae, repoEntry, null); return Response.ok().build(); } catch (Exception e) { log.error("Trying to add a participant to a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } private List<Identity> loadIdentities(UserVO[] users) { List<Long> identityKeys = new ArrayList<>(); for(UserVO user:users) { identityKeys.add(user.getKey()); } return securityManager.loadIdentityByKeys(identityKeys); } /** * Removes the participant from the repository entry. * @response.representation.200.doc The user is removed as participant from the repository entry * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The repository entry or the user cannot be found * @param repoEntryKey The key of the repository entry * @param identityKey The user's id * @param request The HTTP request * @return */ @DELETE @Path("participants/{identityKey}") public Response removeParticipant(@PathParam("repoEntryKey") String repoEntryKey, @PathParam("identityKey") Long identityKey, @Context HttpServletRequest request) { try { RepositoryEntry repoEntry = lookupRepositoryEntry(repoEntryKey); if(repoEntry == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if (!isAuthorEditor(repoEntry, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } Identity identityToRemove = securityManager.loadIdentityByKey(identityKey); if(identityToRemove == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } final UserRequest ureq = RestSecurityHelper.getUserRequest(request); repositoryManager.removeParticipants(ureq.getIdentity(), Collections.singletonList(identityToRemove), repoEntry, null, false); return Response.ok().build(); } catch (Exception e) { log.error("Trying to remove a participant from a repository entry", e); return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } } /** * Download the export zip file of a repository entry. * @response.representation.mediaType multipart/form-data * @response.representation.doc Download the resource file * @response.representation.200.mediaType application/zip * @response.representation.200.doc Download the repository entry as export zip file * @response.representation.200.example {@link org.olat.restapi.support.vo.Examples#SAMPLE_REPOENTRYVO} * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The resource could not found * @response.representation.406.doc Download of this resource is not possible * @response.representation.409.doc The resource is locked * @param repoEntryKey * @param request The HTTP request * @return */ @GET @Path("file") @Produces({"application/zip", MediaType.APPLICATION_OCTET_STREAM}) public Response getRepoFileById(@PathParam("repoEntryKey")String repoEntryKey, @Context HttpServletRequest request) { RepositoryEntry re = lookupRepositoryEntry(repoEntryKey); if(re == null) return Response.serverError().status(Status.NOT_FOUND).build(); RepositoryHandler typeToDownload = RepositoryHandlerFactory.getInstance().getRepositoryHandler(re); if(typeToDownload == null) return Response.serverError().status(Status.NOT_FOUND).build(); OLATResource ores = OLATResourceManager.getInstance().findResourceable(re.getOlatResource()); if(ores == null) return Response.serverError().status(Status.NOT_FOUND).build(); Identity identity = getIdentity(request); boolean isAuthor = RestSecurityHelper.isAuthor(request); boolean isOwner = repositoryManager.isOwnerOfRepositoryEntry(identity, re); if(!(isAuthor | isOwner)) return Response.serverError().status(Status.UNAUTHORIZED).build(); boolean canDownload = re.getCanDownload() && typeToDownload.supportsDownload(); if(!canDownload) return Response.serverError().status(Status.NOT_ACCEPTABLE).build(); boolean isAlreadyLocked = typeToDownload.isLocked(ores); LockResult lockResult = null; try { lockResult = typeToDownload.acquireLock(ores, identity); if(lockResult == null || (lockResult != null && lockResult.isSuccess() && !isAlreadyLocked)) { MediaResource mr = typeToDownload.getAsMediaResource(ores, false); if(mr != null) { repositoryService.incrementDownloadCounter(re); return Response.ok(mr.getInputStream()).cacheControl(cc).build(); // success } else return Response.serverError().status(Status.NO_CONTENT).build(); } else return Response.serverError().status(Status.CONFLICT).build(); } finally { if((lockResult != null && lockResult.isSuccess() && !isAlreadyLocked)) typeToDownload.releaseLock(lockResult); } } @POST @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response updateEntry(@PathParam("repoEntryKey") String repoEntryKey, RepositoryEntryVO vo, @Context HttpServletRequest request) { if(!RestSecurityHelper.isAuthor(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } final RepositoryEntry re = lookupRepositoryEntry(repoEntryKey); if(re == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } RepositoryEntryLifecycle lifecycle = null; RepositoryEntryLifecycleVO lifecycleVo = vo.getLifecycle(); if(lifecycleVo != null) { RepositoryEntryLifecycleDAO lifecycleDao = CoreSpringFactory.getImpl(RepositoryEntryLifecycleDAO.class); if(lifecycleVo.getKey() != null) { lifecycle = lifecycleDao.loadById(lifecycleVo.getKey()); if(lifecycle.isPrivateCycle()) { //check date String fromStr = lifecycleVo.getValidFrom(); String toStr = lifecycleVo.getValidTo(); String label = lifecycleVo.getLabel(); String softKey = lifecycleVo.getSoftkey(); Date from = ObjectFactory.parseDate(fromStr); Date to = ObjectFactory.parseDate(toStr); lifecycle.setLabel(label); lifecycle.setSoftKey(softKey); lifecycle.setValidFrom(from); lifecycle.setValidTo(to); } } else { String fromStr = lifecycleVo.getValidFrom(); String toStr = lifecycleVo.getValidTo(); String label = lifecycleVo.getLabel(); String softKey = lifecycleVo.getSoftkey(); Date from = ObjectFactory.parseDate(fromStr); Date to = ObjectFactory.parseDate(toStr); lifecycle = lifecycleDao.create(label, softKey, true, from, to); } } RepositoryEntry reloaded = repositoryManager.setDescriptionAndName(re, vo.getDisplayname(), vo.getDescription(), vo.getLocation(), vo.getAuthors(), vo.getExternalId(), vo.getExternalRef(), vo.getManagedFlags(), lifecycle); RepositoryEntryVO rvo = ObjectFactory.get(reloaded); return Response.ok(rvo).build(); } /** * Replace a resource in the repository and update its display name. The implementation is * limited to CP. * @response.representation.mediaType multipart/form-data * @response.representation.doc Import the resource file * @response.representation.200.qname {http://www.example.com}repositoryEntryVO * @response.representation.200.mediaType application/xml, application/json * @response.representation.200.doc Replace the resource and return the updated repository entry * @response.representation.200.example {@link org.olat.restapi.support.vo.Examples#SAMPLE_REPOENTRYVO} * @response.representation.401.doc The roles of the authenticated user are not sufficient * @param repoEntryKey The key or soft key of the repository entry * @param filename The name of the file * @param file The file input stream * @param displayname The display name * @param request The HTTP request * @return */ @POST @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Consumes({MediaType.MULTIPART_FORM_DATA}) public Response replaceResource(@PathParam("repoEntryKey") String repoEntryKey, @Context HttpServletRequest request) { if(!RestSecurityHelper.isAuthor(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } MultipartReader reader = null; try { final RepositoryEntry re = lookupRepositoryEntry(repoEntryKey); if(re == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } reader = new MultipartReader(request); File tmpFile = reader.getFile(); String displayname = reader.getValue("displayname"); String location = reader.getValue("location"); String authors = reader.getValue("authors"); String description = reader.getValue("description"); String externalId = reader.getValue("externalId"); String externalRef = reader.getValue("externalRef"); String managedFlags = reader.getValue("managedFlags"); Identity identity = RestSecurityHelper.getUserRequest(request).getIdentity(); RepositoryEntry replacedRe; if(tmpFile == null) { replacedRe = repositoryManager.setDescriptionAndName(re, displayname, description, location, authors, externalId, externalRef, managedFlags, re.getLifecycle()); } else { long length = tmpFile.length(); if(length == 0) { return Response.serverError().status(Status.NO_CONTENT).build(); } replacedRe = replaceFileResource(identity, re, tmpFile); if(replacedRe == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else { replacedRe = repositoryManager.setDescriptionAndName(replacedRe, displayname, description, location, authors, externalId, externalRef, managedFlags, replacedRe.getLifecycle()); } } RepositoryEntryVO vo = ObjectFactory.get(replacedRe); return Response.ok(vo).build(); } catch (Exception e) { log.error("Error while importing a file",e); } finally { MultipartReader.closeQuietly(reader); } return Response.serverError().status(Status.INTERNAL_SERVER_ERROR).build(); } private RepositoryEntry replaceFileResource(Identity identity, RepositoryEntry re, File fResource) { if (re == null) throw new NullPointerException("RepositoryEntry cannot be null"); FileResourceManager frm = FileResourceManager.getInstance(); File currentResource = frm.getFileResource(re.getOlatResource()); if (currentResource == null || !currentResource.exists()) { log.debug("Current resource file doesn't exist"); return null; } String typeName = re.getOlatResource().getResourceableTypeName(); if (typeName.equals(ImsCPFileResource.TYPE_NAME)) { if (currentResource.delete()) { FileUtils.copyFileToFile(fResource, currentResource, false); String repositoryHome = FolderConfig.getCanonicalRepositoryHome(); String relUnzipDir = frm.getUnzippedDirRel(re.getOlatResource()); File unzipDir = new File(repositoryHome, relUnzipDir); if (unzipDir != null && unzipDir.exists()) { FileUtils.deleteDirsAndFiles(unzipDir, true, true); } frm.unzipFileResource(re.getOlatResource()); } log.audit("Resource: " + re.getOlatResource() + " replaced by " + identity.getName()); return re; } log.debug("Cannot replace a resource of the type: " + typeName); return null; } /** * Delete a resource by id * * @response.representation.200.doc The metadatas of the deleted resource * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The course not found * @param courseId The course resourceable's id * @param request The HTTP request * @return It returns the XML representation of the <code>Structure</code> * object representing the course. */ @DELETE @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) public Response deleteCourse(@PathParam("repoEntryKey") String repoEntryKey, @Context HttpServletRequest request) { if(!isAuthor(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } RepositoryEntry re = lookupRepositoryEntry(repoEntryKey); if(re == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if (!isAuthorEditor(re, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } UserRequest ureq = getUserRequest(request); RepositoryService rs = CoreSpringFactory.getImpl(RepositoryService.class); ErrorList errors = rs.deletePermanently(re, ureq.getIdentity(), ureq.getUserSession().getRoles(), ureq.getLocale()); if(errors.hasErrors()) { return Response.serverError().status(500).build(); } ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_DELETE, getClass(), LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry)); return Response.ok().build(); } /** * Change the status of a course by id. The possible status are: * <ul> * <li>closed</li> * <li>unclosed</li> * <li>unpublished</li> * <li>deleted</li> * <li>restored</li> * </ul> * * @response.representation.200.doc The metadatas of the deleted course * @response.representation.401.doc The roles of the authenticated user are not sufficient * @response.representation.404.doc The course not found * @param request The HTTP request * @return It returns the XML representation of the <code>Structure</code> * object representing the course. */ @POST @Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON}) @Path("status") public Response deleteCoursePermanently(@PathParam("repoEntryKey") String repoEntryKey, @FormParam("newStatus") String newStatus, @Context HttpServletRequest request) { if(!isAuthor(request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } RepositoryEntry re = lookupRepositoryEntry(repoEntryKey); if(re == null) { return Response.serverError().status(Status.NOT_FOUND).build(); } else if (!isAuthorEditor(re, request)) { return Response.serverError().status(Status.UNAUTHORIZED).build(); } RepositoryService rs = CoreSpringFactory.getImpl(RepositoryService.class); if("closed".equals(newStatus)) { rs.closeRepositoryEntry(re); log.audit("REST closing course: " + re.getDisplayname() + " [" + re.getKey() + "]"); ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_CLOSE, getClass(), LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry)); } else if("unclosed".equals(newStatus)) { rs.uncloseRepositoryEntry(re); log.audit("REST unclosing course: " + re.getDisplayname() + " [" + re.getKey() + "]"); ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_UPDATE, getClass(), LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry)); } else if("unpublished".equals(newStatus)) { rs.unpublishRepositoryEntry(re); log.audit("REST unpublishing course: " + re.getDisplayname() + " [" + re.getKey() + "]"); ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_DEACTIVATE, getClass(), LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry)); } else if("deleted".equals(newStatus)) { Identity identity = getIdentity(request); rs.deleteSoftly(re, identity, true); log.audit("REST deleting (soft) course: " + re.getDisplayname() + " [" + re.getKey() + "]"); ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_TRASH, getClass(), LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry)); } else if("restored".equals(newStatus)) { rs.restoreRepositoryEntry(re); log.audit("REST restoring course: " + re.getDisplayname() + " [" + re.getKey() + "]"); ThreadLocalUserActivityLogger.log(LearningResourceLoggingAction.LEARNING_RESOURCE_RESTORE, getClass(), LoggingResourceable.wrap(re, OlatResourceableType.genRepoEntry)); } return Response.ok().build(); } private Response getIdentityInSecurityGroup(RepositoryEntry re, String role) { List<Identity> identities = repositoryService.getMembers(re, role); int count = 0; UserVO[] ownerVOs = new UserVO[identities.size()]; for(Identity identity:identities) { ownerVOs[count++] = UserVOFactory.get(identity, true, true); } return Response.ok(ownerVOs).build(); } private RepositoryEntry lookupRepositoryEntry(String key) { Long repoEntryKey = longId(key); RepositoryEntry re = null; if(repoEntryKey != null) {//looks like a primary key re = repositoryManager.lookupRepositoryEntry(repoEntryKey); } if(re == null) {// perhaps a soft key re = repositoryManager.lookupRepositoryEntryBySoftkey(key, false); } return re; } private Long longId(String key) { try { for(int i=key.length(); i-->0; ) { if(!Character.isDigit(key.charAt(i))) { return null; } } return new Long(key); } catch(NumberFormatException ex) { return null; } } }