Browse Source

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.
Dan Helfman 10 years ago
parent
commit
3af5af18c5

+ 2
- 2
NEWS View File

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

+ 8
- 5
controller/Root.py View File

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

+ 3
- 3
controller/Users.py View File

@@ -402,13 +402,13 @@ class Users( object ):
402 402
       authenticated = user,
403 403
     )
404 404
 
405
-  @expose( view = Json )
405
+  @expose()
406 406
   @update_auth
407 407
   def logout( self ):
408 408
     """
409 409
     Deauthenticate the user and log them out of their current session.
410 410
 
411
-    @rtype: json dict
411
+    @rtype: dict
412 412
     @return: { 'redirect': url, 'deauthenticated': True }
413 413
     """
414 414
     return dict(
@@ -463,7 +463,7 @@ class Users( object ):
463 463
       user = user,
464 464
       notebooks = notebooks + anon_notebooks,
465 465
       login_url = login_url,
466
-      logout_url = self.__https_url + u"/",
466
+      logout_url = self.__https_url + u"/users/logout",
467 467
       rate_plan = ( user.rate_plan < len( self.__rate_plans ) ) and self.__rate_plans[ user.rate_plan ] or {},
468 468
     )
469 469
 

+ 51
- 7
controller/test/Test_root.py View File

@@ -9,7 +9,7 @@ class Test_root( Test_controller ):
9 9
   def setUp( self ):
10 10
     Test_controller.setUp( self )
11 11
 
12
-    self.notebook = Notebook.create( self.database.next_id( Notebook ), u"my notebook" )
12
+    self.notebook = Notebook.create( self.database.next_id( Notebook ), u"my notebook", trash_id = u"foo" )
13 13
     self.database.save( self.notebook )
14 14
 
15 15
     self.anon_notebook = Notebook.create( self.database.next_id( Notebook ), u"Luminotes" )
@@ -20,6 +20,12 @@ class Test_root( Test_controller ):
20 20
     )
21 21
     self.database.save( self.anon_note )
22 22
 
23
+    self.login_note = Note.create(
24
+      self.database.next_id( Note ), u"<h3>login</h3>",
25
+      notebook_id = self.anon_notebook.object_id,
26
+    )
27
+    self.database.save( self.login_note )
28
+
23 29
     self.blog_notebook = Notebook.create( self.database.next_id( Notebook ), u"Luminotes blog" )
24 30
     self.database.save( self.blog_notebook )
25 31
     self.blog_note = Note.create(
@@ -65,20 +71,40 @@ class Test_root( Test_controller ):
65 71
     result = self.http_get( "/" )
66 72
 
67 73
     assert result
68
-    assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
74
+    assert result.get( u"redirect" ) is None
75
+    assert result[ u"user" ].username == u"anonymous"
76
+    assert len( result[ u"notebooks" ] ) == 4
77
+    assert result[ u"first_notebook" ] == None
78
+    assert result[ u"login_url" ] == u"https://luminotes.com/notebooks/%s?note_id=%s" % (
79
+      self.anon_notebook.object_id, self.login_note.object_id,
80
+    )
81
+    assert result[ u"logout_url" ] == u"https://luminotes.com/users/logout"
82
+    assert result[ u"rate_plan" ]
83
+
84
+  def test_index_after_login_without_referer( self ):
85
+    self.login()
86
+
87
+    result = self.http_get(
88
+      "/",
89
+      session_id = self.session_id,
90
+    )
69 91
 
70
-  def test_index_after_login( self ):
92
+    assert result
93
+    assert result.get( u"redirect" ) == u"https://luminotes.com/notebooks/%s" % self.notebook.object_id
94
+
95
+  def test_index_after_login_with_referer( self ):
71 96
     self.login()
72 97
 
73 98
     result = self.http_get(
74 99
       "/",
100
+      headers = [ ( u"Referer", "http://whee" ) ],
75 101
       session_id = self.session_id,
76 102
     )
77 103
 
78
-    assert result.get( u"redirect" )
79
-    assert result.get( u"redirect" ).startswith( self.settings[ u"global" ][ u"luminotes.https_url" ] )
104
+    assert result
105
+    assert result.get( u"redirect" ) == u"https://luminotes.com/"
80 106
 
81
-  def test_index_with_https_after_login( self ):
107
+  def test_index_with_https_after_login_without_referer( self ):
82 108
     self.login()
83 109
 
84 110
     result = self.http_get(
@@ -87,9 +113,27 @@ class Test_root( Test_controller ):
87 113
       pretend_https = True,
88 114
     )
89 115
 
116
+    assert result
117
+    assert result.get( u"redirect" ) == u"https://luminotes.com/notebooks/%s" % self.notebook.object_id
118
+
119
+  def test_index_with_https_after_login_with_referer( self ):
120
+    self.login()
121
+
122
+    result = self.http_get(
123
+      "/",
124
+      session_id = self.session_id,
125
+      headers = [ ( u"Referer", "http://whee" ) ],
126
+      pretend_https = True,
127
+    )
128
+
90 129
     assert result
91 130
     assert result.get( u"redirect" ) is None
92
-    assert result[ u"notebook" ].object_id == self.anon_notebook.object_id
131
+    assert result[ u"user" ].username == self.user.username
132
+    assert len( result[ u"notebooks" ] ) == 5
133
+    assert result[ u"first_notebook" ].object_id == self.notebook.object_id
134
+    assert result[ u"login_url" ] == None
135
+    assert result[ u"logout_url" ] == u"https://luminotes.com/users/logout"
136
+    assert result[ u"rate_plan" ]
93 137
 
94 138
   def test_default( self ):
95 139
     result = self.http_get(

+ 11
- 11
controller/test/Test_users.py View File

@@ -161,7 +161,7 @@ class Test_users( Test_controller ):
161 161
     assert notebook.owner == False
162 162
 
163 163
     assert result.get( u"login_url" ) is None
164
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
164
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
165 165
 
166 166
     rate_plan = result[ u"rate_plan" ]
167 167
     assert rate_plan[ u"name" ] == u"super"
@@ -238,7 +238,7 @@ class Test_users( Test_controller ):
238 238
     assert notebook.owner == False
239 239
 
240 240
     assert result.get( u"login_url" ) is None
241
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
241
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
242 242
 
243 243
     rate_plan = result[ u"rate_plan" ]
244 244
     assert rate_plan[ u"name" ] == u"super"
@@ -301,7 +301,7 @@ class Test_users( Test_controller ):
301 301
     assert notebook.owner == False
302 302
 
303 303
     assert result.get( u"login_url" ) is None
304
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
304
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
305 305
 
306 306
     rate_plan = result[ u"rate_plan" ]
307 307
     assert rate_plan[ u"name" ] == u"super"
@@ -396,7 +396,7 @@ class Test_users( Test_controller ):
396 396
     assert result[ u"notebooks" ][ 4 ].read_write == False
397 397
     assert result[ u"notebooks" ][ 4 ].owner == False
398 398
     assert result[ u"login_url" ] is None
399
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
399
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
400 400
 
401 401
     rate_plan = result[ u"rate_plan" ]
402 402
     assert rate_plan
@@ -419,7 +419,7 @@ class Test_users( Test_controller ):
419 419
       self.anon_notebook.object_id,
420 420
       login_note.object_id,
421 421
     )
422
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
422
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
423 423
 
424 424
     rate_plan = result[ u"rate_plan" ]
425 425
     assert rate_plan
@@ -608,7 +608,7 @@ class Test_users( Test_controller ):
608 608
       self.anon_notebook.object_id,
609 609
       login_note.object_id,
610 610
     )
611
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
611
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
612 612
 
613 613
     rate_plan = result[ u"rate_plan" ]
614 614
     assert rate_plan
@@ -3247,7 +3247,7 @@ class Test_users( Test_controller ):
3247 3247
     assert result[ u"notebooks" ][ 0 ].owner == True
3248 3248
 
3249 3249
     assert result[ u"login_url" ] == None
3250
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
3250
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
3251 3251
 
3252 3252
     rate_plan = result[ u"rate_plan" ]
3253 3253
     assert rate_plan
@@ -3286,7 +3286,7 @@ class Test_users( Test_controller ):
3286 3286
     assert result[ u"notebooks" ][ 0 ].owner == True
3287 3287
 
3288 3288
     assert result[ u"login_url" ] == None
3289
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
3289
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
3290 3290
 
3291 3291
     rate_plan = result[ u"rate_plan" ]
3292 3292
     assert rate_plan
@@ -3324,7 +3324,7 @@ class Test_users( Test_controller ):
3324 3324
     assert result[ u"notebooks" ][ 0 ].owner == True
3325 3325
 
3326 3326
     assert result[ u"login_url" ] == None
3327
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
3327
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
3328 3328
 
3329 3329
     rate_plan = result[ u"rate_plan" ]
3330 3330
     assert rate_plan
@@ -3362,7 +3362,7 @@ class Test_users( Test_controller ):
3362 3362
     assert result[ u"notebooks" ][ 0 ].owner == True
3363 3363
 
3364 3364
     assert result[ u"login_url" ] == None
3365
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
3365
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
3366 3366
 
3367 3367
     rate_plan = result[ u"rate_plan" ]
3368 3368
     assert rate_plan
@@ -3398,7 +3398,7 @@ class Test_users( Test_controller ):
3398 3398
     assert result[ u"notebooks" ][ 0 ].owner == True
3399 3399
 
3400 3400
     assert result[ u"login_url" ] == None
3401
-    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/"
3401
+    assert result[ u"logout_url" ] == self.settings[ u"global" ][ u"luminotes.https_url" ] + u"/users/logout"
3402 3402
 
3403 3403
     rate_plan = result[ u"rate_plan" ]
3404 3404
     assert rate_plan

+ 135
- 0
static/css/product.css View File

@@ -0,0 +1,135 @@
1
+.header {
2
+  background-color: #b0d0ff;
3
+  height: 60px;
4
+  margin-left: 0;
5
+  margin-right: 0;
6
+  border-bottom: 1px solid #999999;
7
+}
8
+
9
+.luminotes_title {
10
+  float: left;
11
+}
12
+
13
+.header_links {
14
+  text-align: right;
15
+  font-size: 85%;
16
+  padding-top: 5px;
17
+}
18
+
19
+.header_user_links {
20
+  text-align: right;
21
+  font-size: 85%;
22
+  padding-top: 10px;
23
+}
24
+
25
+.hook_area {
26
+  padding-top: 1.5em;
27
+  padding-bottom: 1.5em;
28
+  width: 100%;
29
+  background-color: #ffffff;
30
+  border-bottom: 1px solid #cccccc;
31
+}
32
+
33
+.wide_center_area {
34
+  width: 840px;
35
+  height: 100%;
36
+  margin: 0 auto;
37
+}
38
+
39
+.center_area {
40
+  width: 30em;
41
+  height: 100%;
42
+  margin: 0 auto;
43
+}
44
+
45
+.explanation {
46
+  width: 400px;
47
+  min-height: 300px;
48
+  text-align: center;
49
+  margin-right: 20px;
50
+}
51
+
52
+.hook_bullet_list {
53
+  list-style-type: none;
54
+}
55
+
56
+.hook_action_area {
57
+  background-color: #ffff99;
58
+  font-weight: bold;
59
+  padding: 1em;
60
+}
61
+
62
+.hook_action {
63
+  font-size: 105%;
64
+}
65
+
66
+.hook_action_or {
67
+  font-size: 85%;
68
+}
69
+
70
+.screenshot {
71
+  float: right;
72
+  width: 400px;
73
+  height: 300px;
74
+  text-align: center;
75
+  margin-left: 20px;
76
+}
77
+
78
+.heading {
79
+  padding: 0.5em;
80
+  margin-top: 0.5em;
81
+  text-align: center;
82
+}
83
+
84
+.bold_link {
85
+  font-weight: bold;
86
+}
87
+
88
+.what_is_luminotes_area {
89
+  float: right;
90
+  width: 400px;
91
+  margin-left: 20px;
92
+}
93
+
94
+.what_is_luminotes_text {
95
+  text-align: left;
96
+}
97
+
98
+.quotes_area {
99
+  width: 400px;
100
+}
101
+
102
+.quote {
103
+  text-align: left; 
104
+  margin-bottom: 2em;
105
+}
106
+
107
+.quote_title {
108
+  font-size: 120%;
109
+  margin-bottom: 0em;
110
+}
111
+
112
+.quote_text {
113
+  margin-top: 0.5em;
114
+  margin-bottom: 0.5em;
115
+}
116
+
117
+.quote_signature {
118
+  font-size: 85%;
119
+  font-style: italic;
120
+  margin-top: 0em;
121
+  color: #444444;
122
+}
123
+
124
+.footer {
125
+  background-color: #b0d0ff;
126
+  height: 2em;
127
+  margin-left: 0;
128
+  margin-right: 0;
129
+  border-top: 1px solid #999999;
130
+}
131
+
132
+.footer_links {
133
+  font-size: 85%;
134
+  padding-top: 0.5em;
135
+}

BIN
static/images/hook.png View File


BIN
static/images/hook.xcf View File


BIN
static/images/luminotes_title.png View File


BIN
static/images/luminotes_title.xcf View File


BIN
static/images/luminotes_title_full.png View File


BIN
static/images/quotes.png View File


BIN
static/images/quotes.xcf View File


BIN
static/images/screenshot_small.png View File


BIN
static/images/sub_hook.png View File


BIN
static/images/sub_hook.xcf View File


BIN
static/images/what_is_luminotes.png View File


BIN
static/images/what_is_luminotes.xcf View File


+ 2
- 1
static/js/Wiki.js View File

@@ -76,11 +76,12 @@ function Wiki( invoker ) {
76 76
   }
77 77
 
78 78
   var self = this;
79
+  var top_window = window;
79 80
   var logout_link = getElement( "logout_link" );
80 81
   if ( logout_link ) {
81 82
     connect( "logout_link", "onclick", function ( event ) {
82 83
       self.save_editor( null, false, function () {
83
-        self.invoker.invoke( "/users/logout", "POST" );
84
+        top_window.location = "/users/logout";
84 85
       } );
85 86
       event.stop();
86 87
     } );

+ 1
- 3
view/Error_page.py View File

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

+ 5
- 1
view/Main_page.py View File

@@ -91,6 +91,10 @@ class Main_page( Page ):
91 91
     Page.__init__(
92 92
       self,
93 93
       title,
94
+      Script( type = u"text/javascript", src = u"/static/js/MochiKit.js" ) or None,
95
+      Script( type = u"text/javascript", src = u"/static/js/Invoker.js" ) or None,
96
+      Script( type = u"text/javascript", src = u"/static/js/Editor.js" ) or None,
97
+      Script( type = u"text/javascript", src = u"/static/js/Wiki.js" ) or None,
94 98
       Input( type = u"hidden", name = u"storage_bytes", id = u"storage_bytes", value = user.storage_bytes ),
95 99
       Input( type = u"hidden", name = u"rate_plan", id = u"rate_plan", value = json( rate_plan ) ),
96 100
       Input( type = u"hidden", name = u"notebooks", id = u"notebooks", value = json( notebooks ) ),
@@ -125,7 +129,7 @@ class Main_page( Page ):
125 129
               id = u"search_and_user_area",
126 130
             ),
127 131
             Div(
128
-              A( Img( src = u"/static/images/luminotes_title.png", width = u"206", height = u"69" ), href = u"/", title = u"Luminotes personal wiki notebook" ),
132
+              A( Img( src = u"/static/images/luminotes_title_full.png", width = u"206", height = u"69" ), href = u"/", title = u"Luminotes personal wiki notebook" ),
129 133
               id = u"title_area",
130 134
             ),
131 135
             id = u"top_area",

+ 6
- 2
view/Not_found_page.py View File

@@ -1,14 +1,19 @@
1 1
 from Page import Page
2
-from Tags import Div, H2, P, A
2
+from Tags import Div, H2, P, A, Img
3 3
 
4 4
 
5 5
 class Not_found_page( Page ):
6 6
   def __init__( self, support_email ):
7 7
     title = u"404"
8
+    header_image = Div(
9
+      A( Img( src = u"/static/images/luminotes_title_full.png", width = u"206", height = u"69" ), href = u"/", alt = u"Luminotes personal wiki notebook" ),
10
+      class_ = u"error_header",
11
+    )
8 12
 
9 13
     Page.__init__(
10 14
       self,
11 15
       title,
16
+      header_image,
12 17
       Div(
13 18
         H2( title ),
14 19
         P(
@@ -20,5 +25,4 @@ class Not_found_page( Page ):
20 25
         ),
21 26
         class_ = u"error_box",
22 27
       ),
23
-      include_js = False,
24 28
     )

+ 1
- 11
view/Page.py View File

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

+ 248
- 0
view/Product_page.py View File

@@ -0,0 +1,248 @@
1
+from Page import Page
2
+from Tags import Link, Div, Img, A, P, Table, Td, Li, Span, I
3
+
4
+
5
+class Product_page( Page ):
6
+  def __init__( self, user, notebooks, first_notebook, login_url, logout_url, rate_plan ):
7
+    Page.__init__(
8
+      self,
9
+      None, # use the default title
10
+      Link( rel = u"stylesheet", type = u"text/css", href = u"/static/css/product.css" ),
11
+
12
+      Div(
13
+        Div(
14
+          Img(
15
+            src ="/static/images/luminotes_title.png",
16
+            class_ = u"luminotes_title", width = u"193", height = u"60",
17
+            alt = u"Luminotes",
18
+          ),
19
+          ( login_url and user.username == u"anonymous" ) and Div(
20
+            A( u"sign up", href = u"/sign_up", class_ = u"bold_link" ), u" | ",
21
+            A(
22
+              u"login",
23
+              href = login_url,
24
+              id = u"login_link",
25
+              class_ = u"bold_link",
26
+            ),
27
+            class_ = u"header_user_links",
28
+          ) or Div(
29
+            u"logged in as %s" % ( user.username or u"a guest" ),
30
+            u" | ",
31
+            first_notebook and Span(
32
+              A(
33
+                u"my wiki",
34
+                href = u"/notebooks/%s" % first_notebook.object_id,
35
+              ),
36
+              u" | ",
37
+            ) or None,
38
+            user.username and Span(
39
+              A(
40
+                u"upgrade",
41
+                href = u"/upgrade",
42
+                title = u"Upgrade your Luminotes account.",
43
+                class_ = u"bold_link",
44
+              ),
45
+              " | ",
46
+            ) or Span(
47
+              A(
48
+                u"sign up",
49
+                href = u"/sign_up",
50
+                title = u"Sign up for a real Luminotes account.",
51
+              ),
52
+              " | ",
53
+            ) or None,
54
+            A(
55
+              u"logout",
56
+              href = logout_url,
57
+              id = u"logout_link",
58
+              title = u"Sign out of your account.",
59
+            ),
60
+            class_ = u"header_user_links",
61
+          ),
62
+          Div(
63
+            Span( u"home", class_ = u"bold_link" ), u" | ",
64
+            A( u"tour", href = u"/take_a_tour" ), u" | ",
65
+            A( u"demo", href = u"/users/demo" ), u" | ",
66
+            A( u"pricing", href = u"/upgrade" ), u" | ",
67
+            A( u"faq", href = u"/faq" ), u" | ",
68
+            A( u"help", href = u"/guide" ), u" | ",
69
+            A( u"contact", href = u"/contact_info" ), u" | ",
70
+            A( u"team", href = u"/meet_the_team" ), u" | ",
71
+            A( u"blog", href = u"/blog" ), u" | ",
72
+            A( u"privacy", href = u"/privacy" ),
73
+            class_ = u"header_links",
74
+          ),
75
+          class_ = u"wide_center_area",
76
+        ),
77
+        class_ = u"header",
78
+      ),
79
+
80
+      Div(
81
+        Div(
82
+          Div(
83
+            A(
84
+              Img( src = u"/static/images/screenshot_small.png", width = u"400", height = u"291" ),
85
+              href = u"/take_a_tour",
86
+            ),
87
+            class_ = u"screenshot",
88
+          ),
89
+          Div(
90
+            Div(
91
+              Img(
92
+                src = u"/static/images/hook.png",
93
+                width = u"400", height = u"51",
94
+                alt = u"Collect your thoughts.",
95
+              ),
96
+            ),
97
+            P(
98
+              Img(
99
+                src = u"/static/images/sub_hook.png",
100
+                width = u"307", height = u"54",
101
+                alt = u"Get organized with your own Luminotes personal wiki notebook.",
102
+              ),
103
+            ),
104
+            Table(
105
+              Td(
106
+                Li( u"Gather all of your ideas into one place." ),
107
+                Li( u"Easily link together related concepts." ),
108
+                Li( u"Share your wiki with anyone." ),
109
+                align = u"left",
110
+              ),
111
+              align = u"center",
112
+            ),
113
+            P(
114
+              A( u"Take a tour", href = u"/take_a_tour", class_ = u"hook_action" ), u", ",
115
+              A( u"Try the demo", href = u"/users/demo", class_ = u"hook_action" ), u", ",
116
+              Span( u" or ", class_ = u"hook_action_or" ),
117
+              A( u"Sign up", href = u"/sign_up", class_ = u"hook_action"  ),
118
+              class_ = u"hook_action_area",
119
+              separator = u"",
120
+            ),
121
+            class_ = u"explanation",
122
+          ),
123
+          class_ = u"wide_center_area",
124
+        ),
125
+        class_ = u"hook_area",
126
+      ),
127
+
128
+      Div(
129
+        Div(
130
+          Img(
131
+            src = u"/static/images/what_is_luminotes.png",
132
+            class_ = u"heading", width = u"214", height = u"29",
133
+            alt = u"What is Luminotes?",
134
+          ),
135
+          Div(
136
+            P(
137
+              u"""
138
+              Luminotes is a personal wiki notebook for organizing your notes and ideas.
139
+              You don't have to use any special markup codes or install any software. You
140
+              simply start typing.
141
+              """,
142
+            ),
143
+            P(
144
+              u"""
145
+              With Luminotes, you deal with several notes all at once on the same web page,
146
+              so you get a big-picture view of what you're working on and can easily make
147
+              links from one concept to another.
148
+              """,
149
+              A( u"Read more.", href = u"/take_a_tour" ),
150
+            ),
151
+            P(
152
+              u"""
153
+              Luminotes is open source / free software and licensed under the terms of the
154
+              GNU GPL.
155
+              """,
156
+            ),
157
+            class_ = u"what_is_luminotes_text",
158
+          ),
159
+          class_ = u"what_is_luminotes_area",
160
+        ),
161
+
162
+        Div(
163
+          P(
164
+            Img(
165
+              src = u"/static/images/quotes.png",
166
+              class_ = u"heading", width = u"253", height = u"31",
167
+              alt = u"What people are saying",
168
+            ),
169
+          ),
170
+
171
+          Div(
172
+            Div(
173
+              u'"',
174
+              Span(
175
+                u"What I love most about Luminotes is the ", I( u"simplicity" ), u" of it.",
176
+                class_ = u"quote_title",
177
+                separator = u"",
178
+              ),
179
+              u"""
180
+              Maybe I have a touch of ADD, but I get so distracted with other products and
181
+              all the gadgets, bells, and whistles they offer. I spend more time fiddling
182
+              with the features than actually working. Luminotes, for me, recreates the old
183
+              index card method we all used for term papers in high school."
184
+              """,
185
+              class_ = u"quote_text",
186
+              separator = u"",
187
+            ),
188
+            Div(
189
+              u"-Michael Miller, President &amp; CEO, Mighty Hero Entertainment, Inc.",
190
+              class_ = u"quote_signature"
191
+            ),
192
+            class_ = u"quote",
193
+          ),
194
+
195
+          Div(
196
+            Div(
197
+              u'"',
198
+              Span(
199
+                u"I just wanted to thank you for the great work with Luminotes!",
200
+                class_ = u"quote_title",
201
+              ),
202
+              u"""
203
+              I use it both at home and at work, and it's a big help!"
204
+              """,
205
+              class_ = u"quote_text",
206
+              separator = u"",
207
+            ),
208
+            Div(
209
+              u"-Brian M.B. Keaney",
210
+              class_ = u"quote_signature",
211
+            ),
212
+            class_ = u"quote",
213
+          ),
214
+
215
+          class_ = u"quotes_area",
216
+        ),
217
+        class_ = u"wide_center_area",
218
+      ),
219
+
220
+      Div(
221
+        P(
222
+          A( u"Take a tour", href = u"/take_a_tour", class_ = u"hook_action" ), u", ",
223
+          A( u"Try the demo", href = u"/users/demo", class_ = u"hook_action" ), u", ",
224
+          Span( u" or ", class_ = u"hook_action_or" ),
225
+          A( u"Sign up", href = u"/sign_up", class_ = u"hook_action"  ),
226
+          class_ = u"hook_action_area",
227
+          separator = u"",
228
+        ),
229
+        class_ = u"center_area",
230
+      ),
231
+      P(),
232
+
233
+      Div(
234
+        Div(
235
+          Div(
236
+            u"Copyright &copy;2008 Luminotes", u" | ",
237
+            A( u"download", href = u"/download" ), u" | ",
238
+            A( u"contact", href = u"/contact_info" ), u" | ",
239
+            A( u"team", href = u"/meet_the_team" ), u" | ",
240
+            A( u"blog", href = u"/blog" ), u" | ",
241
+            A( u"privacy", href = u"/privacy" ),
242
+            class_ = u"footer_links",
243
+          ),
244
+          class_ = u"wide_center_area",
245
+        ),
246
+        class_ = u"footer",
247
+      ),
248
+    )