001: /**********************************************************************************
002: * $URL:https://source.sakaiproject.org/svn/osp/trunk/common/api-impl/src/java/org/theospi/portfolio/guidance/impl/GuidanceManagerImpl.java $
003: * $Id:GuidanceManagerImpl.java 9134 2006-05-08 20:28:42Z chmaurer@iupui.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2005, 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.theospi.portfolio.guidance.impl;
021:
022: import org.apache.commons.logging.Log;
023: import org.apache.commons.logging.LogFactory;
024: import org.jdom.CDATA;
025: import org.jdom.Document;
026: import org.jdom.Element;
027: import org.jdom.JDOMException;
028: import org.jdom.input.SAXBuilder;
029: import org.jdom.output.XMLOutputter;
030: import org.sakaiproject.authz.api.SecurityService;
031: import org.sakaiproject.content.api.ContentCollection;
032: import org.sakaiproject.content.api.ContentHostingService;
033: import org.sakaiproject.content.api.ContentResource;
034: import org.sakaiproject.content.api.ContentResourceEdit;
035: import org.sakaiproject.entity.api.EntityManager;
036: import org.sakaiproject.entity.api.Reference;
037: import org.sakaiproject.entity.api.ResourceProperties;
038: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
039: import org.sakaiproject.exception.IdUnusedException;
040: import org.sakaiproject.exception.ServerOverloadException;
041: import org.sakaiproject.exception.TypeException;
042: import org.sakaiproject.exception.PermissionException;
043: import org.sakaiproject.metaobj.shared.mgt.ContentEntityUtil;
044: import org.sakaiproject.metaobj.shared.mgt.IdManager;
045: import org.sakaiproject.metaobj.shared.model.Id;
046: import org.sakaiproject.metaobj.shared.model.MimeType;
047: import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
048: import org.theospi.portfolio.guidance.mgt.GuidanceManager;
049: import org.theospi.portfolio.guidance.model.Guidance;
050: import org.theospi.portfolio.guidance.model.GuidanceItem;
051: import org.theospi.portfolio.guidance.model.GuidanceItemAttachment;
052: import org.theospi.portfolio.security.AllowMapSecurityAdvisor;
053: import org.theospi.portfolio.security.AuthorizationFacade;
054:
055: import java.io.*;
056: import java.util.*;
057: import java.util.zip.*;
058: import java.net.URLEncoder;
059:
060: /**
061: * Created by IntelliJ IDEA.
062: * User: John Ellis
063: * Date: Nov 11, 2005
064: * Time: 1:00:57 PM
065: * To change this template use File | Settings | File Templates.
066: */
067: public class GuidanceManagerImpl extends HibernateDaoSupport implements
068: GuidanceManager {
069:
070: protected final Log logger = LogFactory.getLog(getClass());
071:
072: private AuthorizationFacade authorizationFacade;
073: private SecurityService securityService;
074: private EntityManager entityManager;
075: private IdManager idManager;
076: private ContentHostingService contentHostingService;
077:
078: public Guidance createNew(String description, String siteId,
079: Id securityQualifier, String securityViewFunction,
080: String securityEditFunction) {
081: Guidance guidance = new Guidance(getIdManager().createId(),
082: description, siteId, securityQualifier,
083: securityViewFunction, securityEditFunction);
084:
085: GuidanceItem instruction = new GuidanceItem(guidance,
086: Guidance.INSTRUCTION_TYPE);
087: guidance.getItems().add(instruction);
088:
089: GuidanceItem example = new GuidanceItem(guidance,
090: Guidance.EXAMPLE_TYPE);
091: guidance.getItems().add(example);
092:
093: GuidanceItem rationale = new GuidanceItem(guidance,
094: Guidance.RATIONALE_TYPE);
095: guidance.getItems().add(rationale);
096:
097: return guidance;
098: }
099:
100: public Guidance getGuidance(Id guidanceId) {
101: return getGuidance(guidanceId, true);
102: }
103:
104: protected Guidance getGuidance(Id guidanceId, boolean checkAuthz) {
105: Guidance guidance = (Guidance) getHibernateTemplate().get(
106: Guidance.class, guidanceId);
107:
108: if (guidance == null) {
109: return null;
110: }
111:
112: if (guidance.getSecurityQualifier() != null && checkAuthz) {
113: getAuthorizationFacade().checkPermission(
114: guidance.getSecurityViewFunction(),
115: guidance.getSecurityQualifier());
116: }
117:
118: if (assureAccess(guidance)) {
119: getHibernateTemplate().save(guidance);
120: }
121:
122: return guidance;
123: }
124:
125: /**
126: * Pushes the files in the guidance into the security bypass advisor thus allowing the
127: * files to be read.
128: * @return boolean whether or not the guidance has been changed
129: */
130: public boolean assureAccess(Guidance guidance) {
131: boolean changed = false;
132: // setup access to the files
133: List refs = new ArrayList();
134: for (Iterator i = guidance.getItems().iterator(); i.hasNext();) {
135: GuidanceItem item = (GuidanceItem) i.next();
136: for (Iterator j = item.getAttachments().iterator(); j
137: .hasNext();) {
138: GuidanceItemAttachment attachment = (GuidanceItemAttachment) j
139: .next();
140: if (checkAttachment(attachment)) {
141: refs.add(attachment.getBaseReference().getBase()
142: .getReference());
143: } else {
144: j.remove();
145: String guidanceText = item.getText();
146: int fileLocation = -1;
147:
148: String encodedRef = attachment.getBaseReference()
149: .getBase().getReference();
150: encodedRef = encodedRef.replaceAll(" ", "%20");
151: do {
152: fileLocation = guidanceText.indexOf(encodedRef);
153: if (fileLocation >= 0) {
154: int startChar = guidanceText.lastIndexOf(
155: "<a", fileLocation);
156: int lastChar = guidanceText.indexOf("</a",
157: fileLocation);
158: lastChar = guidanceText.indexOf(">",
159: lastChar) + 1;
160: guidanceText = guidanceText.substring(0,
161: startChar)
162: + "--File Deleted--"
163: + guidanceText.substring(lastChar);
164: }
165: } while (fileLocation > 0);
166: item.setText(guidanceText);
167:
168: changed = true;
169: }
170: }
171: }
172:
173: getSecurityService()
174: .pushAdvisor(
175: new AllowMapSecurityAdvisor(
176: ContentHostingService.EVENT_RESOURCE_READ,
177: refs));
178:
179: return changed;
180: }
181:
182: /**
183: * This checks for the existance of a resource in a non-permission checking way.
184: * If there isn't a uuid for a resource or there isn't a resolved id then it doesn't exist
185: * @param attachment
186: * @return boolean true if a resource exists, false if it does not
187: */
188: protected boolean checkAttachment(GuidanceItemAttachment attachment) {
189: String id = attachment.getBaseReference().getBase().getId();
190:
191: String uuid = getContentHostingService().getUuid(id);
192: if (uuid == null)
193: return false;
194: return getContentHostingService().resolveUuid(uuid) != null;
195: }
196:
197: public Guidance saveGuidance(Guidance guidance) {
198: if (guidance.isNewObject()) {
199: guidance.setNewId(guidance.getId());
200: guidance.setId(null);
201: getHibernateTemplate().save(guidance);
202: guidance.setNewObject(false);
203: } else {
204: getHibernateTemplate().saveOrUpdate(guidance);
205: }
206:
207: return guidance;
208: }
209:
210: public void deleteGuidance(Guidance guidance) {
211: getHibernateTemplate().delete(guidance);
212: }
213:
214: public Reference decorateReference(Guidance guidance,
215: String reference) {
216: String fullRef = ContentEntityUtil.getInstance().buildRef(
217: GuidanceEntityProducer.GUIDANCE_PRODUCER,
218: guidance.getSiteId(), guidance.getId().getValue(),
219: reference);
220:
221: return getEntityManager().newReference(fullRef);
222: }
223:
224: public List listGuidances(String siteId) {
225: return getHibernateTemplate().findByNamedQuery(
226: "listGuidancesBySite", siteId);
227: }
228:
229: public Guidance getGuidance(String id) {
230: return getGuidance(id, true);
231: }
232:
233: public Guidance getGuidance(String id, boolean checkAuthz) {
234: return getGuidance(getIdManager().getId(id), checkAuthz);
235: }
236:
237: public void packageGuidanceForExport(List guidanceIds,
238: OutputStream os) throws IOException {
239: CheckedOutputStream checksum = new CheckedOutputStream(os,
240: new Adler32());
241: ZipOutputStream zos = new ZipOutputStream(
242: new BufferedOutputStream(checksum));
243: List exportedRefs = new ArrayList();
244: for (Iterator i = guidanceIds.iterator(); i.hasNext();) {
245: String id = (String) i.next();
246: processGuidance(id, zos, exportedRefs);
247: }
248:
249: zos.finish();
250: zos.flush();
251: }
252:
253: protected void processGuidance(String guidanceId,
254: ZipOutputStream zos, List exportedRefs) throws IOException {
255: Guidance guidance = getGuidance(guidanceId);
256: for (Iterator i = guidance.getItems().iterator(); i.hasNext();) {
257: GuidanceItem item = (GuidanceItem) i.next();
258: try {
259: processItem(zos, item, exportedRefs);
260: } catch (ServerOverloadException e) {
261: throw new RuntimeException(e);
262: }
263: }
264:
265: ZipEntry definitionFile = new ZipEntry("guidance-" + guidanceId
266: + ".xml");
267: zos.putNextEntry(definitionFile);
268: Document doc = createGuidanceAsXml(guidance);
269: String docStr = (new XMLOutputter()).outputString(doc);
270: zos.write(docStr.getBytes("UTF-8"));
271: zos.closeEntry();
272: }
273:
274: protected void processItem(ZipOutputStream zos, GuidanceItem item,
275: List exportedRefs) throws IOException,
276: ServerOverloadException {
277: for (Iterator i = item.getAttachments().iterator(); i.hasNext();) {
278: GuidanceItemAttachment attachment = (GuidanceItemAttachment) i
279: .next();
280: if (!exportedRefs.contains(attachment.getBaseReference()
281: .getBase().getReference())) {
282: processAttachment(zos, attachment);
283: exportedRefs.add(attachment.getBaseReference()
284: .getBase().getReference());
285: }
286: }
287: }
288:
289: protected void processAttachment(ZipOutputStream zos,
290: GuidanceItemAttachment attachment)
291: throws ServerOverloadException, IOException {
292: ContentResource entity = (ContentResource) attachment
293: .getBaseReference().getBase().getEntity();
294:
295: String newName = entity.getProperties().getProperty(
296: entity.getProperties().getNamePropDisplayName());
297: String cleanedName = newName.substring(newName
298: .lastIndexOf('\\') + 1);
299: String entryName = "attachments/" + entity.getContentType()
300: + "/" + getAttachmentRefHash(attachment) + "/"
301: + cleanedName;
302:
303: storeFileInZip(zos, entity.streamContent(), entryName);
304: }
305:
306: protected int getAttachmentRefHash(GuidanceItemAttachment attachment) {
307: return attachment.getBaseReference().getBase().getReference()
308: .hashCode();
309: }
310:
311: protected void storeFileInZip(ZipOutputStream zos, InputStream in,
312: String entryName) throws IOException {
313:
314: byte data[] = new byte[1024 * 10];
315:
316: if (File.separatorChar == '\\') {
317: entryName = entryName.replace('\\', '/');
318: }
319:
320: ZipEntry newfileEntry = new ZipEntry(entryName);
321:
322: zos.putNextEntry(newfileEntry);
323:
324: BufferedInputStream origin = new BufferedInputStream(in,
325: data.length);
326:
327: int count;
328: while ((count = origin.read(data, 0, data.length)) != -1) {
329: zos.write(data, 0, count);
330: }
331: zos.closeEntry();
332: in.close();
333: }
334:
335: protected Document createGuidanceAsXml(Guidance guidance) {
336: Element rootNode = new Element("guidance");
337:
338: rootNode.setAttribute("formatVersion", "2.1");
339: addNode(rootNode, "id", guidance.getId().getValue());
340: addNode(rootNode, "description", guidance.getDescription());
341: addNode(rootNode, "securityEditFunction", guidance
342: .getSecurityEditFunction());
343: addNode(rootNode, "securityViewFunction", guidance
344: .getSecurityViewFunction());
345: Element items = new Element("items");
346:
347: for (Iterator i = guidance.getItems().iterator(); i.hasNext();) {
348: GuidanceItem item = (GuidanceItem) i.next();
349: addItem(items, item);
350: }
351:
352: rootNode.addContent(items);
353:
354: return new Document(rootNode);
355: }
356:
357: protected void addItem(Element items, GuidanceItem item) {
358: Element itemElement = new Element("item");
359:
360: addNode(itemElement, "type", item.getType());
361: addNode(itemElement, "text", item.getText());
362:
363: Element attachments = new Element("attachments");
364:
365: for (Iterator i = item.getAttachments().iterator(); i.hasNext();) {
366: GuidanceItemAttachment attachment = (GuidanceItemAttachment) i
367: .next();
368: addAttachment(attachments, attachment);
369: }
370: itemElement.addContent(attachments);
371: items.addContent(itemElement);
372: }
373:
374: protected void addAttachment(Element attachments,
375: GuidanceItemAttachment attachment) {
376: Element attachmentElement = new Element("attachment");
377: addNode(attachmentElement, "ref", attachment.getBaseReference()
378: .getBase().getReference());
379: addNode(attachmentElement, "url", attachment.getFullReference()
380: .getBase().getUrl());
381: attachments.addContent(attachmentElement);
382: }
383:
384: protected void addNode(Element parentNode, String name, String value) {
385: Element attrNode = new Element(name);
386: attrNode.addContent(new CDATA(value));
387: parentNode.addContent(attrNode);
388: }
389:
390: /**
391: * This function is up to spec but incomplete. The guidance security qualifier
392: * needs to be set on these objects so they can be retrieved.
393: *
394: * @param parent the parent resource folder where attachments go
395: * @param siteId the site which will recieve the imported "stuff"
396: * @param in The Input stream representing the output stream of the export
397: * @return Map contains a map with keys being of type String as old Ids and the
398: * values as being the Guidance object
399: */
400: public Map importGuidanceList(ContentCollection parent,
401: String siteId, InputStream in) throws IOException {
402: Map guidanceMap = new Hashtable();
403: ZipInputStream zis = new ZipInputStream(in);
404: ZipEntry currentEntry = zis.getNextEntry();
405:
406: Map attachmentMap = new Hashtable();
407:
408: while (currentEntry != null) {
409: if (!currentEntry.isDirectory()) {
410: if (currentEntry.getName().startsWith("guidance-")) {
411: importGuidance(siteId, zis, guidanceMap);
412: } else if (currentEntry.getName().startsWith(
413: "attachments/")) {
414: importAttachmentRef(parent, currentEntry, siteId,
415: zis, attachmentMap);
416: }
417: }
418:
419: zis.closeEntry();
420: currentEntry = zis.getNextEntry();
421: }
422:
423: postPocessAttachments(guidanceMap.values(), attachmentMap);
424:
425: for (Iterator i = guidanceMap.values().iterator(); i.hasNext();) {
426: Guidance guidance = (Guidance) i.next();
427: saveGuidance(guidance);
428: }
429:
430: return guidanceMap;
431: }
432:
433: protected void postPocessAttachments(Collection guidances,
434: Map attachmentMap) {
435: for (Iterator i = guidances.iterator(); i.hasNext();) {
436: Guidance guidance = (Guidance) i.next();
437: postProcessGuidance(guidance, attachmentMap);
438: }
439: }
440:
441: protected void postProcessGuidance(Guidance guidance,
442: Map attachmentMap) {
443: for (Iterator i = guidance.getItems().iterator(); i.hasNext();) {
444: GuidanceItem item = (GuidanceItem) i.next();
445: postProcessGuidanceItem(item, attachmentMap);
446: }
447: }
448:
449: protected void postProcessGuidanceItem(GuidanceItem item,
450: Map attachmentMap) {
451: List guidanceAttachments = new ArrayList();
452:
453: for (Iterator i = item.getAttachments().iterator(); i.hasNext();) {
454: AttachmentImportWrapper wrapper = (AttachmentImportWrapper) i
455: .next();
456: Reference baseRef = getEntityManager().newReference(
457: (String) attachmentMap.get(""
458: + wrapper.getOldRef().hashCode()));
459: Reference fullRef = decorateReference(item.getGuidance(),
460: baseRef.getReference());
461: GuidanceItemAttachment newAttachment = new GuidanceItemAttachment(
462: item, baseRef, fullRef);
463: item.setText(substitueText(wrapper.getOldUrl(),
464: newAttachment, item.getText()));
465: guidanceAttachments.add(newAttachment);
466: }
467:
468: item.setAttachments(guidanceAttachments);
469: }
470:
471: protected String substitueText(String oldUrl,
472: GuidanceItemAttachment newAttachment, String text) {
473: return text.replaceAll(oldUrl, newAttachment.getFullReference()
474: .getBase().getUrl());
475: }
476:
477: protected void importAttachmentRef(ContentCollection fileParent,
478: ZipEntry currentEntry, String siteId, ZipInputStream zis,
479: Map attachmentMap) {
480: File file = new File(currentEntry.getName());
481:
482: MimeType mimeType = new MimeType(file.getParentFile()
483: .getParentFile().getParentFile().getName(), file
484: .getParentFile().getParentFile().getName());
485:
486: String contentType = mimeType.getValue();
487:
488: String oldId = file.getParentFile().getName();
489:
490: try {
491: ByteArrayOutputStream bos = new ByteArrayOutputStream();
492: int c = zis.read();
493:
494: while (c != -1) {
495: bos.write(c);
496: c = zis.read();
497: }
498:
499: String fileId = fileParent.getId() + file.getName();
500: ContentResource rez = null;
501: try {
502: rez = getContentHostingService().getResource(fileId);
503: } catch (IdUnusedException iduue) {
504: logger.info(iduue);
505: }
506: if (rez == null) {
507: ContentResourceEdit resource = getContentHostingService()
508: .addResource(fileId);
509: ResourcePropertiesEdit resourceProperties = resource
510: .getPropertiesEdit();
511: resourceProperties.addProperty(
512: ResourceProperties.PROP_DISPLAY_NAME, file
513: .getName());
514: resource.setContent(bos.toByteArray());
515: resource.setContentType(contentType);
516: getContentHostingService().commitResource(resource);
517: rez = resource;
518: }
519: attachmentMap.put(oldId, rez.getReference());
520: } catch (Exception exp) {
521: throw new RuntimeException(exp);
522: }
523: }
524:
525: protected void importGuidance(String siteId, InputStream is,
526: Map guidanceMap) throws IOException {
527: SAXBuilder builder = new SAXBuilder();
528: Document document = null;
529:
530: byte[] bytes = readStreamToBytes(is);
531:
532: try {
533: document = builder.build(new ByteArrayInputStream(bytes));
534: } catch (JDOMException e) {
535: throw new RuntimeException(e);
536: }
537: Element docRoot = document.getRootElement();
538:
539: Id oldGuidanceId = getIdManager().getId(
540: docRoot.getChildText("id"));
541: String description = docRoot.getChildText("description");
542: String viewFunc = docRoot.getChildText("securityViewFunction");
543: String editFunc = docRoot.getChildText("securityEditFunction");
544:
545: Guidance guidance = new Guidance(getIdManager().createId(),
546: description, siteId, null, viewFunc, editFunc);
547:
548: List itemElements = docRoot.getChild("items").getChildren(
549: "item");
550: List items = new ArrayList();
551: for (Iterator i = itemElements.iterator(); i.hasNext();) {
552: items.add(importItem(guidance, (Element) i.next()));
553: }
554: guidance.setItems(items);
555: guidanceMap.put(oldGuidanceId.getValue(), guidance);
556: }
557:
558: protected byte[] readStreamToBytes(InputStream inStream)
559: throws IOException {
560: ByteArrayOutputStream bytes = new ByteArrayOutputStream();
561: byte data[] = new byte[10 * 1024];
562:
563: int count;
564: while ((count = inStream.read(data, 0, 10 * 1024)) != -1) {
565: bytes.write(data, 0, count);
566: }
567: byte[] tmp = bytes.toByteArray();
568: bytes.close();
569: return tmp;
570: }
571:
572: protected GuidanceItem importItem(Guidance guidance, Element element) {
573: String type = element.getChildText("type");
574: GuidanceItem item = new GuidanceItem(guidance, type);
575: item.setText(element.getChildText("text"));
576:
577: List attachmentElements = element.getChild("attachments")
578: .getChildren("attachment");
579: List attachments = new ArrayList();
580: for (Iterator i = attachmentElements.iterator(); i.hasNext();) {
581: attachments.add(importAttachment(item, (Element) i.next()));
582: }
583: item.setAttachments(attachments);
584:
585: return item;
586: }
587:
588: protected AttachmentImportWrapper importAttachment(
589: GuidanceItem item, Element element) {
590: return new AttachmentImportWrapper(element.getChildText("ref"),
591: element.getChildText("url"));
592: }
593:
594: /**
595: * Pulls all Guidance
596: * @return List of Guidance
597: */
598: public List getGuidanceForWarehousing() {
599: List guidance = getHibernateTemplate().findByNamedQuery(
600: "listGuidances");
601:
602: for (Iterator i = guidance.iterator(); i.hasNext();) {
603: Guidance w = (Guidance) i.next();
604: }
605:
606: return guidance;
607: }
608:
609: public AuthorizationFacade getAuthorizationFacade() {
610: return authorizationFacade;
611: }
612:
613: public void setAuthorizationFacade(
614: AuthorizationFacade authorizationFacade) {
615: this .authorizationFacade = authorizationFacade;
616: }
617:
618: public SecurityService getSecurityService() {
619: return securityService;
620: }
621:
622: public void setSecurityService(SecurityService securityService) {
623: this .securityService = securityService;
624: }
625:
626: public EntityManager getEntityManager() {
627: return entityManager;
628: }
629:
630: public void setEntityManager(EntityManager entityManager) {
631: this .entityManager = entityManager;
632: }
633:
634: public IdManager getIdManager() {
635: return idManager;
636: }
637:
638: public void setIdManager(IdManager idManager) {
639: this .idManager = idManager;
640: }
641:
642: public ContentHostingService getContentHostingService() {
643: return contentHostingService;
644: }
645:
646: public void setContentHostingService(
647: ContentHostingService contentHostingService) {
648: this.contentHostingService = contentHostingService;
649: }
650: }
|