diff --git a/install/onionshare.nsi b/install/onionshare.nsi index e5f989bb..0a9e6cf2 100644 --- a/install/onionshare.nsi +++ b/install/onionshare.nsi @@ -201,7 +201,9 @@ 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\info.png" File "${BINPATH}\share\images\logo.png" File "${BINPATH}\share\images\logo_transparent.png" File "${BINPATH}\share\images\logo_grayscale.png" @@ -209,7 +211,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,7 +383,9 @@ 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\info.png" Delete "$INSTDIR\share\images\logo.png" Delete "$INSTDIR\share\images\logo_transparent.png" Delete "$INSTDIR\share\images\logo_grayscale.png" @@ -388,7 +393,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" diff --git a/onionshare/web.py b/onionshare/web.py index 3eef67c7..7d1b72e8 100644 --- a/onionshare/web.py +++ b/onionshare/web.py @@ -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 diff --git a/onionshare_gui/file_selection.py b/onionshare_gui/file_selection.py index d52fd017..ba3df69d 100644 --- a/onionshare_gui/file_selection.py +++ b/onionshare_gui/file_selection.py @@ -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,23 @@ 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) + + if file_count > 1: + self.info_label.setText(strings._('gui_file_info', True).format(file_count, total_size_readable)) + else: + self.info_label.setText(strings._('gui_file_info_single', 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() diff --git a/onionshare_gui/onionshare_gui.py b/onionshare_gui/onionshare_gui.py index 722ba82f..af90ad58 100644 --- a/onionshare_gui/onionshare_gui.py +++ b/onionshare_gui/onionshare_gui.py @@ -56,7 +56,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.setWindowTitle('OnionShare') self.setWindowIcon(QtGui.QIcon(common.get_resource_path('images/logo.png'))) - self.setMinimumWidth(350) + self.setMinimumWidth(430) # Load settings self.config = config @@ -91,9 +91,11 @@ class OnionShareGui(QtWidgets.QMainWindow): self.starting_server_step2.connect(self.start_server_step2) self.starting_server_step3.connect(self.start_server_step3) self.starting_server_error.connect(self.start_server_error) + self.server_status.button_clicked.connect(self.clear_message) # Filesize warning self.filesize_warning = QtWidgets.QLabel() + self.filesize_warning.setWordWrap(True) self.filesize_warning.setStyleSheet('padding: 10px 0; font-weight: bold; color: #333333;') self.filesize_warning.hide() @@ -133,24 +135,32 @@ class OnionShareGui(QtWidgets.QMainWindow): # Status bar self.status_bar = QtWidgets.QStatusBar() self.status_bar.setSizeGripEnabled(False) - self.status_bar.setStyleSheet("QStatusBar::item { border: 0px; }") + statusBar_cssStyleData =""" + QStatusBar { + font-style: italic; + color: #666666; + } + + QStatusBar::item { + border: 0px; + }""" + + self.status_bar.setStyleSheet(statusBar_cssStyleData) self.status_bar.addPermanentWidget(self.server_status_indicator) self.status_bar.addPermanentWidget(self.settings_button) self.setStatusBar(self.status_bar) # Status bar, zip progress bar self._zip_progress_bar = None - - # 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() + # Status bar, sharing messages + self.server_share_status_label = QtWidgets.QLabel('') + self.server_share_status_label.setStyleSheet('QLabel { font-style: italic; color: #666666; padding: 2px; }') + self.status_bar.insertWidget(0, self.server_share_status_label) # 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) @@ -189,16 +199,15 @@ class OnionShareGui(QtWidgets.QMainWindow): self.check_for_updates() def update_primary_action(self): - common.log('OnionShareGui', 'update_primary_action') - # Resize window - self.adjustSize() - # Show or hide primary action layout if self.file_selection.file_list.count() > 0: self.primary_action.show() else: self.primary_action.hide() + # Resize window + self.adjustSize() + def update_server_status_indicator(self): common.log('OnionShareGui', 'update_server_status_indicator') @@ -316,6 +325,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.downloads_container.hide() self.downloads.reset_downloads() self.status_bar.clearMessage() + self.server_share_status_label.setText('') # Reset web counters web.download_count = 0 @@ -376,7 +386,6 @@ class OnionShareGui(QtWidgets.QMainWindow): self.starting_server_error.emit(e.strerror) return - #self.status_bar.showMessage(strings._('gui_starting_server2', True)) t = threading.Thread(target=finish_starting_server, kwargs={'self': self}) t.daemon = True t.start() @@ -398,7 +407,7 @@ class OnionShareGui(QtWidgets.QMainWindow): self.filesize_warning.setText(strings._("large_filesize", True)) self.filesize_warning.show() - if self.server_status.timer_enabled: + if self.settings.get('shutdown_timeout'): # Convert the date value to seconds between now and then now = QtCore.QDateTime.currentDateTime() self.timeout = now.secsTo(self.server_status.timeout) @@ -411,9 +420,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 @@ -453,10 +459,10 @@ 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.file_selection.file_list.adjustSize() self.set_server_active(False) + self.stop_server_finished.emit() def check_for_updates(self): """ @@ -539,7 +545,8 @@ class OnionShareGui(QtWidgets.QMainWindow): # close on finish? if not web.get_stay_open(): self.server_status.stop_server() - self.status_bar.showMessage(strings._('closing_automatically', True)) + self.status_bar.clearMessage() + self.server_share_status_label.setText(strings._('closing_automatically', True)) else: if self.server_status.status == self.server_status.STATUS_STOPPED: self.downloads.cancel_download(event["data"]["id"]) @@ -554,30 +561,34 @@ class OnionShareGui(QtWidgets.QMainWindow): # If the auto-shutdown timer has stopped, stop the server if self.server_status.status == self.server_status.STATUS_STARTED: - if self.app.shutdown_timer and self.server_status.timer_enabled: + if self.app.shutdown_timer and self.settings.get('shutdown_timeout'): if self.timeout > 0: if not self.app.shutdown_timer.is_alive(): # If there were no attempts to download the share, or all downloads are done, we can stop if web.download_count == 0 or web.done: self.server_status.stop_server() - self.status_bar.showMessage(strings._('close_on_timeout', True)) + self.status_bar.clearMessage() + self.server_share_status_label.setText(strings._('close_on_timeout', True)) # A download is probably still running - hold off on stopping the share else: - self.status_bar.showMessage(strings._('timeout_download_still_running', True)) + self.status_bar.clearMessage() + self.server_share_status_label.setText(strings._('timeout_download_still_running', True)) def copy_url(self): """ When the URL gets copied to the clipboard, display this in the status bar. """ common.log('OnionShareGui', 'copy_url') - self.status_bar.showMessage(strings._('gui_copied_url', True), 2000) + if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'): + self.systemTray.showMessage(strings._('gui_copied_url_title', True), strings._('gui_copied_url', True)) def copy_hidservauth(self): """ When the stealth onion service HidServAuth gets copied to the clipboard, display this in the status bar. """ common.log('OnionShareGui', 'copy_hidservauth') - self.status_bar.showMessage(strings._('gui_copied_hidservauth', True), 2000) + if self.systemTray.supportsMessages() and self.settings.get('systray_notifications'): + self.systemTray.showMessage(strings._('gui_copied_hidservauth_title', True), strings._('gui_copied_hidservauth', True)) def clear_message(self): """ @@ -589,11 +600,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) diff --git a/onionshare_gui/server_status.py b/onionshare_gui/server_status.py index 2715d57b..a5bdb1f6 100644 --- a/onionshare_gui/server_status.py +++ b/onionshare_gui/server_status.py @@ -30,6 +30,7 @@ class ServerStatus(QtWidgets.QWidget): server_started = QtCore.pyqtSignal() server_stopped = QtCore.pyqtSignal() server_canceled = QtCore.pyqtSignal() + button_clicked = QtCore.pyqtSignal() url_copied = QtCore.pyqtSignal() hidservauth_copied = QtCore.pyqtSignal() @@ -48,17 +49,15 @@ class ServerStatus(QtWidgets.QWidget): self.settings = settings - # Helper boolean as this is used in a few places - self.timer_enabled = False - # Shutdown timeout layout self.shutdown_timeout_label = QtWidgets.QLabel(strings._('gui_settings_shutdown_timeout', True)) self.shutdown_timeout = QtWidgets.QDateTimeEdit() # Set proposed timeout to be 5 minutes into the future + self.shutdown_timeout.setDisplayFormat("hh:mm A MMM d, yy") self.shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) # Onion services can take a little while to start, so reduce the risk of it expiring too soon by setting the minimum to 2 min from now self.shutdown_timeout.setMinimumDateTime(QtCore.QDateTime.currentDateTime().addSecs(120)) - self.shutdown_timeout.setCurrentSectionIndex(4) + self.shutdown_timeout.setCurrentSection(QtWidgets.QDateTimeEdit.MinuteSection) shutdown_timeout_layout = QtWidgets.QHBoxLayout() shutdown_timeout_layout.addWidget(self.shutdown_timeout_label) shutdown_timeout_layout.addWidget(self.shutdown_timeout) @@ -84,6 +83,7 @@ class ServerStatus(QtWidgets.QWidget): self.url.setFont(url_font) self.url.setWordWrap(True) self.url.setMinimumHeight(60) + self.url.setMinimumSize(self.url.sizeHint()) self.url.setStyleSheet('QLabel { background-color: #ffffff; color: #000000; padding: 10px; border: 1px solid #666666; }') url_buttons_style = 'QPushButton { color: #3f7fcf; }' @@ -115,22 +115,6 @@ class ServerStatus(QtWidgets.QWidget): self.update() - def shutdown_timeout_toggled(self, checked): - """ - Shutdown timer option was toggled. If checked, show the timer settings. - """ - if checked: - self.timer_enabled = True - # Hide the checkbox, show the options - self.shutdown_timeout_label.show() - # Reset the default timer to 5 minutes into the future after toggling the option on - self.shutdown_timeout.setDateTime(QtCore.QDateTime.currentDateTime().addSecs(300)) - self.shutdown_timeout.show() - else: - self.timer_enabled = False - self.shutdown_timeout_label.hide() - self.shutdown_timeout.hide() - def shutdown_timeout_reset(self): """ Reset the timeout in the UI after stopping a share @@ -146,10 +130,19 @@ class ServerStatus(QtWidgets.QWidget): if self.status == self.STATUS_STARTED: self.url_description.show() - if self.settings.get('close_after_first_download'): - self.url_label.setText(strings._('gui_url_label_one_time', True)) + info_image = common.get_resource_path('images/info.png') + self.url_label.setText(strings._('gui_url_label', True).format(info_image)) + # Show a Tool Tip explaining the lifecycle of this URL + if self.settings.get('save_private_key'): + if self.settings.get('close_after_first_download'): + self.url_label.setToolTip(strings._('gui_url_label_onetime_and_persistent', True)) + else: + self.url_label.setToolTip(strings._('gui_url_label_persistent', True)) else: - self.url_label.setText(strings._('gui_url_label', True)) + if self.settings.get('close_after_first_download'): + self.url_label.setToolTip(strings._('gui_url_label_onetime', True)) + else: + self.url_label.setToolTip(strings._('gui_url_label_stay_open', True)) self.url_label.show() self.url.setText('http://{0:s}/{1:s}'.format(self.app.onion_host, self.web.slug)) @@ -213,7 +206,7 @@ class ServerStatus(QtWidgets.QWidget): Toggle starting or stopping the server. """ if self.status == self.STATUS_STOPPED: - if self.timer_enabled: + if self.settings.get('shutdown_timeout'): # Get the timeout chosen, stripped of its seconds. This prevents confusion if the share stops at (say) 37 seconds past the minute chosen self.timeout = self.shutdown_timeout.dateTime().toPyDateTime().replace(second=0, microsecond=0) # If the timeout has actually passed already before the user hit Start, refuse to start the server. @@ -227,6 +220,7 @@ class ServerStatus(QtWidgets.QWidget): self.stop_server() elif self.status == self.STATUS_WORKING: self.cancel_server() + self.button_clicked.emit() def start_server(self): """ diff --git a/onionshare_gui/settings_dialog.py b/onionshare_gui/settings_dialog.py index d105ea42..ba9b26ed 100644 --- a/onionshare_gui/settings_dialog.py +++ b/onionshare_gui/settings_dialog.py @@ -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)) diff --git a/share/html/404.html b/share/html/404.html index 2e2b06f0..09d0fc3c 100644 --- a/share/html/404.html +++ b/share/html/404.html @@ -2,6 +2,7 @@ Error 404 + -

{{ filename }} ▼

-

{{ filesize_human }} (compressed)

-

This zip file contains the following contents:

+ +
+
+ +
+ +

OnionShare

+
+ - - + + {% for info in file_info.dirs %} - - + + {% endfor %} {% for info in file_info.files %} - - + + {% endfor %}
TypeNameFilename Size
{{ info.basename }} + + {{ info.basename }} + {{ info.size_human }}
{{ info.basename }} + + {{ info.basename }} + {{ info.size_human }}
diff --git a/share/images/favicon.ico b/share/images/favicon.ico new file mode 100644 index 00000000..63e65d8b Binary files /dev/null and b/share/images/favicon.ico differ diff --git a/share/images/info.png b/share/images/info.png new file mode 100644 index 00000000..4be4e65e Binary files /dev/null and b/share/images/info.png differ diff --git a/share/images/settings_inactive.png b/share/images/settings_inactive.png deleted file mode 100644 index 1b35201b..00000000 Binary files a/share/images/settings_inactive.png and /dev/null differ diff --git a/share/images/web_file.png b/share/images/web_file.png new file mode 100644 index 00000000..1931aff0 Binary files /dev/null and b/share/images/web_file.png differ diff --git a/share/images/web_folder.png b/share/images/web_folder.png new file mode 100644 index 00000000..3ca5df21 Binary files /dev/null and b/share/images/web_folder.png differ diff --git a/share/locale/en.json b/share/locale/en.json index fd112845..d703fd0c 100644 --- a/share/locale/en.json +++ b/share/locale/en.json @@ -13,9 +13,9 @@ "no_available_port": "Could not start the Onion service as there was no available port.", "download_page_loaded": "Download page loaded", "other_page_loaded": "Address loaded", - "close_on_timeout": "Closing automatically because timeout was reached", - "closing_automatically": "Closing automatically because download finished", - "timeout_download_still_running": "Waiting for download to complete before auto-stopping", + "close_on_timeout": "Stopped because timer expired", + "closing_automatically": "Stopped because download finished", + "timeout_download_still_running": "Waiting for download to complete", "large_filesize": "Warning: Sending large files could take hours", "error_tails_invalid_port": "Invalid value, port must be an integer", "error_tails_unknown_root": "Unknown error with Tails root process", @@ -44,8 +44,10 @@ "gui_copy_hidservauth": "Copy HidServAuth", "gui_downloads": "Downloads:", "gui_canceled": "Canceled", - "gui_copied_url": "Copied address to clipboard", - "gui_copied_hidservauth": "Copied HidServAuth line to clipboard", + "gui_copied_url_title": "Copied OnionShare address", + "gui_copied_url": "The OnionShare address has been copied to clipboard", + "gui_copied_hidservauth_title": "Copied HidServAuth", + "gui_copied_hidservauth": "The HidServAuth line has been copied to clipboard", "gui_starting_server1": "Starting Tor onion service...", "gui_starting_server2": "Crunching files...", "gui_please_wait": "Starting... Click to cancel", @@ -133,11 +135,15 @@ "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": "Anyone with this link can download your files using Tor Browser:", - "gui_url_label": "Your Download Address", - "gui_url_label_one_time": "Your One-Time Download Address", + "gui_url_label": "Your Download Address ", + "gui_url_label_persistent": "This share will not stop automatically

Every share will have the same address (to use one-time addresses, disable persistence in the Settings)", + "gui_url_label_stay_open": "This share will not stop automatically", + "gui_url_label_onetime": "This share will stop after the first download", + "gui_url_label_onetime_and_persistent": "This share will stop after the first download

Every share will have the same address (to use one-time addresses, disable 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, {}", + "gui_file_info_single": "{} File, {}" } diff --git a/test/conftest.py b/test/conftest.py index 0a3bc806..8f10162b 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -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