View Javadoc
1   /*
2    * Copyright (C) 2003-2008 eXo Platform SAS.
3    *
4    * This program is free software; you can redistribute it and/or
5    * modify it under the terms of the GNU Affero General Public License
6    * as published by the Free Software Foundation; either version 3
7    * of the License, or (at your option) any later version.
8    *
9    * This program is distributed in the hope that it will be useful,
10   * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   * GNU General Public License for more details.
13   *
14   * You should have received a copy of the GNU General Public License
15   * along with this program; if not, see<http://www.gnu.org/licenses/>.
16   */
17  package org.exoplatform.services.wcm.extensions.publication;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.TreeSet;
23  
24  import javax.jcr.Node;
25  import javax.jcr.lock.Lock;
26  
27  import org.apache.commons.lang.StringUtils;
28  import org.exoplatform.container.xml.InitParams;
29  import org.exoplatform.ecm.utils.lock.LockUtil;
30  import org.exoplatform.ecm.webui.utils.Utils;
31  import org.exoplatform.services.ecm.publication.PublicationPlugin;
32  import org.exoplatform.services.ecm.publication.PublicationService;
33  import org.exoplatform.services.jcr.core.ManageableRepository;
34  import org.exoplatform.services.log.ExoLogger;
35  import org.exoplatform.services.log.Log;
36  import org.exoplatform.services.security.Identity;
37  import org.exoplatform.services.security.IdentityRegistry;
38  import org.exoplatform.services.wcm.core.NodeLocation;
39  import org.exoplatform.services.wcm.core.NodetypeConstant;
40  import org.exoplatform.services.wcm.extensions.publication.context.impl.ContextConfig.Context;
41  import org.exoplatform.services.wcm.extensions.publication.impl.PublicationManagerImpl;
42  import org.exoplatform.services.wcm.extensions.publication.lifecycle.authoring.AuthoringPublicationConstant;
43  import org.exoplatform.services.wcm.extensions.publication.lifecycle.impl.LifecyclesConfig.Lifecycle;
44  import org.exoplatform.services.wcm.extensions.publication.lifecycle.impl.LifecyclesConfig.State;
45  import org.exoplatform.services.wcm.extensions.utils.ContextComparator;
46  import org.exoplatform.services.wcm.publication.WebpagePublicationPlugin;
47  import org.exoplatform.services.wcm.utils.WCMCoreUtils;
48  
49  public class WCMPublicationServiceImpl
50                                        extends
51                                        org.exoplatform.services.wcm.publication.WCMPublicationServiceImpl {
52  
53    private static final Log LOG = ExoLogger.getLogger(WCMPublicationServiceImpl.class.getName());
54    
55    private String publicationLocation = "collaboration:/";
56    
57    private String[] notAllowChildNodeEnrollInPubliction = new String[] { NodetypeConstant.EXO_WEBCONTENT };
58  
59    /**
60     * Instantiates a new WCM publication service. This service delegate to
61     * PublicationService to manage the publication
62     */
63    public WCMPublicationServiceImpl(InitParams initParams) {
64      super();
65      this.publicationService = WCMCoreUtils.getService(PublicationService.class);
66      if(initParams.getValueParam("publicationLocation") != null) {
67        publicationLocation = initParams.getValueParam("publicationLocation").getValue();
68      }
69      if(initParams.getValueParam("notAllowChildNodeEnrollInPubliction") != null) {
70        if(initParams.getValueParam("notAllowChildNodeEnrollInPubliction").getValue().indexOf(";") > -1) {
71          notAllowChildNodeEnrollInPubliction = 
72                  initParams.getValueParam("notAllowChildNodeEnrollInPubliction").getValue().split(";");
73        }
74        
75      }
76    }
77  
78    /**
79     * This default implementation uses "States and versions based publication" as
80     * a default lifecycle for all sites and "Simple Publishing" for the root
81     * user.
82     */
83    public void enrollNodeInLifecycle(Node node, String siteName, String remoteUser) {
84      try {
85        if (LOG.isInfoEnabled()) LOG.info(node.getPath() + "::" + siteName + "::"+remoteUser);
86  
87        PublicationManagerImpl publicationManagerImpl = WCMCoreUtils.getService(PublicationManagerImpl.class);
88  
89        ContextComparator comparator = new ContextComparator();
90        TreeSet<Context> treeSetContext = new TreeSet<Context>(comparator);
91        treeSetContext.addAll(publicationManagerImpl.getContexts());
92        for (Context context : treeSetContext) {
93          boolean pathVerified = true;
94          boolean nodetypeVerified = true;
95          boolean siteVerified = true;
96          boolean membershipVerified = true;
97          String path = context.getPath();
98          String nodetype = context.getNodetype();
99          String site = context.getSite();
100         List<String> memberships = new ArrayList<String>();
101         if (context.getMembership() != null) {
102           memberships.add(context.getMembership());
103         }
104         if (context.getMemberships() != null) {
105           memberships.addAll(context.getMemberships());
106         }
107         if (path != null) {
108           String workspace = node.getSession().getWorkspace().getName();
109           ManageableRepository manaRepository = (ManageableRepository) node.getSession()
110                                                                            .getRepository();
111           String repository = manaRepository.getConfiguration().getName();
112           String[] pathTab = path.split(":");
113           pathVerified = node.getPath().contains(pathTab[2]) && (repository.equals(pathTab[0]))
114               && (workspace.equals(pathTab[1]));
115         }
116         if (nodetype != null)
117           nodetypeVerified = nodetype.equals(node.getPrimaryNodeType().getName());
118         if (site != null)
119           siteVerified = site.equals(siteName);
120         if (memberships.size() > 0) {
121           for (String membership : memberships) {
122             String[] membershipTab = membership.split(":");
123             IdentityRegistry identityRegistry = WCMCoreUtils.getService(IdentityRegistry.class);
124             Identity identity = identityRegistry.getIdentity(remoteUser);
125             membershipVerified = identity.isMemberOf(membershipTab[1], membershipTab[0]);
126             if (membershipVerified)
127               break;
128           }
129         }
130         if (pathVerified && nodetypeVerified && siteVerified && membershipVerified) {
131           Lifecycle lifecycle = publicationManagerImpl.getLifecycle(context.getLifecycle());
132           String lifecycleName = this.getWebpagePublicationPlugins()
133                                      .get(lifecycle.getPublicationPlugin())
134                                      .getLifecycleName();
135           if (node.canAddMixin("publication:authoring")) {
136             node.addMixin("publication:authoring");
137             node.setProperty("publication:lastUser", remoteUser);
138             node.setProperty("publication:lifecycle", lifecycle.getName());
139 
140           }
141           enrollNodeInLifecycle(node, lifecycleName);
142           setInitialState(node, lifecycle, remoteUser);
143           break;
144         }
145       }
146     } catch (Exception ex) {
147       if (LOG.isErrorEnabled()) {
148         LOG.error("Couldn't complete the enrollement : ", ex);
149       }
150     }
151   }
152 
153   /**
154    * Automatically move to initial state if 'automatic'
155    *
156    * @param node
157    * @param lifecycle
158    * @throws Exception
159    */
160   private void setInitialState(Node node, Lifecycle lifecycle, String remoteUser) throws Exception {
161     List<State> states = lifecycle.getStates();
162     if (states == null || states.size() <= 0) {
163       if (LOG.isWarnEnabled()) {
164         LOG.warn("could not find an initial state in lifecycle " + lifecycle.getName());
165       }
166     } else {
167       String initialState = states.get(0).getState();
168       PublicationPlugin publicationPlugin = publicationService.getPublicationPlugins()
169                                                               .get(AuthoringPublicationConstant.LIFECYCLE_NAME);
170       HashMap<String, String> context = new HashMap<String, String>();
171 
172       NodeLocation currentRevisionLocation = NodeLocation.getNodeLocationByNode(node);
173 
174       Node currentRevision = getCurrentRevision(currentRevisionLocation);
175       if (currentRevision != null) {
176         context.put(AuthoringPublicationConstant.CURRENT_REVISION_NAME, currentRevision.getName());
177       }
178       try {
179         if (node.isLocked()) {
180           Lock lock = node.getLock();
181           String owner = lock.getLockOwner();
182           if (LOG.isInfoEnabled())
183             LOG.info("node is locked by owner, unlocking it for enrollement");
184           if (node.holdsLock() && remoteUser.equals(owner)) {
185             String lockToken = LockUtil.getLockToken(node);
186             if (lockToken != null) {
187               node.getSession().addLockToken(lockToken);
188             }
189             node.unlock();
190             node.removeMixin(Utils.MIX_LOCKABLE);
191             // remove lock from Cache
192             LockUtil.removeLock(node);
193           }
194         }
195 
196         context.put(AuthoringPublicationConstant.IS_INITIAL_PHASE, "true");
197         node.setProperty("publication:lastUser", remoteUser);
198         publicationPlugin.changeState(node, initialState, context);         
199       } catch (Exception e) {
200         if (LOG.isErrorEnabled()) {
201           LOG.error("Error setting staged state : ", e);
202         }
203       }
204     }
205 
206   }
207 
208   public Node getCurrentRevision(NodeLocation currentRevisionLocation) {
209     return NodeLocation.getNodeByLocation(currentRevisionLocation);
210   }
211 
212   /**
213    * This default implementation checks if the state is valid then delegates the
214    * update to the node WebpagePublicationPlugin.
215    */
216   public void updateLifecyleOnChangeContent(Node node,
217                                             String siteName,
218                                             String remoteUser,
219                                             String newState) throws Exception {
220     if(!node.getPath().startsWith(publicationLocation.split(":")[1]) 
221 			&& !publicationService.isNodeEnrolledInLifecycle(node)) return;
222     if(node.getPrimaryNodeType().getName().equals(NodetypeConstant.NT_FILE)) {
223       for(String nodeType : notAllowChildNodeEnrollInPubliction) {
224         if(!allowEnrollInPublication(node, nodeType)) return;
225       }
226     }
227     if (!publicationService.isNodeEnrolledInLifecycle(node)) {
228       enrollNodeInLifecycle(node, siteName, remoteUser);
229     }
230     String lifecycleName = publicationService.getNodeLifecycleName(node);
231     WebpagePublicationPlugin publicationPlugin = this.getWebpagePublicationPlugins()
232                                                      .get(lifecycleName);
233 
234     publicationPlugin.updateLifecyleOnChangeContent(node, remoteUser, newState);
235 
236     listenerService.broadcast(UPDATE_EVENT, cmsService, node);
237   }
238   
239   private boolean allowEnrollInPublication(Node node, String nodeType) throws Exception {
240     String path = node.getPath();
241     Node parentNode = node.getParent();
242     while(!path.equals("/") && path.length() > 0) {
243       parentNode = (Node)node.getSession().getItem(path);
244       if(parentNode.isNodeType(nodeType)) return false;
245       path = path.substring(0, path.lastIndexOf("/"));
246     }
247     return true;
248   }
249 }