Commits

Anonymous committed d596da8 Merge

Comments (0)

Files changed (7)

 022f611b70f6d8ccd53cc92aa093bb4bd65b6947 0.6.4
 1b643ff2c467ac0815ec4f2beee56c997e2eb092 1.0b1
 6ad1620d22b7ae0a4b2fbc29b94260543ac43459 1.0b1
+8627f9eac04256a9a2a454762a3b7867ed15c19f 1.0b2
 
 tip (development version)
 -------------------------
+* webhelpers.html.tools:
+  - New helper ``js_obfuscate`` implements the old rails helper of the same name.
+
+1.0b2 (2009-12-21)
+------------------
 * webhelpers.constants:
   - Fix spelling of Massachusetts.
 * webhelpers.feedgenerator:
-  - Disable 'generator' and 'source' properties; they seem to be defined
-    in the wrong place.
+  - Sync with Django rev 11910. This adds GeoRSS and makes the API more
+    extensible, as well as fixing a few bugs.
+    (Re-added the Atom1 'published' property.)
+    (The 'generator' and 'source' properties were lost, but they weren't
+    working correctly anyway.)
+    GeoRSS usage: use the Geo\* classes and add  ``geometry=(lat, lon)`` to
+    each news item. Other shapes and a (not yet implemented) Geometry class are
+    allowed; see the source.
+    Note: you should specify the latitude first, but the longitude appears
+    first in the newsfeed.  This is a feature, to comply with the spec.
 * webhelpers.html:
   - New ``HTML.cdata()`` method for producing "<!![CDATA[ ... ]]>" sections.
   - The basic tag builders (``HTML.a()`` and ``HTML.tag("a")``) now have a
   - Reimplement ``highlight()`` using the HTML builder. New arguments add
     flexibility.  Deprecate the ``highlighter`` argument, which creates tags
     via string interpolation.
+  - Fixed ``auto_link()`` to parse slash characters in query string.
+    Patch by hanula; Bitbucket issue #10.
   - Fix HTML overescaping and underescaping in auto_link().  Patch by Marius
     Gedminas.  A parsing bug remains: 
     http://pylonshq.com/project/pylonshq/ticket/657

tests/test_feedgenerator.py

+import datetime
+
+from nose.tools import eq_
+
+import webhelpers.feedgenerator as fg
+
+def test_simple_feed():
+    pubdate = datetime.datetime(2009, 12, 18, 23, 45, 12)
+    feed = fg.Rss201rev2Feed(
+        title=u"Poynter E-Media Tidbits",
+        link=u"http://www.poynter.org/column.asp?id=31",
+        description=u"A group weblog by the sharpest minds in online media/journalism/publishing.",
+        language=u"en",
+    )
+    feed.add_item(
+        title="Hello", 
+        link=u"http://www.holovaty.com/test/",
+        description="Testing.",  
+        pubdate=pubdate)
+    result = feed.writeString("utf-8")
+    control = """<?xml version="1.0" encoding="utf-8"?>\n<rss version="2.0"><channel><title>Poynter E-Media Tidbits</title><link>http://www.poynter.org/column.asp?id=31</link><description>A group weblog by the sharpest minds in online media/journalism/publishing.</description><language>en</language><lastBuildDate>Fri, 18 Dec 2009 23:45:12 -0000</lastBuildDate><item><title>Hello</title><link>http://www.holovaty.com/test/</link><description>Testing.</description><pubDate>Fri, 18 Dec 2009 23:45:12 -0000</pubDate></item></channel></rss>"""
+    eq_(result, control)
+
+
+def test_escaping():
+    pubdate = datetime.datetime(2009, 12, 18, 23, 45, 12)
+    feed = fg.Rss201rev2Feed(
+        title=u"Poynter E-Media Tidbits",
+        link=u"http://www.poynter.org/column.asp?id=31",
+        description=u"A group weblog by the <em>sharpest</em> minds in online media & journalism.",
+        language=u"en",
+    )
+    feed.add_item(
+        title="Hello", 
+        link=u"http://www.holovaty.com/test/",
+        description="Testing.",  
+        pubdate=pubdate)
+    result = feed.writeString("utf-8")
+    control = """<?xml version="1.0" encoding="utf-8"?>\n<rss version="2.0"><channel><title>Poynter E-Media Tidbits</title><link>http://www.poynter.org/column.asp?id=31</link><description>A group weblog by the &lt;em&gt;sharpest&lt;/em&gt; minds in online media &amp; journalism.</description><language>en</language><lastBuildDate>Fri, 18 Dec 2009 23:45:12 -0000</lastBuildDate><item><title>Hello</title><link>http://www.holovaty.com/test/</link><description>Testing.</description><pubDate>Fri, 18 Dec 2009 23:45:12 -0000</pubDate></item></channel></rss>"""
+    eq_(result, control)
+def test_geo_point_feed():
+    pubdate = datetime.datetime(2009, 12, 18, 23, 45, 12)
+    feed = fg.GeoAtom1Feed(
+        title=u"Poynter E-Media Tidbits",
+        link=u"http://www.poynter.org/column.asp?id=31",
+        description=u"A group weblog by the sharpest minds in online media/journalism/publishing.",
+        language=u"en",
+    )
+    feed.add_item(
+        title="Hello", 
+        link=u"http://www.holovaty.com/test/",
+        description="Testing.",  
+        pubdate=pubdate,
+        geometry=(-120.5, 50.5))
+    result = feed.writeString("utf-8")
+    f = open("/tmp/feed", "w")
+    f.write(result)
+    f.close()
+    control = """<?xml version="1.0" encoding="utf-8"?>
+<feed xmlns="http://www.w3.org/2005/Atom" xmlns:georss="http://www.georss.org/georss" xml:lang="en"><title>Poynter E-Media Tidbits</title><link href="http://www.poynter.org/column.asp?id=31" rel="alternate"></link><id>http://www.poynter.org/column.asp?id=31</id><updated>2009-12-18T23:45:12Z</updated><entry><title>Hello</title><link href="http://www.holovaty.com/test/" rel="alternate"></link><updated>2009-12-18T23:45:12Z</updated><published>2009-12-18T23:45:12Z</published><id>tag:www.holovaty.com,2009-12-18:/test/</id><summary type="html">Testing.</summary><georss:point>50.500000 -120.500000</georss:point></entry></feed>"""
+    eq_(result, control)

tests/test_tools.py

             literal('http://www.pylonshq.com/contact;new%20with%20spaces'),
             literal('http://www.pylonshq.com/contact;new?with=query&string=params'),
             literal('http://www.pylonshq.com/~minam/contact;new?with=query&string=params'),
-            literal('http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007')
+            literal('http://en.wikipedia.org/wiki/Wikipedia:Today%27s_featured_picture_%28animation%29/January_20%2C_2007'),
+            literal('http://www.pylonshq.com/foo.cgi?date=01/01/01')
             ]
         for url in urls:
             self.assertEqual('<a href="%s">%s</a>' % (url, url),

unfinished/grid_demo.py

+"""Demos for webhelpers.html.grid
+
+Run this module as a script::
+
+    python -m webhelpers.html.grid_demo OUTPUT_DIRECTORY
+ Dec 16 19:39:54 PST 2009
+"""
+
+import optparse
+import os
+import urllib
+
+from webhelpers.html import *
+from webhelpers.html.grid import Grid
+from webhelpers.html.tags import link_to
+# XXX You may find other helpers in webhelpers.html.tags useful too
+
+#### Global constants ####
+USAGE = "python -m %s OUTPUT_DIRECTORY" % __name__
+
+DESCRIPTION = """\
+Run the demos in this module and put the HTML output in
+OUTPUT_DIRECTORY."""
+
+STYLESHEET = """\
+/* Put styles here. */
+"""
+
+HTML_TEMPLATE = literal("""\
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+    <head>
+        <title>%(title)s</title>
+        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
+        <link rel="stylesheet" type="text/css" href="demo.css" />
+    </head>
+    <body>
+        <h1>%(title)s</h1>
+
+        <table>
+%(grid)s
+        </table>
+
+        <p>%(description)s</p>
+    </body>
+</html>
+"""
+# XXX There should be helpers to create a basic HTML file.
+
+#### Demo base class ####
+class _DemoBase(object):
+    title = None
+    description = None
+
+    def get_grid(): 
+        raise NotImplementedError("subclass responsibility")
+
+
+#### Demo classes ###
+class TicketsDemo(_DemoBase):
+    title = "Tickets"
+    description = """\
+This table shows [XXX mention features]."""
+
+    def get_grid(self):
+        """
+        lets override how rows look like
+        subject is link
+        categories and status hold text based on param of item text , the
+        translations are dicts holding translation strings correlated with
+        integers from db, in this example
+        """
+        columns = ['_numbered','subject','category','status','date']
+        g = Grid(c.tickets, columns=columns)
+        g.format = {
+            "subject": self.subject,
+            "category": self.category,
+            "status": self.status,
+            # XXX What about 'Date' column?
+            }
+        return g
+
+    def subject(self, i, item):
+        # XXX This module can't depend on 'app_globals' or 'url' or
+        # external data. Define data within this method or class or
+        # in a base class.
+        # Could use HTML.a() instead of link_to().
+        u = url("/tickets/view", ticket_id=item["id"])
+        a = link_to(item["subject"], u)
+        return HTML.td(a)
+
+    def category(self, i, item):
+        return HTML.td(item["category"])
+
+    def status(self, i, item):
+        return HTML.td(item["status"])
+
+
+demos = [x for x in globals().iteritems() if
+    isinstance(x, _DemoBase) and x is not _DemoBase]
+
+#### Utility functions ####
+def url(urlpath, **params):
+    # This should be a helper and I think it's defined somewhere but I
+    # can't think of where.
+    return urlpath + "?" + urllib.urlencode(params)
+
+def write_file(dir, filename, content):
+    print "... writing '%s'" % filename
+    path = os.path.join(dir, filename)
+    f = open(path, "w")
+    f.write(content)
+    f.close()
+
+#### Main routine ####
+def main():
+    parser = optparse.OptionParser(usage=USAGE, description=DESCRIPTION)
+    opts, args = parser.parse_args()
+    if len(args) != 1:
+        parser.error("wrong number of command-line arguments")
+    dir = args[0]
+    if not os.path.exists(dir):
+        os.makedirs(dir)
+    print "Putting output in directory '%s'" % dir
+    write_file(dir, "demo.css", STYLESHEET)
+    for class_ in demos:
+        d = class_()
+        title = d.name or d.__class__.__name__
+        filename = name + ".html"
+        dic = {
+            "title": d.name or d.__class__.__name__.lower(),
+            "description" d.description,
+            "grid": d.get_grid(),
+            }
+        html = HTML_TEMPLATE % dic
+        write_file(dir, filename, html)
+
+if __name__ == "__main__":  main()

webhelpers/feedgenerator.py

-# Copyright (c) 2005, the Lawrence Journal-World
+# Copyright (c) Django Software Foundation and individual contributors.
 # All rights reserved.
 # 
 # Redistribution and use in source and binary forms, with or without modification,
 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
-# LAST SYNCED WITH DJANGO SOURCE - JANUARY 5th, 2008 - DJANGO REVISION 6996
-# http://code.djangoproject.com/log/django/trunk/django/utils/feedgenerator.py
-"""Syndication feed generation library -- used for generating RSS, etc.
+# Last synched with Django source 2009-12-18 (Django revision 11910):
+# http://code.djangoproject.com/browser/django/trunk/django/utils/feedgenerator.py
+# http://code.djangoproject.com/browser/django/trunk/django/contrib/gis/feeds.py
+
+# WebHelpers changes from original:
+# ---------------------------------
+# - Combine ``django.utils.feedgenerator`` and ``django.contrib.gis.feeds``.
+# - Change imports: 
+#     * ``SimpleXMLGenerator`` and ``iri_to_uri`` are in ``webhelpers.util``.
+#     * Add local copy of ``force_unicode``.
+# - Delete parts that depend on Django's ORM system: ``Feed`` class, 
+#   ``BaseFeed`` and ``FeedDoesNotExist`` imports.
+# - Delete ``to_unicode`` lambdas and ``force_unicode`` import; these seem
+#   unnecessary.
+# - Change docstring imports.
+# - Apply ``rfc3339_date`` bugfix (02044132a2ef) to both that function and
+#   and ``rfc2822_date``.  (``.tzinfo`` attribute may not exist in datetime
+#   objects.)
+# - Apply 'published' property patch (1f234b039b58).
+# - Note: 'generator' and 'source' properties were lost from a previous
+#   revision of webhelpers.feedgenerator. The implementation had a bug and
+#   can't be used as is.
+
+
+"""
+Syndication feed generation library -- used for generating RSS, etc.
 
 Sample usage:
 
+>>> import webhelpers.feedgenerator as feedgenerator
 >>> feed = feedgenerator.Rss201rev2Feed(
 ...     title=u"Poynter E-Media Tidbits",
 ...     link=u"http://www.poynter.org/column.asp?id=31",
 
 For definitions of the different versions of RSS, see:
 http://diveintomark.org/archives/2004/02/04/incompatible-rss
-
 """
 
-from util import SimplerXMLGenerator, iri_to_uri
-import datetime, re, time
-import email.Utils
+import re
+import datetime
+from webhelpers.util import SimplerXMLGenerator, iri_to_uri
+
+#### The following code comes from ``django.utils.feedgenerator`` ####
 
 def rfc2822_date(date):
-    return email.Utils.formatdate(time.mktime(date.timetuple()))
+    # We do this ourselves to be timezone aware, email.Utils is not tz aware.
+    if getattr(date, "tzinfo", False):
+        time_str = date.strftime('%a, %d %b %Y %H:%M:%S ')
+        offset = date.tzinfo.utcoffset(date)
+        timezone = (offset.days * 24 * 60) + (offset.seconds / 60)
+        hour, minute = divmod(timezone, 60)
+        return time_str + "%+03d%02d" % (hour, minute)
+    else:
+        return date.strftime('%a, %d %b %Y %H:%M:%S -0000')
 
 def rfc3339_date(date):
     if getattr(date, "tzinfo", False):
-        return date.strftime('%Y-%m-%dT%H:%M:%S%z')
+        time_str = date.strftime('%Y-%m-%dT%H:%M:%S')
+        offset = date.tzinfo.utcoffset(date)
+        timezone = (offset.days * 24 * 60) + (offset.seconds / 60)
+        hour, minute = divmod(timezone, 60)
+        return time_str + "%+03d:%02d" % (hour, minute)
     else:
         return date.strftime('%Y-%m-%dT%H:%M:%SZ')
 
 def get_tag_uri(url, date):
-    """Creates a TagURI. See http://diveintomark.org/archives/2004/05/28/howto-atom-id"""
+    "Creates a TagURI. See http://diveintomark.org/archives/2004/05/28/howto-atom-id"
     tag = re.sub('^http://', '', url)
     if date is not None:
         tag = re.sub('/', ',%s:/' % date.strftime('%Y-%m-%d'), tag, 1)
     tag = re.sub('#', '/', tag)
-    return 'tag:' + tag
+    return u'tag:' + tag
 
 class SyndicationFeed(object):
-    """Base class for all syndication feeds. Subclasses should provide write()"""
+    "Base class for all syndication feeds. Subclasses should provide write()"
     def __init__(self, title, link, description, language=None, author_email=None,
             author_name=None, author_link=None, subtitle=None, categories=None,
-            feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, 
-            generator=None, source=None):
+            feed_url=None, feed_copyright=None, feed_guid=None, ttl=None, **kwargs):
+        if categories:
+            categories = [force_unicode(c) for c in categories]
         self.feed = {
             'title': title,
-            'link': link,
+            'link': iri_to_uri(link),
             'description': description,
             'language': language,
             'author_email': author_email,
             'author_name': author_name,
-            'author_link': author_link,
+            'author_link': iri_to_uri(author_link),
             'subtitle': subtitle,
             'categories': categories or (),
             'feed_url': iri_to_uri(feed_url),
             'feed_copyright': feed_copyright,
             'id': feed_guid or link,
             'ttl': ttl,
-            'generator': generator,
-            'source': source,
         }
+        self.feed.update(kwargs)
         self.items = []
 
     def add_item(self, title, link, description, author_email=None,
         author_name=None, author_link=None, pubdate=None, comments=None,
-        unique_id=None, enclosure=None, categories=(), item_copyright=None, ttl=None):
+        unique_id=None, enclosure=None, categories=(), item_copyright=None,
+        ttl=None, **kwargs):
         """
         Adds an item to the feed. All args are expected to be Python Unicode
         objects except pubdate, which is a datetime.datetime object, and
         enclosure, which is an instance of the Enclosure class.
-        
         """
-        self.items.append({
+        item = {
             'title': title,
             'link': iri_to_uri(link),
             'description': description,
             'author_email': author_email,
             'author_name': author_name,
-            'author_link': author_link,
+            'author_link': iri_to_uri(author_link),
             'pubdate': pubdate,
             'comments': comments,
             'unique_id': unique_id,
             'categories': categories or (),
             'item_copyright': item_copyright,
             'ttl': ttl,
-        })
+        }
+        item.update(kwargs)
+        self.items.append(item)
 
     def num_items(self):
         return len(self.items)
 
+    def root_attributes(self):
+        """
+        Return extra attributes to place on the root (i.e. feed/channel) element.
+        Called from write().
+        """
+        return {}
+
+    def add_root_elements(self, handler):
+        """
+        Add elements in the root (i.e. feed/channel) element. Called
+        from write().
+        """
+        pass
+
+    def item_attributes(self, item):
+        """
+        Return extra attributes to place on each item (i.e. item/entry) element.
+        """
+        return {}
+
+    def add_item_elements(self, handler, item):
+        """
+        Add elements on each item (i.e. item/entry) element.
+        """
+        pass
+
     def write(self, outfile, encoding):
-        """Outputs the feed in the given encoding to outfile, which is a file-like
+        """
+        Outputs the feed in the given encoding to outfile, which is a file-like
         object. Subclasses should override this.
-        
         """
         raise NotImplementedError
 
     def writeString(self, encoding):
-        """Returns the feed in the given encoding as a string."""
+        """
+        Returns the feed in the given encoding as a string.
+        """
         from StringIO import StringIO
         s = StringIO()
         self.write(s, encoding)
         return s.getvalue()
 
     def latest_post_date(self):
-        """Returns the latest item's pubdate. 
-        
-        If none of them have a pubdate, this returns the current date/time.
-        
+        """
+        Returns the latest item's pubdate. If none of them have a pubdate,
+        this returns the current date/time.
         """
         updates = [i['pubdate'] for i in self.items if i['pubdate'] is not None]
         if len(updates) > 0:
             return datetime.datetime.now()
 
 class Enclosure(object):
-    """Represents an RSS enclosure"""
+    "Represents an RSS enclosure"
     def __init__(self, url, length, mime_type):
         "All args are expected to be Python Unicode objects"
         self.length, self.mime_type = length, mime_type
     def write(self, outfile, encoding):
         handler = SimplerXMLGenerator(outfile, encoding)
         handler.startDocument()
-        handler.startElement(u"rss", {u"version": self._version})
-        handler.startElement(u"channel", {})
+        handler.startElement(u"rss", self.rss_attributes())
+        handler.startElement(u"channel", self.root_attributes())
+        self.add_root_elements(handler)
+        self.write_items(handler)
+        self.endChannelElement(handler)
+        handler.endElement(u"rss")
+
+    def rss_attributes(self):
+        return {u"version": self._version}
+
+    def write_items(self, handler):
+        for item in self.items:
+            handler.startElement(u'item', self.item_attributes(item))
+            self.add_item_elements(handler, item)
+            handler.endElement(u"item")
+
+    def add_root_elements(self, handler):
         handler.addQuickElement(u"title", self.feed['title'])
         handler.addQuickElement(u"link", self.feed['link'])
         handler.addQuickElement(u"description", self.feed['description'])
             handler.addQuickElement(u"category", cat)
         if self.feed['feed_copyright'] is not None:
             handler.addQuickElement(u"copyright", self.feed['feed_copyright'])
-        handler.addQuickElement(u"lastBuildDate", rfc2822_date(self.latest_post_date()).decode('ascii'))
+        handler.addQuickElement(u"lastBuildDate", rfc2822_date(self.latest_post_date()).decode('utf-8'))
         if self.feed['ttl'] is not None:
             handler.addQuickElement(u"ttl", self.feed['ttl'])
-        self.write_items(handler)
-        self.endChannelElement(handler)
-        handler.endElement(u"rss")
 
     def endChannelElement(self, handler):
         handler.endElement(u"channel")
 
 class RssUserland091Feed(RssFeed):
     _version = u"0.91"
-    def write_items(self, handler):
-        for item in self.items:
-            handler.startElement(u"item", {})
-            handler.addQuickElement(u"title", item['title'])
-            handler.addQuickElement(u"link", item['link'])
-            if item['description'] is not None:
-                handler.addQuickElement(u"description", item['description'])
-            handler.endElement(u"item")
+    def add_item_elements(self, handler, item):
+        handler.addQuickElement(u"title", item['title'])
+        handler.addQuickElement(u"link", item['link'])
+        if item['description'] is not None:
+            handler.addQuickElement(u"description", item['description'])
 
 class Rss201rev2Feed(RssFeed):
     # Spec: http://blogs.law.harvard.edu/tech/rss
     _version = u"2.0"
-    def write_items(self, handler):
-        for item in self.items:
-            handler.startElement(u"item", {})
-            handler.addQuickElement(u"title", item['title'])
-            handler.addQuickElement(u"link", item['link'])
-            if item['description'] is not None:
-                handler.addQuickElement(u"description", item['description'])
+    def add_item_elements(self, handler, item):
+        handler.addQuickElement(u"title", item['title'])
+        handler.addQuickElement(u"link", item['link'])
+        if item['description'] is not None:
+            handler.addQuickElement(u"description", item['description'])
 
-            # Author information.
-            if item["author_name"] and item["author_email"]:
-                handler.addQuickElement(u"author", "%s (%s)" % \
-                    (item['author_email'], item['author_name']))
-            elif item["author_email"]:
-                handler.addQuickElement(u"author", item["author_email"])
-            elif item["author_name"]:
-                handler.addQuickElement(u"dc:creator", item["author_name"], {"xmlns:dc": u"http://purl.org/dc/elements/1.1/"})
+        # Author information.
+        if item["author_name"] and item["author_email"]:
+            handler.addQuickElement(u"author", "%s (%s)" % \
+                (item['author_email'], item['author_name']))
+        elif item["author_email"]:
+            handler.addQuickElement(u"author", item["author_email"])
+        elif item["author_name"]:
+            handler.addQuickElement(u"dc:creator", item["author_name"], {"xmlns:dc": u"http://purl.org/dc/elements/1.1/"})
 
-            if item['pubdate'] is not None:
-                handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('ascii'))
-            if item['comments'] is not None:
-                handler.addQuickElement(u"comments", item['comments'])
-            if item['unique_id'] is not None:
-                handler.addQuickElement(u"guid", item['unique_id'])
-            if item['ttl'] is not None:
-                handler.addQuickElement(u"ttl", item['ttl'])
-            # XXX DISABLED: these are defined at the feed level.
-            #if item['generator'] is not None:
-            #    handler.addQuickElement(u"generator", item['generator'])
-            #if item['source'] is not None:
-            #    handler.addQuickElement(u"source", item['source'])
+        if item['pubdate'] is not None:
+            handler.addQuickElement(u"pubDate", rfc2822_date(item['pubdate']).decode('utf-8'))
+        if item['comments'] is not None:
+            handler.addQuickElement(u"comments", item['comments'])
+        if item['unique_id'] is not None:
+            handler.addQuickElement(u"guid", item['unique_id'])
+        if item['ttl'] is not None:
+            handler.addQuickElement(u"ttl", item['ttl'])
 
-            # Enclosure.
-            if item['enclosure'] is not None:
-                handler.addQuickElement(u"enclosure", '',
-                    {u"url": item['enclosure'].url, u"length": item['enclosure'].length,
-                        u"type": item['enclosure'].mime_type})
+        # Enclosure.
+        if item['enclosure'] is not None:
+            handler.addQuickElement(u"enclosure", '',
+                {u"url": item['enclosure'].url, u"length": item['enclosure'].length,
+                    u"type": item['enclosure'].mime_type})
 
-            # Categories.
-            for cat in item['categories']:
-                handler.addQuickElement(u"category", cat)
-
-            handler.endElement(u"item")
+        # Categories.
+        for cat in item['categories']:
+            handler.addQuickElement(u"category", cat)
 
 class Atom1Feed(SyndicationFeed):
     # Spec: http://atompub.org/2005/07/11/draft-ietf-atompub-format-10.html
     mime_type = 'application/atom+xml'
     ns = u"http://www.w3.org/2005/Atom"
+
     def write(self, outfile, encoding):
         handler = SimplerXMLGenerator(outfile, encoding)
         handler.startDocument()
+        handler.startElement(u'feed', self.root_attributes())
+        self.add_root_elements(handler)
+        self.write_items(handler)
+        handler.endElement(u"feed")
+
+    def root_attributes(self):
         if self.feed['language'] is not None:
-            handler.startElement(u"feed", {u"xmlns": self.ns, u"xml:lang": self.feed['language']})
+            return {u"xmlns": self.ns, u"xml:lang": self.feed['language']}
         else:
-            handler.startElement(u"feed", {u"xmlns": self.ns})
+            return {u"xmlns": self.ns}
+
+    def add_root_elements(self, handler):
         handler.addQuickElement(u"title", self.feed['title'])
         handler.addQuickElement(u"link", "", {u"rel": u"alternate", u"href": self.feed['link']})
         if self.feed['feed_url'] is not None:
             handler.addQuickElement(u"link", "", {u"rel": u"self", u"href": self.feed['feed_url']})
-        handler.addQuickElement(u"id", self.feed['link'])
-        handler.addQuickElement(u"updated", rfc3339_date(self.latest_post_date()).decode('ascii'))
+        handler.addQuickElement(u"id", self.feed['id'])
+        handler.addQuickElement(u"updated", rfc3339_date(self.latest_post_date()).decode('utf-8'))
         if self.feed['author_name'] is not None:
             handler.startElement(u"author", {})
             handler.addQuickElement(u"name", self.feed['author_name'])
             handler.addQuickElement(u"category", "", {u"term": cat})
         if self.feed['feed_copyright'] is not None:
             handler.addQuickElement(u"rights", self.feed['feed_copyright'])
-        self.write_items(handler)
-        handler.endElement(u"feed")
 
     def write_items(self, handler):
         for item in self.items:
-            handler.startElement(u"entry", {})
-            handler.addQuickElement(u"title", item['title'])
-            handler.addQuickElement(u"link", u"", {u"href": item['link'], u"rel": u"alternate"})
-            if item['pubdate'] is not None:
-                handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('ascii'))
-                handler.addQuickElement(u"published", rfc3339_date(item['pubdate']).decode('ascii'))
+            handler.startElement(u"entry", self.item_attributes(item))
+            self.add_item_elements(handler, item)
+            handler.endElement(u"entry")
 
-            # Author information.
-            if item['author_name'] is not None:
-                handler.startElement(u"author", {})
-                handler.addQuickElement(u"name", item['author_name'])
-                if item['author_email'] is not None:
-                    handler.addQuickElement(u"email", item['author_email'])
-                if item['author_link'] is not None:
-                    handler.addQuickElement(u"uri", item['author_link'])
-                handler.endElement(u"author")
+    def add_item_elements(self, handler, item):
+        handler.addQuickElement(u"title", item['title'])
+        handler.addQuickElement(u"link", u"", {u"href": item['link'], u"rel": u"alternate"})
+        if item['pubdate'] is not None:
+            handler.addQuickElement(u"updated", rfc3339_date(item['pubdate']).decode('utf-8'))
+            handler.addQuickElement(u"published", rfc3339_date(item['pubdate']).decode('utf-8'))
 
-            # Unique ID.
-            if item['unique_id'] is not None:
-                unique_id = item['unique_id']
-            else:
-                unique_id = get_tag_uri(item['link'], item['pubdate'])
-            handler.addQuickElement(u"id", unique_id)
+        # Author information.
+        if item['author_name'] is not None:
+            handler.startElement(u"author", {})
+            handler.addQuickElement(u"name", item['author_name'])
+            if item['author_email'] is not None:
+                handler.addQuickElement(u"email", item['author_email'])
+            if item['author_link'] is not None:
+                handler.addQuickElement(u"uri", item['author_link'])
+            handler.endElement(u"author")
 
-            # Summary.
-            if item['description'] is not None:
-                handler.addQuickElement(u"summary", item['description'], {u"type": u"html"})
+        # Unique ID.
+        if item['unique_id'] is not None:
+            unique_id = item['unique_id']
+        else:
+            unique_id = get_tag_uri(item['link'], item['pubdate'])
+        handler.addQuickElement(u"id", unique_id)
 
-            # Enclosure.
-            if item['enclosure'] is not None:
-                handler.addQuickElement(u"link", '',
-                    {u"rel": u"enclosure",
-                     u"href": item['enclosure'].url,
-                     u"length": item['enclosure'].length,
-                     u"type": item['enclosure'].mime_type})
+        # Summary.
+        if item['description'] is not None:
+            handler.addQuickElement(u"summary", item['description'], {u"type": u"html"})
 
-            # Categories:
-            for cat in item['categories']:
-                handler.addQuickElement(u"category", u"", {u"term": cat})
+        # Enclosure.
+        if item['enclosure'] is not None:
+            handler.addQuickElement(u"link", '',
+                {u"rel": u"enclosure",
+                 u"href": item['enclosure'].url,
+                 u"length": item['enclosure'].length,
+                 u"type": item['enclosure'].mime_type})
 
-            # Rights.
-            if item['item_copyright'] is not None:
-                handler.addQuickElement(u"rights", item['item_copyright'])
+        # Categories.
+        for cat in item['categories']:
+            handler.addQuickElement(u"category", u"", {u"term": cat})
 
-            handler.endElement(u"entry")
+        # Rights.
+        if item['item_copyright'] is not None:
+            handler.addQuickElement(u"rights", item['item_copyright'])
 
 # This isolates the decision of what the system default is, so calling code can
 # do "feedgenerator.DefaultFeed" instead of "feedgenerator.Rss201rev2Feed".
 DefaultFeed = Rss201rev2Feed
+
+
+#### The following code comes from ``django.contrib.gis.feeds`` ####
+
+class GeoFeedMixin(object):
+    """
+    This mixin provides the necessary routines for SyndicationFeed subclasses
+    to produce simple GeoRSS or W3C Geo elements.
+    """
+
+    def georss_coords(self, coords):
+        """
+        In GeoRSS coordinate pairs are ordered by lat/lon and separated by
+        a single white space.  Given a tuple of coordinates, this will return
+        a unicode GeoRSS representation.
+        """
+        return u' '.join([u'%f %f' % (coord[1], coord[0]) for coord in coords])
+
+    def add_georss_point(self, handler, coords, w3c_geo=False):
+        """
+        Adds a GeoRSS point with the given coords using the given handler.
+        Handles the differences between simple GeoRSS and the more pouplar
+        W3C Geo specification.
+        """
+        if w3c_geo:
+            lon, lat = coords[:2]
+            handler.addQuickElement(u'geo:lat', u'%f' % lat)
+            handler.addQuickElement(u'geo:lon', u'%f' % lon)
+        else:
+            handler.addQuickElement(u'georss:point', self.georss_coords((coords,)))
+
+    def add_georss_element(self, handler, item, w3c_geo=False):
+        """
+        This routine adds a GeoRSS XML element using the given item and handler.
+        """
+        # Getting the Geometry object.
+        geom = item.get('geometry', None)
+        if not geom is None:
+            if isinstance(geom, (list, tuple)):
+                # Special case if a tuple/list was passed in.  The tuple may be
+                # a point or a box
+                box_coords = None
+                if isinstance(geom[0], (list, tuple)):
+                    # Box: ( (X0, Y0), (X1, Y1) )
+                    if len(geom) == 2:
+                        box_coords = geom
+                    else:
+                        raise ValueError('Only should be two sets of coordinates.')
+                else:
+                    if len(geom) == 2:
+                        # Point: (X, Y)
+                        self.add_georss_point(handler, geom, w3c_geo=w3c_geo)
+                    elif len(geom) == 4:
+                        # Box: (X0, Y0, X1, Y1)
+                        box_coords = (geom[:2], geom[2:])
+                    else:
+                        raise ValueError('Only should be 2 or 4 numeric elements.')
+                # If a GeoRSS box was given via tuple.
+                if not box_coords is None:
+                    if w3c_geo: raise ValueError('Cannot use simple GeoRSS box in W3C Geo feeds.')
+                    handler.addQuickElement(u'georss:box', self.georss_coords(box_coords))
+            else:
+                # Getting the lower-case geometry type.
+                gtype = str(geom.geom_type).lower()
+                if gtype == 'point':
+                    self.add_georss_point(handler, geom.coords, w3c_geo=w3c_geo) 
+                else:
+                    if w3c_geo: raise ValueError('W3C Geo only supports Point geometries.')
+                    # For formatting consistent w/the GeoRSS simple standard:
+                    # http://georss.org/1.0#simple
+                    if gtype in ('linestring', 'linearring'):
+                        handler.addQuickElement(u'georss:line', self.georss_coords(geom.coords))
+                    elif gtype in ('polygon',):
+                        # Only support the exterior ring.
+                        handler.addQuickElement(u'georss:polygon', self.georss_coords(geom[0].coords))
+                    else:
+                        raise ValueError('Geometry type "%s" not supported.' % geom.geom_type)
+
+### SyndicationFeed subclasses ###
+class GeoRSSFeed(Rss201rev2Feed, GeoFeedMixin):
+    def rss_attributes(self):
+        attrs = super(GeoRSSFeed, self).rss_attributes()
+        attrs[u'xmlns:georss'] = u'http://www.georss.org/georss'
+        return attrs
+
+    def add_item_elements(self, handler, item):
+        super(GeoRSSFeed, self).add_item_elements(handler, item)
+        self.add_georss_element(handler, item)
+
+    def add_root_elements(self, handler):
+        super(GeoRSSFeed, self).add_root_elements(handler)
+        self.add_georss_element(handler, self.feed)
+
+class GeoAtom1Feed(Atom1Feed, GeoFeedMixin):
+    def root_attributes(self):
+        attrs = super(GeoAtom1Feed, self).root_attributes()
+        attrs[u'xmlns:georss'] = u'http://www.georss.org/georss'
+        return attrs
+
+    def add_item_elements(self, handler, item):
+        super(GeoAtom1Feed, self).add_item_elements(handler, item)
+        self.add_georss_element(handler, item)
+
+    def add_root_elements(self, handler):
+        super(GeoAtom1Feed, self).add_root_elements(handler)
+        self.add_georss_element(handler, self.feed)
+
+class W3CGeoFeed(Rss201rev2Feed, GeoFeedMixin):
+    def rss_attributes(self):
+        attrs = super(W3CGeoFeed, self).rss_attributes()
+        attrs[u'xmlns:geo'] = u'http://www.w3.org/2003/01/geo/wgs84_pos#'
+        return attrs
+
+    def add_item_elements(self, handler, item):
+        super(W3CGeoFeed, self).add_item_elements(handler, item)
+        self.add_georss_element(handler, item, w3c_geo=True)
+
+    def add_root_elements(self, handler):
+        super(W3CGeoFeed, self).add_root_elements(handler)
+        self.add_georss_element(handler, self.feed, w3c_geo=True)

webhelpers/html/tools.py

 __all__ = [
     'auto_link', 
     'button_to', 
+    'js_obfuscate',
     'highlight', 
     'mail_to',
     'strip_links',
                           (?:\.[-\w]+)*            # remaining subdomains or domain
                           (?::\d+)?                # port
                           (?:/(?:(?:[~\w\+%-]|(?:[,.;:][^\s$]))+)?)* # path
-                          (?:\?[\w\+%&=.;-]+)?     # query string
+                          (?:\?[\w\+\/%&=.;-]+)?     # query string
                           (?:\#[\w\-]*)?           # trailing anchor
                         )
                         ([\.,"'?!;:]|\s|<|$)       # trailing text
     return HTML.form(method=form_method, action=url, class_="button-to",
                      c=[HTML.div(method_tag, HTML.input(**html_options))])
 
+def js_obfuscate(content):
+    """Obfuscate data in a Javascript tag.
+    
+    Example::
+        
+        >>> js_obfuscate("<input type='hidden' name='check' value='valid' />")
+        literal(u'<script type="text/javascript">\\n//<![CDATA[\\neval(unescape(\\'%64%6f%63%75%6d%65%6e%74%2e%77%72%69%74%65%28%27%3c%69%6e%70%75%74%20%74%79%70%65%3d%27%68%69%64%64%65%6e%27%20%6e%61%6d%65%3d%27%63%68%65%63%6b%27%20%76%61%6c%75%65%3d%27%76%61%6c%69%64%27%20%2f%3e%27%29%3b\\'))\\n//]]>\\n</script>')
+        
+    """
+    doc_write = "document.write('%s');" % content
+    obfuscated = ''.join(['%%%x' % ord(x) for x in doc_write])
+    complete = "eval(unescape('%s'))" % obfuscated
+    cdata = HTML.cdata("\n", complete, "\n//")
+    return HTML.script("\n//", cdata, "\n", type="text/javascript")
+
 
 def mail_to(email_address, name=None, cc=None, bcc=None, subject=None, 
     body=None, replace_at=None, replace_dot=None, encode=None, **html_options):
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.