Last active
September 8, 2017 20:37
-
-
Save grugnog/35874bf250edc6bd531edae152b95e43 to your computer and use it in GitHub Desktop.
Stylesheet to convert a JIRA report list exported as XML into basic markdown, including all non-empty fields.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<!-- | |
Stylesheet to convert a JIRA report list exported as XML into | |
basic markdown, including all non-empty fields. | |
To run: saxon-xslt -xsl:jira2md.xsl jira-export.xml | |
--> | |
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:functx="http://www.functx.com" xmlns:fn="http://www.w3.org/2005/xpath-functions" xmlns:xs="http://www.w3.org/2001/XMLSchema" version="3.0"> | |
<xsl:output omit-xml-declaration="yes" method="text" indent="no"/> | |
<xsl:strip-space elements="*"/> | |
<xsl:template match="/"> | |
<xsl:apply-templates select="rss/channel"/> | |
</xsl:template> | |
<xsl:variable name="vAllowedSymbols" select="'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789- '"/> | |
<xsl:template match="rss/channel"> | |
<xsl:for-each select="item"> | |
<!-- Sanitized file name --> | |
<xsl:result-document method="text" indent="no" href="{substring(normalize-space(translate(title, translate(title, $vAllowedSymbols, ''), '')),1,250)}.md"> | |
<xsl:apply-templates select="."/> | |
</xsl:result-document> | |
</xsl:for-each> | |
</xsl:template> | |
<xsl:template match="item"> | |
<xsl:text># </xsl:text> | |
<xsl:value-of select="title"/> | |
<xsl:for-each select="*"> | |
<xsl:choose> | |
<!-- Only add sections with actual content --> | |
<xsl:when test="string-length(.) > 0"> | |
<xsl:text>
</xsl:text> | |
<xsl:text>## </xsl:text> | |
<xsl:value-of select="concat(upper-case(substring(name(.), 1, 1)), lower-case(substring(name(.), 2)))"/> | |
<xsl:text>
</xsl:text> | |
<xsl:choose> | |
<!-- Loop over comments adding author and date --> | |
<xsl:when test="name(.) = 'comments'"> | |
<xsl:for-each select="comment"> | |
<xsl:text>### Comment by </xsl:text> | |
<xsl:value-of select="./@author" disable-output-escaping="yes"/> | |
<xsl:text>
</xsl:text> | |
<xsl:text>*</xsl:text> | |
<xsl:value-of select="./@created" disable-output-escaping="yes"/> | |
<xsl:text>*</xsl:text> | |
<xsl:text>

</xsl:text> | |
<!-- Process comment HTML to markdown --> | |
<xsl:for-each select="parse-xml-fragment(.)"> | |
<xsl:apply-templates select="* | node()"/> | |
</xsl:for-each> | |
</xsl:for-each> | |
</xsl:when> | |
<xsl:when test="name(.) = 'description'"> | |
<!-- Process description HTML to markdown --> | |
<xsl:for-each select="parse-xml-fragment(.)"> | |
<xsl:apply-templates select="* | node()"/> | |
</xsl:for-each> | |
</xsl:when> | |
<xsl:when test="count(*) > 1"> | |
<xsl:for-each select="*"> | |
<xsl:text>* </xsl:text> | |
<xsl:apply-templates select="* | node()"/> | |
<xsl:text>
</xsl:text> | |
</xsl:for-each> | |
<xsl:text>
</xsl:text> | |
</xsl:when> | |
<xsl:otherwise> | |
<xsl:apply-templates select="* | node()"/> | |
<xsl:text>
</xsl:text> | |
</xsl:otherwise> | |
</xsl:choose> | |
</xsl:when> | |
</xsl:choose> | |
</xsl:for-each> | |
</xsl:template> | |
<xsl:template match="title"> | |
<xsl:value-of select="."/> | |
</xsl:template> | |
<xsl:template match="customfieldname|name"> | |
<xsl:text>**</xsl:text> | |
<xsl:value-of select="."/> | |
<xsl:text>:</xsl:text> | |
<xsl:text>** </xsl:text> | |
</xsl:template> | |
<!-- HTML to markdown rules courtesy of https://gist.github.com/gabetax/1702774 --> | |
<!-- Required for li indenting --> | |
<xsl:function name="functx:repeat-string" as="xs:string"> | |
<xsl:param name="stringToRepeat" as="xs:string?"/> | |
<xsl:param name="count" as="xs:integer"/> | |
<xsl:sequence select="string-join((for $i in 1 to $count return $stringToRepeat), '')"/> | |
</xsl:function> | |
<xsl:template match="li"> | |
<xsl:value-of select="functx:repeat-string(' ', count(ancestor::li))"/> | |
<xsl:choose> | |
<xsl:when test="name(..) = 'ol'"> | |
<xsl:value-of select="position()"/> | |
<xsl:text>. </xsl:text> | |
</xsl:when> | |
<xsl:otherwise> | |
<xsl:text>* </xsl:text> | |
</xsl:otherwise> | |
</xsl:choose> | |
<xsl:value-of select="normalize-space(concat(., ' '))"/> | |
<xsl:apply-templates select="* except (ul|ol)"/> | |
<xsl:text> | |
</xsl:text> | |
<xsl:apply-templates select="ul|ol"/> | |
</xsl:template> | |
<!-- Don't process text() nodes for these - prevents unnecessary whitespace --> | |
<xsl:template match="ul|ol"> | |
<xsl:apply-templates select="* except text()"/> | |
</xsl:template> | |
<xsl:template match="a"> | |
<xsl:text>[</xsl:text> | |
<xsl:apply-templates select="node()|text()"/> | |
<xsl:text>](</xsl:text> | |
<xsl:value-of select="@href"/> | |
<xsl:text>)</xsl:text> | |
</xsl:template> | |
<xsl:template match="img"> | |
<xsl:text></xsl:text> | |
</xsl:template> | |
<xsl:template match="strong|b"> | |
<xsl:text>**</xsl:text> | |
<xsl:value-of select="."/> | |
<xsl:text>**</xsl:text> | |
</xsl:template> | |
<xsl:template match="em|i"> | |
<xsl:text>*</xsl:text> | |
<xsl:value-of select="."/> | |
<xsl:text>*</xsl:text> | |
</xsl:template> | |
<xsl:template match="code"> | |
<!-- todo: skip the ` if inside a pre --> | |
<xsl:text>`</xsl:text> | |
<xsl:value-of select="."/> | |
<xsl:text>`</xsl:text> | |
</xsl:template> | |
<xsl:template match="br"> | |
<xsl:text> | |
</xsl:text> | |
</xsl:template> | |
<!-- Block elements --> | |
<xsl:template match="hr"> | |
<xsl:text>---- | |
</xsl:text> | |
</xsl:template> | |
<xsl:template match="p|div"> | |
<xsl:apply-templates select="*|text()"/> | |
<xsl:text> | |
</xsl:text> | |
<!-- Block element --> | |
</xsl:template> | |
<xsl:template match="*[matches(name(), 'h[1-6]')]"> | |
<xsl:value-of select="functx:repeat-string('#', xs:integer(substring(name(), 2)))"/> | |
<xsl:text> | |
</xsl:text> | |
<xsl:apply-templates select="*|text()"/> | |
<xsl:text> | |
</xsl:text> | |
<!-- Block element --> | |
</xsl:template> | |
<xsl:template match="pre"> | |
<xsl:text> | |
</xsl:text> | |
<xsl:value-of select="replace(concat(., ' '), ' ', ' ')"/> | |
<xsl:text> | |
</xsl:text> | |
<!-- Block element --> | |
</xsl:template> | |
<xsl:template match="blockquote"> | |
<xsl:text>> </xsl:text> | |
<xsl:value-of select="replace(concat(., ' '), ' ', ' > ')"/> | |
<xsl:text> | |
</xsl:text> | |
<!-- Block element --> | |
</xsl:template> | |
</xsl:stylesheet> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment