diff options
author | wurstsalat <mailtrash@posteo.de> | 2023-11-26 19:48:16 +0300 |
---|---|---|
committer | wurstsalat <mailtrash@posteo.de> | 2023-11-26 19:56:12 +0300 |
commit | 66c8ce2fd8a753073160d1adcceb9e9eca19760a (patch) | |
tree | 77c7e6ee1bc70bfce933aa37616693a255ed3932 | |
parent | 59f681ba7afeb89e2398aefde495a0f47988d966 (diff) |
imprv: Add DBMigration dialog
-rw-r--r-- | gajim/common/storage/archive/storage.py | 12 | ||||
-rw-r--r-- | gajim/data/gui/db_migration.ui | 219 | ||||
-rw-r--r-- | gajim/gtk/builder.pyi | 8 | ||||
-rw-r--r-- | gajim/gtk/const.py | 1 | ||||
-rw-r--r-- | gajim/gtk/db_migration.py | 93 |
5 files changed, 330 insertions, 3 deletions
diff --git a/gajim/common/storage/archive/storage.py b/gajim/common/storage/archive/storage.py index 51155bc9e..f3f15b486 100644 --- a/gajim/common/storage/archive/storage.py +++ b/gajim/common/storage/archive/storage.py @@ -33,7 +33,7 @@ from gajim.common.const import MAX_MESSAGE_CORRECTION_DELAY from gajim.common.modules.contacts import GroupchatContact from gajim.common.storage.archive.const import MessageState from gajim.common.storage.archive.const import MessageType -# from gajim.common.storage.archive.migration_v8 import MigrationV8 +from gajim.common.storage.archive.migration_v8 import MigrationV8 from gajim.common.storage.archive.statements import ARCHIVE_CREATE_STMT from gajim.common.storage.archive.statements import FIND_CORRECTED_MSG_STMT from gajim.common.storage.archive.statements import \ @@ -66,6 +66,8 @@ from gajim.common.storage.base import SqliteStorage from gajim.common.storage.base import timeit from gajim.common.storage.base import VALUE_MISSING +from gajim.gtk.util import open_window + CURRENT_USER_VERSION = 7 log = logging.getLogger('gajim.c.storage.archive') @@ -190,8 +192,12 @@ class MessageArchiveStorage(SqliteStorage): ] self._execute_multiple(statements) - # if user_version < 8: - # MigrationV8(self._con).run() + if user_version < 8: + open_window( + 'DBMigration', + migration_routine=MigrationV8, + db_connection=self._con, + db_path=self._path) @staticmethod def _like(search_str: str) -> str: diff --git a/gajim/data/gui/db_migration.ui b/gajim/data/gui/db_migration.ui new file mode 100644 index 000000000..adcb61593 --- /dev/null +++ b/gajim/data/gui/db_migration.ui @@ -0,0 +1,219 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Generated with glade 3.40.0 --> +<interface> + <requires lib="gtk+" version="3.24"/> + <object class="GtkStack" id="stack"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="transition-type">crossfade</property> + <property name="interpolate-size">True</property> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="homogeneous">True</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Database Migration</property> + <style> + <class name="large-header"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Your chat history is being migrated. +This may take a while…</property> + <property name="justify">center</property> + <style> + <class name="dim-label"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkSpinner"> + <property name="width-request">42</property> + <property name="height-request">42</property> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="active">True</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="status_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="justify">center</property> + <property name="wrap">True</property> + <property name="max-width-chars">52</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="name">migration-page</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="homogeneous">True</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Database Migration</property> + <style> + <class name="large-header"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">dialog-error-symbolic</property> + <property name="icon_size">6</property> + <style> + <class name="error-color"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel" id="error_label"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="justify">center</property> + <property name="wrap">True</property> + <property name="max-width-chars">52</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + <child> + <object class="GtkButton"> + <property name="label" translatable="yes">_Close</property> + <property name="visible">True</property> + <property name="can-focus">True</property> + <property name="receives-default">True</property> + <property name="halign">center</property> + <property name="valign">center</property> + <property name="use-underline">True</property> + <signal name="clicked" handler="_on_close_button_clicked" swapped="no"/> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">3</property> + </packing> + </child> + </object> + <packing> + <property name="name">error-page</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkBox"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="orientation">vertical</property> + <property name="spacing">6</property> + <property name="homogeneous">True</property> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Database Migration</property> + <style> + <class name="large-header"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">0</property> + </packing> + </child> + <child> + <object class="GtkImage"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="icon-name">feather-check-symbolic</property> + <property name="icon_size">6</property> + <style> + <class name="error-color"/> + </style> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">1</property> + </packing> + </child> + <child> + <object class="GtkLabel"> + <property name="visible">True</property> + <property name="can-focus">False</property> + <property name="label" translatable="yes">Your chat history database has been migrated successfully.</property> + <property name="justify">center</property> + <property name="wrap">True</property> + <property name="max-width-chars">52</property> + </object> + <packing> + <property name="expand">False</property> + <property name="fill">True</property> + <property name="position">2</property> + </packing> + </child> + </object> + <packing> + <property name="name">success-page</property> + <property name="position">2</property> + </packing> + </child> + </object> +</interface> diff --git a/gajim/gtk/builder.pyi b/gajim/gtk/builder.pyi index 0e46cffe2..d87717fe6 100644 --- a/gajim/gtk/builder.pyi +++ b/gajim/gtk/builder.pyi @@ -315,6 +315,12 @@ class ContactTooltipBuilder(Builder): resources_box: Gtk.Box +class DbMigrationBuilder(Builder): + stack: Gtk.Stack + status_label: Gtk.Label + error_label: Gtk.Label + + class EmojiChooserBuilder(Builder): box: Gtk.Box search: Gtk.SearchEntry @@ -1019,6 +1025,8 @@ def get_builder(file_name: Literal['contact_info.ui'], widgets: list[str] = ...) @overload def get_builder(file_name: Literal['contact_tooltip.ui'], widgets: list[str] = ...) -> ContactTooltipBuilder: ... # noqa @overload +def get_builder(file_name: Literal['db_migration.ui'], widgets: list[str] = ...) -> DbMigrationBuilder: ... # noqa +@overload def get_builder(file_name: Literal['emoji_chooser.ui'], widgets: list[str] = ...) -> EmojiChooserBuilder: ... # noqa @overload def get_builder(file_name: Literal['exception_dialog.ui'], widgets: list[str] = ...) -> ExceptionDialogBuilder: ... # noqa diff --git a/gajim/gtk/const.py b/gajim/gtk/const.py index b2bd9d6e7..84516fbc6 100644 --- a/gajim/gtk/const.py +++ b/gajim/gtk/const.py @@ -147,6 +147,7 @@ WINDOW_MODULES = { 'AddContact': 'gajim.gtk.add_contact', 'AdHocCommands': 'gajim.gtk.adhoc', 'AdvancedConfig': 'gajim.gtk.advanced_config', + 'DBMigration': 'gajim.gtk.db_migration', 'BlockingList': 'gajim.gtk.blocking', 'Bookmarks': 'gajim.gtk.bookmarks', 'CertificateDialog': 'gajim.gtk.certificate_dialog', diff --git a/gajim/gtk/db_migration.py b/gajim/gtk/db_migration.py new file mode 100644 index 000000000..307d81cf9 --- /dev/null +++ b/gajim/gtk/db_migration.py @@ -0,0 +1,93 @@ +# This file is part of Gajim. +# +# Gajim is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published +# by the Free Software Foundation; version 3 only. +# +# Gajim is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with Gajim. If not, see <http://www.gnu.org/licenses/>. + +from __future__ import annotations + +from typing import Any + +import logging +import shutil +import time +from collections.abc import Callable +from pathlib import Path +from sqlite3 import Connection + +from gi.repository import Gdk +from gi.repository import Gtk + +from gajim.common import app +from gajim.common.helpers import get_random_string +from gajim.common.i18n import _ + +from gajim.gtk.builder import get_builder + +log = logging.getLogger('gajim.gtk.db_migration') + + +class DBMigration(Gtk.ApplicationWindow): + def __init__( + self, + migration_routine: Callable[..., Any], + db_connection: Connection, + db_path: Path, + ) -> None: + Gtk.ApplicationWindow.__init__(self) + self.set_application(app.app) + self.set_position(Gtk.WindowPosition.CENTER) + self.set_show_menubar(False) + self.set_type_hint(Gdk.WindowTypeHint.DIALOG) + self.set_resizable(True) + self.set_name('DBMigration') + self.set_title(_('Database Migration')) + + self.set_resizable(False) + self.set_modal(True) + self.set_deletable(False) + + self._ui = get_builder('db_migration.ui') + self.add(self._ui.stack) + self._ui.connect_signals(self) + + self._ui.status_label.set_text(_('Creating backup…')) + + self.connect('destroy', self._on_destroy) + self.show_all() + + random_string = get_random_string(6) + db_backup_path = db_path.parent / f'{db_path.name}.{random_string}.old' + try: + shutil.copy(db_path, db_backup_path) + except PermissionError as e: + self._ui.error_label.set_text( + _('Could not create backup file: %s') % e + ) + self._ui.stack.set_visible_child_name('error-page') + self._show_error_page() + return + + self._ui.status_label.set_text(_('Migrating chat history database…')) + migration_routine(db_connection).run() + + self._ui.stack.set_visible_child_name('success-page') + time.sleep(5) + self.destroy() + + def _show_error_page(self) -> None: + self._ui.stack.set_visible_child_name('error-page') + + def _on_close_button_clicked(self, _button: Gtk.Button) -> None: + self.destroy() + + def _on_destroy(self, _widget: DBMigration) -> None: + app.check_finalize(self) |