Internet DRAFT - draft-sayre-atompub-xhtml-micro
draft-sayre-atompub-xhtml-micro
Network Working Group R. Sayre
Internet-Draft September 3, 2005
Expires: March 7, 2006
XHTML Microformats for the Atom Publishing Protocol
draft-sayre-atompub-xhtml-micro-00.txt
Status of this Memo
By submitting this Internet-Draft, each author represents that any
applicable patent or other IPR claims of which he or she is aware
have been or will be disclosed, and any of which he or she becomes
aware will be disclosed, in accordance with Section 6 of BCP 79.
Internet-Drafts are working documents of the Internet Engineering
Task Force (IETF), its areas, and its working groups. Note that
other groups may also distribute working documents as Internet-
Drafts.
Internet-Drafts are draft documents valid for a maximum of six months
and may be updated, replaced, or obsoleted by other documents at any
time. It is inappropriate to use Internet-Drafts as reference
material or to cite them other than as "work in progress."
The list of current Internet-Drafts can be accessed at
http://www.ietf.org/ietf/1id-abstracts.txt.
The list of Internet-Draft Shadow Directories can be accessed at
http://www.ietf.org/shadow.html.
This Internet-Draft will expire on March 7, 2006.
Copyright Notice
Copyright (C) The Internet Society (2005).
Abstract
This memo presents a number of XHTML microformats for use with the
Atom Publishing Protocol.
Editorial Note
To provide feedback on this Internet-Draft, join the atom-protocol
mailing list (http://www.imc.org/atom-protocol/index.html) [1].
Sayre Expires March 7, 2006 [Page 1]
Internet-Draft XHTML Microformats for the APP September 2005
Table of Contents
1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
2. Notational Conventions . . . . . . . . . . . . . . . . . . . . 4
3. hCat: Atom Categories . . . . . . . . . . . . . . . . . . . . 5
3.1 Creating and Editing hCat Categories . . . . . . . . . . . 7
4. hError: Error Documents . . . . . . . . . . . . . . . . . . . 9
5. Security Considerations . . . . . . . . . . . . . . . . . . . 11
6. References . . . . . . . . . . . . . . . . . . . . . . . . . . 12
6.1 Normative References . . . . . . . . . . . . . . . . . . . 12
6.2 Informative References . . . . . . . . . . . . . . . . . . 12
Author's Address . . . . . . . . . . . . . . . . . . . . . . . 13
A. An Example hCat and hError Server . . . . . . . . . . . . . . 14
B. An Example hCat and hError Client . . . . . . . . . . . . . . 20
Intellectual Property and Copyright Statements . . . . . . . . 31
Sayre Expires March 7, 2006 [Page 2]
Internet-Draft XHTML Microformats for the APP September 2005
1. Introduction
Atom Publishing Protocol [APP] client implementations require a fair
amount of ancillary server-provided data in order to provide a smooth
user experience. Rather than invent a plethora of new XML formats,
this specification chooses to present a number of XHTML profiles
[XHTML], colloquially known as "microformats". Visit
<http://microformats.org> for more information.
Sayre Expires March 7, 2006 [Page 3]
Internet-Draft XHTML Microformats for the APP September 2005
2. Notational Conventions
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
document are to be interpreted as described in [RFC2119].
Sayre Expires March 7, 2006 [Page 4]
Internet-Draft XHTML Microformats for the APP September 2005
3. hCat: Atom Categories
hCat is an XHTML profile for encoding the three standard attributes
of Atom category elements [AtomFormat]. By providing a definition
list containing encoded category information, servers can present
clients with a list of known categories in an XHTML definition list.
hCat also allows description of endpoints for category editing
through a simple HTTP-based protocol, described in Section 3.1.
A sample category definition
...
<dt>birds</dt>
<dd>
<span class="label">ISBN birds</span>
<a href="http://example.org/category/4" rel="hCat">Edit</a>
<a href="http://example.org/ISBN/" rel="scheme"></a>
</dd>
...
The three standard properties of an Atom category are 'term',
'label', and 'scheme'. The 'term' attribute is required. hCat
encodes the categories provided by a server within an xhtml:dl
element with a 'class' attribute value of 'category'. Each category
is presented as a pair of elements: xhtml:dt and xhtml:dd. Each
element pair MAY contain or be enclosed by additional markup, but
this specification assigns it no significance.
hCat XHTML documents MUST indicate their profile in the 'head'
element. The profile URI for hCat documents is
"http://www.example.org/2005/Atom/hCat" [example URI--do not deploy].
Sayre Expires March 7, 2006 [Page 5]
Internet-Draft XHTML Microformats for the APP September 2005
An example hCat document
<!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">
<head profile="http://www.example.org/2005/Atom/hCat">
<title>Atom Category Demo</title>
</head>
<body>
<h1>Atom Category Demo</h1>
<form class="hCat" action="http://example.org/cat" method="post">
<input id="term" type="text" />
<input id="label" type="text" />
<input id="scheme" type="text" />
</form>
<dl class="hCat">
<dt>cats</dt>
<dd>
<a href="http://example.org/category/1" rel="hCat">Edit</a>
</dd>
<dt>dogs</dt>
<dd>
<a href="http://example.org/category/2" rel="hCat">Edit</a>
<a href="http://www.example.com" rel="scheme"></a>
</dd>
<dt>atom</dt>
<dd>
<span class="label">The Atom Syndication Format</span>
<a href="http://example.org/category/3" rel="hCat">Edit</a>
</dd>
<dt>birds</dt>
<dd>
<span class="label">ISBN birds</span>
<a href="http://example.org/category/4" rel="hCat">Edit</a>
<a href="http://example.org/ISBN/" rel="scheme"></a>
</dd>
</dl>
</body>
</html>
The REQUIRED 'term' property is found in the content of each xhtml:dt
element. The content of the xhtml:dt element MUST NOT contain child
Sayre Expires March 7, 2006 [Page 6]
Internet-Draft XHTML Microformats for the APP September 2005
elements. Terms are also known as 'tags'.
The OPTIONAL 'label' property is found in an xhtml:span element
directly contained by each xhtml:dd element. The xhtml:span element
MUST have 'label' as one of its 'class' values. There MUST NOT be
more than one such xhtml:span within each xhtml:dd element.
The OPTIONAL 'scheme' property is found in an xhtml:a element
directly contained by each xhtml:dd element. The xhtml:a element
MUST have 'scheme' as one of its 'rel' values. There MUST NOT be
more than one such xhtml:a within each xhtml:dd element.
Each definition MAY contain an xhtml:a element with a 'rel' value of
'hCat'. This URI provided by the value of the element's 'href'
attribute indicates a URI that responds to a simple protocol for
editing of categories, which is described in the next section.
3.1 Creating and Editing hCat Categories
In addition to listing categories provided by a server, hCat XHTML
documents allow conforming clients to edit and add categories. The
server MAY communicate a URI where it accepts new categories by
including an xhtml:form element with a 'class' attribute of 'hCat' in
the hCat XHTML document. There MUST NOT be more than one xhtml:form
element containing a class of 'hCat'. It would be preferable to use
an 'id' attribute in this case, but many implementations depend on a
generated value for a form's 'id' attribute. The xhtml:form element
MUST be contained by the xhtml:body element, but MUST NOT be
contained by an hCat definition list.
The values provided by the attributes of the xhtml:form element
provide guidance for implementations wishing to submit content to the
server. Servers MUST accept the MIME-type 'application/
x-www-form-urlencoded', which is the default value for xhtml:forms.
o As in XHTML, the 'action' attribute indicates the URI where new
categories are submitted.
o The 'accept-charset' attribute of the xhtml:form element MUST be
present, and the list of accepted character sets SHOULD include
'utf-8'.
o The value of the 'method' attribute MUST NOT be 'get', and SHOULD
be 'post'. Naive user agents will likely treat other values as if
they were 'get'.
The 'application/x-www-form-urlencoded' MIME-type is a name/value
pair format. Servers identify their preferred name for each atom:
Sayre Expires March 7, 2006 [Page 7]
Internet-Draft XHTML Microformats for the APP September 2005
category property using xhtml:input elements contained by the hCat
xhtml:form element. The property is identified by the 'id' attribute
of the xhtml:input element. For example,
<input name="foo" id="term" type="text" />
would indicate that the 'term' property is to be sent as the value of
the 'foo' parameter.
The information provided by the hCat xhtml:form also applies to
editing categories. To update a category, clients can send the
parameters indicated by the form in an HTTP PUT request directed to
the hCat URI conveyed by the relevant xhtml:a element in each
xhtml:dd element. HTTP DELETE requests sent to that URI remove the
category. This specification does not define a response to HTTP GET
for such URIs, but servers SHOULD provide one (see [WEBARCH]).
Servers MAY extend the hCat protocol with information in that
response. This specification does not define a response to HTTP
POST, but other specifications might do so. For example, a system
supporting hierarchical categories might use HTTP POST requests to
append a new category as a sub-category.
If the server encounters an error condition, the response body SHOULD
be an hError XHTML document (Section 4).
Sayre Expires March 7, 2006 [Page 8]
Internet-Draft XHTML Microformats for the APP September 2005
4. hError: Error Documents
HTTP provides response codes which indicate the success or failure of
a given request, but does not go into great detail on textual
diagnostics for the end-user (see [RFC3117], Section 3.3). hError is
an XHTML profile that encodes error information intended for the end-
user.
hCat XHTML documents MUST indicate their profile in the 'head'
element. The profile URI for hCat documents is
"http://www.example.org/2005/Atom/hError" [example URI--do not
deploy].
hCat provides three locations for error information:
1. The xhtml:title element MUST be present, and contains a short
description of the error. Longer titles risk truncation due to
GUI constraints.
2. An xhtml:p element with an 'id' attribute value of 'hError' MAY
be present, and contains a longer description of the issue. The
hError xhtml:p element MAY contain child elements, but many user
interfaces will not display HTML.
3. An xhtml:span element with an 'id' attribute value of 'hErrorId'
MAY be present, and contains a identifier for the error. This
error identifer could be a unique identifier useful for locating
a transaction during support requests, or a more general code
identifying a specific condition.
Sayre Expires March 7, 2006 [Page 9]
Internet-Draft XHTML Microformats for the APP September 2005
An example hError XHTML document
<!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">
<head profile="http://www.example.org/2005/Atom/hError">
<title>We're Sorry! File not found.</title>
</head>
<body>
<p id="hError">
Hmm, can't find that entry.
You can contact an administrator for help:
admin@example.com.
</p>
<p>
The unique identifier for this error is
<span id="hErrorId">574DDDF2-A1E1-4898-B21B-EBB2DD16B38C</span>.
</p>
</body>
</html>
Sayre Expires March 7, 2006 [Page 10]
Internet-Draft XHTML Microformats for the APP September 2005
5. Security Considerations
[[anchor5: What isn't a consideration with this? ...will fix]]
Sayre Expires March 7, 2006 [Page 11]
Internet-Draft XHTML Microformats for the APP September 2005
6. References
6.1 Normative References
[APP] Gregorio, J. and B. de hOra, "The Atom Publishing
Protocol", work-in-progress, July 2005.
[AtomFormat]
Nottingham, M. and R. Sayre, "The Atom Syndication
Format", IESG Approved, awaiting RFC number, July 2005.
[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
Requirement Levels", BCP 14, RFC 2119, March 1997.
[XHTML] Pemberton, S., "XHTML 1.0 The Extensible HyperText Markup
Language (Second Edition)", W3C REC REC-xhtml1-20020801,
August 2002,
<http://www.w3.org/TR/2002/REC-xhtml1-2002080>.
6.2 Informative References
[RFC3117] Rose, M., "On the Design of Application Protocols",
RFC 3117, November 2001.
[WEBARCH] Walsh, N. and I. Jacobs, "Architecture of the World Wide
Web, Volume One", W3C REC REC-webarch-20041215,
December 2004,
<http://www.w3.org/TR/2004/REC-webarch-20041215>.
Sayre Expires March 7, 2006 [Page 12]
Internet-Draft XHTML Microformats for the APP September 2005
URIs
[1] <http://www.imc.org/atom-protocol/index.html>
Author's Address
Robert Sayre
Email: rfsayre@boswijck.com
URI: http://boswijck.com
Sayre Expires March 7, 2006 [Page 13]
Internet-Draft XHTML Microformats for the APP September 2005
Appendix A. An Example hCat and hError Server
You can run this server from the command line using the Python
programming language <http://www.python.org>.
Syntax: "python this_script.py"
This will start a server running at 'http://localhost:8888'. You can
visit in your browser, and try the client in Appendix B.
On Microsoft Windows, the server may not respond to an interrupt
command (^Z CR) immediately. Visit the server one more time after
attempting to interrupt, and the server will quit.
Patches welcome :)
import BaseHTTPServer, cgi
categories = [{'term':u'\u201CHello World!\u201D',
'label':u'Not much to describe',
'scheme':None},
{'term':u'Hello World!',
'label':None,
'scheme':None},
{'term':u'Hello World!',
'label':u'Not much to describe',
'scheme':None},
{'term':u'Hello World!',
'label':u'An example label',
'scheme':u'http://example.com'}]
hcat_template = u"""
<!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'>
<head profile='http://www.example.org/2005/Atom/hCat'>
<title>hCat Demo</title>
</head>
<body>
<h1>hCat Category Demo</h1>
<p><em>Warning:</em> requires
I\u00F1t\u00EBrn\u00E2ti\u00F4n\u00E0liz\u00E6ti\u00F8n</p>
<form class='hCat' action='add'
method='post' accept-charset='utf-8'>
<fieldset>
Term: <input name='term' id='term' type='text' /><br />
Sayre Expires March 7, 2006 [Page 14]
Internet-Draft XHTML Microformats for the APP September 2005
Label: <input name='label' id='label' type='text' /><br />
Scheme: <input name='scheme' id='scheme' type='text' /><br />
<input type='submit' value='Add' />
</fieldset>
</form>
<dl class='category'>
%s
</dl>
</body>
</html>
"""
herror_template = u"""
<!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'>
<head profile='http://www.example.org/2005/Atom/hError'>
<title>Error %(code)d: %(shortDesc)s</title>
</head>
<body>
<p>Error %(code)d: %(shortDesc)s</p>
%(hError)s
%(hErrorId)s
</body>
</html>
"""
cat_template = u"""
Term: %(term)s
Label: %(label)s
Scheme: %(scheme)s"""
class HCatHandler(BaseHTTPServer.BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/":
self.send_response(200)
self.send_header('Content-type',self._gen_ctype_header())
self.end_headers()
resp = hcat_template % self.get_cats()
self.wfile.write(resp.encode('utf-8'))
elif self._is_cat(self.path):
cat = categories[int(self.path[8])]
self.send_response(200)
self.send_header('Content-Type',
'text/plain; charset=utf-8')
self.end_headers()
Sayre Expires March 7, 2006 [Page 15]
Internet-Draft XHTML Microformats for the APP September 2005
resp = cat_template % cat
self.wfile.write(resp.encode('utf-8'))
else:
self.send_herror(404, "File not found",
"Are you sure the address is right?",
self.gen_err_id())
def do_DELETE(self):
if (self._is_cat(self.path)
and categories[int(self.path[8])]):
categories[int(self.path[8])] = None
self.send_response(204)
self.end_headers()
else:
self.send_herror(404, "File not found",
"Category does not exist",
self.gen_err_id())
def do_PUT(self):
data = self._read_data()
if not data:
return
if (self._is_cat(self.path)
and categories[int(self.path[8])]):
form = cgi.parse_qs(data)
if not (form.has_key("term")):
self.send_herror(500, "Error",
"Term is required.",
self.gen_err_id())
return
categories[int(self.path[8])] = self.get_cat(form)
self.send_response(204)
self.end_headers()
else:
self.send_herror(404, "File not found",
"Category does not exist",
self.gen_err_id())
def do_POST(self):
redirect = "/"
host = self.headers.getheader('host')
if(host):
redirect = "http://" + host + redirect
data = self._read_data()
if not data:
return
if self.path == "/add":
form = cgi.parse_qs(data)
if not (form.has_key("term")):
Sayre Expires March 7, 2006 [Page 16]
Internet-Draft XHTML Microformats for the APP September 2005
self.send_herror(500, "Error",
"Term is required.",
self.gen_err_id())
return
categories.append(self.get_cat(form))
self.send_response(303)
self.send_header('Location',redirect)
self.end_headers()
else:
self.send_herror(404, "POSTed to incorrect location",
"Are you sure the address is right?",
self.gen_err_id())
def _read_data(self):
con_type = self.headers.getheader('content-type')
if con_type != 'application/x-www-form-urlencoded':
msg = "The content type was %s" % con_type
self.send_herror(415, "Unsupported Content-Type", msg,
self.gen_err_id())
return
clen = self.headers.getheader('content-length')
if clen:
clen = int(clen)
else:
self.send_herror(411, "Missing Content-Length", None,
self.gen_err_id())
return
return self.rfile.read(clen)
def _is_cat(self, path):
if (path.startswith('/ed?cat=') and int(path[8]) >= 0
and int(path[8]) < len(categories)):
return True
else:
return False
def _gen_ctype_header(self):
# we want to send XHTML, but will fudge with
# text/html if necessary (IE)
# TODO: parse these correctly
prefer = 'application/xhtml+xml'
template = '%s; charset=utf-8'
accept = self.headers.get('Accept')
if accept:
if prefer in accept:
ctype = prefer
else:
ctype = 'text/html'
Sayre Expires March 7, 2006 [Page 17]
Internet-Draft XHTML Microformats for the APP September 2005
else:
ctype = prefer
return '%s; charset=utf-8' % ctype
def get_cat(self,form):
def find_in_form(k):
if form.has_key(k):
return unicode(form[k][0],'utf-8')
else:
return None
return {'term':find_in_form('term'),
'label':find_in_form('label'),
'scheme':find_in_form('scheme')}
# loop through the categories in memory and return dt/dd pairs
def get_cats(self):
res = u""
for i in range(len(categories)):
if categories[i]:
cat = categories[i]
res += u" <dt>%s</dt>\n" % cat['term']
res += u" <dd>\n"
if cat['label']:
res += u" <span class='label'>"
res += cat['label']
res += u"</span>\n"
res += u" <a href='/ed?cat=%d'" % i
res += " rel='hCat'>Edit</a>\n"
if cat['scheme']:
res += u" <a href='"
res += cat['scheme']
res += u"' rel='scheme'>"
res += u"</a>\n"
res += u" </dd>\n\n"
return res
def send_herror(self, code, short_desc,
long_desc=None, err_id=None):
# log with the base class facilities
try:
short, long = self.responses[code]
except KeyError:
short, long = '???', '???'
message = "%s id: %s" % (short,err_id)
self.log_error("code %d, message %s", code, message)
Sayre Expires March 7, 2006 [Page 18]
Internet-Draft XHTML Microformats for the APP September 2005
# prepare hError doc
if long_desc:
hError = "<p id='hError'>%s</p>" % long_desc
else:
hError = ''
if err_id:
hErrorId = """<p>The identifier for this error is:
<span id='hErrorId'>%s</span></p>""" % err_id
else:
hErrorId = ''
content = (herror_template %
{'code':code,
'shortDesc': short_desc,
'hError': hError,
'hErrorId': hErrorId})
self.send_response(code, short_desc)
self.send_header("Content-Type", "text/html")
self.send_header('Connection', 'close')
self.end_headers()
if self.command != 'HEAD' and (code >= 200 and
code not in (204, 304)):
self.wfile.write(content)
def gen_err_id(self):
from random import choice
lnd='0123456789'
return ''.join(map(lambda x,y=lnd: choice(y), range(10)))
PORT = 8888
httpd = BaseHTTPServer.HTTPServer(("", PORT), HCatHandler)
print "serving at port", PORT
httpd.serve_forever()
Sayre Expires March 7, 2006 [Page 19]
Internet-Draft XHTML Microformats for the APP September 2005
Appendix B. An Example hCat and hError Client
You can run this client from the command line using the Python
programming language <http://www.python.org>. A sample server is
included in Appendix A.
Syntax: "python this_script.py http://example.com"
This script uses a SAX handler <http://www.saxproject.org> in an
effort to test viability in performance constrained environments.
Patches welcome :)
import sys,os,urllib2,urllib,urlparse,httplib
import xml.sax
from xml.sax.handler import *
import cStringIO
class Category:
def __init__(self, term):
self.term = term
self.label = None
self.scheme = None
self.uri = None
def __str__(self):
s = u'term: %s label: %s, scheme: %s'
ret = s % (self.term,self.label,self.scheme)
return ret.encode('unicode-escape')
class HCatForm:
pass
HCAT_PROFILE = u'http://www.example.org/2005/Atom/hCat'
HERROR_PROFILE = u'http://www.example.org/2005/Atom/hError'
XHTML_NS = u'http://www.w3.org/1999/xhtml'
CLOSE = 0
UE = 'unicode-escape'
FORM_ENCODED='application/x-www-form-urlencoded'
# states
(START,END,IN_HTML,
IN_HEAD,IN_BODY,IN_DL,
IN_DT,IN_DD,IN_LABEL,
IN_FORM, IN_P, IN_SPAN, IN_TITLE) = (1,2,3,4,5,
6,7,8,9,10,
11,12,13)
Sayre Expires March 7, 2006 [Page 20]
Internet-Draft XHTML Microformats for the APP September 2005
class HCatHandler(ContentHandler):
def __init__(self, baseURI):
self._base = baseURI
self.state = START
self._buf = ''
self.cats = []
self._lists = []
self.form = None
self.transitions = {
START: {(XHTML_NS,u'html'):self.open_html},
IN_HTML: {(XHTML_NS,u'html',CLOSE):self.close_html,
(XHTML_NS,u'head'):self.open_head,
(XHTML_NS,u'body'):self.open_body},
IN_HEAD: {(XHTML_NS,u'head',CLOSE):self.close_head},
IN_BODY: {(XHTML_NS,u'body',CLOSE):self.close_body,
(XHTML_NS,u'dl'):self.open_dl,
(XHTML_NS,u'form'):self.open_form},
IN_DL: {(XHTML_NS,u'dl',CLOSE):self.close_dl,
(XHTML_NS,u'dt'):self.open_dt,
(XHTML_NS,u'dd'):self.open_dd},
IN_DT: {(XHTML_NS,u'dt',CLOSE):self.close_dt},
IN_DD: {(XHTML_NS,u'dd',CLOSE):self.close_dd,
(XHTML_NS,u'span'):self.open_span,
(XHTML_NS,u'a'):self.open_anchor,
(XHTML_NS,u'dl'):self.open_dl},
IN_LABEL: {(XHTML_NS,u'span',CLOSE):self.close_span},
IN_FORM: {(XHTML_NS,u'form',CLOSE):self.close_form,
(XHTML_NS,u'input'):self.open_input,
(XHTML_NS,u'dl'):self.open_dl}
}
def open_html(self, name, attrs):
return IN_HTML
def close_html(self, name):
return END
def open_head(self, name, attrs):
profile = attrs.get((None,u'profile'))
if profile and HCAT_PROFILE in profile.split():
return IN_HEAD
else:
return END
def close_head(self, name):
return IN_HTML
def open_body(self, name, attrs):
Sayre Expires March 7, 2006 [Page 21]
Internet-Draft XHTML Microformats for the APP September 2005
return IN_BODY
def close_body(self, name):
return IN_HTML
def open_dl(self, name, attrs):
if attrs.get((None,u'class'))==u'category':
self._lists.append(self.state)
return IN_DL
else:
return None
def close_dl(self, name):
return self._lists.pop(-1)
def open_dt(self, name, attrs):
self._buf = ""
return IN_DT
def close_dt(self, name):
cat = Category(self._buf)
self.cats.append(cat)
return IN_DL
def open_dd(self, name, attrs):
self._buf = ""
return IN_DD
def close_dd(self, name):
return IN_DL
def open_span(self, name, attrs):
if attrs.get((None,u'class'))==u'label':
self._buf=''
return IN_LABEL
else:
return None
def close_span(self, name):
self.cats[-1].label = self._buf
return IN_DD
def open_form(self, name, attrs):
class_att = attrs.get((None,u'class'))
if class_att and u'hCat' in class_att.split():
self.form = HCatForm()
uri = attrs.get((None,u'action'))
if uri:
Sayre Expires March 7, 2006 [Page 22]
Internet-Draft XHTML Microformats for the APP September 2005
self.form.uri = urlparse.urljoin(self._base,uri)
csets = attrs.get((None,u'accept-charset'))
# charsets are 'space and/or comma separated' :/
csets = [x for subseq in
[x.split(',') for x in csets.split()]
for x in subseq if x != '']
self.form.charsets = csets
return IN_FORM
else:
return None
def close_form(self, name):
return IN_BODY
def open_input(self, name, attrs):
params = ('term','scheme','label')
input_id = attrs.get((None,u'id'))
if input_id:
name = attrs.get((None,u'name'))
if input_id=='term':
self.form.term = name
elif input_id=='label':
self.form.label = name
elif input_id=='scheme':
self.form.scheme = name
return None
def open_anchor(self, name, attrs):
rel = attrs.get((None,u'rel'))
href = attrs.get((None,u'href'))
if rel and u'scheme' in rel.split():
self.cats[-1].scheme = href
elif rel and u'hCat' in rel.split():
self.cats[-1].uri = urlparse.urljoin(self._base,href)
return None
## SAX Events
def startElementNS(self, name, qname, attrs):
try:
trans_func = self.transitions[self.state][name]
self.state = trans_func(name,attrs) or self.state
except KeyError:
pass
def endElementNS(self, name, qname):
try:
event = (name[0],name[1],CLOSE)
trans_func = self.transitions[self.state][event]
Sayre Expires March 7, 2006 [Page 23]
Internet-Draft XHTML Microformats for the APP September 2005
self.state = trans_func(name) or self.state
except KeyError:
pass
def characters(self, chars):
self._buf += chars
class HErrorHandler(ContentHandler):
def __init__(self):
self.state = START
self._buf = ''
self.title = None
self.hError = None
self.hErrorId = None
self.transitions = {
START: {(XHTML_NS,u'html'):self.open_html},
IN_HTML: {(XHTML_NS,u'html',CLOSE):self.close_html,
(XHTML_NS,u'head'):self.open_head,
(XHTML_NS,u'body'):self.open_body},
IN_HEAD: {(XHTML_NS,u'head',CLOSE):self.close_head,
(XHTML_NS,u'title'):self.open_title},
IN_TITLE: {(XHTML_NS,u'title',CLOSE):self.close_title},
IN_BODY: {(XHTML_NS,u'body',CLOSE):self.close_body,
(XHTML_NS,u'p'):self.open_p,
(XHTML_NS,u'span'):self.open_span},
IN_P: {(XHTML_NS,u'p',CLOSE):self.close_p},
IN_SPAN: {(XHTML_NS,u'span',CLOSE):self.close_span},
}
def open_html(self, name, attrs):
return IN_HTML
def close_html(self, name):
return END
def open_head(self, name, attrs):
profile = attrs.get((None,u'profile'))
if profile and HERROR_PROFILE in profile.split():
return IN_HEAD
else:
return END
def close_head(self, name):
return IN_HTML
def open_title(self, name, attrs):
Sayre Expires March 7, 2006 [Page 24]
Internet-Draft XHTML Microformats for the APP September 2005
self._buf = ''
return IN_TITLE
def close_title(self, name):
self.title = self._buf
return IN_HEAD
def open_body(self, name, attrs):
return IN_BODY
def close_body(self, name):
return IN_HTML
def open_p(self, name, attrs):
if attrs.get((None,u'id'))==u'hError':
self._buf = ''
return IN_P
else:
return None
def close_p(self, name):
self.hError = self._buf
return IN_BODY
def open_span(self, name, attrs):
if attrs.get((None,u'id'))==u'hErrorId':
self._buf = ''
return IN_SPAN
else:
return None
def close_span(self, name):
self.hErrorId = self._buf
return IN_BODY
## SAX Events
def startElementNS(self, name, qname, attrs):
try:
trans_func = self.transitions[self.state][name]
self.state = trans_func(name,attrs) or self.state
except KeyError:
pass
def endElementNS(self, name, qname):
try:
event = (name[0],name[1],CLOSE)
trans_func = self.transitions[self.state][event]
self.state = trans_func(name) or self.state
Sayre Expires March 7, 2006 [Page 25]
Internet-Draft XHTML Microformats for the APP September 2005
except KeyError:
pass
def characters(self, chars):
self._buf += chars
def prompt(str):
if str[-1:] != ' ': str=str+': '
if len(str) > 40:
print str
sys.stdout.flush()
return raw_input('--> ')
else:
return raw_input(str)
class HCatEditor:
def __init__(self, uri):
self._cats = []
self.uri = uri
self._form = None
def refresh_cats(self):
request = urllib2.Request(self.uri)
try:
data = self.make_request(request).read()
self.parse(data)
except urllib2.HTTPError,e:
self.print_error(e.read())
def parse(self,data):
hCat_handler = HCatHandler(self.uri)
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_namespaces, 1)
parser.setContentHandler(hCat_handler)
# you may want to provide a local resolver for
# the DTDs, so you don't hit w3.org
# parser.setEntityResolver(XhtmlResolver())
parser.parse(cStringIO.StringIO(data))
self._cats=hCat_handler.cats
self._form=hCat_handler.form
print '\n---- Server Categories ----'
for i in range(len(self._cats)):
print '[%d] %s' % (i+1,self._cats[i])
print '---------------------------\n'
def make_request(self, request):
Sayre Expires March 7, 2006 [Page 26]
Internet-Draft XHTML Microformats for the APP September 2005
request.add_header('User-agent',
'hCatDemo/00 +http://franklinmint.fm')
accept_list = 'application/xhtml+xml,text/html,'
accept_list += 'text/plain;q=0.9,*/*;q=0.5'
request.add_header('Accept', accept_list)
opener = urllib2.build_opener()
return opener.open(request)
def enter_field(self,noun,current):
if(current):
curr = current.encode(UE)
new = prompt('Enter a %s [%s]' % (noun,curr))
else:
new = prompt('Enter a %s' % noun)
if new != '':
return new
else:
return current
def enter_fields(self, term=None, label=None, scheme=None):
params = {}
term = unicode(self.enter_field('term',term),UE)
if self._form.label:
label = self.enter_field('label',label)
if label is not None:
label = unicode(label,UE)
if self._form.scheme:
scheme = self.enter_field('scheme',scheme)
if scheme is not None:
scheme = unicode(scheme,UE)
if label:
params[self._form.label] = label.encode('utf-8')
if scheme:
params[self._form.scheme] = scheme.encode('utf-8')
params[self._form.term] = term.encode('utf-8')
return params
def choose_add(self):
if not self._form:
print "No add capability"
return
if not 'utf-8' in self._form.charsets:
print "I can only use utf-8 for this demo..."
print "the form provided:",self._form.charsets
return
params = self.enter_fields()
request = urllib2.Request(self._form.uri,
urllib.urlencode(params))
Sayre Expires March 7, 2006 [Page 27]
Internet-Draft XHTML Microformats for the APP September 2005
resp = self.make_request(request)
self.uri = resp.url
self.parse(resp.read())
def pick_cat(self):
try:
cat_num = int(prompt('Pick a number from the list')) - 1
if cat_num < 0 or cat_num >= len(self._cats):
raise ValueError
except ValueError:
print 'Value not in list.'
return None
if not self._cats[cat_num].uri:
print "Category has no editing capability"
return None
return cat_num
def choose_delete(self):
cat_num = self.pick_cat()
if cat_num is not None:
parts = urlparse.urlsplit(self._cats[cat_num].uri)
conn = httplib.HTTPConnection(parts[1])
conn.request('DELETE', parts[2]+'?'+parts[3])
response = conn.getresponse()
if response.status in range(200,299):
print "\nOK"
self.refresh_cats()
elif response.status in range(400,599):
data = response.read()
self.print_error(data)
else:
print response.reason
return
def choose_edit(self):
cat_num = self.pick_cat()
if cat_num is not None:
label = self._cats[cat_num].label
scheme = self._cats[cat_num].scheme
if label:
label = label.encode(UE)
if scheme:
scheme = scheme.encode(UE)
term = self._cats[cat_num].term
p = self.enter_fields(term.encode(UE),
label, scheme)
body = urllib.urlencode(p)
Sayre Expires March 7, 2006 [Page 28]
Internet-Draft XHTML Microformats for the APP September 2005
headers = {'Content-Type':FORM_ENCODED,
'Content-Length':len(body)}
parts = urlparse.urlsplit(self._cats[cat_num].uri)
conn = httplib.HTTPConnection(parts[1])
conn.request('PUT', parts[2]+'?'+parts[3],
body, headers)
response = conn.getresponse()
if response.status in range(200,299):
print "\nOK"
self.refresh_cats()
elif response.status in range(400,599):
data = response.read()
self.print_error(data)
else:
print response.reason
return
def print_error(self,data):
hError_handler = HErrorHandler()
parser = xml.sax.make_parser()
parser.setFeature(xml.sax.handler.feature_namespaces, 1)
parser.setContentHandler(hError_handler)
# you may want to provide a local resolver for
# the DTDs, so you don't hit w3.org.
# parser.setEntityResolver(XhtmlResolver())
print "An Error occured.\n"
try:
parser.parse(cStringIO.StringIO(data))
if hError_handler.title:
print hError_handler.title
if hError_handler.hError:
print "Description:"
print hError_handler.hError
if hError_handler.hErrorId:
print "ID:" + hError_handler.hErrorId
except xml.sax.SAXException, e:
pass
print ""
def usage():
print "Usage: %s http_uri" % os.path.basename(sys.argv[0])
def main():
args = sys.argv[1:]
if "-h" in args or "--help" in args or len(args)==0:
usage()
sys.exit(2)
editor = HCatEditor(args[0])
Sayre Expires March 7, 2006 [Page 29]
Internet-Draft XHTML Microformats for the APP September 2005
editor.refresh_cats()
msg = "Choose: [a]dd, [e]dit, [d]elete, [r]efresh, [q]uit"
while True:
x = prompt(msg)
if x == 'q':
return 0
elif x == 'r':
editor.refresh_cats()
elif x == 'e':
editor.choose_edit()
elif x == 'd':
editor.choose_delete()
elif x == 'a':
editor.choose_add()
else:
print 'Unknown option.\n'
if __name__ == "__main__":
main()
Sayre Expires March 7, 2006 [Page 30]
Internet-Draft XHTML Microformats for the APP September 2005
Intellectual Property Statement
The IETF takes no position regarding the validity or scope of any
Intellectual Property Rights or other rights that might be claimed to
pertain to the implementation or use of the technology described in
this document or the extent to which any license under such rights
might or might not be available; nor does it represent that it has
made any independent effort to identify any such rights. Information
on the procedures with respect to rights in RFC documents can be
found in BCP 78 and BCP 79.
Copies of IPR disclosures made to the IETF Secretariat and any
assurances of licenses to be made available, or the result of an
attempt made to obtain a general license or permission for the use of
such proprietary rights by implementers or users of this
specification can be obtained from the IETF on-line IPR repository at
http://www.ietf.org/ipr.
The IETF invites any interested party to bring to its attention any
copyrights, patents or patent applications, or other proprietary
rights that may cover technology that may be required to implement
this standard. Please address the information to the IETF at
ietf-ipr@ietf.org.
The IETF has been notified of intellectual property rights claimed in
regard to some or all of the specification contained in this
document. For more information consult the online list of claimed
rights.
Disclaimer of Validity
This document and the information contained herein are provided on an
"AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
Copyright Statement
Copyright (C) The Internet Society (2005). This document is subject
to the rights, licenses and restrictions contained in BCP 78, and
except as set forth therein, the authors retain all their rights.
Sayre Expires March 7, 2006 [Page 31]
Internet-Draft XHTML Microformats for the APP September 2005
Acknowledgment
Funding for the RFC Editor function is currently provided by the
Internet Society.
Sayre Expires March 7, 2006 [Page 32]