001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/textarea/tags/sakai_2-4-1/FCKeditor/connector/src/java/org/sakaiproject/connector/fck/FCKConnectorServlet.java $
003: * $Id: FCKConnectorServlet.java 22383 2007-03-10 07:39:53Z joshua.ryan@asu.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 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.sakaiproject.connector.fck;
021:
022: import java.io.IOException;
023: import java.io.PrintWriter;
024: import java.util.ArrayList;
025: import java.util.HashMap;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Map;
029:
030: import javax.servlet.ServletException;
031: import javax.servlet.http.HttpServlet;
032: import javax.servlet.http.HttpServletRequest;
033: import javax.servlet.http.HttpServletResponse;
034: import javax.xml.parsers.DocumentBuilder;
035: import javax.xml.parsers.DocumentBuilderFactory;
036: import javax.xml.parsers.ParserConfigurationException;
037: import javax.xml.transform.Transformer;
038: import javax.xml.transform.TransformerFactory;
039: import javax.xml.transform.dom.DOMSource;
040: import javax.xml.transform.stream.StreamResult;
041:
042: import org.apache.commons.fileupload.DiskFileUpload;
043: import org.apache.commons.fileupload.FileItem;
044: import org.sakaiproject.authz.api.SecurityAdvisor;
045: import org.sakaiproject.authz.cover.SecurityService;
046: import org.sakaiproject.content.api.ContentCollection;
047: import org.sakaiproject.content.api.ContentCollectionEdit;
048: import org.sakaiproject.content.api.ContentResource;
049: import org.sakaiproject.content.cover.ContentHostingService;
050: import org.sakaiproject.entity.api.Entity;
051: import org.sakaiproject.entity.api.ResourceProperties;
052: import org.sakaiproject.entity.api.ResourcePropertiesEdit;
053: import org.sakaiproject.event.cover.NotificationService;
054: import org.sakaiproject.exception.IdUsedException;
055: import org.sakaiproject.exception.PermissionException;
056: import org.sakaiproject.tool.cover.SessionManager;
057: import org.sakaiproject.util.StringUtil;
058: import org.sakaiproject.util.Validator;
059: import org.w3c.dom.Document;
060: import org.w3c.dom.Element;
061: import org.w3c.dom.Node;
062:
063: /**
064: * Conenctor Servlet to upload and browse files to a Sakai worksite for the FCK editor.<br>
065: *
066: * This servlet accepts 4 commands used to retrieve and create files and folders from a worksite resource.
067: * The allowed commands are:
068: * <ul>
069: * <li>GetFolders: Retrive the list of directory under the current folder
070: * <li>GetFoldersAndFiles: Retrive the list of files and directory under the current folder
071: * <li>CreateFolder: Create a new directory under the current folder
072: * <li>FileUpload: Send a new file to the server (must be sent with a POST)
073: * </ul>
074: *
075: * The current user must have a valid sakai session with permissions to access the realm associated
076: * with the resource.
077: *
078: * @author Joshua Ryan (joshua.ryan@asu.edu) merged servlets and Sakai-ified them
079: *
080: * This connector is loosely based on two servlets found on the FCK website http://www.fckeditor.net/
081: * written by Simone Chiaretta (simo@users.sourceforge.net)
082: *
083: */
084:
085: public class FCKConnectorServlet extends HttpServlet {
086:
087: private static final long serialVersionUID = 1L;
088:
089: private static final String FCK_ADVISOR_BASE = "fck.security.advisor.";
090: private static final String FCK_EXTRA_COLLECTIONS_BASE = "fck.extra.collections.";
091:
092: /**
093: * Manage the Get requests (GetFolders, GetFoldersAndFiles, CreateFolder).<br>
094: *
095: * The servlet accepts commands sent in the following format:<br>
096: * connector?Command=CommandName&Type=ResourceType&CurrentFolder=FolderPath<br><br>
097: * It executes the command and then return the results to the client in XML format.
098: *
099: * Valid values for Type are: Image, File, Flash and Link
100: *
101: */
102: public void doGet(HttpServletRequest request,
103: HttpServletResponse response) throws ServletException,
104: IOException {
105:
106: response.setContentType("text/xml; charset=UTF-8");
107: response.setHeader("Cache-Control", "no-cache");
108: PrintWriter out = null;
109:
110: String commandStr = request.getParameter("Command");
111: String type = request.getParameter("Type");
112: String currentFolder = request.getParameter("CurrentFolder");
113:
114: String collectionBase = request.getPathInfo();
115:
116: SecurityAdvisor advisor = (SecurityAdvisor) SessionManager
117: .getCurrentSession().getAttribute(
118: FCK_ADVISOR_BASE + collectionBase);
119: if (advisor != null) {
120: SecurityService.pushAdvisor(advisor);
121: }
122:
123: Document document = null;
124: try {
125: DocumentBuilderFactory factory = DocumentBuilderFactory
126: .newInstance();
127: DocumentBuilder builder = factory.newDocumentBuilder();
128: document = builder.newDocument();
129: } catch (ParserConfigurationException pce) {
130: pce.printStackTrace();
131: }
132:
133: Node root = createCommonXml(document, commandStr, type,
134: currentFolder, ContentHostingService
135: .getUrl(currentFolder));
136:
137: if ("GetFolders".equals(commandStr)) {
138: getFolders(currentFolder, root, document, collectionBase);
139: } else if ("GetFoldersAndFiles".equals(commandStr)) {
140: getFolders(currentFolder, root, document, collectionBase);
141: getFiles(currentFolder, root, document, type);
142: } else if ("CreateFolder".equals(commandStr)) {
143: String newFolderStr = request.getParameter("NewFolderName");
144: String status = "110";
145:
146: try {
147: ContentCollectionEdit edit = ContentHostingService
148: .addCollection(currentFolder
149: + Validator
150: .escapeResourceName(newFolderStr)
151: + Entity.SEPARATOR);
152: ResourcePropertiesEdit resourceProperties = edit
153: .getPropertiesEdit();
154: resourceProperties.addProperty(
155: ResourceProperties.PROP_DISPLAY_NAME,
156: newFolderStr);
157: String altRoot = getAltReferenceRoot(currentFolder);
158: if (altRoot != null)
159: resourceProperties
160: .addProperty(
161: ContentHostingService.PROP_ALTERNATE_REFERENCE,
162: altRoot);
163:
164: ContentHostingService.commitCollection(edit);
165:
166: status = "0";
167: } catch (IdUsedException iue) {
168: status = "101";
169: } catch (PermissionException pex) {
170: status = "103";
171: } catch (Exception e) {
172: status = "102";
173: }
174: setCreateFolderResponse(status, root, document);
175: }
176:
177: document.getDocumentElement().normalize();
178: try {
179: out = response.getWriter();
180: TransformerFactory tFactory = TransformerFactory
181: .newInstance();
182: Transformer transformer = tFactory.newTransformer();
183:
184: DOMSource source = new DOMSource(document);
185:
186: StreamResult result = new StreamResult(out);
187: transformer.transform(source, result);
188:
189: } catch (Exception ex) {
190: ex.printStackTrace();
191: } finally {
192: if (out != null) {
193: out.close();
194: }
195: }
196:
197: if (advisor != null) {
198: SecurityService.clearAdvisors();
199: }
200: }
201:
202: /**
203: * Manage the Post requests (FileUpload).<br>
204: *
205: * The servlet accepts commands sent in the following format:<br>
206: * connector?Command=FileUpload&Type=ResourceType&CurrentFolder=FolderPath<br><br>
207: * It stores the file (renaming it in case a file with the same name exists) and then return an HTML file
208: * with a javascript command in it.
209: *
210: */
211: public void doPost(HttpServletRequest request,
212: HttpServletResponse response) throws ServletException,
213: IOException {
214:
215: response.setContentType("text/html; charset=UTF-8");
216: response.setHeader("Cache-Control", "no-cache");
217: PrintWriter out = null;
218:
219: String command = request.getParameter("Command");
220:
221: String currentFolder = request.getParameter("CurrentFolder");
222: String collectionBase = request.getPathInfo();
223:
224: SecurityAdvisor advisor = (SecurityAdvisor) SessionManager
225: .getCurrentSession().getAttribute(
226: FCK_ADVISOR_BASE + collectionBase);
227: if (advisor != null) {
228: SecurityService.pushAdvisor(advisor);
229: }
230:
231: String fileName = "";
232: String errorMessage = "";
233:
234: String status = "0";
235:
236: if (!"FileUpload".equals(command)
237: && !"QuickUpload".equals(command)) {
238: status = "203";
239: } else {
240: DiskFileUpload upload = new DiskFileUpload();
241: try {
242: List items = upload.parseRequest(request);
243:
244: Map fields = new HashMap();
245:
246: Iterator iter = items.iterator();
247: while (iter.hasNext()) {
248:
249: FileItem item = (FileItem) iter.next();
250: if (item.isFormField()) {
251: fields.put(item.getFieldName(), item
252: .getString());
253: } else {
254: fields.put(item.getFieldName(), item);
255: }
256: }
257: FileItem uplFile = (FileItem) fields.get("NewFile");
258:
259: String filePath = uplFile.getName();
260: filePath = filePath.replace('\\', '/');
261: String[] pathParts = filePath.split("/");
262: fileName = pathParts[pathParts.length - 1];
263:
264: String nameWithoutExt = fileName;
265: String ext = "";
266:
267: if (fileName.lastIndexOf(".") > 0) {
268: nameWithoutExt = fileName.substring(0, fileName
269: .lastIndexOf("."));
270: ext = fileName.substring(fileName.lastIndexOf("."));
271: }
272:
273: String mime = uplFile.getContentType();
274:
275: int counter = 1;
276: boolean done = false;
277:
278: while (!done) {
279: try {
280: ResourcePropertiesEdit resourceProperties = ContentHostingService
281: .newResourceProperties();
282: resourceProperties.addProperty(
283: ResourceProperties.PROP_DISPLAY_NAME,
284: fileName);
285:
286: String altRoot = getAltReferenceRoot(currentFolder);
287: if (altRoot != null)
288: resourceProperties
289: .addProperty(
290: ContentHostingService.PROP_ALTERNATE_REFERENCE,
291: altRoot);
292:
293: int noti = NotificationService.NOTI_NONE;
294:
295: ContentHostingService.addResource(currentFolder
296: + fileName, mime, uplFile.get(),
297: resourceProperties, noti);
298: done = true;
299: } catch (IdUsedException iue) {
300: //the name is already used, so we do a slight rename to prevent the colision
301: fileName = nameWithoutExt + "(" + counter + ")"
302: + ext;
303: status = "201";
304: counter++;
305: }
306:
307: catch (Exception ex) {
308: //this user can't write where they are trying to write.
309: done = true;
310: ex.printStackTrace();
311: status = "203";
312: }
313: }
314: } catch (Exception ex) {
315: ex.printStackTrace();
316: status = "203";
317: }
318: }
319:
320: try {
321: out = response.getWriter();
322: out.println("<script type=\"text/javascript\">");
323:
324: if ("QuickUpload".equals(command)) {
325: out.println("window.parent.OnUploadCompleted(" + status
326: + ",'"
327: + ContentHostingService.getUrl(currentFolder)
328: + fileName + "','" + fileName + "','"
329: + errorMessage + "');");
330: } else {
331: out
332: .println("window.parent.frames['frmUpload'].OnUploadCompleted("
333: + status + ",'" + fileName + "');");
334: }
335:
336: out.println("</script>");
337: } catch (Exception e) {
338: e.printStackTrace();
339: } finally {
340: if (out != null) {
341: out.close();
342: }
343: }
344:
345: if (advisor != null) {
346: SecurityService.clearAdvisors();
347: }
348: }
349:
350: private void setCreateFolderResponse(String status, Node root,
351: Document doc) {
352: Element element = doc.createElement("Error");
353: element.setAttribute("number", status);
354: root.appendChild(element);
355: }
356:
357: private void getFolders(String dir, Node root, Document doc,
358: String collectionBase) {
359: Element folders = doc.createElement("Folders");
360: root.appendChild(folders);
361:
362: ContentCollection collection = null;
363:
364: Map map = null;
365: Iterator foldersIterator = null;
366:
367: try {
368: //hides the real root level stuff and just shows the users the
369: //the root folders of all the top collections they actually have access to.
370: if (dir.split("/").length == 2) {
371: List collections = new ArrayList();
372: map = ContentHostingService.getCollectionMap();
373: if (map != null && map.keySet() != null) {
374: collections.addAll(map.keySet());
375: }
376: List extras = (List) SessionManager.getCurrentSession()
377: .getAttribute(
378: FCK_EXTRA_COLLECTIONS_BASE
379: + collectionBase);
380: if (extras != null) {
381: collections.addAll(extras);
382: }
383:
384: foldersIterator = collections.iterator();
385: } else if (dir.split("/").length > 2) {
386: collection = ContentHostingService.getCollection(dir);
387: if (collection != null
388: && collection.getMembers() != null) {
389: foldersIterator = collection.getMembers()
390: .iterator();
391: }
392: }
393: } catch (Exception e) {
394: e.printStackTrace();
395: //not a valid collection? file list will be empty and so will the doc
396: }
397: if (foldersIterator != null) {
398: String current = null;
399:
400: while (foldersIterator.hasNext()) {
401: try {
402: current = (String) foldersIterator.next();
403: ContentCollection myCollection = ContentHostingService
404: .getCollection(current);
405: Element element = doc.createElement("Folder");
406: element.setAttribute("url", current);
407: element.setAttribute("name", myCollection
408: .getProperties().getProperty(
409: myCollection.getProperties()
410: .getNamePropDisplayName()));
411: folders.appendChild(element);
412: } catch (Exception e) {
413: //do nothing, we either don't have access to the collction or it's a resource
414: }
415: }
416: }
417: }
418:
419: private void getFiles(String dir, Node root, Document doc,
420: String type) {
421: Element files = doc.createElement("Files");
422: root.appendChild(files);
423:
424: ContentCollection collection = null;
425:
426: try {
427: collection = ContentHostingService.getCollection(dir);
428: } catch (Exception e) {
429: //do nothing, file will be empty and so will doc
430: }
431: if (collection != null) {
432: Iterator iterator = collection.getMemberResources()
433: .iterator();
434:
435: while (iterator.hasNext()) {
436: try {
437: ContentResource current = (ContentResource) iterator
438: .next();
439:
440: String ext = current.getProperties().getProperty(
441: current.getProperties()
442: .getNamePropContentType());
443:
444: if (("File".equals(type) && (ext != null))
445: || ("Flash".equals(type) && ext
446: .equalsIgnoreCase("application/x-shockwave-flash"))
447: || ("Image".equals(type) && ext
448: .startsWith("image"))
449: || "Link".equals(type)) {
450:
451: String id = current.getId();
452:
453: Element element = doc.createElement("File");
454: // displaying the id instead of the display name because the url used
455: // for linking in the FCK editor uses what is returned...
456: element
457: .setAttribute(
458: "name",
459: current
460: .getProperties()
461: .getProperty(
462: current
463: .getProperties()
464: .getNamePropDisplayName()));
465: element.setAttribute("url", current.getUrl());
466:
467: if (current.getProperties().getProperty(
468: current.getProperties()
469: .getNamePropContentLength()) != null) {
470:
471: element
472: .setAttribute(
473: "size",
474: ""
475: + current
476: .getProperties()
477: .getPropertyFormatted(
478: current
479: .getProperties()
480: .getNamePropContentLength()));
481: } else {
482: element.setAttribute("size", "0");
483:
484: }
485: files.appendChild(element);
486: }
487: } catch (ClassCastException e) {
488: //it's a colleciton not an item
489: } catch (Exception e) {
490: //do nothing, we don't have access to the item
491: }
492: }
493: }
494: }
495:
496: private Node createCommonXml(Document doc, String commandStr,
497: String type, String currentPath, String currentUrl) {
498: Element root = doc.createElement("Connector");
499: doc.appendChild(root);
500: root.setAttribute("command", commandStr);
501: root.setAttribute("resourceType", type);
502:
503: Element element = doc.createElement("CurrentFolder");
504: element.setAttribute("path", currentPath);
505: element.setAttribute("url", currentUrl);
506: root.appendChild(element);
507:
508: return root;
509:
510: }
511:
512: private String getAltReferenceRoot(String id) {
513: String altRoot = null;
514: try {
515: altRoot = StringUtil
516: .trimToNull(ContentHostingService
517: .getProperties(id)
518: .getProperty(
519: ContentHostingService.PROP_ALTERNATE_REFERENCE));
520: } catch (Exception e) {
521: // do nothing, we either didn't have permission or the id is bogus
522: }
523: if (altRoot != null && !"/".equals(altRoot)
524: && !"".equals(altRoot)) {
525: if (!altRoot.startsWith(Entity.SEPARATOR))
526: altRoot = Entity.SEPARATOR + altRoot;
527: if (altRoot.endsWith(Entity.SEPARATOR))
528: altRoot = altRoot.substring(0, altRoot.length()
529: - Entity.SEPARATOR.length());
530: return altRoot;
531: } else
532: return null;
533: }
534:
535: }
|