From 640a6325f09499bc353c0e6f730ccb754131c34f Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Mon, 17 Sep 2012 21:49:56 +0100 Subject: [PATCH 1/5] Adds processing of description formatting --- fdroidserver/common.py | 179 ++++++++++++++++++++++++++++++++++++++--- fdroidserver/update.py | 17 +++- 2 files changed, 183 insertions(+), 13 deletions(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 587b2c2c..a1aa0f9f 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -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 += '

' + self.state = self.stNONE + def endul(self): + self.text_html += '' + self.state = self.stNONE + def endol(self): + self.text_html += '' + 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 += '' + else: + formatted += '' + self.bold = not self.bold + txt = txt[3:] + else: + if html: + if self.ital: + formatted += '' + else: + formatted += '' + 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 += '' + cgi.escape(urltext) + '' + 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 += '' + cgi.escape(urltxt) + '' + 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 += '
    ' + self.state = self.stUL + self.text_html += '
  • ' + self.text_plain += '*' + self.addtext(line[1:]) + self.text_html += '
  • ' + elif line.startswith('#'): + self.endcur([self.stOL]) + if self.state != self.stOL: + self.text_html += '
      ' + self.state = self.stOL + self.text_html += '
    1. ' + self.text_plain += '*' #TODO: lazy - put the numbers in! + self.addtext(line[1:]) + self.text_html += '
    2. ' + else: + self.endcur([self.stPARA]) + if self.state == self.stNONE: + self.text_html += '

      ' + 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. diff --git a/fdroidserver/update.py b/fdroidserver/update.py index 89cb1676..a7dceaaf 100644 --- a/fdroidserver/update.py +++ b/fdroidserver/update.py @@ -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 From 4691cfa5155007befb56cd0e161ac9b99eab0332 Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Mon, 17 Sep 2012 22:12:26 +0100 Subject: [PATCH 2/5] Fixes link title truncation --- fdroidserver/common.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fdroidserver/common.py b/fdroidserver/common.py index 88765c11..9e6855a0 100644 --- a/fdroidserver/common.py +++ b/fdroidserver/common.py @@ -709,7 +709,7 @@ class DescriptionFormatter: if index2 == -1: urltxt = url else: - urltxt = url[index2 + 1] + urltxt = url[index2 + 1:] url = url[:index] linkified_html += '' + cgi.escape(urltxt) + '' linkified_plain += urltxt From 23a6e52db1743c2800e65f792525c889c3439d70 Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Mon, 17 Sep 2012 22:15:48 +0100 Subject: [PATCH 3/5] Use formatted descriptions in web repo browser --- wp-fdroid/wp-fdroid.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/wp-fdroid/wp-fdroid.php b/wp-fdroid/wp-fdroid.php index 5f02a073..fbd6393a 100644 --- a/wp-fdroid/wp-fdroid.php +++ b/wp-fdroid/wp-fdroid.php @@ -164,7 +164,7 @@ class FDroid case "summary": $summary=$el; break; - case "description": + case "desc": $desc=$el; break; case "license": @@ -246,7 +246,7 @@ class FDroid $out.="
      ".$summary."

      "; $out.=""; - $out.="

      ".$desc."

      "; + $out.=str_replace('href="fdroid.app:', 'href="/repository/browse/?fdid=', $desc); if(isset($antifeatures)) { $antifeaturesArray = explode(',',$antifeatures); @@ -640,7 +640,7 @@ class FDroid case "summary": $appinfo['summary']=$el; break; - case "description": + case "desc": $appinfo['description']=$el; break; case "license": From a11d55620b513bab5e354a54c55ec7c34e92a634 Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Mon, 17 Sep 2012 22:28:08 +0100 Subject: [PATCH 4/5] Basic docs for description formatting --- docs/fdroid.texi | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/docs/fdroid.texi b/docs/fdroid.texi index 52f9a39c..3c866862 100644 --- a/docs/fdroid.texi +++ b/docs/fdroid.texi @@ -583,8 +583,22 @@ A brief summary of what the application is. @cindex Description -A full description of the application. This can span multiple lines, and is -terminated by a line containing a single '.'. +A full description of the application. This can span multiple lines (which +should be kept to a maximum of 80 characters), and is terminated by a line +containing a single '.'. + +Basic MediaWiki-style formatting can be used. Leaving a blank line starts a +new paragraph. Surrounding text with @code{''} make it italic, and with +@code{'''} makes it bold. + +You can link to another app in the repo by using @code{[[app.id]]}. The link +will be made appropriately whether in the Android client, the web repo +browser or the wiki. The link text will be the apps name. + +Links to web addresses can be done using @code{[http://example.com Text]}. + +Bulletted lists are done by simply starting each item with a @code{*} on +a new line, and numbered lists are the same but using @code{#}. @node Repo Type @section Repo Type From f0c6402062ec4d0be161c9490b75eea44dc678de Mon Sep 17 00:00:00 2001 From: Ciaran Gultnieks Date: Tue, 18 Sep 2012 10:06:34 +0100 Subject: [PATCH 5/5] Rejects bad import url for github --- fdroidserver/import.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/fdroidserver/import.py b/fdroidserver/import.py index 947a6b20..6ec83460 100644 --- a/fdroidserver/import.py +++ b/fdroidserver/import.py @@ -126,6 +126,9 @@ def main(): if url.startswith('https://github.com'): if url.endswith('/'): url = url[:-1] + if url.endswith('.git'): + print "A github URL should point to the project, not the git repo" + sys.exit(1) projecttype = 'github' repo = url + '.git' repotype = 'git'