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