Skip to content

Commit cbfaee3

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 760bcf3 commit cbfaee3

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
@@ -66,16 +66,14 @@ def wrapper(*args):
6666
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, BROWSER_TYPE_ZEN_FLATPAK = range(10)
6767

6868
class ei_task:
69-
def __init__(self, result_callback, update_callback, builder, webAppLauncherSelf, window, stop_event, task):
69+
def __init__(self, result_callback, update_callback, builder, webAppLauncherSelf, window, task):
7070
self.result_callback = result_callback
7171
self.update_callback = update_callback
7272
self.builder = builder
7373
self.webAppLauncherSelf = webAppLauncherSelf
7474
self.path = ""
7575
self.window = window
76-
self.stop_event = stop_event
7776
self.task = task
78-
self.include_browserdata = False
7977
self.result = "error"
8078

8179
class Browser:
@@ -565,16 +563,12 @@ def download_favicon(url):
565563
images = sorted(images, key = lambda x: x[1].height, reverse=True)
566564
return images
567565

566+
@_async
568567
def export_config(ei_task_info: ei_task):
569568
# The export process in the background.
570569
try:
571-
# Collect all files
572-
webapps = get_all_desktop_files()
573-
if ei_task_info.include_browserdata:
574-
ice_files = get_all_files(ICE_DIR)
575-
else:
576-
ice_files = get_all_files(ICONS_DIR)
577-
files = webapps + ice_files
570+
# Search all files
571+
files = get_all_desktop_files() + get_all_icons()
578572
total = len(files)
579573
update_interval = 1 if int(total / 100) < 1 else int(total / 100)
580574

@@ -586,99 +580,46 @@ def export_config(ei_task_info: ei_task):
586580
if counter % update_interval == 0:
587581
progress = round(counter / total, 2)
588582
GLib.idle_add(ei_task_info.update_callback, ei_task_info, progress)
589-
590-
if ei_task_info.stop_event.is_set():
591-
# The user aborts the process.
592-
tar.close()
593-
clean_up_export(ei_task_info)
594-
return "cancelled"
583+
595584
counter += 1
596-
585+
586+
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
597587
ei_task_info.result = "ok"
598588
except Exception as e:
599589
print(e)
600590
ei_task_info.result = "error"
601591

602592
GLib.idle_add(ei_task_info.result_callback, ei_task_info)
603593

604-
def clean_up_export(ei_task_info: ei_task):
605-
# Remove the rest of the exported file when the user aborts the process.
606-
if os.path.exists(ei_task_info.path):
607-
os.remove(ei_task_info.path)
608-
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
609-
594+
@_async
610595
def import_config(ei_task_info: ei_task):
611596
# The import process in the background.
612597
try:
613-
# Make a list of the files beforehand so that the file structure can be restored
614-
# if the user aborts the import process.
615-
files_before = get_files_dirs(ICE_DIR) + get_files_dirs(APPS_DIR)
616-
617598
with tarfile.open(ei_task_info.path, "r:gz") as tar:
618599
files = tar.getnames()
619600
total = len(files)
620601
base_dir = os.path.dirname(ICE_DIR)
621602
update_interval = 1 if int(total / 100) < 1 else int(total / 100)
622603
counter = 0
623604
for file in files:
624-
# Exclude the file if it belongs to the browser data.
625-
no_browserdata = ei_task_info.include_browserdata == False
626-
is_ice_dir = file.startswith("ice/")
627-
is_no_icon = not file.startswith("ice/icons")
628-
if not(no_browserdata and is_ice_dir and is_no_icon):
629-
tar.extract(file, base_dir)
630-
605+
tar.extract(file, base_dir)
631606
if file.startswith("applications/"):
632-
# Redefine the "Exec" section. This is necessary if the username or browser path differs.
607+
# Rewrite the "Exec" section. This is necessary if the username differs.
633608
path = os.path.join(base_dir, file)
634609
update_exec_path(path)
635610

636611
if counter % update_interval == 0:
637612
progress = round(counter / total, 2)
638613
GLib.idle_add(ei_task_info.update_callback, ei_task_info, progress)
639-
640-
if ei_task_info.stop_event.is_set():
641-
tar.close()
642-
clean_up_import(ei_task_info, files_before)
643-
return "cancelled"
644614
counter += 1
615+
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
645616
ei_task_info.result = "ok"
646617
except Exception as e:
647618
print(e)
648619
ei_task_info.result = "error"
649620

650621
GLib.idle_add(ei_task_info.result_callback, ei_task_info)
651622

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

683624
def get_all_desktop_files():
684625
# Search all web apps and desktop files.
@@ -690,37 +631,31 @@ def get_all_desktop_files():
690631
files.append({"full_path":full_path, "arcname":arcname})
691632
return files
692633

693-
def get_all_files(base_dir):
634+
def get_all_icons():
694635
# List all the files in a directory.
695636
files = []
696-
for root, dirs, filenames in os.walk(base_dir):
637+
for root, dirs, filenames in os.walk(ICONS_DIR):
697638
for filename in filenames:
698639
full_path = os.path.join(root, filename)
699640
arcname = ""
700-
if base_dir == ICONS_DIR:
701-
arcname += "ice/"
702-
arcname += os.path.relpath(full_path, os.path.dirname(base_dir))
641+
arcname += os.path.relpath(full_path, os.path.dirname(ICE_DIR))
703642
files.append({"full_path":full_path, "arcname":arcname})
704643
return files
705644

706-
def get_files_dirs(base_dir):
707-
# List all the files and subdirectories within a directory.
708-
paths = []
709-
for dirpath, dirnames, filenames in os.walk(base_dir):
710-
paths.append(dirpath)
711-
for name in filenames:
712-
paths.append(os.path.join(dirpath, name))
713-
return paths
714645

715-
def update_exec_path(path):
716-
# This updates the 'exec' section of an imported web application or creates the browser directory for it.
717-
config = configparser.RawConfigParser()
718-
config.optionxform = str
719-
config.read(path)
646+
def get_codename(path):
720647
codename = os.path.basename(path)
721648
codename = codename.replace(".desktop", "")
722649
codename = codename.replace("WebApp-", "")
723650
codename = codename.replace("webapp-", "")
651+
return codename
652+
653+
def update_exec_path(path):
654+
# This updates the 'exec' section of an imported web application or creates the profile directory for it.
655+
config = configparser.RawConfigParser()
656+
config.optionxform = str
657+
config.read(path)
658+
codename = get_codename(path)
724659
webapp = WebAppLauncher(path, codename)
725660
browsers = WebAppManager.get_supported_browsers()
726661
if "/" in webapp.icon:
@@ -731,7 +666,7 @@ def update_exec_path(path):
731666
iconpath = webapp.icon
732667

733668
browser = next((browser for browser in browsers if browser.name == webapp.web_browser), None)
734-
new_exec_line = WebAppManager.get_exec_string(None, browser, webapp.codename, webapp.custom_parameters, iconpath, webapp.isolate_profile, webapp.navbar, webapp.privatewindow, webapp.url)
669+
new_exec_line = WebAppManager.get_exec_string(None, browser, codename, webapp.custom_parameters, iconpath, webapp.isolate_profile, webapp.navbar, webapp.privatewindow, webapp.url)
735670
config.set("Desktop Entry", "Exec", new_exec_line)
736671
with open(path, 'w') as configfile:
737672
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_ZEN_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_ZEN_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()
@@ -555,28 +557,23 @@ def load_webapps(self):
555557
# Export and Import feature "ei"
556558
def open_ei_tool(self, action):
557559
# Open the import / export window
558-
gladefile = "/usr/share/webapp-manager/ei_tool.ui"
559560
builder = Gtk.Builder()
560561
builder.set_translation_domain(APP)
561-
builder.add_from_file(gladefile)
562+
builder.add_from_file(EI_TOOL_GLADEFILE)
562563
window = builder.get_object("window")
564+
563565
# Translate text and prepare widgets
564566
if action == "export":
565567
window.set_title(_("Export Tool"))
566568
else:
567569
window.set_title(_("Import Tool"))
568570
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)
572571
builder.get_object("start_button").set_label(_("Start"))
573572
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))
573+
builder.get_object("select_location_button").connect("clicked", lambda widget: self.ei_select_location(ei_task_info))
576574

577575
# 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)
576+
ei_task_info = ei_task(self.show_ei_result, self.update_ei_progress, builder, self, window, action)
580577
window.show()
581578

582579
def ei_start_process(self, button, ei_task_info: ei_task):
@@ -585,25 +582,24 @@ def ei_start_process(self, button, ei_task_info: ei_task):
585582
path = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
586583
if path != "":
587584
ei_task_info.path = path
588-
ei_task_info.include_browserdata = ei_task_info.builder.get_object("include_browserdata").get_active()
589585
button.set_sensitive(False)
590586
if ei_task_info.task == "export":
591-
thread = threading.Thread(target=export_config, args=(ei_task_info,))
587+
export_config(ei_task_info)
588+
#thread = threading.Thread(target=export_config, args=(ei_task_info,))
592589
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))
590+
import_config(ei_task_info)
591+
#thread = threading.Thread(target=import_config, args=(ei_task_info,))
592+
#thread.start()
597593

598594

599-
def select_location(self, ei_task_info: ei_task):
595+
def ei_select_location(self, ei_task_info: ei_task):
600596
# Open the file chooser window
601597
if ei_task_info.task == "export":
602598
buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
603599
dialog = Gtk.FileChooserDialog(_("Export Configuration - Please choose a file location"), self.window, Gtk.FileChooserAction.SAVE, buttons)
604600
else:
605601
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)
602+
dialog = Gtk.FileChooserDialog(_("Import Configuration - Please select the archive"), self.window, Gtk.FileChooserAction.OPEN, buttons)
607603

608604
filter = Gtk.FileFilter()
609605
filter.set_name(".tar.gz")
@@ -615,41 +611,20 @@ def select_location(self, ei_task_info: ei_task):
615611
if ei_task_info.task == "export":
616612
path += ".tar.gz"
617613
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)
628614
dialog.destroy()
629615

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-
639616
def update_ei_progress(self, ei_task_info:ei_task, progress):
640-
# Update the progress bar or close the tool window by 100%.
617+
# Update the progress bar
641618
try:
642-
ei_task_info.builder.get_object("progress").set_fraction(progress)
643-
if progress == 1:
644-
ei_task_info.window.destroy()
619+
ei_task_info.builder.get_object("progress").set_fraction(progress)
645620
except:
646621
# The user closed the progress window
647622
pass
648623

649624

650625
def show_ei_result(self, ei_task_info:ei_task):
651626
# Displays a success or failure message when the process is complete.
652-
ei_task_info.window.destroy()
627+
ei_task_info.webAppLauncherSelf.load_webapps()
653628
if ei_task_info.result == "ok":
654629
message = _(ei_task_info.task.capitalize() + " completed!")
655630
else:
@@ -663,16 +638,19 @@ def show_ei_result(self, ei_task_info:ei_task):
663638
result = dialog.run()
664639
if result == 10:
665640
# Open Containing Folder
666-
print("open folder")
667641
os.system("xdg-open " + os.path.dirname(ei_task_info.path))
668642
else:
669643
dialog = Gtk.MessageDialog(text=message, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK)
670644
dialog.run()
671645

672646
dialog.destroy()
673-
ei_task_info.webAppLauncherSelf.load_webapps()
647+
try:
648+
ei_task_info.window.destroy()
649+
except:
650+
# User closed the window manually
651+
pass
652+
674653

675654
if __name__ == "__main__":
676655
application = MyApplication("org.x.webapp-manager", Gio.ApplicationFlags.FLAGS_NONE)
677-
application.run()
678-
656+
application.run()

0 commit comments

Comments
 (0)