diff options
Diffstat (limited to 'modules/installed')
-rw-r--r-- | modules/installed/first_boot.py | 120 | ||||
-rw-r--r-- | modules/installed/help/help.py | 7 | ||||
-rw-r--r-- | modules/installed/lib/forms.py | 33 |
3 files changed, 152 insertions, 8 deletions
diff --git a/modules/installed/first_boot.py b/modules/installed/first_boot.py new file mode 100644 index 0000000..9cf7474 --- /dev/null +++ b/modules/installed/first_boot.py @@ -0,0 +1,120 @@ +from urlparse import urlparse +import os, cherrypy, re +from gettext import gettext as _ +from plugin_mount import PagePlugin, PluginMount, FormPlugin +from modules.auth import require +from forms import Form +import util as u +from vendor.withsqlite.withsqlite import sqlite_db +import cfg + +class FirstBoot(PagePlugin): + def __init__(self, *args, **kwargs): + PagePlugin.__init__(self, *args, **kwargs) + self.register_page("firstboot") # this is the url this page will hang off of (/firstboot) + + @cherrypy.expose + def index(self, *args, **kwargs): + return self.state0(*args, **kwargs) + + ## TODO: flesh out these tests values + def valid_box_name_p(self, name): + name = name.strip() + if re.search("\W", name): + return False + return True + def valid_box_key_p(self, key): + return True + def generate_box_key(self): + return "fake key" + + @cherrypy.expose + def state0(self, message="", box_name="", box_key="", submitted=False): + """ + In this state, we do time config over HTTP, name the box and + server key selection. + + All the parameters are form inputs. They get passed in when + the form is submitted. This method checks the inputs and if + they validate, uses them to take action. If they do not + validate, it displays the form to give the user a chance to + input correct values. It might display an error message (in + the message parameter). + + message is an optional string that we can display to the + user. It's a good place to put error messages. + """ + + ## Until LDAP is in place, we'll put the box name and key in the cfg.store_file + ## Must resist the sick temptation to write an LDAP interface to the sqlite file + with sqlite_db(cfg.store_file, table="thisbox", autocommit=True) as db: + db['about'] = "This table is for information about this FreedomBox" + if box_name: + if self.valid_box_name_p(box_name): + db['box_name'] = box_name + else: + message += _("Invalid box name.") + elif 'box_name' in db and db['box_name']: + box_name = db['box_name'] + #TODO: set /etc/hostname to box name via ex machina + + if box_key: + if self.valid_box_key_p(box_key): + db['box_key'] = box_key + else: + message += _("Invalid key!") + elif 'box_key' in db and db['box_key']: + box_key = _("We already have a key for this box on file.") #TODO: Think this through and handle more gracefully + elif submitted and not box_key: + box_key = self.generate_box_key() + db['box_key'] = box_key + + + if box_name and box_key and self.valid_box_name_p(box_name) and self.valid_box_key_p(box_key): + ## Update state to 1 and head there + with sqlite_db(cfg.store_file, table="firstboot", autocommit=True) as db: + db['state']=1 + raise cherrypy.InternalRedirect('/firstboot/state1') + + main = "<p>Welcome. It looks like this FreedomBox isn't set up yet. We'll need to ask you a just few questions to get started.</p>" + form = Form(title="Welcome to Your FreedomBox!", + action="/firstboot", + name="whats_my_name", + message=message) + if not box_name: + box_name = cfg.box_name + form.html("<p>For convenience, your FreedomBox needs a name. It should be something short that doesn't contain spaces or punctuation. 'Willard' would be a good name. 'Freestyle McFreedomBox!!!' would not.</p>") + form.text_input('Name your FreedomBox', id="box_name", value=box_name) + form.html("<p>%(box_name)s uses cryptographic keys so it can prove its identity when talking to you. %(box_name)s can make a key for itself, but if one already exists (from a prior FreedomBox, for example), you can paste it below. This key should not be the same as your key because you are not your FreedomBox!</p>" % {'box_name':cfg.box_name}) + form.text_box("If you want, paste your box's key here.", id="box_key", value=box_key) + form.hidden(name="submitted", value="True") + form.submit("Box it up!") + + main += form.render() + return self.fill_template(template="base", title=_("First Boot!"), main=main, + sidebar_right=_("""<strong>Getting Help</strong><p>We've done our best to make your FreedomBox easy to use. If you have questions during setup, there are a few places to turn for help. TODO: add links to such help.</p>""")) + + @cherrypy.expose + def state1(self, message=None): + """ + State 1 is when we have a box name and key. In this state, + our task is to provide a certificate and maybe to guide the + user through installing it. We automatically move to State 2, + which is an HTTPS connection. + + TODO: HTTPS failure in State 2 should returns to state 1. + """ + main = """<p>Here's a certificate. +TODO: explain all this cert stuff to the user.</p> +<p>TODO: add instrux for installing certificate.</p> +<p>After you have installed +""" + if False: + ## Update state to 2 and head there + with sqlite_db(cfg.store_file, table="firstboot", autocommit=True) as db: + db['state']=1 + #TODO: switch to HTTPS + raise cherrypy.InternalRedirect('/firstboot/state1') + + return self.fill_template(template="base", title=_("Installing the Certificate"), main=main, + sidebar_right=_("""<strong>Getting Help</strong><p>We've done our best to make your FreedomBox easy to use. If you have questions during setup, there are a few places to turn for help. TODO: add links to such help.</p>""")) diff --git a/modules/installed/help/help.py b/modules/installed/help/help.py index 049f600..e05123b 100644 --- a/modules/installed/help/help.py +++ b/modules/installed/help/help.py @@ -83,7 +83,6 @@ class View(PagePlugin): if page not in ['design', 'plinth', 'hacking', 'faq']: raise cherrypy.HTTPError(404, "The path '/help/view/%s' was not found." % page) return self.fill_template(template="err", main="<p>Sorry, as much as I would like to show you that page, I don't seem to have a page named %s!</p>" % page) - IF = open(os.path.join("doc", "%s.part.html" % page), 'r') - main = IF.read() - IF.close() - return self.fill_template(template="two_col", title=_("%s Documentation" % cfg.product_name), main=main) + with open(os.path.join("doc", "%s.part.html" % page), 'r') as IF: + main = IF.read() + return self.fill_template(title=_("%s Documentation" % cfg.product_name), main=main) diff --git a/modules/installed/lib/forms.py b/modules/installed/lib/forms.py index df78eea..2be810f 100644 --- a/modules/installed/lib/forms.py +++ b/modules/installed/lib/forms.py @@ -1,9 +1,32 @@ +""" +The Form class is a helper class that takes parameters and method +calls and can return html for a form with appropriate hooks for css +styling. It should allow you to display a form but have the +formatting and styling added by the class. You can worry less about +how it looks while still getting consistent, decent-looking forms. + +Take a look at the FirstBoot class for an example of forms in action. + +Copyright 2011-2013 James Vasile + +This software is released to you (yes, you) under the terms of the GNU +Affero General Public License, version 3 or later, available at +<http://www.gnu.org/licenses/agpl.html>. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. +""" class Form(): def __init__(self, action=None, cls='form', title=None, onsubmit=None, name=None, message='', method="get"): """Note that there appears to be a bug in cherrypy whereby forms submitted via post don't have their fields included in kwargs for the default index method. So we use get by - default, though it's not as neat.""" + default, though it's not as neat. + + TODO: file bug on this w/ CherryPy project + """ action = self.get_form_attrib_text('action', action) onsubmit = self.get_form_attrib_text('onsubmit', onsubmit) @@ -65,13 +88,15 @@ class Form(): <span>%s</span> <input type="%s" class="inputtext" name="%s" id="%s" value="%s" size="%s"/> </label>""" % (label, type, name, id, value, size) - def text_box(self, label='', name=None, id=None): + def hidden(self, name=None, id=None, value=''): + self.text_input(type="hidden", name=name, id=id, value=value) + def text_box(self, label='', name=None, id=None, value=""): name, id = self.name_or_id(name, id) self.text += """ <label> <span>%s</span> - <textarea class="textbox" name="%s" id="%s"></textarea> - </label>""" % (label, name, id) + <textarea class="textbox" name="%s" id="%s">%s</textarea> + </label>""" % (label, name, id, value) def submit(self, label='', name=None, id=None): name, id = self.name_or_id(name, id) self.text += """ |