Stop invalidating the entire session before generating the page...
[zzz-spline-frontpage.git] / splinext / frontpage / controllers / frontpage.py
1 import datetime
2 import logging
3
4 from pylons import config, request, response, session, tmpl_context as c, url
5 from pylons.controllers.util import abort, redirect
6 from routes import request_config
7 from sqlalchemy.orm.exc import NoResultFound
8
9 from spline.lib import helpers as h
10 from spline.lib.base import BaseController, render
11 from spline.model import meta
12 from splinext.frontpage.sources import max_age_to_datetime
13
14 log = logging.getLogger(__name__)
15
16 class FrontPageController(BaseController):
17
18 def index(self):
19 """Magicaltastic front page.
20
21 Plugins can register a hook called 'frontpage_updates_<type>' to add
22 updates to the front page. `<type>` is an arbitrary string indicating
23 the sort of update the plugin knows how to handle; for example,
24 spline-forum has a `frontpage_updates_forum` hook for posting news from
25 a specific forum.
26
27 Hook handlers should return a list of FrontPageUpdate objects.
28
29 Standard hook parameters are:
30 `limit`, the maximum number of items that should ever be returned.
31 `max_age`, the number of seconds after which items expire.
32 `title`, a name for the source.
33 `icon`, an icon to show next to its name.
34
35 `limit` and `max_age` are also global options.
36
37 Updates are configured in the .ini like so:
38
39 spline-frontpage.sources.foo = updatetype
40 spline-frontpage.sources.foo.opt1 = val1
41 spline-frontpage.sources.foo.opt2 = val2
42
43 Note that the 'foo' name is completely arbitrary and is only used for
44 grouping options together. This will result in a call to:
45
46 run_hooks('frontpage_updates_updatetype', opt1=val1, opt2=val2)
47
48 Local plugins can override the fairly simple index.mako template to
49 customize the front page layout.
50 """
51
52 updates = []
53 global_limit = config['spline-frontpage.limit']
54 global_max_age = max_age_to_datetime(
55 config['spline-frontpage.max_age'])
56
57 c.sources = config['spline-frontpage.sources']
58 for source in c.sources:
59 new_updates = source.poll(global_limit, global_max_age)
60 updates.extend(new_updates)
61
62 # Little optimization: once there are global_limit items, anything
63 # older than the oldest cannot possibly make it onto the list. So,
64 # bump global_max_age to that oldest time if this is ever the case.
65 updates.sort(key=lambda obj: obj.time, reverse=True)
66 del updates[global_limit:]
67
68 if updates and len(updates) == global_limit:
69 global_max_age = updates[-1].time
70
71 # Find the oldest unseen item, to draw a divider after it.
72 # If this stays as None, the divider goes at the top
73 c.last_seen_item = None
74 # Could have a timestamp in the stash if this is a user, or in a cookie
75 # if this session has ever been logged out...
76 times = []
77 for source in (c.user.stash, request.cookies):
78 try:
79 times.append( int(source['frontpage-last-seen-time']) )
80 except (KeyError, ValueError):
81 pass
82
83 if times:
84 last_seen_time = datetime.datetime.fromtimestamp(max(times))
85 for update in updates:
86 if update.time > last_seen_time:
87 c.last_seen_item = update
88 else:
89 break
90
91 # Save ~now~ as the last-seen time
92 now = datetime.datetime.now().strftime('%s')
93 if c.user:
94 c.user.stash['frontpage-last-seen-time'] = now
95 meta.Session.add(c.user)
96 else:
97 response.set_cookie('frontpage-last-seen-time', now)
98
99 # Done! Feed to template
100 c.updates = updates
101
102 ret = render('/index.mako')
103
104 # Commit AFTER rendering the template! Committing invalidates
105 # everything in the session, undoing any eagerloading that may have
106 # been done by sources
107 meta.Session.commit()
108 return ret