Merge branch 'ux-update' of https://github.com/micahflee/onionshare into ux_update_fix_shutdown_timer

This commit is contained in:
Miguel Jacq 2018-02-11 17:15:52 +11:00
commit ed179922d4
No known key found for this signature in database
GPG Key ID: EEA4341C6D97A0B6
15 changed files with 214 additions and 120 deletions

View File

@ -201,6 +201,7 @@ Section "install"
File "${BINPATH}\share\html\index.html"
SetOutPath "$INSTDIR\share\images"
File "${BINPATH}\share\images\favicon.ico"
File "${BINPATH}\share\images\file_delete.png"
File "${BINPATH}\share\images\logo.png"
File "${BINPATH}\share\images\logo_transparent.png"
@ -209,7 +210,8 @@ Section "install"
File "${BINPATH}\share\images\server_stopped.png"
File "${BINPATH}\share\images\server_working.png"
File "${BINPATH}\share\images\settings.png"
File "${BINPATH}\share\images\settings_inactive.png"
File "${BINPATH}\share\images\web_file.png"
File "${BINPATH}\share\images\web_folder.png"
SetOutPath "$INSTDIR\share\locale"
File "${BINPATH}\share\locale\cs.json"
@ -380,6 +382,7 @@ FunctionEnd
Delete "$INSTDIR\share\html\404.html"
Delete "$INSTDIR\share\html\denied.html"
Delete "$INSTDIR\share\html\index.html"
Delete "$INSTDIR\share\images\favicon.ico"
Delete "$INSTDIR\share\images\file_delete.png"
Delete "$INSTDIR\share\images\logo.png"
Delete "$INSTDIR\share\images\logo_transparent.png"
@ -388,7 +391,8 @@ FunctionEnd
Delete "$INSTDIR\share\images\server_stopped.png"
Delete "$INSTDIR\share\images\server_working.png"
Delete "$INSTDIR\share\images\settings.png"
Delete "$INSTDIR\share\images\settings_inactive.png"
Delete "$INSTDIR\share\images\web_file.png"
Delete "$INSTDIR\share\images\web_folder.png"
Delete "$INSTDIR\share\license.txt"
Delete "$INSTDIR\share\locale\cs.json"
Delete "$INSTDIR\share\locale\de.json"

View File

@ -26,6 +26,7 @@ import queue
import socket
import sys
import tempfile
import base64
from distutils.version import LooseVersion as Version
from urllib.request import urlopen
@ -125,6 +126,12 @@ def add_request(request_type, path, data=None):
})
# Load and base64 encode images to pass into templates
favicon_b64 = base64.b64encode(open(common.get_resource_path('images/favicon.ico'), 'rb').read()).decode()
logo_b64 = base64.b64encode(open(common.get_resource_path('images/logo.png'), 'rb').read()).decode()
folder_b64 = base64.b64encode(open(common.get_resource_path('images/web_folder.png'), 'rb').read()).decode()
file_b64 = base64.b64encode(open(common.get_resource_path('images/web_file.png'), 'rb').read()).decode()
slug = None
@ -206,7 +213,10 @@ def index(slug_candidate):
global stay_open, download_in_progress
deny_download = not stay_open and download_in_progress
if deny_download:
r = make_response(render_template_string(open(common.get_resource_path('html/denied.html')).read()))
r = make_response(render_template_string(
open(common.get_resource_path('html/denied.html')).read(),
favicon_b64=favicon_b64
))
for header, value in security_headers:
r.headers.set(header, value)
return r
@ -215,6 +225,10 @@ def index(slug_candidate):
r = make_response(render_template_string(
open(common.get_resource_path('html/index.html')).read(),
favicon_b64=favicon_b64,
logo_b64=logo_b64,
folder_b64=folder_b64,
file_b64=file_b64,
slug=slug,
file_info=file_info,
filename=os.path.basename(zip_filename),
@ -243,7 +257,10 @@ def download(slug_candidate):
global stay_open, download_in_progress, done
deny_download = not stay_open and download_in_progress
if deny_download:
r = make_response(render_template_string(open(common.get_resource_path('html/denied.html')).read()))
r = make_response(render_template_string(
open(common.get_resource_path('html/denied.html')).read(),
favicon_b64=favicon_b64
))
for header,value in security_headers:
r.headers.set(header, value)
return r
@ -355,7 +372,10 @@ def page_not_found(e):
force_shutdown()
print(strings._('error_rate_limit'))
r = make_response(render_template_string(open(common.get_resource_path('html/404.html')).read()), 404)
r = make_response(render_template_string(
open(common.get_resource_path('html/404.html')).read(),
favicon_b64=favicon_b64
), 404)
for header, value in security_headers:
r.headers.set(header, value)
return r

View File

@ -145,7 +145,8 @@ class FileList(QtWidgets.QListWidget):
count = len(event.mimeData().urls())
self.drop_count.setText('+{}'.format(count))
self.drop_count.setGeometry(self.width() - 60, self.height() - 40, 50, 30)
size_hint = self.drop_count.sizeHint()
self.drop_count.setGeometry(self.width() - size_hint.width() - 10, self.height() - size_hint.height() - 10, size_hint.width(), size_hint.height())
self.drop_count.show()
event.accept()
else:
@ -207,15 +208,24 @@ class FileList(QtWidgets.QListWidget):
icon = ip.icon(fileinfo)
if os.path.isfile(filename):
size = common.human_readable_filesize(fileinfo.size())
size_bytes = fileinfo.size()
size_readable = common.human_readable_filesize(size_bytes)
else:
size = common.human_readable_filesize(common.dir_size(filename))
item_name = '{0:s} ({1:s})'.format(basename, size)
size_bytes = common.dir_size(filename)
size_readable = common.human_readable_filesize(size_bytes)
# Create a new item
item = QtWidgets.QListWidgetItem(item_name)
item.setToolTip(size)
item = QtWidgets.QListWidgetItem()
item.setIcon(icon)
item.size_bytes = size_bytes
# Item's name and size labels
item_name = QtWidgets.QLabel(basename)
item_name.setWordWrap(False)
item_name.setSizePolicy(QtWidgets.QSizePolicy.Ignored, QtWidgets.QSizePolicy.Fixed)
item_name.setStyleSheet('QLabel { color: #000000; font-size: 13px; }')
item_size = QtWidgets.QLabel(size_readable)
item_size.setStyleSheet('QLabel { color: #666666; font-size: 11px; }')
# Item's delete button
def delete_item():
@ -229,16 +239,22 @@ class FileList(QtWidgets.QListWidget):
item.item_button.setFlat(True)
item.item_button.setIcon( QtGui.QIcon(common.get_resource_path('images/file_delete.png')) )
item.item_button.clicked.connect(delete_item)
item.item_button.setSizePolicy(QtWidgets.QSizePolicy.Fixed, QtWidgets.QSizePolicy.Fixed)
# Create an item widget to display on the item
item_widget_layout = QtWidgets.QHBoxLayout()
item_widget_layout.addStretch()
item_widget_layout.addWidget(item.item_button)
item_widget = QtWidgets.QWidget()
item_widget.setLayout(item_widget_layout)
# Create the item's widget and layouts
item_vlayout = QtWidgets.QVBoxLayout()
item_vlayout.addWidget(item_name)
item_vlayout.addWidget(item_size)
item_hlayout = QtWidgets.QHBoxLayout()
item_hlayout.addLayout(item_vlayout)
item_hlayout.addWidget(item.item_button)
widget = QtWidgets.QWidget()
widget.setLayout(item_hlayout)
item.setSizeHint(widget.sizeHint())
self.addItem(item)
self.setItemWidget(item, item_widget)
self.setItemWidget(item, widget)
self.files_updated.emit()
@ -252,12 +268,17 @@ class FileSelection(QtWidgets.QVBoxLayout):
super(FileSelection, self).__init__()
self.server_on = False
# file list
# Info label
self.info_label = QtWidgets.QLabel()
self.info_label.setStyleSheet('QLabel { font-size: 12px; color: #666666; }')
# File list
self.file_list = FileList()
self.file_list.currentItemChanged.connect(self.update)
self.file_list.files_dropped.connect(self.update)
self.file_list.files_updated.connect(self.update)
# buttons
# Buttons
self.add_button = QtWidgets.QPushButton(strings._('gui_add', True))
self.add_button.clicked.connect(self.add)
self.delete_button = QtWidgets.QPushButton(strings._('gui_delete', True))
@ -267,7 +288,8 @@ class FileSelection(QtWidgets.QVBoxLayout):
button_layout.addWidget(self.add_button)
button_layout.addWidget(self.delete_button)
# add the widgets
# Add the widgets
self.addWidget(self.info_label)
self.addWidget(self.file_list)
self.addLayout(button_layout)
@ -277,6 +299,20 @@ class FileSelection(QtWidgets.QVBoxLayout):
"""
Update the GUI elements based on the current state.
"""
# Update the info label
file_count = self.file_list.count()
if file_count == 0:
self.info_label.hide()
else:
total_size_bytes = 0
for index in range(self.file_list.count()):
item = self.file_list.item(index)
total_size_bytes += item.size_bytes
total_size_readable = common.human_readable_filesize(total_size_bytes)
self.info_label.setText(strings._('gui_file_info', True).format(file_count, total_size_readable))
self.info_label.show()
# All buttons should be hidden if the server is on
if self.server_on:
self.add_button.hide()

View File

@ -156,16 +156,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.server_share_status_label.setStyleSheet('QLabel { font-style: italic; color: #666666; padding: 2px; }')
self.status_bar.insertWidget(0, self.server_share_status_label)
# Persistent URL notification
self.persistent_url_label = QtWidgets.QLabel(strings._('persistent_url_in_use', True))
self.persistent_url_label.setStyleSheet('font-weight: bold; color: #333333;')
self.persistent_url_label.hide()
# Primary action layout
primary_action_layout = QtWidgets.QVBoxLayout()
primary_action_layout.addWidget(self.server_status)
primary_action_layout.addWidget(self.filesize_warning)
primary_action_layout.addWidget(self.persistent_url_label)
primary_action_layout.addWidget(self.downloads_container)
self.primary_action = QtWidgets.QWidget()
self.primary_action.setLayout(primary_action_layout)
@ -204,7 +198,6 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.check_for_updates()
def update_primary_action(self):
common.log('OnionShareGui', 'update_primary_action')
# Resize window
self.adjustSize()
@ -426,9 +419,6 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.stop_server()
self.start_server_error(strings._('gui_server_started_after_timeout'))
if self.settings.get('save_private_key'):
self.persistent_url_label.show()
def start_server_error(self, error):
"""
If there's an error when trying to start the onion service
@ -460,7 +450,6 @@ class OnionShareGui(QtWidgets.QMainWindow):
# Remove ephemeral service, but don't disconnect from Tor
self.onion.cleanup(stop_tor=False)
self.filesize_warning.hide()
self.persistent_url_label.hide()
self.stop_server_finished.emit()
self.set_server_active(False)
@ -599,11 +588,10 @@ class OnionShareGui(QtWidgets.QMainWindow):
"""
Disable the Settings button while an OnionShare server is active.
"""
self.settings_button.setEnabled(not active)
if active:
self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings_inactive.png')) )
self.settings_button.hide()
else:
self.settings_button.setIcon( QtGui.QIcon(common.get_resource_path('images/settings.png')) )
self.settings_button.show()
# Disable settings menu action when server is active
self.settingsAction.setEnabled(not active)

View File

@ -127,10 +127,15 @@ class ServerStatus(QtWidgets.QWidget):
if self.status == self.STATUS_STARTED:
self.url_description.show()
if self.settings.get('close_after_first_download'):
if self.settings.get('save_private_key'):
self.url_label.setText(strings._('gui_url_label_persistent', True))
self.url_label.setToolTip(strings._('gui_url_persistence_warning', True))
elif self.settings.get('close_after_first_download'):
self.url_label.setText(strings._('gui_url_label_one_time', True))
self.url_label.setToolTip('')
else:
self.url_label.setText(strings._('gui_url_label', True))
self.url_label.setToolTip('')
self.url_label.show()
self.url.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug))

View File

@ -86,12 +86,14 @@ class SettingsDialog(QtWidgets.QDialog):
stealth_details.setWordWrap(True)
stealth_details.setTextInteractionFlags(QtCore.Qt.TextBrowserInteraction)
stealth_details.setOpenExternalLinks(True)
stealth_details.setMinimumSize(stealth_details.sizeHint())
self.stealth_checkbox = QtWidgets.QCheckBox()
self.stealth_checkbox.setCheckState(QtCore.Qt.Unchecked)
self.stealth_checkbox.setText(strings._("gui_settings_stealth_option", True))
hidservauth_details = QtWidgets.QLabel(strings._('gui_settings_stealth_hidservauth_string', True))
hidservauth_details.setWordWrap(True)
hidservauth_details.setMinimumSize(hidservauth_details.sizeHint())
hidservauth_details.hide()
self.hidservauth_copy_button = QtWidgets.QPushButton(strings._('gui_copy_hidservauth', True))

View File

@ -2,6 +2,7 @@
<html>
<head>
<title>Error 404</title>
<link href="data:image/x-icon;base64,{{favicon_b64}}" rel="icon" type="image/x-icon" />
<style type="text/css">
body {
background-color: #FFC4D5;

View File

@ -2,6 +2,7 @@
<html>
<head>
<title>OnionShare</title>
<link href="data:image/x-icon;base64,{{favicon_b64}}" rel="icon" type="image/x-icon" />
<style>
body {
background-color: #222222;
@ -15,4 +16,4 @@
<body>
<p>OnionShare download in progress</p>
</body>
</html>
</html>

View File

@ -2,104 +2,137 @@
<html>
<head>
<title>OnionShare</title>
<link href="data:image/x-icon;base64,{{favicon_b64}}" rel="icon" type="image/x-icon" />
<style type="text/css">
body {
background-color: #222222;
color: #ffffff;
text-align: center;
font-family: sans-serif;
padding: 5em 1em;
}
.button {
-moz-box-shadow:inset 0px 1px 0px 0px #cae3fc;
-webkit-box-shadow:inset 0px 1px 0px 0px #cae3fc;
box-shadow:inset 0px 1px 0px 0px #cae3fc;
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #79bbff), color-stop(1, #4197ee) );
background:-moz-linear-gradient( center top, #79bbff 5%, #4197ee 100% );
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#79bbff', endColorstr='#4197ee');
background-color:#79bbff;
-webkit-border-top-left-radius:12px;
-moz-border-radius-topleft:12px;
border-top-left-radius:12px;
-webkit-border-top-right-radius:12px;
-moz-border-radius-topright:12px;
border-top-right-radius:12px;
-webkit-border-bottom-right-radius:12px;
-moz-border-radius-bottomright:12px;
border-bottom-right-radius:12px;
-webkit-border-bottom-left-radius:12px;
-moz-border-radius-bottomleft:12px;
border-bottom-left-radius:12px;
text-indent:0;
border:1px solid #469df5;
display:inline-block;
color:#ffffff;
font-size:29px;
font-weight:bold;
font-style:normal;
height:50px;
line-height:50px;
text-decoration:none;
text-align:center;
text-shadow:1px 1px 0px #287ace;
padding: 0 20px;
}
.button:hover {
background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #4197ee), color-stop(1, #79bbff) );
background:-moz-linear-gradient( center top, #4197ee 5%, #79bbff 100% );
filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#4197ee', endColorstr='#79bbff');
background-color:#4197ee;
}.button:active {
position:relative;
top:1px;
}
.clearfix:after {
content: ".";
display: block;
clear: both;
visibility: hidden;
line-height: 0;
height: 0;
}
.download-size {
color: #999999;
}
.download-description {
padding: 10px;
}
.file-list {
margin: 50px auto 0 auto;
padding: 10px;
text-align: left;
background-color: #333333;
}
.file-list th {
padding: 5px;
font-weight: bold;
color: #999999;
}
.file-list td {
padding: 5px;
}
body {
margin: 0;
font-family: Helvetica;
}
header {
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
background: #fcfcfc;
background: -webkit-linear-gradient(top, #fcfcfc 0%, #f2f2f2 100%);
padding: 0.8rem;
}
header .logo {
vertical-align: middle;
width: 45px;
height: 45px;
}
header h1 {
display: inline-block;
margin: 0 0 0 0.5rem;
vertical-align: middle;
font-weight: normal;
font-size: 1.5rem;
color: #666666;
}
header .right {
float: right;
font-size: .75rem;
}
header .right ul li {
display: inline;
margin: 0 0 0 .5rem;
font-size: 1rem;
}
header .button {
color: #ffffff;
background-color: #4e064f;
padding: 10px;
border-radius: 5px;
text-decoration: none;
margin-left: 1rem;
cursor: pointer;
}
table.file-list {
width: 100%;
margin: 0 auto;
border-collapse: collapse;
}
table.file-list th {
text-align: left;
text-transform: uppercase;
font-weight: normal;
color: #666666;
padding: 0.5rem;
}
table.file-list tr {
border-bottom: 1px solid #e0e0e0;
}
table.file-list td {
white-space: nowrap;
padding: 0.5rem 10rem 0.5rem 0.8rem;
}
table.file-list td img {
vertical-align: middle;
margin-right: 0.5rem;
}
table.file-list td:last-child {
width: 100%;
}
</style>
<meta name="onionshare-filename" content="{{ filename }}">
<meta name="onionshare-filesize" content="{{ filesize }}">
</head>
<body>
<p><a class="button" href='/{{ slug }}/download'>{{ filename }} &#x25BC;</a></p>
<p class="download-size"><strong title="{{ filesize }} bytes">{{ filesize_human }} (compressed)</strong></p>
<p class="download-description">This zip file contains the following contents:</p>
<header class="clearfix">
<div class="right">
<ul>
<li>Total size: <strong>{{ filesize_human }}</strong> (compressed)</li>
<li><a class="button" href='/{{ slug }}/download'>Download Files</a></li>
</ul>
</div>
<img class="logo" src="data:image/png;base64,{{logo_b64}}" title="OnionShare">
<h1>OnionShare</h1>
</header>
<table class="file-list">
<tr>
<th>Type</th>
<th>Name</th>
<th>Filename</th>
<th>Size</th>
<th></th>
</tr>
{% for info in file_info.dirs %}
<tr>
<td><img width="30" height="30" title="" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAB3RJTUUH3ggbFCQXOpwjbQAAAM5JREFUSMftlksOwyAMRDFCbNjkDL1kz9K75Cq9AZF3kYIM7oqoP/KpcFK1mRUrnsbW2Fbq0N+JiPhZRMS1OZAfzLzocwCAGmC9V2Vhjduarj8CzynGqIwxsDl4SXV267E4uFTN33X8deCxDyV1XXeRYJo1UUDEa9M0pxoRK+b4HiqRcz3nVAJKRK+TSxqaGbrkNKUkBn0oNSK2+T0MA1dau9NzO4QwuvPen1lAxQtEsq/vNpSWhvZ9v/3IRMTWOQezkyuEoKy14tfHoU11A6Mr5AxrpuMVAAAAAElFTkSuQmCC" /></td>
<td>{{ info.basename }}</td>
<td>
<img width="30" height="30" title="" alt="" src="data:image/png;base64,{{ folder_b64 }}" />
{{ info.basename }}
</td>
<td>{{ info.size_human }}</td>
<td></td>
</tr>
{% endfor %}
{% for info in file_info.files %}
<tr>
<td><img width="30" height="30" title="" alt="" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAeCAYAAAA7MK6iAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAA7EAAAOxAGVKw4bAAAAB3RJTUUH3ggbFCQqYvRvfAAAAJFJREFUSMftlUkOwCAIAEWJ//+uIUivtYmtG7VN8KYHRhlA5zYtuB6klCTG2BcEAHrBeN4ws3jvX3lxQZmFiogMgVesVrhKXlvgakKf4KqVdAdXL+Ea3O8aILh0GnUMEhjtw9mLbUv1tx33fgItynBVoN+k2hybY3Nsjg08Bs45q4GYuQ4OIQARLYcSkUPEoiUP4j5IFasKOnUAAAAASUVORK5CYII=" /></td>
<td>{{ info.basename }}</td>
<td>
<img width="30" height="30" title="" alt="" src="data:image/png;base64,{{ file_b64 }}" />
{{ info.basename }}
</td>
<td>{{ info.size_human }}</td>
<td></td>
</tr>
{% endfor %}
</table>

BIN
share/images/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 513 B

BIN
share/images/web_file.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

BIN
share/images/web_folder.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 338 B

View File

@ -133,11 +133,13 @@
"gui_server_timeout_expired": "The chosen timeout has already expired.\nPlease update the timeout and then you may start sharing.",
"share_via_onionshare": "Share via OnionShare",
"gui_save_private_key_checkbox": "Use a persistent address\n(unchecking will delete any saved address)",
"persistent_url_in_use": "This share is using a persistent address",
"gui_url_description": "<b>Anyone</b> with this link can <b>download</b> your files using <b>Tor Browser</b>:",
"gui_url_label": "Your Download Address",
"gui_url_label_persistent": "Your <strong>Persistent</strong> Download Address <em>(what's this?)</em>",
"gui_url_label_one_time": "Your One-Time Download Address",
"gui_url_persistence_warning": "Every share will have the same URL.<br><br>If you want to go back to using one-time URLs, turn off persistence in the Settings.",
"gui_status_indicator_stopped": "Ready to Share",
"gui_status_indicator_working": "Starting...",
"gui_status_indicator_started": "Sharing"
"gui_status_indicator_started": "Sharing",
"gui_file_info": "{} Files, {}"
}

View File

@ -1,13 +1,15 @@
import sys
# Force tests to look for resources in the source code tree
sys.onionshare_dev_mode = True
import os
import shutil
import sys
import tempfile
import pytest
from onionshare import common
@pytest.fixture
def temp_dir_1024():
""" Create a temporary directory that has a single file of a