def show_context_menu(self, pos): if self.is_revoked or self.is_current_user or not self.current_user_is_admin: return global_pos = self.mapToGlobal(pos) menu = QMenu(self) action = menu.addAction(_("ACTION_USER_MENU_REVOKE")) action.triggered.connect(self.revoke) menu.exec_(global_pos)
def exec_modal(cls, jobs_ctx, config, addr, parent): w = cls(jobs_ctx=jobs_ctx, config=config, addr=addr) d = GreyedDialog(w, _("TEXT_CLAIM_USER_TITLE"), parent=parent) w.dialog = d w.line_edit_token.setFocus() if d.exec_() == QDialog.Accepted and w.status: return w.status return None
def show_context_menu(self, pos): if not self.is_current_device: return global_pos = self.mapToGlobal(pos) menu = QMenu(self) action = menu.addAction(_("ACTION_DEVICE_MENU_CHANGE_PASSWORD")) action.triggered.connect(self.change_password) menu.exec_(global_pos)
def show_mount_widget(self, user_info=None): self.clear_widgets() self.menu.activate_files() self.label_title.setText(_("ACTION_MENU_DOCUMENTS")) if user_info is not None: self.mount_widget.workspaces_widget.set_user_info(user_info) self.mount_widget.show() self.mount_widget.show_workspaces_widget(user_info=user_info)
def delete_workspace(self, workspace_fs): if isinstance(workspace_fs, WorkspaceFSTimestamped): self.unmount_workspace(workspace_fs.workspace_id, workspace_fs.timestamp) return else: workspace_name = workspace_fs.get_workspace_name() result = ask_question( self, _("TEXT_WORKSPACE_DELETE_TITLE"), _("TEXT_WORKSPACE_DELETE_INSTRUCTIONS_workspace").format( workspace=workspace_name), [_("ACTION_DELETE_WORKSPACE_CONFIRM"), _("ACTION_CANCEL")], ) if result != _("ACTION_DELETE_WORKSPACE_CONFIRM"): return
def on_tab_state_changed(self, tab, state): idx = self.tab_center.indexOf(tab) if idx == -1: return if state == "login": if self._get_login_tab_index() != -1: self.tab_center.removeTab(idx) else: self.tab_center.setTabToolTip(idx, _("TEXT_TAB_TITLE_LOG_IN_SCREEN")) self.tab_center.setTabText(idx, _("TEXT_TAB_TITLE_LOG_IN_SCREEN")) elif state == "connected": device = tab.current_device tab_name = f"{device.organization_id}:{device.user_id}@{device.device_name}" self.tab_center.setTabToolTip(idx, tab_name) self.tab_center.setTabText(idx, tab_name) if self.tab_center.count() == 1: self.tab_center.setTabsClosable(False)
def _on_button_start_clicked(self): self.button_start.setDisabled(True) self.button_start.setText(_("TEXT_CLAIM_DEVICE_WAITING")) self.wait_peer_job = self.jobs_ctx.submit_job( ThreadSafeQtSignal(self, "wait_peer_success", QtToTrioJob), ThreadSafeQtSignal(self, "wait_peer_error", QtToTrioJob), self.claimer.wait_peer, )
def try_login(self): if not self.combo_username.currentText(): return available_device = self.devices[self.combo_username.currentText()] self.button_login.setDisabled(True) self.button_login.setText(_("ACTION_LOGGING_IN")) self.login_with_password_clicked.emit(available_device.key_file_path, self.line_edit_password.text())
def _on_unshare_error(self, job): exc = job.exc show_error( self, _("TEXT_WORKSPACE_SHARING_UNSHARE_ERROR_workspace-user").format( workspace=exc.params.get("workspace_name"), user=exc.params.get("user") ), )
def _on_get_users_error(self, job): assert job.is_finished() assert job.status != "ok" if job.status == "offline": show_error(self, _("TEXT_WORKSPACE_SHARING_OFFLINE")) self.spinner.hide() self.widget_users.show()
def show_context_menu(self, pos): global_pos = self.mapToGlobal(pos) menu = QMenu(self) if sys.platform == "darwin": action = menu.addAction(_("ACTION_WORKSPACE_OPEN_IN_FINDER")) else: action = menu.addAction( _("ACTION_WORKSPACE_OPEN_IN_FILE_EXPLORER")) action.triggered.connect(self.button_open_workspace_clicked) if not self.timestamped: action = menu.addAction(_("ACTION_WORKSPACE_RENAME")) action.triggered.connect(self.button_rename_clicked) action = menu.addAction(_("ACTION_WORKSPACE_SHARE")) action.triggered.connect(self.button_share_clicked) action = menu.addAction(_("ACTION_WORKSPACE_SEE_IN_THE_PAST")) action.triggered.connect(self.button_remount_ts_clicked) if self.reencryption_needs and self.reencryption_needs.need_reencryption: action = menu.addAction(_("ACTION_WORKSPACE_REENCRYPT")) action.triggered.connect(self.button_reencrypt_clicked) else: action = menu.addAction(_("ACTION_WORKSPACE_DELETE")) action.triggered.connect(self.button_delete_clicked) menu.exec_(global_pos)
def _on_req_error(self): assert self.req_job assert self.req_job.is_finished() assert self.req_job.status != "ok" status = self.req_job.status if status == "cancelled": return errmsg = None if status == "email_already_exists": errmsg = _("TEXT_ORG_WIZARD_EMAIL_ALREADY_EXISTS") elif status == "organization_already_exists": errmsg = _("TEXT_ORG_WIZARD_ORGANIZATION_ALREADY_EXISTS") elif status == "invalid_email": errmsg = _("TEXT_ORG_WIZARD_INVALID_EMAIL") elif status == "invalid_organization_id": errmsg = _("TEXT_ORG_WIZARD_INVALID_ORGANIZATION_ID") elif status == "invalid_response": errmsg = _("TEXT_ORG_WIZARD_INVALID_RESPONSE") elif status == "offline": errmsg = _("TEXT_ORG_WIZARD_OFFLINE") else: errmsg = _("TEXT_ORG_WIZARD_UNKNOWN_FAILURE") exc = self.req_job.exc if exc.params.get("exc"): exc = exc.params.get("exc") show_error(self, errmsg, exception=exc) self.req_job = None self.button_validate.setEnabled(True) self.button_previous.show()
def _on_invite_user_success(self, job): assert job.is_finished() assert job.status == "ok" email, invitation_addr, email_sent_status = job.ret if email_sent_status == InvitationEmailSentStatus.SUCCESS: SnackbarManager.inform( _("TEXT_USER_INVITE_SUCCESS_email").format(email=email)) elif email_sent_status == InvitationEmailSentStatus.BAD_RECIPIENT: show_info_copy_link( self, _("TEXT_EMAIL_FAILED_TO_SEND_TITLE"), _("TEXT_INVITE_USER_EMAIL_BAD_RECIPIENT_directlink").format( directlink=invitation_addr), _("ACTION_COPY_ADDR"), str(invitation_addr), ) else: show_info_copy_link( self, _("TEXT_EMAIL_FAILED_TO_SEND_TITLE"), _("TEXT_INVITE_USER_EMAIL_NOT_AVAILABLE_directlink").format( directlink=invitation_addr), _("ACTION_COPY_ADDR"), str(invitation_addr), ) self.reset()
def goto_file_clicked(self): file_link = get_text_input( self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_TITLE"), _("TEXT_WORKSPACE_GOTO_FILE_LINK_INSTRUCTIONS"), placeholder=_("TEXT_WORKSPACE_GOTO_FILE_LINK_PLACEHOLDER"), default_text="", button_text=_("ACTION_GOTO_FILE_LINK"), ) if not file_link: return try: addr = BackendOrganizationFileLinkAddr.from_url(file_link, allow_http_redirection=True) except ValueError as exc: show_error(self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_INVALID_LINK"), exception=exc) return button = self.get_workspace_button(addr.workspace_id) if button is not None: try: path = button.workspace_fs.decrypt_file_link_path(addr) except ValueError as exc: show_error(self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_INVALID_LINK"), exception=exc) return self.load_workspace(button.workspace_fs, path=path, selected=True) return show_error(self, _("TEXT_WORKSPACE_GOTO_FILE_LINK_WORKSPACE_NOT_FOUND"))
def go_to_file_link(self, addr: BackendOrganizationFileLinkAddr) -> None: # Try to use the file link on the already logged in cores for idx in range(self.tab_center.count()): if self.tab_center.tabText(idx) == _("TEXT_TAB_TITLE_LOG_IN_SCREEN"): continue w = self.tab_center.widget(idx) if ( not w or not w.core or w.core.device.organization_addr.organization_id != addr.organization_id ): continue central_widget = w.get_central_widget() if not central_widget: continue try: central_widget.go_to_file_link(addr) except GoToFileLinkBadOrganizationIDError: continue except GoToFileLinkBadWorkspaceIDError: # Switch tab so user understand where the error comes from self.switch_to_tab(idx) show_error( self, _("TEXT_FILE_LINK_WORKSPACE_NOT_FOUND_organization").format( organization=addr.organization_id ), ) return except GoToFileLinkPathDecryptionError: # Switch tab so user understand where the error comes from self.switch_to_tab(idx) show_error(self, _("TEXT_INVALID_URL")) return else: self.switch_to_tab(idx) return # The file link is from an organization we'r not currently logged in # or we don't have any device related to self.switch_to_login_tab(addr)
def _on_join_org_clicked(self): url = get_text_input( parent=self, title=_("TEXT_JOIN_ORG_URL_TITLE"), message=_("TEXT_JOIN_ORG_URL_INSTRUCTIONS"), placeholder=_("TEXT_JOIN_ORG_URL_PLACEHOLDER"), ) if url is None: return elif url == "": show_error(self, _("TEXT_JOIN_ORG_INVALID_URL")) return action_addr = None try: action_addr = BackendActionAddr.from_url(url) except ValueError as exc: show_error(self, _("TEXT_INVALID_URL"), exception=exc) return if isinstance(action_addr, BackendOrganizationBootstrapAddr): self._on_create_org_clicked(action_addr) elif isinstance(action_addr, BackendInvitationAddr): if action_addr.invitation_type == InvitationType.USER: self._on_claim_user_clicked(action_addr) elif action_addr.invitation_type == InvitationType.DEVICE: self._on_claim_device_clicked(action_addr) else: show_error(self, _("TEXT_INVALID_URL")) return else: show_error(self, _("TEXT_INVALID_URL")) return
def _on_get_claimer_sas_error(self, job): if self.get_claimer_sas_job != job: return self.get_claimer_sas_job = None assert job assert job.is_finished() assert job.status != "ok" if job.status != "cancelled": msg = _("TEXT_GREET_DEVICE_GET_CLAIMER_SAS_ERROR") exc = None if job.exc: exc = job.exc.params.get("origin", None) if isinstance(exc, InvitePeerResetError): msg = _("TEXT_GREET_DEVICE_PEER_RESET") elif isinstance(exc, InviteAlreadyUsedError): msg = _("TEXT_INVITATION_ALREADY_USED") show_error(self, msg, exception=exc) self.failed.emit(job)
def create_workspace_clicked(self): workspace_name = get_text_input( parent=self, title=_("TEXT_WORKSPACE_NEW_TITLE"), message=_("TEXT_WORKSPACE_NEW_INSTRUCTIONS"), placeholder=_("TEXT_WORKSPACE_NEW_PLACEHOLDER"), button_text=_("ACTION_WORKSPACE_NEW_CREATE"), validator=validators.WorkspaceNameValidator(), ) if not workspace_name: return self.jobs_ctx.submit_job( ThreadSafeQtSignal(self, "create_success", QtToTrioJob), ThreadSafeQtSignal(self, "create_error", QtToTrioJob), _do_workspace_create, core=self.core, workspace_name=workspace_name, )
def _wait_status_shown(): assert file_status_w.label_location.text().endswith(str(Path("/foo/bar.txt"))) assert file_status_w.label_workspace.text() == "wksp1" assert file_status_w.label_filetype.text() == "file" assert file_status_w.label_size.text() == "0 B" assert file_status_w.label_created_on.text() != "" and file_status_w.label_created_on.text() != _( "TEXT_FILE_INFO_NON_APPLICABLE" ) assert file_status_w.label_created_by.text() == "Boby McBobFace" assert file_status_w.label_last_updated_on.text() != "" and file_status_w.label_last_updated_on.text() != _( "TEXT_FILE_INFO_NON_APPLICABLE" ) assert file_status_w.label_last_updated_by.text() == "Boby McBobFace" assert file_status_w.label_availability.text() == _("TEXT_YES") assert file_status_w.label_uploaded.text() == _("TEXT_YES") assert file_status_w.label_local.text() == "0/0 (100%)" assert file_status_w.label_remote.text() == "0/0 (100%)" assert file_status_w.label_default_block_size.text() == get_filesize(DEFAULT_BLOCK_SIZE)
def on_get_file_path_clicked(self): files = self.table_files.selected_files() if len(files) != 1: return path = self.current_directory / files[0].name addr = self.jobs_ctx.run_sync(self.workspace_fs.generate_file_link, path) desktop.copy_to_clipboard(addr.to_url()) show_info(self, _("TEXT_FILE_LINK_COPIED_TO_CLIPBOARD"))
def revoke_user(self, user_info): result = ask_question( self, _("TEXT_USER_REVOCATION_TITLE"), _("TEXT_USER_REVOCATION_INSTRUCTIONS_user").format( user=user_info.short_user_display), [_("ACTION_USER_REVOCATION_CONFIRM"), _("ACTION_CANCEL")], ) if result != _("ACTION_USER_REVOCATION_CONFIRM"): return self.jobs_ctx.submit_job( ThreadSafeQtSignal(self, "revoke_success", QtToTrioJob), ThreadSafeQtSignal(self, "revoke_error", QtToTrioJob), _do_revoke_user, core=self.core, user_info=user_info, )
def __init__(self, message, dialog=None, button_text=None): super().__init__() self.setupUi(self) self.dialog = dialog self.label_message.setText(message) self.label_icon.apply_style() self.button_ok.setText(_("ACTION_CONTINUE") or button_text) self.button_ok.clicked.connect(self._on_button_clicked) self.button_ok.setFocus()
def import_folder_clicked(self): path = QDialogInProcess.getExistingDirectory( self, _("TEXT_FILE_IMPORT_FOLDER"), self.default_import_path) if not path: return self.default_import_path = str(path) self.jobs_ctx.submit_job(self.import_success, self.import_error, self._do_import, [path], self.current_directory)
def import_files_clicked(self): paths, filters = QDialogInProcess.getOpenFileNames( self, _("TEXT_FILE_IMPORT_FILES"), self.default_import_path) if not paths: return self.default_import_path = str(pathlib.Path(paths[0]).parent) self.jobs_ctx.submit_job(self.import_success, self.import_error, self._do_import, paths, self.current_directory)
def _on_signify_trust_error(self, job): if job != self.signify_trust_job: return self.signify_trust_job = None assert job assert job.is_finished() assert job.status != "ok" if job.status != "cancelled": exc = None msg = _("TEXT_CLAIM_USER_SIGNIFY_TRUST_ERROR") if job.exc: exc = job.exc.params.get("origin", None) if isinstance(exc, InvitePeerResetError): msg = _("TEXT_CLAIM_USER_PEER_RESET") elif isinstance(exc, BackendInvitationAlreadyUsed): msg = _("TEXT_INVITATION_ALREADY_USED") show_error(self, msg, exception=exc) self.failed.emit(job)
def _on_wait_peer_trust_error(self, job): if self.wait_peer_trust_job != job: return self.wait_peer_trust_job = None assert job assert job.is_finished() assert job.status != "ok" if job.status != "cancelled": msg = _("TEXT_GREET_USER_WAIT_PEER_TRUST_ERROR") exc = None if job.exc: exc = job.exc.params.get("origin", None) if isinstance(exc, InvitePeerResetError): msg = _("TEXT_GREET_USER_PEER_RESET") elif isinstance(exc, InviteAlreadyUsedError): msg = _("TEXT_INVITATION_ALREADY_USED") show_error(self, msg, exception=exc) self.failed.emit(job)
def show_modal(cls, core, jobs_ctx, token, parent, on_finished): w = cls(core=core, jobs_ctx=jobs_ctx, token=token) d = GreyedDialog(w, _("TEXT_GREET_USER_TITLE"), parent=parent, width=1000) w.dialog = d d.finished.connect(on_finished) # Unlike exec_, show is asynchronous and works within the main Qt loop d.show() return w
def change_password(self): if self.line_edit_password.text() != self.line_edit_password_check.text(): show_error(self, _("TEXT_CHANGE_PASSWORD_PASSWORD_MISMATCH")) else: key_file = get_key_file(self.core.config.config_dir, self.core.device) try: change_device_password( key_file, self.line_edit_old_password.text(), self.line_edit_password.text() ) show_info(self, _("TEXT_CHANGE_PASSWORD_SUCCESS")) if self.dialog: self.dialog.accept() elif QApplication.activeModalWidget(): QApplication.activeModalWidget().accept() else: logger.warning("Cannot close dialog when changing password info") except LocalDeviceCryptoError as exc: show_error(self, _("TEXT_CHANGE_PASSWORD_INVALID_PASSWORD"), exception=exc)
def create_folder_clicked(self): folder_name = get_text_input( self, _("TEXT_FILE_CREATE_FOLDER_TITLE"), _("TEXT_FILE_CREATE_FOLDER_INSTRUCTIONS"), placeholder=_("TEXT_FILE_CREATE_FOLDER_PLACEHOLDER"), button_text=_("ACTION_FILE_CREATE_FOLDER"), ) if not folder_name: return self.jobs_ctx.submit_job( ThreadSafeQtSignal(self, "folder_create_success", QtToTrioJob), ThreadSafeQtSignal(self, "folder_create_error", QtToTrioJob), _do_folder_create, workspace_fs=self.workspace_fs, path=self.current_directory / folder_name, )
def open_files(self): files = self.table_files.selected_files() if len(files) == 1: self.open_file(files[0][2]) else: result = ask_question( self, _("TEXT_FILE_OPEN_MULTIPLE_TITLE_count").format( count=len(files)), _("TEXT_FILE_OPEN_MULTIPLE_INSTRUCTIONS_count").format( count=len(files)), [_("ACTION_FILE_OPEN_MULTIPLE"), _("ACTION_CANCEL")], ) if result != _("ACTION_FILE_OPEN_MULTIPLE"): return for f in files: self.open_file(f[2])