Example #1
0
 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)
Example #2
0
 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
Example #3
0
 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)
Example #4
0
 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)
Example #5
0
 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
Example #6
0
 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()
Example #11
0
    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)
Example #12
0
    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()
Example #13
0
    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"))
Example #15
0
    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)
Example #16
0
    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
Example #17
0
 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)
Example #18
0
 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,
     )
Example #19
0
 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)
Example #20
0
 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"))
Example #21
0
 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,
     )
Example #22
0
 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()
Example #23
0
 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)
Example #24
0
 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)
Example #25
0
 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)
Example #26
0
 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)
Example #29
0
    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,
        )
Example #30
0
 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])