witten
/
luminotes
Archived
1
0
Fork 0

* tools/updatedb.py now takes an optional navigation note id parameter, so the titleless navigation note can be updated.

* both tools/initdb.py and tools/updatedb.py set link ids appropriately, rather than just leaving them all as "new"
 * both tools/initdb.py and tools/updatedb.py set certain links to use https if configured as such in config/Common.py
 * tools/initdb.py no longer creates a default "witten" user
 * Added target="_top" to links that should replace the top-level window, and updated client code to ignore links with targets.
 * Removed form_base_url from Editor.js, as https URL is now tacked on by initdb.py/updatedb.py instead.
This commit is contained in:
Dan Helfman 2007-08-02 04:01:11 +00:00
parent a5694d3661
commit b58f5cb1a1
10 changed files with 108 additions and 81 deletions

10
INSTALL
View File

@ -17,7 +17,7 @@ changes, because it uses CherryPy's built-in web server with auto-reload
enabled, so the server will automatically reload any modified source files as enabled, so the server will automatically reload any modified source files as
soon as they're modified. soon as they're modified.
To start the server in development mode: To start the server in development mode, run:
python2.5 luminotes.py -d python2.5 luminotes.py -d
@ -35,10 +35,10 @@ mode doesn't support auto-reload, and logging goes to file (luminotes.log)
instead of the console, but performance should be better than in development instead of the console, but performance should be better than in development
mode. mode.
If you want to use SSL, Edit static/js/Editor.js and change the value of If you want to use SSL, edit config/Common.py and change the value of
form_base_url to the URL of your SSL server. For instance: luminotes.https_url to the URL of your SSL server. For instance:
form_base_url = "https://luminotes.com"; "luminotes.https_url": "https://luminotes.com",
Then you'll need to configure your web server to forward requests for Then you'll need to configure your web server to forward requests for
non-static pages to CherryPy. These instructions are for Apache, but in non-static pages to CherryPy. These instructions are for Apache, but in
@ -57,7 +57,7 @@ installed. These rules cause Apache to serve static files itself, while
passing through requests for dynamic pages to the CherryPy web server running passing through requests for dynamic pages to the CherryPy web server running
locally. locally.
To actually start the production mode server: To actually start the production mode server, run:
python2.5 luminotes.py python2.5 luminotes.py

View File

@ -15,5 +15,6 @@ settings = {
"encoding_filter.encoding": "utf-8", "encoding_filter.encoding": "utf-8",
"decoding_filter.on": True, "decoding_filter.on": True,
"decoding_filter.encoding": "utf-8", "decoding_filter.encoding": "utf-8",
"luminotes.https_url": "",
}, },
} }

View File

@ -28,11 +28,11 @@ You're free to title your wiki notes as you see fit.</li>
<li><b>notebook change history</b><br /> <li><b>notebook change history</b><br />
You can go back and review previous revisions of all your notes, so you never Make updates without worry. You can always go back and view older versions of
lose a single word.</li> your wiki.</li>
</ul> </ul>
<p> <p>
Sound interesting? Then <a href="/notebooks/%s?note_id=new">take a tour</a> or <a href="/notebooks/%s?note_id=new">try it out</a> for yourself! Sound interesting? Then <a href="/notebooks/%s?note_id=new">take a tour</a> or <a href="/notebooks/%s?note_id=new" target="_top">try it out</a> for yourself!
</p> </p>

View File

@ -1,6 +1,6 @@
<h3>login</h3> <h3>login</h3>
No account yet? Want to make a wiki? You can <a href="/notebooks/%s?note_id=new">try it out</a> for free. No account yet? Want to make a wiki? You can <a href="/notebooks/%s?note_id=new" target="_top">try it out</a> for free.
<form id="login_form"> <form id="login_form">
<p> <p>

View File

@ -1,5 +1,5 @@
<a href="/notebooks/%s?note_id=new">about</a> - <a href="/notebooks/%s?note_id=new">about</a> -
<a href="/notebooks/%s?note_id=new">features</a> - <a href="/notebooks/%s?note_id=new">features</a> -
<a href="/notebooks/%s?note_id=new">take a tour</a> - <a href="/notebooks/%s?note_id=new">take a tour</a> -
<a href="/notebooks/%s?note_id=new">try it out</a> - <a href="/notebooks/%s?note_id=new" target="_top">try it out</a> -
<a href="/notebooks/%s?note_id=new">login</a> <a href="/notebooks/%s?note_id=new" target="_top">login</a>

View File

@ -14,7 +14,8 @@ so not all browsers will work for editing your wiki. Supported browsers include:
</ul> </ul>
<p> <p>
The following web browsers are known <i>not</i> to work with Luminotes: The following web browsers are known <i>not</i> to work with some or all
Luminotes features, at least currently:
</p> </p>
<ul> <ul>
@ -25,5 +26,6 @@ The following web browsers are known <i>not</i> to work with Luminotes:
</ul> </ul>
If you're looking for a personal wiki with more minimal browser requirements, If you're looking for a personal wiki with more minimal browser requirements,
you might want to try TiddlyWiki. If you're looking for a more general-purpose you might want to try <a href="http://tiddlywiki.com/"
wiki for multiple users, check out MoinMoin. target="_top">TiddlyWiki</a>. And for a more general-purpose wiki, check out
<a href="http://moinmoin.wikiwikiweb.de/" target="_top">MoinMoin</a>.

View File

@ -1,4 +1,3 @@
form_base_url = "";
note_titles = {} // map from note title to the open editor for that note note_titles = {} // map from note title to the open editor for that note
function Editor( id, notebook_id, note_text, revisions_list, insert_after_iframe_id, read_write, startup, highlight, focus ) { function Editor( id, notebook_id, note_text, revisions_list, insert_after_iframe_id, read_write, startup, highlight, focus ) {
@ -140,7 +139,7 @@ Editor.prototype.finish_init = function () {
if ( signup_button ) { if ( signup_button ) {
var signup_form = withDocument( this.document, function () { return getElement( "signup_form" ); } ); var signup_form = withDocument( this.document, function () { return getElement( "signup_form" ); } );
connect( signup_button, "onclick", function ( event ) { connect( signup_button, "onclick", function ( event ) {
signal( self, "submit_form", form_base_url + "/users/signup", signup_form ); event.stop(); signal( self, "submit_form", "/users/signup", signup_form ); event.stop();
} ); } );
} }
@ -148,7 +147,7 @@ Editor.prototype.finish_init = function () {
if ( login_button ) { if ( login_button ) {
var login_form = withDocument( this.document, function () { return getElement( "login_form" ); } ); var login_form = withDocument( this.document, function () { return getElement( "login_form" ); } );
connect( login_button, "onclick", function ( event ) { connect( login_button, "onclick", function ( event ) {
signal( self, "submit_form", form_base_url + "/users/login", login_form ); event.stop(); signal( self, "submit_form", "/users/login", login_form ); event.stop();
} ); } );
} }
@ -256,9 +255,6 @@ Editor.prototype.mouse_clicked = function ( event ) {
if ( event.mouse().button.middle || event.mouse().button.right ) if ( event.mouse().button.middle || event.mouse().button.right )
return; return;
event.stop();
signal( this, "state_changed", this );
// search through the tree of elements containing the clicked target. if a link isn't found, bail // search through the tree of elements containing the clicked target. if a link isn't found, bail
var link = event.target() var link = event.target()
while ( link.nodeName != "A" ) { while ( link.nodeName != "A" ) {
@ -266,9 +262,15 @@ Editor.prototype.mouse_clicked = function ( event ) {
if ( !link ) if ( !link )
return; return;
} }
if ( !link.href )
// ignore external links pointing outside of this wiki (indicated by the presence of a link
// target), and let the browser handle them normally
if ( !link.href || link.target )
return; return;
event.stop();
signal( this, "state_changed", this );
// in case the link is to ourself, first grab the most recent version of our title // in case the link is to ourself, first grab the most recent version of our title
this.scrape_title(); this.scrape_title();

0
tools/__init__.py Normal file
View File

View File

@ -12,7 +12,7 @@ from model.User import User
class Initializer( object ): class Initializer( object ):
HTML_PATH = u"static/html" HTML_PATH = u"static/html"
ENTRY_FILES = [ # the second element of the tuple is whether to show the note on startup NOTE_FILES = [ # the second element of the tuple is whether to show the note on startup
( u"navigation.html", True ), ( u"navigation.html", True ),
( u"about.html", True ), ( u"about.html", True ),
( u"features.html", True ), ( u"features.html", True ),
@ -29,15 +29,11 @@ class Initializer( object ):
self.database = database self.database = database
self.main_notebook = None self.main_notebook = None
self.read_only_main_notebook = None self.read_only_main_notebook = None
self.user_notebook = None
self.user = None
self.anonymous = None self.anonymous = None
threads = ( threads = (
self.create_main_notebook(), self.create_main_notebook(),
self.create_anonymous_user(), self.create_anonymous_user(),
self.create_user_notebook(),
self.create_user(),
) )
for thread in threads: for thread in threads:
@ -50,25 +46,28 @@ class Initializer( object ):
main_notebook_id = ( yield Scheduler.SLEEP ) main_notebook_id = ( yield Scheduler.SLEEP )
self.main_notebook = Notebook( main_notebook_id, u"Luminotes" ) self.main_notebook = Notebook( main_notebook_id, u"Luminotes" )
for ( filename, startup ) in self.ENTRY_FILES: # create the read-only view of the main notebook
full_filename = os.path.join( self.HTML_PATH, filename ) self.database.next_id( self.scheduler.thread )
contents = file( full_filename ).read().replace( "%s", main_notebook_id ) read_only_main_notebook_id = ( yield Scheduler.SLEEP )
self.read_only_main_notebook = Read_only_notebook( read_only_main_notebook_id, self.main_notebook )
# create an id for each note
note_ids = {}
for ( filename, startup ) in self.NOTE_FILES:
self.database.next_id( self.scheduler.thread ) self.database.next_id( self.scheduler.thread )
note_id = ( yield Scheduler.SLEEP ) note_ids[ filename ] = ( yield Scheduler.SLEEP )
note = Note( note_id, contents ) for ( filename, startup ) in self.NOTE_FILES:
full_filename = os.path.join( self.HTML_PATH, filename )
contents = fix_note_contents( file( full_filename ).read(), read_only_main_notebook_id, note_ids )
note = Note( note_ids[ filename ], contents )
self.main_notebook.add_note( note ) self.main_notebook.add_note( note )
if startup: if startup:
self.main_notebook.add_startup_note( note ) self.main_notebook.add_startup_note( note )
self.database.save( self.main_notebook ) self.database.save( self.main_notebook )
# create the read-only view of the main notebook
self.database.next_id( self.scheduler.thread )
read_only_main_notebook_id = ( yield Scheduler.SLEEP )
self.read_only_main_notebook = Read_only_notebook( read_only_main_notebook_id, self.main_notebook )
self.database.save( self.read_only_main_notebook ) self.database.save( self.read_only_main_notebook )
def create_anonymous_user( self ): def create_anonymous_user( self ):
@ -79,29 +78,6 @@ class Initializer( object ):
self.anonymous = User( anonymous_user_id, u"anonymous", None, None, notebooks ) self.anonymous = User( anonymous_user_id, u"anonymous", None, None, notebooks )
self.database.save( self.anonymous ) self.database.save( self.anonymous )
def create_user_notebook( self ):
# create the user notebook along with a startup note
self.database.next_id( self.scheduler.thread )
user_notebook_id = ( yield Scheduler.SLEEP )
self.user_notebook = Notebook( user_notebook_id, u"my notebook" )
self.database.next_id( self.scheduler.thread )
note_id = ( yield Scheduler.SLEEP )
note = Note( note_id, u"<h3>" )
self.user_notebook.add_note( note )
self.user_notebook.add_startup_note( note )
self.database.save( self.user_notebook )
def create_user( self ):
# create the user
self.database.next_id( self.scheduler.thread )
user_id = ( yield Scheduler.SLEEP )
notebooks = [ self.user_notebook ]
self.user = User( user_id, u"witten", u"dev", u"witten@torsion.org", notebooks )
self.database.save( self.user )
def main(): def main():
if os.path.exists( "data.db" ): if os.path.exists( "data.db" ):
@ -113,5 +89,31 @@ def main():
scheduler.wait_until_idle() scheduler.wait_until_idle()
def fix_note_contents( contents, notebook_id, note_ids ):
import re
from config.Common import settings
LINK_PATTERN = re.compile( '(<a href=")([^"]+?note_id=)([^"]*)("[^>]*>)([^<]*)(</a>)' )
# plug in the notebook id where appropriate
contents = contents.replace( "%s", notebook_id )
# stitch together note links to use the actual note ids of the referenced notes.
# also, use the https URL for certain links if one is configured
def fix_link( match ):
title = match.group( 5 )
https_url = u""
if title in ( u"try it out", u"login" ):
https_url = settings[ u"global" ].get( u"luminotes.https_url", u"" )
return u"".join( [
match.group( 1 ), https_url, match.group( 2 ), note_ids[ title + ".html" ], match.group( 3 ),
match.group( 4 ), match.group( 5 ), match.group( 6 ),
] )
return LINK_PATTERN.sub( fix_link, contents )
if __name__ == "__main__": if __name__ == "__main__":
main() main()

View File

@ -2,15 +2,16 @@
import os import os
import os.path import os.path
from config.Common import settings
from controller.Database import Database from controller.Database import Database
from controller.Scheduler import Scheduler from controller.Scheduler import Scheduler
from model.Note import Note from model.Note import Note
from tools.initdb import fix_note_contents
class Initializer( object ): class Initializer( object ):
HTML_PATH = u"static/html" HTML_PATH = u"static/html"
ENTRY_FILES = [ # the second element of the tuple is whether to show the note on startup NOTE_FILES = [ # the second element of the tuple is whether to show the note on startup
#( u"navigation.html", True ), # skip for now, since the navigtaion note doesn't have a title
( u"about.html", True ), ( u"about.html", True ),
( u"features.html", True ), ( u"features.html", True ),
( u"take a tour.html", False ), ( u"take a tour.html", False ),
@ -21,9 +22,10 @@ class Initializer( object ):
( u"advanced browser features.html", False ), ( u"advanced browser features.html", False ),
] ]
def __init__( self, scheduler, database ): def __init__( self, scheduler, database, navigation_note_id = None ):
self.scheduler = scheduler self.scheduler = scheduler
self.database = database self.database = database
self.navigation_note_id = navigation_note_id
threads = ( threads = (
self.update_main_notebook(), self.update_main_notebook(),
@ -36,29 +38,31 @@ class Initializer( object ):
def update_main_notebook( self ): def update_main_notebook( self ):
self.database.load( u"User anonymous", self.scheduler.thread ) self.database.load( u"User anonymous", self.scheduler.thread )
anonymous = ( yield Scheduler.SLEEP ) anonymous = ( yield Scheduler.SLEEP )
read_only_main_notebook = anonymous.notebooks[ 0 ]
main_notebook = anonymous.notebooks[ 0 ]._Read_only_notebook__wrapped main_notebook = anonymous.notebooks[ 0 ]._Read_only_notebook__wrapped
startup_notes = [] startup_notes = []
# update all of the notes in the main notebook # get the id for each note
for ( filename, startup ) in self.ENTRY_FILES: note_ids = {}
full_filename = os.path.join( self.HTML_PATH, filename ) for ( filename, startup ) in self.NOTE_FILES:
contents = file( full_filename ).read().replace( "%s", main_notebook.object_id )
title = filename.replace( u".html", u"" ) title = filename.replace( u".html", u"" )
note = main_notebook.lookup_note_by_title( title ) note = main_notebook.lookup_note_by_title( title )
note_ids[ filename ] = note.object_id
if note: # update the navigation note if its id was given
main_notebook.update_note( note, contents ) if self.navigation_note_id:
# if for some reason the note isn't present, create it self.database.next_id( self.scheduler.thread )
else: next_id = ( yield Scheduler.SLEEP )
self.database.next_id( self.scheduler.thread ) note = main_notebook.lookup_note( self.navigation_note_id )
note_id = ( yield Scheduler.SLEEP ) self.update_note( "navigation.html", True, main_notebook, read_only_main_notebook, startup_notes, next_id, note_ids, note )
note = Note( note_id, contents )
main_notebook.add_note( note )
main_notebook.remove_startup_note( note ) # update all of the notes in the main notebook
if startup: for ( filename, startup ) in self.NOTE_FILES:
startup_notes.append( note ) self.database.next_id( self.scheduler.thread )
next_id = ( yield Scheduler.SLEEP )
title = filename.replace( u".html", u"" )
note = main_notebook.lookup_note_by_title( title )
self.update_note( filename, startup, main_notebook, read_only_main_notebook, startup_notes, next_id, note_ids, note )
for note in startup_notes: for note in startup_notes:
main_notebook.add_startup_note( note ) main_notebook.add_startup_note( note )
@ -66,13 +70,29 @@ class Initializer( object ):
main_notebook.name = u"Luminotes" main_notebook.name = u"Luminotes"
self.database.save( main_notebook ) self.database.save( main_notebook )
def update_note( self, filename, startup, main_notebook, read_only_main_notebook, startup_notes, next_id, note_ids, note = None ):
full_filename = os.path.join( self.HTML_PATH, filename )
contents = fix_note_contents( file( full_filename ).read(), read_only_main_notebook.object_id, note_ids )
def main(): if note:
main_notebook.update_note( note, contents )
# if for some reason the note isn't present, create it
else:
note = Note( next_id, contents )
main_notebook.add_note( note )
main_notebook.remove_startup_note( note )
if startup:
startup_notes.append( note )
def main( args ):
scheduler = Scheduler() scheduler = Scheduler()
database = Database( scheduler, "data.db" ) database = Database( scheduler, "data.db" )
initializer = Initializer( scheduler, database ) initializer = Initializer( scheduler, database, args and args[ 0 ] or None )
scheduler.wait_until_idle() scheduler.wait_until_idle()
if __name__ == "__main__": if __name__ == "__main__":
main() import sys
main( sys.argv[ 1: ] )