* 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:
parent
a5694d3661
commit
b58f5cb1a1
10
INSTALL
10
INSTALL
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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": "",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>.
|
||||||
|
|
|
@ -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();
|
||||||
|
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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: ] )
|
||||||
|
|
Reference in New Issue