|
7 | 7 | import shutil
|
8 | 8 | import subprocess
|
9 | 9 | import warnings
|
| 10 | +import threading |
10 | 11 |
|
11 | 12 | # 2. Related third party imports.
|
12 | 13 | import gi
|
|
21 | 22 | from gi.repository import Gtk, Gdk, Gio, XApp, GdkPixbuf
|
22 | 23 |
|
23 | 24 | # 3. Local application/library specific imports.
|
24 |
| -from common import _async, idle, WebAppManager, download_favicon, ICONS_DIR, BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_ZEN_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP |
| 25 | +from common import _async, idle, WebAppManager, download_favicon, BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_ZEN_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP, export_config, import_config, ei_task, check_browser_directories_tar |
25 | 26 |
|
26 | 27 | setproctitle.setproctitle("webapp-manager")
|
27 | 28 |
|
@@ -124,6 +125,20 @@ def __init__(self, application):
|
124 | 125 | self.window.add_accel_group(accel_group)
|
125 | 126 | menu = self.builder.get_object("main_menu")
|
126 | 127 | item = Gtk.ImageMenuItem()
|
| 128 | + item.set_image(Gtk.Image.new_from_icon_name("document-send-symbolic", Gtk.IconSize.MENU)) |
| 129 | + item.set_label(_("Export")) |
| 130 | + item.connect("activate", lambda widget: self.open_ei_tool("export")) |
| 131 | + key, mod = Gtk.accelerator_parse("<Control><Shift>E") |
| 132 | + item.add_accelerator("activate", accel_group, key, mod, Gtk.AccelFlags.VISIBLE) |
| 133 | + menu.append(item) |
| 134 | + item = Gtk.ImageMenuItem() |
| 135 | + item.set_image(Gtk.Image.new_from_icon_name("document-open-symbolic", Gtk.IconSize.MENU)) |
| 136 | + item.set_label(_("Import")) |
| 137 | + item.connect("activate", lambda widget: self.open_ei_tool("import")) |
| 138 | + key, mod = Gtk.accelerator_parse("<Control><Shift>I") |
| 139 | + item.add_accelerator("activate", accel_group, key, mod, Gtk.AccelFlags.VISIBLE) |
| 140 | + menu.append(item) |
| 141 | + item = Gtk.ImageMenuItem() |
127 | 142 | item.set_image(
|
128 | 143 | Gtk.Image.new_from_icon_name("preferences-desktop-keyboard-shortcuts-symbolic", Gtk.IconSize.MENU))
|
129 | 144 | item.set_label(_("Keyboard Shortcuts"))
|
@@ -537,6 +552,125 @@ def load_webapps(self):
|
537 | 552 | self.stack.set_visible_child_name("main_page")
|
538 | 553 | self.headerbar.set_subtitle(_("Run websites as if they were apps"))
|
539 | 554 |
|
| 555 | + # Export and Import feature "ei" |
| 556 | + def open_ei_tool(self, action): |
| 557 | + # Open the import / export window |
| 558 | + gladefile = "/usr/share/webapp-manager/ei_tool.ui" |
| 559 | + builder = Gtk.Builder() |
| 560 | + builder.set_translation_domain(APP) |
| 561 | + builder.add_from_file(gladefile) |
| 562 | + window = builder.get_object("window") |
| 563 | + # Translate text and prepare widgets |
| 564 | + if action == "export": |
| 565 | + window.set_title(_("Export Tool")) |
| 566 | + else: |
| 567 | + window.set_title(_("Import Tool")) |
| 568 | + builder.get_object("choose_location_text").set_text(_("Choose a location")) |
| 569 | + builder.get_object("include_browserdata").set_label(_("BETA: Include Browser data (Config, Cache, Extensions...)\nIt requires the same browser version on the destination computer\nIt might take some time.")) |
| 570 | + builder.get_object("no_browser_data").set_text(_("Browser data import not available because \nit is not included in the importet file.")) |
| 571 | + builder.get_object("no_browser_data").set_visible(False) |
| 572 | + builder.get_object("start_button").set_label(_("Start")) |
| 573 | + builder.get_object("start_button").connect("clicked", lambda button: self.ei_start_process(button, ei_task_info)) |
| 574 | + builder.get_object("cancel_button").set_visible(False) |
| 575 | + builder.get_object("select_location_button").connect("clicked", lambda widget: self.select_location(ei_task_info)) |
| 576 | + |
| 577 | + # Prepare ei_task_info which stores all the values for the import / export |
| 578 | + stop_event = threading.Event() |
| 579 | + ei_task_info = ei_task(self.show_ei_result, self.update_ei_progress, builder, self, window, stop_event, action) |
| 580 | + window.show() |
| 581 | + |
| 582 | + def ei_start_process(self, button, ei_task_info: ei_task): |
| 583 | + # Start the import / export process |
| 584 | + buffer = ei_task_info.builder.get_object("file_path").get_buffer() |
| 585 | + path = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True) |
| 586 | + if path != "": |
| 587 | + ei_task_info.path = path |
| 588 | + ei_task_info.include_browserdata = ei_task_info.builder.get_object("include_browserdata").get_active() |
| 589 | + button.set_sensitive(False) |
| 590 | + if ei_task_info.task == "export": |
| 591 | + thread = threading.Thread(target=export_config, args=(ei_task_info,)) |
| 592 | + else: |
| 593 | + thread = threading.Thread(target=import_config, args=(ei_task_info,)) |
| 594 | + thread.start() |
| 595 | + ei_task_info.builder.get_object("cancel_button").set_visible(True) |
| 596 | + ei_task_info.builder.get_object("cancel_button").connect("clicked", lambda button: self.abort_ei(button, ei_task_info, thread)) |
| 597 | + |
| 598 | + |
| 599 | + def select_location(self, ei_task_info: ei_task): |
| 600 | + # Open the file chooser window |
| 601 | + if ei_task_info.task == "export": |
| 602 | + buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK) |
| 603 | + dialog = Gtk.FileChooserDialog(_("Export Configuration - Please choose a file location"), self.window, Gtk.FileChooserAction.SAVE, buttons) |
| 604 | + else: |
| 605 | + buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) |
| 606 | + dialog = Gtk.FileChooserDialog(_("Import Configuration - Please select the file"), self.window, Gtk.FileChooserAction.OPEN, buttons) |
| 607 | + |
| 608 | + filter = Gtk.FileFilter() |
| 609 | + filter.set_name(".tar.gz") |
| 610 | + filter.add_pattern("*.tar.gz") |
| 611 | + dialog.add_filter(filter) |
| 612 | + response = dialog.run() |
| 613 | + if response == Gtk.ResponseType.OK: |
| 614 | + path = dialog.get_filename() |
| 615 | + if ei_task_info.task == "export": |
| 616 | + path += ".tar.gz" |
| 617 | + ei_task_info.builder.get_object("file_path").get_buffer().set_text(path) |
| 618 | + |
| 619 | + # Check if include browser data is available |
| 620 | + include_browser_available = True |
| 621 | + if ei_task_info.task == "import": |
| 622 | + if not check_browser_directories_tar(path): |
| 623 | + include_browser_available = False |
| 624 | + |
| 625 | + ei_task_info.builder.get_object("include_browserdata").set_sensitive(include_browser_available) |
| 626 | + ei_task_info.builder.get_object("no_browser_data").set_visible(not include_browser_available) |
| 627 | + ei_task_info.builder.get_object("include_browserdata").set_active(include_browser_available) |
| 628 | + dialog.destroy() |
| 629 | + |
| 630 | + |
| 631 | + def abort_ei(self, button, ei_task_info:ei_task, thread): |
| 632 | + # Abort the export / import process |
| 633 | + button.set_sensitive(False) |
| 634 | + self.update_ei_progress(ei_task_info, 0) |
| 635 | + # The backend function will automatically clean up after the stop flag is triggered. |
| 636 | + ei_task_info.stop_event.set() |
| 637 | + thread.join() |
| 638 | + |
| 639 | + def update_ei_progress(self, ei_task_info:ei_task, progress): |
| 640 | + # Update the progress bar or close the tool window by 100%. |
| 641 | + try: |
| 642 | + ei_task_info.builder.get_object("progress").set_fraction(progress) |
| 643 | + if progress == 1: |
| 644 | + ei_task_info.window.destroy() |
| 645 | + except: |
| 646 | + # The user closed the progress window |
| 647 | + pass |
| 648 | + |
| 649 | + |
| 650 | + def show_ei_result(self, ei_task_info:ei_task): |
| 651 | + # Displays a success or failure message when the process is complete. |
| 652 | + ei_task_info.window.destroy() |
| 653 | + if ei_task_info.result == "ok": |
| 654 | + message = _(ei_task_info.task.capitalize() + " completed!") |
| 655 | + else: |
| 656 | + message = _(ei_task_info.task.capitalize() + " failed!") |
| 657 | + |
| 658 | + if ei_task_info.result == "ok" and ei_task_info.task == "export": |
| 659 | + # This dialog box gives users the option to open the containing directory. |
| 660 | + dialog = Gtk.Dialog(message, ei_task_info.webAppLauncherSelf.window, None, (_("Open Containing Folder"), 10, Gtk.STOCK_OK, Gtk.ButtonsType.OK)) |
| 661 | + dialog.get_content_area().add(Gtk.Label(label=_("Configuration has been exported successfully. This is the file location:")+"\n"+ei_task_info.path)) |
| 662 | + dialog.show_all() |
| 663 | + result = dialog.run() |
| 664 | + if result == 10: |
| 665 | + # Open Containing Folder |
| 666 | + print("open folder") |
| 667 | + os.system("xdg-open " + os.path.dirname(ei_task_info.path)) |
| 668 | + else: |
| 669 | + dialog = Gtk.MessageDialog(text=message, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK) |
| 670 | + dialog.run() |
| 671 | + |
| 672 | + dialog.destroy() |
| 673 | + ei_task_info.webAppLauncherSelf.load_webapps() |
540 | 674 |
|
541 | 675 | if __name__ == "__main__":
|
542 | 676 | application = MyApplication("org.x.webapp-manager", Gio.ApplicationFlags.FLAGS_NONE)
|
|
0 commit comments