Drupal: zpráva od uživatele se před uložením prožene přes XSLT případně Tidy.
authorFrantišek Kučera <franta-hg@frantovo.cz>
Thu, 20 Oct 2011 10:50:58 +0200
changeset 103a788bf0e1080
parent 102 d843b4fee5dc
child 104 b4c8a2760d6f
Drupal: zpráva od uživatele se před uložením prožene přes XSLT případně Tidy.
src/org/sonews/storage/DrupalMessage.java
src/org/sonews/storage/impl/DrupalDatabase.java
     1.1 --- a/src/org/sonews/storage/DrupalMessage.java	Thu Oct 20 09:59:04 2011 +0200
     1.2 +++ b/src/org/sonews/storage/DrupalMessage.java	Thu Oct 20 10:50:58 2011 +0200
     1.3 @@ -43,6 +43,7 @@
     1.4  import javax.mail.internet.MimeMessage;
     1.5  import javax.mail.internet.MimeMultipart;
     1.6  import javax.xml.transform.Transformer;
     1.7 +import javax.xml.transform.TransformerException;
     1.8  import javax.xml.transform.TransformerFactory;
     1.9  import javax.xml.transform.stream.StreamResult;
    1.10  import javax.xml.transform.stream.StreamSource;
    1.11 @@ -76,7 +77,7 @@
    1.12  	 * @param rs ResultSet containing message data. No {@link ResultSet#next()} will be called, just values from current row will be read.
    1.13  	 * @param constructBody true if whole message should be constructed | false if we need only message headers (body will be dummy).
    1.14  	 */
    1.15 -	public DrupalMessage(ResultSet rs, String myDomain, boolean constructBody) throws SQLException, UnsupportedEncodingException, MessagingException {
    1.16 +	public DrupalMessage(ResultSet rs, String myDomain, boolean constructBody) throws SQLException, UnsupportedEncodingException, MessagingException, TransformerException, IOException {
    1.17  		super(Session.getDefaultInstance(System.getProperties()));
    1.18  
    1.19  		groupID = rs.getLong("group_id");
    1.20 @@ -99,7 +100,13 @@
    1.21  
    1.22  			/** XHTML part */
    1.23  			MimeBodyPart htmlPart = new MimeBodyPart();
    1.24 -			String xhtmlText = readXhtmlText(rs);
    1.25 +			String xhtmlText = readXhtmlText(
    1.26 +					rs.getString("text"),
    1.27 +					rs.getString("subject"),
    1.28 +					rs.getInt("parent_id"),
    1.29 +					rs.getString("urlBase"),
    1.30 +					rs.getString("wwwRead"),
    1.31 +					rs.getString("wwwPost"));
    1.32  			htmlPart.setContent(xhtmlText, XHTML_CONTENT_TYPE);
    1.33  
    1.34  			/** Plain text part */
    1.35 @@ -172,60 +179,72 @@
    1.36  		}
    1.37  	}
    1.38  
    1.39 -	private String readXhtmlText(ResultSet rs) {
    1.40 +	private String readXhtmlText(String text, String subject, long parentId, String urlBase, String wwwRead, String wwwPost) throws TransformerException, IOException {
    1.41  		/**
    1.42  		 * TODO: 
    1.43  		 *		- znovupoužívat XSL transformér
    1.44  		 *		- používat cache, ukládat si vygenerované články
    1.45  		 */
    1.46 +		String inputText = makeSimpleXHTML(text);
    1.47 +
    1.48 +		TransformerFactory tf = TransformerFactory.newInstance();
    1.49 +		Transformer paragraphTransformer = tf.newTransformer(new StreamSource(Resource.getAsStream("helpers/mimeXhtmlPart-make-paragraphs.xsl")));
    1.50 +
    1.51 +		String paragraphedText;
    1.52 +		boolean tidyWasUsed = false;
    1.53  		try {
    1.54 -			String inputText = makeSimpleXHTML(rs.getString("text"));
    1.55 +			StringReader input = new StringReader(inputText);
    1.56 +			StringWriter output = new StringWriter(2 * inputText.length());
    1.57 +			paragraphTransformer.transform(new StreamSource(input), new StreamResult(output));
    1.58 +			paragraphedText = output.toString();
    1.59 +		} catch (Exception e) {
    1.60 +			log.log(Level.FINER, "HTML input was shitty – Tidy had to be called.", e);
    1.61 +			StringReader input = new StringReader(tidyXhtml(inputText));
    1.62 +			StringWriter output = new StringWriter(2 * inputText.length());
    1.63 +			paragraphTransformer.transform(new StreamSource(input), new StreamResult(output));
    1.64 +			paragraphedText = output.toString();
    1.65 +			tidyWasUsed = true;
    1.66 +		}
    1.67  
    1.68 -			TransformerFactory tf = TransformerFactory.newInstance();
    1.69 -			Transformer paragraphTransformer = tf.newTransformer(new StreamSource(Resource.getAsStream("helpers/mimeXhtmlPart-make-paragraphs.xsl")));
    1.70 +		Transformer xhtmlTransformer = tf.newTransformer(new StreamSource(Resource.getAsStream("helpers/mimeXhtmlPart.xsl")));
    1.71 +		xhtmlTransformer.setParameter("isRoot", (parentId == 0));
    1.72 +		xhtmlTransformer.setParameter("title", subject);
    1.73 +		xhtmlTransformer.setParameter("urlBase", urlBase);
    1.74 +		xhtmlTransformer.setParameter("wwwRead", wwwRead);
    1.75 +		xhtmlTransformer.setParameter("wwwPost", wwwPost);
    1.76 +		xhtmlTransformer.setParameter("headComment", String.format("Drupal-NNTP bridge. Transformed: %1$tc. Tidy had to be used: %2$b", new Date(), tidyWasUsed));
    1.77 +		StringReader input = new StringReader(paragraphedText);
    1.78 +		StringWriter output = new StringWriter(2 * paragraphedText.length());
    1.79 +		xhtmlTransformer.transform(new StreamSource(input), new StreamResult(output));
    1.80  
    1.81 -			String paragraphedText;
    1.82 -			boolean tidyWasUsed = false;
    1.83 -			try {
    1.84 -				StringReader input = new StringReader(inputText);
    1.85 -				StringWriter output = new StringWriter(2 * inputText.length());
    1.86 -				paragraphTransformer.transform(new StreamSource(input), new StreamResult(output));
    1.87 -				paragraphedText = output.toString();
    1.88 -			} catch (Exception e) {
    1.89 -				log.log(Level.FINER, "HTML input was shitty – Tidy had to be called.", e);
    1.90 -				StringReader input = new StringReader(tidyXhtml(inputText));
    1.91 -				StringWriter output = new StringWriter(2 * inputText.length());
    1.92 -				paragraphTransformer.transform(new StreamSource(input), new StreamResult(output));
    1.93 -				paragraphedText = output.toString();
    1.94 -				tidyWasUsed = true;
    1.95 -			}
    1.96 -
    1.97 -			Transformer xhtmlTransformer = tf.newTransformer(new StreamSource(Resource.getAsStream("helpers/mimeXhtmlPart.xsl")));
    1.98 -			xhtmlTransformer.setParameter("isRoot", (rs.getInt("parent_id") == 0));
    1.99 -			xhtmlTransformer.setParameter("title", rs.getString("subject"));
   1.100 -			xhtmlTransformer.setParameter("urlBase", rs.getString("urlBase"));
   1.101 -			xhtmlTransformer.setParameter("wwwRead", rs.getString("wwwRead"));
   1.102 -			xhtmlTransformer.setParameter("wwwPost", rs.getString("wwwPost"));
   1.103 -			xhtmlTransformer.setParameter("headComment", String.format("Drupal-NNTP bridge. Transformed: %1$tc. Tidy had to be used: %2$b", new Date(), tidyWasUsed));
   1.104 -			StringReader input = new StringReader(paragraphedText);
   1.105 -			StringWriter output = new StringWriter(2 * paragraphedText.length());
   1.106 -			xhtmlTransformer.transform(new StreamSource(input), new StreamResult(output));
   1.107 -
   1.108 -			return output.toString();
   1.109 -		} catch (Exception e) {
   1.110 -			/**
   1.111 -			 * TODO: lepší ošetření chyby
   1.112 -			 */
   1.113 -			log.log(Level.WARNING, "Error while transforming article to XHTML", e);
   1.114 -			return makeSimpleXHTML("<p>Při transformaci příspěvku bohužel došlo k chybě.</p>");
   1.115 -		}
   1.116 +		return output.toString();
   1.117  	}
   1.118  
   1.119 +	/**
   1.120 +	 * Does not parse XML works just with text.
   1.121 +	 * @param body XHTML fragment that should be put between &lt;body&gt; and &lt;/body&gt;
   1.122 +	 * @return simple XHTML document (body wrapped in html and body tags)
   1.123 +	 */
   1.124  	private static String makeSimpleXHTML(String body) {
   1.125  		return "<html xmlns=\"http://www.w3.org/1999/xhtml\"><body>" + body + "</body></html>";
   1.126  	}
   1.127  
   1.128  	/**
   1.129 +	 * Does not parse XML works just with text.
   1.130 +	 * @param xhtml whole XHTML page
   1.131 +	 * @return content between &lt;body&gt; and &lt;/body&gt; tags.
   1.132 +	 */
   1.133 +	private static String makeFragmentXHTML(String xhtml) {
   1.134 +		final String startTag = "<body>";
   1.135 +		final String endTag = "</body>";
   1.136 +
   1.137 +		int start = xhtml.indexOf(startTag) + startTag.length();
   1.138 +		int end = xhtml.lastIndexOf(endTag);
   1.139 +
   1.140 +		return xhtml.substring(start, end);
   1.141 +	}
   1.142 +
   1.143 +	/**
   1.144  	 * TODO: refaktorovat, přesunout
   1.145  	 */
   1.146  	private static String tidyXhtml(String inputText) throws IOException {
   1.147 @@ -425,4 +444,33 @@
   1.148  		writeTo(baos, skipHeaders.toArray(new String[skipHeaders.size()]));
   1.149  		return baos.toByteArray();
   1.150  	}
   1.151 +
   1.152 +	/**
   1.153 +	 * Transforms message content to valid XHTML and strips html and body tags.
   1.154 +	 * When receiving message from user through NNTP 
   1.155 +	 * this method is used to get text that should be saved into databse.
   1.156 +	 * @return XHTML fragment – content between &lt;body&gt; and &lt;/body&gt; tags.
   1.157 +	 */
   1.158 +	public String getBodyXhtmlFragment() throws StorageBackendException {
   1.159 +		/**
   1.160 +		 * TODO: podporovat i zprávy přímo v HTML a multipart.
   1.161 +		 */
   1.162 +		try {
   1.163 +			Object c = getContent();
   1.164 +			if (isMimeType("text/plain") && c instanceof String) {
   1.165 +				String xhtml = readXhtmlText(
   1.166 +						(String) c,
   1.167 +						getSubject(),
   1.168 +						getParentID(),
   1.169 +						null,
   1.170 +						null,
   1.171 +						null);
   1.172 +				return makeFragmentXHTML(xhtml);
   1.173 +			} else {
   1.174 +				throw new StorageBackendException("Only text/plain messages are supported for now – post it as plain text please.");
   1.175 +			}
   1.176 +		} catch (Exception e) {
   1.177 +			throw new StorageBackendException(e);
   1.178 +		}
   1.179 +	}
   1.180  }
     2.1 --- a/src/org/sonews/storage/impl/DrupalDatabase.java	Thu Oct 20 09:59:04 2011 +0200
     2.2 +++ b/src/org/sonews/storage/impl/DrupalDatabase.java	Thu Oct 20 10:50:58 2011 +0200
     2.3 @@ -414,26 +414,16 @@
     2.4  				if (parentID == null || groupID == null) {
     2.5  					throw new StorageBackendException("No valid In-Reply-To header was found → rejecting posted message.");
     2.6  				} else {
     2.7 -					if (m.isMimeType("text/plain")) {
     2.8 -						Object content = m.getContent();
     2.9 -						if (content instanceof String) {
    2.10 -							String subject = m.getSubject();
    2.11 -							String text = (String) content;
    2.12  
    2.13 -							/**
    2.14 -							 * TODO: validovat a transformovat text
    2.15 -							 * (v současné době se o to stará až Drupal při výstupu)
    2.16 -							 */
    2.17 -							if (subject == null || subject.length() < 1) {
    2.18 -								subject = text.substring(0, Math.min(10, text.length()));
    2.19 -							}
    2.20 +					String subject = m.getSubject();
    2.21 +					String text = m.getBodyXhtmlFragment();
    2.22  
    2.23 -							insertArticle(article.getAuthenticatedUser(), subject, text, parentID, groupID);
    2.24 -							log.log(Level.INFO, "User ''{0}'' has posted an article", article.getAuthenticatedUser());
    2.25 -						}
    2.26 -					} else {
    2.27 -						throw new StorageBackendException("Only text/plain messages are supported for now – post it as plain text please.");
    2.28 +					if (subject == null || subject.length() < 1) {
    2.29 +						subject = text.substring(0, Math.min(10, text.length()));
    2.30  					}
    2.31 +
    2.32 +					insertArticle(article.getAuthenticatedUser(), subject, text, parentID, groupID);
    2.33 +					log.log(Level.INFO, "User ''{0}'' has posted an article", article.getAuthenticatedUser());
    2.34  				}
    2.35  			} catch (Exception e) {
    2.36  				throw new StorageBackendException(e);