root/zgeo.atom/trunk/zgeo/atom/browser.py @ 1314

Revision 1314, 10.1 KB (checked in by seang, 12 months ago)

Remove commented code.

Line 
1from zope.traversing.browser.interfaces import IAbsoluteURL
2from zope.dublincore.interfaces import ICMFDublinCore
3
4try:
5    from Products.Five.browser.pagetemplatefile import ViewPageTemplateFile
6    raise Exception, "Five's ViewPageTemplateFile doesn't work with named templating"
7except:
8    from zope.app.pagetemplate import ViewPageTemplateFile
9
10from zope.interface import implements
11from zope.publisher.browser import BrowserPage
12from zope.formlib.namedtemplate import NamedTemplate
13from zope.formlib.namedtemplate import NamedTemplateImplementation
14from zope.component import getMultiAdapter, queryAdapter
15import zope.security.proxy
16
17from zgeo.geographer.interfaces import IGeoreferenced
18from zgeo.atom.interfaces import IAtomBase, IEntry, ILink
19from zgeo.atom.interfaces import IFeed, ISubscriptionFeed, ISearchFeed
20from zgeo.atom.interfaces import IWriteAtomMetadata, IAtomPublishable
21from zgeo.atom.interfaces import IAtomPubPOSTable
22from zgeo.atom.link import Link
23import zope.datetime
24
25# zgeo.spatialindex is required to use a bbox parameter with paging search
26# feeds
27try:
28    from zgeo.spatialindex.site import get_catalog
29except ImportError:
30    def noner(arg):
31        return None
32    get_catalog = noner
33
34def coords_to_georss(geom):
35    gtype = geom.type
36    if gtype == 'Point':
37        coords = (geom.coordinates,)
38    elif gtype == 'Polygon':
39        coords = geom.coordinates[0]
40    else:
41        coords = geom.coordinates
42    tuples = ('%f %f' % (c[1], c[0]) for c in coords)
43    return ' '.join(tuples)
44
45def rfc3339(date):
46    ts = zope.datetime.time(date)
47    return zope.datetime.iso8601_date(ts)
48
49def absoluteURL(ob, request):
50    return getMultiAdapter((ob, request), IAbsoluteURL)()
51
52
53class NullGeometry(object):
54    type = None
55    coordinates = None
56
57
58class NullGeoItem(object):
59    id = None
60    properties = None
61
62    def __init__(self):
63        self.geometry = NullGeometry()
64
65
66class AtomBase(BrowserPage):
67
68    """Not to be instantiated.
69    """
70    implements(IAtomBase)
71
72    @property
73    def id(self):
74        context = zope.security.proxy.removeSecurityProxy(self.context)
75        atom = IWriteAtomMetadata(context)
76        if atom.id is None:
77            return atom.setId()
78        return atom.id
79
80    @property
81    def title(self):
82        return self.dc.Title()
83
84    @property
85    def updated(self):
86        return rfc3339(self.dc.ModificationDate())
87
88
89    @property
90    def author(self):
91        return {
92            'name': self.dc.Creator()
93,
94            'uri': '',
95            'email': ''
96            }
97
98    @property
99    def links(self):
100        """Override this."""
101        raise NotImplementedError
102
103
104class LinkEntry(AtomBase):
105
106    implements(IEntry)
107
108    __name__ = 'atom-entry'
109    template = NamedTemplate('template-atom-entry')
110
111    def __init__(self, context, request):
112        self.context = context
113        self.request = request
114        self.dc = zope.security.proxy.removeSecurityProxy(
115            ICMFDublinCore(self.context)
116            )
117        try:
118            self.geom = IGeoreferenced(self.context)
119        except:
120            self.geom = NullGeometry()
121
122    @property
123    def published(self):
124        return rfc3339(self.dc.CreationDate())
125
126    @property
127    def summary(self):
128        return self.dc.Description()
129
130    @property
131    def links(self):
132        items = {
133            'alternate': Link(
134                absoluteURL(self.context, self.request),
135                rel='alternate',
136                type='text/html')
137            }
138        if IAtomPublishable.providedBy(self.context):
139            items['edit'] = Link(
140                "%s/atom-entry" % absoluteURL(self.context, self.request),
141                rel='edit',
142                type='application/atom+xml;type=entry')
143        return items
144
145    @property
146    def hasPoint(self):
147        return int(self.geom.type == 'Point')
148
149    @property
150    def hasLineString(self):
151        return int(self.geom.type == 'LineString')
152
153    @property
154    def hasPolygon(self):
155        return int(self.geom.type == 'Polygon')
156
157    @property
158    def coords_georss(self):
159        return coords_to_georss(self.geom)
160
161    def __call__(self):
162        return self.template().encode('utf-8')
163
164
165class FeedBase(AtomBase):
166
167    implements(IFeed)
168
169    def __init__(self, context, request):
170        self.context = context
171        self.request = request
172        self.dc = zope.security.proxy.removeSecurityProxy(
173            ICMFDublinCore(self.context)
174            )
175
176    @property
177    def links(self):
178        raise NotImplementedError
179
180    @property
181    def entries(self):
182        context = zope.security.proxy.removeSecurityProxy(self.context)
183        for item in context.values():
184            yield LinkEntry(item, self.request)
185
186    def collection_href(self):
187        if IAtomPubPOSTable.providedBy(self.context):
188            return '%s/atompub-collection' % absoluteURL(
189                                                self.context, self.request)
190        return None
191
192
193class SubscriptionFeed(FeedBase):
194
195    implements(ISubscriptionFeed)
196
197    __name__ = 'atom-subscription-feed'
198    template = NamedTemplate('template-atom-subscription-feed')
199
200    @property
201    def links(self):
202        return {
203            'alternate': Link(
204                            absoluteURL(self.context, self.request),
205                            rel='alternate',
206                            type='text/html'
207                            ),
208            'self': Link(
209                self.request.getURL(),
210                rel='self',
211                type='application/atom+xml'
212                ),
213            'previous-archive': Link('None', rel='previous-archive')
214            }
215
216    def __call__(self):
217        return self.template().encode('utf-8')
218
219
220class SearchFeed(FeedBase):
221
222    implements(ISearchFeed)
223
224    __name__ = 'atom-search-feed'
225    template = NamedTemplate('template-atom-search-feed')
226    page_size = 20
227
228    def __init__(self, context, request):
229        self.context = context
230        self.request = request
231        self.dc = ICMFDublinCore(self.context)
232        self.catalog = None
233        self.bounds = None
234        self.page = 1
235        self.num_results = 0
236        self.num_pages = 0
237        self.results = []
238
239    def parse_bbox(self, bbox=None):
240        if bbox is None:
241            b = self.request.form.get('bbox')
242            if b is None:
243                return None
244        else:
245            b = bbox
246        return tuple(float(x) for x in b.split(','))
247
248    def _first_link(self):
249        url = "%s/@@%s" % (
250            absoluteURL(self.context, self.request), self.__name__
251            )
252        if self.bounds:
253            url = "%s?bbox=%f,%f,%f,%f" % ((url,) + self.bounds)
254        return url
255
256    def _last_link(self):
257        url = "%s/@@%s?page=-1" % (
258            absoluteURL(self.context, self.request), self.__name__
259            )
260        if self.bounds:
261            url = "%s&bbox=%f,%f,%f,%f" % ((url,) + self.bounds)
262        return url
263
264    def _previous_link(self):
265        if self.page == 1:
266            return 'None'
267        url = "%s/@@%s?page=%d" % (
268            absoluteURL(self.context, self.request),
269            self.__name__,
270            self.page - 1
271            )
272        if self.bounds:
273            url = "%s&bbox=%f,%f,%f,%f" % ((url,) + self.bounds)
274        return url
275
276    def _next_link(self):
277        if self.page == -1 or self.page >= self.num_pages:
278            return 'None'
279        url = "%s/@@%s?page=%d" % (
280            absoluteURL(self.context, self.request),
281            self.__name__,
282            self.page + 1
283            )
284        if self.bounds:
285            url = "%s&bbox=%f,%f,%f,%f" % ((url,) + self.bounds)
286        return url
287
288    def update(self):
289        self.bounds = self.parse_bbox()
290        if self.bounds is not None:
291            self.catalog = get_catalog(self.context)
292            if self.catalog is None:
293                raise Exception, "Spatial search is not supported in the absence of a spatial catalog"
294            results = self.catalog.searchResults(bounds=self.bounds)
295            num_results = len(results)
296            self.results = results
297        else:
298            results = list(self.context.values())
299            num_results = len(results)
300            self.results = results
301       
302        self.num_pages = num_results/self.page_size + num_results%self.page_size
303        self.num_results = num_results
304       
305        page = int(self.request.form.get('page', 1))
306        if page > 1 and page > self.num_pages:
307            raise Exception, "Page number exceeds number of pages"
308        elif page < 0 and -page > self.num_pages:
309            raise Exception, "Page number exceeds number of pages"
310        else:
311            self.page = page
312
313    @property
314    def entries(self):
315        if self.page >= 0:
316            begin = (self.page-1) * self.page_size
317            end = begin + self.page_size
318        else:
319            begin = self.num_results + (self.page * self.page_size)
320            end = begin + self.page_size
321        if end > self.num_results: end = self.num_results
322        for result in list(self.results)[begin:end]:
323            yield LinkEntry(result, self.request)
324
325    @property
326    def links(self):
327        return {
328            'alternate': Link(
329                absoluteURL(self.context, self.request),
330                rel='alternate',
331                type='text/html'
332                ),
333            'self': Link(
334                '%s/@@%s' % (absoluteURL(self.context, self.request), self.__name__),
335                rel='self',
336                type='application/atom+xml'
337                ),
338            'first': Link(self._first_link(), rel='first'),
339            'last': Link(self._last_link(), rel='last'),
340            'previous': Link(self._previous_link(), rel='previous'),
341            'next': Link(self._next_link(), rel='next'),
342            }
343
344    def __call__(self):
345        self.update()
346        return self.template().encode('utf-8')
347
348
349# Named template implementations
350
351entry_template = NamedTemplateImplementation(
352    ViewPageTemplateFile('entry.pt')
353    )
354
355subscription_feed_template = NamedTemplateImplementation(
356    ViewPageTemplateFile('subscription_feed.pt')
357    )
358
359search_feed_template = NamedTemplateImplementation(
360    ViewPageTemplateFile('search_feed.pt')
361    )
362
Note: See TracBrowser for help on using the browser.