1
2 __docformat__ = 'epytext'
3 __doc__ = """Represents an Atom Publisging Protocol service entity.
4 """
5
6 import amara
7 from amplee.utils import ATOM10_NS, ATOM10_PREFIX, ATOMPUB_PREFIX, \
8 ATOMPUB_NS, XML_PREFIX, XML_NS, XMLNS_PREFIX, XMLNS_NS
9 from amplee.utils import generate_uuid_uri, get_isodate, qname
10 from amplee.comparer import app_edited_comparer
11 from amplee.error import UnknownResource
12
13 __all__ = ['AtomPubService']
14
16 - def __init__(self, store, xml_attrs=None):
17 """Atom Publisging Protocol service entity.
18
19 @type store: L{AtomPubStore}
20 @param store: L{AtomPubStore} instance or C{None}
21 @type xml_attrs: dict
22 @param xml_attrs: allows for XML attributes to be provided: lang, base
23 """
24 self.store = store
25 self.xml_attrs = xml_attrs or {}
26
27
28
29 self.workspaces = []
30
31 self.xslt_path = None
32
35
37 """Returns a workspace per its identifier.
38
39 @type name_or_id: string
40 @param name_or_id: matches the workspace attribute of the
41 same name.
42 """
43 for workspace in self.workspaces:
44 if workspace.name_or_id == name_or_id:
45 return workspace
46
48 """Returns all the collections belonging to workspaces
49 of this service.
50
51 @rtype: list
52 @return: the list of L{AtomPubCollection} belonging
53 to this service.
54 """
55 collections = []
56 for workspace in self.workspaces:
57 collections.extend(workspace.collections)
58 return collections
59
61 """Returns a collection identified by C{name_or_id}
62
63 @type name_or_id: string
64 @param name_or_id: identifier for the collection to be looked up
65 @rtype: L{AtomPubCollection}
66 @return: collection associated with the provided identifier
67 """
68 for workspace in self.workspaces:
69 for collection in workspace.collections:
70 if collection.name_or_id == name_or_id:
71 return collection
72
74 """Returns a collection identified by its URI.
75
76 @type uri: string
77 @param uri: absolute URI of the collection.
78 @rtype: L{AtomPubCollection}
79 @return: collection associated with the provided identifier
80 """
81 for workspace in self.workspaces:
82 for collection in workspace.collections:
83 if collection.get_base_edit_uri() == uri:
84 return collection
85
86
88 """Generates and returns a L{amara} instance of the service
89 document and its workspaces.
90
91 @type prefix: string
92 @param prefix: XML prefix to use
93
94 @type namespace: string
95 @param namespace: namespace associated with the service document
96
97 @type include_workspace_id: bool
98 @param include_workspace_id: if you want the C{name_or_id} attribute value as C{xml:id}
99 of the {app:workspace} element, set this to C{True}.
100
101 @rtype: L{amara.root_base}
102 @return: amara instance of the service document
103 """
104 d = amara.create_document(prefixes={ATOM10_PREFIX: ATOM10_NS})
105 if self.xslt_path:
106 pi = amara.bindery.pi_base(u"xml-stylesheet", u'href="%s" type="text/xsl"' % self.xslt_path)
107 d.xml_append(pi)
108
109 attrs = {}
110 if self.xml_attrs is not None:
111 for attr in self.xml_attrs:
112 attrs[(qname(attr, XML_PREFIX), XML_NS)] = self.xml_attrs[attr]
113
114 service = d.xml_append(d.xml_create_element(qname(u"service", prefix),
115 ns=namespace, attributes=attrs))
116
117 for workspace in self.workspaces:
118 w = workspace.to_workspace(prefix=prefix, namespace=namespace,
119 include_id=include_workspace_id)
120 service.xml_append(w.rootNode)
121
122 return d
123
127 """
128 Generates a new atom feed from the passed items dictionnary.
129
130 @type items: dict
131 @param items: C{{collection_name: [member_ids,]}} as returned for instance
132 by the L{Store.list_members} method.
133
134 @type entry_processor: callable
135 @param entry_processor: callable that takes a
136 member instance as parameter and returns a L{amara.root_base} representing
137 the entry after being processed. Useful for instance to transform
138 the entry into a more public face.
139
140 @type id: string
141 @param id: id of the feed and if not provided a urn:uuid will be generated.
142
143 @type xslt_path: string
144 @param xslt_path: if provided it will be inserted as a processing
145 instruction.
146
147 @type member_comparer: callable
148 @param member_comparer: used to
149 compare two entries when they are sorted within. By default
150 they are sorted by app:edited element.
151
152 @rtype: L{amara.root_base}
153 @return: an amara instance representing the Atom feed.
154 """
155 d = amara.create_document()
156 if xslt_path:
157 pi = amara.bindery.pi_base(u"xml-stylesheet", u'href="%s" type="text/xsl"' % xslt_path)
158 d.xml_append(pi)
159
160 feed = d.xml_create_element(qname(u"feed", ATOM10_PREFIX), ns=ATOM10_NS)
161 d.xml_append(feed)
162
163 uuid = id or generate_uuid_uri()
164 feed.xml_append(d.xml_create_element(qname(u"id", ATOM10_PREFIX),
165 ns=ATOM10_NS, content=uuid))
166 feed.xml_append(d.xml_create_element(qname(u"updated", ATOM10_PREFIX),
167 ns=ATOM10_NS, content=get_isodate()))
168 feed.xml_append(d.xml_create_element(qname(u"title", ATOM10_PREFIX), ns=ATOM10_NS,
169 attributes={u'type': u'text'}, content=title))
170
171 xml_base = self.xml_attrs.get('base', None)
172 if xml_base:
173 feed.xml_set_attribute((qname(u'base', XML_PREFIX), XML_NS), xml_base)
174
175 xml_lang = self.xml_attrs.get('lang', None)
176 if xml_lang:
177 feed.xml_set_attribute((qname(u'lang', XML_PREFIX), XML_NS), xml_lang)
178
179 if not callable(entry_processor):
180
181
182 entry_processor = lambda m: m.atom
183
184 entries = []
185 for collection_name in items:
186 c = self.get_collection(collection_name)
187 for member_id in items[collection_name]:
188 try:
189 member = c.load_member(member_id)
190 entry = entry_processor(member)
191 entries.append(entry.childNodes[0])
192 except UnknownResource:
193 pass
194
195 entries.sort(cmp=member_comparer)
196
197 for entry in entries:
198 feed.xml_append(entry)
199
200 return d
201