package net.cyklotron.cms.structure.internal; import static net.cyklotron.cms.documents.DocumentMetadataHelper.cdata; import static net.cyklotron.cms.documents.DocumentMetadataHelper.doc; import static net.cyklotron.cms.documents.DocumentMetadataHelper.dom4jToText; import static net.cyklotron.cms.documents.DocumentMetadataHelper.elm; import static net.cyklotron.cms.documents.DocumentMetadataHelper.selectFirstText; import static net.cyklotron.cms.documents.DocumentMetadataHelper.textToDom4j; import java.io.StringWriter; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; import org.dom4j.Document; import org.dom4j.Element; import org.jcontainer.dna.Logger; import org.objectledge.coral.entity.EntityDoesNotExistException; import org.objectledge.coral.session.CoralSession; import org.objectledge.coral.store.Resource; import org.objectledge.encodings.HTMLEntityDecoder; import org.objectledge.encodings.HTMLEntityEncoder; import org.objectledge.html.HTMLException; import org.objectledge.html.HTMLService; import org.objectledge.parameters.Parameters; import org.objectledge.pipeline.ProcessingException; import org.objectledge.templating.TemplatingContext; import org.objectledge.upload.FileUpload; import org.objectledge.upload.UploadContainer; import org.objectledge.upload.UploadLimitExceededException; import org.objectledge.utils.StringUtils; import net.cyklotron.cms.CmsNodeResource; import net.cyklotron.cms.category.CategoryResource; import net.cyklotron.cms.category.CategoryResourceImpl; import net.cyklotron.cms.category.CategoryService; import net.cyklotron.cms.documents.DocumentNodeResource; import net.cyklotron.cms.files.DirectoryResource; import net.cyklotron.cms.files.DirectoryResourceImpl; import net.cyklotron.cms.files.FileResource; import net.cyklotron.cms.files.FileResourceImpl; import net.cyklotron.cms.related.RelatedService; import net.cyklotron.cms.structure.NavigationNodeResource; import net.cyklotron.cms.structure.NavigationNodeResourceImpl; /** * Data object used by ProposeDocument view and action. *

* Feels kind like breaking open door, but I'm not willing to learn formtool just to make one silly * screen. *

*

* Not threadsafe, but there should be no need to share this object among thread. *

* * @author rafal */ public class ProposedDocumentData { // component configuration private boolean calendarTree; private boolean inheritCategories; private boolean attachmentsEnabled; private int attachmentsMaxCount; private int attachmentsMaxSize; private String attachmentsAllowedFormats; private List attachmentFormatList; private long attachmentDirId; // form data private String name; private String title; private String docAbstract; private String content; private String eventPlace; private String organizedBy; private String organizedAddress; private String organizedPhone; private String organizedFax; private String organizedEmail; private String organizedWww; private String sourceName; private String sourceUrl; private String proposerCredentials; private String proposerEmail; private String description; private String editorialNote; private Date validityStart; private Date validityEnd; private Date eventStart; private Date eventEnd; private Set availableCategories; private Set selectedCategories; private List attachments; private List attachmentDescriptions; private boolean removalRequested; // validation private String validationFailure; // helper objects private static final HTMLEntityEncoder ENCODER = new HTMLEntityEncoder(); private static final HTMLEntityDecoder DECODER = new HTMLEntityDecoder(); private final DateFormat format = DateFormat.getDateTimeInstance(); // origin (node where ProposeDocument screen is embedded) private NavigationNodeResource origin; private boolean addDocumentVisualEditor; public ProposedDocumentData(Parameters configuration) { setConfiguration(configuration); } public ProposedDocumentData() { // remember to call setConfiguration later } public void setConfiguration(Parameters configuration) { calendarTree = configuration.getBoolean("calendar_tree", true); inheritCategories = configuration.getBoolean("inherit_categories", true); attachmentsEnabled = configuration.getBoolean("attachments_enabled", false); attachmentsMaxCount = configuration.getInt("attachments_max_count", 0); attachmentsMaxSize = configuration.getInt("attachments_max_size", 0); attachmentsAllowedFormats = configuration.get("attachments_allowed_formats", "jpg gif doc rtf pdf xls"); attachmentFormatList = Arrays.asList(attachmentsAllowedFormats.toLowerCase().split("\\s+")); attachmentDirId = configuration.getLong("attachments_dir_id", -1L); addDocumentVisualEditor = configuration.getBoolean("add_document_visual_editor", false); } public void fromParameters(Parameters parameters, CoralSession coralSession) throws EntityDoesNotExistException { name = stripTags(dec(parameters.get("name", ""))); title = stripTags(dec(parameters.get("title", ""))); docAbstract = stripTags(dec(parameters.get("abstract", ""))); content = parameters.get("content", ""); eventPlace = stripTags(dec(parameters.get("event_place", ""))); organizedBy = stripTags(dec(parameters.get("organized_by", ""))); organizedAddress = stripTags(dec(parameters.get("organized_address", ""))); organizedPhone = stripTags(dec(parameters.get("organized_phone", ""))); organizedFax = stripTags(dec(parameters.get("organized_fax", ""))); organizedEmail = stripTags(dec(parameters.get("organized_email", ""))); organizedWww = stripTags(dec(parameters.get("organized_www", ""))); sourceName = stripTags(dec(parameters.get("source_name", ""))); sourceUrl = stripTags(dec(parameters.get("source_url", ""))); proposerCredentials = stripTags(dec(parameters.get("proposer_credentials", ""))); proposerEmail = stripTags(dec(parameters.get("proposer_email", ""))); description = stripTags(dec(parameters.get("description", ""))); editorialNote = stripTags(dec(parameters.get("editorial_note", ""))); validityStart = getDate(parameters, "validity_start"); validityEnd = getDate(parameters, "validity_end"); eventStart = getDate(parameters, "event_start"); eventEnd = getDate(parameters, "event_end"); selectedCategories = new HashSet(); for (long categoryId : parameters.getLongs("selected_categories")) { if(categoryId != -1) { selectedCategories.add(CategoryResourceImpl.getCategoryResource(coralSession, categoryId)); } } availableCategories = new HashSet(); for (long categoryId : parameters.getLongs("available_categories")) { availableCategories.add(CategoryResourceImpl.getCategoryResource(coralSession, categoryId)); } if(attachmentsEnabled) { attachmentDescriptions = new ArrayList(attachmentsMaxCount); for (int i = 1; i <= attachmentsMaxCount; i++) { attachmentDescriptions.add(stripTags(dec(parameters.get("attachment_description_" + i, "")))); } attachments = new ArrayList(attachmentsMaxCount); for (int i = 1; i <= attachmentsMaxCount; i++) { long fileId = parameters.getLong("attachment_id_" + i, -1); if(fileId != -1) { attachments.add(FileResourceImpl.getFileResource(coralSession, fileId)); } } } } /** * Transfers the data into the templating context. *

* This is needed to keep the exiting templates working *

* * @param templatingContext */ public void toTemplatingContext(TemplatingContext templatingContext) { templatingContext.put("name", enc(name)); templatingContext.put("title", enc(title)); templatingContext.put("abstract", enc(docAbstract)); templatingContext.put("content", enc(content)); templatingContext.put("event_place", enc(eventPlace)); templatingContext.put("organized_by", enc(organizedBy)); templatingContext.put("organized_address", enc(organizedAddress)); templatingContext.put("organized_phone", enc(organizedPhone)); templatingContext.put("organized_fax", enc(organizedFax)); templatingContext.put("organized_email", enc(organizedEmail)); templatingContext.put("organized_www", enc(organizedWww)); templatingContext.put("source_name", enc(sourceName)); templatingContext.put("source_url", enc(sourceUrl)); templatingContext.put("proposer_credentials", enc(proposerCredentials)); templatingContext.put("proposer_email", enc(proposerEmail)); templatingContext.put("description", enc(description)); setDate(templatingContext, "validity_start", validityStart); setDate(templatingContext, "validity_end", validityEnd); setDate(templatingContext, "event_start", eventStart); setDate(templatingContext, "event_end", eventEnd); templatingContext.put("selected_categories", selectedCategories); if(attachmentsEnabled) { templatingContext.put("attachments_enabled", attachmentsEnabled); templatingContext.put("attachments_max_count", attachmentsMaxCount); int remaining = attachmentsMaxCount - attachments.size(); remaining = remaining >= 0 ? remaining : 0; templatingContext.put("attachments_remaining_count", remaining); templatingContext.put("attachments_max_size", attachmentsMaxSize); templatingContext.put("attachments_allowed_formats", attachmentsAllowedFormats); templatingContext.put("current_attachments", attachments); // fill up with empty strings to make template logic more simple while(attachmentDescriptions.size() < attachmentsMaxCount) { attachmentDescriptions.add(""); } templatingContext.put("attachment_descriptions", enc(attachmentDescriptions)); } templatingContext.put("editorial_note", enc(editorialNote)); templatingContext.put("add_document_visual_editor", addDocumentVisualEditor); } public void fromNode(DocumentNodeResource node, CategoryService categoryService, RelatedService relatedService, CoralSession coralSession) { // calendarTree // inheritCategories name = stripTags(node.getName()); title = stripTags(node.getTitle()); docAbstract = stripTags(node.getAbstract()); content = node.getContent(); description = stripTags(node.getDescription()); validityStart = node.getValidityStart(); validityEnd = node.getValidityEnd(); eventPlace = stripTags(node.getEventPlace()); eventStart = node.getEventStart(); eventEnd = node.getEventEnd(); try { Document metaDom = textToDom4j(node.getMeta()); organizedBy = stripTags(selectFirstText(metaDom, "/meta/organisation/name")); organizedAddress = stripTags(selectFirstText(metaDom, "/meta/organisation/address")); organizedPhone = stripTags(selectFirstText(metaDom, "/meta/organisation/tel")); organizedFax = stripTags(selectFirstText(metaDom, "/meta/organisation/fax")); organizedEmail = stripTags(selectFirstText(metaDom, "/meta/organisation/e-mail")); organizedWww = stripTags(selectFirstText(metaDom, "/meta/organisation/url")); sourceName = stripTags(selectFirstText(metaDom, "/meta/sources/source/name")); sourceUrl = stripTags(selectFirstText(metaDom, "/meta/sources/source/url")); proposerCredentials = stripTags(selectFirstText(metaDom, "/meta/authors/author/name")); proposerEmail = stripTags(selectFirstText(metaDom, "/meta/authors/author/e-mail")); } catch(HTMLException e) { throw new RuntimeException("malformed metadada in resource " + node.getIdString(), e); } selectedCategories = new HashSet(Arrays.asList(categoryService .getCategories(coralSession, node, false))); if(attachmentsEnabled) { List resources = new ArrayList(Arrays.asList(relatedService .getRelatedTo(coralSession, node, node.getRelatedResourcesSequence(), null))); attachments = new ArrayList(attachmentsMaxCount); attachmentDescriptions = new ArrayList(attachmentsMaxCount); if(node.isThumbnailDefined()) { attachments.add(node.getThumbnail()); attachmentDescriptions.add(stripTags(node.getThumbnail().getDescription())); } for (Resource attachment : resources) { if(attachment instanceof FileResource) { attachments.add(attachment); attachmentDescriptions.add(stripTags(((CmsNodeResource)attachment) .getDescription())); } } } } public void toNode(DocumentNodeResource node) { // set attributes to new node node.setDescription(enc(description)); if(addDocumentVisualEditor) { node.setContent(content); } else { node.setContent(makePara(stripTags(content))); } node.setAbstract(enc(docAbstract)); node.setValidityStart(validityStart); node.setValidityEnd(validityEnd); node.setEventStart(eventStart); node.setEventEnd(eventEnd); node.setEventPlace(enc(eventPlace)); Document doc = doc(getMetaElm()); node.setMeta(dom4jToText(doc)); } private Element getMetaElm() { return elm("meta", elm("authors", elm("author", elm("name", enc(proposerCredentials)), elm( "e-mail", enc(proposerEmail)))), elm("sources", elm("source", elm("name", enc(sourceName)), elm("url", enc(sourceUrl)))), elm("editor"), elm("organisation", elm( "name", enc(organizedBy)), elm("address", enc(organizedAddress)), elm("tel", enc(organizedPhone)), elm("fax", enc(organizedFax)), elm("e-mail", enc(organizedEmail)), elm("url", enc(organizedWww)), elm("id", "0"))); } public void fromProposal(DocumentNodeResource node, CoralSession coralSession) { try { Document proposalDom = textToDom4j(node.getProposedContent()); name = dec(selectFirstText(proposalDom, "/document/name")); title = dec(selectFirstText(proposalDom, "/document/title")); docAbstract = dec(selectFirstText(proposalDom, "/document/abstract")); // DECODE HTML content = dec(selectFirstText(proposalDom, "/document/content")); description = dec(selectFirstText(proposalDom, "/document/description")); validityStart = text2date(dec(selectFirstText(proposalDom, "/document/validity/start"))); validityEnd = text2date(dec(selectFirstText(proposalDom, "/document/validity/end"))); eventPlace = dec(selectFirstText(proposalDom, "/document/event/place")); eventStart = text2date(dec(selectFirstText(proposalDom, "/document/event/start"))); eventEnd = text2date(dec(selectFirstText(proposalDom, "/document/event/end"))); organizedBy = dec(selectFirstText(proposalDom, "/document/meta/organisation/name")); organizedAddress = dec(selectFirstText(proposalDom, "/document/meta/organisation/address")); organizedPhone = dec(selectFirstText(proposalDom, "/document/meta/organisation/tel")); organizedFax = dec(selectFirstText(proposalDom, "/document/meta/organisation/fax")); organizedEmail = dec(selectFirstText(proposalDom, "/document/meta/organisation/e-mail")); organizedWww = dec(selectFirstText(proposalDom, "/document/meta/organisation/url")); sourceName = dec(selectFirstText(proposalDom, "/document/meta/sources/source/name")); sourceUrl = dec(selectFirstText(proposalDom, "/document/meta/sources/source/url")); proposerCredentials = dec(selectFirstText(proposalDom, "/document/meta/authors/author/name")); proposerEmail = dec(selectFirstText(proposalDom, "/document/meta/authors/author/e-mail")); selectedCategories = new HashSet(); for (Element categoryNode : (List)proposalDom .selectNodes("/document/categories/category/ref")) { long categoryId = Long.parseLong(categoryNode.getTextTrim()); selectedCategories.add(CategoryResourceImpl.getCategoryResource(coralSession, categoryId)); } attachments = new ArrayList(); attachmentDescriptions = new ArrayList(); for (Element attachmentNode : (List)proposalDom .selectNodes("/document/attachments/attachment")) { long fileId = Long.parseLong(attachmentNode.elementTextTrim("ref")); attachments.add(FileResourceImpl.getFileResource(coralSession, fileId)); attachmentDescriptions.add(dec(attachmentNode.elementText("description"))); } removalRequested = selectFirstText(proposalDom, "/document/request").equals("remove"); long originId = Long.parseLong(selectFirstText(proposalDom, "/document/origin/ref")); origin = NavigationNodeResourceImpl.getNavigationNodeResource(coralSession, originId); editorialNote = dec(selectFirstText(proposalDom, "/document/editorial/note")); } catch(HTMLException e) { throw new RuntimeException("malformed proposed changes descriptor", e); } catch(EntityDoesNotExistException e) { throw new RuntimeException("invalid resource id in proposed changes descriptor", e); } } public void toProposal(DocumentNodeResource node) { Element categoriesElm = elm("categories"); for (CategoryResource category : selectedCategories) { categoriesElm.add(elm("category", elm("ref", category.getIdString()))); } Element attachmentsElm = elm("attachments"); if(attachmentsEnabled) { Iterator attachmentIterator = attachments.iterator(); Iterator descriptionIterator = attachmentDescriptions.iterator(); while(attachmentIterator.hasNext()) { attachmentsElm.add(elm("attachment", elm("ref", attachmentIterator.next() .getIdString()), elm("description", descriptionIterator.next()))); } } Document doc = doc(elm("document", elm("request", removalRequested ? "remove" : "update"), elm("origin", elm("ref", origin.getIdString())), elm("name", enc(name)), elm("title", enc(title)), elm("abstract", enc(docAbstract)), elm("content", cdata(content)), elm("description", enc(description)), elm("editorial", elm("note", enc(editorialNote))), elm("validity", elm("start", date2text(validityStart)), elm("end", date2text(validityEnd))), elm("event", elm( "place", enc(eventPlace)), elm("start", date2text(eventStart)), elm("end", date2text(eventEnd))), getMetaElm(), categoriesElm, attachmentsElm)); node.setProposedContent(dom4jToText(doc)); } private static Date text2date(String text) { if(text.equals("undefined")) { return null; } else { return new Date(Long.parseLong(text)); } } private static String date2text(Date date) { if(date == null) { return "undefined"; } else { return Long.toString(date.getTime()); } } // validation public boolean isValid(HTMLService htmlService) { if(name.equals("")) { setValidationFailure("navi_name_empty"); return false; } if(title.equals("")) { setValidationFailure("navi_title_empty"); return false; } if(proposerCredentials.equals("")) { setValidationFailure("proposer_credentials_empty"); return false; } try { StringWriter errorWriter = new StringWriter(); Document contentDom = htmlService.textToDom4j(content, errorWriter, "proposeDocument"); if(contentDom == null) { setValidationFailure("invalid_html"); return false; } else { htmlService.removeEmptyParas(contentDom); StringWriter contentWriter = new StringWriter(); htmlService.dom4jToText(contentDom, contentWriter, true); content = contentWriter.toString(); } } catch(HTMLException e) { setValidationFailure("invalid_html"); return false; } return true; } public boolean isFileUploadValid(CoralSession coralSession, FileUpload fileUpload) throws ProcessingException { boolean valid = true; if(attachmentsEnabled) { // check if attachment_dir_id is configured, points to a directory, and user has write // rights try { DirectoryResource dir = DirectoryResourceImpl.getDirectoryResource(coralSession, attachmentDirId); if(!dir.canAddChild(coralSession, coralSession.getUserSubject())) { validationFailure = "attachment_dir_misconfigured"; valid = false; } } catch(Exception e) { validationFailure = "attachment_dir_misconfigured"; valid = false; } if(valid) { fileCheck: for (int i = attachments.size(); i < attachmentsMaxCount; i++) { try { UploadContainer uploadedFile = getAttachmentContainer(i, fileUpload); if(uploadedFile != null) { if(uploadedFile.getSize() > attachmentsMaxSize * 1024) { validationFailure = "attachment_size_exceeded"; valid = false; break fileCheck; } String fileName = uploadedFile.getFileName(); String fileExt = fileName.substring(fileName.lastIndexOf('.') + 1) .trim().toLowerCase(); if(!attachmentFormatList.contains(fileExt)) { validationFailure = "attachment_type_not_allowed"; valid = false; break fileCheck; } } } catch(UploadLimitExceededException e) { validationFailure = "upload_size_exceeded"; // i18n valid = false; break fileCheck; } } } } return valid; } // getters for configuration public boolean isAttachmentsEnabled() { return attachmentsEnabled; } public int getAttachmentsMaxCount() { return attachmentsMaxCount; } // getters public String getName() { return name; } public String getTitle() { return title; } public String getAbstract() { return docAbstract; } public String getContent() { return content; } public String getEventPlace() { return eventPlace; } public Date getEventStart() { return eventStart; } public Date getEventEnd() { return eventEnd; } public Date getValidityStart() { return validityStart; } public Date getValidityEnd() { return validityEnd; } public String getOrganizedBy() { return organizedBy; } public String getOrganizedAddress() { return organizedAddress; } public String getOrganizedPhone() { return organizedPhone; } public String getOrganizedFax() { return organizedFax; } public String getOrganizedEmail() { return organizedEmail; } public String getOrganizedWww() { return organizedWww; } public String getSourceName() { return sourceName; } public String getSourceUrl() { return sourceUrl; } public String getProposerCredentials() { return proposerCredentials; } public String getProposerEmail() { return proposerEmail; } public String getDescription() { return description; } public String getEditorialNote() { return editorialNote; } public boolean isCalendarTree() { return calendarTree; } public boolean isInheritCategories() { return inheritCategories; } public Set getSelectedCategories() { return selectedCategories; } public Set getAvailableCategories() { return availableCategories; } // attachments public DirectoryResource getAttachmenDirectory(CoralSession coralSession) throws EntityDoesNotExistException { return DirectoryResourceImpl.getDirectoryResource(coralSession, attachmentDirId); } public String getAttachmentDescription(int index) { if(index >= 0 && index < attachmentDescriptions.size()) { return attachmentDescriptions.get(index); } else { return ""; } } public String getAttachmentDescription(Resource file) { return getAttachmentDescription(attachments.indexOf(file)); } public List getAttachmentDescriptions() { return attachmentDescriptions; } public UploadContainer getAttachmentContainer(int index, FileUpload fileUpload) throws UploadLimitExceededException { return fileUpload.getContainer("attachment_" + (index + 1)); } public List getAttachments() { return attachments; } public void setTitle(String title) { this.title = title; } public void setDocAbstract(String docAbstract) { this.docAbstract = docAbstract; } public void setContent(String content) { this.content = content; } public void setEventPlace(String eventPlace) { this.eventPlace = eventPlace; } public void setOrganizedBy(String organizedBy) { this.organizedBy = organizedBy; } public void setOrganizedAddress(String organizedAddress) { this.organizedAddress = organizedAddress; } public void setOrganizedPhone(String organizedPhone) { this.organizedPhone = organizedPhone; } public void setOrganizedFax(String organizedFax) { this.organizedFax = organizedFax; } public void setOrganizedEmail(String organizedEmail) { this.organizedEmail = organizedEmail; } public void setOrganizedWww(String organizedWww) { this.organizedWww = organizedWww; } public void setSourceName(String sourceName) { this.sourceName = sourceName; } public void setSourceUrl(String sourceUrl) { this.sourceUrl = sourceUrl; } public void setProposerCredentials(String proposerCredentials) { this.proposerCredentials = proposerCredentials; } public void setProposerEmail(String proposerEmail) { this.proposerEmail = proposerEmail; } public void setDescription(String description) { this.description = description; } public void setValidityStart(Date validityStart) { this.validityStart = validityStart; } public void setValidityEnd(Date validityEnd) { this.validityEnd = validityEnd; } public void setEventStart(Date eventStart) { this.eventStart = eventStart; } public void setEventEnd(Date eventEnd) { this.eventEnd = eventEnd; } public void setSelectedCategories(Set selectedCategories) { this.selectedCategories = selectedCategories; } public void setAttachments(List attachments) { this.attachments = new ArrayList(attachmentsMaxCount); attachmentDescriptions = new ArrayList(attachmentsMaxCount); for (Resource attachment : attachments) { if(attachment instanceof FileResource) { this.attachments.add(attachment); attachmentDescriptions.add(((CmsNodeResource)attachment).getDescription()); } } } public void addAttachment(FileResource file) { attachments.add(file); attachmentDescriptions.add(file.getDescription()); } public FileResource removeAttachment(long fileId, CoralSession coralSession) throws EntityDoesNotExistException { FileResource file = FileResourceImpl.getFileResource(coralSession, fileId); int index = attachments.indexOf(file); attachments.remove(index); attachmentDescriptions.remove(index); return file; } public boolean isRemovalRequested() { return removalRequested; } public void setRemovalRequested(boolean removalRequested) { this.removalRequested = removalRequested; } public NavigationNodeResource getOrigin() { return origin; } public void setOrigin(NavigationNodeResource origin) { this.origin = origin; } public void setEditorialNote(String editorialNote) { this.editorialNote = editorialNote; } // utitily public void setValidationFailure(String validationFailure) { this.validationFailure = validationFailure; } public String getValidationFailure() { return validationFailure; } private Date getDate(Parameters parameters, String key) { if(parameters.isDefined(key) && parameters.get(key).trim().length() > 0) { return parameters.getDate(key); } else { return null; } } /** * Filters document content according to 'proposeDocument' cleanup profile. This method is * called when creating change proposal from document to avoid showing document author any * markup they could not edit using the restricted editor. * * @param htmlService HTML Service. */ public static String cleanupContent(String content, HTMLService htmlService) throws ProcessingException { if(content == null || content.trim().length() == 0) { return ""; } try { StringWriter errorWriter = new StringWriter(); Document contentDom = htmlService.textToDom4j(content, errorWriter, "proposeDocument"); if(contentDom == null) { throw new ProcessingException("HTML processing failure"); } else { htmlService.removeEmptyParas(contentDom); StringWriter contentWriter = new StringWriter(); htmlService.dom4jToText(contentDom, contentWriter, true); return contentWriter.toString(); } } catch(HTMLException e) { throw new ProcessingException("HTML processing failure", e); } } public void cleanupContent(HTMLService htmlService) throws ProcessingException { content = cleanupContent(content, htmlService); } private void setDate(TemplatingContext templatingContext, String key, Date value) { if(value != null) { templatingContext.put(key, value.getTime()); } } private String formatDate(Date date) { if(date != null) { return format.format(date); } else { return "Undefined"; } } /** * Strips HTML tags from the input string. */ public static String stripTags(String s) { return s == null ? s : s.replaceAll("<[^>]*?>", " "); } /** * Converts newline into HTML paragraphs. */ public static String makePara(String content) { content = content.replaceAll("\r\n", "\n"); content = content.replaceAll("\n+", "

\n

"); content = "

" + content + "

"; content = content.replaceAll("

\\s*

", ""); return content; } private String enc(String s) { return ENCODER.encodeAttribute(s, "UTF-16"); } private List enc(List l) { List result = new ArrayList(l.size()); for (String s : l) { result.add(enc(s)); } return l; } private String dec(String s) { return DECODER.decode(s); } public static String getAttachmentName(String fileName) { StringBuilder buff = new StringBuilder(); SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss"); buff.append(df.format(new Date())); // timestamp buff.append("_"); // separator fileName = StringUtils.iso1toUtf8(fileName); fileName = StringUtils.unaccentLatinChars(fileName); // unaccent accented latin characters fileName = fileName.replaceAll("[^A-Za-z0-9-_.]+", "_"); // squash everything except // alphanumerics and allowed // punctuation fileName = fileName.replaceAll("_{2,}", "_"); // contract sequences of multiple _ buff.append(fileName); return buff.toString(); } public void logProposal(Logger logger, DocumentNodeResource node) { // build proposals log StringBuilder proposalsDump = new StringBuilder(); proposalsDump.append("----------------------------------\n"); proposalsDump.append("Document id: ").append(node.getIdString()).append("\n"); proposalsDump.append("Document path: ").append(node.getPath()).append("\n"); proposalsDump.append("Created: ").append(node.getCreationTime()).append("\n"); proposalsDump.append("Created by: ").append(node.getCreatedBy().getName()).append("\n"); proposalsDump.append("Document title: ").append(title).append("\n"); proposalsDump.append("Event start: ").append(formatDate(eventStart)).append("\n"); proposalsDump.append("Event end: ").append(formatDate(eventEnd)).append("\n"); proposalsDump.append("Document validity start: ").append(formatDate(validityStart)).append( "\n"); proposalsDump.append("Document validity end: ").append(formatDate(validityEnd)) .append("\n"); proposalsDump.append("Organized by: ").append(organizedBy).append("\n"); proposalsDump.append("Organizer address: ").append(organizedAddress).append("\n"); proposalsDump.append("Organizer phone: ").append(organizedPhone).append("\n"); proposalsDump.append("Organizer fax: ").append(organizedFax).append("\n"); proposalsDump.append("Organizer email: ").append(organizedEmail).append("\n"); proposalsDump.append("Organizer URL: ").append(organizedWww).append("\n"); proposalsDump.append("Source name: ").append(sourceName).append("\n"); proposalsDump.append("Source URL: ").append(sourceUrl).append("\n"); proposalsDump.append("Proposer credentials: ").append(proposerCredentials).append("\n"); proposalsDump.append("Proposer email: ").append(proposerEmail).append("\n"); proposalsDump.append("Administrative description: ").append(proposerEmail).append("\n"); proposalsDump.append("Content: \n").append(content).append("\n"); logger.debug(proposalsDump.toString()); } }