001/* 002 * Copyright 2021 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.forms.dao; 017 018import java.time.LocalDate; 019import java.time.ZonedDateTime; 020import java.util.ArrayList; 021import java.util.HashMap; 022import java.util.List; 023import java.util.Map; 024import java.util.Optional; 025import java.util.Set; 026import java.util.stream.Collectors; 027 028import javax.jcr.Node; 029import javax.jcr.Repository; 030import javax.jcr.RepositoryException; 031 032import org.apache.avalon.framework.component.Component; 033import org.apache.avalon.framework.service.ServiceException; 034import org.apache.avalon.framework.service.ServiceManager; 035import org.apache.avalon.framework.service.Serviceable; 036import org.apache.commons.lang.StringUtils; 037 038import org.ametys.core.observation.Event; 039import org.ametys.core.observation.ObservationManager; 040import org.ametys.core.right.RightManager; 041import org.ametys.core.right.RightManager.RightResult; 042import org.ametys.core.ui.Callable; 043import org.ametys.core.user.CurrentUserProvider; 044import org.ametys.core.user.UserIdentity; 045import org.ametys.core.util.DateUtils; 046import org.ametys.core.util.I18nUtils; 047import org.ametys.plugins.core.user.UserHelper; 048import org.ametys.plugins.forms.FormEvents; 049import org.ametys.plugins.forms.dao.FormEntryDAO.Sort; 050import org.ametys.plugins.forms.helper.ScheduleOpeningHelper; 051import org.ametys.plugins.forms.repository.CopyFormUpdater; 052import org.ametys.plugins.forms.repository.CopyFormUpdaterExtensionPoint; 053import org.ametys.plugins.forms.repository.Form; 054import org.ametys.plugins.forms.repository.Form.ExpirationPolicy; 055import org.ametys.plugins.forms.repository.FormDirectory; 056import org.ametys.plugins.forms.repository.FormEntry; 057import org.ametys.plugins.forms.repository.FormFactory; 058import org.ametys.plugins.forms.repository.FormQuestion; 059import org.ametys.plugins.forms.rights.FormsDirectoryRightAssignmentContext; 060import org.ametys.plugins.repository.AmetysObject; 061import org.ametys.plugins.repository.AmetysObjectIterable; 062import org.ametys.plugins.repository.AmetysObjectResolver; 063import org.ametys.plugins.repository.ModifiableAmetysObject; 064import org.ametys.plugins.repository.UnknownAmetysObjectException; 065import org.ametys.plugins.repository.jcr.NameHelper; 066import org.ametys.plugins.repository.provider.AbstractRepository; 067import org.ametys.plugins.workflow.support.WorkflowHelper; 068import org.ametys.runtime.authentication.AccessDeniedException; 069import org.ametys.runtime.i18n.I18nizableText; 070import org.ametys.runtime.model.ElementDefinition; 071import org.ametys.runtime.plugin.component.AbstractLogEnabled; 072import org.ametys.web.parameters.view.ViewParametersManager; 073import org.ametys.web.repository.page.ModifiableZoneItem; 074import org.ametys.web.repository.page.Page; 075import org.ametys.web.repository.page.SitemapElement; 076import org.ametys.web.repository.page.ZoneDAO; 077import org.ametys.web.repository.page.ZoneItem; 078import org.ametys.web.repository.site.SiteManager; 079import org.ametys.web.service.Service; 080import org.ametys.web.service.ServiceExtensionPoint; 081 082/** 083 * The form DAO 084 */ 085public class FormDAO extends AbstractLogEnabled implements Serviceable, Component 086{ 087 /** The Avalon role */ 088 public static final String ROLE = FormDAO.class.getName(); 089 /** The right id to handle forms */ 090 public static final String HANDLE_FORMS_RIGHT_ID = "Plugins_Forms_Right_Handle"; 091 092 private static final String __FORM_NAME_PREFIX = "form-"; 093 094 /** The Ametys object resolver */ 095 protected AmetysObjectResolver _resolver; 096 /** The current user provider */ 097 protected CurrentUserProvider _userProvider; 098 /** I18n Utils */ 099 protected I18nUtils _i18nUtils; 100 /** The form directory DAO */ 101 protected FormDirectoryDAO _formDirectoryDAO; 102 /** The form page DAO */ 103 protected FormPageDAO _formPageDAO; 104 /** The form entry DAO */ 105 protected FormEntryDAO _formEntryDAO; 106 /** The user helper */ 107 protected UserHelper _userHelper; 108 /** The JCR repository. */ 109 protected Repository _repository; 110 /** The right manager */ 111 protected RightManager _rightManager; 112 /** The service extension point */ 113 protected ServiceExtensionPoint _serviceEP; 114 /** The zone DAO */ 115 protected ZoneDAO _zoneDAO; 116 /** The schedule opening helper */ 117 protected ScheduleOpeningHelper _scheduleOpeningHelper; 118 /** The workflow helper */ 119 protected WorkflowHelper _workflowHelper; 120 /** The site manager */ 121 protected SiteManager _siteManager; 122 /** Observer manager. */ 123 protected ObservationManager _observationManager; 124 /** The current user provider. */ 125 protected CurrentUserProvider _currentUserProvider; 126 127 /** The copy form updater extension point */ 128 protected CopyFormUpdaterExtensionPoint _copyFormEP; 129 130 public void service(ServiceManager manager) throws ServiceException 131 { 132 _resolver = (AmetysObjectResolver) manager.lookup(AmetysObjectResolver.ROLE); 133 _userProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 134 _userHelper = (UserHelper) manager.lookup(UserHelper.ROLE); 135 _i18nUtils = (I18nUtils) manager.lookup(I18nUtils.ROLE); 136 _formDirectoryDAO = (FormDirectoryDAO) manager.lookup(FormDirectoryDAO.ROLE); 137 _formPageDAO = (FormPageDAO) manager.lookup(FormPageDAO.ROLE); 138 _formEntryDAO = (FormEntryDAO) manager.lookup(FormEntryDAO.ROLE); 139 _repository = (Repository) manager.lookup(AbstractRepository.ROLE); 140 _rightManager = (RightManager) manager.lookup(RightManager.ROLE); 141 _serviceEP = (ServiceExtensionPoint) manager.lookup(ServiceExtensionPoint.ROLE); 142 _zoneDAO = (ZoneDAO) manager.lookup(ZoneDAO.ROLE); 143 _scheduleOpeningHelper = (ScheduleOpeningHelper) manager.lookup(ScheduleOpeningHelper.ROLE); 144 _workflowHelper = (WorkflowHelper) manager.lookup(WorkflowHelper.ROLE); 145 _siteManager = (SiteManager) manager.lookup(SiteManager.ROLE); 146 _observationManager = (ObservationManager) manager.lookup(ObservationManager.ROLE); 147 _currentUserProvider = (CurrentUserProvider) manager.lookup(CurrentUserProvider.ROLE); 148 _copyFormEP = (CopyFormUpdaterExtensionPoint) manager.lookup(CopyFormUpdaterExtensionPoint.ROLE); 149 } 150 151 /** 152 * Check if a user have read rights on a form 153 * @param userIdentity the user 154 * @param form the form 155 * @return true if the user have read rights on a form 156 */ 157 public boolean hasReadRightOnForm(UserIdentity userIdentity, Form form) 158 { 159 return _rightManager.hasReadAccess(userIdentity, form); 160 } 161 162 /** 163 * Check if a user have write rights on a form element 164 * @param userIdentity the user 165 * @param formElement the form element 166 * @return true if the user have write rights on a form element 167 */ 168 public boolean hasWriteRightOnForm(UserIdentity userIdentity, AmetysObject formElement) 169 { 170 return _rightManager.hasRight(userIdentity, HANDLE_FORMS_RIGHT_ID, formElement) == RightResult.RIGHT_ALLOW; 171 } 172 173 /** 174 * Check if a user have write rights on a form 175 * @param userIdentity the user 176 * @param form the form 177 * @return true if the user have write rights on a form 178 */ 179 public boolean hasRightAffectationRightOnForm(UserIdentity userIdentity, Form form) 180 { 181 return hasWriteRightOnForm(userIdentity, form) || _rightManager.hasRight(userIdentity, "Runtime_Rights_Rights_Handle", "/cms") == RightResult.RIGHT_ALLOW; 182 } 183 184 /** 185 * Check rights for a form element as ametys object 186 * @param formElement the form element as ametys object 187 */ 188 public void checkHandleFormRight(AmetysObject formElement) 189 { 190 UserIdentity user = _userProvider.getUser(); 191 if (!hasWriteRightOnForm(user, formElement)) 192 { 193 throw new AccessDeniedException("User '" + user + "' tried to handle forms without convenient right [" + HANDLE_FORMS_RIGHT_ID + "]"); 194 } 195 } 196 197 /** 198 * Get all forms from a site 199 * @param siteName the site name 200 * @return the list of form 201 */ 202 public List<Form> getForms(String siteName) 203 { 204 String xpathQuery = "//element(" + siteName + ", ametys:site)//element(*, ametys:form)"; 205 return _resolver.query(xpathQuery) 206 .stream() 207 .filter(Form.class::isInstance) 208 .map(Form.class::cast) 209 .collect(Collectors.toList()); 210 } 211 212 /** 213 * Get the form properties 214 * @param formId The form's id 215 * @param full <code>true</code> to get full information on form 216 * @param withRights <code>true</code> to have rights in the properties 217 * @return The form properties 218 */ 219 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 220 public Map<String, Object> getFormProperties (String formId, boolean full, boolean withRights) 221 { 222 try 223 { 224 Form form = _resolver.resolveById(formId); 225 return getFormProperties(form, full, true); 226 } 227 catch (UnknownAmetysObjectException e) 228 { 229 getLogger().warn("Can't find form with id: {}. It probably has just been deleted", formId, e); 230 Map<String, Object> infos = new HashMap<>(); 231 infos.put("id", formId); 232 return infos; 233 } 234 } 235 236 /** 237 * Get the form properties 238 * @param form The form 239 * @param full <code>true</code> to get full information on form 240 * @param withRights <code>true</code> to have rights in the properties 241 * @return The form properties 242 */ 243 public Map<String, Object> getFormProperties (Form form, boolean full, boolean withRights) 244 { 245 Map<String, Object> infos = new HashMap<>(); 246 247 List<FormEntry> entries = _formEntryDAO.getFormEntries(form, false, List.of(new Sort(FormEntry.ATTRIBUTE_SUBMIT_DATE, "descending"))); 248 List<SitemapElement> pages = getFormPage(form.getId(), form.getSiteName()); 249 String workflowName = form.getWorkflowName(); 250 251 infos.put("type", "root"); 252 infos.put("isForm", true); 253 infos.put("author", _userHelper.user2json(form.getAuthor(), true)); 254 infos.put("contributor", _userHelper.user2json(form.getContributor())); 255 infos.put("lastModificationDate", DateUtils.zonedDateTimeToString(form.getLastModificationDate())); 256 infos.put("creationDate", DateUtils.zonedDateTimeToString(form.getCreationDate())); 257 infos.put("entriesAmount", entries.size()); 258 infos.put("lastEntry", _getLastSubmissionDate(entries)); 259 infos.put("workflowLabel", StringUtils.isNotBlank(workflowName) ? _workflowHelper.getWorkflowLabel(workflowName) : new I18nizableText("plugin.forms", "PLUGINS_FORMS_FORMS_EDITOR_WORKFLOW_NO_WORKFLOW")); 260 261 /** Use in the bus message */ 262 infos.put("id", form.getId()); 263 infos.put("name", form.getName()); 264 infos.put("title", form.getTitle()); 265 infos.put("fullPath", getFormFullPath(form.getId())); 266 infos.put("pages", _getPagesInfos(pages)); 267 infos.put("hasChildren", form.getPages().size() > 0); 268 infos.put("workflowName", workflowName); 269 270 infos.put("isConfigured", isFormConfigured(form)); 271 272 UserIdentity currentUser = _userProvider.getUser(); 273 if (withRights) 274 { 275 Set<String> userRights = _getUserRights(form); 276 infos.put("rights", userRights); 277 infos.put("canEditRight", userRights.contains(HANDLE_FORMS_RIGHT_ID) || _rightManager.hasRight(currentUser, "Runtime_Rights_Rights_Handle", "/cms") == RightResult.RIGHT_ALLOW); 278 } 279 else 280 { 281 boolean canWrite = hasWriteRightOnForm(currentUser, form); 282 infos.put("canWrite", canWrite); 283 infos.put("canEditRight", canWrite || _rightManager.hasRight(currentUser, "Runtime_Rights_Rights_Handle", "/cms") == RightResult.RIGHT_ALLOW); 284 infos.put("canRead", hasReadRightOnForm(currentUser, form)); 285 } 286 287 if (full) 288 { 289 infos.put("isPublished", !pages.isEmpty()); 290 infos.put("hasEntries", !entries.isEmpty()); 291 infos.put("nbEntries", form.getActiveEntries().size()); 292 293 FormDirectory formDirectoriesRoot = _formDirectoryDAO.getFormDirectoriesRootNode(form.getSiteName()); 294 String parentId = form.getParent().getId().equals(formDirectoriesRoot.getId()) ? FormDirectoryDAO.ROOT_FORM_DIRECTORY_ID : form.getParent().getId(); 295 infos.put("parentId", parentId); 296 297 infos.put("isLimitedToOneEntryByUser", form.isLimitedToOneEntryByUser()); 298 infos.put("isEntriesLimited", form.isEntriesLimited()); 299 Optional<Long> maxEntries = form.getMaxEntries(); 300 if (maxEntries.isPresent()) 301 { 302 infos.put("maxEntries", maxEntries.get()); 303 } 304 infos.put("isQueueEnabled", form.isQueueEnabled()); 305 Optional<Long> queueSize = form.getQueueSize(); 306 if (queueSize.isPresent()) 307 { 308 infos.put("queueSize", queueSize.get()); 309 } 310 311 infos.put("expirationEnabled", form.isExpirationEnabled()); 312 313 LocalDate startDate = form.getStartDate(); 314 LocalDate endDate = form.getEndDate(); 315 if (startDate != null || endDate != null) 316 { 317 infos.put("scheduleStatus", _scheduleOpeningHelper.getStatus(form)); 318 if (startDate != null) 319 { 320 infos.put("startDate", DateUtils.localDateToString(startDate)); 321 } 322 if (endDate != null) 323 { 324 infos.put("endDate", DateUtils.localDateToString(endDate)); 325 } 326 } 327 328 infos.put("adminEmails", form.hasValue(Form.ADMIN_EMAIL_SUBJECT)); 329 infos.put("receiptAcknowledgement", form.hasValue(Form.RECEIPT_SENDER)); 330 infos.put("isAnonymous", _rightManager.hasAnonymousReadAccess(form)); 331 } 332 333 return infos; 334 } 335 336 /** 337 * Get the form title 338 * @param formId the form id 339 * @return the form title 340 */ 341 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 342 public String getFormTitle(String formId) 343 { 344 Form form = _resolver.resolveById(formId); 345 return form.getTitle(); 346 } 347 348 /** 349 * Get the form full path 350 * @param formId the form id 351 * @return the form full path 352 */ 353 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 354 public String getFormFullPath(String formId) 355 { 356 Form form = _resolver.resolveById(formId); 357 358 String separator = " > "; 359 String fullPath = form.getTitle(); 360 361 FormDirectory parent = form.getParent(); 362 if (!_formDirectoryDAO.isRoot(parent)) 363 { 364 fullPath = _formDirectoryDAO.getFormDirectoryPath(parent, separator) + separator + fullPath; 365 } 366 367 return fullPath; 368 } 369 370 /** 371 * Get user rights for the given form 372 * @param form the form 373 * @return the set of rights 374 */ 375 protected Set<String> _getUserRights (Form form) 376 { 377 UserIdentity user = _userProvider.getUser(); 378 return _rightManager.getUserRights(user, form); 379 } 380 381 /** 382 * Creates a {@link Form}. 383 * @param siteName The site name 384 * @param parentId The id of the parent. 385 * @param name name The desired name for the new {@link Form} 386 * @return The id of the created form 387 * @throws Exception if an error occurs during the form creation process 388 */ 389 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 390 public Map<String, String> createForm (String siteName, String parentId, String name) throws Exception 391 { 392 Map<String, String> result = new HashMap<>(); 393 394 FormDirectory parentDirectory = _formDirectoryDAO.getFormDirectory(siteName, parentId); 395 _formDirectoryDAO.checkHandleFormDirectoriesRight(parentDirectory); 396 397 String uniqueName = NameHelper.getUniqueAmetysObjectName(parentDirectory, __FORM_NAME_PREFIX + name); 398 Form form = parentDirectory.createChild(uniqueName, FormFactory.FORM_NODETYPE); 399 400 form.setTitle(name); 401 form.setAuthor(_userProvider.getUser()); 402 form.setCreationDate(ZonedDateTime.now()); 403 form.setLastModificationDate(ZonedDateTime.now()); 404 405 parentDirectory.saveChanges(); 406 String formId = form.getId(); 407 408 _formPageDAO.createPage(formId, _i18nUtils.translate(new I18nizableText("plugin.forms", "PLUGINS_FORMS_CREATE_PAGE_DEFAULT_NAME"))); 409 410 result.put("id", formId); 411 result.put("name", form.getTitle()); 412 413 return result; 414 } 415 416 /** 417 * Rename a {@link Form} 418 * @param id The id of the form 419 * @param newName The new name for the form 420 * @return A result map 421 */ 422 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 423 public Map<String, String> renameForm (String id, String newName) 424 { 425 Map<String, String> results = new HashMap<>(); 426 427 Form form = _resolver.resolveById(id); 428 checkHandleFormRight(form); 429 430 String uniqueName = NameHelper.getUniqueAmetysObjectName(form.getParent(), __FORM_NAME_PREFIX + newName); 431 Node node = form.getNode(); 432 try 433 { 434 form.setTitle(newName); 435 form.setContributor(_userProvider.getUser()); 436 form.setLastModificationDate(ZonedDateTime.now()); 437 438 node.getSession().move(node.getPath(), node.getParent().getPath() + '/' + uniqueName); 439 node.getSession().save(); 440 441 results.put("newName", form.getTitle()); 442 } 443 catch (RepositoryException e) 444 { 445 getLogger().warn("Form renaming failed.", e); 446 results.put("message", "cannot-rename"); 447 } 448 449 results.put("id", id); 450 return results; 451 } 452 453 /** 454 * Copies and pastes a form. 455 * @param formDirectoryId The id of the form directory target of the copy 456 * @param formId The id of the form to copy 457 * @return The results 458 */ 459 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 460 public Map<String, String> copyForm(String formDirectoryId, String formId) 461 { 462 Map<String, String> result = new HashMap<>(); 463 464 Form originalForm = _resolver.resolveById(formId); 465 FormDirectory parentFormDirectory = _resolver.resolveById(formDirectoryId); 466 _formDirectoryDAO.checkHandleFormDirectoriesRight(parentFormDirectory); 467 468 String uniqueName = NameHelper.getUniqueAmetysObjectName(parentFormDirectory, __FORM_NAME_PREFIX + originalForm.getTitle()); 469 470 Form cForm = originalForm.copyTo(parentFormDirectory, uniqueName); 471 originalForm.copyTo(cForm); 472 473 String copyTitle = _i18nUtils.translate(new I18nizableText("plugin.forms", "PLUGIN_FORMS_TREE_COPY_NAME_PREFIX")) + originalForm.getTitle(); 474 cForm.setTitle(copyTitle); 475 cForm.setAuthor(_userProvider.getUser()); 476 cForm.setCreationDate(ZonedDateTime.now()); 477 cForm.setLastModificationDate(ZonedDateTime.now()); 478 479 for (String epId : _copyFormEP.getExtensionsIds()) 480 { 481 CopyFormUpdater copyFormUpdater = _copyFormEP.getExtension(epId); 482 copyFormUpdater.updateForm(originalForm, cForm); 483 } 484 485 result.put("id", cForm.getId()); 486 487 return result; 488 } 489 490 /** 491 * Deletes a {@link Form}. 492 * @param id The id of the form to delete 493 * @return The id of the form 494 */ 495 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 496 public Map<String, String> deleteForm (String id) 497 { 498 Map<String, String> result = new HashMap<>(); 499 500 Form form = _resolver.resolveById(id); 501 checkHandleFormRight(form); 502 503 List<SitemapElement> pages = getFormPage(form.getId(), form.getSiteName()); 504 if (!pages.isEmpty()) 505 { 506 throw new AccessDeniedException("Can't delete form ('" + form.getId() + "') which contains pages"); 507 } 508 509 ModifiableAmetysObject parent = form.getParent(); 510 form.remove(); 511 parent.saveChanges(); 512 513 result.put("id", id); 514 return result; 515 } 516 517 /** 518 * Moves a {@link Form} 519 * @param siteName name of the site 520 * @param id The id of the form 521 * @param newParentId The id of the new parent directory of the form. 522 * @return A result map 523 */ 524 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 525 public Map<String, Object> moveForm(String siteName, String id, String newParentId) 526 { 527 Map<String, Object> results = new HashMap<>(); 528 Form form = _resolver.resolveById(id); 529 FormDirectory directory = _formDirectoryDAO.getFormDirectory(siteName, newParentId); 530 531 if (hasWriteRightOnForm(_userProvider.getUser(), form) && _formDirectoryDAO.hasWriteRightOnFormDirectory(_userProvider.getUser(), directory)) 532 { 533 _formDirectoryDAO.move(form, siteName, newParentId, results); 534 } 535 else 536 { 537 results.put("message", "not-allowed"); 538 } 539 540 results.put("id", form.getId()); 541 return results; 542 } 543 544 /** 545 * Change workflow of a {@link Form} 546 * @param formId The id of the form 547 * @param workflowName The name of new workflow 548 * @return A result map 549 */ 550 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 551 public Map<String, String> setWorkflow (String formId, String workflowName) 552 { 553 Map<String, String> results = new HashMap<>(); 554 555 Form form = _resolver.resolveById(formId); 556 checkHandleFormRight(form); 557 558 form.setWorkflowName(workflowName); 559 form.setContributor(_userProvider.getUser()); 560 form.setLastModificationDate(ZonedDateTime.now()); 561 562 form.saveChanges(); 563 564 results.put("id", formId); 565 566 Map<String, Object> eventParams = new HashMap<>(); 567 eventParams.put("form", form); 568 _observationManager.notify(new Event(FormEvents.FORM_MODIFIED, _currentUserProvider.getUser(), eventParams)); 569 570 return results; 571 } 572 573 /** 574 * Get the submission date of the last entry to the form 575 * @param entries A list of form entry ordered by submission date 576 * @return the date of the last submission 577 */ 578 protected ZonedDateTime _getLastSubmissionDate(List<FormEntry> entries) 579 { 580 return entries.isEmpty() ? null : entries.get(0).getSubmitDate(); 581 } 582 583 /** 584 * Get all zone items which contains the form 585 * @param formId the form id 586 * @param siteName the site name 587 * @return the zone items 588 */ 589 public AmetysObjectIterable<ModifiableZoneItem> getFormZoneItems(String formId, String siteName) 590 { 591 String xpathQuery = "//element(" + siteName + ", ametys:site)//element(*, ametys:zoneItem)[@ametys-internal:service = 'org.ametys.forms.service.Display' and ametys:service_parameters/@ametys:formId = '" + formId + "']"; 592 return _resolver.query(xpathQuery); 593 } 594 595 /** 596 * Get the locale to use for a given form 597 * @param form the form 598 * @return the locale to use, can be null if the form is not published on a page 599 */ 600 public String getFormLocale(Form form) 601 { 602 List<SitemapElement> zoneItems = getFormPage(form.getId(), form.getSiteName()); 603 604 return zoneItems.stream() 605 .findFirst() 606 .map(SitemapElement::getSitemapName) 607 .orElse(null); 608 } 609 610 /** 611 * Get all the page where the form is published 612 * @param formId the form id 613 * @param siteName the site name 614 * @return the list of page 615 */ 616 public List<SitemapElement> getFormPage(String formId, String siteName) 617 { 618 AmetysObjectIterable<ModifiableZoneItem> zoneItems = getFormZoneItems(formId, siteName); 619 620 return zoneItems.stream() 621 .map(z -> z.getZone().getSitemapElement()) 622 .collect(Collectors.toList()); 623 } 624 625 /** 626 * Get the page names 627 * @param pages the list of page 628 * @return the list of page name 629 */ 630 protected List<Map<String, Object>> _getPagesInfos(List<SitemapElement> pages) 631 { 632 List<Map<String, Object>> pagesInfos = new ArrayList<>(); 633 for (SitemapElement sitemapElement : pages) 634 { 635 Map<String, Object> info = new HashMap<>(); 636 info.put("id", sitemapElement.getId()); 637 info.put("title", sitemapElement.getTitle()); 638 info.put("isPage", sitemapElement instanceof Page); 639 640 pagesInfos.add(info); 641 } 642 643 return pagesInfos; 644 } 645 646 /** 647 * Get all the view available for the form display service 648 * @param formId the form identifier 649 * @param siteName the site name 650 * @param language the language 651 * @return the views as json 652 * @throws Exception if an error occurred 653 */ 654 @Callable (rights = Callable.SKIP_BUILTIN_CHECK) 655 public List<Map<String, Object>> getFormDisplayViews(String formId, String siteName, String language) throws Exception 656 { 657 List<Map<String, Object>> jsonifiedViews = new ArrayList<>(); 658 659 Service service = _serviceEP.getExtension("org.ametys.forms.service.Display"); 660 ElementDefinition viewElementDefinition = (ElementDefinition) service.getParameters().getOrDefault(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME, null); 661 662 String xpathQuery = "//element(" + siteName + ", ametys:site)/ametys-internal:sitemaps/" + language 663 + "//element(*, ametys:zoneItem)[@ametys-internal:service = 'org.ametys.forms.service.Display' and ametys:service_parameters/@ametys:formId = '" + formId + "']"; 664 AmetysObjectIterable<ModifiableZoneItem> zoneItems = _resolver.query(xpathQuery); 665 666 Optional<Object> existedServiceView = zoneItems.stream() 667 .map(ZoneItem::getServiceParameters) 668 .map(sp -> sp.getValue(ViewParametersManager.SERVICE_VIEW_DEFAULT_MODEL_ITEM_NAME)) 669 .findFirst(); 670 671 Map<String, I18nizableText> typedEntries = viewElementDefinition.getEnumerator().getEntries(); 672 for (String id : typedEntries.keySet()) 673 { 674 Map<String, Object> viewAsJson = new HashMap<>(); 675 viewAsJson.put("id", id); 676 viewAsJson.put("label", typedEntries.get(id)); 677 678 Boolean isServiceView = existedServiceView.map(s -> s.equals(id)).orElse(false); 679 if (isServiceView || existedServiceView.isEmpty() && id.equals(viewElementDefinition.getDefaultValue())) 680 { 681 viewAsJson.put("isDefault", true); 682 } 683 684 jsonifiedViews.add(viewAsJson); 685 } 686 687 return jsonifiedViews; 688 } 689 690 /** 691 * <code>true</code> if the form is well configured 692 * @param form the form 693 * @return <code>true</code> if the form is well configured 694 */ 695 public boolean isFormConfigured(Form form) 696 { 697 List<FormQuestion> questions = form.getQuestions(); 698 return !questions.isEmpty() && !questions.stream().anyMatch(q -> !q.getType().isQuestionConfigured(q)); 699 } 700 701 /** 702 * Get the dashboard URI 703 * @param siteName the site name 704 * @return the dashboard URI 705 */ 706 public String getDashboardUri(String siteName) 707 { 708 String xpathQuery = "//element(" + siteName + ", ametys:site)//element(*, ametys:zoneItem)[@ametys-internal:service = 'org.ametys.plugins.forms.workflow.service.dashboard']"; 709 AmetysObjectIterable<ModifiableZoneItem> zoneItems = _resolver.query(xpathQuery); 710 711 Optional<Page> dashboardPage = zoneItems.stream() 712 .map(z -> z.getZone().getSitemapElement()) 713 .filter(Page.class::isInstance) 714 .map(Page.class::cast) 715 .findAny(); 716 717 if (dashboardPage.isPresent()) 718 { 719 Page page = dashboardPage.get(); 720 String pagePath = page.getSitemap().getName() + "/" + page.getPathInSitemap() + ".html"; 721 722 String url = _siteManager.getSite(siteName).getUrl(); 723 return url + "/" + pagePath; 724 } 725 726 return StringUtils.EMPTY; 727 } 728 729 /** 730 * Get the admin dashboard URI 731 * @param siteName the site name 732 * @return the admin dashboard URI 733 */ 734 public String getAdminDashboardUri(String siteName) 735 { 736 String xpathQuery = "//element(" + siteName + ", ametys:site)//element(*, ametys:zoneItem)[@ametys-internal:service = 'org.ametys.plugins.forms.workflow.service.admin.dashboard']"; 737 AmetysObjectIterable<ModifiableZoneItem> zoneItems = _resolver.query(xpathQuery); 738 739 Optional<Page> dashboardPage = zoneItems.stream() 740 .map(z -> z.getZone().getSitemapElement()) 741 .filter(Page.class::isInstance) 742 .map(Page.class::cast) 743 .findAny(); 744 745 if (dashboardPage.isPresent()) 746 { 747 Page page = dashboardPage.get(); 748 String pagePath = page.getSitemap().getName() + "/" + page.getPathInSitemap() + ".html"; 749 750 String url = _siteManager.getSite(siteName).getUrl(); 751 return url + "/" + pagePath; 752 } 753 754 return StringUtils.EMPTY; 755 } 756 757 /** 758 * Get the form expiration values 759 * @param formId the id of the form 760 * @return the values as a JSON map for edition 761 */ 762 @Callable (rights = HANDLE_FORMS_RIGHT_ID, rightContext = FormsDirectoryRightAssignmentContext.ID, paramIndex = 0) 763 public Map<String, Object> getFormExpiration(String formId) 764 { 765 Form form = _resolver.resolveById(formId); 766 767 return Map.of(Form.EXPIRATION_ENABLED, form.isExpirationEnabled(), 768 Form.EXPIRATION_PERIOD, form.getExpirationPeriod(), 769 Form.EXPIRATION_POLICY, form.getExpirationPolicy().name()); 770 } 771 772 /** 773 * Set the form expiration policy 774 * @param formId the id of the form 775 * @param expirationPeriod the expiration period in months 776 * @param expirationPolicy the expiration policy 777 */ 778 @Callable (rights = HANDLE_FORMS_RIGHT_ID, rightContext = FormsDirectoryRightAssignmentContext.ID, paramIndex = 0) 779 public void setExpirationPolicy(String formId, long expirationPeriod, String expirationPolicy) 780 { 781 Form form = _resolver.resolveById(formId); 782 783 form.setExpirationPolicy(true, expirationPeriod, ExpirationPolicy.valueOf(expirationPolicy)); 784 form.saveChanges(); 785 786 _observationManager.notify(new Event(FormEvents.FORM_MODIFIED, _currentUserProvider.getUser(), Map.of("form", form))); 787 } 788 789 /** 790 * Remove the form expiration policy 791 * @param formId the id of the form 792 */ 793 public void removeExpirationPolicy(String formId) 794 { 795 Form form = _resolver.resolveById(formId); 796 797 form.setExpirationPolicy(false, -1, null); 798 form.saveChanges(); 799 800 _observationManager.notify(new Event(FormEvents.FORM_MODIFIED, _currentUserProvider.getUser(), Map.of("form", form))); 801 } 802 803}