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;
}
}