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.survey.dao; 017 018import java.util.ArrayList; 019import java.util.HashMap; 020import java.util.Iterator; 021import java.util.List; 022import java.util.Map; 023 024import javax.jcr.Node; 025import javax.jcr.NodeIterator; 026import javax.jcr.Repository; 027import javax.jcr.RepositoryException; 028import javax.jcr.Session; 029import javax.jcr.query.Query; 030 031import org.apache.avalon.framework.service.ServiceException; 032import org.apache.avalon.framework.service.ServiceManager; 033import org.apache.commons.lang.StringUtils; 034 035import org.ametys.cms.FilterNameHelper; 036import org.ametys.core.observation.Event; 037import org.ametys.core.ui.Callable; 038import org.ametys.plugins.repository.AmetysObjectIterable; 039import org.ametys.plugins.repository.AmetysRepositoryException; 040import org.ametys.plugins.repository.RepositoryConstants; 041import org.ametys.plugins.repository.UnknownAmetysObjectException; 042import org.ametys.plugins.repository.provider.AbstractRepository; 043import org.ametys.plugins.survey.SurveyEvents; 044import org.ametys.plugins.survey.repository.Survey; 045import org.ametys.plugins.survey.repository.SurveyPage; 046import org.ametys.plugins.survey.repository.SurveyQuestion; 047import org.ametys.plugins.survey.repository.SurveyQuestion.QuestionType; 048import org.ametys.plugins.survey.repository.SurveyRule; 049import org.ametys.plugins.survey.repository.SurveyRule.RuleType; 050 051/** 052 * DAO for manipulating survey pages. 053 * 054 */ 055public class PageDAO extends AbstractDAO 056{ 057 /** The Avalon role */ 058 public static final String ROLE = PageDAO.class.getName(); 059 060 /** The repository */ 061 private Repository _repository; 062 063 /** The Question DAO */ 064 private QuestionDAO _questionDAO; 065 066 @Override 067 public void service(ServiceManager serviceManager) throws ServiceException 068 { 069 super.service(serviceManager); 070 _repository = (Repository) serviceManager.lookup(AbstractRepository.ROLE); 071 _questionDAO = (QuestionDAO) serviceManager.lookup(QuestionDAO.ROLE); 072 } 073 074 /** 075 * Gets properties of a survey page 076 * @param id The id of the survey page 077 * @return The properties 078 */ 079 @Callable 080 public Map<String, Object> getPage (String id) 081 { 082 SurveyPage page = _resolver.resolveById(id); 083 084 return getPage(page); 085 } 086 087 /** 088 * Gets properties of a survey page 089 * @param page The survey page 090 * @return The properties 091 */ 092 public Map<String, Object> getPage (SurveyPage page) 093 { 094 Map<String, Object> properties = new HashMap<>(); 095 096 properties.put("id", page.getId()); 097 properties.put("label", page.getLabel()); 098 properties.put("title", page.getTitle()); 099 properties.put("description", page.getDescription()); 100 101 properties.putAll(getPictureInfo(page)); 102 103 return properties; 104 } 105 106 /** 107 * Determines if a page is the last of survey's pages. 108 * @param id The page id 109 * @return True if the page is the last one. 110 */ 111 @Callable 112 public boolean isLastPage (String id) 113 { 114 SurveyPage page = _resolver.resolveById(id); 115 116 Survey survey = page.getParent(); 117 AmetysObjectIterable<SurveyPage> pages = survey.getChildren(); 118 Iterator<SurveyPage> it = pages.iterator(); 119 120 SurveyPage lastPage = null; 121 while (it.hasNext()) 122 { 123 lastPage = it.next(); 124 } 125 126 boolean isLast = lastPage != null && id.equals(lastPage.getId()); 127 return isLast; 128 } 129 130 /** 131 * Creates a survey page. 132 * @param values The survey page's values 133 * @return The id of the created survey page 134 * @throws Exception if an exception occurs during the page creation process 135 */ 136 @Callable 137 public Map<String, String> createPage (Map<String, Object> values) throws Exception 138 { 139 Map<String, String> result = new HashMap<>(); 140 141 String surveyId = StringUtils.defaultString((String) values.get("surveyId")); 142 Survey survey = _resolver.resolveById(surveyId); 143 144 String label = StringUtils.defaultString((String) values.get("label")); 145 String originalName = FilterNameHelper.filterName(label); 146 147 // Find unique name 148 String name = originalName; 149 int index = 2; 150 while (survey.hasChild(name)) 151 { 152 name = originalName + "-" + (index++); 153 } 154 155 SurveyPage page = survey.createChild(name, "ametys:survey-page"); 156 _setValues(page, values); 157 158 survey.saveChanges(); 159 160 Map<String, Object> eventParams = new HashMap<>(); 161 eventParams.put("survey", survey); 162 _observationManager.notify(new Event(SurveyEvents.SURVEY_MODIFIED, _getCurrentUser(), eventParams)); 163 164 result.put("id", page.getId()); 165 166 return result; 167 } 168 169 /** 170 * Edits a survey page. 171 * @param values The survey page's values 172 * @return The id of the edited survey page and the id of its survey parent 173 */ 174 @Callable 175 public Map<String, String> editPage (Map<String, Object> values) 176 { 177 Map<String, String> result = new HashMap<>(); 178 179 String id = StringUtils.defaultString((String) values.get("id")); 180 SurveyPage page = _resolver.resolveById(id); 181 182 _setValues(page, values); 183 184 page.saveChanges(); 185 186 Map<String, Object> eventParams = new HashMap<>(); 187 eventParams.put("survey", page.getSurvey()); 188 _observationManager.notify(new Event(SurveyEvents.SURVEY_MODIFIED, _getCurrentUser(), eventParams)); 189 190 result.put("id", page.getId()); 191 result.put("surveyId", page.getSurvey().getId()); 192 193 return result; 194 } 195 196 private void _setValues (SurveyPage page, Map<String, Object> values) 197 { 198 page.setTitle(StringUtils.defaultString((String) values.get("title"))); 199 page.setLabel(StringUtils.defaultString((String) values.get("label"))); 200 page.setDescription(StringUtils.defaultString((String) values.get("description"))); 201 202 page.setPictureAlternative(StringUtils.defaultString((String) values.get("picture-alternative"))); 203 setPicture(page, StringUtils.defaultString((String) values.get("picture"))); 204 } 205 206 /** 207 * Copies and pastes a survey page. 208 * @param surveyId The id of the survey, target of the copy 209 * @param pageId The id of the page to copy 210 * @return The id of the created page 211 */ 212 @Callable 213 public Map<String, String> copyPage(String surveyId, String pageId) 214 { 215 Map<String, String> result = new HashMap<>(); 216 217 SurveyPage originalPage = _resolver.resolveById(pageId); 218 Survey parentSurvey = _resolver.resolveById(surveyId); 219 220 // Find unique name 221 String originalName = originalPage.getName(); 222 String name = originalName; 223 int index = 2; 224 while (parentSurvey.hasChild(name)) 225 { 226 name = originalName + "-" + (index++); 227 } 228 229 SurveyPage cPage = originalPage.copyTo(parentSurvey, name); 230 231 Survey originalSurvey = originalPage.getSurvey(); 232 if (!originalSurvey.getId().equals(parentSurvey.getId())) 233 { 234 // Update rules references after copy 235 updateReferencesAfterCopy (originalPage.getSurvey(), parentSurvey, cPage); 236 } 237 238 parentSurvey.saveChanges(); 239 240 Map<String, Object> eventParams = new HashMap<>(); 241 eventParams.put("survey", parentSurvey); 242 _observationManager.notify(new Event(SurveyEvents.SURVEY_MODIFIED, _getCurrentUser(), eventParams)); 243 244 result.put("id", cPage.getId()); 245 246 return result; 247 } 248 249 /** 250 * Deletes a survey page. 251 * @param id The id of the survey page to delete 252 * @return The id of the deleted survey page and the id of its survey parent 253 */ 254 @Callable 255 public Map<String, String> deletePage (String id) 256 { 257 Map<String, String> result = new HashMap<>(); 258 259 SurveyPage page = _resolver.resolveById(id); 260 Survey survey = page.getParent(); 261 262 page.remove(); 263 264 // Remove rules references 265 _removeReferencesFromPages (id); 266 _removeReferencesFromQuestions(id); 267 268 survey.saveChanges(); 269 270 Map<String, Object> eventParams = new HashMap<>(); 271 eventParams.put("survey", survey); 272 _observationManager.notify(new Event(SurveyEvents.SURVEY_MODIFIED, _getCurrentUser(), eventParams)); 273 274 result.put("id", id); 275 result.put("surveyId", survey.getId()); 276 277 return result; 278 } 279 280 /** 281 * Adds a a new rule to a page. 282 * @param id The id of the page 283 * @param rule The rule type 284 * @param page The page to jump or skip 285 * @return An empty map 286 */ 287 @Callable 288 public Map<String, Object> addRule (String id, String rule, String page) 289 { 290 SurveyPage surveyPage = _resolver.resolveById(id); 291 292 surveyPage.setRule(RuleType.valueOf(rule), page); 293 surveyPage.saveChanges(); 294 295 Map<String, Object> eventParams = new HashMap<>(); 296 eventParams.put("survey", surveyPage.getSurvey()); 297 _observationManager.notify(new Event(SurveyEvents.SURVEY_MODIFIED, _getCurrentUser(), eventParams)); 298 299 return new HashMap<>(); 300 } 301 302 /** 303 * Deletes a rule to a page 304 * @param id The id of the page 305 * @return An empty map 306 */ 307 @Callable 308 public Map<String, Object> deleteRule (String id) 309 { 310 SurveyPage surveyPage = _resolver.resolveById(id); 311 312 surveyPage.deleteRule(); 313 surveyPage.saveChanges(); 314 315 Map<String, Object> eventParams = new HashMap<>(); 316 eventParams.put("survey", surveyPage.getSurvey()); 317 _observationManager.notify(new Event(SurveyEvents.SURVEY_MODIFIED, _getCurrentUser(), eventParams)); 318 319 return new HashMap<>(); 320 } 321 322 /** 323 * Gets the rule for a survey page. 324 * @param id The id of the survey page. 325 * @return The rule, or null 326 */ 327 @Callable 328 public Map<String, Object> getRule (String id) 329 { 330 Map<String, Object> result = new HashMap<>(); 331 332 SurveyPage page = _resolver.resolveById(id); 333 SurveyRule rule = page.getRule(); 334 335 if (rule != null) 336 { 337 result.put("type", rule.getType().name()); 338 String pageId = rule.getPage(); 339 if (pageId != null) 340 { 341 try 342 { 343 SurveyPage pageAO = _resolver.resolveById(pageId); 344 result.put("page", pageId); 345 result.put("pageName", pageAO.getLabel()); 346 } 347 catch (UnknownAmetysObjectException e) 348 { 349 // The page does not exist anymore 350 } 351 } 352 } 353 else 354 { 355 result = null; 356 } 357 358 return result; 359 } 360 361 /** 362 * Gets the branches for a survey page. 363 * @param id The id of the survey page. 364 * @return The branches 365 */ 366 @Callable 367 public Map<String, Object> getBranches (String id) 368 { 369 Map<String, Object> result = new HashMap<>(); 370 371 SurveyPage page = _resolver.resolveById(id); 372 373 result.put("id", page.getId()); 374 375 List<Object> questions = new ArrayList<>(); 376 AmetysObjectIterable<SurveyQuestion> questionsAO = page.getChildren(); 377 int index = 1; 378 for (SurveyQuestion question : questionsAO) 379 { 380 if (question.getType() == QuestionType.SINGLE_CHOICE || question.getType() == QuestionType.MULTIPLE_CHOICE) 381 { 382 questions.add(_questionDAO.getRules(question.getId(), index)); 383 } 384 index++; 385 } 386 result.put("questions", questions); 387 388 // SAX page rule 389 result.put("rule", getRule(id)); 390 391 return result; 392 } 393 394 private void _removeReferencesFromPages (String pageId) 395 { 396 Session session = null; 397 String jcrQuery = "//element(*, ametys:survey-page)/element(*, ametys:survey-rule)[@ametys-internal:page='" + pageId + "']"; 398 399 try 400 { 401 session = _repository.login(); 402 @SuppressWarnings("deprecation") 403 Query query = session.getWorkspace().getQueryManager().createQuery(jcrQuery, Query.XPATH); 404 405 NodeIterator nodes = query.execute().getNodes(); 406 while (nodes.hasNext()) 407 { 408 Node ruleNode = nodes.nextNode(); 409 Node pageNode = ruleNode.getParent(); 410 411 pageNode.getNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":rule").remove(); 412 pageNode.getSession().save(); 413 } 414 } 415 catch (RepositoryException ex) 416 { 417 if (session != null) 418 { 419 session.logout(); 420 } 421 422 throw new AmetysRepositoryException("An error occurred executing the JCR query : " + jcrQuery, ex); 423 } 424 } 425 426 private void _removeReferencesFromQuestions (String pageId) 427 { 428 Session session = null; 429 String jcrQuery = "//element(*, ametys:survey-question)//element(*, ametys:survey-rule)[@ametys-internal:page='" + pageId + "']"; 430 431 try 432 { 433 session = _repository.login(); 434 @SuppressWarnings("deprecation") 435 Query query = session.getWorkspace().getQueryManager().createQuery(jcrQuery, Query.XPATH); 436 437 NodeIterator nodes = query.execute().getNodes(); 438 while (nodes.hasNext()) 439 { 440 Node ruleNode = nodes.nextNode(); 441 Node questionNode = ruleNode.getParent().getParent(); 442 443 questionNode.getNode(RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":rules").getNode(ruleNode.getName()).remove(); 444 questionNode.getSession().save(); 445 } 446 } 447 catch (RepositoryException ex) 448 { 449 if (session != null) 450 { 451 session.logout(); 452 } 453 454 throw new AmetysRepositoryException("An error occurred executing the JCR query : " + jcrQuery, ex); 455 } 456 } 457}