1 13 package info.magnolia.module.admininterface; 14 15 import info.magnolia.cms.beans.config.ContentRepository; 16 import info.magnolia.cms.beans.runtime.Document; 17 import info.magnolia.cms.beans.runtime.MultipartForm; 18 import info.magnolia.cms.core.Content; 19 import info.magnolia.cms.core.HierarchyManager; 20 import info.magnolia.cms.core.ItemType; 21 import info.magnolia.cms.core.NodeData; 22 import info.magnolia.cms.core.Path; 23 import info.magnolia.cms.gui.control.ControlImpl; 24 import info.magnolia.cms.gui.control.File; 25 import info.magnolia.cms.gui.fckeditor.FCKEditorTmpFiles; 26 import info.magnolia.cms.gui.misc.FileProperties; 27 import info.magnolia.cms.security.AccessDeniedException; 28 import info.magnolia.cms.security.Digester; 29 import info.magnolia.cms.util.ContentUtil; 30 import info.magnolia.cms.util.DateUtil; 31 import info.magnolia.cms.util.ExclusiveWrite; 32 import info.magnolia.cms.util.LinkUtil; 33 import info.magnolia.cms.util.NodeDataUtil; 34 import info.magnolia.context.MgnlContext; 35 36 import java.io.FileInputStream ; 37 import java.io.FileNotFoundException ; 38 import java.io.IOException ; 39 import java.io.InputStream ; 40 import java.util.Calendar ; 41 import java.util.GregorianCalendar ; 42 import java.util.Iterator ; 43 import java.util.Map ; 44 import java.util.TimeZone ; 45 import java.util.regex.Matcher ; 46 import java.util.regex.Pattern ; 47 48 import javax.jcr.Node; 49 import javax.jcr.PathNotFoundException; 50 import javax.jcr.PropertyType; 51 import javax.jcr.RepositoryException; 52 import javax.jcr.Value; 53 import javax.jcr.ValueFactory; 54 55 import org.apache.commons.codec.binary.Base64; 56 import org.apache.commons.io.FileUtils; 57 import org.apache.commons.io.IOUtils; 58 import org.apache.commons.lang.StringUtils; 59 import org.apache.commons.lang.exception.NestableRuntimeException; 60 import org.devlib.schmidt.imageinfo.ImageInfo; 61 import org.slf4j.Logger; 62 import org.slf4j.LoggerFactory; 63 64 65 93 public class SaveHandlerImpl implements SaveHandler { 94 95 98 public static Logger log = LoggerFactory.getLogger(SaveHandlerImpl.class); 99 100 103 private MultipartForm form; 104 105 108 private boolean create; 109 110 private ItemType creationItemType = ItemType.CONTENTNODE; 111 112 115 private String repository = ContentRepository.WEBSITE; 116 117 120 private String path; 121 122 125 private String nodeCollectionName; 126 127 130 private String nodeName; 131 132 135 private String paragraph; 136 137 140 public SaveHandlerImpl() { 141 } 142 143 147 public void init(MultipartForm form) { 148 this.setForm(form); 149 } 150 151 154 public boolean save() { 155 156 String [] saveInfos = getForm().getParameterValues("mgnlSaveInfo"); 158 if (saveInfos == null) { 159 log.info("Nothing to save, mgnlSaveInfo parameter not found."); 160 return true; 161 } 162 163 synchronized (ExclusiveWrite.getInstance()) { 164 String path = this.getPath(); 166 167 HierarchyManager hm = MgnlContext.getHierarchyManager(this.getRepository()); 168 try { 169 Content page = this.getPageNode(hm); 171 172 if (page == null) { 173 return false; 175 } 176 177 Content node = this.getSaveNode(hm, page); 178 179 this.setNodeName(node.getName()); 181 if (StringUtils.isEmpty(node.getMetaData().getTemplate())) { 182 node.getMetaData().setTemplate(this.getParagraph()); 183 } 184 185 node.updateMetaData(); 187 page.updateMetaData(); 188 189 for (int i = 0; i < saveInfos.length; i++) { 191 String saveInfo = saveInfos[i]; 192 processSaveInfo(node, saveInfo); 193 } 194 195 try { 197 MultipartForm form = getForm(); 198 Map docs = form.getDocuments(); 199 Iterator iter = docs.keySet().iterator(); 200 while (iter.hasNext()) { 201 form.getDocument((String ) iter.next()).delete(); 202 } 203 } 204 catch (Exception e) { 205 log.error("Could not delete temp documents from form"); 206 } 207 208 if (log.isDebugEnabled()) { 209 log.debug("Saving {}", path); } 211 212 hm.save(); 213 } 214 catch (RepositoryException re) { 215 log.error(re.getMessage(), re); 216 return false; 217 } 218 } 219 return true; 220 } 221 222 230 protected void processSaveInfo(Content node, String saveInfo) throws PathNotFoundException, RepositoryException, 231 AccessDeniedException { 232 233 String name; 234 int type = PropertyType.STRING; 235 int valueType = ControlImpl.VALUETYPE_SINGLE; 236 int isRichEditValue = 0; 237 int encoding = ControlImpl.ENCODING_NO; 238 String [] values = {StringUtils.EMPTY}; 239 if (StringUtils.contains(saveInfo, ',')) { 240 String [] info = StringUtils.split(saveInfo, ','); 241 name = info[0]; 242 if (info.length >= 2) { 243 type = PropertyType.valueFromName(info[1]); 244 } 245 if (info.length >= 3) { 246 valueType = Integer.valueOf(info[2]).intValue(); 247 } 248 if (info.length >= 4) { 249 isRichEditValue = Integer.valueOf(info[3]).intValue(); 250 } 251 if (info.length >= 5) { 252 encoding = Integer.valueOf(info[4]).intValue(); 253 } 254 } 255 else { 256 name = saveInfo; 257 } 258 259 if (type == PropertyType.BINARY) { 260 processBinary(node, name); 261 } 262 else { 263 values = getForm().getParameterValues(name); 264 if (valueType == ControlImpl.VALUETYPE_MULTIPLE) { 265 processMultiple(node, name, type, values); 266 } 267 else if (isRichEditValue != ControlImpl.RICHEDIT_NONE) { 268 processRichEdit(node, name, type, isRichEditValue, encoding, values); 269 } 270 else if (type == PropertyType.DATE) { 271 processDate(node, name, type, valueType, encoding, values); 272 } 273 else { 274 processCommon(node, name, type, valueType, encoding, values); 275 } 276 } 277 } 278 279 protected void processDate(Content node, String name, int type, int valueType, int encoding, String [] values) { 280 try { 281 if (StringUtils.isEmpty(values[0])) { 282 if (log.isDebugEnabled()) { 283 log.debug("Date has no value. Deleting node data" + name); 284 } 285 if(node.hasNodeData(name)){ 286 node.deleteNodeData(name); 287 } 288 } 289 else { 290 Calendar utc = DateUtil.getUTCCalendarFromDialogString(values[0]); 291 NodeDataUtil.getOrCreate(node, name).setValue(utc); 292 } 293 } 294 catch (Exception e) { 295 log.error("Could not update date value of node:" + node.getHandle() + " of property:" + name, e); 296 } 297 } 298 299 311 protected void processRichEdit(Content node, String name, int type, int isRichEditValue, int encoding, 312 String [] values) throws PathNotFoundException, RepositoryException, AccessDeniedException { 313 String valueStr = StringUtils.EMPTY; 314 if (values != null) { 315 valueStr = values[0]; } 317 318 valueStr = cleanLineBreaks(valueStr, isRichEditValue); 319 320 if (isRichEditValue == ControlImpl.RICHEDIT_FCK) { 321 valueStr = updateLinks(node, name, valueStr); 322 } 323 324 processString(node, name, type, encoding, values, valueStr); 325 } 326 327 334 private String cleanLineBreaks(String valueStr, int isRichEditValue) { 335 valueStr = LinkUtil.convertAbsoluteLinksToUUIDs(valueStr); 337 338 valueStr = StringUtils.replace(valueStr, "\r\n", " "); valueStr = StringUtils.replace(valueStr, "\n", " "); 341 valueStr = StringUtils.replace(valueStr, "</br>", StringUtils.EMPTY); valueStr = StringUtils.replace(valueStr, "<br>", "<br/>"); valueStr = StringUtils.replace(valueStr, "<BR>", "<br />"); valueStr = StringUtils.replace(valueStr, "<br/>", "<br />"); valueStr = StringUtils.replace(valueStr, "<P><br />", "<P>"); 348 if (isRichEditValue != ControlImpl.RICHEDIT_FCK) { 350 valueStr = replacePByBr(valueStr, "p"); } 352 return valueStr; 353 } 354 355 366 private String updateLinks(Content node, String name, String valueStr) throws AccessDeniedException, 367 RepositoryException, PathNotFoundException { 368 369 HierarchyManager hm = MgnlContext.getHierarchyManager(this.getRepository()); 371 372 Pattern imagePattern = Pattern.compile("(<(a|img)[^>]+(href|src)[ ]*=[ ]*\")([^\"]*)(\"[^>]*>)"); 373 Pattern uuidPattern = Pattern.compile(MgnlContext.getContextPath() + "/tmp/fckeditor/([^/]*)/[^\"]*"); 374 375 Content filesNode = ContentUtil.getOrCreateContent(node, name + "_files", ItemType.CONTENTNODE); 376 377 String pageName = StringUtils.substringAfterLast(this.getPath(), "/"); 378 379 Matcher srcMatcher = imagePattern.matcher(valueStr); 381 StringBuffer res = new StringBuffer (); 382 while (srcMatcher.find()) { 383 String src = srcMatcher.group(4); 384 385 src = StringUtils.replace(src, "../../../", MgnlContext.getContextPath() + "/"); 387 388 String link = src; 389 390 Matcher uuidMatcher = uuidPattern.matcher(src); 392 393 if (uuidMatcher.find()) { 394 String uuid = uuidMatcher.group(1); 395 396 Document doc = FCKEditorTmpFiles.getDocument(uuid); 397 String fileNodeName = Path.getUniqueLabel(hm, filesNode.getHandle(), "file"); 398 SaveHandlerImpl.saveDocument(filesNode, doc, fileNodeName, "", ""); 399 String subpath = StringUtils.removeStart(filesNode.getHandle(), this.getPath() + "/"); 400 link = pageName + "/" + subpath + "/" + fileNodeName + "/" + doc.getFileNameWithExtension(); 401 doc.delete(); 402 try { 403 FileUtils.deleteDirectory(new java.io.File (Path.getTempDirectory() + "/fckeditor/" + uuid)); 404 } 405 catch (IOException e) { 406 log.error("can't delete tmp file [" + Path.getTempDirectory() + "/fckeditor/" + uuid + "]"); 407 } 408 } 409 else if (src.startsWith(MgnlContext.getContextPath() + this.getPath())) { 413 link = pageName + StringUtils.removeStart(src, MgnlContext.getContextPath() + this.getPath()); 414 } 415 416 link = StringUtils.replace(link, "$", "\\$"); 418 419 srcMatcher.appendReplacement(res, "$1" + link + "$5"); } 421 srcMatcher.appendTail(res); 422 valueStr = res.toString(); 423 return valueStr; 424 } 425 426 438 protected void processCommon(Content node, String name, int type, int valueType, int encoding, String [] values) 439 throws PathNotFoundException, RepositoryException, AccessDeniedException { 440 String valueStr = StringUtils.EMPTY; 441 if (values != null) { 442 valueStr = values[0]; } 444 445 processString(node, name, type, encoding, values, valueStr); 446 } 447 448 460 protected void processString(Content node, String name, int type, int encoding, String [] values, String valueStr) 461 throws PathNotFoundException, RepositoryException, AccessDeniedException { 462 boolean remove = false; 464 boolean write = false; 465 if (encoding == ControlImpl.ENCODING_BASE64) { 466 if (StringUtils.isNotBlank(valueStr)) { 467 valueStr = new String (Base64.encodeBase64(valueStr.getBytes())); 468 write = true; 469 } 470 } 471 else if (encoding == ControlImpl.ENCODING_UNIX) { 472 if (StringUtils.isNotEmpty(valueStr)) { 473 valueStr = Digester.getSHA1Hex(valueStr); 474 write = true; 475 } 476 } 477 else { 478 if (values == null || StringUtils.isEmpty(valueStr)) { 480 remove = true; 481 } 482 else { 483 write = true; 484 } 485 } 486 if (remove) { 487 processRemoveCommon(node, name); 488 } 489 else if (write) { 490 processWriteCommon(node, name, valueStr, type); 491 } 492 } 493 494 497 protected void processRemoveCommon(Content node, String name) throws PathNotFoundException, RepositoryException { 498 NodeData data = node.getNodeData(name); 499 500 if (data.isExist()) { 501 node.deleteNodeData(name); 502 } 503 } 504 505 513 protected void processWriteCommon(Content node, String name, String valueStr, int type) 514 throws AccessDeniedException, RepositoryException { 515 Value value = this.getValue(valueStr, type); 516 if (null != value) { 517 NodeData data = NodeDataUtil.getOrCreate(node, name); 518 data.setValue(value); 519 } 520 } 521 522 532 protected void processMultiple(Content node, String name, int type, String [] values) throws RepositoryException, 533 PathNotFoundException, AccessDeniedException { 534 try { 536 node.delete(name); 537 } 538 catch (PathNotFoundException e) { 539 if (log.isDebugEnabled()) { 540 log.debug("Exception caught: " + e.getMessage(), e); } 542 } 543 if (values != null && values.length != 0) { 544 Content multiNode = node.createContent(name, ItemType.CONTENTNODE); 545 try { 546 multiNode.deleteNodeData("creationdate"); } 549 catch (RepositoryException re) { 550 if (log.isDebugEnabled()) { 551 log.debug("Exception caught: " + re.getMessage(), re); } 553 } 554 for (int j = 0; j < values.length; j++) { 555 String valueStr = values[j]; 556 if (StringUtils.isNotEmpty(valueStr)) { 557 Value value = this.getValue(valueStr, type); 558 multiNode.createNodeData(Integer.toString(j)).setValue(value); 559 } 560 } 561 } 562 } 563 564 572 protected void processBinary(Content node, String name) throws PathNotFoundException, RepositoryException, 573 AccessDeniedException { 574 Document doc = getForm().getDocument(name); 575 if (doc == null && getForm().getParameter(name + "_" + File.REMOVE) != null) { try { 577 node.deleteNodeData(name); 578 } 579 catch (RepositoryException re) { 580 if (log.isDebugEnabled()) { 581 log.debug("Exception caught: " + re.getMessage(), re); } 583 } 584 } 585 else { 586 String fileName = getForm().getParameter(name + "_" + FileProperties.PROPERTY_FILENAME); 587 String template = getForm().getParameter(name + "_" + FileProperties.PROPERTY_TEMPLATE); 588 589 SaveHandlerImpl.saveDocument(node, doc, name, fileName, template); 590 } 591 } 592 593 598 public Value getValue(String s) { 599 return this.getValue(s, PropertyType.STRING); 600 } 601 602 607 public Value getValue(long l) { 608 HierarchyManager hm = MgnlContext.getHierarchyManager(this.getRepository()); 609 ValueFactory valueFactory; 610 try { 611 valueFactory = hm.getWorkspace().getSession().getValueFactory(); 612 } 613 catch (RepositoryException e) { 614 throw new NestableRuntimeException(e); 615 } 616 return valueFactory.createValue(l); 617 } 618 619 625 public Value getValue(String valueStr, int type) { 626 627 ValueFactory valueFactory = null; 628 629 HierarchyManager hm = MgnlContext.getHierarchyManager(this.getRepository()); 630 try { 631 valueFactory = hm.getWorkspace().getSession().getValueFactory(); 632 } 633 catch (RepositoryException e) { 634 throw new NestableRuntimeException(e); 635 } 636 637 Value value = null; 638 639 if (type == PropertyType.REFERENCE) { 640 try { 641 Node referencedNode = hm.getWorkspace().getSession().getNodeByUUID(valueStr); 642 643 value = valueFactory.createValue(referencedNode); 644 } 645 catch (RepositoryException re) { 646 if (log.isDebugEnabled()) { 647 log.debug("Cannot retrieve the referenced node by UUID: " + valueStr, re); 648 } 649 } 650 } 651 else { 652 value = NodeDataUtil.getValue(valueStr, type, valueFactory); 653 } 654 655 return value; 656 } 657 658 662 protected static String replacePByBr(final String value, String tagName) { 663 664 if (StringUtils.isBlank(value)) { 665 return value; 666 } 667 668 String fixedValue = value; 669 670 String pre = "<" + tagName + ">"; String post = "</" + tagName + ">"; 673 if (fixedValue.endsWith(post)) { 675 fixedValue = StringUtils.substringBeforeLast(fixedValue, post); 676 } 677 678 fixedValue = StringUtils.replace(fixedValue, pre + " " + post, "\n "); fixedValue = StringUtils.replace(fixedValue, pre, StringUtils.EMPTY); 680 fixedValue = StringUtils.replace(fixedValue, post, "\n\n "); 682 if (!tagName.equals(tagName.toUpperCase())) { 683 fixedValue = replacePByBr(fixedValue, tagName.toUpperCase()); 684 } 685 return fixedValue; 686 } 687 688 691 public boolean isCreate() { 692 return create; 693 } 694 695 698 public void setCreate(boolean create) { 699 this.create = create; 700 } 701 702 705 public ItemType getCreationItemType() { 706 return creationItemType; 707 } 708 709 712 public void setCreationItemType(ItemType creationItemType) { 713 this.creationItemType = creationItemType; 714 } 715 716 719 protected MultipartForm getForm() { 720 return form; 721 } 722 723 727 protected void setForm(MultipartForm form) { 728 this.form = form; 729 } 730 731 735 public void setRepository(String repository) { 736 this.repository = repository; 737 } 738 739 743 public String getRepository() { 744 return repository; 745 } 746 747 755 protected Content getPageNode(HierarchyManager hm) throws RepositoryException, AccessDeniedException, 756 PathNotFoundException { 757 Content page = null; 758 String path = this.getPath(); 759 try { 760 page = hm.getContent(path); 761 } 762 catch (RepositoryException e) { 763 if (this.isCreate()) { 764 String parentPath = StringUtils.substringBeforeLast(path, "/"); String label = StringUtils.substringAfterLast(path, "/"); if (StringUtils.isEmpty(parentPath)) { 767 page = hm.getRoot(); 768 } 769 else { 770 page = hm.getContent(parentPath); 771 } 772 page = page.createContent(label, this.getCreationItemType()); 773 } 774 else { 775 log.error("Tried to save a not existing node with path {}. use create = true to force creation", path); } 777 } 778 return page; 779 } 780 781 790 protected Content getSaveNode(HierarchyManager hm, Content rootNode) throws AccessDeniedException, 791 RepositoryException { 792 Content node = null; 793 794 Content nodeCollection = null; 796 if (StringUtils.isNotEmpty(this.getNodeCollectionName())) { 797 try { 798 nodeCollection = rootNode.getContent(this.getNodeCollectionName()); 799 } 800 catch (RepositoryException re) { 801 nodeCollection = rootNode.createContent(this.getNodeCollectionName(), ItemType.CONTENTNODE); 803 if (log.isDebugEnabled()) { 804 log.debug("Create - " + nodeCollection.getHandle()); } 806 } 807 } 808 else { 809 nodeCollection = rootNode; 810 } 811 812 if (StringUtils.isNotEmpty(this.getNodeName())) { 814 try { 815 node = nodeCollection.getContent(this.getNodeName()); 816 } 817 catch (RepositoryException re) { 818 if (this.getNodeName().equals("mgnlNew")) { this.setNodeName(Path.getUniqueLabel(hm, nodeCollection.getHandle(), "0")); } 822 node = nodeCollection.createContent(this.getNodeName(), this.getCreationItemType()); 823 } 824 } 825 else { 826 node = nodeCollection; 827 } 828 return node; 829 } 830 831 842 public static void saveDocument(Content node, Document doc, String name, String fileName, String template) 843 throws PathNotFoundException, RepositoryException, AccessDeniedException { 844 845 NodeData data = node.getNodeData(name); 846 if (doc != null) { 847 if (!data.isExist()) { 848 data = node.createNodeData(name, PropertyType.BINARY); 849 850 log.debug("creating under - {}", node.getHandle()); log.debug("creating node data for binary store - {}", name); 853 } 854 data.setValue(doc.getStream()); 855 if (log.isDebugEnabled()) { 856 log.debug("Node data updated"); } 858 } 859 if (data != null) { 860 if (fileName == null || fileName.equals(StringUtils.EMPTY)) { 861 fileName = doc.getFileName(); 862 } 863 data.setAttribute(FileProperties.PROPERTY_FILENAME, fileName); 864 if (doc != null) { 865 data.setAttribute(FileProperties.PROPERTY_CONTENTTYPE, doc.getType()); 866 867 Calendar value = new GregorianCalendar (TimeZone.getDefault()); 868 data.setAttribute(FileProperties.PROPERTY_LASTMODIFIED, value); 869 870 data.setAttribute(FileProperties.PROPERTY_SIZE, Long.toString(doc.getLength())); 871 872 data.setAttribute(FileProperties.PROPERTY_EXTENSION, doc.getExtension()); 873 874 data.setAttribute(FileProperties.PROPERTY_TEMPLATE, template); 875 876 InputStream raf = null; 877 try { 878 ImageInfo ii = new ImageInfo(); 879 raf = new FileInputStream (doc.getFile()); 880 ii.setInput(raf); 881 if (ii.check()) { 882 data.setAttribute(FileProperties.PROPERTY_WIDTH, Long.toString(ii.getWidth())); 883 data.setAttribute(FileProperties.PROPERTY_HEIGHT, Long.toString(ii.getHeight())); 884 } 886 } 887 catch (FileNotFoundException e) { 888 log.error("FileNotFoundException caught when parsing {}, image data will not be available", doc 889 .getFile() 890 .getAbsolutePath()); 891 } 892 finally { 893 IOUtils.closeQuietly(raf); 894 } 895 896 } 901 } 902 903 } 904 905 908 public String getNodeCollectionName() { 909 return this.nodeCollectionName; 910 } 911 912 915 public void setNodeCollectionName(String nodeCollectionName) { 916 this.nodeCollectionName = nodeCollectionName; 917 } 918 919 922 public String getNodeName() { 923 return this.nodeName; 924 } 925 926 929 public void setNodeName(String nodeName) { 930 this.nodeName = nodeName; 931 } 932 933 936 public String getParagraph() { 937 return this.paragraph; 938 } 939 940 943 public void setParagraph(String paragraph) { 944 this.paragraph = paragraph; 945 } 946 947 950 public String getPath() { 951 return this.path; 952 } 953 954 957 public void setPath(String path) { 958 this.path = path; 959 } 960 961 } | Popular Tags |