chiark / gitweb /
Adds processing of description formatting
authorCiaran Gultnieks <ciaran@ciarang.com>
Mon, 17 Sep 2012 20:49:56 +0000 (21:49 +0100)
committerCiaran Gultnieks <ciaran@ciarang.com>
Mon, 17 Sep 2012 20:49:56 +0000 (21:49 +0100)
fdroidserver/common.py
fdroidserver/update.py

index 587b2c2c840336a07f9a44930cfaf214a271f0fe..a1aa0f9f6d17051cd26349fe6480b9253c5951c6 100644 (file)
@@ -21,6 +21,7 @@ import shutil
 import subprocess
 import time
 import operator
+import cgi
 
 def getvcs(vcstype, remote, local, sdk_path):
     if vcstype == 'git':
@@ -612,19 +613,177 @@ def read_metadata(verbose=False):
         apps.append(parse_metadata(metafile, verbose=verbose))
     return apps
 
+# Formatter for descriptions. Create an instance, and call parseline() with
+# each line of the description source from the metadata. At the end, call
+# end() and then text_plain, text_wiki and text_html will contain the result.
+class DescriptionFormatter:
+    stNONE = 0
+    stPARA = 1
+    stUL = 2
+    stOL = 3
+    bold = False
+    ital = False
+    state = stNONE
+    text_plain = ''
+    text_wiki = ''
+    text_html = ''
+    linkResolver = None
+    def __init__(self, linkres):
+        self.linkResolver = linkres
+    def endcur(self, notstates=None):
+        if notstates and self.state in notstates:
+            return
+        if self.state == self.stPARA:
+            self.endpara()
+        elif self.state == self.stUL:
+            self.endul()
+        elif self.state == self.stOL:
+            self.endol()
+    def endpara(self):
+        self.text_plain += '\n'
+        self.text_html += '</p>'
+        self.state = self.stNONE
+    def endul(self):
+        self.text_html += '</ul>'
+        self.state = self.stNONE
+    def endol(self):
+        self.text_html += '</ol>'
+        self.state = self.stNONE
+
+    def formatted(self, txt, html):
+        formatted = ''
+        if html:
+            txt = cgi.escape(txt)
+        while True:
+            index = txt.find("''")
+            if index == -1:
+                return formatted + txt
+            formatted += txt[:index]
+            txt = txt[index:]
+            if txt.startswith("'''"):
+                if html:
+                    if self.bold:
+                        formatted += '</b>'
+                    else:
+                        formatted += '<b>'
+                self.bold = not self.bold
+                txt = txt[3:]
+            else:
+                if html:
+                    if self.ital:
+                        formatted += '</i>'
+                    else:
+                        formatted += '<i>'
+                self.ital = not self.ital
+                txt = txt[2:]
+
+
+    def linkify(self, txt):
+        linkified_plain = ''
+        linkified_html = ''
+        while True:
+            index = txt.find("[")
+            if index == -1:
+                return (linkified_plain + self.formatted(txt, False), linkified_html + self.formatted(txt, True))
+            linkified_plain += self.formatted(txt[:index], False)
+            linkified_html += self.formatted(txt[:index], True)
+            txt = txt[index:]
+            if txt.startswith("[["):
+                index = txt.find("]]")
+                if index == -1:
+                    raise MetaDataException("Unterminated ]]")
+                url = txt[2:index]
+                if self.linkResolver:
+                    url, urltext = self.linkResolver(url)
+                else:
+                    urltext = url
+                linkified_html += '<a href="' + url + '">' + cgi.escape(urltext) + '</a>'
+                linkified_plain += urltext
+                txt = txt[index+2:]
+            else:
+                index = txt.find("]")
+                if index == -1:
+                    raise MetaDataException("Unterminated ]")
+                url = txt[2:index]
+                index2 = url.find(' ')
+                if index2 == -1:
+                    urltxt = url
+                else:
+                    urltxt = url[index2 + 1]
+                    url = url[:index]
+                linkified_html += '<a href="' + url + '">' + cgi.escape(urltxt) + '</a>'
+                linkified_plain += urltxt
+                if urltxt != url:
+                    linkified_plain += ' (' + url + ')'
+                txt = txt[index+1:]
+
+    def addtext(self, txt):
+        p, h = self.linkify(txt)
+        self.text_plain += p
+        self.text_html += h
+
+    def parseline(self, line):
+        self.text_wiki += line + '\n'
+        if len(line) == 0:
+            self.endcur()
+        elif line.startswith('*'):
+            self.endcur([self.stUL])
+            if self.state != self.stUL:
+                self.text_html += '<ul>'
+                self.state = self.stUL
+            self.text_html += '<li>'
+            self.text_plain += '*'
+            self.addtext(line[1:])
+            self.text_html += '</li>'
+        elif line.startswith('#'):
+            self.endcur([self.stOL])
+            if self.state != self.stOL:
+                self.text_html += '<ol>'
+                self.state = self.stOL
+            self.text_html += '<li>'
+            self.text_plain += '*' #TODO: lazy - put the numbers in!
+            self.addtext(line[1:])
+            self.text_html += '</li>'
+        else:
+            self.endcur([self.stPARA])
+            if self.state == self.stNONE:
+                self.text_html += '<p>'
+                self.state = self.stPARA
+            elif self.state == self.stPARA:
+                self.text_html += ' '
+                self.text_plain += ' '
+            self.addtext(line)
+
+    def end(self):
+        self.endcur()
 
 # Parse multiple lines of description as written in a metadata file, returning
-# a single string.
-def parse_description(lines):
-    text = ''
+# a single string in plain text format.
+def description_plain(lines, linkres):
+    ps = DescriptionFormatter(linkres)
     for line in lines:
-        if len(line) == 0:
-            text += '\n\n'
-        else:
-            if not text.endswith('\n') and len(text) > 0:
-                text += ' '
-            text += line
-    return text
+        ps.parseline(line)
+    ps.end()
+    return ps.text_plain
+
+# Parse multiple lines of description as written in a metadata file, returning
+# a single string in wiki format.
+def description_wiki(lines):
+    ps = DescriptionFormatter(None)
+    for line in lines:
+        ps.parseline(line)
+    ps.end()
+    return ps.text_wiki
+
+# Parse multiple lines of description as written in a metadata file, returning
+# a single string in HTML format.
+def description_html(lines,linkres):
+    ps = DescriptionFormatter(linkres)
+    for line in lines:
+        ps.parseline(line)
+    ps.end()
+    return ps.text_html
+
 
 
 # Extract some information from the AndroidManifest.xml at the given path.
index 89cb167672750f9efca0b5a8589bed2f6dabef78..a7dceaaf400fde6a67509e76dc2e37a642dc1907 100644 (file)
@@ -30,7 +30,7 @@ from xml.dom.minidom import Document
 from optparse import OptionParser
 import time
 import common
-
+from common import MetaDataException
 
 # Update the wiki. 'apps' is a list of all applications and everything we know
 # about them, and 'apks' likewise. Set 'verbose' to True for verbose output.
@@ -66,7 +66,7 @@ def update_wiki(apps, apks, verbose=False):
         wikidata += " - [http://f-droid.org/repository/browse/?fdid=" + app['id'] + " view in repository]\n\n"
 
         wikidata += "=Description=\n"
-        wikidata += common.parse_description(app['Description']) + "\n"
+        wikidata += common.description_wiki(app['Description']) + "\n"
 
         # Get a list of all packages for this application...
         apklist = []
@@ -438,6 +438,10 @@ def main():
         el = doc.createElement(name)
         el.appendChild(doc.createTextNode(value))
         parent.appendChild(el)
+    def addElementCDATA(name, value, doc, parent):
+        el = doc.createElement(name)
+        el.appendChild(doc.createCDATASection(value))
+        parent.appendChild(el)
 
     root = doc.createElement("fdroid")
     doc.appendChild(root)
@@ -510,8 +514,15 @@ def main():
                 addElement('name', app['Name'], doc, apel)
                 addElement('summary', app['Summary'], doc, apel)
                 addElement('icon', app['icon'], doc, apel)
+                def linkres(link):
+                    for app in apps:
+                        if app['id'] == link:
+                            return ("fdroid.app:" + link, app['Name'])
+                    raise MetaDataException("Cannot resolve app id " + link)
                 addElement('description',
-                        common.parse_description(app['Description']), doc, apel)
+                        common.description_plain(app['Description'], linkres), doc, apel)
+                addElement('desc', 
+                        common.description_html(app['Description'], linkres), doc, apel)
                 addElement('license', app['License'], doc, apel)
                 if 'Category' in app:
                     # We put the first (primary) category in LAST, which will have