Skip to content

Commit 38828ea

Browse files
committed
feature "import/export" (2nd iteration)
- remove unnecessary code and simplify it - remove the unstable profile directory export and import functionality - remove the abort function and button, as the process is so quick that they are completely unnecessary. - define gladefiles constants at the beginning of webapp-manager.py
1 parent d8442bf commit 38828ea

File tree

3 files changed

+123
-263
lines changed

3 files changed

+123
-263
lines changed

usr/lib/webapp-manager/common.py

Lines changed: 24 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -65,16 +65,14 @@ def wrapper(*args):
6565
BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP, BROWSER_TYPE_LIBREWOLF_FLATPAK, BROWSER_TYPE_WATERFOX_FLATPAK, BROWSER_TYPE_FLOORP_FLATPAK, BROWSER_TYPE_CHROMIUM, BROWSER_TYPE_EPIPHANY, BROWSER_TYPE_FALKON = range(9)
6666

6767
class ei_task:
68-
def __init__(self, result_callback, update_callback, builder, webAppLauncherSelf, window, stop_event, task):
68+
def __init__(self, result_callback, update_callback, builder, webAppLauncherSelf, window, task):
6969
self.result_callback = result_callback
7070
self.update_callback = update_callback
7171
self.builder = builder
7272
self.webAppLauncherSelf = webAppLauncherSelf
7373
self.path = ""
7474
self.window = window
75-
self.stop_event = stop_event
7675
self.task = task
77-
self.include_browserdata = False
7876
self.result = "error"
7977

8078
class Browser:
@@ -559,16 +557,12 @@ def download_favicon(url):
559557
images = sorted(images, key = lambda x: x[1].height, reverse=True)
560558
return images
561559

560+
@_async
562561
def export_config(ei_task_info: ei_task):
563562
# The export process in the background.
564563
try:
565-
# Collect all files
566-
webapps = get_all_desktop_files()
567-
if ei_task_info.include_browserdata:
568-
ice_files = get_all_files(ICE_DIR)
569-
else:
570-
ice_files = get_all_files(ICONS_DIR)
571-
files = webapps + ice_files
564+
# Search all files
565+
files = get_all_desktop_files() + get_all_icons()
572566
total = len(files)
573567
update_interval = 1 if int(total / 100) < 1 else int(total / 100)
574568

@@ -580,99 +574,46 @@ def export_config(ei_task_info: ei_task):
580574
if counter % update_interval == 0:
581575
progress = round(counter / total, 2)
582576
GLib.idle_add(ei_task_info.update_callback, ei_task_info, progress)
583-
584-
if ei_task_info.stop_event.is_set():
585-
# The user aborts the process.
586-
tar.close()
587-
clean_up_export(ei_task_info)
588-
return "cancelled"
577+
589578
counter += 1
590-
579+
580+
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
591581
ei_task_info.result = "ok"
592582
except Exception as e:
593583
print(e)
594584
ei_task_info.result = "error"
595585

596586
GLib.idle_add(ei_task_info.result_callback, ei_task_info)
597587

598-
def clean_up_export(ei_task_info: ei_task):
599-
# Remove the rest of the exported file when the user aborts the process.
600-
if os.path.exists(ei_task_info.path):
601-
os.remove(ei_task_info.path)
602-
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
603-
588+
@_async
604589
def import_config(ei_task_info: ei_task):
605590
# The import process in the background.
606591
try:
607-
# Make a list of the files beforehand so that the file structure can be restored
608-
# if the user aborts the import process.
609-
files_before = get_files_dirs(ICE_DIR) + get_files_dirs(APPS_DIR)
610-
611592
with tarfile.open(ei_task_info.path, "r:gz") as tar:
612593
files = tar.getnames()
613594
total = len(files)
614595
base_dir = os.path.dirname(ICE_DIR)
615596
update_interval = 1 if int(total / 100) < 1 else int(total / 100)
616597
counter = 0
617598
for file in files:
618-
# Exclude the file if it belongs to the browser data.
619-
no_browserdata = ei_task_info.include_browserdata == False
620-
is_ice_dir = file.startswith("ice/")
621-
is_no_icon = not file.startswith("ice/icons")
622-
if not(no_browserdata and is_ice_dir and is_no_icon):
623-
tar.extract(file, base_dir)
624-
599+
tar.extract(file, base_dir)
625600
if file.startswith("applications/"):
626-
# Redefine the "Exec" section. This is necessary if the username or browser path differs.
601+
# Rewrite the "Exec" section. This is necessary if the username differs.
627602
path = os.path.join(base_dir, file)
628603
update_exec_path(path)
629604

630605
if counter % update_interval == 0:
631606
progress = round(counter / total, 2)
632607
GLib.idle_add(ei_task_info.update_callback, ei_task_info, progress)
633-
634-
if ei_task_info.stop_event.is_set():
635-
tar.close()
636-
clean_up_import(ei_task_info, files_before)
637-
return "cancelled"
638608
counter += 1
609+
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
639610
ei_task_info.result = "ok"
640611
except Exception as e:
641612
print(e)
642613
ei_task_info.result = "error"
643614

644615
GLib.idle_add(ei_task_info.result_callback, ei_task_info)
645616

646-
def clean_up_import(ei_task_info: ei_task, files_before):
647-
# Delete all imported files if the import process is aborted.
648-
try:
649-
# Search all new files
650-
files_now = get_files_dirs(ICE_DIR) + get_files_dirs(APPS_DIR)
651-
new_files = list(set(files_now) - set(files_before))
652-
for file in new_files:
653-
if os.path.exists(file):
654-
if os.path.isdir(file):
655-
shutil.rmtree(file)
656-
else:
657-
os.remove(file)
658-
659-
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
660-
except Exception as e:
661-
print(e)
662-
663-
def check_browser_directories_tar(path):
664-
# Check if the archive contains browser data.
665-
try:
666-
with tarfile.open(path, "r:gz") as tar:
667-
for member in tar:
668-
parts = member.name.strip("/").split("/")
669-
if parts[0] == "ice" and parts[1] != "icons":
670-
tar.close()
671-
return True
672-
tar.close()
673-
return False
674-
except:
675-
return False
676617

677618
def get_all_desktop_files():
678619
# Search all web apps and desktop files.
@@ -684,37 +625,31 @@ def get_all_desktop_files():
684625
files.append({"full_path":full_path, "arcname":arcname})
685626
return files
686627

687-
def get_all_files(base_dir):
628+
def get_all_icons():
688629
# List all the files in a directory.
689630
files = []
690-
for root, dirs, filenames in os.walk(base_dir):
631+
for root, dirs, filenames in os.walk(ICONS_DIR):
691632
for filename in filenames:
692633
full_path = os.path.join(root, filename)
693634
arcname = ""
694-
if base_dir == ICONS_DIR:
695-
arcname += "ice/"
696-
arcname += os.path.relpath(full_path, os.path.dirname(base_dir))
635+
arcname += os.path.relpath(full_path, os.path.dirname(ICE_DIR))
697636
files.append({"full_path":full_path, "arcname":arcname})
698637
return files
699638

700-
def get_files_dirs(base_dir):
701-
# List all the files and subdirectories within a directory.
702-
paths = []
703-
for dirpath, dirnames, filenames in os.walk(base_dir):
704-
paths.append(dirpath)
705-
for name in filenames:
706-
paths.append(os.path.join(dirpath, name))
707-
return paths
708639

709-
def update_exec_path(path):
710-
# This updates the 'exec' section of an imported web application or creates the browser directory for it.
711-
config = configparser.RawConfigParser()
712-
config.optionxform = str
713-
config.read(path)
640+
def get_codename(path):
714641
codename = os.path.basename(path)
715642
codename = codename.replace(".desktop", "")
716643
codename = codename.replace("WebApp-", "")
717644
codename = codename.replace("webapp-", "")
645+
return codename
646+
647+
def update_exec_path(path):
648+
# This updates the 'exec' section of an imported web application or creates the profile directory for it.
649+
config = configparser.RawConfigParser()
650+
config.optionxform = str
651+
config.read(path)
652+
codename = get_codename(path)
718653
webapp = WebAppLauncher(path, codename)
719654
browsers = WebAppManager.get_supported_browsers()
720655
if "/" in webapp.icon:
@@ -725,7 +660,7 @@ def update_exec_path(path):
725660
iconpath = webapp.icon
726661

727662
browser = next((browser for browser in browsers if browser.name == webapp.web_browser), None)
728-
new_exec_line = WebAppManager.get_exec_string(None, browser, webapp.codename, webapp.custom_parameters, iconpath, webapp.isolate_profile, webapp.navbar, webapp.privatewindow, webapp.url)
663+
new_exec_line = WebAppManager.get_exec_string(None, browser, codename, webapp.custom_parameters, iconpath, webapp.isolate_profile, webapp.navbar, webapp.privatewindow, webapp.url)
729664
config.set("Desktop Entry", "Exec", new_exec_line)
730665
with open(path, 'w') as configfile:
731666
config.write(configfile, space_around_delimiters=False)

usr/lib/webapp-manager/webapp-manager.py

Lines changed: 28 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from gi.repository import Gtk, Gdk, Gio, XApp, GdkPixbuf
2323

2424
# 3. Local application/library specific imports.
25-
from common import _async, idle, WebAppManager, download_favicon, BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP, export_config, import_config, ei_task, check_browser_directories_tar
25+
from common import _async, idle, WebAppManager, download_favicon, BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP, export_config, import_config, ei_task, ICONS_DIR
2626

2727
setproctitle.setproctitle("webapp-manager")
2828

@@ -38,6 +38,10 @@
3838
CATEGORY_ID, CATEGORY_NAME = range(2)
3939
BROWSER_OBJ, BROWSER_NAME = range(2)
4040

41+
# Gladefiles
42+
MAIN_WINDOW_GLADEFILE = "/usr/share/webapp-manager/webapp-manager.ui"
43+
SHORTCUTS_GLADEFILE = "/usr/share/webapp-manager/shortcuts.ui"
44+
EI_TOOL_GLADEFILE = "/usr/share/webapp-manager/ei_tool.ui"
4145

4246
class MyApplication(Gtk.Application):
4347
# Main initialization routine
@@ -68,10 +72,9 @@ def __init__(self, application):
6872
self.icon_theme = Gtk.IconTheme.get_default()
6973

7074
# Set the Glade file
71-
gladefile = "/usr/share/webapp-manager/webapp-manager.ui"
7275
self.builder = Gtk.Builder()
7376
self.builder.set_translation_domain(APP)
74-
self.builder.add_from_file(gladefile)
77+
self.builder.add_from_file(MAIN_WINDOW_GLADEFILE)
7578
self.window = self.builder.get_object("main_window")
7679
self.window.set_title(_("Web Apps"))
7780
self.window.set_icon_name("webapp-manager")
@@ -238,10 +241,9 @@ def data_func_surface(self, column, cell, model, iter_, *args):
238241
cell.set_property("surface", surface)
239242

240243
def open_keyboard_shortcuts(self, widget):
241-
gladefile = "/usr/share/webapp-manager/shortcuts.ui"
242244
builder = Gtk.Builder()
243245
builder.set_translation_domain(APP)
244-
builder.add_from_file(gladefile)
246+
builder.add_from_file(SHORTCUTS_GLADEFILE)
245247
window = builder.get_object("shortcuts-webappmanager")
246248
window.set_title(_("Web Apps"))
247249
window.show()
@@ -559,28 +561,23 @@ def load_webapps(self):
559561
# Export and Import feature "ei"
560562
def open_ei_tool(self, action):
561563
# Open the import / export window
562-
gladefile = "/usr/share/webapp-manager/ei_tool.ui"
563564
builder = Gtk.Builder()
564565
builder.set_translation_domain(APP)
565-
builder.add_from_file(gladefile)
566+
builder.add_from_file(EI_TOOL_GLADEFILE)
566567
window = builder.get_object("window")
568+
567569
# Translate text and prepare widgets
568570
if action == "export":
569571
window.set_title(_("Export Tool"))
570572
else:
571573
window.set_title(_("Import Tool"))
572574
builder.get_object("choose_location_text").set_text(_("Choose a location"))
573-
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."))
574-
builder.get_object("no_browser_data").set_text(_("Browser data import not available because \nit is not included in the importet file."))
575-
builder.get_object("no_browser_data").set_visible(False)
576575
builder.get_object("start_button").set_label(_("Start"))
577576
builder.get_object("start_button").connect("clicked", lambda button: self.ei_start_process(button, ei_task_info))
578-
builder.get_object("cancel_button").set_visible(False)
579-
builder.get_object("select_location_button").connect("clicked", lambda widget: self.select_location(ei_task_info))
577+
builder.get_object("select_location_button").connect("clicked", lambda widget: self.ei_select_location(ei_task_info))
580578

581579
# Prepare ei_task_info which stores all the values for the import / export
582-
stop_event = threading.Event()
583-
ei_task_info = ei_task(self.show_ei_result, self.update_ei_progress, builder, self, window, stop_event, action)
580+
ei_task_info = ei_task(self.show_ei_result, self.update_ei_progress, builder, self, window, action)
584581
window.show()
585582

586583
def ei_start_process(self, button, ei_task_info: ei_task):
@@ -589,25 +586,24 @@ def ei_start_process(self, button, ei_task_info: ei_task):
589586
path = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
590587
if path != "":
591588
ei_task_info.path = path
592-
ei_task_info.include_browserdata = ei_task_info.builder.get_object("include_browserdata").get_active()
593589
button.set_sensitive(False)
594590
if ei_task_info.task == "export":
595-
thread = threading.Thread(target=export_config, args=(ei_task_info,))
591+
export_config(ei_task_info)
592+
#thread = threading.Thread(target=export_config, args=(ei_task_info,))
596593
else:
597-
thread = threading.Thread(target=import_config, args=(ei_task_info,))
598-
thread.start()
599-
ei_task_info.builder.get_object("cancel_button").set_visible(True)
600-
ei_task_info.builder.get_object("cancel_button").connect("clicked", lambda button: self.abort_ei(button, ei_task_info, thread))
594+
import_config(ei_task_info)
595+
#thread = threading.Thread(target=import_config, args=(ei_task_info,))
596+
#thread.start()
601597

602598

603-
def select_location(self, ei_task_info: ei_task):
599+
def ei_select_location(self, ei_task_info: ei_task):
604600
# Open the file chooser window
605601
if ei_task_info.task == "export":
606602
buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
607603
dialog = Gtk.FileChooserDialog(_("Export Configuration - Please choose a file location"), self.window, Gtk.FileChooserAction.SAVE, buttons)
608604
else:
609605
buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
610-
dialog = Gtk.FileChooserDialog(_("Import Configuration - Please select the file"), self.window, Gtk.FileChooserAction.OPEN, buttons)
606+
dialog = Gtk.FileChooserDialog(_("Import Configuration - Please select the archive"), self.window, Gtk.FileChooserAction.OPEN, buttons)
611607

612608
filter = Gtk.FileFilter()
613609
filter.set_name(".tar.gz")
@@ -619,41 +615,20 @@ def select_location(self, ei_task_info: ei_task):
619615
if ei_task_info.task == "export":
620616
path += ".tar.gz"
621617
ei_task_info.builder.get_object("file_path").get_buffer().set_text(path)
622-
623-
# Check if include browser data is available
624-
include_browser_available = True
625-
if ei_task_info.task == "import":
626-
if not check_browser_directories_tar(path):
627-
include_browser_available = False
628-
629-
ei_task_info.builder.get_object("include_browserdata").set_sensitive(include_browser_available)
630-
ei_task_info.builder.get_object("no_browser_data").set_visible(not include_browser_available)
631-
ei_task_info.builder.get_object("include_browserdata").set_active(include_browser_available)
632618
dialog.destroy()
633619

634-
635-
def abort_ei(self, button, ei_task_info:ei_task, thread):
636-
# Abort the export / import process
637-
button.set_sensitive(False)
638-
self.update_ei_progress(ei_task_info, 0)
639-
# The backend function will automatically clean up after the stop flag is triggered.
640-
ei_task_info.stop_event.set()
641-
thread.join()
642-
643620
def update_ei_progress(self, ei_task_info:ei_task, progress):
644-
# Update the progress bar or close the tool window by 100%.
621+
# Update the progress bar
645622
try:
646-
ei_task_info.builder.get_object("progress").set_fraction(progress)
647-
if progress == 1:
648-
ei_task_info.window.destroy()
623+
ei_task_info.builder.get_object("progress").set_fraction(progress)
649624
except:
650625
# The user closed the progress window
651626
pass
652627

653628

654629
def show_ei_result(self, ei_task_info:ei_task):
655630
# Displays a success or failure message when the process is complete.
656-
ei_task_info.window.destroy()
631+
ei_task_info.webAppLauncherSelf.load_webapps()
657632
if ei_task_info.result == "ok":
658633
message = _(ei_task_info.task.capitalize() + " completed!")
659634
else:
@@ -667,16 +642,19 @@ def show_ei_result(self, ei_task_info:ei_task):
667642
result = dialog.run()
668643
if result == 10:
669644
# Open Containing Folder
670-
print("open folder")
671645
os.system("xdg-open " + os.path.dirname(ei_task_info.path))
672646
else:
673647
dialog = Gtk.MessageDialog(text=message, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK)
674648
dialog.run()
675649

676650
dialog.destroy()
677-
ei_task_info.webAppLauncherSelf.load_webapps()
651+
try:
652+
ei_task_info.window.destroy()
653+
except:
654+
# User closed the window manually
655+
pass
656+
678657

679658
if __name__ == "__main__":
680659
application = MyApplication("org.x.webapp-manager", Gio.ApplicationFlags.FLAGS_NONE)
681-
application.run()
682-
660+
application.run()

0 commit comments

Comments
 (0)