Redesigned Welcome page using Twitter Bootstrap CSS.

Author: Tony Arcieri <tarcieri>
Signed-off-by: David-Sarah Hopwood <david-sarah@jacaranda.org>
This commit is contained in:
David-Sarah Hopwood 2013-03-15 02:28:38 +00:00
parent 427193d855
commit 709be93a29
7 changed files with 4317 additions and 114 deletions

View File

@ -1,6 +1,8 @@
from base64 import b32encode
import os, sys, time, simplejson
import os, re, sys, time, simplejson
from cStringIO import StringIO
from twisted.trial import unittest
from twisted.internet import defer
from twisted.internet import threads # CLI tests use deferToThread
@ -1096,16 +1098,13 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
public = "uri/" + self._root_directory_uri
d = getPage(base)
def _got_welcome(page):
# XXX This test is oversensitive to formatting
expected = "Connected to <span>%d</span>\n of <span>%d</span> known storage servers:" % (self.numclients, self.numclients)
self.failUnless(expected in page,
"I didn't see the right 'connected storage servers'"
" message in: %s" % page
)
expected = "<th>My nodeid:</th> <td class=\"nodeid mine data-chars\">%s</td>" % (b32encode(self.clients[0].nodeid).lower(),)
self.failUnless(expected in page,
"I didn't see the right 'My nodeid' message "
"in: %s" % page)
html = page.replace('\n', ' ')
connected_re = "Connected to <span>%d</span>[ ]*of <span>%d</span> known storage servers" % (self.numclients, self.numclients)
self.failUnless(re.search(connected_re, html),
"I didn't see the right '%s' message in:\n%s" % (connected_re, page))
nodeid_re = "<th>Node ID:</th>[ ]*<td>%s</td>" % (re.escape(b32encode(self.clients[0].nodeid).lower()),)
self.failUnless(re.search(nodeid_re, html),
"I didn't see the right '%s' message in:\n%s" % (nodeid_re, page))
self.failUnless("Helper: 0 active uploads" in page)
d.addCallback(_got_welcome)
d.addCallback(self.log, "done with _got_welcome")
@ -1113,9 +1112,9 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
# get the welcome page from the node that uses the helper too
d.addCallback(lambda res: getPage(self.helper_webish_url))
def _got_welcome_helper(page):
self.failUnless("Connected to helper?: <span>yes</span>" in page,
page)
self.failUnless("Not running helper" in page)
html = page.replace('\n', ' ')
self.failUnless(re.search('<div class="status-indicator connected-yes"></div>[ ]*Helper', html), page)
self.failUnlessIn("Not running helper", page)
d.addCallback(_got_welcome_helper)
d.addCallback(lambda res: getPage(base + public))

View File

@ -600,14 +600,15 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
def test_welcome(self):
d = self.GET("/")
def _check(res):
self.failUnlessIn('Welcome to Tahoe-LAFS', res)
self.failUnlessIn('<title>Tahoe-LAFS - Welcome</title>', res)
self.failUnlessIn(FAVICON_MARKUP, res)
self.failUnlessIn('href="https://tahoe-lafs.org/"', res)
self.failUnlessIn('<a href="status/">Recent and Active Operations</a>', res)
self.failUnlessIn('<a href="status">Recent and Active Operations</a>', res)
self.failUnlessIn('<a href="statistics">Operational Statistics</a>', res)
self.failUnlessIn('<input type="hidden" name="t" value="report-incident" />', res)
res_u = res.decode('utf-8')
self.failUnlessIn(u'<th>My nickname:</th> <td class="nickname mine">fake_nickname \u263A</td></tr>', res_u)
self.failUnlessIn(u'<td>fake_nickname \u263A</td>', res_u)
self.failUnlessIn(u'<div class="nickname">other_nickname \u263B</div>', res_u)
self.failUnlessIn(u'\u00A9 <a href="https://tahoe-lafs.org/">Tahoe-LAFS Software Foundation', res_u)
self.s.basedir = 'web/test_welcome'
fileutil.make_dirs("web/test_welcome")
@ -624,8 +625,10 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
self.s.uploader.helper_furl = None
return self.GET("/")
d.addCallback(_set_no_helper)
d.addCallback(lambda res:
self.failUnlessIn('Connected to helper?: <span>not configured</span>', res))
def _check_no_helper(res):
html = res.replace('\n', ' ')
self.failUnless(re.search('<div class="status-indicator connected-not-configured"></div>[ ]*Helper', html), res)
d.addCallback(_check_no_helper)
# enable helper, not connected
def _set_helper_not_connected(ign):
@ -633,8 +636,10 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
self.s.uploader.helper_connected = False
return self.GET("/")
d.addCallback(_set_helper_not_connected)
d.addCallback(lambda res:
self.failUnlessIn('Connected to helper?: <span>no</span>', res))
def _check_helper_not_connected(res):
html = res.replace('\n', ' ')
self.failUnless(re.search('<div class="status-indicator connected-no"></div>[ ]*Helper', html), res)
d.addCallback(_check_helper_not_connected)
# enable helper, connected
def _set_helper_connected(ign):
@ -642,8 +647,10 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
self.s.uploader.helper_connected = True
return self.GET("/")
d.addCallback(_set_helper_connected)
d.addCallback(lambda res:
self.failUnlessIn('Connected to helper?: <span>yes</span>', res))
def _check_helper_connected(res):
html = res.replace('\n', ' ')
self.failUnless(re.search('<div class="status-indicator connected-yes"></div>[ ]*Helper', html), res)
d.addCallback(_check_helper_connected)
return d
def test_storage(self):
@ -3186,12 +3193,13 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
d = self.GET("/")
def _after_get_welcome_page(res):
MKDIR_BUTTON_RE = re.compile(
'<form action="([^"]*)" method="post".*?'
'<input type="hidden" name="t" value="([^"]*)" />'
'<input type="hidden" name="([^"]*)" value="([^"]*)" />'
'<input type="submit" value="Create a directory" />',
re.I)
mo = MKDIR_BUTTON_RE.search(res)
'<form action="([^"]*)" method="post".*'
'<input type="hidden" name="t" value="([^"]*)" />[ ]*'
'<input type="hidden" name="([^"]*)" value="([^"]*)" />[ ]*'
'<input type="submit" class="btn" value="Create a directory[^"]*" />')
html = res.replace('\n', ' ')
mo = MKDIR_BUTTON_RE.search(html)
self.failUnless(mo, html)
formaction = mo.group(1)
formt = mo.group(2)
formaname = mo.group(3)

View File

@ -255,10 +255,12 @@ class Root(rend.Page):
rhost_s = "%s:%d" % (rhost.host, rhost.port)
else:
rhost_s = str(rhost)
connected = "Yes: to " + rhost_s
addr = rhost_s
connected = "yes"
since = server.get_last_connect_time()
else:
connected = "No"
addr = "N/A"
connected = "no"
since = server.get_last_loss_time()
announced = server.get_announcement_time()
announcement = server.get_announcement()
@ -266,6 +268,7 @@ class Root(rend.Page):
service_name = announcement["service-name"]
TIME_FORMAT = "%H:%M:%S %d-%b-%Y"
ctx.fillSlots("address", addr)
ctx.fillSlots("connected", connected)
ctx.fillSlots("connected-bool", bool(rhost))
ctx.fillSlots("since", time.strftime(TIME_FORMAT,
@ -357,10 +360,9 @@ class Root(rend.Page):
form = T.form(action="report_incident", method="post",
enctype="multipart/form-data")[
T.fieldset[
T.legend(class_="freeform-form-label")["Report an Incident"],
T.input(type="hidden", name="t", value="report-incident"),
"What went wrong?:"+SPACE,
"What went wrong?"+SPACE,
T.input(type="text", name="details"), SPACE,
T.input(type="submit", value="Report!"),
T.input(type="submit", value=u"Report \u00BB"),
]]
return T.div[form]

3990
src/allmydata/web/static/css/bootstrap.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,95 @@
body {
padding-top: 60px;
padding-bottom: 40px;
}
.sidebar-nav {
padding: 9px 0;
}
.node-info {
margin-top: 4px;
line-height: 14px;
}
.node-info th {
text-align: right;
}
.node-info td {
text-align: left;
padding-top: 3px;
padding-left: 5px;
color: white;
}
.nav-form {
padding-left: 10px;
}
.status {
margin-right: 5px;
}
.grid-status {
padding: 40px;
margin-bottom: 30px;
background-color: #eeeeee;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
}
.grid-status h1 {
font-size: 40px;
margin-bottom: 20px;
}
.grid-status .status-indicator {
margin-top: 5px;
}
.furl {
font-size: 0.8em;
}
.peerid {
font-size: 0.8em;
}
.nickname {
font-size: 1.2em;
font-weight: bold;
}
.nodeid {
font-size: 0.8em;
}
.minutia {
font-size: 0.8em;
width: 50%;
}
.status-indicator {
float: left;
width: 16px;
height: 16px;
border-radius: 50%;
display: inline-block;
vertical-align: middle;
margin-right: 4px;
margin-top: 2px;
}
.connected-yes {
background: #468847;
}
.connected-no {
background: #B94A48;
}
.connected-not-configured {
background: #444444;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.6 KiB

View File

@ -1,95 +1,204 @@
<html xmlns:n="http://nevow.com/ns/nevow/0.1"><head>
<title>Tahoe-LAFS - Welcome</title>
<link href="/tahoe.css" rel="stylesheet" type="text/css"/>
<link href="/icon.png" rel="shortcut icon" />
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head><body>
<!DOCTYPE html>
<html lang="en" xmlns:n="http://nevow.com/ns/nevow/0.1">
<head>
<meta charset="utf-8"/>
<title>Tahoe-LAFS - Welcome</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content="Tahoe-LAFS is a free and open distributed storage system"/>
<meta name="author" content="Tahoe-LAFS"/>
<h1>Welcome to Tahoe-LAFS!</h1>
<!-- Le styles -->
<link href="/css/bootstrap.css" rel="stylesheet"/>
<link href="/css/new-tahoe.css" rel="stylesheet"/>
<div class="section" id="this-client">
<h2>This Client</h2>
<!-- Le fav and touch icons -->
<link rel="shortcut icon" href="/icon.png" />
</head>
<p>
<a href="status/">Recent and Active Operations</a>,
<a href="statistics">Operational Statistics</a>
</p>
<body>
<table class="node-info table-headings-left">
<tr><th>My nickname:</th> <td class="nickname mine" n:render="data" n:data="my_nickname" /></tr>
<tr><th>My nodeid:</th> <td class="nodeid mine data-chars" n:render="string" n:data="my_nodeid" /></tr>
<tr><th>My versions:</th> <td n:render="string" n:data="version" /></tr>
<tr><th>Tahoe-LAFS code imported from:</th> <td n:render="data" n:data="import_path" /></tr>
<tr><th>Services running:</th> <td n:render="services" /></tr>
</table>
</div>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="/"><img src="/img/logo.png" alt="Tahoe-LAFS"/></a>
<table class="node-info pull-right">
<tr>
<th>Nickname:</th>
<td n:render="data" n:data="my_nickname" />
</tr>
<tr>
<th>Node ID:</th>
<td n:render="string" n:data="my_nodeid" />
</tr>
</table>
</div>
</div>
</div>
<div id="controls">
<h2>Controls</h2>
<div class="container-fluid">
<div class="row-fluid">
<div class="span3">
<div class="well sidebar-nav nav">
<div class="nav-header">Open Tahoe-URI:</div>
<div class="nav-form">
<form action="uri" method="get" enctype="multipart/form-data">
<input type="text" name="uri" />
<p><input type="submit" class="btn" value="View File or Directory &#187;" /></p>
</form>
</div>
<hr/>
<p>There are also controls for each directory on that directory's page.</p>
<div class="nav-header">Download Tahoe-URI:</div>
<div class="nav-form">
<form action="uri" method="get" enctype="multipart/form-data">
<label for="download-uri">
URI
<input type="text" name="uri" />
</label>
<label for="download-filename">
Filename
<input type="text" name="filename" />
</label>
<p><input type="submit" class="btn" value="Download File &#187;" /></p>
</form>
</div>
<hr/>
<div n:render="mkdir_form" />
<div n:render="view_form" />
<div n:render="upload_form" />
<div n:render="download_form" />
</div>
<div class="nav-header">Upload File</div>
<div class="nav-form">
<form action="uri" method="post" enctype="multipart/form-data">
<input type="file" class="freeform-input-file" name="file" />
<input type="hidden" name="t" value="upload" />
<div class="section" id="grid">
<h2>Status of the Storage Grid</h2>
<label for="upload-chk" class="radio">
<input type="radio" checked="checked" id="upload-chk" value="chk" name="format" />
Immutable
</label>
<div>
<n:attr name="class">connected-<n:invisible n:render="string" n:data="connected_to_introducer" /></n:attr>
<div>Introducer: <span class="data-chars" n:render="string" n:data="introducer_furl" /></div>
<div>Connected to introducer?: <span n:render="string" n:data="connected_to_introducer" /></div>
</div>
<label for="upload-sdmf" class="radio">
<input type="radio" id="upload-sdmf" value="sdmf" name="format" />
SDMF
</label>
<div>
<n:attr name="class">connected-<n:invisible n:render="string" n:data="connected_to_helper" /></n:attr>
<div>Helper: <span n:render="string" n:data="helper_furl" /></div>
<div>Connected to helper?: <span n:render="string" n:data="connected_to_helper_description" /></div>
</div>
<label for="upload-mdmf" class="radio">
<input type="radio" id="upload-mdmf" value="mdmf" name="format" />
MDMF (experimental)
</label>
<p>Connected to <span n:render="string" n:data="connected_storage_servers" />
of <span n:render="string" n:data="known_storage_servers" /> known storage servers:</p>
<p><input type="submit" class="btn" value="Upload File &#187;" /></p>
</form>
</div>
<hr/>
<div>
<table class="services table-headings-top" n:render="sequence" n:data="services">
<tr n:pattern="header">
<th>Service Name</th>
<th class="nickname-and-peerid">
<div class="service-nickname">Nickname</div>
<div class="nodeid data-chars">PeerID</div></th>
<th>Connected?</th>
<th>Since</th>
<th>First Announced</th>
<th>Version</th>
</tr>
<tr n:pattern="item" n:render="service_row">
<td class="service-service-name"><n:slot name="service_name"/></td>
<td class="nickname-and-peerid">
<div class="nickname"><n:slot name="nickname"/></div>
<div class="nodeid data-chars"><n:slot name="peerid"/></div></td>
<td>
<n:attr name="class">service-connected connected-<n:slot name="connected-bool"/></n:attr>
<n:slot name="connected"/>
</td>
<td class="service-since"> <n:slot name="since"/></td>
<td class="service-announced"> <n:slot name="announced"/></td>
<td class="service-version"> <n:slot name="version"/></td>
</tr>
<tr n:pattern="empty"><td>no peers!</td></tr>
</table>
</div>
</div>
<div class="nav-header">Create Directory</div>
<div class="nav-form">
<form action="uri" method="post" enctype="multipart/form-data">
<label for="mkdir-sdmf" class="radio">
<input type="radio" checked="checked" id="mkdir-sdmf" value="sdmf" name="format" />
SDMF
</label>
<div class="section" id="other-resources">
<h2>Other Resources</h2>
<label for="mkdir-mdmf" class="radio">
<input type="radio" id="mkdir-mdmf" value="mdmf" name="format" />
MDMF (experimental)
</label>
<div>Please visit the <a target="_blank" href="https://tahoe-lafs.org/">Tahoe-LAFS home page</a> for
code updates and bug reporting.</div>
<input type="hidden" name="t" value="mkdir" />
<input type="hidden" name="redirect_to_result" value="true" />
<input type="submit" class="btn" value="Create a directory &#187;" />
</form>
</div>
<div n:render="incident_button" />
</div>
</div><!--/.well -->
<div class="well sidebar-nav">
<div class="nav-header">
<ul class="nav nav-list">
<li class="nav-header">Tools</li>
<li><a href="status">Recent and Active Operations</a></li>
<li><a href="statistics">Operational Statistics</a></li>
</ul>
</div>
<hr/>
<div class="nav-header">
<ul class="nav nav-list">
<li class="nav-header">Report an Incident</li>
<li><div n:render="incident_button" /></li>
</ul>
</div>
</div><!--/.well -->
</div><!--/span-->
<div class="span9">
<div style="margin-bottom: 16px">
<h1 style="font-size: 48px">Grid Status</h1>
</div>
<div class="grid-status">
<div class="row-fluid">
<div class="span6">
<div>
<h3>
<div><n:attr name="class">status-indicator connected-<n:invisible n:render="string" n:data="connected_to_introducer" /></n:attr></div>
Introducer
</h3>
<div class="furl" n:render="string" n:data="introducer_furl" />
</div>
<div>
<h3>
<div><n:attr name="class">status-indicator connected-<n:invisible n:render="string" n:data="connected_to_helper" /></n:attr></div>
Helper
</h3>
<div class="furl" n:render="string" n:data="helper_furl" />
</div>
</div><!--/span-->
<div class="span6">
<div class="span4 services">
<h3>Services</h3>
<div n:render="services" />
</div><!--/span-->
</div><!--/span-->
</div><!--/row-->
</div>
<div class="row-fluid">
<h2>
Connected to <span n:render="string" n:data="connected_storage_servers" />
of <span n:render="string" n:data="known_storage_servers" /> known storage servers
</h2>
</div><!--/row-->
<table class="table table-striped table-bordered peer-status" n:render="sequence" n:data="services">
<thead>
<tr n:pattern="header">
<td><h3>Nickname</h3></td>
<td><h3>Address</h3></td>
<td><h3>Service</h3></td>
<td><h3>Since</h3></td>
<td><h3>Announced</h3></td>
<td><h3>Version</h3></td>
</tr>
</thead>
<tr n:pattern="item" n:render="service_row">
<td class="nickname-and-peerid">
<div><n:attr name="class">status-indicator connected-<n:slot name="connected"/></n:attr></div>
<div class="nickname"><n:slot name="nickname"/></div>
<div class="nodeid"><n:slot name="peerid"/></div>
</td>
<td class="address"><n:slot name="address"/></td>
<td class="service-service-name"><n:slot name="service_name"/></td>
<td class="service-since timestamp"><n:slot name="since"/></td>
<td class="service-announced timestamp"><n:slot name="announced"/></td>
<td class="service-version"><n:slot name="version"/></td>
</tr>
<tr n:pattern="empty"><td>You are not presently connected to any peers</td></tr>
</table>
</div><!--/span-->
</div><!--/row-->
</body></html>
<hr/>
<footer>
<p>&#169; <a href="https://tahoe-lafs.org/">Tahoe-LAFS Software Foundation 2013</a></p>
<p class="minutia" n:render="string" n:data="version"></p>
<p class="minutia">Tahoe-LAFS code imported from: <span n:render="data" n:data="import_path" /></p>
</footer>
</div><!--/.fluid-container-->
</body>
</html>