package org.exoplatform.wiki.utils; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Properties; import java.util.Stack; import javax.jcr.Node; import javax.jcr.PathNotFoundException; import javax.jcr.RepositoryException; import javax.jcr.ValueFormatException; import javax.mail.internet.AddressException; import javax.mail.internet.InternetAddress; import org.exoplatform.container.ExoContainer; import org.exoplatform.container.ExoContainerContext; import org.exoplatform.container.PortalContainer; import org.exoplatform.ks.common.Common; import org.exoplatform.ks.common.UserHelper; import org.exoplatform.portal.config.UserACL; import org.exoplatform.portal.config.model.PortalConfig; import org.exoplatform.services.jcr.RepositoryService; import org.exoplatform.services.jcr.access.AccessControlEntry; import org.exoplatform.services.jcr.access.AccessControlList; import org.exoplatform.services.jcr.access.PermissionType; import org.exoplatform.services.jcr.core.ExtendedNode; import org.exoplatform.services.log.ExoLogger; import org.exoplatform.services.log.Log; import org.exoplatform.services.mail.Message; import org.exoplatform.services.scheduler.JobSchedulerService; import org.exoplatform.services.security.ConversationState; import org.exoplatform.services.security.Identity; import org.exoplatform.services.security.IdentityConstants; import org.exoplatform.wiki.chromattic.ext.ntdef.NTVersion; import org.exoplatform.wiki.mow.api.Page; import org.exoplatform.wiki.mow.api.Wiki; import org.exoplatform.wiki.mow.api.WikiNodeType; import org.exoplatform.wiki.mow.api.WikiNodeType.Definition; import org.exoplatform.wiki.mow.api.WikiType; import org.exoplatform.wiki.mow.core.api.MOWService; import org.exoplatform.wiki.mow.core.api.ModelImpl; import org.exoplatform.wiki.mow.core.api.WikiStoreImpl; import org.exoplatform.wiki.mow.core.api.wiki.AttachmentImpl; import org.exoplatform.wiki.mow.core.api.wiki.PageImpl; import org.exoplatform.wiki.mow.core.api.wiki.SimplePageImpl; import org.exoplatform.wiki.mow.core.api.wiki.WikiContainer; import org.exoplatform.wiki.mow.core.api.wiki.WikiHome; import org.exoplatform.wiki.rendering.RenderingService; import org.exoplatform.wiki.service.WikiContext; import org.exoplatform.wiki.service.WikiPageParams; import org.exoplatform.wiki.service.WikiService; import org.exoplatform.wiki.service.diff.DiffResult; import org.exoplatform.wiki.service.diff.DiffService; import org.xwiki.rendering.syntax.Syntax; public class Utils { public static final String SLASH = "SLASH"; public static final String DOT = "DOT"; private static final Log log_ = ExoLogger.getLogger(Utils.class); private static final String JCR_WEBDAV_SERVICE_BASE_URI = "/jcr"; final private static String MIMETYPE_TEXTHTML = "text/html"; //The path should get from NodeHierarchyCreator public static String getPortalWikisPath() { String path = "/exo:applications/" + WikiNodeType.Definition.WIKI_APPLICATION + "/" + WikiNodeType.Definition.WIKIS ; return path ; } /** * @return * <li> portal name if wiki is portal type</li> * <li> groupid if wiki is group type</li> * <li> userid if wiki is personal type</li> * @throws IllegalArgumentException if jcr path is not of a wiki page node. */ public static String getSpaceIdByJcrPath(String jcrPath) throws IllegalArgumentException { String wikiType = getWikiType(jcrPath); if (PortalConfig.PORTAL_TYPE.equals(wikiType)) { return getPortalIdByJcrPath(jcrPath); } else if (PortalConfig.GROUP_TYPE.equals(wikiType)) { return getGroupIdByJcrPath(jcrPath); } else if (PortalConfig.USER_TYPE.equals(wikiType)) { return getUserIdByJcrPath(jcrPath); } else { throw new IllegalArgumentException(jcrPath + " is not jcr path of a wiki page node!"); } } /** * @param jcrPath follows the format /Groups/$GROUP/ApplicationData/eXoWiki/[wikipage] * @return $GROUP of jcrPath * @throws IllegalArgumentException if jcrPath is not as expected. */ public static String getGroupIdByJcrPath(String jcrPath) throws IllegalArgumentException { int pos1 = jcrPath.indexOf("/Groups/"); int pos2 = jcrPath.indexOf("/ApplicationData"); if (pos1 >= 0 && pos2 > 0) { return jcrPath.substring(pos1 + "/Groups/".length(), pos2); } else { throw new IllegalArgumentException(jcrPath + " is not jcr path of a group wiki page node!"); } } /** * @param jcrPath follows the format /Users/$USERNAME/ApplicationData/eXoWiki/... * @return $USERNAME of jcrPath * @throws IllegalArgumentException if jcrPath is not as expected. */ public static String getUserIdByJcrPath(String jcrPath) throws IllegalArgumentException { int pos1 = jcrPath.indexOf("/Users/"); int pos2 = jcrPath.indexOf("/ApplicationData"); if (pos1 >= 0 && pos2 > 0) { return jcrPath.substring(pos1 + "/Users/".length(), pos2); } else { throw new IllegalArgumentException(jcrPath + " is not jcr path of a personal wiki page node!"); } } /** * @param jcrPath follows the format /exo:applications/eXoWiki/wikis/$PORTAL/... * @return $PORTAL of jcrPath * @throws IllegalArgumentException if jcrPath is not as expected. */ public static String getPortalIdByJcrPath(String jcrPath) throws IllegalArgumentException { String portalPath = getPortalWikisPath(); int pos1 = jcrPath.indexOf(portalPath); if (pos1 >= 0) { String restPath = jcrPath.substring(pos1 + portalPath.length() + 1); return restPath.substring(0, restPath.indexOf("/")); } else { throw new IllegalArgumentException(jcrPath + " is not jcr path of a portal wiki page node!"); } } /** * @param jcrPath absolute jcr path of page node. * @return type of wiki page. */ public static String getWikiType(String jcrPath) throws IllegalArgumentException { if (jcrPath.startsWith("/exo:applications/")) { return PortalConfig.PORTAL_TYPE; } else if (jcrPath.startsWith("/Groups/")) { return PortalConfig.GROUP_TYPE; } else if (jcrPath.startsWith("/Users/")) { return PortalConfig.USER_TYPE; } else { throw new IllegalArgumentException(jcrPath + " is not jcr path of a wiki page node!"); } } /** * Validate {@code wikiOwner} depending on {@code wikiType}. <br> * If wikiType is {@link PortalConfig#GROUP_TYPE}, {@code wikiOwner} is checked to removed slashes at the begin and the end point of it. * @param wikiType * @param wikiOwner * @return wikiOwner after validated. */ public static String validateWikiOwner(String wikiType, String wikiOwner){ if(wikiType != null && wikiType.equals(PortalConfig.GROUP_TYPE)) { if(wikiOwner == null || wikiOwner.length() == 0){ return ""; } if(wikiOwner.startsWith("/")){ wikiOwner = wikiOwner.substring(1,wikiOwner.length()); } if(wikiOwner.endsWith("/")){ wikiOwner = wikiOwner.substring(0,wikiOwner.length()-1); } } return wikiOwner; } public static String getDefaultRestBaseURI() { StringBuilder sb = new StringBuilder(); sb.append("/"); sb.append(PortalContainer.getCurrentPortalContainerName()); sb.append("/"); sb.append(PortalContainer.getCurrentRestContextName()); return sb.toString(); } public static String getDefaultRepositoryWebDavUri() { StringBuilder sb = new StringBuilder(); sb.append(getDefaultRestBaseURI()); sb.append(JCR_WEBDAV_SERVICE_BASE_URI); sb.append("/"); RepositoryService repositoryService = (RepositoryService) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(RepositoryService.class); sb.append(repositoryService.getConfig().getDefaultRepositoryName()); sb.append("/"); return sb.toString(); } public static void reparePermissions(AttachmentImpl att) throws Exception { MOWService mowService = (MOWService) PortalContainer.getComponent(MOWService.class); WikiStoreImpl store = (WikiStoreImpl) mowService.getModel().getWikiStore(); Node attNode = (Node) store.getSession().getJCRSession().getItem(att.getPath()); ExtendedNode extNode = (ExtendedNode) attNode; if (extNode.canAddMixin("exo:privilegeable")) extNode.addMixin("exo:privilegeable"); String[] arrayPers = { PermissionType.READ }; extNode.setPermission("any", arrayPers); attNode.getSession().save(); } public static String getDocumentURL(WikiContext wikiContext) { StringBuilder sb = new StringBuilder(); sb.append(wikiContext.getPortalURL()); sb.append(wikiContext.getPortletURI()); sb.append("/"); if (!PortalConfig.PORTAL_TYPE.equalsIgnoreCase(wikiContext.getType())) { sb.append(wikiContext.getType().toLowerCase()); sb.append("/"); sb.append(Utils.validateWikiOwner(wikiContext.getType(), wikiContext.getOwner())); sb.append("/"); } sb.append(wikiContext.getPageId()); return sb.toString(); } public static String getCurrentUser() { try { ConversationState conversationState = ConversationState.getCurrent(); return conversationState.getIdentity().getUserId(); }catch(Exception e){} return "system" ; } public static Collection<Wiki> getWikisByType(WikiType wikiType) { MOWService mowService = (MOWService) PortalContainer.getComponent(MOWService.class); WikiStoreImpl store = (WikiStoreImpl) mowService.getModel().getWikiStore(); return store.getWikiContainer(wikiType).getAllWikis(); } public static Wiki getWiki(WikiPageParams params) { Collection<Wiki> wikis = getWikisByType(WikiType.valueOf(params.getType().toUpperCase())); for (Wiki wiki : wikis) { if (wiki.getOwner().equals(params.getOwner())) { return wiki; } } return null; } public static Wiki[] getAllWikiSpace() { MOWService mowService = (MOWService) PortalContainer.getComponent(MOWService.class); WikiStoreImpl store = (WikiStoreImpl) mowService.getModel().getWikiStore(); return store.getWikis().toArray(new Wiki[]{}) ; } public static boolean isDescendantPage(PageImpl page, PageImpl parentPage) throws Exception { Iterator<PageImpl> iter = parentPage.getChildPages().values().iterator(); while (iter.hasNext()) { PageImpl childpage = (PageImpl) iter.next(); if (childpage.equals(page)) return true; if (isDescendantPage(page, childpage)) return true; } return false; } public static Object getObject(String path, String type) throws Exception { WikiService wservice = (WikiService)ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(WikiService.class); return wservice.findByPath(path, type) ; } public static Object getObjectFromParams(WikiPageParams param) throws Exception { WikiService wikiService = (WikiService) ExoContainerContext.getCurrentContainer() .getComponentInstanceOfType(WikiService.class); MOWService mowService = (MOWService) ExoContainerContext.getCurrentContainer() .getComponentInstanceOfType(MOWService.class); WikiStoreImpl store = (WikiStoreImpl) mowService.getModel().getWikiStore(); String wikiType = param.getType(); String wikiOwner = param.getOwner(); String wikiPageId = param.getPageId(); if (wikiOwner != null && wikiPageId != null) { if (!wikiPageId.equals(WikiNodeType.Definition.WIKI_HOME_NAME)) { // Object is a page Page expandPage = (Page) wikiService.getPageById(wikiType, wikiOwner, wikiPageId); return expandPage; } else { // Object is a wiki home page Wiki wiki = store.getWikiContainer(WikiType.valueOf(wikiType.toUpperCase())) .getWiki(wikiOwner); WikiHome wikiHome = (WikiHome) wiki.getWikiHome(); return wikiHome; } } else if (wikiOwner != null) { // Object is a wiki Wiki wiki = store.getWikiContainer(WikiType.valueOf(wikiType.toUpperCase())) .getWiki(wikiOwner); return wiki; } else if (wikiType != null) { // Object is a space return wikiType; } else { return null; } } public static Stack<WikiPageParams> getStackParams(PageImpl page) throws Exception { Stack<WikiPageParams> stack = new Stack<WikiPageParams>(); Wiki wiki = page.getWiki(); if (wiki != null) { while (page != null) { stack.push(new WikiPageParams(wiki.getType(), wiki.getOwner(), page.getName())); page = page.getParentPage(); } } return stack; } public static WikiPageParams getWikiPageParams(Page page) { Wiki wiki = ((PageImpl) page).getWiki(); String wikiType = wiki.getType(); WikiPageParams params = new WikiPageParams(wikiType, wiki.getOwner(), page.getName()); return params; } public static void sendMailOnChangeContent(AttachmentImpl content) throws Exception { ExoContainer container = ExoContainerContext.getCurrentContainer(); DiffService diffService = (DiffService) container.getComponentInstanceOfType(DiffService.class); RenderingService renderingService = (RenderingService) container.getComponentInstanceOfType(RenderingService.class); Common common = new Common(); Message message = new Message(); ConversationState conversationState = ConversationState.getCurrent(); // Get author String author = conversationState.getIdentity().getUserId(); // Get watchers' mails PageImpl page = content.getParentPage(); List<String> list = page.getWatchedMixin().getWatchers(); List<String> emailList = new ArrayList<String>(); for (int i = 0; i < list.size(); i++) { emailList.add(UserHelper.getEmailUser(list.get(i))); } // Get differences String pageTitle = page.getTitle(); String currentVersionContent = content.getText(); NTVersion previousVersion = page.getVersionableMixin().getBaseVersion(); String previousVersionContent = ((AttachmentImpl) previousVersion.getNTFrozenNode() .getChildren() .get(WikiNodeType.Definition.CONTENT)).getText(); DiffResult diffResult = diffService.getDifferencesAsHTML(previousVersionContent, currentVersionContent, false); String fullContent = renderingService.render(currentVersionContent, page.getSyntax(), Syntax.XHTML_1_0.toIdString(), false); if (diffResult.getChanges() == 0) { diffResult.setDiffHTML("No changes, new revision is created."); } StringBuilder sbt = new StringBuilder(); sbt.append("<html>") .append(" <head>") .append(" <link rel=\"stylesheet\" href=\""+renderingService.getCssURL() +"\" type=\"text/css\">") .append(" </head>") .append(" <body>") .append(" Page <a href=\""+page.getURL()+"\">" + page.getTitle() +"</a> is modified by " +page.getAuthor()) .append(" <br/><br/>") .append(" Changes("+ diffResult.getChanges()+")") .append(" <br/><br/>") .append( insertStyle(diffResult.getDiffHTML())) .append(" Full content: ") .append(" <br/><br/>") .append( fullContent) .append(" </body>") .append("</html>"); // Create message message.setFrom(makeNotificationSender(author)); message.setSubject("\"" + pageTitle + "\" page was modified"); message.setMimeType(MIMETYPE_TEXTHTML); message.setBody(sbt.toString()); try { JobSchedulerService schedulerService = (JobSchedulerService) container.getComponentInstanceOfType(JobSchedulerService.class); if (schedulerService != null) common.sendEmailNotification(emailList, message, "KnowledgeSuite"); } catch (Exception e) { log_.debug("Failed to run job for send email notification", e); } } public static boolean isWikiAvailable(String wikiType, String wikiOwner) { MOWService mowService = (MOWService) ExoContainerContext.getCurrentContainer() .getComponentInstanceOfType(MOWService.class); ModelImpl model = mowService.getModel(); WikiStoreImpl wStore = (WikiStoreImpl) model.getWikiStore(); WikiContainer<Wiki> container = wStore.getWikiContainer(WikiType.valueOf(wikiType.toUpperCase())); return (container.contains(wikiOwner) != null); } public static HashMap<String, String[]> getACLForAdmins() { HashMap<String, String[]> permissionMap = new HashMap<String, String[]>(); UserACL userACL = (UserACL) ExoContainerContext.getCurrentContainer().getComponentInstanceOfType(UserACL.class); permissionMap.put(userACL.getSuperUser(), org.exoplatform.services.jcr.access.PermissionType.ALL); permissionMap.put(userACL.getAdminGroups(), org.exoplatform.services.jcr.access.PermissionType.ALL); for (String group : userACL.getPortalCreatorGroups()) { permissionMap.put(group, org.exoplatform.services.jcr.access.PermissionType.ALL); } return permissionMap; } /** * Has permission. * * @param acl * access control list * @param permission * permissions array * @param user * user Identity * @return boolean */ public static boolean hasPermission(AccessControlList acl, String[] permission, Identity user) { String userId = user.getUserId(); if (userId.equals(IdentityConstants.SYSTEM)) { // SYSTEM has permission everywhere return true; } else if (userId.equals(acl.getOwner())) { // Current user is owner of node so has all privileges return true; } else if (userId.equals(IdentityConstants.ANONIM)) { List<String> anyPermissions = acl.getPermissions(IdentityConstants.ANY); if (anyPermissions.size() < permission.length) return false; for (int i = 0; i < permission.length; i++) { if (!anyPermissions.contains(permission[i])) return false; } return true; } else { if (acl.getPermissionsSize() > 0 && permission.length > 0) { // check permission to perform all of the listed actions for (int i = 0; i < permission.length; i++) { // check specific actions if (!isPermissionMatch(acl.getPermissionEntries(), permission[i], user)) return false; } return true; } return false; } } private static boolean isPermissionMatch(List<AccessControlEntry> existedPermission, String testPermission, Identity user) { for (int i = 0, length = existedPermission.size(); i < length; i++) { AccessControlEntry ace = existedPermission.get(i); // match action if (testPermission.equals(ace.getPermission())) { // match any if (IdentityConstants.ANY.equals(ace.getIdentity())) return true; else if (ace.getIdentity().indexOf(":") == -1) { // just user if (ace.getIdentity().equals(user.getUserId())) return true; } else if (user.isMemberOf(ace.getMembershipEntry())) return true; } } return false; } private static String makeNotificationSender(String from) { InternetAddress addr = null; if (from == null) return null; try { addr = new InternetAddress(from); } catch (AddressException e) { if (log_.isDebugEnabled()) { log_.debug("value of 'from' field in message made by forum notification feature is not in format of mail address", e); } return null; } Properties props = new Properties(System.getProperties()); String mailAddr = props.getProperty("gatein.email.smtp.from"); if (mailAddr == null || mailAddr.length() == 0) mailAddr = props.getProperty("mail.from"); if (mailAddr != null) { try { InternetAddress serMailAddr = new InternetAddress(mailAddr); addr.setAddress(serMailAddr.getAddress()); return addr.toUnicodeString(); } catch (AddressException e) { if (log_.isDebugEnabled()) { log_.debug("value of 'gatein.email.smtp.from' or 'mail.from' in configuration file is not in format of mail address", e); } return null; } } else { return null; } } private static String insertStyle(String rawHTML) { String result = rawHTML; result = result.replaceAll("class=\"diffaddword\"", "style=\"background: #b5ffbf;\""); result = result.replaceAll("<span class=\"diffremoveword\">", "<span style=\" background: #ffd8da;text-decoration: line-through;\">"); result = result.replaceAll("<pre class=\"diffremoveword\">", "<pre style=\" background: #ffd8da;\">"); return result; } public static Page makeSimplePage(Node pageNode) throws ValueFormatException, PathNotFoundException, RepositoryException { String name = pageNode.getProperty("exo:name").getString(); String title = name; if (pageNode.hasProperty(Definition.TITLE)) { title = pageNode.getProperty(Definition.TITLE).getString(); } String owner = pageNode.getProperty(Definition.OWNER).getString(); SimplePageImpl page = new SimplePageImpl(name, title, owner); if (pageNode.hasProperty(Definition.CREATED_DATE)) page = page.createDate(pageNode.getProperty(Definition.CREATED_DATE).getDate().getTime()); if (pageNode.hasProperty(Definition.UPDATED_DATE)) page = page.updateDate(pageNode.getProperty(Definition.UPDATED_DATE).getDate().getTime()); if (pageNode.hasProperty(Definition.AUTHOR)) page = page.author(pageNode.getProperty(Definition.AUTHOR).getString()); if (pageNode.hasProperty(Definition.SYNTAX)) page = page.syntax(pageNode.getProperty(Definition.SYNTAX).getString()); if (pageNode.hasProperty(Definition.COMMENT)) page = page.comment(pageNode.getProperty(Definition.COMMENT).getString()); if (pageNode.hasProperty(Definition.URL)) page = page.url(pageNode.getProperty(Definition.URL).getString()); return page; } }