/********************************************************************************** * $URL:https://source.sakaiproject.org/svn/osp/trunk/common/api-impl/src/java/org/theospi/portfolio/guidance/impl/GuidanceManagerImpl.java $ * $Id:GuidanceManagerImpl.java 9134 2006-05-08 20:28:42Z chmaurer@iupui.edu $ *********************************************************************************** * * Copyright (c) 2005, 2006, 2007, 2008 The Sakai Foundation * * Licensed under the Educational Community License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.opensource.org/licenses/ECL-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * **********************************************************************************/ package org.theospi.portfolio.guidance.impl; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.jdom.CDATA; import org.jdom.Document; import org.jdom.Element; import org.jdom.JDOMException; import org.jdom.input.SAXBuilder; import org.jdom.output.XMLOutputter; import org.sakaiproject.authz.api.SecurityService; import org.sakaiproject.content.api.ContentCollection; import org.sakaiproject.content.api.ContentHostingService; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.content.api.ContentResourceEdit; import org.sakaiproject.entity.api.EntityManager; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.entity.api.ResourcePropertiesEdit; import org.sakaiproject.exception.IdUnusedException; import org.sakaiproject.exception.ServerOverloadException; import org.sakaiproject.metaobj.shared.mgt.ContentEntityUtil; import org.sakaiproject.metaobj.shared.mgt.IdManager; import org.sakaiproject.metaobj.shared.model.Id; import org.sakaiproject.metaobj.shared.model.MimeType; import org.sakaiproject.metaobj.security.AllowMapSecurityAdvisor; import org.springframework.orm.hibernate3.support.HibernateDaoSupport; import org.theospi.portfolio.guidance.mgt.GuidanceManager; import org.theospi.portfolio.guidance.model.Guidance; import org.theospi.portfolio.guidance.model.GuidanceItem; import org.theospi.portfolio.guidance.model.GuidanceItemAttachment; import org.theospi.portfolio.security.AuthorizationFacade; import java.io.*; import java.util.*; import java.util.zip.*; /** * Created by IntelliJ IDEA. * User: John Ellis * Date: Nov 11, 2005 * Time: 1:00:57 PM * To change this template use File | Settings | File Templates. */ public class GuidanceManagerImpl extends HibernateDaoSupport implements GuidanceManager { protected final Log logger = LogFactory.getLog(getClass()); private AuthorizationFacade authorizationFacade; private SecurityService securityService; private EntityManager entityManager; private IdManager idManager; private ContentHostingService contentHostingService; public Guidance createNew(String description, String siteId, Id securityQualifier, String securityViewFunction, String securityEditFunction) { Guidance guidance = new Guidance(getIdManager().createId(), description, siteId, securityQualifier, securityViewFunction, securityEditFunction); /* GuidanceItem instruction = new GuidanceItem(guidance, Guidance.INSTRUCTION_TYPE); guidance.getItems().add(instruction); GuidanceItem example = new GuidanceItem(guidance, Guidance.EXAMPLE_TYPE); guidance.getItems().add(example); GuidanceItem rationale = new GuidanceItem(guidance, Guidance.RATIONALE_TYPE); guidance.getItems().add(rationale); GuidanceItem rubric = new GuidanceItem(guidance, Guidance.RUBRIC_TYPE); guidance.getItems().add(rubric); GuidanceItem expectations = new GuidanceItem(guidance, Guidance.EXPECTATIONS_TYPE); guidance.getItems().add(expectations); */ return guidance; } public Guidance getGuidance(Id guidanceId) { return getGuidance(guidanceId, true); } protected Guidance getGuidance(Id guidanceId, boolean checkAuthz) { Guidance guidance = (Guidance)getHibernateTemplate().get(Guidance.class, guidanceId); if (guidance == null) { return null; } if (guidance.getSecurityQualifier() != null && checkAuthz) { getAuthorizationFacade().checkPermission(guidance.getSecurityViewFunction(), guidance.getSecurityQualifier()); } if (assureAccess(guidance)) { getHibernateTemplate().save(guidance); } return guidance; } /** * Pushes the files in the guidance into the security bypass advisor thus allowing the * files to be read. * @return boolean whether or not the guidance has been changed */ public boolean assureAccess(Guidance guidance) { boolean changed = false; // setup access to the files List refs = new ArrayList(); for (Iterator i=guidance.getItems().iterator();i.hasNext();) { GuidanceItem item = (GuidanceItem)i.next(); for (Iterator j=item.getAttachments().iterator();j.hasNext();) { GuidanceItemAttachment attachment = (GuidanceItemAttachment)j.next(); if (checkAttachment(attachment)) { refs.add(attachment.getBaseReference().getBase().getReference()); } else { j.remove(); String guidanceText = item.getText(); int fileLocation = -1; String encodedRef = attachment.getBaseReference().getBase().getReference(); encodedRef = encodedRef.replaceAll(" ", "%20"); do { fileLocation = guidanceText.indexOf(encodedRef); if(fileLocation >= 0) { int startChar = guidanceText.lastIndexOf("<a", fileLocation); int lastChar = guidanceText.indexOf("</a", fileLocation); lastChar = guidanceText.indexOf(">", lastChar)+ 1; guidanceText = guidanceText.substring(0, startChar) + "--File Deleted--" + guidanceText.substring(lastChar); } } while(fileLocation > 0); item.setText(guidanceText); changed = true; } } } getSecurityService().pushAdvisor(new AllowMapSecurityAdvisor(ContentHostingService.EVENT_RESOURCE_READ, refs)); return changed; } /** * This checks for the existance of a resource in a non-permission checking way. * If there isn't a uuid for a resource or there isn't a resolved id then it doesn't exist * @param attachment * @return boolean true if a resource exists, false if it does not */ protected boolean checkAttachment(GuidanceItemAttachment attachment) { String id = attachment.getBaseReference().getBase().getId(); String uuid = getContentHostingService().getUuid(id); if(uuid == null) return false; return getContentHostingService().resolveUuid(uuid) != null; } public Guidance saveGuidance(Guidance guidance) { if (guidance.isNewObject()) { guidance.setNewId(guidance.getId()); guidance.setId(null); getHibernateTemplate().save(guidance); guidance.setNewObject(false); } else { getHibernateTemplate().saveOrUpdate(guidance); } return guidance; } public void deleteGuidance(Guidance guidance) { getHibernateTemplate().delete(guidance); } public Reference decorateReference(Guidance guidance, String reference) { String fullRef = ContentEntityUtil.getInstance().buildRef(GuidanceEntityProducer.GUIDANCE_PRODUCER, guidance.getSiteId(), guidance.getId().getValue(), reference); return getEntityManager().newReference(fullRef); } public List listGuidances(String siteId) { return getHibernateTemplate().findByNamedQuery("listGuidancesBySite", siteId); } public Guidance getGuidance(String id) { return getGuidance(id, true); } public Guidance getGuidance(String id, boolean checkAuthz) { return getGuidance(getIdManager().getId(id), checkAuthz); } public void packageGuidanceForExport(List guidanceIds, OutputStream os) throws IOException { CheckedOutputStream checksum = new CheckedOutputStream(os, new Adler32()); ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream( checksum)); List exportedRefs = new ArrayList(); for (Iterator i=guidanceIds.iterator();i.hasNext();) { String id = (String) i.next(); processGuidance(id, zos, exportedRefs); } zos.finish(); zos.flush(); } protected void processGuidance(String guidanceId, ZipOutputStream zos, List exportedRefs) throws IOException { Guidance guidance = getGuidance(guidanceId); for (Iterator i=guidance.getItems().iterator();i.hasNext();) { GuidanceItem item = (GuidanceItem) i.next(); try { processItem(zos, item, exportedRefs); } catch (ServerOverloadException e) { throw new RuntimeException(e); } } ZipEntry definitionFile = new ZipEntry("guidance-"+guidanceId+".xml"); zos.putNextEntry(definitionFile); Document doc = createGuidanceAsXml(guidance); String docStr = (new XMLOutputter()).outputString(doc); zos.write(docStr.getBytes("UTF-8")); zos.closeEntry(); } protected void processItem(ZipOutputStream zos, GuidanceItem item, List exportedRefs) throws IOException, ServerOverloadException { for (Iterator i=item.getAttachments().iterator();i.hasNext();) { GuidanceItemAttachment attachment = (GuidanceItemAttachment)i.next(); if (!exportedRefs.contains(attachment.getBaseReference().getBase().getReference())) { processAttachment(zos, attachment); exportedRefs.add(attachment.getBaseReference().getBase().getReference()); } } } protected void processAttachment(ZipOutputStream zos, GuidanceItemAttachment attachment) throws ServerOverloadException, IOException { ContentResource entity = (ContentResource) attachment.getBaseReference().getBase().getEntity(); String newName = entity.getProperties().getProperty(entity.getProperties().getNamePropDisplayName()); String cleanedName = newName.substring(newName.lastIndexOf('\\')+1); String entryName = "attachments/" + entity.getContentType() + "/" + getAttachmentRefHash(attachment) + "/" + cleanedName; storeFileInZip(zos, entity.streamContent(), entryName); } protected int getAttachmentRefHash(GuidanceItemAttachment attachment) { return attachment.getBaseReference().getBase().getReference().hashCode(); } protected void storeFileInZip(ZipOutputStream zos, InputStream in, String entryName) throws IOException { byte data[] = new byte[1024 * 10]; if (File.separatorChar == '\\') { entryName = entryName.replace('\\', '/'); } ZipEntry newfileEntry = new ZipEntry(entryName); zos.putNextEntry(newfileEntry); BufferedInputStream origin = new BufferedInputStream(in, data.length); int count; while ((count = origin.read(data, 0, data.length)) != -1) { zos.write(data, 0, count); } zos.closeEntry(); in.close(); } protected Document createGuidanceAsXml(Guidance guidance) { Element rootNode = new Element("guidance"); rootNode.setAttribute("formatVersion", "2.1"); addNode(rootNode, "id", guidance.getId().getValue()); addNode(rootNode, "description", guidance.getDescription()); addNode(rootNode, "securityEditFunction", guidance.getSecurityEditFunction()); addNode(rootNode, "securityViewFunction", guidance.getSecurityViewFunction()); Element items = new Element("items"); for (Iterator i=guidance.getItems().iterator();i.hasNext();) { GuidanceItem item = (GuidanceItem) i.next(); addItem(items, item); } rootNode.addContent(items); return new Document(rootNode); } protected void addItem(Element items, GuidanceItem item) { Element itemElement = new Element("item"); addNode(itemElement, "type", item.getType()); addNode(itemElement, "text", item.getText()); Element attachments = new Element("attachments"); for (Iterator i=item.getAttachments().iterator();i.hasNext();) { GuidanceItemAttachment attachment = (GuidanceItemAttachment) i.next(); addAttachment(attachments, attachment); } itemElement.addContent(attachments); items.addContent(itemElement); } protected void addAttachment(Element attachments, GuidanceItemAttachment attachment) { Element attachmentElement = new Element("attachment"); addNode(attachmentElement, "ref", attachment.getBaseReference().getBase().getReference()); addNode(attachmentElement, "url", attachment.getFullReference().getBase().getUrl()); attachments.addContent(attachmentElement); } protected void addNode(Element parentNode, String name, String value) { Element attrNode = new Element(name); attrNode.addContent(new CDATA(value)); parentNode.addContent(attrNode); } /** * This function is up to spec but incomplete. The guidance security qualifier * needs to be set on these objects so they can be retrieved. * * @param parent the parent resource folder where attachments go * @param siteId the site which will recieve the imported "stuff" * @param in The Input stream representing the output stream of the export * @return Map contains a map with keys being of type String as old Ids and the * values as being the Guidance object */ public Map importGuidanceList(ContentCollection parent, String siteId, InputStream in) throws IOException { Map guidanceMap = new Hashtable(); ZipInputStream zis = new ZipInputStream(in); ZipEntry currentEntry = zis.getNextEntry(); Map attachmentMap = new Hashtable(); while (currentEntry != null) { if (!currentEntry.isDirectory()) { if (currentEntry.getName().startsWith("guidance-")) { importGuidance(siteId, zis, guidanceMap); } else if (currentEntry.getName().startsWith("attachments/")) { importAttachmentRef(parent, currentEntry, siteId, zis, attachmentMap); } } zis.closeEntry(); currentEntry = zis.getNextEntry(); } postPocessAttachments(guidanceMap.values(), attachmentMap); for (Iterator i=guidanceMap.values().iterator();i.hasNext();) { Guidance guidance = (Guidance) i.next(); saveGuidance(guidance); } return guidanceMap; } protected void postPocessAttachments(Collection guidances, Map attachmentMap) { for (Iterator i=guidances.iterator();i.hasNext();) { Guidance guidance = (Guidance) i.next(); postProcessGuidance(guidance, attachmentMap); } } protected void postProcessGuidance(Guidance guidance, Map attachmentMap) { for (Iterator i=guidance.getItems().iterator();i.hasNext();) { GuidanceItem item = (GuidanceItem)i.next(); postProcessGuidanceItem(item, attachmentMap); } } protected void postProcessGuidanceItem(GuidanceItem item, Map attachmentMap) { List guidanceAttachments = new ArrayList(); for (Iterator i=item.getAttachments().iterator();i.hasNext();) { AttachmentImportWrapper wrapper = (AttachmentImportWrapper) i.next(); Reference baseRef = getEntityManager().newReference( (String)attachmentMap.get("" + wrapper.getOldRef().hashCode())); Reference fullRef = decorateReference(item.getGuidance(), baseRef.getReference()); GuidanceItemAttachment newAttachment = new GuidanceItemAttachment(item, baseRef, fullRef); item.setText(substitueText(wrapper.getOldUrl(), newAttachment, item.getText())); guidanceAttachments.add(newAttachment); } item.setAttachments(guidanceAttachments); } protected String substitueText(String oldUrl, GuidanceItemAttachment newAttachment, String text) { return text.replaceAll(oldUrl, newAttachment.getFullReference().getBase().getUrl()); } protected void importAttachmentRef(ContentCollection fileParent, ZipEntry currentEntry, String siteId, ZipInputStream zis, Map attachmentMap) { File file = new File(currentEntry.getName()); MimeType mimeType = new MimeType(file.getParentFile().getParentFile().getParentFile().getName(), file.getParentFile().getParentFile().getName()); String contentType = mimeType.getValue(); String oldId = file.getParentFile().getName(); try { ByteArrayOutputStream bos = new ByteArrayOutputStream(); int c = zis.read(); while (c != -1) { bos.write(c); c = zis.read(); } String fileId = fileParent.getId() + file.getName(); ContentResource rez = null; try { rez = getContentHostingService().getResource(fileId); } catch(IdUnusedException iduue) { logger.info(iduue); } if(rez == null) { ContentResourceEdit resource = getContentHostingService().addResource(fileId); ResourcePropertiesEdit resourceProperties = resource.getPropertiesEdit(); resourceProperties.addProperty (ResourceProperties.PROP_DISPLAY_NAME, file.getName()); resource.setContent(bos.toByteArray()); resource.setContentType(contentType); getContentHostingService().commitResource(resource); rez = resource; } attachmentMap.put(oldId, rez.getReference()); } catch (Exception exp) { throw new RuntimeException(exp); } } protected void importGuidance(String siteId, InputStream is, Map guidanceMap) throws IOException { SAXBuilder builder = new SAXBuilder(); builder.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true); // SAK-23131 Document document = null; byte []bytes = readStreamToBytes(is); try { document = builder.build(new ByteArrayInputStream(bytes)); } catch (JDOMException e) { throw new RuntimeException(e); } Element docRoot = document.getRootElement(); Id oldGuidanceId = getIdManager().getId(docRoot.getChildText("id")); String description = docRoot.getChildText("description"); String viewFunc = docRoot.getChildText("securityViewFunction"); String editFunc = docRoot.getChildText("securityEditFunction"); Guidance guidance = new Guidance(getIdManager().createId(), description, siteId, null, viewFunc, editFunc); List itemElements = docRoot.getChild("items").getChildren("item"); List items = new ArrayList(); for (Iterator i=itemElements.iterator();i.hasNext();) { items.add(importItem(guidance, (Element)i.next())); } guidance.setItems(items); guidanceMap.put(oldGuidanceId.getValue(), guidance); } protected byte[] readStreamToBytes(InputStream inStream) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(); byte data[] = new byte[10*1024]; int count; while ((count = inStream.read(data, 0, 10*1024)) != -1) { bytes.write(data, 0, count); } byte []tmp = bytes.toByteArray(); bytes.close(); return tmp; } protected GuidanceItem importItem(Guidance guidance, Element element) { String type = element.getChildText("type"); GuidanceItem item = new GuidanceItem(guidance, type); item.setText(element.getChildText("text")); List attachmentElements = element.getChild("attachments").getChildren("attachment"); List attachments = new ArrayList(); for (Iterator i=attachmentElements.iterator();i.hasNext();) { attachments.add(importAttachment(item, (Element)i.next())); } item.setAttachments(attachments); return item; } protected AttachmentImportWrapper importAttachment(GuidanceItem item, Element element) { return new AttachmentImportWrapper(element.getChildText("ref"), element.getChildText("url")); } /** * Pulls all Guidance * @return List of Guidance */ public List getGuidanceForWarehousing() { List guidance = getHibernateTemplate().findByNamedQuery("listGuidances"); for(Iterator i = guidance.iterator(); i.hasNext(); ) { Guidance w = (Guidance)i.next(); } return guidance; } public AuthorizationFacade getAuthorizationFacade() { return authorizationFacade; } public void setAuthorizationFacade(AuthorizationFacade authorizationFacade) { this.authorizationFacade = authorizationFacade; } public SecurityService getSecurityService() { return securityService; } public void setSecurityService(SecurityService securityService) { this.securityService = securityService; } public EntityManager getEntityManager() { return entityManager; } public void setEntityManager(EntityManager entityManager) { this.entityManager = entityManager; } public IdManager getIdManager() { return idManager; } public void setIdManager(IdManager idManager) { this.idManager = idManager; } public ContentHostingService getContentHostingService() { return contentHostingService; } public void setContentHostingService(ContentHostingService contentHostingService) { this.contentHostingService = contentHostingService; } }