Skip to content

Commit e9dd83c

Browse files
committed
feature "import/export" (3rd iteration)
- update text and function names - removed the ei_task class as it is no longer needed - created a get_codename() function as this is used twice in common.py - removed the tool window and the progress tracking because they are not needed - removed the browser profile export feature because it is unstable. It takes extremely long and does not work with different browser versions - implemented a function which checks during the import process whether the browser is installed on the device. If not, it searches an alternative browser on the device. - declared gladefiles paths as constants at the beginning of webapp-manager.py - removed all trailing spaces - applied the maximum line length
1 parent cbfaee3 commit e9dd83c

File tree

4 files changed

+162
-317
lines changed

4 files changed

+162
-317
lines changed

usr/lib/webapp-manager/common.py

Lines changed: 49 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -65,17 +65,6 @@ def wrapper(*args):
6565
ICONS_DIR = os.path.join(ICE_DIR, "icons")
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

68-
class ei_task:
69-
def __init__(self, result_callback, update_callback, builder, webAppLauncherSelf, window, task):
70-
self.result_callback = result_callback
71-
self.update_callback = update_callback
72-
self.builder = builder
73-
self.webAppLauncherSelf = webAppLauncherSelf
74-
self.path = ""
75-
self.window = window
76-
self.task = task
77-
self.result = "error"
78-
7968
class Browser:
8069

8170
def __init__(self, browser_type, name, exec_path, test_path):
@@ -171,7 +160,7 @@ def get_webapps(self):
171160
for filename in os.listdir(APPS_DIR):
172161
if filename.lower().startswith("webapp-") and filename.endswith(".desktop"):
173162
path = os.path.join(APPS_DIR, filename)
174-
codename = filename.replace("webapp-", "").replace("WebApp-", "").replace(".desktop", "")
163+
codename = get_codename(path)
175164
if not os.path.isdir(path):
176165
try:
177166
webapp = WebAppLauncher(path, codename)
@@ -319,8 +308,8 @@ def create_webapp(self, name, url, icon, category, browser, custom_parameters, i
319308
falkon_orig_prof_dir = os.path.join(os.path.expanduser("~/.config/falkon/profiles"), codename)
320309
os.symlink(falkon_profile_path, falkon_orig_prof_dir)
321310

322-
323-
def get_exec_string(self, browser, codename, custom_parameters, icon, isolate_profile, navbar, privatewindow, url):
311+
@staticmethod
312+
def get_exec_string( browser, codename, custom_parameters, icon, isolate_profile, navbar, privatewindow, url):
324313
if browser.browser_type in [BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP, BROWSER_TYPE_ZEN_FLATPAK]:
325314
# Firefox based
326315
if browser.browser_type == BROWSER_TYPE_FIREFOX:
@@ -564,61 +553,41 @@ def download_favicon(url):
564553
return images
565554

566555
@_async
567-
def export_config(ei_task_info: ei_task):
568-
# The export process in the background.
556+
def export_webapps(callback, path):
557+
# The background export process
569558
try:
570-
# Search all files
571-
files = get_all_desktop_files() + get_all_icons()
572-
total = len(files)
573-
update_interval = 1 if int(total / 100) < 1 else int(total / 100)
574-
559+
desktop_files = get_all_desktop_files()
575560
# Write the .tar.gz file
576-
with tarfile.open(ei_task_info.path, "w:gz") as tar:
577-
counter = 0
578-
for file in files:
579-
tar.add(file["full_path"], arcname=file["arcname"])
580-
if counter % update_interval == 0:
581-
progress = round(counter / total, 2)
582-
GLib.idle_add(ei_task_info.update_callback, ei_task_info, progress)
583-
584-
counter += 1
585-
586-
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
587-
ei_task_info.result = "ok"
561+
with tarfile.open(path, "w:gz") as tar:
562+
for desktop_file in desktop_files:
563+
tar.add(desktop_file["full_path"], arcname=desktop_file["arcname"])
564+
tar.add(ICONS_DIR, "ice/icons/")
565+
result = "ok"
588566
except Exception as e:
589567
print(e)
590-
ei_task_info.result = "error"
591-
592-
GLib.idle_add(ei_task_info.result_callback, ei_task_info)
568+
result = "error"
569+
570+
callback(result, "export", path)
593571

594572
@_async
595-
def import_config(ei_task_info: ei_task):
596-
# The import process in the background.
573+
def import_webapps(callback, path):
574+
# The background import process
597575
try:
598-
with tarfile.open(ei_task_info.path, "r:gz") as tar:
576+
result = "ok"
577+
with tarfile.open(path, "r:gz") as tar:
599578
files = tar.getnames()
600-
total = len(files)
601579
base_dir = os.path.dirname(ICE_DIR)
602-
update_interval = 1 if int(total / 100) < 1 else int(total / 100)
603-
counter = 0
604580
for file in files:
605581
tar.extract(file, base_dir)
606582
if file.startswith("applications/"):
607-
# Rewrite the "Exec" section. This is necessary if the username differs.
583+
# Rewrite the "Exec" section. It will apply the new paths and will search for browsers
608584
path = os.path.join(base_dir, file)
609-
update_exec_path(path)
610-
611-
if counter % update_interval == 0:
612-
progress = round(counter / total, 2)
613-
GLib.idle_add(ei_task_info.update_callback, ei_task_info, progress)
614-
counter += 1
615-
GLib.idle_add(ei_task_info.update_callback, ei_task_info, 1)
616-
ei_task_info.result = "ok"
585+
update_imported_desktop(path)
617586
except Exception as e:
618587
print(e)
619-
ei_task_info.result = "error"
588+
result = "error"
620589

621-
GLib.idle_add(ei_task_info.result_callback, ei_task_info)
590+
callback(result, "import", path)
622591

623592

624593
def get_all_desktop_files():
@@ -631,45 +600,42 @@ def get_all_desktop_files():
631600
files.append({"full_path":full_path, "arcname":arcname})
632601
return files
633602

634-
def get_all_icons():
635-
# List all the files in a directory.
636-
files = []
637-
for root, dirs, filenames in os.walk(ICONS_DIR):
638-
for filename in filenames:
639-
full_path = os.path.join(root, filename)
640-
arcname = ""
641-
arcname += os.path.relpath(full_path, os.path.dirname(ICE_DIR))
642-
files.append({"full_path":full_path, "arcname":arcname})
643-
return files
644-
645603

646604
def get_codename(path):
647-
codename = os.path.basename(path)
648-
codename = codename.replace(".desktop", "")
649-
codename = codename.replace("WebApp-", "")
650-
codename = codename.replace("webapp-", "")
605+
filename = os.path.basename(path)
606+
codename = filename.replace(".desktop", "").replace("WebApp-", "").replace("webapp-", "")
651607
return codename
652608

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)
659-
webapp = WebAppLauncher(path, codename)
660-
browsers = WebAppManager.get_supported_browsers()
609+
def update_imported_desktop(path):
610+
webapp = WebAppLauncher(path, get_codename(path))
661611
if "/" in webapp.icon:
662612
# Update Icon Path
663-
iconpath = ICONS_DIR + "/" + os.path.basename(webapp.icon)
664-
config.set("Desktop Entry", "Icon", iconpath)
613+
iconpath = os.path.join(ICONS_DIR, os.path.basename(webapp.icon))
665614
else:
666615
iconpath = webapp.icon
667616

668-
browser = next((browser for browser in browsers if browser.name == webapp.web_browser), None)
669-
new_exec_line = WebAppManager.get_exec_string(None, browser, codename, webapp.custom_parameters, iconpath, webapp.isolate_profile, webapp.navbar, webapp.privatewindow, webapp.url)
670-
config.set("Desktop Entry", "Exec", new_exec_line)
671-
with open(path, 'w') as configfile:
672-
config.write(configfile, space_around_delimiters=False)
617+
# Check if the browser is installed
618+
browsers = WebAppManager.get_supported_browsers()
619+
configured_browser = next((browser for browser in browsers if browser.name == webapp.web_browser), None)
620+
if os.path.exists(configured_browser.test_path) == False:
621+
# If the browser is not installed, search another browser.
622+
# 1. Sort browsers by same browser type
623+
# 2. Sort the browsers by similarity of the name of the missing browser
624+
similar_browsers = browsers
625+
similar_browsers.sort(key=lambda browser: (
626+
browser.browser_type == configured_browser.browser_type,
627+
configured_browser.name.split(" ")[0].lower() not in browser.name.lower()
628+
))
629+
configured_browser = None
630+
for browser in similar_browsers:
631+
if os.path.exists(browser.test_path):
632+
configured_browser = browser
633+
break
634+
635+
print(webapp.web_browser, "-Browser not installed")
636+
637+
WebAppManager.edit_webapp(WebAppManager, path, webapp.name, configured_browser, webapp.url, iconpath, webapp.category,
638+
webapp.custom_parameters, webapp.codename, webapp.isolate_profile, webapp.navbar, webapp.privatewindow)
673639

674640
if __name__ == "__main__":
675641
download_favicon(sys.argv[1])

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

Lines changed: 34 additions & 78 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@
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, ICONS_DIR
25+
from common import BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_ZEN_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP, ICONS_DIR
26+
from common import _async, idle, WebAppManager, download_favicon, export_webapps, import_webapps
2627

2728
setproctitle.setproctitle("webapp-manager")
2829

@@ -41,7 +42,6 @@
4142
# Gladefiles
4243
MAIN_WINDOW_GLADEFILE = "/usr/share/webapp-manager/webapp-manager.ui"
4344
SHORTCUTS_GLADEFILE = "/usr/share/webapp-manager/shortcuts.ui"
44-
EI_TOOL_GLADEFILE = "/usr/share/webapp-manager/ei_tool.ui"
4545

4646
class MyApplication(Gtk.Application):
4747
# Main initialization routine
@@ -130,14 +130,14 @@ def __init__(self, application):
130130
item = Gtk.ImageMenuItem()
131131
item.set_image(Gtk.Image.new_from_icon_name("document-send-symbolic", Gtk.IconSize.MENU))
132132
item.set_label(_("Export"))
133-
item.connect("activate", lambda widget: self.open_ei_tool("export"))
133+
item.connect("activate", lambda widget: self.ei_select_location("export"))
134134
key, mod = Gtk.accelerator_parse("<Control><Shift>E")
135135
item.add_accelerator("activate", accel_group, key, mod, Gtk.AccelFlags.VISIBLE)
136136
menu.append(item)
137137
item = Gtk.ImageMenuItem()
138138
item.set_image(Gtk.Image.new_from_icon_name("document-open-symbolic", Gtk.IconSize.MENU))
139139
item.set_label(_("Import"))
140-
item.connect("activate", lambda widget: self.open_ei_tool("import"))
140+
item.connect("activate", lambda widget: self.ei_select_location("import"))
141141
key, mod = Gtk.accelerator_parse("<Control><Shift>I")
142142
item.add_accelerator("activate", accel_group, key, mod, Gtk.AccelFlags.VISIBLE)
143143
menu.append(item)
@@ -554,102 +554,58 @@ def load_webapps(self):
554554
self.stack.set_visible_child_name("main_page")
555555
self.headerbar.set_subtitle(_("Run websites as if they were apps"))
556556

557-
# Export and Import feature "ei"
558-
def open_ei_tool(self, action):
559-
# Open the import / export window
560-
builder = Gtk.Builder()
561-
builder.set_translation_domain(APP)
562-
builder.add_from_file(EI_TOOL_GLADEFILE)
563-
window = builder.get_object("window")
564-
565-
# Translate text and prepare widgets
566-
if action == "export":
567-
window.set_title(_("Export Tool"))
568-
else:
569-
window.set_title(_("Import Tool"))
570-
builder.get_object("choose_location_text").set_text(_("Choose a location"))
571-
builder.get_object("start_button").set_label(_("Start"))
572-
builder.get_object("start_button").connect("clicked", lambda button: self.ei_start_process(button, ei_task_info))
573-
builder.get_object("select_location_button").connect("clicked", lambda widget: self.ei_select_location(ei_task_info))
574-
575-
# Prepare ei_task_info which stores all the values for the import / export
576-
ei_task_info = ei_task(self.show_ei_result, self.update_ei_progress, builder, self, window, action)
577-
window.show()
578-
579-
def ei_start_process(self, button, ei_task_info: ei_task):
580-
# Start the import / export process
581-
buffer = ei_task_info.builder.get_object("file_path").get_buffer()
582-
path = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
583-
if path != "":
584-
ei_task_info.path = path
585-
button.set_sensitive(False)
586-
if ei_task_info.task == "export":
587-
export_config(ei_task_info)
588-
#thread = threading.Thread(target=export_config, args=(ei_task_info,))
589-
else:
590-
import_config(ei_task_info)
591-
#thread = threading.Thread(target=import_config, args=(ei_task_info,))
592-
#thread.start()
593-
594-
595-
def ei_select_location(self, ei_task_info: ei_task):
596-
# Open the file chooser window
597-
if ei_task_info.task == "export":
557+
# "ei" means export and import feature
558+
def ei_select_location(self, task):
559+
# Open the file chooser dialog
560+
if task == "export":
598561
buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK)
599-
dialog = Gtk.FileChooserDialog(_("Export Configuration - Please choose a file location"), self.window, Gtk.FileChooserAction.SAVE, buttons)
562+
title = _("Export WebApps - Select file location")
563+
dialog = Gtk.FileChooserDialog(title, self.window, Gtk.FileChooserAction.SAVE, buttons)
600564
else:
601565
buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK)
602-
dialog = Gtk.FileChooserDialog(_("Import Configuration - Please select the archive"), self.window, Gtk.FileChooserAction.OPEN, buttons)
603-
566+
title = _("Import WebApps - Select the archive")
567+
dialog = Gtk.FileChooserDialog(title, self.window, Gtk.FileChooserAction.OPEN, buttons)
568+
604569
filter = Gtk.FileFilter()
605570
filter.set_name(".tar.gz")
606571
filter.add_pattern("*.tar.gz")
607572
dialog.add_filter(filter)
608573
response = dialog.run()
609574
if response == Gtk.ResponseType.OK:
610575
path = dialog.get_filename()
611-
if ei_task_info.task == "export":
612-
path += ".tar.gz"
613-
ei_task_info.builder.get_object("file_path").get_buffer().set_text(path)
576+
if path != "":
577+
if task == "export":
578+
path += ".tar.gz"
579+
export_webapps(self.show_ei_result, path)
580+
else:
581+
import_webapps(self.show_ei_result, path)
614582
dialog.destroy()
615583

616-
def update_ei_progress(self, ei_task_info:ei_task, progress):
617-
# Update the progress bar
618-
try:
619-
ei_task_info.builder.get_object("progress").set_fraction(progress)
620-
except:
621-
# The user closed the progress window
622-
pass
623-
624-
625-
def show_ei_result(self, ei_task_info:ei_task):
584+
def show_ei_result(self, result, task, path):
626585
# Displays a success or failure message when the process is complete.
627-
ei_task_info.webAppLauncherSelf.load_webapps()
628-
if ei_task_info.result == "ok":
629-
message = _(ei_task_info.task.capitalize() + " completed!")
630-
else:
631-
message = _(ei_task_info.task.capitalize() + " failed!")
632-
633-
if ei_task_info.result == "ok" and ei_task_info.task == "export":
586+
self.load_webapps()
587+
if result == "ok" and task == "export":
634588
# This dialog box gives users the option to open the containing directory.
635-
dialog = Gtk.Dialog(message, ei_task_info.webAppLauncherSelf.window, None, (_("Open Containing Folder"), 10, Gtk.STOCK_OK, Gtk.ButtonsType.OK))
636-
dialog.get_content_area().add(Gtk.Label(label=_("Configuration has been exported successfully. This is the file location:")+"\n"+ei_task_info.path))
589+
title = _("Export completed!")
590+
button_text = _("Open Containing Folder")
591+
dialog = Gtk.Dialog(title, self.window, None, (button_text, 10, Gtk.STOCK_OK, Gtk.ButtonsType.OK))
592+
dialog.get_content_area().add(Gtk.Label(label=_("WebApps have been exported successfully.")))
637593
dialog.show_all()
638594
result = dialog.run()
639595
if result == 10:
640596
# Open Containing Folder
641-
os.system("xdg-open " + os.path.dirname(ei_task_info.path))
597+
os.system("xdg-open " + os.path.dirname(path))
642598
else:
599+
if result == "ok" and task == "import":
600+
message = _("Import completed!")
601+
elif result != "ok" and task == "import":
602+
message = _("Import failed!")
603+
elif result != "ok" and task == "export":
604+
message = _("Export failed!")
605+
643606
dialog = Gtk.MessageDialog(text=message, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK)
644607
dialog.run()
645-
646608
dialog.destroy()
647-
try:
648-
ei_task_info.window.destroy()
649-
except:
650-
# User closed the window manually
651-
pass
652-
653609

654610
if __name__ == "__main__":
655611
application = MyApplication("org.x.webapp-manager", Gio.ApplicationFlags.FLAGS_NONE)

0 commit comments

Comments
 (0)