# HG changeset patch
# User František Kučera <franta-hg@frantovo.cz>
# Date 1319100658 -7200
# Node ID a788bf0e10801af0a670d359e5e15337cc3503d5
# Parent  d843b4fee5dc222eafb777655d122adcb45278bf
Drupal: zpráva od uživatele se před uložením prožene přes XSLT případně Tidy.

diff -r d843b4fee5dc -r a788bf0e1080 src/org/sonews/storage/DrupalMessage.java
--- a/src/org/sonews/storage/DrupalMessage.java	Thu Oct 20 09:59:04 2011 +0200
+++ b/src/org/sonews/storage/DrupalMessage.java	Thu Oct 20 10:50:58 2011 +0200
@@ -43,6 +43,7 @@
 import javax.mail.internet.MimeMessage;
 import javax.mail.internet.MimeMultipart;
 import javax.xml.transform.Transformer;
+import javax.xml.transform.TransformerException;
 import javax.xml.transform.TransformerFactory;
 import javax.xml.transform.stream.StreamResult;
 import javax.xml.transform.stream.StreamSource;
@@ -76,7 +77,7 @@
 	 * @param rs ResultSet containing message data. No {@link ResultSet#next()} will be called, just values from current row will be read.
 	 * @param constructBody true if whole message should be constructed | false if we need only message headers (body will be dummy).
 	 */
-	public DrupalMessage(ResultSet rs, String myDomain, boolean constructBody) throws SQLException, UnsupportedEncodingException, MessagingException {
+	public DrupalMessage(ResultSet rs, String myDomain, boolean constructBody) throws SQLException, UnsupportedEncodingException, MessagingException, TransformerException, IOException {
 		super(Session.getDefaultInstance(System.getProperties()));
 
 		groupID = rs.getLong("group_id");
@@ -99,7 +100,13 @@
 
 			/** XHTML part */
 			MimeBodyPart htmlPart = new MimeBodyPart();
-			String xhtmlText = readXhtmlText(rs);
+			String xhtmlText = readXhtmlText(
+					rs.getString("text"),
+					rs.getString("subject"),
+					rs.getInt("parent_id"),
+					rs.getString("urlBase"),
+					rs.getString("wwwRead"),
+					rs.getString("wwwPost"));
 			htmlPart.setContent(xhtmlText, XHTML_CONTENT_TYPE);
 
 			/** Plain text part */
@@ -172,60 +179,72 @@
 		}
 	}
 
-	private String readXhtmlText(ResultSet rs) {
+	private String readXhtmlText(String text, String subject, long parentId, String urlBase, String wwwRead, String wwwPost) throws TransformerException, IOException {
 		/**
 		 * TODO: 
 		 *		- znovupoužívat XSL transformér
 		 *		- používat cache, ukládat si vygenerované články
 		 */
+		String inputText = makeSimpleXHTML(text);
+
+		TransformerFactory tf = TransformerFactory.newInstance();
+		Transformer paragraphTransformer = tf.newTransformer(new StreamSource(Resource.getAsStream("helpers/mimeXhtmlPart-make-paragraphs.xsl")));
+
+		String paragraphedText;
+		boolean tidyWasUsed = false;
 		try {
-			String inputText = makeSimpleXHTML(rs.getString("text"));
+			StringReader input = new StringReader(inputText);
+			StringWriter output = new StringWriter(2 * inputText.length());
+			paragraphTransformer.transform(new StreamSource(input), new StreamResult(output));
+			paragraphedText = output.toString();
+		} catch (Exception e) {
+			log.log(Level.FINER, "HTML input was shitty – Tidy had to be called.", e);
+			StringReader input = new StringReader(tidyXhtml(inputText));
+			StringWriter output = new StringWriter(2 * inputText.length());
+			paragraphTransformer.transform(new StreamSource(input), new StreamResult(output));
+			paragraphedText = output.toString();
+			tidyWasUsed = true;
+		}
 
-			TransformerFactory tf = TransformerFactory.newInstance();
-			Transformer paragraphTransformer = tf.newTransformer(new StreamSource(Resource.getAsStream("helpers/mimeXhtmlPart-make-paragraphs.xsl")));
+		Transformer xhtmlTransformer = tf.newTransformer(new StreamSource(Resource.getAsStream("helpers/mimeXhtmlPart.xsl")));
+		xhtmlTransformer.setParameter("isRoot", (parentId == 0));
+		xhtmlTransformer.setParameter("title", subject);
+		xhtmlTransformer.setParameter("urlBase", urlBase);
+		xhtmlTransformer.setParameter("wwwRead", wwwRead);
+		xhtmlTransformer.setParameter("wwwPost", wwwPost);
+		xhtmlTransformer.setParameter("headComment", String.format("Drupal-NNTP bridge. Transformed: %1$tc. Tidy had to be used: %2$b", new Date(), tidyWasUsed));
+		StringReader input = new StringReader(paragraphedText);
+		StringWriter output = new StringWriter(2 * paragraphedText.length());
+		xhtmlTransformer.transform(new StreamSource(input), new StreamResult(output));
 
-			String paragraphedText;
-			boolean tidyWasUsed = false;
-			try {
-				StringReader input = new StringReader(inputText);
-				StringWriter output = new StringWriter(2 * inputText.length());
-				paragraphTransformer.transform(new StreamSource(input), new StreamResult(output));
-				paragraphedText = output.toString();
-			} catch (Exception e) {
-				log.log(Level.FINER, "HTML input was shitty – Tidy had to be called.", e);
-				StringReader input = new StringReader(tidyXhtml(inputText));
-				StringWriter output = new StringWriter(2 * inputText.length());
-				paragraphTransformer.transform(new StreamSource(input), new StreamResult(output));
-				paragraphedText = output.toString();
-				tidyWasUsed = true;
-			}
-
-			Transformer xhtmlTransformer = tf.newTransformer(new StreamSource(Resource.getAsStream("helpers/mimeXhtmlPart.xsl")));
-			xhtmlTransformer.setParameter("isRoot", (rs.getInt("parent_id") == 0));
-			xhtmlTransformer.setParameter("title", rs.getString("subject"));
-			xhtmlTransformer.setParameter("urlBase", rs.getString("urlBase"));
-			xhtmlTransformer.setParameter("wwwRead", rs.getString("wwwRead"));
-			xhtmlTransformer.setParameter("wwwPost", rs.getString("wwwPost"));
-			xhtmlTransformer.setParameter("headComment", String.format("Drupal-NNTP bridge. Transformed: %1$tc. Tidy had to be used: %2$b", new Date(), tidyWasUsed));
-			StringReader input = new StringReader(paragraphedText);
-			StringWriter output = new StringWriter(2 * paragraphedText.length());
-			xhtmlTransformer.transform(new StreamSource(input), new StreamResult(output));
-
-			return output.toString();
-		} catch (Exception e) {
-			/**
-			 * TODO: lepší ošetření chyby
-			 */
-			log.log(Level.WARNING, "Error while transforming article to XHTML", e);
-			return makeSimpleXHTML("<p>Při transformaci příspěvku bohužel došlo k chybě.</p>");
-		}
+		return output.toString();
 	}
 
+	/**
+	 * Does not parse XML works just with text.
+	 * @param body XHTML fragment that should be put between &lt;body&gt; and &lt;/body&gt;
+	 * @return simple XHTML document (body wrapped in html and body tags)
+	 */
 	private static String makeSimpleXHTML(String body) {
 		return "<html xmlns=\"http://www.w3.org/1999/xhtml\"><body>" + body + "</body></html>";
 	}
 
 	/**
+	 * Does not parse XML works just with text.
+	 * @param xhtml whole XHTML page
+	 * @return content between &lt;body&gt; and &lt;/body&gt; tags.
+	 */
+	private static String makeFragmentXHTML(String xhtml) {
+		final String startTag = "<body>";
+		final String endTag = "</body>";
+
+		int start = xhtml.indexOf(startTag) + startTag.length();
+		int end = xhtml.lastIndexOf(endTag);
+
+		return xhtml.substring(start, end);
+	}
+
+	/**
 	 * TODO: refaktorovat, přesunout
 	 */
 	private static String tidyXhtml(String inputText) throws IOException {
@@ -425,4 +444,33 @@
 		writeTo(baos, skipHeaders.toArray(new String[skipHeaders.size()]));
 		return baos.toByteArray();
 	}
+
+	/**
+	 * Transforms message content to valid XHTML and strips html and body tags.
+	 * When receiving message from user through NNTP 
+	 * this method is used to get text that should be saved into databse.
+	 * @return XHTML fragment – content between &lt;body&gt; and &lt;/body&gt; tags.
+	 */
+	public String getBodyXhtmlFragment() throws StorageBackendException {
+		/**
+		 * TODO: podporovat i zprávy přímo v HTML a multipart.
+		 */
+		try {
+			Object c = getContent();
+			if (isMimeType("text/plain") && c instanceof String) {
+				String xhtml = readXhtmlText(
+						(String) c,
+						getSubject(),
+						getParentID(),
+						null,
+						null,
+						null);
+				return makeFragmentXHTML(xhtml);
+			} else {
+				throw new StorageBackendException("Only text/plain messages are supported for now – post it as plain text please.");
+			}
+		} catch (Exception e) {
+			throw new StorageBackendException(e);
+		}
+	}
 }
diff -r d843b4fee5dc -r a788bf0e1080 src/org/sonews/storage/impl/DrupalDatabase.java
--- a/src/org/sonews/storage/impl/DrupalDatabase.java	Thu Oct 20 09:59:04 2011 +0200
+++ b/src/org/sonews/storage/impl/DrupalDatabase.java	Thu Oct 20 10:50:58 2011 +0200
@@ -414,26 +414,16 @@
 				if (parentID == null || groupID == null) {
 					throw new StorageBackendException("No valid In-Reply-To header was found → rejecting posted message.");
 				} else {
-					if (m.isMimeType("text/plain")) {
-						Object content = m.getContent();
-						if (content instanceof String) {
-							String subject = m.getSubject();
-							String text = (String) content;
 
-							/**
-							 * TODO: validovat a transformovat text
-							 * (v současné době se o to stará až Drupal při výstupu)
-							 */
-							if (subject == null || subject.length() < 1) {
-								subject = text.substring(0, Math.min(10, text.length()));
-							}
+					String subject = m.getSubject();
+					String text = m.getBodyXhtmlFragment();
 
-							insertArticle(article.getAuthenticatedUser(), subject, text, parentID, groupID);
-							log.log(Level.INFO, "User ''{0}'' has posted an article", article.getAuthenticatedUser());
-						}
-					} else {
-						throw new StorageBackendException("Only text/plain messages are supported for now – post it as plain text please.");
+					if (subject == null || subject.length() < 1) {
+						subject = text.substring(0, Math.min(10, text.length()));
 					}
+
+					insertArticle(article.getAuthenticatedUser(), subject, text, parentID, groupID);
+					log.log(Level.INFO, "User ''{0}'' has posted an article", article.getAuthenticatedUser());
 				}
 			} catch (Exception e) {
 				throw new StorageBackendException(e);