001/* 002 * Copyright 2015 Anyware Services 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.ametys.plugins.cart.actions; 017 018import java.util.ArrayList; 019import java.util.Collections; 020import java.util.Comparator; 021import java.util.Date; 022import java.util.HashMap; 023import java.util.List; 024import java.util.Map; 025 026import org.apache.avalon.framework.parameters.Parameters; 027import org.apache.avalon.framework.service.ServiceException; 028import org.apache.avalon.framework.service.ServiceManager; 029import org.apache.cocoon.acting.ServiceableAction; 030import org.apache.cocoon.environment.ObjectModelHelper; 031import org.apache.cocoon.environment.Redirector; 032import org.apache.cocoon.environment.Request; 033import org.apache.cocoon.environment.SourceResolver; 034import org.apache.commons.lang.ArrayUtils; 035 036import org.ametys.cms.repository.Content; 037import org.ametys.cms.repository.WorkflowAwareContent; 038import org.ametys.core.cocoon.JSonReader; 039import org.ametys.core.user.CurrentUserProvider; 040import org.ametys.core.user.UserIdentity; 041import org.ametys.core.user.UserManager; 042import org.ametys.core.util.DateUtils; 043import org.ametys.plugins.cart.Cart; 044import org.ametys.plugins.cart.CartElement; 045import org.ametys.plugins.cart.CartsDAO; 046import org.ametys.plugins.cart.ContentElement; 047import org.ametys.plugins.cart.QueryFromDirectoryElement; 048import org.ametys.plugins.core.user.UserHelper; 049import org.ametys.plugins.queriesdirectory.QueryDAO; 050import org.ametys.plugins.repository.AmetysObjectResolver; 051import org.ametys.plugins.repository.AmetysRepositoryException; 052import org.ametys.plugins.repository.UnknownAmetysObjectException; 053import org.ametys.plugins.repository.version.VersionAwareAmetysObject; 054import org.ametys.plugins.workflow.store.AmetysStep; 055import org.ametys.plugins.workflow.support.WorkflowHelper; 056import org.ametys.plugins.workflow.support.WorkflowProvider; 057import org.ametys.plugins.workflow.support.WorkflowProvider.AmetysObjectWorkflow; 058import org.ametys.runtime.i18n.I18nizableText; 059 060import com.opensymphony.workflow.WorkflowException; 061import com.opensymphony.workflow.spi.Step; 062 063/** 064 * SAX a {@link Cart} 065 * 066 */ 067public class GetCartElementsAction extends ServiceableAction 068{ 069 /** The Ametys object resolver */ 070 protected AmetysObjectResolver _resolver; 071 /** The workflow */ 072 protected WorkflowProvider _workflowProvider; 073 /** The workflow helper */ 074 protected WorkflowHelper _workflowHelper; 075 /** The current user provider */ 076 protected CurrentUserProvider _userProvider; 077 /** The user manager */ 078 protected UserManager _userManager; 079 /** The user helper */ 080 protected UserHelper _userHelper; 081 /** The carts DAO */ 082 protected CartsDAO _cartsDAO; 083 /** The query DAO */ 084 protected QueryDAO _queryDAO; 085 086 @Override 087 public void service(ServiceManager serviceManager) throws ServiceException 088 { 089 super.service(serviceManager); 090 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 091 _workflowProvider = (WorkflowProvider) serviceManager.lookup(WorkflowProvider.ROLE); 092 _workflowHelper = (WorkflowHelper) serviceManager.lookup(WorkflowHelper.ROLE); 093 _userManager = (UserManager) serviceManager.lookup(UserManager.ROLE); 094 _userProvider = (CurrentUserProvider) serviceManager.lookup(CurrentUserProvider.ROLE); 095 _userHelper = (UserHelper) serviceManager.lookup(UserHelper.ROLE); 096 _cartsDAO = (CartsDAO) serviceManager.lookup(CartsDAO.ROLE); 097 _queryDAO = (QueryDAO) serviceManager.lookup(QueryDAO.ROLE); 098 } 099 100 @Override 101 public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception 102 { 103 @SuppressWarnings("unchecked") 104 Map jsParameters = (Map<String, Object>) objectModel.get(ObjectModelHelper.PARENT_CONTEXT); 105 106 String id = (String) jsParameters.get("cartId"); 107 108 Map<String, Object> result = new HashMap<>(); 109 List<Map<String, Object>> nodes = new ArrayList<>(); 110 int total = 0; 111 112 try 113 { 114 Cart cart = _resolver.resolveById(id); 115 116 UserIdentity user = _userProvider.getUser(); 117 if (_cartsDAO.canRead(user, cart)) 118 { 119 result.put("label", cart.getTitle()); 120 result.put("description", cart.getDescription()); 121 122 List<CartElement> elements = cart.getElements(); 123 for (CartElement elmt : elements) 124 { 125 boolean isQueryFromDirectory = elmt instanceof QueryFromDirectoryElement; 126 if (!isQueryFromDirectory || _queryDAO.canRead(user, ((QueryFromDirectoryElement) elmt).getQuery())) 127 { 128 nodes.addAll(elementToJSON(elmt)); 129 } 130 } 131 total = elements.size(); 132 } 133 134 result.put("cartElements", nodes); 135 result.put("total", total); 136 } 137 catch (UnknownAmetysObjectException e) 138 { 139 result.put("unknown-cart", true); 140 } 141 142 Request request = ObjectModelHelper.getRequest(objectModel); 143 request.setAttribute(JSonReader.OBJECT_TO_READ, result); 144 145 return EMPTY_MAP; 146 } 147 148 /** 149 * Gets a cart element to JSON format 150 * @param elmt The elemetnt 151 * @return The element's properties 152 */ 153 protected List<Map<String, Object>> elementToJSON (CartElement elmt) 154 { 155 List<Map<String, Object>> elmts = new ArrayList<>(); 156 157 HashMap<String, Object> infos = new HashMap<>(); 158 159 infos.put("elmtId", elmt.getId()); 160 infos.put("title", elmt.getTitle()); 161 infos.put("description", elmt.getDescription()); 162 163 infos.put("lastModification", DateUtils.zonedDateTimeToString(elmt.getLastModified())); 164 infos.put("lastContributor", _userHelper.user2json(elmt.getLastContributor(), true)); 165 166 infos.put("glyphIcon", elmt.getGlyphIcon()); 167 infos.put("smallIcon", elmt.getSmallIcon()); 168 infos.put("mediumIcon", elmt.getMediumIcon()); 169 170 infos.put("type", elmt.getType()); 171 172 if (elmt instanceof ContentElement) 173 { 174 Content content = _resolver.resolveById(elmt.getId()); 175 infos.put("workflow-step", workflowStepToJSON(content)); 176 } 177 178 List<I18nizableText> groups = elmt.getGroups(); 179 for (I18nizableText group : groups) 180 { 181 @SuppressWarnings("unchecked") 182 HashMap<String, Object> clonedInfos = (HashMap<String, Object>) infos.clone(); 183 clonedInfos.put("group", group); 184 elmts.add(clonedInfos); 185 } 186 187 return elmts; 188 } 189 190 /** 191 * Gets the workflow step if the content is a <code>WorkflowAwareContent</code> 192 * @param content The content 193 * @return The workflow step's properties 194 */ 195 protected Map<String, Object> workflowStepToJSON (Content content) 196 { 197 Map<String, Object> infos = new HashMap<>(); 198 199 if (content instanceof WorkflowAwareContent) 200 { 201 WorkflowAwareContent waContent = (WorkflowAwareContent) content; 202 203 try 204 { 205 AmetysObjectWorkflow workflow = _workflowProvider.getAmetysObjectWorkflow(waContent); 206 long workflowId = waContent.getWorkflowId(); 207 String workflowName = workflow.getWorkflowName(workflowId); 208 209 Step currentStep = _getCurrentStep(waContent, workflow); 210 211 int currentStepId = currentStep.getStepId(); 212 213 I18nizableText workflowStepName = new I18nizableText("application", _workflowHelper.getStepName(workflowName, currentStepId)); 214 215 infos.put("id", String.valueOf(currentStepId)); 216 infos.put("name", workflowStepName); 217 if ("application".equals(workflowStepName.getCatalogue())) 218 { 219 infos.put("icon-small", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-small.png"); 220 infos.put("icon-medium", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-medium.png"); 221 infos.put("icon-large", "/plugins/cms/resources_workflow/" + workflowStepName.getKey() + "-large.png"); 222 } 223 else 224 { 225 String pluginName = workflowStepName.getCatalogue().substring("plugin.".length()); 226 infos.put("icon-small", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-small.png"); 227 infos.put("icon-medium", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-medium.png"); 228 infos.put("icon-large", "/plugins/" + pluginName + "/resources/img/workflow/" + workflowStepName.getKey() + "-large.png"); 229 } 230 } 231 catch (AmetysRepositoryException e) 232 { 233 // Current step id was not positioned 234 } 235 catch (WorkflowException e) 236 { 237 // Ignore, just don't SAX the workflow step. 238 } 239 } 240 241 return infos; 242 } 243 244 /** 245 * Get a content's step, wherever it works on the base version or not. 246 * @param content the content. 247 * @param workflow the workflow instance bound to this content 248 * @return the content's workflow step. 249 * @throws WorkflowException if an error occurs. 250 */ 251 protected Step _getCurrentStep(WorkflowAwareContent content, AmetysObjectWorkflow workflow) throws WorkflowException 252 { 253 long workflowId = content.getWorkflowId(); 254 Step currentStep = (Step) workflow.getCurrentSteps(workflowId).get(0); 255 256 if (content instanceof VersionAwareAmetysObject) 257 { 258 VersionAwareAmetysObject vaContent = (VersionAwareAmetysObject) content; 259 String currentRevision = vaContent.getRevision(); 260 261 if (currentRevision != null) 262 { 263 264 String[] allRevisions = vaContent.getAllRevisions(); 265 int currentRevIndex = ArrayUtils.indexOf(allRevisions, currentRevision); 266 267 if (currentRevIndex > -1 && currentRevIndex < (allRevisions.length - 1)) 268 { 269 String nextRevision = allRevisions[currentRevIndex + 1]; 270 271 Date currentRevTimestamp = vaContent.getRevisionTimestamp(); 272 Date nextRevTimestamp = vaContent.getRevisionTimestamp(nextRevision); 273 274 // Get all steps between the two revisions. 275 List<Step> steps = _workflowHelper.getStepsBetween(workflow, workflowId, currentRevTimestamp, nextRevTimestamp); 276 277 // In the old workflow structure 278 // We take the second, which is current revision's last step. 279 if (steps.size() > 0 && steps.get(0) instanceof AmetysStep) 280 { 281 AmetysStep amStep = (AmetysStep) steps.get(0); 282 if (amStep.getProperty("actionFinishDate") != null) 283 { 284 // New workflow structure detected: cut the first workflow step 285 // in the list, as it belongs to the next version. 286 steps = steps.subList(1, steps.size()); 287 } 288 } 289 290 // Order by step descendant. 291 Collections.sort(steps, new Comparator<Step>() 292 { 293 public int compare(Step step1, Step step2) 294 { 295 return -Long.valueOf(step1.getId()).compareTo(step2.getId()); 296 } 297 }); 298 299 // The first step in the list is the current version's last workflow step. 300 if (steps.size() > 0) 301 { 302 currentStep = steps.get(0); 303 } 304 } 305 } 306 } 307 return currentStep; 308 } 309 310}