0001: /**********************************************************************************
0002: * $URL: https://source.sakaiproject.org/svn/archive/tags/sakai_2-4-1/archive-impl/impl/src/java/org/sakaiproject/archive/impl/BasicArchiveService.java $
0003: * $Id: BasicArchiveService.java 28700 2007-04-11 23:26:41Z ajpoland@iupui.edu $
0004: ***********************************************************************************
0005: *
0006: * Copyright (c) 2003, 2004, 2005, 2006 The Sakai Foundation.
0007: *
0008: * Licensed under the Educational Community License, Version 1.0 (the "License");
0009: * you may not use this file except in compliance with the License.
0010: * You may obtain a copy of the License at
0011: *
0012: * http://www.opensource.org/licenses/ecl1.php
0013: *
0014: * Unless required by applicable law or agreed to in writing, software
0015: * distributed under the License is distributed on an "AS IS" BASIS,
0016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0017: * See the License for the specific language governing permissions and
0018: * limitations under the License.
0019: *
0020: **********************************************************************************/package org.sakaiproject.archive.impl;
0021:
0022: import java.io.File;
0023: import java.util.Collections;
0024: import java.util.HashMap;
0025: import java.util.HashSet;
0026: import java.util.Iterator;
0027: import java.util.List;
0028: import java.util.Map;
0029: import java.util.Stack;
0030: import java.util.Vector;
0031:
0032: import org.apache.commons.codec.binary.Base64;
0033: import org.apache.commons.logging.Log;
0034: import org.apache.commons.logging.LogFactory;
0035: import org.sakaiproject.archive.api.ArchiveService;
0036: import org.sakaiproject.authz.api.AuthzGroup;
0037: import org.sakaiproject.authz.api.GroupNotDefinedException;
0038: import org.sakaiproject.authz.api.Role;
0039: import org.sakaiproject.authz.cover.AuthzGroupService;
0040: import org.sakaiproject.authz.cover.SecurityService;
0041: import org.sakaiproject.component.api.ServerConfigurationService;
0042: import org.sakaiproject.component.cover.ComponentManager;
0043: import org.sakaiproject.content.api.ContentHostingService;
0044: import org.sakaiproject.entity.api.EntityManager;
0045: import org.sakaiproject.entity.api.EntityProducer;
0046: import org.sakaiproject.exception.IdInvalidException;
0047: import org.sakaiproject.exception.IdUnusedException;
0048: import org.sakaiproject.exception.IdUsedException;
0049: import org.sakaiproject.exception.InUseException;
0050: import org.sakaiproject.exception.PermissionException;
0051: import org.sakaiproject.site.api.Site;
0052: import org.sakaiproject.site.cover.SiteService;
0053: import org.sakaiproject.time.api.Time;
0054: import org.sakaiproject.time.cover.TimeService;
0055: import org.sakaiproject.user.api.User;
0056: import org.sakaiproject.user.api.UserAlreadyDefinedException;
0057: import org.sakaiproject.user.api.UserEdit;
0058: import org.sakaiproject.user.api.UserIdInvalidException;
0059: import org.sakaiproject.user.api.UserNotDefinedException;
0060: import org.sakaiproject.user.api.UserPermissionException;
0061: import org.sakaiproject.user.cover.UserDirectoryService;
0062: import org.sakaiproject.util.Xml;
0063: import org.w3c.dom.Document;
0064: import org.w3c.dom.Element;
0065: import org.w3c.dom.Node;
0066: import org.w3c.dom.NodeList;
0067:
0068: /**
0069: * <p>...</p>
0070: */
0071: public class BasicArchiveService implements ArchiveService {
0072: /** Our logger. */
0073: private static Log M_log = LogFactory
0074: .getLog(BasicArchiveService.class);
0075:
0076: /** A full path and file name to the storage file. */
0077: protected String m_storagePath = "/";
0078:
0079: protected static HashMap userIdTrans = new HashMap();
0080:
0081: protected HashSet UsersListAllowImport = new HashSet();
0082:
0083: // only the resources created by the followinng roles will be imported
0084: // role sets are different to different system
0085: public String[] SAKAI_roles = { "Affiliate", "Assistant",
0086: "Instructor", "Maintain", "Organizer", "Owner" };
0087: public String[] CT_roles = { "Affiliate", "Assistant",
0088: "Instructor", "Maintain", "Organizer", "Owner" };
0089: public String[] WT_roles = {};
0090:
0091: // tool id updates
0092: private String old_toolId_prefix = "chef.";
0093: private String new_toolId_prefix = "sakai.";
0094: private String[] old_toolIds = { "sakai.noti.prefs",
0095: "sakai.presence", "sakai.siteinfogeneric",
0096: "sakai.sitesetupgeneric", "sakai.threadeddiscussion" };
0097: private String[] new_toolIds = { "sakai.preferences",
0098: "sakai.online", "sakai.siteinfo", "sakai.sitesetup",
0099: "sakai.discussion" };
0100:
0101: // only these Sakai tools will be imported
0102: public String[] SakaiServicesToImport = { "AnnouncementService",
0103: "AssignmentService", "ContentHostingService",
0104: "CalendarService", "DiscussionService",
0105: "MailArchiveService", "SyllabusService",
0106: "RWikiObjectService", "DiscussionForumService",
0107: "WebService" };
0108:
0109: public String[] CT_tools_toImport = {};
0110: public String[] WT_tools_toImport = {};
0111:
0112: //TODO: the draft flag settings for WT and CT
0113:
0114: public HashMap tool_draft_flag = new HashMap();
0115:
0116: /*******************************************************************************
0117: * Dependencies and their setter methods
0118: *******************************************************************************/
0119:
0120: /** Dependency: ServerConfigurationService. */
0121: protected ServerConfigurationService m_serverConfigurationService = null;
0122:
0123: /**
0124: * Dependency: ServerConfigurationService.
0125: * @param service The ServerConfigurationService.
0126: */
0127: public void setServerConfigurationService(
0128: ServerConfigurationService service) {
0129: m_serverConfigurationService = service;
0130: }
0131:
0132: /**
0133: * Configuration: Set the Storage Path.
0134: * @param path The storage path.
0135: */
0136: public void setStoragePath(String path) {
0137: m_storagePath = path;
0138: }
0139:
0140: /** Dependency: EntityManager. */
0141: protected EntityManager m_entityManager = null;
0142:
0143: /**
0144: * Dependency: EntityManager.
0145: *
0146: * @param service
0147: * The EntityManager.
0148: */
0149: public void setEntityManager(EntityManager service) {
0150: m_entityManager = service;
0151: }
0152:
0153: /*******************************************************************************
0154: * Init and Destroy
0155: *******************************************************************************/
0156:
0157: /**
0158: * Final initialization, once all dependencies are set.
0159: */
0160: public void init() {
0161: if ((m_storagePath != null) && (!m_storagePath.endsWith("/"))) {
0162: m_storagePath = m_storagePath + "/";
0163: }
0164:
0165: M_log.info("init(): storage path: " + m_storagePath);
0166: }
0167:
0168: /**
0169: * Final cleanup.
0170: */
0171: public void destroy() {
0172: M_log.info("destroy()");
0173: }
0174:
0175: /*******************************************************************************
0176: * ArchiveService implementation
0177: *******************************************************************************/
0178:
0179: /**
0180: * Create an archive for the resources of a site.
0181: * @param siteId The id of the site to archive.
0182: * @return A log of messages from the archive.
0183: */
0184: public String archive(String siteId) {
0185: StringBuffer results = new StringBuffer();
0186:
0187: if (M_log.isDebugEnabled())
0188: M_log.debug("archive(): site: " + siteId);
0189:
0190: Site theSite = null;
0191: try {
0192: theSite = SiteService.getSite(siteId);
0193: } catch (IdUnusedException e) {
0194: results.append("Site: " + siteId + " not found.\n");
0195: M_log.warn("archive(): site not found: " + siteId);
0196: return results.toString();
0197: }
0198:
0199: // collect all the attachments we need
0200: List attachments = m_entityManager.newReferenceList();
0201:
0202: Time now = TimeService.newTime();
0203:
0204: // this is the folder we are writing files to
0205: String storagePath = m_storagePath + siteId + "-archive/";
0206:
0207: // create the directory for the archive
0208: File dir = new File(m_storagePath + siteId + "-archive/");
0209: dir.mkdirs();
0210:
0211: // for each registered ResourceService, give it a chance to archve
0212: List services = m_entityManager.getEntityProducers();
0213: for (Iterator iServices = services.iterator(); iServices
0214: .hasNext();) {
0215: EntityProducer service = (EntityProducer) iServices.next();
0216: if (service == null)
0217: continue;
0218: if (!service.willArchiveMerge())
0219: continue;
0220:
0221: Document doc = Xml.createDocument();
0222: Stack stack = new Stack();
0223: Element root = doc.createElement("archive");
0224: doc.appendChild(root);
0225: root.setAttribute("source", siteId);
0226: root.setAttribute("server", m_serverConfigurationService
0227: .getServerId());
0228: root.setAttribute("date", now.toString());
0229: root.setAttribute("system", FROM_SAKAI);
0230:
0231: stack.push(root);
0232:
0233: try {
0234: String msg = service.archive(siteId, doc, stack,
0235: storagePath, attachments);
0236: results.append(msg);
0237: } catch (Throwable t) {
0238: results.append(t.toString() + "\n");
0239: }
0240:
0241: stack.pop();
0242:
0243: String fileName = storagePath + service.getLabel() + ".xml";
0244: Xml.writeDocument(doc, fileName);
0245: }
0246:
0247: // archive the collected attachments
0248: if (attachments.size() > 0) {
0249: Document doc = Xml.createDocument();
0250: Stack stack = new Stack();
0251: Element root = doc.createElement("archive");
0252: doc.appendChild(root);
0253: root.setAttribute("source", siteId);
0254: root.setAttribute("server", m_serverConfigurationService
0255: .getServerId());
0256: root.setAttribute("date", now.toString());
0257: root.setAttribute("system", FROM_SAKAI);
0258:
0259: stack.push(root);
0260:
0261: String msg = org.sakaiproject.content.cover.ContentHostingService
0262: .archiveResources(attachments, doc, stack,
0263: storagePath);
0264: results.append(msg);
0265:
0266: stack.pop();
0267:
0268: String fileName = storagePath + "attachment.xml";
0269: Xml.writeDocument(doc, fileName);
0270: }
0271:
0272: // *** Site
0273:
0274: Document doc = Xml.createDocument();
0275: Stack stack = new Stack();
0276: Element root = doc.createElement("archive");
0277: doc.appendChild(root);
0278: root.setAttribute("site", siteId);
0279: root.setAttribute("date", now.toString());
0280: root.setAttribute("system", FROM_SAKAI);
0281:
0282: stack.push(root);
0283:
0284: String msg = archiveSite(theSite, doc, stack);
0285: results.append(msg);
0286:
0287: stack.pop();
0288: Xml.writeDocument(doc, m_storagePath + siteId
0289: + "-archive/site.xml");
0290:
0291: // *** Users
0292: doc = Xml.createDocument();
0293: stack = new Stack();
0294: root = doc.createElement("archive");
0295: doc.appendChild(root);
0296: root.setAttribute("site", siteId);
0297: root.setAttribute("date", now.toString());
0298: root.setAttribute("system", FROM_SAKAI);
0299:
0300: stack.push(root);
0301:
0302: msg = archiveUsers(theSite, doc, stack);
0303: results.append(msg);
0304:
0305: stack.pop();
0306: Xml.writeDocument(doc, m_storagePath + siteId
0307: + "-archive/user.xml");
0308:
0309: return results.toString();
0310:
0311: } // archive
0312:
0313: /**
0314: * Archive the site definition.
0315: * @param site the site.
0316: * @param doc The document to contain the xml.
0317: * @param stack The stack of elements, the top of which will be the containing
0318: * element of the "site" element.
0319: */
0320:
0321: protected static String archiveSite(Site site, Document doc,
0322: Stack stack) {
0323: Element element = doc.createElement(SiteService.APPLICATION_ID);
0324: ((Element) stack.peek()).appendChild(element);
0325: stack.push(element);
0326:
0327: Element siteNode = site.toXml(doc, stack);
0328: stack.push(siteNode);
0329:
0330: // to add the realm node with user list into site
0331: List roles = new Vector();
0332: String realmId = "/site/" + site.getId();
0333: try {
0334: Role role = null;
0335: AuthzGroup realm = AuthzGroupService.getAuthzGroup(realmId);
0336:
0337: Element realmNode = doc.createElement("roles");
0338: ((Element) stack.peek()).appendChild(realmNode);
0339: stack.push(realmNode);
0340:
0341: roles.addAll(realm.getRoles());
0342:
0343: for (int i = 0; i < roles.size(); i++) {
0344: role = (Role) roles.get(i);
0345: String roleId = role.getId();
0346: Element node = doc.createElement(roleId);
0347: realmNode.appendChild(node);
0348:
0349: List users = new Vector();
0350: users.addAll(realm.getUsersHasRole(role.getId()));
0351: for (int j = 0; j < users.size(); j++) {
0352: Element abilityNode = doc.createElement("ability");
0353: abilityNode.setAttribute("roleId", roleId);
0354: abilityNode.setAttribute("userId", ((String) users
0355: .get(j)));
0356: node.appendChild(abilityNode);
0357: }
0358: }
0359: } catch (Exception any) {
0360: // M_log.warn("archve: exception archiving site: "+ site.getId() + ": ", any);
0361: }
0362:
0363: stack.pop();
0364:
0365: return "archiving Site: " + site.getId() + "\n";
0366:
0367: } // archiveSite
0368:
0369: /**
0370: * Archive the users defined in this site (internal users only).
0371: * @param site the site.
0372: * @param doc The document to contain the xml.
0373: * @param stack The stack of elements, the top of which will be the containing
0374: * element of the "site" element.
0375: */
0376: protected static String archiveUsers(Site site, Document doc,
0377: Stack stack) {
0378: Element element = doc
0379: .createElement(UserDirectoryService.APPLICATION_ID);
0380: ((Element) stack.peek()).appendChild(element);
0381: stack.push(element);
0382:
0383: try {
0384: // get the site's user list
0385: List users = new Vector();
0386: String realmId = "/site/" + site.getId();
0387: try {
0388: AuthzGroup realm = AuthzGroupService
0389: .getAuthzGroup(realmId);
0390: users.addAll(UserDirectoryService.getUsers(realm
0391: .getUsers()));
0392: Collections.sort(users);
0393: for (int i = 0; i < users.size(); i++) {
0394: User user = (User) users.get(i);
0395: user.toXml(doc, stack);
0396: }
0397: } catch (GroupNotDefinedException e) {
0398: //Log.warn("chef", "SiteAction.updateParticipantList IdUnusedException " + realmId);
0399: } catch (Exception any) {
0400: }
0401:
0402: } catch (Exception any) {
0403: //.M_log.warn("archve: exception archiving users: "
0404: // + site.getId() + ": ", any);
0405: }
0406:
0407: stack.pop();
0408:
0409: return "archiving the users for Site: " + site.getId() + "\n";
0410:
0411: } // archiveUsers
0412:
0413: /**
0414: * Process a merge for the file, or if it's a directory, for all contained files (one level deep).
0415: * @param fileName The site name (for the archive file) to read from.
0416: * @param mergeId The id string to use to make ids in the merge consistent and unique.
0417: * @param creatorId The creator id
0418: * If null or blank, the date/time string of the merge is used.
0419: */
0420: public String merge(String fileName, String siteId, String creatorId) {
0421: StringBuffer results = new StringBuffer();
0422:
0423: File[] files = null;
0424:
0425: // see if the name is a directory
0426: File file = new File(m_storagePath + fileName);
0427: if ((file == null) || (!file.exists())) {
0428: results.append("file: " + file.getPath() + " not found.\n");
0429: M_log.warn("merge(): file not found: " + file.getPath());
0430: return results.toString();
0431: }
0432:
0433: if (file.isDirectory()) {
0434: files = file.listFiles();
0435: } else {
0436: files = new File[1];
0437: files[0] = file;
0438: }
0439:
0440: // track old to new attachment names
0441: Map attachmentNames = new HashMap();
0442:
0443: // firstly, merge the users
0444: for (int i = 0; i < files.length; i++) {
0445: if ((files[i] != null)
0446: && (files[i].getPath().indexOf("user.xml") != -1)) {
0447: processMerge(files[i].getPath(), siteId, results,
0448: attachmentNames, null);
0449: files[i] = null;
0450: break;
0451: }
0452: }
0453:
0454: // see if there's a site definition
0455: for (int i = 0; i < files.length; i++) {
0456: if ((files[i] != null)
0457: && (files[i].getPath().indexOf("site.xml") != -1)) {
0458: processMerge(files[i].getPath(), siteId, results,
0459: attachmentNames, creatorId);
0460: files[i] = null;
0461: break;
0462: }
0463: }
0464:
0465: // see if there's an attachments definition
0466: for (int i = 0; i < files.length; i++) {
0467: if ((files[i] != null)
0468: && (files[i].getPath().indexOf("attachment.xml") != -1)) {
0469: processMerge(files[i].getPath(), siteId, results,
0470: attachmentNames, null);
0471: files[i] = null;
0472: break;
0473: }
0474: }
0475:
0476: // process each remaining file that is an .xml file
0477: for (int i = 0; i < files.length; i++) {
0478: if (files[i] != null)
0479: if (files[i].getPath().endsWith(".xml"))
0480: processMerge(files[i].getPath(), siteId, results,
0481: attachmentNames, null);
0482: }
0483:
0484: return results.toString();
0485:
0486: } // merge
0487:
0488: /**
0489: * When Sakai is importing an item archived by Sakai, check the creator's role first.
0490: * The item is imported when the role is in the acceptance list,
0491: * @param siteId
0492: * @param userId
0493: * @return boolean value - true: the role is accepted for importing; otherwise, not;
0494: */
0495: public boolean checkSakaiRole(String siteId, String userId) {
0496: // Check - In sakai, if this tool accept this role during importing
0497: // currently, all the tools allowed to be imported, are using the same role set
0498: try {
0499: AuthzGroup realm = AuthzGroupService.getAuthzGroup(siteId);
0500:
0501: // get the role of the user as this realm
0502: Role role = realm.getRole(userId);
0503:
0504: for (int i = 0; i < SAKAI_roles.length; i++) {
0505: if (!SAKAI_roles[i].equalsIgnoreCase(role.getId()))
0506: continue;
0507: return true;
0508: }
0509: } catch (GroupNotDefinedException e) {
0510: }
0511:
0512: return false;
0513:
0514: }//checkSakaiRole
0515:
0516: /**
0517: * When Sakai is importing a role in site.xml, check if it is a qualified role.
0518: * @param roleId
0519: * @return boolean value - true: the role is accepted for importing; otherwise, not;
0520: */
0521: protected boolean checkSystemRole(String system, String roleId) {
0522: if (system.equalsIgnoreCase(FROM_CT)) {
0523: // Check - if CT classic accepts the resource made by this role during importing
0524: for (int i = 0; i < CT_roles.length; i++) {
0525: if (!CT_roles[i].equalsIgnoreCase(roleId))
0526: continue;
0527: return true;
0528: }
0529: } else if (system.equalsIgnoreCase(FROM_SAKAI)) {
0530: // Check - if CTools accepts the resource made by this role during importing
0531: for (int i = 0; i < SAKAI_roles.length; i++) {
0532: if (!SAKAI_roles[i].equalsIgnoreCase(roleId))
0533: continue;
0534: return true;
0535: }
0536: }
0537:
0538: return false;
0539:
0540: }//checkSystemRole
0541:
0542: /*
0543: *
0544: */
0545: protected boolean checkSakaiService(String serviceName) {
0546: for (int i = 0; i < SakaiServicesToImport.length; i++) {
0547: if (serviceName.endsWith(SakaiServicesToImport[i])) {
0548: return true;
0549: }
0550: }
0551: return false;
0552: }
0553:
0554: /**
0555: * When importing an item, check if it needs to be set as a draft
0556: * @param system The system by which this file was archived
0557: * @param toolService The service name of the tool calling this function
0558: * @return boolean value - true: draft requested; false: as it is.
0559: */
0560: /*
0561: public boolean checkToolDraftFlag(String system, String toolService)
0562: {
0563: if (system.equalsIgnoreCase(FROM_SAKAI))
0564: {
0565: if (toolService.equalsIgnoreCase(AnnouncementService.SERVICE_NAME))
0566: {
0567: return SAKAI_annc_draft_import;
0568: }
0569: else if (toolService.equalsIgnoreCase(AssignmentService.SERVICE_NAME))
0570: {
0571: return SAKAI_assign_draft_import;
0572: }
0573: else if (toolService.equalsIgnoreCase(ContentHostingService.SERVICE_NAME))
0574: {
0575: return SAKAI_rsc_draft_import;
0576: }
0577: else if (toolService.equalsIgnoreCase(CalendarService.SERVICE_NAME))
0578: {
0579: return SAKAI_schedule_draft_import;
0580: }
0581: else if (toolService.equalsIgnoreCase(DiscussionService.SERVICE_NAME))
0582: {
0583: return SAKAI_disc_draft_import;
0584: }
0585: else
0586: return false;
0587: }
0588: else
0589: {
0590: // TODO: WT or CT may use different rules
0591: return false;
0592: }
0593:
0594: } //checkToolDraftFlag
0595: */
0596:
0597: /**
0598: * Read in an archive file and merge the entries into the specified site.
0599: * @param fileName The site name (for the archive file) to read from.
0600: * @param siteId The id of the site to merge the content into.
0601: * @param results A buffer to accumulate result messages.
0602: * @param attachmentNames A map of old to new attachment names.
0603: * @param useIdTrans A map of old WorkTools id to new Ctools id
0604: * @param creatorId The creator id
0605: */
0606: protected void processMerge(String fileName, String siteId,
0607: StringBuffer results, Map attachmentNames, String creatorId) {
0608: // correct for windows backslashes
0609: fileName = fileName.replace('\\', '/');
0610:
0611: if (M_log.isDebugEnabled())
0612: M_log.debug("merge(): processing file: " + fileName);
0613:
0614: Site theSite = null;
0615: try {
0616: theSite = SiteService.getSite(siteId);
0617: } catch (IdUnusedException ignore) {
0618: }
0619:
0620: // read the whole file into a DOM
0621: Document doc = Xml.readDocument(fileName);
0622: if (doc == null) {
0623: results
0624: .append("Error reading xml from: " + fileName
0625: + "\n");
0626: return;
0627: }
0628:
0629: // verify the root element
0630: Element root = doc.getDocumentElement();
0631: if (!root.getTagName().equals("archive")) {
0632: results
0633: .append("File: "
0634: + fileName
0635: + " does not contain archive xml. Found this root tag: "
0636: + root.getTagName() + "\n");
0637: return;
0638: }
0639:
0640: // get the from site id
0641: String fromSite = root.getAttribute("source");
0642: String system = root.getAttribute("system");
0643:
0644: // the children
0645: NodeList children = root.getChildNodes();
0646: final int length = children.getLength();
0647: for (int i = 0; i < length; i++) {
0648: Node child = children.item(i);
0649: if (child.getNodeType() != Node.ELEMENT_NODE)
0650: continue;
0651: Element element = (Element) child;
0652:
0653: // look for site stuff
0654: if (element.getTagName().equals(SiteService.APPLICATION_ID)) {
0655: //if the xml file is from WT site, merge it with the translated user ids
0656: if (system.equalsIgnoreCase(FROM_WT))
0657: mergeSite(siteId, fromSite, element, userIdTrans,
0658: creatorId);
0659: else
0660: mergeSite(siteId, fromSite, element,
0661: new HashMap()/*empty userIdMap */,
0662: creatorId);
0663: } else if (element.getTagName().equals(
0664: UserDirectoryService.APPLICATION_ID)) {
0665: try {
0666: // merge the users in only when they are from WorkTools
0667: if (system.equalsIgnoreCase(FROM_WT)) {
0668: String msg = mergeUsers(element, userIdTrans);
0669: results.append(msg);
0670: }
0671: } catch (Exception any) {
0672: }
0673: }
0674:
0675: else {
0676: // we need a site now
0677: if (theSite == null) {
0678: results.append("Site: " + siteId + " not found.\n");
0679: return;
0680: }
0681:
0682: // get the service name
0683: String serviceName = translateServiceName(element
0684: .getTagName());
0685:
0686: // get the service
0687: try {
0688: EntityProducer service = (EntityProducer) ComponentManager
0689: .get(serviceName);
0690:
0691: try {
0692: String msg = "";
0693: // if the xml file is from WT site, merge it with user id translation
0694: if (system.equalsIgnoreCase(FROM_WT))
0695: msg = service.merge(siteId, element,
0696: fileName, fromSite,
0697: attachmentNames, userIdTrans,
0698: new HashSet());
0699: else if (system.equalsIgnoreCase(FROM_SAKAI)
0700: && (checkSakaiService(serviceName)))
0701: msg = service
0702: .merge(
0703: siteId,
0704: element,
0705: fileName,
0706: fromSite,
0707: attachmentNames,
0708: new HashMap() /* empty userIdTran map */,
0709: UsersListAllowImport);
0710: else if (system.equalsIgnoreCase(FROM_CT))
0711: msg = service
0712: .merge(
0713: siteId,
0714: element,
0715: fileName,
0716: fromSite,
0717: attachmentNames,
0718: new HashMap() /* empty userIdTran map */,
0719: UsersListAllowImport);
0720:
0721: results.append(msg);
0722: } catch (Throwable t) {
0723: results.append("Error merging: " + serviceName
0724: + " in file: " + fileName + " : "
0725: + t.toString() + "\n");
0726: }
0727: } catch (Throwable t) {
0728: results
0729: .append("Did not recognize the resource service: "
0730: + serviceName
0731: + " in file: "
0732: + fileName + "\n");
0733: }
0734: }
0735: }
0736:
0737: } // processMerge
0738:
0739: /**
0740: * Check security permission.
0741: * @param lock The lock id string.
0742: * @param reference The resource's reference string, or null if no resource is involved.
0743: * @exception PermissionException thrown if the user does not have access
0744: */
0745: protected void unlock(String lock, String reference)
0746: throws PermissionException {
0747: if (!SecurityService.unlock(lock, reference)) {
0748: // needs to bring back: where is sessionService
0749: // throw new PermissionException(UsageSessionService.getSessionUserId(), lock, reference);
0750: }
0751: } // unlock
0752:
0753: /**
0754: * Merge the site info like description from the site part of the archive file into the site service.
0755: * @param element The XML DOM tree of messages to merge.
0756: * @param siteId The id of the site getting imported into.
0757: */
0758: protected void mergeSiteInfo(Element el, String siteId)
0759: throws IdInvalidException, IdUsedException,
0760: PermissionException, IdUnusedException, InUseException {
0761: // heck security (throws if not permitted)
0762: unlock(SiteService.SECURE_UPDATE_SITE, SiteService
0763: .siteReference(siteId));
0764:
0765: Site edit = SiteService.getSite(siteId);
0766: String desc = el.getAttribute("description-enc");
0767:
0768: try {
0769: byte[] decoded = Base64
0770: .decodeBase64(desc.getBytes("UTF-8"));
0771: byte[] filteredDecoded = decoded;
0772: for (int i = 0; i < decoded.length; i++) {
0773: byte b = decoded[i];
0774: if (b == (byte) -109 || b == (byte) -108) {
0775: // smart quotes, open/close double quote
0776: filteredDecoded[i] = (byte) 34;
0777: } else if (b == (byte) -111 || b == (byte) -110) {
0778: // smart quotes, open/close double quote
0779: filteredDecoded[i] = (byte) 39;
0780: } else if (b == (byte) -106) {
0781: // dash
0782: filteredDecoded[i] = (byte) 45;
0783: }
0784: }
0785: desc = new String(decoded, "UTF-8");
0786: } catch (Exception any) {
0787: M_log.warn("mergeSiteInfo(): exception caught");
0788: }
0789: //edit.setTitle(title);
0790: edit.setDescription(desc);
0791:
0792: SiteService.save(edit);
0793:
0794: return;
0795:
0796: } // mergeSiteInfo
0797:
0798: /**
0799: * Merge the the permission-roles settings into the site
0800: * @param element The XML DOM tree of messages to merge.
0801: * @param siteId The id of the site getting imported into.
0802: */
0803: protected void mergeSiteRoles(Element el, String siteId,
0804: HashMap useIdTrans) throws PermissionException {
0805: // heck security (throws if not permitted)
0806: unlock(SiteService.SECURE_UPDATE_SITE, SiteService
0807: .siteReference(siteId));
0808:
0809: String source = "";
0810:
0811: // el: <roles> node
0812: Node parent0 = el.getParentNode(); // parent0: <site> node
0813: Node parent1 = parent0.getParentNode(); // parent1: <service> node
0814: Node parent = parent1.getParentNode(); // parent: <archive> node containing "system"
0815:
0816: if (parent.getNodeType() == Node.ELEMENT_NODE) {
0817: Element parentEl = (Element) parent;
0818: source = parentEl.getAttribute("system");
0819: }
0820:
0821: List roles = new Vector();
0822: List maintainUsers = new Vector();
0823: List accessUsers = new Vector();
0824:
0825: // to add this user with this role inito this realm
0826: String realmId = "/site/" + siteId;
0827: try {
0828: //AuthzGroup realmEdit = AuthzGroupService.getRealm(realmId);
0829: AuthzGroup realm = AuthzGroupService.getAuthzGroup(realmId);
0830:
0831: //roles.addAll(realmEdit.getRoles());
0832: roles.addAll(realm.getRoles());
0833:
0834: NodeList children = el.getChildNodes();
0835: final int length = children.getLength();
0836: for (int i = 0; i < length; i++) {
0837: Node child = children.item(i);
0838: if (child.getNodeType() != Node.ELEMENT_NODE)
0839: continue;
0840: Element element2 = (Element) child;
0841:
0842: if (source.equalsIgnoreCase(FROM_WT)) {
0843: // from WT, merge the users with roles into the new site
0844:
0845: NodeList children2 = element2.getChildNodes();
0846: final int length2 = children2.getLength();
0847: for (int i2 = 0; i2 < length2; i2++) {
0848: Node child2 = children2.item(i2);
0849: if (child2.getNodeType() != Node.ELEMENT_NODE)
0850: continue;
0851: Element element3 = (Element) child2;
0852: if (!element3.getTagName().equals("ability"))
0853: continue;
0854:
0855: String userId = element3.getAttribute("userId");
0856:
0857: // under 2 conditions the userIdTrans would be empty
0858: // 1st is, even from WT, no user id has been processed
0859: // 2nd is, this user is not from WT. userIdTrans is always empty
0860: if (!userIdTrans.isEmpty()) {
0861: // user the new id if the old one from WT has been replaced
0862: String newId = (String) useIdTrans
0863: .get(userId);
0864: if (newId != null)
0865: userId = newId;
0866: }
0867: try {
0868: User user = UserDirectoryService
0869: .getUser(userId);
0870: String roleId = element3
0871: .getAttribute("roleId");
0872: //Role role = realmEdit.getRole(roleId);
0873: Role role = realm.getRole(roleId);
0874: if (role != null) {
0875: AuthzGroup realmEdit = AuthzGroupService
0876: .getAuthzGroup(realmId);
0877: realmEdit.addMember(user.getId(), role
0878: .getId(), true, false);
0879: AuthzGroupService.save(realmEdit);
0880: }
0881: } catch (UserNotDefinedException e) {
0882: }
0883: }
0884: } else {
0885: // for both CT classic and Sakai CTools
0886:
0887: // check is this roleId is a qualified one
0888: if (!checkSystemRole(source, element2.getTagName()))
0889: continue;
0890:
0891: NodeList children2 = element2.getChildNodes();
0892: final int length2 = children2.getLength();
0893: for (int i2 = 0; i2 < length2; i2++) {
0894: Node child2 = children2.item(i2);
0895: if (child2.getNodeType() != Node.ELEMENT_NODE)
0896: continue;
0897: Element element3 = (Element) child2;
0898: if (!element3.getTagName().equals("ability"))
0899: continue;
0900:
0901: String userId = element3.getAttribute("userId");
0902:
0903: // this user has a qualified role, his/her resource will be imported
0904: UsersListAllowImport.add(userId);
0905: }
0906: } // if - elseif - elseif
0907: } // for
0908: } catch (Exception err) {
0909: M_log.warn("()mergeSiteRoles realm edit exception caught"
0910: + realmId);
0911: }
0912: return;
0913:
0914: } // mergeSiteRoles
0915:
0916: /**
0917: * Merge the user list into the the system.
0918: * Translate the id to the siteId.
0919: * @param element The XML DOM tree of messages to merge.
0920: */
0921:
0922: protected String mergeUsers(Element element, HashMap useIdTrans)
0923: throws IdInvalidException, IdUsedException,
0924: PermissionException {
0925: String msg = "";
0926: int count = 0;
0927:
0928: // The flag showing from WT
0929: boolean fromWT = false;
0930: boolean fromCTclassic = false;
0931: boolean fromCTools = false;
0932: String source = "";
0933:
0934: Node parent = element.getParentNode();
0935: if (parent.getNodeType() == Node.ELEMENT_NODE) {
0936: Element parentEl = (Element) parent;
0937: source = parentEl.getAttribute("system");
0938: }
0939:
0940: if (source != null) {
0941: if (source.equalsIgnoreCase(FROM_CT))
0942: fromCTclassic = true;
0943: else if (source.equalsIgnoreCase(FROM_WT))
0944: fromWT = true;
0945: else
0946: fromCTools = true;
0947: } else
0948: fromCTools = true;
0949:
0950: NodeList children = element.getChildNodes();
0951: final int length = children.getLength();
0952: for (int i = 0; i < length; i++) {
0953: Node child = children.item(i);
0954: if (child.getNodeType() != Node.ELEMENT_NODE)
0955: continue;
0956: Element element2 = (Element) child;
0957: if (!element2.getTagName().equals("user"))
0958: continue;
0959:
0960: //for WorkTools
0961: if (fromWT) {
0962: // Worktools use email address as Id
0963: String wtId = element2.getAttribute("id");
0964: // check if this is an umich address
0965: if (wtId.endsWith("umich.edu")) {
0966: // if this id is a UM unique name
0967: // trim the first part of the email as the id
0968: String userId = wtId
0969: .substring(0, wtId.indexOf("@"));
0970: // change the id to be the first part of wtId
0971: element2.setAttribute("id", userId);
0972:
0973: // add the entry (wtEmail <-> userId) into map
0974: useIdTrans.put(wtId, userId);
0975:
0976: try {
0977: User user = UserDirectoryService
0978: .getUser(userId);
0979: // if this user id exists, do nothing.
0980: } catch (UserNotDefinedException e) {
0981: // this umich id does not exit, merging will create a new user with this id
0982: try {
0983: UserEdit userEdit = UserDirectoryService
0984: .mergeUser(element2);
0985: UserDirectoryService.commitEdit(userEdit);
0986: count++;
0987: // because it is an UM id, report it in the merge log.
0988: msg
0989: .concat("The user id "
0990: + userId
0991: + "("
0992: + wtId
0993: + ") doesn't exist, and was just created. \n");
0994: } catch (UserIdInvalidException error) {
0995: msg
0996: .concat("This user with id -"
0997: + wtId
0998: + ", can't be merged because of the invalid email address.\n");
0999: } catch (UserAlreadyDefinedException error) {
1000: } catch (UserPermissionException error) {
1001: }
1002: }
1003:
1004: } else {
1005: // for non-UM email, process as a friend account id
1006: try {
1007: // test if it is a friend account id
1008: User user = UserDirectoryService.getUser(wtId);
1009: // if the user exists, do nothing.
1010: } catch (UserNotDefinedException e) {
1011: try {
1012: // if this isn't such a friend email address,
1013: // create a new friend account for it
1014: UserEdit userEdit = UserDirectoryService
1015: .mergeUser(element2);
1016: UserDirectoryService.commitEdit(userEdit);
1017: count++;
1018: } catch (UserIdInvalidException error) {
1019: msg
1020: .concat("This user with id -"
1021: + wtId
1022: + ", can't be merged because of the invalid email address.\n");
1023: } catch (UserAlreadyDefinedException error) {
1024: } catch (UserPermissionException error) {
1025: }
1026: }
1027:
1028: }
1029: }
1030: // not allowing merging users form CT classic or CTools.
1031: /*
1032: else if (fromCTclassic)
1033: {
1034: String ctId = element2.getAttribute("id");
1035:
1036: try
1037: {
1038: User user = UserDirectoryService.getUser(ctId);
1039: }
1040: catch(IdUnusedException e)
1041: {
1042: // if this umich id does not exit, report it in importing log.
1043: msg.concat("The user id " + ctId + " doesn't exist, and was just created. \n");
1044: }
1045:
1046: try
1047: {
1048: // override the old one or create a new user with this id
1049: UserEdit userEdit = UserDirectoryService.mergeUser(element2);
1050: UserDirectoryService.commitEdit(userEdit);
1051: count++;
1052: }
1053: catch(IdInvalidException error)
1054: {
1055: msg.concat("This user with id -" + ctId + ", can't be merged because of the invalid email address.\n");
1056: }
1057: catch(IdUsedException error)
1058: {
1059: }
1060: catch(PermissionException error)
1061: {
1062: }
1063:
1064: }
1065: else if (fromCTools)
1066: {
1067: try
1068: {
1069: UserEdit userEdit = UserDirectoryService.mergeUser(element2);
1070: UserDirectoryService.commitEdit(userEdit);
1071: count++;
1072: }
1073: catch(IdInvalidException error)
1074: {
1075: msg.concat("This user with id -" + element2.getAttribute("id") + ", can't be merged because of the invalid email address.\n");
1076: }
1077: catch(IdUsedException error)
1078: {
1079: }
1080: catch(PermissionException error)
1081: {
1082: }
1083: }
1084: */
1085: }
1086:
1087: msg = msg + "merging user" + "(" + count + ") users\n";
1088: return msg;
1089:
1090: } // mergeUsers
1091:
1092: /**
1093: * Merge the site definition from the site part of the archive file into the site service.
1094: * Translate the id to the siteId.
1095: * @param siteId The id of the site getting imported into.
1096: * @param fromSiteId The id of the site the archive was made from.
1097: * @param element The XML DOM tree of messages to merge.
1098: * @param creatorId The creator id
1099: */
1100:
1101: protected void mergeSite(String siteId, String fromSiteId,
1102: Element element, HashMap useIdTrans, String creatorId) {
1103: String source = "";
1104:
1105: Node parent = element.getParentNode();
1106: if (parent.getNodeType() == Node.ELEMENT_NODE) {
1107: Element parentEl = (Element) parent;
1108: source = parentEl.getAttribute("system");
1109: }
1110:
1111: NodeList children = element.getChildNodes();
1112: final int length = children.getLength();
1113: for (int i = 0; i < length; i++) {
1114: Node child = children.item(i);
1115: if (child.getNodeType() != Node.ELEMENT_NODE)
1116: continue;
1117: Element element2 = (Element) child;
1118: if (!element2.getTagName().equals("site"))
1119: continue;
1120:
1121: NodeList toolChildren = element2
1122: .getElementsByTagName("tool");
1123: final int tLength = toolChildren.getLength();
1124: for (int i2 = 0; i2 < tLength; i2++) {
1125: Element element3 = (Element) toolChildren.item(i2);
1126: String toolId = element3.getAttribute("toolId");
1127: if (toolId != null) {
1128: toolId = toolId.replaceAll(old_toolId_prefix,
1129: new_toolId_prefix);
1130: for (int j = 0; j < old_toolIds.length; j++) {
1131: toolId = toolId.replaceAll(old_toolIds[i],
1132: new_toolIds[i]);
1133: }
1134: }
1135: element3.setAttribute("toolId", toolId);
1136: }
1137:
1138: // merge the site info first
1139: try {
1140: SiteService.merge(siteId, element2, creatorId);
1141: mergeSiteInfo(element2, siteId);
1142: } catch (Exception any) {
1143: }
1144:
1145: Site site = null;
1146: try {
1147: site = SiteService.getSite(siteId);
1148: } catch (IdUnusedException e) {
1149: M_log.warn(this + "The site with id " + siteId
1150: + " doesn't exit");
1151: return;
1152: }
1153:
1154: if (site != null) {
1155: NodeList children2 = element2.getChildNodes();
1156: final int length2 = children2.getLength();
1157: for (int i2 = 0; i2 < length2; i2++) {
1158: Node child2 = children2.item(i2);
1159: if (child2.getNodeType() != Node.ELEMENT_NODE)
1160: continue;
1161: Element element3 = (Element) child2;
1162: if (!element3.getTagName().equals("roles"))
1163: continue;
1164:
1165: // only merge roles when from Worktools
1166: //if (source.equalsIgnoreCase(FROM_WT))
1167: //{
1168: // merge the permission-roles
1169:
1170: // mergeSiteRoles will merge users from WT into the new site
1171: // it also creates a set for the users from CT or CTools having a certain role
1172: try {
1173: mergeSiteRoles(element3, siteId, useIdTrans);
1174: } catch (PermissionException e1) {
1175: //}
1176: }
1177: }
1178: }
1179: }
1180: } // mergeSite
1181:
1182: /**
1183: * Merge the content attachment resources from the attachments part of the
1184: * archive file into the content hosting service.
1185: * Map the attachment folder to something new based on the mergeId.
1186: * @param mergeId The value pre-pended to the numeric attachment folder id from the archive.
1187: * to make it unique here.
1188: * @param root The XML DOM tree of content to merge.
1189: */
1190: /*
1191: protected void mergeAttachments(String mergeId, Element root)
1192: {
1193: try
1194: {
1195: ContentHostingService service = (ContentHostingService)TurbineServices.getInstance()
1196: .getService(ContentHostingService.SERVICE_NAME);
1197:
1198: NodeList children = root.getChildNodes();
1199: final int length = children.getLength();
1200: for(int i = 0; i < length; i++)
1201: {
1202: Node child = children.item(i);
1203: if (child.getNodeType() == Node.ELEMENT_NODE)
1204: {
1205: Element element = (Element)child;
1206:
1207: // for "resource" kids
1208: if (element.getTagName().equals("resource"))
1209: {
1210: // map the attachment area folder name
1211: String oldId = element.getAttribute("id");
1212: if (oldId.startsWith("/attachment/"))
1213: {
1214: String newId = "/attachment/"
1215: + mergeId + "-"
1216: + oldId.substring(12);
1217: element.setAttribute("id", newId);
1218: }
1219:
1220: // resource: add if missing
1221: service.mergeResource(element);
1222: }
1223: }
1224: }
1225: }
1226: catch (Exception any)
1227: {
1228: M_log.warn("mergeAttachments(): exception: ", any);
1229: }
1230: } // mergeAttachments
1231: */
1232:
1233: /**
1234: * Old archives have the old CHEF 1.2 service names...
1235: */
1236: protected String translateServiceName(String name) {
1237: if ("org.chefproject.service.GenericContentHostingService"
1238: .equals(name)) {
1239: return ContentHostingService.class.getName();
1240: }
1241:
1242: return name;
1243: }
1244:
1245: } // BasicArchiveService
|