/********************************************************************************** * $URL: $ * $Id: $ *********************************************************************************** * * Author: Charles Hedrick, hedrick@rutgers.edu * * Copyright (c) 2013 Rutgers, the State University of New Jersey * * 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.sakaiproject.lessonbuildertool.ccexport; import java.io.IOException; import java.io.OutputStream; import java.io.PrintStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.HashSet; import java.util.HashMap; import java.util.Collections; import java.util.SortedSet; import java.util.SortedMap; import java.util.TreeSet; import java.util.TreeMap; import java.util.Comparator; import java.util.Date; import java.util.Map; import java.util.Iterator; import java.net.URLEncoder; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.commons.lang.StringEscapeUtils; import org.sakaiproject.component.cover.ServerConfigurationService; import org.sakaiproject.component.cover.ComponentManager; import org.w3c.dom.Document; import org.sakaiproject.site.api.Group; import org.sakaiproject.site.api.Site; import org.sakaiproject.site.cover.SiteService; import org.sakaiproject.tool.api.Session; import org.sakaiproject.tool.cover.ToolManager; import org.sakaiproject.tool.cover.SessionManager; import org.sakaiproject.content.api.ContentResource; import org.sakaiproject.content.cover.ContentHostingService; import org.sakaiproject.memory.api.Cache; import org.sakaiproject.memory.api.CacheRefresher; import org.sakaiproject.memory.api.MemoryService; import uk.org.ponder.messageutil.MessageLocator; import org.sakaiproject.lessonbuildertool.SimplePageItem; import org.sakaiproject.lessonbuildertool.model.SimplePageToolDao; import org.sakaiproject.db.cover.SqlService; import org.sakaiproject.db.api.SqlReader; import java.sql.Connection; import java.sql.ResultSet; import org.sakaiproject.lessonbuildertool.ccexport.ZipPrintStream; import org.sakaiproject.lessonbuildertool.service.LessonEntity; import org.sakaiproject.api.app.messageforums.BaseForum; import org.sakaiproject.api.app.messageforums.DiscussionForum; import org.sakaiproject.api.app.messageforums.DiscussionTopic; import org.sakaiproject.api.app.messageforums.Topic; import org.sakaiproject.api.app.messageforums.MessageForumsForumManager; import org.sakaiproject.api.app.messageforums.ui.DiscussionForumManager; import org.sakaiproject.api.app.messageforums.MessageForumsMessageManager; import org.sakaiproject.api.app.messageforums.PermissionLevelManager; import org.sakaiproject.api.app.messageforums.MessageForumsTypeManager; import org.sakaiproject.api.app.messageforums.AreaManager; import org.sakaiproject.api.app.messageforums.Attachment; import org.sakaiproject.api.app.messageforums.DBMembershipItem; import org.sakaiproject.api.app.messageforums.PermissionLevel; import org.sakaiproject.api.app.messageforums.PermissionsMask; import org.sakaiproject.api.app.messageforums.ui.UIPermissionsManager; import org.sakaiproject.component.app.messageforums.MembershipItem; import org.sakaiproject.entity.api.Reference; import org.sakaiproject.entity.api.ResourceProperties; import org.sakaiproject.util.Validator; import org.sakaiproject.util.FormattedText; /* * set up as a singleton, but CCExport is not */ public class ForumsExport { private static Log log = LogFactory.getLog(ForumsExport.class); private static SimplePageToolDao simplePageToolDao; public void setSimplePageToolDao(Object dao) { simplePageToolDao = (SimplePageToolDao) dao; } static MessageLocator messageLocator = null; public void setMessageLocator(MessageLocator m) { messageLocator = m; } static ForumsExport next = null; public void setNext(ForumsExport n) { next = n; } static MessageForumsForumManager forumManager = (MessageForumsForumManager) ComponentManager.get("org.sakaiproject.api.app.messageforums.MessageForumsForumManager"); static MessageForumsMessageManager messageManager = (MessageForumsMessageManager) ComponentManager.get("org.sakaiproject.api.app.messageforums.MessageForumsMessageManager"); public void init () { // currently nothing to do log.info("init()"); } public void destroy() { log.info("destroy()"); } public static class ForumAttachment { public String logical; public String physical; } public static class ForumItem { public String id; public String title; public String text; public List<ForumAttachment> attachments; } public List<ForumItem> getItemsInSite(String siteId) { List<ForumItem> ret = new ArrayList<ForumItem>(); String siteRef = "/group/" + siteId + "/"; Site site = null; try { site = SiteService.getSite(siteId); } catch (Exception impossible) { // if site doesn't exist, it seems silly to try any more tools return null; } if(site.getToolForCommonId("sakai.forums") == null) { // Forums is not in this site. Move on to the next provider. if (next != null) return next.getItemsInSite(siteId); return null; } for (DiscussionForum forum: forumManager.getForumsForMainPage()) { if (!forum.getDraft()) { for (DiscussionTopic topic: (Set<DiscussionTopic>)forum.getTopicsSet()) { if (topic.getDraft().equals(Boolean.FALSE)) { ForumItem item = new ForumItem(); item.attachments = new ArrayList<ForumAttachment>(); item.id = LessonEntity.FORUM_TOPIC + "/" + topic.getId(); List<Attachment> attachments = topic.getAttachments(); for (Attachment attachment: attachments) { String sakaiId = attachment.getAttachmentId(); ForumAttachment a = new ForumAttachment(); a.logical = sakaiId; a.physical = sakaiId; item.attachments.add(a); } ret.add(item); } } } } if (next != null) { List<ForumItem> newItems = next.getItemsInSite(siteId); if (newItems != null) ret.addAll(newItems); } return ret; } // find topics in site public List<String> getEntitiesInSite(String siteId, CCExport bean) { String siteRef = "/group/" + siteId + "/"; List<String> ret = new ArrayList<String>(); List<ForumItem> items = getItemsInSite(siteId); if (items != null) for (ForumItem item: items) { ret.add(item.id); List<ForumAttachment> attachments = item.attachments; for (ForumAttachment attach: attachments) { // this code is to identify attachments that aren't in the normal // site resources. In that case we have to make a copy of it String url = null; // if it is a URL, need the URL rather than copying the file String logical = attach.logical; String physical = attach.physical; if (!physical.startsWith("///")) { try { ContentResource res = ContentHostingService.getResource(physical); String type = res.getContentType(); if ("text/url".equals(type)) { url = new String(res.getContent()); } } catch (Exception e) { } } if (url != null) ; // if it's a URL we don't need a file else if (! physical.startsWith(siteRef)) { // if in resources, already included int lastSlash = logical.lastIndexOf("/"); String lastAtom = logical.substring(lastSlash + 1); bean.addFile(physical, "attachments/" + item.id + "/" + lastAtom, null); } } } return ret; } public ForumItem getContents(String forumRef) { if (!forumRef.startsWith(LessonEntity.FORUM_TOPIC + "/")) { if (next == null) return null; else return next.getContents(forumRef); } int i = forumRef.indexOf("/"); String forumString = forumRef.substring(i+1); Long forumId = new Long(forumString); Topic topic = forumManager.getTopicById(true, forumId); if (topic == null) return null; ForumItem ret = new ForumItem(); ret.id = forumRef; ret.title = topic.getTitle(); String text = topic.getExtendedDescription(); // html if (text == null || text.trim().equals("")) { text = topic.getShortDescription(); if (text != null) text = FormattedText.convertPlaintextToFormattedText(text); } if (text == null) text = ""; ret.text = text; ret.attachments = new ArrayList<ForumAttachment>(); List<Attachment> attachments = topic.getAttachments(); for (Attachment attachment: attachments) { String sakaiId = attachment.getAttachmentId(); ForumAttachment a = new ForumAttachment(); a.logical = sakaiId; a.physical = sakaiId; ret.attachments.add(a); } return ret; } public boolean outputEntity(String forumRef, ZipPrintStream out, PrintStream errStream, CCExport bean, CCExport.Resource resource, int version) { ForumItem item = getContents(forumRef); // according to the spec, attachments must be Learnning Object web content. That is, they can // be files but not URLs, and they must be in a special directory for this forum topic. // Since we need to be able to support URLs, don't include any attachments. Instead, // append it as URLs at the end of the document. out.println("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"); switch (version) { case CCExport.V11: out.println("<topic xmlns=\"http://www.imsglobal.org/xsd/imsccv1p1/imsdt_v1p1\""); out.println(" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.imsglobal.org/xsd/imsccv1p1/imsdt_v1p1 http://www.imsglobal.org/profile/cc/ccv1p1/ccv1p1_imsdt_v1p1.xsd\">"); break; default: out.println("<topic xmlns=\"http://www.imsglobal.org/xsd/imsccv1p2/imsdt_v1p2\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:schemaLocation=\"http://www.imsglobal.org/xsd/imsccv1p2/imsdt_v1p2 http://www.imsglobal.org/profile/cc/ccv1p2/ccv1p2_imsdt_v1p2.xsd\">"); } out.println(" <title>" + StringEscapeUtils.escapeXml(item.title) + "</title>"); String text = "<div>" + item.text + " </div>"; for (ForumAttachment a: item.attachments) { String logical = a.logical; String physical = a.physical; String location = bean.getLocation(physical); int lastSlash = logical.lastIndexOf("/"); String lastAtom = logical.substring(lastSlash + 1); String URL = null; if (!physical.startsWith("///")) { try { ContentResource res = ContentHostingService.getResource(physical); String type = res.getContentType(); if ("text/url".equals(type)) { URL = new String(res.getContent()); } } catch (Exception e) { } } if (URL != null) lastAtom = URL; // for URL use the whole URL for the text else { URL = "../" + bean.getLocation(physical); URL = "$IMS-CC-FILEBASE$" + Validator.escapeUrl(URL.replaceAll("//", "/")); } text += "\n<a href=\"" + URL + "\">" + StringEscapeUtils.escapeHtml(lastAtom) + "</a><br/>"; bean.addDependency(resource, physical); } out.println(" <text texttype=\"text/html\">" + bean.fixup(text, resource) + "</text>"); out.println("</topic>"); return true; } }