001/* 002 * Copyright 2010 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.jcr; 017 018import java.util.ArrayList; 019import java.util.Collection; 020import java.util.HashMap; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028import javax.jcr.Node; 029import javax.jcr.NodeIterator; 030import javax.jcr.Property; 031import javax.jcr.PropertyIterator; 032import javax.jcr.Repository; 033import javax.jcr.RepositoryException; 034import javax.jcr.Session; 035import javax.jcr.Value; 036import javax.jcr.query.Query; 037import javax.jcr.query.QueryManager; 038 039import org.apache.avalon.framework.component.Component; 040import org.apache.avalon.framework.logger.AbstractLogEnabled; 041import org.apache.avalon.framework.service.ServiceException; 042import org.apache.avalon.framework.service.ServiceManager; 043import org.apache.avalon.framework.service.Serviceable; 044import org.apache.commons.lang.StringUtils; 045import org.apache.jackrabbit.JcrConstants; 046 047import org.ametys.cms.FilterNameHelper; 048import org.ametys.cms.repository.Content; 049import org.ametys.cms.repository.DefaultContent; 050import org.ametys.plugins.forms.Field; 051import org.ametys.plugins.forms.Field.FieldType; 052import org.ametys.plugins.forms.Form; 053import org.ametys.plugins.forms.FormsException; 054import org.ametys.plugins.repository.AmetysObjectResolver; 055import org.ametys.plugins.repository.RepositoryConstants; 056import org.ametys.plugins.repository.jcr.JCRAmetysObject; 057import org.ametys.plugins.repository.provider.AbstractRepository; 058import org.ametys.runtime.plugin.component.PluginAware; 059import org.ametys.web.repository.site.SiteManager; 060 061/** 062 * Form properties manager : stores and retrieves form properties. 063 */ 064public class FormPropertiesManager extends AbstractLogEnabled implements Serviceable, Component, PluginAware 065{ 066 /** Pattern for options value */ 067 public static final Pattern OPTION_VALUE_PATTERN = Pattern.compile("^option-([0-9]+)-value$"); 068 069 /** The avalon component ROLE. */ 070 public static final String ROLE = FormPropertiesManager.class.getName(); 071 072 /** JCR relative path to root node. */ 073 public static final String ROOT_REPO = AmetysObjectResolver.ROOT_REPO; 074 075 /** Plugins root node name. */ 076 public static final String PLUGINS_NODE = "ametys-internal:plugins"; 077 078 /** Forms node name. */ 079 public static final String FORMS_NODE = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":forms"; 080 081 /** Language property */ 082 public static final String LANGUAGE_PROPERTY = RepositoryConstants.NAMESPACE_PREFIX_INTERNAL + ":language"; 083 084 /** Site property */ 085 public static final String SITE_PROPERTY = RepositoryConstants.NAMESPACE_PREFIX + ":site"; 086 087 /** "ID" property name. */ 088 public static final String FORM_PROPERTY_ID = RepositoryConstants.NAMESPACE_PREFIX + ":id"; 089 090 /** "Label" property name. */ 091 public static final String FORM_PROPERTY_LABEL = RepositoryConstants.NAMESPACE_PREFIX + ":label"; 092 093 /** "Receipt field ID" property name. */ 094 public static final String FORM_PROPERTY_RECEIPT_FIELD_ID = RepositoryConstants.NAMESPACE_PREFIX + ":receipt-field-id"; 095 096 /** "Receipt field ID" property name. */ 097 public static final String FORM_PROPERTY_RECEIPT_FROM_ADDRESS = RepositoryConstants.NAMESPACE_PREFIX + ":receipt-from-address"; 098 099 /** "Receipt field ID" property name. */ 100 public static final String FORM_PROPERTY_RECEIPT_SUBJECT = RepositoryConstants.NAMESPACE_PREFIX + ":receipt-subject"; 101 102 /** "Receipt field ID" property name. */ 103 public static final String FORM_PROPERTY_RECEIPT_BODY = RepositoryConstants.NAMESPACE_PREFIX + ":receipt-body"; 104 105 /** The uuid of the page where to redirect to */ 106 public static final String FORM_PROPERTY_REDIRECT_TO = RepositoryConstants.NAMESPACE_PREFIX + ":redirect-to"; 107 108 /** "Emails" property name. */ 109 public static final String FORM_PROPERTY_EMAILS = RepositoryConstants.NAMESPACE_PREFIX + ":notification-emails"; 110 111 /** "Workflow name" property name. */ 112 public static final String FORM_PROPERTY_WORKFLOW_NAME = RepositoryConstants.NAMESPACE_PREFIX + ":workflow-name"; 113 114 /** "ID" field property name. */ 115 public static final String FIELD_PROPERTY_ID = RepositoryConstants.NAMESPACE_PREFIX + ":id"; 116 117 /** "Type" field property name. */ 118 public static final String FIELD_PROPERTY_TYPE = RepositoryConstants.NAMESPACE_PREFIX + ":type"; 119 120 /** "Name" field property name. */ 121 public static final String FIELD_PROPERTY_NAME = RepositoryConstants.NAMESPACE_PREFIX + ":name"; 122 123 /** "Label" field property name. */ 124 public static final String FIELD_PROPERTY_LABEL = RepositoryConstants.NAMESPACE_PREFIX + ":label"; 125 126 /** Field properties prefix. */ 127 public static final String FIELD_PROPERTY_PREFIX = RepositoryConstants.NAMESPACE_PREFIX + ":property-"; 128 129 /** The JCR repository. */ 130 protected Repository _repository; 131 132 /** The Site manager. */ 133 protected SiteManager _siteManager; 134 135 /** The resolver for ametys objects */ 136 protected AmetysObjectResolver _resolver; 137 138 /** The plugin name. */ 139 protected String _pluginName; 140 141 @Override 142 public void service(ServiceManager serviceManager) throws ServiceException 143 { 144 _repository = (Repository) serviceManager.lookup(AbstractRepository.ROLE); 145 _siteManager = (SiteManager) serviceManager.lookup(SiteManager.ROLE); 146 _resolver = (AmetysObjectResolver) serviceManager.lookup(AmetysObjectResolver.ROLE); 147 } 148 149 @Override 150 public void setPluginInfo(String pluginName, String featureName, String id) 151 { 152 _pluginName = pluginName; 153 } 154 155 /** 156 * Get a form from the repository. 157 * @param id the form ID. 158 * @return the Form or null if no form with this ID exists. 159 * @throws FormsException if an error occurs. 160 */ 161 public Form getForm(String id) throws FormsException 162 { 163 return getForm(null, id); 164 } 165 166 /** 167 * Get a form from the repository. 168 * @param siteName the site name. 169 * @param id the form ID. 170 * @return the Form or null if no form with this ID exists. 171 * @throws FormsException if an error occurs. 172 */ 173 public Form getForm(String siteName, String id) throws FormsException 174 { 175 Session session = null; 176 try 177 { 178 session = _repository.login(); 179 180 Form form = null; 181 182 // Build the query 183 String xpathQuery = "//element(*, ametys:content)"; 184 if (siteName != null) 185 { 186 xpathQuery += "[@" + SITE_PROPERTY + "='" + siteName + "']"; 187 } 188 xpathQuery += "/" + FORMS_NODE + "/*[@" + FORM_PROPERTY_ID + " = '" + id + "']"; 189 190 QueryManager queryManager = session.getWorkspace().getQueryManager(); 191 @SuppressWarnings("deprecation") 192 Query query = queryManager.createQuery(xpathQuery, Query.XPATH); 193 NodeIterator nodeIterator = query.execute().getNodes(); 194 195 if (nodeIterator.hasNext()) 196 { 197 Node node = nodeIterator.nextNode(); 198 199 form = _extractForm(node); 200 } 201 202 return form; 203 } 204 catch (RepositoryException e) 205 { 206 throw new FormsException("Error executing the query to find the form of id " + id, e); 207 } 208 finally 209 { 210 if (session != null) 211 { 212 session.logout(); 213 } 214 } 215 } 216 217 /** 218 * Get the most recent frozen node that contain the form of the given id 219 * @param formId the id of the form 220 * @return the list of frozen content nodes containing the form 221 * @throws FormsException if an error occurs while retrieving the forms' frozen content nodes 222 */ 223 public Node getMostRecentFormFrozenContent(String formId) throws FormsException 224 { 225 Session session = null; 226 try 227 { 228 session = _repository.login(); 229 230 String xpathQuery = "//element(" + formId + ", nt:frozenNode)/../.. order by @" + RepositoryConstants.NAMESPACE_PREFIX + ":" + DefaultContent.METADATA_MODIFIED + " descending"; 231 232 QueryManager queryManager = session.getWorkspace().getQueryManager(); 233 @SuppressWarnings("deprecation") 234 Query query = queryManager.createQuery(xpathQuery, Query.XPATH); 235 NodeIterator nodeIterator = query.execute().getNodes(); 236 237 if (nodeIterator.hasNext()) 238 { 239 return (Node) nodeIterator.next(); 240 } 241 242 return null; 243 } 244 catch (RepositoryException e) 245 { 246 throw new FormsException("Error executing the query to find the frozen content nodes for the form '" + formId + "'.", e); 247 } 248 finally 249 { 250 if (session != null) 251 { 252 session.logout(); 253 } 254 } 255 } 256 257 /** 258 * Get the forms in a specified content. 259 * @param content the content. 260 * @return the forms as a list. 261 * @throws FormsException if an error occurs. 262 */ 263 public List<Form> getForms(Content content) throws FormsException 264 { 265 Session session = null; 266 try 267 { 268 List<Form> forms = new ArrayList<>(); 269 270 session = _repository.login(); 271 272 // FIXME API getNode should be VersionableAmetysObject 273 if (content instanceof JCRAmetysObject) 274 { 275 Node contentNode = ((JCRAmetysObject) content).getNode(); 276 277 if (contentNode.hasNode(FORMS_NODE)) 278 { 279 Node formsNode = contentNode.getNode(FORMS_NODE); 280 281 NodeIterator nodes = formsNode.getNodes(); 282 283 while (nodes.hasNext()) 284 { 285 Node node = nodes.nextNode(); 286 287 Form form = _extractForm(node); 288 289 if (form != null) 290 { 291 forms.add(form); 292 } 293 } 294 } 295 } 296 return forms; 297 } 298 catch (RepositoryException e) 299 { 300 getLogger().error("Error getting forms for a content.", e); 301 throw new FormsException("Error getting forms for a content.", e); 302 } 303 finally 304 { 305 if (session != null) 306 { 307 session.logout(); 308 } 309 } 310 } 311 312 /** 313 * Get all the contents containing at least one form of the given site with the given language 314 * @param siteName the site name. 315 * @param language the language 316 * @return the forms' list or null if none was found 317 * @throws FormsException if an error occurs. 318 */ 319 public List<Node> getFormContentNodes(String siteName, String language) throws FormsException 320 { 321 List<Node> contentNodes = new ArrayList<> (); 322 Session session = null; 323 try 324 { 325 session = _repository.login(); 326 327 String xpathQuery = "//element(*, ametys:content)[@" + SITE_PROPERTY + " = '" + siteName + "' and @" + LANGUAGE_PROPERTY + " = '" + language + "']/" + FORMS_NODE; 328 329 QueryManager queryManager = session.getWorkspace().getQueryManager(); 330 @SuppressWarnings("deprecation") 331 Query query = queryManager.createQuery(xpathQuery, Query.XPATH); 332 NodeIterator nodeIterator = query.execute().getNodes(); 333 334 while (nodeIterator.hasNext()) 335 { 336 Node formNode = nodeIterator.nextNode(); 337 contentNodes.add(formNode.getParent()); 338 } 339 340 return contentNodes; 341 } 342 catch (RepositoryException e) 343 { 344 throw new FormsException("Error executing the query to find the forms of the site '" + siteName + "' and of language '" + language + "'.", e); 345 } 346 finally 347 { 348 if (session != null) 349 { 350 session.logout(); 351 } 352 } 353 } 354 355 /** 356 * Get the content containing the form with the given id 357 * @param formId the id of the form 358 * @return the {@link Content} containing the form or <code>null</code> if not found 359 * @throws FormsException if something goes wrong when either querying the form JCR node or finding its parent {@link Content} 360 */ 361 public Content getFormContent(String formId) throws FormsException 362 { 363 Session session = null; 364 try 365 { 366 session = _repository.login(); 367 368 // Build the query 369 String xpathQuery = "//element(*, ametys:content)/" + FORMS_NODE + "/*[@" + FORM_PROPERTY_ID + " = '" + formId + "']"; 370 371 // Execute 372 QueryManager queryManager = session.getWorkspace().getQueryManager(); 373 @SuppressWarnings("deprecation") 374 Query query = queryManager.createQuery(xpathQuery, Query.XPATH); 375 NodeIterator nodeIterator = query.execute().getNodes(); 376 377 if (nodeIterator.hasNext()) 378 { 379 Node node = nodeIterator.nextNode().getParent().getParent(); 380 Content content = (Content) _resolver.resolve(node, false); 381 return content; 382 } 383 384 return null; 385 } 386 catch (RepositoryException e) 387 { 388 throw new FormsException("Error executing the query to find the content containing the form of id " + formId, e); 389 } 390 finally 391 { 392 if (session != null) 393 { 394 session.logout(); 395 } 396 } 397 398 } 399 400 401 /** 402 * Extract all the form objects from a node 403 * @param node the node 404 * @return the forms list of this node 405 * @throws FormsException if an error occurs 406 * @throws RepositoryException if an error occurs when getting the properties of a node 407 */ 408 public List<Form> getForms(Node node) throws FormsException, RepositoryException 409 { 410 List<Form> forms = new ArrayList<> (); 411 try 412 { 413 if (node.hasNode(FORMS_NODE)) 414 { 415 Node formsNode = node.getNode(FORMS_NODE); 416 if (formsNode != null) 417 { 418 NodeIterator formsNodeIterator = formsNode.getNodes(); 419 while (formsNodeIterator.hasNext()) 420 { 421 Node formNode = formsNodeIterator.nextNode(); 422 Form form = _extractForm(formNode); 423 if (form != null) 424 { 425 forms.add(form); 426 } 427 } 428 } 429 } 430 431 return forms; 432 } 433 catch (RepositoryException e) 434 { 435 throw new FormsException("Error executing the query to find the forms of the node '" + node.getName() + "' (" + node.getIdentifier() + ").", e); 436 } 437 } 438 439 /** 440 * Store the properties of a form in the repository. 441 * @param siteName the site name. 442 * @param form the form object. 443 * @param content the form content. 444 * @throws FormsException if an error occurs storing the form. 445 */ 446 public void createForm(String siteName, Form form, Content content) throws FormsException 447 { 448 try 449 { 450 // FIXME API getNode should be VersionableAmetysObject 451 if (content instanceof JCRAmetysObject) 452 { 453 Node contentNode = ((JCRAmetysObject) content).getNode(); 454 455 Node formsNode = _createOrGetFormsNode(contentNode); 456 457 Node formNode = _storeForm(formsNode, form); 458 459 _fillFormNode(formNode, form); 460 461 for (Field field : form.getFields()) 462 { 463 Node fieldNode = _storeField(formNode, field); 464 _fillFieldNode(fieldNode, field); 465 } 466 467 contentNode.getSession().save(); 468 } 469 } 470 catch (RepositoryException e) 471 { 472 throw new FormsException("Repository exception while storing the form properties.", e); 473 } 474 } 475 476 /** 477 * Update the properties of a form in the repository. 478 * @param siteName the site name. 479 * @param form the form object. 480 * @param content the form content. 481 * @throws FormsException if an error occurs storing the form. 482 */ 483 public void updateForm(String siteName, Form form, Content content) throws FormsException 484 { 485 Session session = null; 486 try 487 { 488 session = _repository.login(); 489 490 String id = form.getId(); 491 492 // FIXME API getNode should be VersionableAmetysObject 493 if (content instanceof JCRAmetysObject) 494 { 495 String xpathQuery = "//element(*, ametys:content)/" + FORMS_NODE + "/*[@" + FORM_PROPERTY_ID + " = '" + id + "']"; 496 497 QueryManager queryManager = session.getWorkspace().getQueryManager(); 498 @SuppressWarnings("deprecation") 499 Query query = queryManager.createQuery(xpathQuery, Query.XPATH); 500 NodeIterator nodeIterator = query.execute().getNodes(); 501 502 if (nodeIterator.hasNext()) 503 { 504 Node formNode = nodeIterator.nextNode(); 505 506 _fillFormNode(formNode, form); 507 508 _updateFields(form, formNode); 509 510 if (session.hasPendingChanges()) 511 { 512 session.save(); 513 } 514 } 515 } 516 } 517 catch (RepositoryException e) 518 { 519 throw new FormsException("Repository exception while storing the form properties.", e); 520 } 521 finally 522 { 523 if (session != null) 524 { 525 session.logout(); 526 } 527 } 528 } 529 530 /** 531 * Get the value for display 532 * @param field The field 533 * @param value The value 534 * @return The value to display 535 */ 536 public String getDisplayValue (Field field, String value) 537 { 538 Map<String, String> properties = field.getProperties(); 539 for (String key : properties.keySet()) 540 { 541 Matcher matcher = OPTION_VALUE_PATTERN.matcher(key); 542 if (matcher.matches()) 543 { 544 if (value.equals(properties.get(key))) 545 { 546 String index = matcher.group(1); 547 if (properties.containsKey("option-" + index + "-label")) 548 { 549 return properties.get("option-" + index + "-label"); 550 } 551 } 552 } 553 } 554 return value; 555 } 556 557 /** 558 * Extracts a form from a JCR Node. 559 * @param formNode the form node. 560 * @return the Form object. 561 * @throws RepositoryException if a repository error occurs. 562 */ 563 protected Form _extractForm(Node formNode) throws RepositoryException 564 { 565 Form form = null; 566 567 String id = _getSingleProperty(formNode, FORM_PROPERTY_ID, ""); 568 if (!StringUtils.isEmpty(id)) 569 { 570 String label = _getSingleProperty(formNode, FORM_PROPERTY_LABEL, ""); 571 String receiptFieldId = _getSingleProperty(formNode, FORM_PROPERTY_RECEIPT_FIELD_ID, ""); 572 String receiptFieldBody = _getSingleProperty(formNode, FORM_PROPERTY_RECEIPT_BODY, ""); 573 String receiptFieldSubject = _getSingleProperty(formNode, FORM_PROPERTY_RECEIPT_SUBJECT, ""); 574 String receiptFieldFromAddress = _getSingleProperty(formNode, FORM_PROPERTY_RECEIPT_FROM_ADDRESS, ""); 575 String redirectTo = _getSingleProperty(formNode, FORM_PROPERTY_REDIRECT_TO, ""); 576 Collection<String> emails = _getMultipleProperty(formNode, FORM_PROPERTY_EMAILS); 577 String workflowName = _getSingleProperty(formNode, FORM_PROPERTY_WORKFLOW_NAME, ""); 578 579 form = new Form(); 580 581 form.setId(id); 582 form.setLabel(label); 583 form.setReceiptFieldId(receiptFieldId); 584 form.setReceiptFieldBody(receiptFieldBody); 585 form.setReceiptFieldSubject(receiptFieldSubject); 586 form.setReceiptFieldFromAddress(receiptFieldFromAddress); 587 form.setNotificationEmails(new HashSet<>(emails)); 588 form.setRedirectTo(redirectTo); 589 form.setWorkflowName(workflowName); 590 591 _extractFields(formNode, form); 592 } 593 594 return form; 595 } 596 597 /** 598 * Extracts a form from a JCR Node. 599 * @param formNode the form node. 600 * @param form the form object. 601 * @throws RepositoryException if a repository error occurs. 602 */ 603 protected void _extractFields(Node formNode, Form form) throws RepositoryException 604 { 605 NodeIterator nodes = formNode.getNodes(); 606 while (nodes.hasNext()) 607 { 608 Node node = nodes.nextNode(); 609 610 Field field = _extractField(node); 611 612 form.getFields().add(field); 613 } 614 } 615 616 /** 617 * Extracts a field from a JCR Node. 618 * @param fieldNode the field node. 619 * @return the Field object. 620 * @throws RepositoryException if a repository error occurs. 621 */ 622 protected Field _extractField(Node fieldNode) throws RepositoryException 623 { 624 Field field = null; 625 626 String id = _getSingleProperty(fieldNode, FIELD_PROPERTY_ID, ""); 627 String type = _getSingleProperty(fieldNode, FIELD_PROPERTY_TYPE, ""); 628 629 // TODO Try/catch in case of enum name not found. 630 FieldType fieldType = FieldType.valueOf(type); 631 632 if (!StringUtils.isEmpty(id) && !StringUtils.isEmpty(type)) 633 { 634 String name = _getSingleProperty(fieldNode, FIELD_PROPERTY_NAME, ""); 635 String label = _getSingleProperty(fieldNode, FIELD_PROPERTY_LABEL, ""); 636 Map<String, String> properties = _getFieldProperties(fieldNode); 637 638 field = new Field(fieldType); 639 640 field.setId(id); 641 field.setName(name); 642 field.setLabel(label); 643 field.setProperties(properties); 644 } 645 646 return field; 647 } 648 649 /** 650 * Persist the form in a repository node. 651 * @param contentNode the content node in which the form is to be stored. 652 * @param form the form object to persist. 653 * @return the newly created form node. 654 * @throws RepositoryException if a repository error occurs while filling the node. 655 */ 656 protected Node _storeForm(Node contentNode, Form form) throws RepositoryException 657 { 658 String name = form.getId(); 659 if (StringUtils.isBlank(name)) 660 { 661 name = "form"; 662 } 663 664 String nodeName = FilterNameHelper.filterName(name); 665 String notExistingNodeName = _getNotExistingNodeName(contentNode, nodeName); 666 667 Node formNode = contentNode.addNode(notExistingNodeName); 668 formNode.addMixin(JcrConstants.MIX_REFERENCEABLE); 669 670 return formNode; 671 } 672 673 /** 674 * Fill a form node. 675 * @param formNode the form node. 676 * @param form the form object. 677 * @throws RepositoryException if a repository error occurs while filling the node. 678 * @throws FormsException if a forms error occurs while filling the node. 679 */ 680 protected void _fillFormNode(Node formNode, Form form) throws RepositoryException, FormsException 681 { 682 Set<String> emails = form.getNotificationEmails(); 683 String[] emailArray = emails.toArray(new String[emails.size()]); 684 685 formNode.setProperty(FORM_PROPERTY_ID, form.getId()); 686 formNode.setProperty(FORM_PROPERTY_LABEL, form.getLabel()); 687 formNode.setProperty(FORM_PROPERTY_RECEIPT_FIELD_ID, form.getReceiptFieldId()); 688 formNode.setProperty(FORM_PROPERTY_RECEIPT_BODY, form.getReceiptFieldBody()); 689 formNode.setProperty(FORM_PROPERTY_RECEIPT_SUBJECT, form.getReceiptFieldSubject()); 690 formNode.setProperty(FORM_PROPERTY_RECEIPT_FROM_ADDRESS, form.getReceiptFieldFromAddress()); 691 formNode.setProperty(FORM_PROPERTY_EMAILS, emailArray); 692 formNode.setProperty(FORM_PROPERTY_REDIRECT_TO, form.getRedirectTo()); 693 formNode.setProperty(FORM_PROPERTY_WORKFLOW_NAME, form.getWorkflowName()); 694 } 695 696 /** 697 * Store a field node. 698 * @param formNode the form node. 699 * @param field the field. 700 * @return the newly created field node. 701 * @throws RepositoryException if a repository error occurs while filling the node. 702 * @throws FormsException if a forms error occurs while filling the node. 703 */ 704 protected Node _storeField(Node formNode, Field field) throws RepositoryException, FormsException 705 { 706 String name = field.getId(); 707 if (StringUtils.isBlank(name)) 708 { 709 name = "field"; 710 } 711 712 String nodeName = FilterNameHelper.filterName(name); 713 String notExistingNodeName = _getNotExistingNodeName(formNode, nodeName); 714 715 Node fieldNode = formNode.addNode(notExistingNodeName); 716 717 fieldNode.addMixin(JcrConstants.MIX_REFERENCEABLE); 718 719 return fieldNode; 720 } 721 722 /** 723 * Fill a field node. 724 * @param fieldNode the field node. 725 * @param field the field object. 726 * @throws RepositoryException if a repository error occurs while filling the node. 727 * @throws FormsException if a forms error occurs while filling the node. 728 */ 729 protected void _fillFieldNode(Node fieldNode, Field field) throws RepositoryException, FormsException 730 { 731 fieldNode.setProperty(FIELD_PROPERTY_ID, field.getId()); 732 fieldNode.setProperty(FIELD_PROPERTY_TYPE, field.getType().toString()); 733 fieldNode.setProperty(FIELD_PROPERTY_NAME, field.getName()); 734 fieldNode.setProperty(FIELD_PROPERTY_LABEL, field.getLabel()); 735 736 Map<String, String> fieldProperties = field.getProperties(); 737 for (String propertyName : fieldProperties.keySet()) 738 { 739 String value = fieldProperties.get(propertyName); 740 if (value != null) 741 { 742 String name = FIELD_PROPERTY_PREFIX + propertyName; 743 fieldNode.setProperty(name, value); 744 } 745 } 746 } 747 /** 748 * Update the field nodes of a form. 749 * @param form the new form object. 750 * @param formNode the node of the form to update. 751 * @throws RepositoryException if a repository error occurs while updating the fields. 752 * @throws FormsException if a forms error occurs while updating the fields. 753 */ 754 protected void _updateFields(Form form, Node formNode) throws RepositoryException, FormsException 755 { 756 Map<String, Field> fieldMap = form.getFieldMap(); 757 758 NodeIterator fieldNodes = formNode.getNodes(); 759 760 while (fieldNodes.hasNext()) 761 { 762 Node fieldNode = fieldNodes.nextNode(); 763 if (fieldNode.hasProperty(FIELD_PROPERTY_ID)) 764 { 765 String fieldId = fieldNode.getProperty(FIELD_PROPERTY_ID).getString(); 766 767 // The field still exist in the new form : update its properties. 768 if (fieldMap.containsKey(fieldId)) 769 { 770 Field field = fieldMap.get(fieldId); 771 772 _fillFieldNode(fieldNode, field); 773 774 fieldMap.remove(fieldId); 775 } 776 else 777 { 778 // The field doesn't exist anymore : delete it. 779 fieldNode.remove(); 780 } 781 } 782 } 783 784 // Now the map contains the field to add. 785 for (Map.Entry<String, Field> entry : fieldMap.entrySet()) 786 { 787 Field newField = entry.getValue(); 788 Node fieldNode = _storeField(formNode, newField); 789 _fillFieldNode(fieldNode, newField); 790 } 791 } 792 793 /** 794 * Get a name for a node which doesn't already exist in this node. 795 * @param container the container node. 796 * @param baseName the base wanted node name. 797 * @return the name, free to be taken. 798 * @throws RepositoryException if a repository error occurs. 799 */ 800 protected String _getNotExistingNodeName(Node container, String baseName) throws RepositoryException 801 { 802 String name = baseName; 803 804 int index = 2; 805 while (container.hasNode(name)) 806 { 807 name = baseName + index; 808 index++; 809 } 810 811 return name; 812 } 813 814 /** 815 * Get a single property value. 816 * @param node the JCR node. 817 * @param propertyName the name of the property to get. 818 * @param defaultValue the default value if the property does not exist. 819 * @return the single property value. 820 * @throws RepositoryException if a repository error occurs. 821 */ 822 protected String _getSingleProperty(Node node, String propertyName, String defaultValue) throws RepositoryException 823 { 824 String value = defaultValue; 825 826 if (node.hasProperty(propertyName)) 827 { 828 value = node.getProperty(propertyName).getString(); 829 } 830 831 return value; 832 } 833 834 /** 835 * Get the values of a string array property. 836 * @param node the node. 837 * @param propertyName the name of the property to get. 838 * @return the values. 839 * @throws RepositoryException if a repository error occurs. 840 */ 841 protected Collection<String> _getMultipleProperty(Node node, String propertyName) throws RepositoryException 842 { 843 List<String> values = new ArrayList<>(); 844 845 if (node.hasProperty(propertyName)) 846 { 847 Value[] propertyValues = node.getProperty(propertyName).getValues(); 848 for (Value value : propertyValues) 849 { 850 values.add(value.getString()); 851 } 852 } 853 854 return values; 855 } 856 857 /** 858 * Get additional configuration from properties. 859 * @param node the JCR node. 860 * @return the additional configuration as a Map. 861 * @throws RepositoryException if a repository error occurs. 862 */ 863 protected Map<String, String> _getFieldProperties(Node node) throws RepositoryException 864 { 865 Map<String, String> values = new HashMap<>(); 866 867 PropertyIterator propertyIt = node.getProperties(FIELD_PROPERTY_PREFIX + "*"); 868 while (propertyIt.hasNext()) 869 { 870 Property property = propertyIt.nextProperty(); 871 String propName = property.getName(); 872 String name = propName.substring(FIELD_PROPERTY_PREFIX.length(), propName.length()); 873 String value = property.getString(); 874 875 values.put(name, value); 876 } 877 878 return values; 879 } 880 881 /** 882 * Remove a form 883 * @param form The form to remove 884 * @param content The content holding the form 885 * @throws FormsException of an exception occurs when manipulating the forms' repository nodes 886 */ 887 public void remove(Form form, Content content) throws FormsException 888 { 889 Session session = null; 890 try 891 { 892 session = _repository.login(); 893 894 String id = form.getId(); 895 896 String xpathQuery = "//element(*, ametys:content)[@jcr:uuid = '" + ((JCRAmetysObject) content).getNode().getIdentifier() + "']/" + FORMS_NODE + "/*[@" + FORM_PROPERTY_ID + " = '" + id + "']"; 897 898 QueryManager queryManager = session.getWorkspace().getQueryManager(); 899 @SuppressWarnings("deprecation") 900 Query query = queryManager.createQuery(xpathQuery, Query.XPATH); 901 NodeIterator nodeIterator = query.execute().getNodes(); 902 903 if (nodeIterator.hasNext()) 904 { 905 Node formNode = nodeIterator.nextNode(); 906 formNode.remove(); 907 908 if (session.hasPendingChanges()) 909 { 910 session.save(); 911 } 912 } 913 } 914 catch (RepositoryException e) 915 { 916 throw new FormsException("Repository exception while storing the form properties.", e); 917 } 918 finally 919 { 920 if (session != null) 921 { 922 session.logout(); 923 } 924 } 925 } 926 927 /** 928 * Get or create the forms node in a content node. 929 * @param baseNode the content base node. 930 * @return the forms node. 931 * @throws RepositoryException if an error occurs. 932 */ 933 protected Node _createOrGetFormsNode(Node baseNode) throws RepositoryException 934 { 935 Node node = null; 936 if (baseNode.hasNode(FORMS_NODE)) 937 { 938 node = baseNode.getNode(FORMS_NODE); 939 } 940 else 941 { 942 node = baseNode.addNode(FORMS_NODE, "nt:unstructured"); 943 } 944 return node; 945 } 946}