38. Creating a Dynamic Front Page#
In this chapter we will:
Create a standalone view used for the front page
Show dynamic content
Use ajax to load content
Embed tweets about ploneconf
The topics we cover are:
Standalone views
Querying the catalog by date
DRY ("Don't Repeat Yourself")
macros
patterns
38.1. The Front Page#
Register the view in browser/configure.zcml
:
<browser:page
name="frontpageview"
for="*"
layer="ploneconf.site.interfaces.IPloneconfSiteLayer"
class=".frontpage.FrontpageView"
template="templates/frontpageview.pt"
permission="zope2.View"
/>
Add the view to a file browser/frontpage.py
. We want a list of all talks that happen today.
1# -*- coding: utf-8 -*-
2from plone import api
3from Products.Five.browser import BrowserView
4
5import datetime
6
7
8class FrontpageView(BrowserView):
9 """The view of the conference frontpage
10 """
11
12 def talks(self):
13 """Get today's talks"""
14 results = []
15 today = datetime.date.today()
16 brains = api.content.find(
17 portal_type='talk',
18 sort_on='start',
19 sort_order='descending',
20 )
21 for brain in brains:
22 if brain.start.date() == today:
23 results.append({
24 'title': brain.Title,
25 'description': brain.Description,
26 'url': brain.getURL(),
27 'audience': ', '.join(brain.audience or []),
28 'type_of_talk': brain.type_of_talk,
29 'speaker': brain.speaker,
30 'room': brain.room,
31 'start': brain.start,
32 })
33 return results
We do not constrain the search to a certain folder to also find the party and the sprints.
With
if brain.start.date() == today:
we test if the talk is today.It would be more effective to query the catalog for events that happen in the daterange between today and tomorrow:
1today = datetime.date.today() 2tomorrow = today + datetime.timedelta(days=1) 3date_range_query = {'query': (today, tomorrow), 'range': 'min:max'} 4brains = api.content.find( 5 portal_type='talk', 6 start=date_range_query, 7 sort_on='start', 8 sort_order='ascending' 9)
The
sort_on='start'
sorts the results returned by the catalog by start-date.By removing the
portal_type='talk'
from the query you could include other events in the schedule (like the party or sightseeing-tours). But you'd have to take care to not create AttributeErrors by accessing fields that are specific to talk. To work around that usespeaker = getattr(brain, 'speaker', None)
and testing withif speaker is not None:
The rest is identical to what the talklistview does.
38.2. The template#
Create the template browser/templates/frontpageview.pt
(for now without talks). Display the rich text field to allow the frontpage to be edited.
1<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
2 metal:use-macro="context/main_template/macros/master"
3 i18n:domain="ploneconf.site">
4<body>
5
6<metal:content-core fill-slot="content-core">
7
8 <div id="parent-fieldname-text"
9 tal:condition="python: getattr(context, 'text', None)"
10 tal:content="structure python:context.text.output_relative_to(view.context)" />
11
12</metal:content-core>
13
14</body>
15</html>
Now you could add the whole code that we used for the talklistview again. But instead we go D.R.Y. and reuse the talklistview by turning it into a macro.
Edit browser/templates/talklistview.pt
and wrap the list in a macro definition:
1<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
2 metal:use-macro="context/main_template/macros/master"
3 i18n:domain="ploneconf.site">
4<body>
5 <metal:content-core fill-slot="content-core">
6
7 <metal:talklist define-macro="talklist">
8 <table class="listing"
9 id="talks"
10 tal:define="talks python:view.talks()">
11 <thead>
12 <tr>
13 <th>Title</th>
14 <th>Speaker</th>
15 <th>Audience</th>
16 <th>Time</th>
17 <th>Room</th>
18 </tr>
19 </thead>
20 <tbody>
21 <tr tal:repeat="talk talks">
22 <td>
23 <a href=""
24 class="pat-contentloader"
25 data-pat-contentloader="url:${python:talk['url']}?ajax_load=1;content:#content;target:.talkinfo > *"
26 tal:attributes="href python:talk['url'];
27 title python:talk['description']"
28 tal:content="python:talk['title']">
29 The 7 sins of plone-development
30 </a>
31 </td>
32 <td tal:content="python:talk['speaker']">
33 Philip Bauer
34 </td>
35 <td tal:content="python:talk['audience']">
36 Advanced
37 </td>
38 <td class="pat-moment"
39 data-pat-moment="format:calendar"
40 tal:content="python:talk['start']">
41 Time
42 </td>
43 <td tal:content="python:talk['room']">
44 101
45 </td>
46 </tr>
47 <tr tal:condition="not:talks">
48 <td colspan=5>
49 No talks so far :-(
50 </td>
51 </tr>
52 </tbody>
53 </table>
54 <div class="talkinfo"><span /></div>
55 </metal:talklist>
56
57 </metal:content-core>
58</body>
59</html>
Now use that macro in browser/templates/frontpageview.pt
1<div class="col-lg-6">
2 <h2>Todays Talks</h2>
3 <div metal:use-macro="context/@@talklistview/talklist">
4 Instead of this the content of the macro will appear...
5 </div>
6</div>
Calling that macro in Python looks like this metal:use-macro="python: context.restrictedTraverse('talklistview')['talklist']"
Note
In talklistview.pt
the call view/talks"
calls the method talks()
from the browser view TalkListView
to get the talks. Reused as a macro on the frontpage it now uses the method talks()
by the frontpageView
to get a different list!
It is not always smart to do that since you might want to display other data. E.g. for a list of todays talks you don't want show the date but only the time using data-pat-moment="format:LT"
Also this frontpage will probably not win a beauty-contest. But that's not the task of this training.
Exercise 1#
Change the link to open the talk-info in a modal.
38.3. Twitter#
You might also want to embed a twitter feed into the page. Luckily twitter makes it easy to do that.
When you browse to the publish.twitter.com and have them create a snippet for @ploneconf and paste it in the template wrapped in a <div class="col-lg-6">...</div>
to have the talklist next to the feeds:
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"
metal:use-macro="context/main_template/macros/master"
i18n:domain="ploneconf.site">
<body>
<metal:content-core fill-slot="content-core">
<div id="parent-fieldname-text"
tal:condition="python: getattr(context, 'text', None)"
tal:content="structure python:context.text.output_relative_to(view.context)" />
<div class="col-lg-6">
<h2>Todays Talks</h2>
<div metal:use-macro="context/@@talklistview/talklist">
Instead of this the content of the macro will appear...
</div>
</div>
<div class="col-lg-6">
<a class="twitter-timeline" data-height="600" data-dnt="true" href="https://x.com/ploneconf?ref_src=twsrc%5Etfw">Tweets by ploneconf</a> <script async src="https://platform.x.com/widgets.js" charset="utf-8"></script>
</div>
</metal:content-core>
</body>
</html>
38.4. Activating the view#
The view is meant to be used with documents (or any other type that has a rich text field 'text'). The easiest way to use it is setting it as the default view for the Document that is currently the default page for the portal. By default that document has the id front-page
.
You can either access it directly at http://localhost:8080/Plone/front-page or by disabling the default page for the portal and it should show up in the navigation. Try out the new view like this: http://localhost:8080/Plone/front-page/frontpageview.
To set that view by hand as the default view for front-page
in the ZMI: http://localhost:8080/Plone/front-page/manage_propertiesForm. Add a new property layout
and set it to frontpageview
.
Done. This way you can still use the button Edit to edit the frontpage.
See also