witten
/
luminotes
Archived
1
0
Fork 0

Changed controller.Users.logout() to not be a JSON method so just hitting the

"/users/logout" URL will log you out and redirect you the front page.

Made front page prettier and better at explaining Luminotes (hopefully). The
front page is no longer a wiki, but is instead a mostly static page.
This commit is contained in:
Dan Helfman 2008-03-01 00:26:06 +00:00
parent 04c2569ae9
commit 3af5af18c5
24 changed files with 473 additions and 46 deletions

4
NEWS
View File

@ -1,5 +1,5 @@
1.2.1: March ??, 2008
* ?
1.2.1: February 29, 2008
* Updated the front page of the site to look nicer and better explain things.
1.2.0: February 25, 2008
* Users can now upload files to attach to their notes.

View File

@ -11,6 +11,7 @@ from model.Note import Note
from model.Notebook import Notebook
from model.User import User
from view.Main_page import Main_page
from view.Product_page import Product_page
from view.Notebook_rss import Notebook_rss
from view.Upgrade_note import Upgrade_note
from view.Json import Json
@ -132,7 +133,7 @@ class Root( object ):
redirect = u"/users/redeem_invite/%s" % invite_id,
)
@expose( view = Main_page )
@expose( view = Product_page )
@strongly_expire
@grab_user_id
@validate(
@ -158,12 +159,14 @@ class Root( object ):
# if the user is logged in and not using https, then redirect to the https version of the page (if available)
if https_url and cherrypy.request.remote_addr != https_proxy_ip:
return dict( redirect = https_url )
return dict( redirect = u"%s/" % https_url )
result = self.__users.current( user_id )
main_notebooks = [ nb for nb in result[ "notebooks" ] if nb.name == u"Luminotes" ]
result.update( self.__notebooks.contents( main_notebooks[ 0 ].object_id, user_id = user_id ) )
parents = [ notebook for notebook in result[ u"notebooks" ] if notebook.trash_id and not notebook.deleted ]
if len( parents ) > 0:
result[ "first_notebook" ] = parents[ 0 ]
else:
result[ "first_notebook" ] = None
return result

View File

@ -402,13 +402,13 @@ class Users( object ):
authenticated = user,
)
@expose( view = Json )
@expose()
@update_auth
def logout( self ):
"""
Deauthenticate the user and log them out of their current session.
@rtype: json dict
@rtype: dict
@return: { 'redirect': url, 'deauthenticated': True }
"""
return dict(
@ -463,7 +463,7 @@ class Users( object ):
user = user,
notebooks = notebooks + anon_notebooks,
login_url = login_url,
logout_url = self.__https_url + u"/",
logout_url = self.__https_url + u"/users/logout",
rate_plan = ( user.rate_plan < len( self.__rate_plans ) ) and self.__rate_plans[ user.rate_plan ] or {},
)

View File

@ -9,7 +9,7 @@ class Test_root( Test_controller ):
def setUp( self ):
Test_controller.setUp( self )
self.notebook = Notebook.create( self.database.next_id( Notebook ), u"my notebook" )
self.notebook = Notebook.create( self.database.next_id( Notebook ), u"my notebook", trash_id = u"foo" )
self.database.save( self.notebook )
self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"Luminotes" )
@ -20,6 +20,12 @@ class Test_root( Test_controller ):
)
self.database.save( self.anon_note )
self.login_note = Note.create(
self.database.next_id( Note ), u"<h3>login</h3>",
notebook_id = self.anon_notebook.object_id,
)
self.database.save( self.login_note )
self.blog_notebook = Notebook.create( self.database.next_id( Notebook ), u"Luminotes blog" )
self.database.save( self.blog_notebook )
self.blog_note = Note.create(
@ -65,9 +71,17 @@ class Test_root( Test_controller ):
result = self.http_get( "/" )
assert result
assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
assert result.get( u"redirect" ) is None
assert result[ u"user" ].username == u"anonymous"
assert len( result[ u"notebooks" ] ) == 4
assert result[ u"first_notebook" ] == None
assert result[ u"login_url" ] == u"https://luminotes.com/notebooks/%s?note_id=%s" % (
self.anon_notebook.object_id, self.login_note.object_id,
)
assert result[ u"logout_url" ] == u"https://luminotes.com/users/logout"
assert result[ u"rate_plan" ]
def test_index_after_login( self ):
def test_index_after_login_without_referer( self ):
self.login()
result = self.http_get(
@ -75,10 +89,22 @@ class Test_root( Test_controller ):
session_id = self.session_id,
)
assert result.get( u"redirect" )
assert result.get( u"redirect" ).startswith( self.settings[ u"global" ][ u"luminotes.https_url" ] )
assert result
assert result.get( u"redirect" ) == u"https://luminotes.com/notebooks/%s" % self.notebook.object_id
def test_index_with_https_after_login( self ):
def test_index_after_login_with_referer( self ):
self.login()
result = self.http_get(
"/",
headers = [ ( u"Referer", "http://whee" ) ],
session_id = self.session_id,
)
assert result
assert result.get( u"redirect" ) == u"https://luminotes.com/"
def test_index_with_https_after_login_without_referer( self ):
self.login()
result = self.http_get(
@ -87,9 +113,27 @@ class Test_root( Test_controller ):
pretend_https = True,
)
assert result
assert result.get( u"redirect" ) == u"https://luminotes.com/notebooks/%s" % self.notebook.object_id
def test_index_with_https_after_login_with_referer( self ):
self.login()
result = self.http_get(
"/",
session_id = self.session_id,
headers = [ ( u"Referer", "http://whee" ) ],
pretend_https = True,
)
assert result
assert result.get( u"redirect" ) is None
assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
assert result[ u"user" ].username == self.user.username
assert len( result[ u"notebooks" ] ) == 5
assert result[ u"first_notebook" ].object_id == self.notebook.object_id
assert result[ u"login_url" ] == None
assert result[ u"logout_url" ] == u"https://luminotes.com/users/logout"
assert result[ u"rate_plan" ]
def test_default( self ):
result = self.http_get(

View File

@ -161,7 +161,7 @@ class Test_users( Test_controller ):
assert notebook.owner == False
assert result.get( u"login_url" ) is None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan[ u"name" ] == u"super"
@ -238,7 +238,7 @@ class Test_users( Test_controller ):
assert notebook.owner == False
assert result.get( u"login_url" ) is None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan[ u"name" ] == u"super"
@ -301,7 +301,7 @@ class Test_users( Test_controller ):
assert notebook.owner == False
assert result.get( u"login_url" ) is None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan[ u"name" ] == u"super"
@ -396,7 +396,7 @@ class Test_users( Test_controller ):
assert result[ u"notebooks" ][ 4 ].read_write == False
assert result[ u"notebooks" ][ 4 ].owner == False
assert result[ u"login_url" ] is None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan
@ -419,7 +419,7 @@ class Test_users( Test_controller ):
self.anon_notebook.object_id,
login_note.object_id,
)
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan
@ -608,7 +608,7 @@ class Test_users( Test_controller ):
self.anon_notebook.object_id,
login_note.object_id,
)
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan
@ -3247,7 +3247,7 @@ class Test_users( Test_controller ):
assert result[ u"notebooks" ][ 0 ].owner == True
assert result[ u"login_url" ] == None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan
@ -3286,7 +3286,7 @@ class Test_users( Test_controller ):
assert result[ u"notebooks" ][ 0 ].owner == True
assert result[ u"login_url" ] == None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan
@ -3324,7 +3324,7 @@ class Test_users( Test_controller ):
assert result[ u"notebooks" ][ 0 ].owner == True
assert result[ u"login_url" ] == None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan
@ -3362,7 +3362,7 @@ class Test_users( Test_controller ):
assert result[ u"notebooks" ][ 0 ].owner == True
assert result[ u"login_url" ] == None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan
@ -3398,7 +3398,7 @@ class Test_users( Test_controller ):
assert result[ u"notebooks" ][ 0 ].owner == True
assert result[ u"login_url" ] == None
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
rate_plan = result[ u"rate_plan" ]
assert rate_plan

135
static/css/product.css Normal file
View File

@ -0,0 +1,135 @@
.header {
background-color: #b0d0ff;
height: 60px;
margin-left: 0;
margin-right: 0;
border-bottom: 1px solid #999999;
}
.luminotes_title {
float: left;
}
.header_links {
text-align: right;
font-size: 85%;
padding-top: 5px;
}
.header_user_links {
text-align: right;
font-size: 85%;
padding-top: 10px;
}
.hook_area {
padding-top: 1.5em;
padding-bottom: 1.5em;
width: 100%;
background-color: #ffffff;
border-bottom: 1px solid #cccccc;
}
.wide_center_area {
width: 840px;
height: 100%;
margin: 0 auto;
}
.center_area {
width: 30em;
height: 100%;
margin: 0 auto;
}
.explanation {
width: 400px;
min-height: 300px;
text-align: center;
margin-right: 20px;
}
.hook_bullet_list {
list-style-type: none;
}
.hook_action_area {
background-color: #ffff99;
font-weight: bold;
padding: 1em;
}
.hook_action {
font-size: 105%;
}
.hook_action_or {
font-size: 85%;
}
.screenshot {
float: right;
width: 400px;
height: 300px;
text-align: center;
margin-left: 20px;
}
.heading {
padding: 0.5em;
margin-top: 0.5em;
text-align: center;
}
.bold_link {
font-weight: bold;
}
.what_is_luminotes_area {
float: right;
width: 400px;
margin-left: 20px;
}
.what_is_luminotes_text {
text-align: left;
}
.quotes_area {
width: 400px;
}
.quote {
text-align: left;
margin-bottom: 2em;
}
.quote_title {
font-size: 120%;
margin-bottom: 0em;
}
.quote_text {
margin-top: 0.5em;
margin-bottom: 0.5em;
}
.quote_signature {
font-size: 85%;
font-style: italic;
margin-top: 0em;
color: #444444;
}
.footer {
background-color: #b0d0ff;
height: 2em;
margin-left: 0;
margin-right: 0;
border-top: 1px solid #999999;
}
.footer_links {
font-size: 85%;
padding-top: 0.5em;
}

BIN
static/images/hook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

BIN
static/images/hook.xcf Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.2 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
static/images/quotes.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

BIN
static/images/quotes.xcf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
static/images/sub_hook.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

BIN
static/images/sub_hook.xcf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

View File

@ -76,11 +76,12 @@ function Wiki( invoker ) {
}
var self = this;
var top_window = window;
var logout_link = getElement( "logout_link" );
if ( logout_link ) {
connect( "logout_link", "onclick", function ( event ) {
self.save_editor( null, false, function () {
self.invoker.invoke( "/users/logout", "POST" );
top_window.location = "/users/logout";
} );
event.stop();
} );

View File

@ -5,7 +5,7 @@ from Tags import Div, H2, P, A, Ul, Li, Strong, Noscript, Img
class Error_page( Page ):
def __init__( self, support_email, message = None ):
header_image = Div(
A( Img( src = u"/static/images/luminotes_title.png", width = u"206", height = u"69" ), href = u"/", title = u"Luminotes personal wiki notebook" ),
A( Img( src = u"/static/images/luminotes_title_full.png", width = u"206", height = u"69" ), href = u"/", alt = u"Luminotes personal wiki notebook" ),
class_ = u"error_header",
)
@ -20,7 +20,6 @@ class Error_page( Page ):
P( message ),
class_ = u"error_box",
),
include_js = False,
)
return
@ -57,5 +56,4 @@ class Error_page( Page ):
),
class_ = u"error_box",
),
include_js = False,
)

View File

@ -91,6 +91,10 @@ class Main_page( Page ):
Page.__init__(
self,
title,
Script( type = u"text/javascript", src = u"/static/js/MochiKit.js" ) or None,
Script( type = u"text/javascript", src = u"/static/js/Invoker.js" ) or None,
Script( type = u"text/javascript", src = u"/static/js/Editor.js" ) or None,
Script( type = u"text/javascript", src = u"/static/js/Wiki.js" ) or None,
Input( type = u"hidden", name = u"storage_bytes", id = u"storage_bytes", value = user.storage_bytes ),
Input( type = u"hidden", name = u"rate_plan", id = u"rate_plan", value = json( rate_plan ) ),
Input( type = u"hidden", name = u"notebooks", id = u"notebooks", value = json( notebooks ) ),
@ -125,7 +129,7 @@ class Main_page( Page ):
id = u"search_and_user_area",
),
Div(
A( Img( src = u"/static/images/luminotes_title.png", width = u"206", height = u"69" ), href = u"/", title = u"Luminotes personal wiki notebook" ),
A( Img( src = u"/static/images/luminotes_title_full.png", width = u"206", height = u"69" ), href = u"/", title = u"Luminotes personal wiki notebook" ),
id = u"title_area",
),
id = u"top_area",

View File

@ -1,14 +1,19 @@
from Page import Page
from Tags import Div, H2, P, A
from Tags import Div, H2, P, A, Img
class Not_found_page( Page ):
def __init__( self, support_email ):
title = u"404"
header_image = Div(
A( Img( src = u"/static/images/luminotes_title_full.png", width = u"206", height = u"69" ), href = u"/", alt = u"Luminotes personal wiki notebook" ),
class_ = u"error_header",
)
Page.__init__(
self,
title,
header_image,
Div(
H2( title ),
P(
@ -20,5 +25,4 @@ class Not_found_page( Page ):
),
class_ = u"error_box",
),
include_js = False,
)

View File

@ -10,22 +10,12 @@ class Page( Html ):
if "id" not in attrs:
attrs[ "id" ] = u"content"
if "include_js" in attrs:
include_js = attrs[ "include_js" ]
del attrs[ "include_js" ]
else:
include_js = True
# move certain types of children from the body to the head
Html.__init__(
self,
Head(
Link( rel = u"stylesheet", type = u"text/css", href = u"/static/css/style.css" ),
include_js and Script( type = u"text/javascript", src = u"/static/js/MochiKit.js" ) or None,
include_js and Script( type = u"text/javascript", src = u"/static/js/Invoker.js" ) or None,
include_js and Script( type = u"text/javascript", src = u"/static/js/Editor.js" ) or None,
include_js and Script( type = u"text/javascript", src = u"/static/js/Wiki.js" ) or None,
include_js and Script( type = u"text/javascript", src = u"https://ssl.google-analytics.com/urchin.js" ) or None,
Script( type = u"text/javascript", src = u"https://ssl.google-analytics.com/urchin.js" ) or None,
Meta( content = u"text/html; charset=UTF-8", http_equiv = u"content-type" ),
[ child for child in children if type( child ) in head_types ],
Title( title and u"%s: %s" % ( app_name, title ) or app_name ),

248
view/Product_page.py Normal file
View File

@ -0,0 +1,248 @@
from Page import Page
from Tags import Link, Div, Img, A, P, Table, Td, Li, Span, I
class Product_page( Page ):
def __init__( self, user, notebooks, first_notebook, login_url, logout_url, rate_plan ):
Page.__init__(
self,
None, # use the default title
Link( rel = u"stylesheet", type = u"text/css", href = u"/static/css/product.css" ),
Div(
Div(
Img(
src ="/static/images/luminotes_title.png",
class_ = u"luminotes_title", width = u"193", height = u"60",
alt = u"Luminotes",
),
( login_url and user.username == u"anonymous" ) and Div(
A( u"sign up", href = u"/sign_up", class_ = u"bold_link" ), u" | ",
A(
u"login",
href = login_url,
id = u"login_link",
class_ = u"bold_link",
),
class_ = u"header_user_links",
) or Div(
u"logged in as %s" % ( user.username or u"a guest" ),
u" | ",
first_notebook and Span(
A(
u"my wiki",
href = u"/notebooks/%s" % first_notebook.object_id,
),
u" | ",
) or None,
user.username and Span(
A(
u"upgrade",
href = u"/upgrade",
title = u"Upgrade your Luminotes account.",
class_ = u"bold_link",
),
" | ",
) or Span(
A(
u"sign up",
href = u"/sign_up",
title = u"Sign up for a real Luminotes account.",
),
" | ",
) or None,
A(
u"logout",
href = logout_url,
id = u"logout_link",
title = u"Sign out of your account.",
),
class_ = u"header_user_links",
),
Div(
Span( u"home", class_ = u"bold_link" ), u" | ",
A( u"tour", href = u"/take_a_tour" ), u" | ",
A( u"demo", href = u"/users/demo" ), u" | ",
A( u"pricing", href = u"/upgrade" ), u" | ",
A( u"faq", href = u"/faq" ), u" | ",
A( u"help", href = u"/guide" ), u" | ",
A( u"contact", href = u"/contact_info" ), u" | ",
A( u"team", href = u"/meet_the_team" ), u" | ",
A( u"blog", href = u"/blog" ), u" | ",
A( u"privacy", href = u"/privacy" ),
class_ = u"header_links",
),
class_ = u"wide_center_area",
),
class_ = u"header",
),
Div(
Div(
Div(
A(
Img( src = u"/static/images/screenshot_small.png", width = u"400", height = u"291" ),
href = u"/take_a_tour",
),
class_ = u"screenshot",
),
Div(
Div(
Img(
src = u"/static/images/hook.png",
width = u"400", height = u"51",
alt = u"Collect your thoughts.",
),
),
P(
Img(
src = u"/static/images/sub_hook.png",
width = u"307", height = u"54",
alt = u"Get organized with your own Luminotes personal wiki notebook.",
),
),
Table(
Td(
Li( u"Gather all of your ideas into one place." ),
Li( u"Easily link together related concepts." ),
Li( u"Share your wiki with anyone." ),
align = u"left",
),
align = u"center",
),
P(
A( u"Take a tour", href = u"/take_a_tour", class_ = u"hook_action" ), u", ",
A( u"Try the demo", href = u"/users/demo", class_ = u"hook_action" ), u", ",
Span( u" or ", class_ = u"hook_action_or" ),
A( u"Sign up", href = u"/sign_up", class_ = u"hook_action" ),
class_ = u"hook_action_area",
separator = u"",
),
class_ = u"explanation",
),
class_ = u"wide_center_area",
),
class_ = u"hook_area",
),
Div(
Div(
Img(
src = u"/static/images/what_is_luminotes.png",
class_ = u"heading", width = u"214", height = u"29",
alt = u"What is Luminotes?",
),
Div(
P(
u"""
Luminotes is a personal wiki notebook for organizing your notes and ideas.
You don't have to use any special markup codes or install any software. You
simply start typing.
""",
),
P(
u"""
With Luminotes, you deal with several notes all at once on the same web page,
so you get a big-picture view of what you're working on and can easily make
links from one concept to another.
""",
A( u"Read more.", href = u"/take_a_tour" ),
),
P(
u"""
Luminotes is open source / free software and licensed under the terms of the
GNU GPL.
""",
),
class_ = u"what_is_luminotes_text",
),
class_ = u"what_is_luminotes_area",
),
Div(
P(
Img(
src = u"/static/images/quotes.png",
class_ = u"heading", width = u"253", height = u"31",
alt = u"What people are saying",
),
),
Div(
Div(
u'"',
Span(
u"What I love most about Luminotes is the ", I( u"simplicity" ), u" of it.",
class_ = u"quote_title",
separator = u"",
),
u"""
Maybe I have a touch of ADD, but I get so distracted with other products and
all the gadgets, bells, and whistles they offer. I spend more time fiddling
with the features than actually working. Luminotes, for me, recreates the old
index card method we all used for term papers in high school."
""",
class_ = u"quote_text",
separator = u"",
),
Div(
u"-Michael Miller, President &amp; CEO, Mighty Hero Entertainment, Inc.",
class_ = u"quote_signature"
),
class_ = u"quote",
),
Div(
Div(
u'"',
Span(
u"I just wanted to thank you for the great work with Luminotes!",
class_ = u"quote_title",
),
u"""
I use it both at home and at work, and it's a big help!"
""",
class_ = u"quote_text",
separator = u"",
),
Div(
u"-Brian M.B. Keaney",
class_ = u"quote_signature",
),
class_ = u"quote",
),
class_ = u"quotes_area",
),
class_ = u"wide_center_area",
),
Div(
P(
A( u"Take a tour", href = u"/take_a_tour", class_ = u"hook_action" ), u", ",
A( u"Try the demo", href = u"/users/demo", class_ = u"hook_action" ), u", ",
Span( u" or ", class_ = u"hook_action_or" ),
A( u"Sign up", href = u"/sign_up", class_ = u"hook_action" ),
class_ = u"hook_action_area",
separator = u"",
),
class_ = u"center_area",
),
P(),
Div(
Div(
Div(
u"Copyright &copy;2008 Luminotes", u" | ",
A( u"download", href = u"/download" ), u" | ",
A( u"contact", href = u"/contact_info" ), u" | ",
A( u"team", href = u"/meet_the_team" ), u" | ",
A( u"blog", href = u"/blog" ), u" | ",
A( u"privacy", href = u"/privacy" ),
class_ = u"footer_links",
),
class_ = u"wide_center_area",
),
class_ = u"footer",
),
)