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.repository; 017 018import java.util.ArrayList; 019import java.util.Date; 020import java.util.GregorianCalendar; 021import java.util.List; 022import java.util.Set; 023import java.util.stream.Collectors; 024 025import javax.jcr.Node; 026import javax.jcr.PathNotFoundException; 027import javax.jcr.RepositoryException; 028 029import org.ametys.plugins.repository.AmetysObject; 030import org.ametys.plugins.repository.AmetysObjectIterable; 031import org.ametys.plugins.repository.AmetysRepositoryException; 032import org.ametys.plugins.repository.ChainedAmetysObjectIterable; 033import org.ametys.plugins.repository.ModifiableTraversableAmetysObject; 034import org.ametys.plugins.repository.RepositoryConstants; 035import org.ametys.web.repository.SiteAwareAmetysObject; 036import org.ametys.web.repository.site.Site; 037 038/** 039 * {@link AmetysObject} representing a survey 040 */ 041public class Survey extends AbstractSurveyElement<SurveyFactory> implements SiteAwareAmetysObject 042{ 043 /** Constants for title metadata. */ 044 private static final String __PROPERTY_TITLE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":title"; 045 /** Constants for header metadata. */ 046 private static final String __PROPERTY_LABEL = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":label"; 047 /** Constants for description metadata. */ 048 private static final String __PROPERTY_DESC = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":description"; 049 /** Constants for description metadata. */ 050 private static final String __PROPERTY_ENDING_MSG = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":endingMessage"; 051 /** Constants for private metadata. */ 052 private static final String __PROPERTY_VALIDATED = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":validated"; 053 /** Constants for private metadata. */ 054 private static final String __PROPERTY_VALIDATION_DATE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":validationDate"; 055 /** Constants for start date metadata. */ 056 private static final String __PROPERTY_START_DATE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":startDate"; 057 /** Constants for end date metadata. */ 058 private static final String __PROPERTY_END_DATE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":endDate"; 059 /** Constants for private metadata. */ 060 private static final String __PROPERTY_REDIRECTION = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":redirection"; 061 062 /** 063 * Creates a {@link Survey}. 064 * @param node the node backing this {@link AmetysObject}. 065 * @param parentPath the parent path in the Ametys hierarchy. 066 * @param factory the {@link SurveyFactory} which creates the AmetysObject. 067 */ 068 public Survey(Node node, String parentPath, SurveyFactory factory) 069 { 070 super(node, parentPath, factory); 071 } 072 073 /** 074 * Retrieves the title. 075 * @return the title. 076 * @throws AmetysRepositoryException if an error occurs. 077 */ 078 public String getTitle() throws AmetysRepositoryException 079 { 080 try 081 { 082 return getNode().getProperty(__PROPERTY_TITLE).getString(); 083 } 084 catch (PathNotFoundException e) 085 { 086 return null; 087 } 088 catch (RepositoryException e) 089 { 090 throw new AmetysRepositoryException("Unable to get title property", e); 091 } 092 } 093 094 /** 095 * Set the title. 096 * @param title the title. 097 * @throws AmetysRepositoryException if an error occurs. 098 */ 099 public void setTitle(String title) throws AmetysRepositoryException 100 { 101 try 102 { 103 getNode().setProperty(__PROPERTY_TITLE, title); 104 } 105 catch (RepositoryException e) 106 { 107 throw new AmetysRepositoryException("Unable to set title property", e); 108 } 109 } 110 111 /** 112 * Retrieves the survey label. 113 * @return the the survey label. 114 * @throws AmetysRepositoryException if an error occurs. 115 */ 116 public String getLabel() throws AmetysRepositoryException 117 { 118 try 119 { 120 return getNode().getProperty(__PROPERTY_LABEL).getString(); 121 } 122 catch (PathNotFoundException e) 123 { 124 return null; 125 } 126 catch (RepositoryException e) 127 { 128 throw new AmetysRepositoryException("Unable to get label property", e); 129 } 130 } 131 132 /** 133 * Set the survey label. 134 * @param label the survey label. 135 * @throws AmetysRepositoryException if an error occurs. 136 */ 137 public void setLabel(String label) throws AmetysRepositoryException 138 { 139 try 140 { 141 getNode().setProperty(__PROPERTY_LABEL, label); 142 } 143 catch (RepositoryException e) 144 { 145 throw new AmetysRepositoryException("Unable to set label property", e); 146 } 147 } 148 149 /** 150 * Retrieves the description. 151 * @return the description. 152 * @throws AmetysRepositoryException if an error occurs. 153 */ 154 public String getDescription() throws AmetysRepositoryException 155 { 156 try 157 { 158 return getNode().getProperty(__PROPERTY_DESC).getString(); 159 } 160 catch (PathNotFoundException e) 161 { 162 return null; 163 } 164 catch (RepositoryException e) 165 { 166 throw new AmetysRepositoryException("Unable to get description property", e); 167 } 168 } 169 170 /** 171 * Set the description. 172 * @param description the description. 173 * @throws AmetysRepositoryException if an error occurs. 174 */ 175 public void setDescription(String description) throws AmetysRepositoryException 176 { 177 try 178 { 179 getNode().setProperty(__PROPERTY_DESC, description); 180 } 181 catch (RepositoryException e) 182 { 183 throw new AmetysRepositoryException("Unable to set description property", e); 184 } 185 } 186 187 /** 188 * Retrieves the ending message. 189 * @return the ending message. 190 * @throws AmetysRepositoryException if an error occurs. 191 */ 192 public String getEndingMessage() throws AmetysRepositoryException 193 { 194 try 195 { 196 return getNode().getProperty(__PROPERTY_ENDING_MSG).getString(); 197 } 198 catch (PathNotFoundException e) 199 { 200 return null; 201 } 202 catch (RepositoryException e) 203 { 204 throw new AmetysRepositoryException("Unable to get ending message property", e); 205 } 206 } 207 208 /** 209 * Set the ending message. 210 * @param message the ending message. 211 * @throws AmetysRepositoryException if an error occurs. 212 */ 213 public void setEndingMessage(String message) throws AmetysRepositoryException 214 { 215 try 216 { 217 getNode().setProperty(__PROPERTY_ENDING_MSG, message); 218 } 219 catch (RepositoryException e) 220 { 221 throw new AmetysRepositoryException("Unable to set ending message property", e); 222 } 223 } 224 225 /** 226 * Determines if the survey is validated. 227 * @return true if the survey is validated. 228 * @throws AmetysRepositoryException if an error occurs. 229 */ 230 public boolean isValidated() throws AmetysRepositoryException 231 { 232 try 233 { 234 return getNode().getProperty(__PROPERTY_VALIDATED).getBoolean(); 235 } 236 catch (PathNotFoundException e) 237 { 238 return false; 239 } 240 catch (RepositoryException e) 241 { 242 throw new AmetysRepositoryException("Unable to get validated property", e); 243 } 244 } 245 246 /** 247 * Valid or invalid survey 248 * @param validated true to validate the survey 249 * @throws AmetysRepositoryException if an error occurs. 250 */ 251 public void setValidated(boolean validated) throws AmetysRepositoryException 252 { 253 try 254 { 255 getNode().setProperty(__PROPERTY_VALIDATED, validated); 256 } 257 catch (RepositoryException e) 258 { 259 throw new AmetysRepositoryException("Unable to validate survey", e); 260 } 261 } 262 263 /** 264 * Set the date of validation 265 * @param date The date of validation 266 * @throws AmetysRepositoryException if an error occurs. 267 */ 268 public void setValidationDate(Date date) throws AmetysRepositoryException 269 { 270 try 271 { 272 if (date != null) 273 { 274 GregorianCalendar calendar = new GregorianCalendar(); 275 calendar.setTime(date); 276 277 getNode().setProperty(__PROPERTY_VALIDATION_DATE, calendar); 278 } 279 else if (getNode().hasProperty(__PROPERTY_VALIDATION_DATE)) 280 { 281 getNode().getProperty(__PROPERTY_VALIDATION_DATE).remove(); 282 } 283 } 284 catch (RepositoryException e) 285 { 286 throw new AmetysRepositoryException("Unable to set date of validation", e); 287 } 288 } 289 290 /** 291 * Get the date of validation 292 * @return the date of validation 293 * @throws AmetysRepositoryException if an error occurs. 294 */ 295 public Date getValidationDate () throws AmetysRepositoryException 296 { 297 try 298 { 299 return getNode().getProperty(__PROPERTY_VALIDATION_DATE).getDate().getTime(); 300 } 301 catch (PathNotFoundException e) 302 { 303 return null; 304 } 305 catch (RepositoryException e) 306 { 307 throw new AmetysRepositoryException("Unable to get validation date property", e); 308 } 309 } 310 311 /** 312 * Set the start date 313 * @param date The start date 314 * @throws AmetysRepositoryException if an error occurs. 315 */ 316 public void setStartDate(Date date) throws AmetysRepositoryException 317 { 318 try 319 { 320 if (date != null) 321 { 322 GregorianCalendar calendar = new GregorianCalendar(); 323 calendar.setTime(date); 324 325 getNode().setProperty(__PROPERTY_START_DATE, calendar); 326 } 327 else if (getNode().hasProperty(__PROPERTY_START_DATE)) 328 { 329 getNode().getProperty(__PROPERTY_START_DATE).remove(); 330 } 331 } 332 catch (RepositoryException e) 333 { 334 throw new AmetysRepositoryException("Unable to set start date", e); 335 } 336 } 337 338 /** 339 * Get the start date 340 * @return the start date 341 * @throws AmetysRepositoryException if an error occurs. 342 */ 343 public Date getStartDate () throws AmetysRepositoryException 344 { 345 try 346 { 347 return getNode().getProperty(__PROPERTY_START_DATE).getDate().getTime(); 348 } 349 catch (PathNotFoundException e) 350 { 351 return null; 352 } 353 catch (RepositoryException e) 354 { 355 throw new AmetysRepositoryException("Unable to get start date property", e); 356 } 357 } 358 359 /** 360 * Set the end date 361 * @param date The end date 362 * @throws AmetysRepositoryException if an error occurs. 363 */ 364 public void setEndDate(Date date) throws AmetysRepositoryException 365 { 366 try 367 { 368 if (date != null) 369 { 370 GregorianCalendar calendar = new GregorianCalendar(); 371 calendar.setTime(date); 372 373 getNode().setProperty(__PROPERTY_END_DATE, calendar); 374 } 375 else if (getNode().hasProperty(__PROPERTY_END_DATE)) 376 { 377 getNode().getProperty(__PROPERTY_END_DATE).remove(); 378 } 379 } 380 catch (RepositoryException e) 381 { 382 throw new AmetysRepositoryException("Unable to set end date", e); 383 } 384 } 385 386 /** 387 * Get the end date 388 * @return the end date 389 * @throws AmetysRepositoryException if an error occurs. 390 */ 391 public Date getEndDate () throws AmetysRepositoryException 392 { 393 try 394 { 395 return getNode().getProperty(__PROPERTY_END_DATE).getDate().getTime(); 396 } 397 catch (PathNotFoundException e) 398 { 399 return null; 400 } 401 catch (RepositoryException e) 402 { 403 throw new AmetysRepositoryException("Unable to get end date property", e); 404 } 405 } 406 407 /** 408 * Retrieves the redirection. 409 * @return the page id of redirection or null. 410 * @throws AmetysRepositoryException if an error occurs. 411 */ 412 public String getRedirection() throws AmetysRepositoryException 413 { 414 try 415 { 416 return getNode().getProperty(__PROPERTY_REDIRECTION).getString(); 417 } 418 catch (PathNotFoundException e) 419 { 420 return null; 421 } 422 catch (RepositoryException e) 423 { 424 throw new AmetysRepositoryException("Unable to get redirection property", e); 425 } 426 } 427 428 /** 429 * Set the redirection. 430 * @param pageId the page id. Can be null to delete redirection 431 * @throws AmetysRepositoryException if an error occurs. 432 */ 433 public void setRedirection(String pageId) throws AmetysRepositoryException 434 { 435 try 436 { 437 if (pageId == null) 438 { 439 if (getNode().hasProperty(__PROPERTY_REDIRECTION)) 440 { 441 getNode().getProperty(__PROPERTY_REDIRECTION).remove(); 442 } 443 } 444 else 445 { 446 getNode().setProperty(__PROPERTY_REDIRECTION, pageId); 447 } 448 } 449 catch (RepositoryException e) 450 { 451 throw new AmetysRepositoryException("Unable to set redirection property", e); 452 } 453 } 454 455 /** 456 * Get the survey pages. 457 * @return the survey pages. 458 * @throws AmetysRepositoryException if an error occurs when retrieving the pages of the survey 459 */ 460 public Set<SurveyPage> getPages() throws AmetysRepositoryException 461 { 462 return getChildren().stream() 463 .filter(child -> child instanceof SurveyPage) 464 .map(child -> (SurveyPage) child) 465 .collect(Collectors.toSet()); 466 } 467 468 /** 469 * Get a question by its name. 470 * @param name the question name. 471 * @return the question. 472 * @throws AmetysRepositoryException if an error occurs when retrieving a question of a survey 473 */ 474 public SurveyQuestion getQuestion(String name) throws AmetysRepositoryException 475 { 476 for (SurveyPage page : getPages()) 477 { 478 if (page.hasChild(name)) 479 { 480 return page.getQuestion(name); 481 } 482 } 483 return null; 484 } 485 486 /** 487 * Get the survey questions. 488 * @return the survey questions. 489 * @throws AmetysRepositoryException if an error occurs when retrieving all the questions of a survey 490 */ 491 public AmetysObjectIterable<SurveyQuestion> getQuestions() throws AmetysRepositoryException 492 { 493 List<AmetysObjectIterable<SurveyQuestion>> questions = new ArrayList<>(); 494 495 for (SurveyPage page : getPages()) 496 { 497 questions.add(page.getQuestions()); 498 } 499 500 return new ChainedAmetysObjectIterable<>(questions); 501 } 502 503 @Override 504 public Site getSite() throws AmetysRepositoryException 505 { 506 return getParent().getParent().getParent().getParent().getParent(); 507 } 508 509 @Override 510 public String getSiteName() throws AmetysRepositoryException 511 { 512 return getSite().getName(); 513 } 514 515 /** 516 * Get the survey language. 517 * @return the survey language. 518 */ 519 public String getLanguage() 520 { 521 return getParent().getName(); 522 } 523 524 /** 525 * Returns a unique question name in the survey 526 * @param originalName The original name 527 * @return a unique question name 528 */ 529 public String findUniqueQuestionName (String originalName) 530 { 531 String name = originalName; 532 int index = 2; 533 while (_hasQuestionName(name)) 534 { 535 name = originalName + "-" + (index++); 536 } 537 return name; 538 } 539 540 private boolean _hasQuestionName (String name) 541 { 542 for (SurveyPage page : getPages()) 543 { 544 if (page.hasQuestion(name)) 545 { 546 return true; 547 } 548 } 549 return false; 550 } 551 552 @Override 553 public Survey copyTo(ModifiableTraversableAmetysObject parent, String name) throws AmetysRepositoryException 554 { 555 Survey survey = parent.createChild(name, "ametys:survey"); 556 557 survey.setTitle(getTitle()); 558 survey.setLabel(getLabel()); 559 560 String description = getDescription(); 561 if (description != null) 562 { 563 survey.setDescription(description); 564 } 565 566 String endingMessage = getEndingMessage(); 567 if (endingMessage != null) 568 { 569 survey.setEndingMessage(endingMessage); 570 } 571 572 copyPictureTo(survey); 573 574 survey.setValidated(false); 575 576 for (SurveyPage surveyPage : getPages()) 577 { 578 surveyPage.copyTo(survey, surveyPage.getName()); 579 } 580 581 // TODO Copy ACL ? 582 583 return survey; 584 } 585 586 @Override 587 public Survey copyTo(ModifiableTraversableAmetysObject parent, String name, List<String> restrictTo) throws AmetysRepositoryException 588 { 589 return copyTo(parent, name); 590 } 591}