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