mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-06-03 08:10:52 +00:00
wui: improved columns in welcome page server list
As discussed at https://tahoe-lafs.org/trac/tahoe-lafs/ticket/1973 and in previous pull request #129. - replace lengthy timestamps with human-readable deltas (eg 1h 2m 3s) - replace "announced" column with "Last RX" column - remove service column (it always said the same thing, "storage") - fix colspan on 'You are not presently connected' message Previous versions, some with github comments:3fe9053134
,486dbfc7bd
, andc89ea62580
,9fabb92486
,bbd8b42a25
Unlike previous attempts, the tests on this one should pass in any timezone. (But like current master, will fail with Nevow >=0.12...) Thanks to an anonymous contributor who wrote some of the tests.
This commit is contained in:
parent
6226f6b497
commit
a2d724aab7
@ -417,7 +417,6 @@ class IStorageBroker(Interface):
|
|||||||
public attributes::
|
public attributes::
|
||||||
|
|
||||||
service_name: the type of service provided, like 'storage'
|
service_name: the type of service provided, like 'storage'
|
||||||
announcement_time: when we first heard about this service
|
|
||||||
last_connect_time: when we last established a connection
|
last_connect_time: when we last established a connection
|
||||||
last_loss_time: when we last lost a connection
|
last_loss_time: when we last lost a connection
|
||||||
|
|
||||||
|
@ -168,7 +168,6 @@ class NativeStorageServer:
|
|||||||
the their version information. I remember information about when we were
|
the their version information. I remember information about when we were
|
||||||
last connected too, even if we aren't currently connected.
|
last connected too, even if we aren't currently connected.
|
||||||
|
|
||||||
@ivar announcement_time: when we first heard about this service
|
|
||||||
@ivar last_connect_time: when we last established a connection
|
@ivar last_connect_time: when we last established a connection
|
||||||
@ivar last_loss_time: when we last lost a connection
|
@ivar last_loss_time: when we last lost a connection
|
||||||
|
|
||||||
@ -216,7 +215,6 @@ class NativeStorageServer:
|
|||||||
self._long_description = tubid_s
|
self._long_description = tubid_s
|
||||||
self._short_description = tubid_s[:6]
|
self._short_description = tubid_s[:6]
|
||||||
|
|
||||||
self.announcement_time = time.time()
|
|
||||||
self.last_connect_time = None
|
self.last_connect_time = None
|
||||||
self.last_loss_time = None
|
self.last_loss_time = None
|
||||||
self.remote_host = None
|
self.remote_host = None
|
||||||
@ -267,8 +265,11 @@ class NativeStorageServer:
|
|||||||
return self.last_connect_time
|
return self.last_connect_time
|
||||||
def get_last_loss_time(self):
|
def get_last_loss_time(self):
|
||||||
return self.last_loss_time
|
return self.last_loss_time
|
||||||
def get_announcement_time(self):
|
def get_last_received_data_time(self):
|
||||||
return self.announcement_time
|
if self.rref is None:
|
||||||
|
return None
|
||||||
|
else:
|
||||||
|
return self.rref.getDataLastReceivedAt()
|
||||||
|
|
||||||
def get_available_space(self):
|
def get_available_space(self):
|
||||||
version = self.get_version()
|
version = self.get_version()
|
||||||
|
@ -173,6 +173,24 @@ class TestMixin(SignalMixin):
|
|||||||
if required_to_quiesce and active:
|
if required_to_quiesce and active:
|
||||||
self.fail("Reactor was still active when it was required to be quiescent.")
|
self.fail("Reactor was still active when it was required to be quiescent.")
|
||||||
|
|
||||||
|
|
||||||
|
class TimezoneMixin(object):
|
||||||
|
|
||||||
|
def setTimezone(self, timezone):
|
||||||
|
unset = object()
|
||||||
|
originalTimezone = os.environ.get('TZ', unset)
|
||||||
|
def restoreTimezone():
|
||||||
|
if originalTimezone is unset:
|
||||||
|
del os.environ['TZ']
|
||||||
|
time.tzset()
|
||||||
|
else:
|
||||||
|
os.environ['TZ'] = originalTimezone
|
||||||
|
time.tzset()
|
||||||
|
os.environ['TZ'] = timezone
|
||||||
|
time.tzset()
|
||||||
|
self.addCleanup(restoreTimezone)
|
||||||
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import win32file
|
import win32file
|
||||||
import win32con
|
import win32con
|
||||||
|
@ -1019,7 +1019,41 @@ class TimeFormat(unittest.TestCase):
|
|||||||
leap_years_1970_to_2047_inclusive = ((2044 - 1968) // 4)
|
leap_years_1970_to_2047_inclusive = ((2044 - 1968) // 4)
|
||||||
self.failUnlessEqual(time_format.format_time(time.gmtime(seconds_per_day*((2048 - 1970)*365+leap_years_1970_to_2047_inclusive))), '2048-01-01 00:00:00')
|
self.failUnlessEqual(time_format.format_time(time.gmtime(seconds_per_day*((2048 - 1970)*365+leap_years_1970_to_2047_inclusive))), '2048-01-01 00:00:00')
|
||||||
|
|
||||||
test_format_time_y2038.todo = "one day we'll move beyond 32-bit time"
|
test_format_time_y2038.todo = "This test is known to fail on systems with 32-bit time_t."
|
||||||
|
|
||||||
|
def test_format_delta(self):
|
||||||
|
time_1 = 1389812723
|
||||||
|
time_5s_delta = 1389812728
|
||||||
|
time_28m7s_delta = 1389814410
|
||||||
|
time_1h_delta = 1389816323
|
||||||
|
time_1d21h46m49s_delta = 1389977532
|
||||||
|
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1, time_1), '0s')
|
||||||
|
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1, time_5s_delta), '5s')
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1, time_28m7s_delta), '28m 7s')
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1, time_1h_delta), '1h 0m 0s')
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1, time_1d21h46m49s_delta), '1d 21h 46m 49s')
|
||||||
|
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1d21h46m49s_delta, time_1), '-')
|
||||||
|
|
||||||
|
# time_1 with a decimal fraction will make the delta 1s less
|
||||||
|
time_1decimal = 1389812723.383963
|
||||||
|
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1decimal, time_5s_delta), '4s')
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1decimal, time_28m7s_delta), '28m 6s')
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1decimal, time_1h_delta), '59m 59s')
|
||||||
|
self.failUnlessEqual(
|
||||||
|
time_format.format_delta(time_1decimal, time_1d21h46m49s_delta), '1d 21h 46m 48s')
|
||||||
|
|
||||||
class CacheDir(unittest.TestCase):
|
class CacheDir(unittest.TestCase):
|
||||||
def test_basic(self):
|
def test_basic(self):
|
||||||
|
@ -172,21 +172,28 @@ class FakeHistory:
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
class FakeDisplayableServer(StubServer):
|
class FakeDisplayableServer(StubServer):
|
||||||
def __init__(self, serverid, nickname):
|
def __init__(self, serverid, nickname, connected,
|
||||||
|
last_connect_time, last_loss_time, last_rx_time):
|
||||||
StubServer.__init__(self, serverid)
|
StubServer.__init__(self, serverid)
|
||||||
self.announcement = {"my-version": "allmydata-tahoe-fake",
|
self.announcement = {"my-version": "allmydata-tahoe-fake",
|
||||||
"service-name": "storage",
|
"service-name": "storage",
|
||||||
"nickname": nickname}
|
"nickname": nickname}
|
||||||
|
self.connected = connected
|
||||||
|
self.last_loss_time = last_loss_time
|
||||||
|
self.last_rx_time = last_rx_time
|
||||||
|
self.last_connect_time = last_connect_time
|
||||||
def is_connected(self):
|
def is_connected(self):
|
||||||
return True
|
return self.connected
|
||||||
def get_permutation_seed(self):
|
def get_permutation_seed(self):
|
||||||
return ""
|
return ""
|
||||||
def get_remote_host(self):
|
def get_remote_host(self):
|
||||||
return ""
|
return ""
|
||||||
def get_last_loss_time(self):
|
def get_last_loss_time(self):
|
||||||
return None
|
return self.last_loss_time
|
||||||
def get_announcement_time(self):
|
def get_last_received_data_time(self):
|
||||||
return None
|
return self.last_rx_time
|
||||||
|
def get_last_connect_time(self):
|
||||||
|
return self.last_connect_time
|
||||||
def get_announcement(self):
|
def get_announcement(self):
|
||||||
return self.announcement
|
return self.announcement
|
||||||
def get_nickname(self):
|
def get_nickname(self):
|
||||||
@ -242,7 +249,13 @@ class FakeClient(Client):
|
|||||||
self.storage_broker = StorageFarmBroker(None, permute_peers=True)
|
self.storage_broker = StorageFarmBroker(None, permute_peers=True)
|
||||||
# fake knowledge of another server
|
# fake knowledge of another server
|
||||||
self.storage_broker.test_add_server("other_nodeid",
|
self.storage_broker.test_add_server("other_nodeid",
|
||||||
FakeDisplayableServer("other_nodeid", u"other_nickname \u263B"))
|
FakeDisplayableServer(
|
||||||
|
serverid="other_nodeid", nickname=u"other_nickname \u263B", connected = True,
|
||||||
|
last_connect_time = 10, last_loss_time = 20, last_rx_time = 30))
|
||||||
|
self.storage_broker.test_add_server("disconnected_nodeid",
|
||||||
|
FakeDisplayableServer(
|
||||||
|
serverid="other_nodeid", nickname=u"disconnected_nickname \u263B", connected = False,
|
||||||
|
last_connect_time = 15, last_loss_time = 25, last_rx_time = 35))
|
||||||
self.introducer_client = None
|
self.introducer_client = None
|
||||||
self.history = FakeHistory()
|
self.history = FakeHistory()
|
||||||
self.uploader = FakeUploader()
|
self.uploader = FakeUploader()
|
||||||
@ -268,14 +281,16 @@ class FakeClient(Client):
|
|||||||
|
|
||||||
MUTABLE_SIZELIMIT = FakeMutableFileNode.MUTABLE_SIZELIMIT
|
MUTABLE_SIZELIMIT = FakeMutableFileNode.MUTABLE_SIZELIMIT
|
||||||
|
|
||||||
class WebMixin(object):
|
class WebMixin(testutil.TimezoneMixin):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
self.setTimezone('UTC-13:00')
|
||||||
self.s = FakeClient()
|
self.s = FakeClient()
|
||||||
self.s.startService()
|
self.s.startService()
|
||||||
self.staticdir = self.mktemp()
|
self.staticdir = self.mktemp()
|
||||||
self.clock = Clock()
|
self.clock = Clock()
|
||||||
|
self.fakeTime = 86460 # 1d 0h 1m 0s
|
||||||
self.ws = webish.WebishServer(self.s, "0", staticdir=self.staticdir,
|
self.ws = webish.WebishServer(self.s, "0", staticdir=self.staticdir,
|
||||||
clock=self.clock)
|
clock=self.clock, now_fn=lambda:self.fakeTime)
|
||||||
self.ws.setServiceParent(self.s)
|
self.ws.setServiceParent(self.s)
|
||||||
self.webish_port = self.ws.getPortnum()
|
self.webish_port = self.ws.getPortnum()
|
||||||
self.webish_url = self.ws.getURL()
|
self.webish_url = self.ws.getURL()
|
||||||
@ -601,7 +616,6 @@ class WebMixin(object):
|
|||||||
self.fail("%s was supposed to Error(302), not get '%s'" %
|
self.fail("%s was supposed to Error(302), not get '%s'" %
|
||||||
(which, res))
|
(which, res))
|
||||||
|
|
||||||
|
|
||||||
class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixin, unittest.TestCase):
|
class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixin, unittest.TestCase):
|
||||||
def test_create(self):
|
def test_create(self):
|
||||||
pass
|
pass
|
||||||
@ -619,6 +633,11 @@ class Web(WebMixin, WebErrorMixin, testutil.StallMixin, testutil.ReallyEqualMixi
|
|||||||
res_u = res.decode('utf-8')
|
res_u = res.decode('utf-8')
|
||||||
self.failUnlessIn(u'<td>fake_nickname \u263A</td>', 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'<div class="nickname">other_nickname \u263B</div>', res_u)
|
||||||
|
self.failUnlessIn(u'Connected to <span>1</span>\n of <span>2</span> known storage servers', res_u)
|
||||||
|
self.failUnlessIn(u'<div class="status-indicator"><img src="img/connected-yes.png" alt="Connected" /></div>\n <a class="timestamp" title="1970-01-01 13:00:10">1d\u00A00h\u00A00m\u00A050s</a>', res_u)
|
||||||
|
self.failUnlessIn(u'<div class="status-indicator"><img src="img/connected-no.png" alt="Disconnected" /></div>\n <a class="timestamp" title="1970-01-01 13:00:25">1d\u00A00h\u00A00m\u00A035s</a>', res_u)
|
||||||
|
self.failUnlessIn(u'<td class="service-last-received-data"><a class="timestamp" title="1970-01-01 13:00:30">1d\u00A00h\u00A00m\u00A030s</a></td>', res_u)
|
||||||
|
self.failUnlessIn(u'<td class="service-last-received-data"><a class="timestamp" title="1970-01-01 13:00:35">1d\u00A00h\u00A00m\u00A025s</a></td>', res_u)
|
||||||
self.failUnlessIn(u'\u00A9 <a href="https://tahoe-lafs.org/">Tahoe-LAFS Software Foundation', res_u)
|
self.failUnlessIn(u'\u00A9 <a href="https://tahoe-lafs.org/">Tahoe-LAFS Software Foundation', res_u)
|
||||||
self.failUnlessIn('<td><h3>Available</h3></td>', res)
|
self.failUnlessIn('<td><h3>Available</h3></td>', res)
|
||||||
self.failUnlessIn('123.5kB', res)
|
self.failUnlessIn('123.5kB', res)
|
||||||
|
@ -66,3 +66,27 @@ def parse_date(s):
|
|||||||
# day
|
# day
|
||||||
return int(iso_utc_time_to_seconds(s + "T00:00:00"))
|
return int(iso_utc_time_to_seconds(s + "T00:00:00"))
|
||||||
|
|
||||||
|
def format_delta(time_1, time_2):
|
||||||
|
if time_1 is None:
|
||||||
|
return "N/A"
|
||||||
|
if time_1 > time_2:
|
||||||
|
return '-'
|
||||||
|
delta = int(time_2 - time_1)
|
||||||
|
seconds = delta % 60
|
||||||
|
delta -= seconds
|
||||||
|
minutes = (delta / 60) % 60
|
||||||
|
delta -= minutes * 60
|
||||||
|
hours = delta / (60*60) % 24
|
||||||
|
delta -= hours * 24
|
||||||
|
days = delta / (24*60*60)
|
||||||
|
if not days:
|
||||||
|
if not hours:
|
||||||
|
if not minutes:
|
||||||
|
return "%ss" % (seconds)
|
||||||
|
else:
|
||||||
|
return "%sm %ss" % (minutes, seconds)
|
||||||
|
else:
|
||||||
|
return "%sh %sm %ss" % (hours, minutes, seconds)
|
||||||
|
else:
|
||||||
|
return "%sd %sh %sm %ss" % (days, hours, minutes, seconds)
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ from allmydata.interfaces import ExistingChildError, NoSuchChildError, \
|
|||||||
MustBeReadonlyError, MustNotBeUnknownRWError, SDMF_VERSION, MDMF_VERSION
|
MustBeReadonlyError, MustNotBeUnknownRWError, SDMF_VERSION, MDMF_VERSION
|
||||||
from allmydata.mutable.common import UnrecoverableFileError
|
from allmydata.mutable.common import UnrecoverableFileError
|
||||||
from allmydata.util import abbreviate
|
from allmydata.util import abbreviate
|
||||||
from allmydata.util.time_format import format_time
|
from allmydata.util.time_format import format_time, format_delta
|
||||||
from allmydata.util.encodingutil import to_str, quote_output
|
from allmydata.util.encodingutil import to_str, quote_output
|
||||||
|
|
||||||
|
|
||||||
@ -213,9 +213,15 @@ def text_plain(text, ctx):
|
|||||||
def spaces_to_nbsp(text):
|
def spaces_to_nbsp(text):
|
||||||
return unicode(text).replace(u' ', u'\u00A0')
|
return unicode(text).replace(u' ', u'\u00A0')
|
||||||
|
|
||||||
|
def render_time_delta(time_1, time_2):
|
||||||
|
return spaces_to_nbsp(format_delta(time_1, time_2))
|
||||||
|
|
||||||
def render_time(t):
|
def render_time(t):
|
||||||
return spaces_to_nbsp(format_time(time.localtime(t)))
|
return spaces_to_nbsp(format_time(time.localtime(t)))
|
||||||
|
|
||||||
|
def render_time_attr(t):
|
||||||
|
return format_time(time.localtime(t))
|
||||||
|
|
||||||
class WebError(Exception):
|
class WebError(Exception):
|
||||||
def __init__(self, text, code=http.BAD_REQUEST):
|
def __init__(self, text, code=http.BAD_REQUEST):
|
||||||
self.text = text
|
self.text = text
|
||||||
|
@ -14,7 +14,7 @@ from allmydata.interfaces import IFileNode
|
|||||||
from allmydata.web import filenode, directory, unlinked, status, operations
|
from allmydata.web import filenode, directory, unlinked, status, operations
|
||||||
from allmydata.web import storage
|
from allmydata.web import storage
|
||||||
from allmydata.web.common import abbreviate_size, getxmlfile, WebError, \
|
from allmydata.web.common import abbreviate_size, getxmlfile, WebError, \
|
||||||
get_arg, RenderMixin, get_format, get_mutable_type, render_time
|
get_arg, RenderMixin, get_format, get_mutable_type, render_time_delta, render_time, render_time_attr
|
||||||
|
|
||||||
|
|
||||||
class URIHandler(RenderMixin, rend.Page):
|
class URIHandler(RenderMixin, rend.Page):
|
||||||
@ -138,12 +138,13 @@ class Root(rend.Page):
|
|||||||
"no": "Disconnected",
|
"no": "Disconnected",
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, client, clock=None):
|
def __init__(self, client, clock=None, now_fn=None):
|
||||||
rend.Page.__init__(self, client)
|
rend.Page.__init__(self, client)
|
||||||
self.client = client
|
self.client = client
|
||||||
# If set, clock is a twisted.internet.task.Clock that the tests
|
# If set, clock is a twisted.internet.task.Clock that the tests
|
||||||
# use to test ophandle expiration.
|
# use to test ophandle expiration.
|
||||||
self.child_operations = operations.OphandleTable(clock)
|
self.child_operations = operations.OphandleTable(clock)
|
||||||
|
self.now_fn = now_fn
|
||||||
try:
|
try:
|
||||||
s = client.getServiceNamed("storage")
|
s = client.getServiceNamed("storage")
|
||||||
except KeyError:
|
except KeyError:
|
||||||
@ -282,7 +283,7 @@ class Root(rend.Page):
|
|||||||
ctx.fillSlots("peerid", server.get_longname())
|
ctx.fillSlots("peerid", server.get_longname())
|
||||||
ctx.fillSlots("nickname", server.get_nickname())
|
ctx.fillSlots("nickname", server.get_nickname())
|
||||||
rhost = server.get_remote_host()
|
rhost = server.get_remote_host()
|
||||||
if rhost:
|
if server.is_connected():
|
||||||
if nodeid == self.client.nodeid:
|
if nodeid == self.client.nodeid:
|
||||||
rhost_s = "(loopback)"
|
rhost_s = "(loopback)"
|
||||||
elif isinstance(rhost, address.IPv4Address):
|
elif isinstance(rhost, address.IPv4Address):
|
||||||
@ -290,29 +291,37 @@ class Root(rend.Page):
|
|||||||
else:
|
else:
|
||||||
rhost_s = str(rhost)
|
rhost_s = str(rhost)
|
||||||
addr = rhost_s
|
addr = rhost_s
|
||||||
connected = "yes"
|
service_connection_status = "yes"
|
||||||
since = server.get_last_connect_time()
|
last_connect_time = server.get_last_connect_time()
|
||||||
|
service_connection_status_rel_time = render_time_delta(last_connect_time, self.now_fn())
|
||||||
|
service_connection_status_abs_time = render_time_attr(last_connect_time)
|
||||||
else:
|
else:
|
||||||
addr = "N/A"
|
addr = "N/A"
|
||||||
connected = "no"
|
service_connection_status = "no"
|
||||||
since = server.get_last_loss_time()
|
last_loss_time = server.get_last_loss_time()
|
||||||
announced = server.get_announcement_time()
|
service_connection_status_rel_time = render_time_delta(last_loss_time, self.now_fn())
|
||||||
|
service_connection_status_abs_time = render_time_attr(last_loss_time)
|
||||||
|
|
||||||
|
last_received_data_time = server.get_last_received_data_time()
|
||||||
|
last_received_data_rel_time = render_time_delta(last_received_data_time, self.now_fn())
|
||||||
|
last_received_data_abs_time = render_time_attr(last_received_data_time)
|
||||||
|
|
||||||
announcement = server.get_announcement()
|
announcement = server.get_announcement()
|
||||||
version = announcement["my-version"]
|
version = announcement["my-version"]
|
||||||
service_name = announcement["service-name"]
|
|
||||||
available_space = server.get_available_space()
|
available_space = server.get_available_space()
|
||||||
if available_space is None:
|
if available_space is None:
|
||||||
available_space = "N/A"
|
available_space = "N/A"
|
||||||
else:
|
else:
|
||||||
available_space = abbreviate_size(available_space)
|
available_space = abbreviate_size(available_space)
|
||||||
ctx.fillSlots("address", addr)
|
ctx.fillSlots("address", addr)
|
||||||
ctx.fillSlots("connected", connected)
|
ctx.fillSlots("service_connection_status", service_connection_status)
|
||||||
ctx.fillSlots("connected_alt", self._connectedalts[connected])
|
ctx.fillSlots("service_connection_status_alt", self._connectedalts[service_connection_status])
|
||||||
ctx.fillSlots("connected-bool", bool(rhost))
|
ctx.fillSlots("connected-bool", bool(rhost))
|
||||||
ctx.fillSlots("since", render_time(since))
|
ctx.fillSlots("service_connection_status_abs_time", service_connection_status_abs_time)
|
||||||
ctx.fillSlots("announced", render_time(announced))
|
ctx.fillSlots("service_connection_status_rel_time", service_connection_status_rel_time)
|
||||||
|
ctx.fillSlots("last_received_data_abs_time", last_received_data_abs_time)
|
||||||
|
ctx.fillSlots("last_received_data_rel_time", last_received_data_rel_time)
|
||||||
ctx.fillSlots("version", version)
|
ctx.fillSlots("version", version)
|
||||||
ctx.fillSlots("service_name", service_name)
|
|
||||||
ctx.fillSlots("available_space", available_space)
|
ctx.fillSlots("available_space", available_space)
|
||||||
|
|
||||||
return ctx.tag
|
return ctx.tag
|
||||||
|
@ -77,3 +77,12 @@ body {
|
|||||||
float: left;
|
float: left;
|
||||||
margin: 5px;
|
margin: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.nickname-and-peerid .timestamp {
|
||||||
|
float: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
a.timestamp {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration:none;
|
||||||
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
<!DOCTYPE html>
|
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0
|
||||||
|
Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||||
<html lang="en" xmlns:n="http://nevow.com/ns/nevow/0.1">
|
<html lang="en" xmlns:n="http://nevow.com/ns/nevow/0.1">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8"/>
|
<meta charset="utf-8"/>
|
||||||
@ -169,27 +170,24 @@
|
|||||||
<tr n:pattern="header">
|
<tr n:pattern="header">
|
||||||
<td><h3>Nickname</h3></td>
|
<td><h3>Nickname</h3></td>
|
||||||
<td><h3>Address</h3></td>
|
<td><h3>Address</h3></td>
|
||||||
<td><h3>Service</h3></td>
|
<td><h3>Last RX</h3></td>
|
||||||
<td><h3>Since</h3></td>
|
|
||||||
<td><h3>Announced</h3></td>
|
|
||||||
<td><h3>Version</h3></td>
|
<td><h3>Version</h3></td>
|
||||||
<td><h3>Available</h3></td>
|
<td><h3>Available</h3></td>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tr n:pattern="item" n:render="service_row">
|
<tr n:pattern="item" n:render="service_row">
|
||||||
<td class="nickname-and-peerid">
|
<td class="nickname-and-peerid">
|
||||||
<div class="status-indicator"><img><n:attr name="src">img/connected-<n:slot name="connected" />.png</n:attr><n:attr name="alt"><n:slot name="connected_alt" /></n:attr></img></div>
|
<div class="status-indicator"><img><n:attr name="src">img/connected-<n:slot name="service_connection_status" />.png</n:attr><n:attr name="alt"><n:slot name="service_connection_status_alt" /></n:attr></img></div>
|
||||||
|
<a class="timestamp"><n:attr name="title"><n:slot name="service_connection_status_abs_time"/></n:attr><n:slot name="service_connection_status_rel_time"/></a>
|
||||||
<div class="nickname"><n:slot name="nickname"/></div>
|
<div class="nickname"><n:slot name="nickname"/></div>
|
||||||
<div class="nodeid"><n:slot name="peerid"/></div>
|
<div class="nodeid"><n:slot name="peerid"/></div>
|
||||||
</td>
|
</td>
|
||||||
<td class="address"><n:slot name="address"/></td>
|
<td class="address"><n:slot name="address"/></td>
|
||||||
<td class="service-service-name"><n:slot name="service_name"/></td>
|
<td class="service-last-received-data"><a class="timestamp"><n:attr name="title"><n:slot name="last_received_data_abs_time"/></n:attr><n:slot name="last_received_data_rel_time"/></a></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>
|
<td class="service-version"><n:slot name="version"/></td>
|
||||||
<td class="service-available-space"><n:slot name="available_space"/></td>
|
<td class="service-available-space"><n:slot name="available_space"/></td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr n:pattern="empty"><td>You are not presently connected to any peers</td></tr>
|
<tr n:pattern="empty"><td colspan="5">You are not presently connected to any peers</td></tr>
|
||||||
</table>
|
</table>
|
||||||
</div><!--/span-->
|
</div><!--/span-->
|
||||||
</div><!--/row-->
|
</div><!--/row-->
|
||||||
|
@ -129,14 +129,14 @@ class WebishServer(service.MultiService):
|
|||||||
name = "webish"
|
name = "webish"
|
||||||
|
|
||||||
def __init__(self, client, webport, nodeurl_path=None, staticdir=None,
|
def __init__(self, client, webport, nodeurl_path=None, staticdir=None,
|
||||||
clock=None):
|
clock=None, now_fn=time.time):
|
||||||
service.MultiService.__init__(self)
|
service.MultiService.__init__(self)
|
||||||
# the 'data' argument to all render() methods default to the Client
|
# the 'data' argument to all render() methods default to the Client
|
||||||
# the 'clock' argument to root.Root is, if set, a
|
# the 'clock' argument to root.Root is, if set, a
|
||||||
# twisted.internet.task.Clock that is provided by the unit tests
|
# twisted.internet.task.Clock that is provided by the unit tests
|
||||||
# so that they can test features that involve the passage of
|
# so that they can test features that involve the passage of
|
||||||
# time in a deterministic manner.
|
# time in a deterministic manner.
|
||||||
self.root = root.Root(client, clock)
|
self.root = root.Root(client, clock, now_fn)
|
||||||
self.buildServer(webport, nodeurl_path, staticdir)
|
self.buildServer(webport, nodeurl_path, staticdir)
|
||||||
if self.root.child_operations:
|
if self.root.child_operations:
|
||||||
self.site.remember(self.root.child_operations, IOpHandleTable)
|
self.site.remember(self.root.child_operations, IOpHandleTable)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user