diff --git a/NEWS b/NEWS index b66cf3a..cb0e597 100644 --- a/NEWS +++ b/NEWS @@ -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. diff --git a/controller/Root.py b/controller/Root.py index b0fcab0..f35dd67 100644 --- a/controller/Root.py +++ b/controller/Root.py @@ -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 diff --git a/controller/Users.py b/controller/Users.py index 20d61be..f555578 100644 --- a/controller/Users.py +++ b/controller/Users.py @@ -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 {}, ) diff --git a/controller/test/Test_root.py b/controller/test/Test_root.py index 37a5f88..dd84789 100644 --- a/controller/test/Test_root.py +++ b/controller/test/Test_root.py @@ -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"

login

", + 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( diff --git a/controller/test/Test_users.py b/controller/test/Test_users.py index e938994..684e80e 100644 --- a/controller/test/Test_users.py +++ b/controller/test/Test_users.py @@ -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 diff --git a/static/css/product.css b/static/css/product.css new file mode 100644 index 0000000..554fd43 --- /dev/null +++ b/static/css/product.css @@ -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; +} diff --git a/static/images/hook.png b/static/images/hook.png new file mode 100644 index 0000000..87f36a3 Binary files /dev/null and b/static/images/hook.png differ diff --git a/static/images/hook.xcf b/static/images/hook.xcf new file mode 100644 index 0000000..4ed4e26 Binary files /dev/null and b/static/images/hook.xcf differ diff --git a/static/images/luminotes_title.png b/static/images/luminotes_title.png index 7c12dbb..8098973 100644 Binary files a/static/images/luminotes_title.png and b/static/images/luminotes_title.png differ diff --git a/static/images/luminotes_title.xcf b/static/images/luminotes_title.xcf index 25888d3..776ff9e 100644 Binary files a/static/images/luminotes_title.xcf and b/static/images/luminotes_title.xcf differ diff --git a/static/images/luminotes_title_full.png b/static/images/luminotes_title_full.png new file mode 100644 index 0000000..7c12dbb Binary files /dev/null and b/static/images/luminotes_title_full.png differ diff --git a/static/images/quotes.png b/static/images/quotes.png new file mode 100644 index 0000000..23f1dbd Binary files /dev/null and b/static/images/quotes.png differ diff --git a/static/images/quotes.xcf b/static/images/quotes.xcf new file mode 100644 index 0000000..e00573e Binary files /dev/null and b/static/images/quotes.xcf differ diff --git a/static/images/screenshot_small.png b/static/images/screenshot_small.png new file mode 100644 index 0000000..ec06bb7 Binary files /dev/null and b/static/images/screenshot_small.png differ diff --git a/static/images/sub_hook.png b/static/images/sub_hook.png new file mode 100644 index 0000000..c562f22 Binary files /dev/null and b/static/images/sub_hook.png differ diff --git a/static/images/sub_hook.xcf b/static/images/sub_hook.xcf new file mode 100644 index 0000000..23cab97 Binary files /dev/null and b/static/images/sub_hook.xcf differ diff --git a/static/images/what_is_luminotes.png b/static/images/what_is_luminotes.png new file mode 100644 index 0000000..b80814e Binary files /dev/null and b/static/images/what_is_luminotes.png differ diff --git a/static/images/what_is_luminotes.xcf b/static/images/what_is_luminotes.xcf new file mode 100644 index 0000000..3c30922 Binary files /dev/null and b/static/images/what_is_luminotes.xcf differ diff --git a/static/js/Wiki.js b/static/js/Wiki.js index c45fb0a..a82780b 100644 --- a/static/js/Wiki.js +++ b/static/js/Wiki.js @@ -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(); } ); diff --git a/view/Error_page.py b/view/Error_page.py index 7145b85..faa0de4 100644 --- a/view/Error_page.py +++ b/view/Error_page.py @@ -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, ) diff --git a/view/Main_page.py b/view/Main_page.py index cc78b13..351c5bf 100644 --- a/view/Main_page.py +++ b/view/Main_page.py @@ -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", diff --git a/view/Not_found_page.py b/view/Not_found_page.py index 894ec4d..c303a25 100644 --- a/view/Not_found_page.py +++ b/view/Not_found_page.py @@ -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, ) diff --git a/view/Page.py b/view/Page.py index 8e89545..a346b74 100644 --- a/view/Page.py +++ b/view/Page.py @@ -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 ), diff --git a/view/Product_page.py b/view/Product_page.py new file mode 100644 index 0000000..b72f99c --- /dev/null +++ b/view/Product_page.py @@ -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 & 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 ©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", + ), + )