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