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