Merge pull request #1020 from micahflee/991_sharing_code

[WIP] Share code between share mode and website mode
This commit is contained in:
Micah Lee 2019-09-15 14:52:57 -07:00 committed by GitHub
commit 51a1f92d99
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
35 changed files with 1063 additions and 396 deletions

View file

@ -22,6 +22,8 @@ from PyQt5 import QtCore, QtWidgets, QtGui
from onionshare import strings
from onionshare.common import AutoStopTimer
from .history import IndividualFileHistoryItem
from ..server_status import ServerStatus
from ..threads import OnionThread
from ..threads import AutoStartTimer
@ -29,7 +31,7 @@ from ..widgets import Alert
class Mode(QtWidgets.QWidget):
"""
The class that ShareMode and ReceiveMode inherit from.
The class that all modes inherit from
"""
start_server_finished = QtCore.pyqtSignal()
stop_server_finished = QtCore.pyqtSignal()
@ -417,3 +419,32 @@ class Mode(QtWidgets.QWidget):
Handle REQUEST_UPLOAD_CANCELED event.
"""
pass
def handle_request_individual_file_started(self, event):
"""
Handle REQUEST_INDVIDIDUAL_FILES_STARTED event.
Used in both Share and Website modes, so implemented here.
"""
self.toggle_history.update_indicator(True)
self.history.requests_count += 1
self.history.update_requests()
item = IndividualFileHistoryItem(self.common, event["data"], event["path"])
self.history.add(event["data"]["id"], item)
def handle_request_individual_file_progress(self, event):
"""
Handle REQUEST_INDVIDIDUAL_FILES_PROGRESS event.
Used in both Share and Website modes, so implemented here.
"""
self.history.update(event["data"]["id"], event["data"]["bytes"])
if self.server_status.status == self.server_status.STATUS_STOPPED:
self.history.cancel(event["data"]["id"])
def handle_request_individual_file_canceled(self, event):
"""
Handle REQUEST_INDVIDIDUAL_FILES_CANCELED event.
Used in both Share and Website modes, so implemented here.
"""
self.history.cancel(event["data"]["id"])

View file

@ -237,6 +237,7 @@ class ReceiveHistoryItemFile(QtWidgets.QWidget):
elif self.common.platform == 'Windows':
subprocess.Popen(['explorer', '/select,{}'.format(abs_filename)])
class ReceiveHistoryItem(HistoryItem):
def __init__(self, common, id, content_length):
super(ReceiveHistoryItem, self).__init__()
@ -341,35 +342,108 @@ class ReceiveHistoryItem(HistoryItem):
self.label.setText(self.get_canceled_label_text(self.started))
class VisitHistoryItem(HistoryItem):
class IndividualFileHistoryItem(HistoryItem):
"""
Download history item, for share mode
Individual file history item, for share mode viewing of individual files
"""
def __init__(self, common, id, total_bytes):
super(VisitHistoryItem, self).__init__()
def __init__(self, common, data, path):
super(IndividualFileHistoryItem, self).__init__()
self.status = HistoryItem.STATUS_STARTED
self.common = common
self.id = id
self.visited = time.time()
self.visited_dt = datetime.fromtimestamp(self.visited)
self.path = path
self.total_bytes = 0
self.downloaded_bytes = 0
self.started = time.time()
self.started_dt = datetime.fromtimestamp(self.started)
self.status = HistoryItem.STATUS_STARTED
# Label
self.label = QtWidgets.QLabel(strings._('gui_visit_started').format(self.visited_dt.strftime("%b %d, %I:%M%p")))
self.directory_listing = 'directory_listing' in data
# Labels
self.timestamp_label = QtWidgets.QLabel(self.started_dt.strftime("%b %d, %I:%M%p"))
self.timestamp_label.setStyleSheet(self.common.css['history_individual_file_timestamp_label'])
self.path_label = QtWidgets.QLabel("{}".format(self.path))
self.status_code_label = QtWidgets.QLabel()
# Progress bar
self.progress_bar = QtWidgets.QProgressBar()
self.progress_bar.setTextVisible(True)
self.progress_bar.setAttribute(QtCore.Qt.WA_DeleteOnClose)
self.progress_bar.setAlignment(QtCore.Qt.AlignHCenter)
self.progress_bar.setValue(0)
self.progress_bar.setStyleSheet(self.common.css['downloads_uploads_progress_bar'])
# Text layout
labels_layout = QtWidgets.QHBoxLayout()
labels_layout.addWidget(self.timestamp_label)
labels_layout.addWidget(self.path_label)
labels_layout.addWidget(self.status_code_label)
labels_layout.addStretch()
# Layout
layout = QtWidgets.QVBoxLayout()
layout.addWidget(self.label)
layout.addLayout(labels_layout)
layout.addWidget(self.progress_bar)
self.setLayout(layout)
def update(self):
self.label.setText(self.get_finished_label_text(self.started_dt))
self.status = HistoryItem.STATUS_FINISHED
# Is a status code already sent?
if 'status_code' in data:
self.status_code_label.setText("{}".format(data['status_code']))
if data['status_code'] >= 200 and data['status_code'] < 300:
self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_2xx'])
if data['status_code'] >= 400 and data['status_code'] < 500:
self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_4xx'])
self.status = HistoryItem.STATUS_FINISHED
self.progress_bar.hide()
return
else:
self.total_bytes = data['filesize']
self.progress_bar.setMinimum(0)
self.progress_bar.setMaximum(data['filesize'])
self.progress_bar.total_bytes = data['filesize']
# Start at 0
self.update(0)
def update(self, downloaded_bytes):
self.downloaded_bytes = downloaded_bytes
self.progress_bar.setValue(downloaded_bytes)
if downloaded_bytes == self.progress_bar.total_bytes:
self.status_code_label.setText("200")
self.status_code_label.setStyleSheet(self.common.css['history_individual_file_status_code_label_2xx'])
self.progress_bar.hide()
self.status = HistoryItem.STATUS_FINISHED
else:
elapsed = time.time() - self.started
if elapsed < 10:
# Wait a couple of seconds for the download rate to stabilize.
# This prevents a "Windows copy dialog"-esque experience at
# the beginning of the download.
pb_fmt = strings._('gui_all_modes_progress_starting').format(
self.common.human_readable_filesize(downloaded_bytes))
else:
pb_fmt = strings._('gui_all_modes_progress_eta').format(
self.common.human_readable_filesize(downloaded_bytes),
self.estimated_time_remaining)
self.progress_bar.setFormat(pb_fmt)
def cancel(self):
self.progress_bar.setFormat(strings._('gui_canceled'))
self.status = HistoryItem.STATUS_CANCELED
@property
def estimated_time_remaining(self):
return self.common.estimated_time_remaining(self.downloaded_bytes,
self.total_bytes,
self.started)
class HistoryItemList(QtWidgets.QScrollArea):
"""
List of items
@ -452,26 +526,30 @@ class History(QtWidgets.QWidget):
# In progress and completed counters
self.in_progress_count = 0
self.completed_count = 0
self.requests_count = 0
# In progress and completed labels
# In progress, completed, and requests labels
self.in_progress_label = QtWidgets.QLabel()
self.in_progress_label.setStyleSheet(self.common.css['mode_info_label'])
self.completed_label = QtWidgets.QLabel()
self.completed_label.setStyleSheet(self.common.css['mode_info_label'])
self.requests_label = QtWidgets.QLabel()
self.requests_label.setStyleSheet(self.common.css['mode_info_label'])
# Header
self.header_label = QtWidgets.QLabel(header_text)
self.header_label.setStyleSheet(self.common.css['downloads_uploads_label'])
clear_button = QtWidgets.QPushButton(strings._('gui_all_modes_clear_history'))
clear_button.setStyleSheet(self.common.css['downloads_uploads_clear'])
clear_button.setFlat(True)
clear_button.clicked.connect(self.reset)
self.clear_button = QtWidgets.QPushButton(strings._('gui_all_modes_clear_history'))
self.clear_button.setStyleSheet(self.common.css['downloads_uploads_clear'])
self.clear_button.setFlat(True)
self.clear_button.clicked.connect(self.reset)
header_layout = QtWidgets.QHBoxLayout()
header_layout.addWidget(self.header_label)
header_layout.addStretch()
header_layout.addWidget(self.in_progress_label)
header_layout.addWidget(self.completed_label)
header_layout.addWidget(clear_button)
header_layout.addWidget(self.requests_label)
header_layout.addWidget(self.clear_button)
# When there are no items
self.empty_image = QtWidgets.QLabel()
@ -549,14 +627,18 @@ class History(QtWidgets.QWidget):
self.completed_count = 0
self.update_completed()
# Reset web requests counter
self.requests_count = 0
self.update_requests()
def update_completed(self):
"""
Update the 'completed' widget.
"""
if self.completed_count == 0:
image = self.common.get_resource_path('images/share_completed_none.png')
image = self.common.get_resource_path('images/history_completed_none.png')
else:
image = self.common.get_resource_path('images/share_completed.png')
image = self.common.get_resource_path('images/history_completed.png')
self.completed_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.completed_count))
self.completed_label.setToolTip(strings._('history_completed_tooltip').format(self.completed_count))
@ -564,14 +646,25 @@ class History(QtWidgets.QWidget):
"""
Update the 'in progress' widget.
"""
if self.mode != 'website':
if self.in_progress_count == 0:
image = self.common.get_resource_path('images/share_in_progress_none.png')
else:
image = self.common.get_resource_path('images/share_in_progress.png')
if self.in_progress_count == 0:
image = self.common.get_resource_path('images/history_in_progress_none.png')
else:
image = self.common.get_resource_path('images/history_in_progress.png')
self.in_progress_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.in_progress_count))
self.in_progress_label.setToolTip(strings._('history_in_progress_tooltip').format(self.in_progress_count))
self.in_progress_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.in_progress_count))
self.in_progress_label.setToolTip(strings._('history_in_progress_tooltip').format(self.in_progress_count))
def update_requests(self):
"""
Update the 'web requests' widget.
"""
if self.requests_count == 0:
image = self.common.get_resource_path('images/history_requests_none.png')
else:
image = self.common.get_resource_path('images/history_requests.png')
self.requests_label.setText('<img src="{0:s}" /> {1:d}'.format(image, self.requests_count))
self.requests_label.setToolTip(strings._('history_requests_tooltip').format(self.requests_count))
class ToggleHistory(QtWidgets.QPushButton):
@ -604,7 +697,7 @@ class ToggleHistory(QtWidgets.QPushButton):
def update_indicator(self, increment=False):
"""
Update the display of the indicator count. If increment is True, then
only increment the counter if Downloads is hidden.
only increment the counter if History is hidden.
"""
if increment and not self.history_widget.isVisible():
self.indicator_count += 1

View file

@ -97,7 +97,7 @@ class ReceiveMode(Mode):
The auto-stop timer expired, should we stop the server? Returns a bool
"""
# If there were no attempts to upload files, or all uploads are done, we can stop
if self.web.receive_mode.upload_count == 0 or not self.web.receive_mode.uploads_in_progress:
if self.web.receive_mode.cur_history_id == 0 or not self.web.receive_mode.uploads_in_progress:
self.server_status.stop_server()
self.server_status_label.setText(strings._('close_on_autostop_timer'))
return True
@ -112,7 +112,7 @@ class ReceiveMode(Mode):
Starting the server.
"""
# Reset web counters
self.web.receive_mode.upload_count = 0
self.web.receive_mode.cur_history_id = 0
self.web.reset_invalid_passwords()
# Hide and reset the uploads if we have previously shared
@ -212,6 +212,8 @@ class ReceiveMode(Mode):
Set the info counters back to zero.
"""
self.history.reset()
self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator()
def update_primary_action(self):
self.common.log('ReceiveMode', 'update_primary_action')

View file

@ -132,7 +132,7 @@ class ShareMode(Mode):
The auto-stop timer expired, should we stop the server? Returns a bool
"""
# If there were no attempts to download the share, or all downloads are done, we can stop
if self.web.share_mode.download_count == 0 or self.web.done:
if self.web.share_mode.cur_history_id == 0 or self.web.done:
self.server_status.stop_server()
self.server_status_label.setText(strings._('close_on_autostop_timer'))
return True
@ -146,7 +146,7 @@ class ShareMode(Mode):
Starting the server.
"""
# Reset web counters
self.web.share_mode.download_count = 0
self.web.share_mode.cur_history_id = 0
self.web.reset_invalid_passwords()
# Hide and reset the downloads if we have previously shared
@ -225,12 +225,6 @@ class ShareMode(Mode):
"""
self.primary_action.hide()
def handle_request_load(self, event):
"""
Handle REQUEST_LOAD event.
"""
self.system_tray.showMessage(strings._('systray_page_loaded_title'), strings._('systray_page_loaded_message'))
def handle_request_started(self, event):
"""
Handle REQUEST_STARTED event.
@ -325,6 +319,8 @@ class ShareMode(Mode):
Set the info counters back to zero.
"""
self.history.reset()
self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator()
@staticmethod
def _compute_total_size(filenames):

View file

@ -41,12 +41,8 @@ class CompressThread(QtCore.QThread):
self.mode.common.log('CompressThread', 'run')
try:
if self.mode.web.share_mode.set_file_info(self.mode.filenames, processed_size_callback=self.set_processed_size):
self.success.emit()
else:
# Cancelled
pass
self.mode.web.share_mode.set_file_info(self.mode.filenames, processed_size_callback=self.set_processed_size)
self.success.emit()
self.mode.app.cleanup_filenames += self.mode.web.share_mode.cleanup_filenames
except OSError as e:
self.error.emit(e.strerror)

View file

@ -30,7 +30,7 @@ from onionshare.web import Web
from ..file_selection import FileSelection
from .. import Mode
from ..history import History, ToggleHistory, VisitHistoryItem
from ..history import History, ToggleHistory
from ...widgets import Alert
class WebsiteMode(Mode):
@ -80,6 +80,8 @@ class WebsiteMode(Mode):
strings._('gui_all_modes_history'),
'website'
)
self.history.in_progress_label.hide()
self.history.completed_label.hide()
self.history.hide()
# Info label
@ -165,12 +167,8 @@ class WebsiteMode(Mode):
Step 3 in starting the server. Display large filesize
warning, if applicable.
"""
if self.web.website_mode.set_file_info(self.filenames):
self.success.emit()
else:
# Cancelled
pass
self.web.website_mode.set_file_info(self.filenames)
self.success.emit()
def start_server_error_custom(self):
"""
@ -208,21 +206,6 @@ class WebsiteMode(Mode):
"""
self.system_tray.showMessage(strings._('systray_site_loaded_title'), strings._('systray_site_loaded_message'))
def handle_request_started(self, event):
"""
Handle REQUEST_STARTED event.
"""
if ( (event["path"] == '') or (event["path"].find(".htm") != -1 ) ):
item = VisitHistoryItem(self.common, event["data"]["id"], 0)
self.history.add(event["data"]["id"], item)
self.toggle_history.update_indicator(True)
self.history.completed_count += 1
self.history.update_completed()
self.system_tray.showMessage(strings._('systray_website_started_title'), strings._('systray_website_started_message'))
def on_reload_settings(self):
"""
If there were some files listed for sharing, we should be ok to re-enable
@ -262,6 +245,8 @@ class WebsiteMode(Mode):
Set the info counters back to zero.
"""
self.history.reset()
self.toggle_history.indicator_count = 0
self.toggle_history.update_indicator()
@staticmethod
def _compute_total_size(filenames):

View file

@ -383,7 +383,7 @@ class OnionShareGui(QtWidgets.QMainWindow):
self.share_mode.server_status.autostart_timer_container.hide()
self.receive_mode.server_status.autostart_timer_container.hide()
self.website_mode.server_status.autostart_timer_container.hide()
d = SettingsDialog(self.common, self.onion, self.qtapp, self.config, self.local_only)
d.settings_saved.connect(reload_settings)
d.exec_()
@ -470,6 +470,15 @@ class OnionShareGui(QtWidgets.QMainWindow):
elif event["type"] == Web.REQUEST_UPLOAD_CANCELED:
mode.handle_request_upload_canceled(event)
elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_STARTED:
mode.handle_request_individual_file_started(event)
elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_PROGRESS:
mode.handle_request_individual_file_progress(event)
elif event["type"] == Web.REQUEST_INDIVIDUAL_FILE_CANCELED:
mode.handle_request_individual_file_canceled(event)
if event["type"] == Web.REQUEST_ERROR_DATA_DIR_CANNOT_CREATE:
Alert(self.common, strings._('error_cannot_create_data_dir').format(event["data"]["receive_mode_dir"]))

View file

@ -212,10 +212,12 @@ class SettingsDialog(QtWidgets.QDialog):
self.close_after_first_download_checkbox = QtWidgets.QCheckBox()
self.close_after_first_download_checkbox.setCheckState(QtCore.Qt.Checked)
self.close_after_first_download_checkbox.setText(strings._("gui_settings_close_after_first_download_option"))
individual_downloads_label = QtWidgets.QLabel(strings._("gui_settings_individual_downloads_label"))
# Sharing options layout
sharing_group_layout = QtWidgets.QVBoxLayout()
sharing_group_layout.addWidget(self.close_after_first_download_checkbox)
sharing_group_layout.addWidget(individual_downloads_label)
sharing_group = QtWidgets.QGroupBox(strings._("gui_settings_sharing_label"))
sharing_group.setLayout(sharing_group_layout)
@ -638,7 +640,6 @@ class SettingsDialog(QtWidgets.QDialog):
self.connect_to_tor_label.show()
self.onion_settings_widget.hide()
def connection_type_bundled_toggled(self, checked):
"""
Connection type bundled was toggled. If checked, hide authentication fields.