diff --git a/usr/lib/webapp-manager/common.py b/usr/lib/webapp-manager/common.py index e9ec1aa..e2f7603 100644 --- a/usr/lib/webapp-manager/common.py +++ b/usr/lib/webapp-manager/common.py @@ -17,9 +17,10 @@ import threading import traceback from typing import Optional +import tarfile # 2. Related third party imports. -from gi.repository import GObject +from gi.repository import GObject, GLib import PIL.Image import requests # Note: BeautifulSoup is an optional import supporting another way of getting a website's favicons. @@ -159,7 +160,7 @@ def get_webapps(self): for filename in os.listdir(APPS_DIR): if filename.lower().startswith("webapp-") and filename.endswith(".desktop"): path = os.path.join(APPS_DIR, filename) - codename = filename.replace("webapp-", "").replace("WebApp-", "").replace(".desktop", "") + codename = get_codename(path) if not os.path.isdir(path): try: webapp = WebAppLauncher(path, codename) @@ -307,8 +308,8 @@ def create_webapp(self, name, url, icon, category, browser, custom_parameters, i falkon_orig_prof_dir = os.path.join(os.path.expanduser("~/.config/falkon/profiles"), codename) os.symlink(falkon_profile_path, falkon_orig_prof_dir) - - def get_exec_string(self, browser, codename, custom_parameters, icon, isolate_profile, navbar, privatewindow, url): + @staticmethod + def get_exec_string( browser, codename, custom_parameters, icon, isolate_profile, navbar, privatewindow, url): if browser.browser_type in [BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP, BROWSER_TYPE_ZEN_FLATPAK]: # Firefox based if browser.browser_type == BROWSER_TYPE_FIREFOX: @@ -551,5 +552,97 @@ def download_favicon(url): images = sorted(images, key = lambda x: x[1].height, reverse=True) return images +@_async +def export_webapps(callback, path): + # The background export process + try: + desktop_files = get_all_desktop_files() + # Write the .tar.gz file + with tarfile.open(path, "w:gz") as tar: + for desktop_file in desktop_files: + tar.add(desktop_file["full_path"], arcname=desktop_file["arcname"]) + tar.add(ICONS_DIR, "ice/icons/") + result = "ok" + except Exception as e: + print(e) + result = "error" + + GLib.idle_add(callback, result, "export", path) + +@_async +def import_webapps(callback, path): + # The background import process + try: + result = "ok" + with tarfile.open(path, "r:gz") as tar: + files = tar.getnames() + base_dir = os.path.dirname(ICE_DIR) + for file in files: + tar.extract(file, base_dir) + if file.startswith("applications/"): + # Rewrite the "Exec" section. It will apply the new paths and will search for browsers + path = os.path.join(base_dir, file) + result = update_imported_desktop(path) + if result == "error": + tar.close() + break + except Exception as e: + print(e) + result = "error" + + GLib.idle_add(callback, result, "import", path) + + +def get_all_desktop_files(): + # Search all web apps and desktop files. + files = [] + for filename in os.listdir(APPS_DIR): + if filename.lower().startswith("webapp-") and filename.endswith(".desktop"): + full_path = os.path.join(APPS_DIR, filename) + arcname = os.path.relpath(full_path, os.path.dirname(APPS_DIR)) + files.append({"full_path":full_path, "arcname":arcname}) + return files + + +def get_codename(path): + filename = os.path.basename(path) + codename = filename.replace(".desktop", "").replace("WebApp-", "").replace("webapp-", "") + return codename + +def update_imported_desktop(path): + try: + webapp = WebAppLauncher(path, get_codename(path)) + if "/" in webapp.icon: + # Update Icon Path + iconpath = os.path.join(ICONS_DIR, os.path.basename(webapp.icon)) + else: + iconpath = webapp.icon + + # Check if the browser is installed + browsers = WebAppManager.get_supported_browsers() + configured_browser = next((browser for browser in browsers if browser.name == webapp.web_browser), None) + if os.path.exists(configured_browser.test_path) == False: + # If the browser is not installed, search another browser. + # 1. Sort browsers by same browser type + # 2. Sort the browsers by similarity of the name of the missing browser + similar_browsers = browsers + similar_browsers.sort(key=lambda browser: ( + browser.browser_type == configured_browser.browser_type, + configured_browser.name.split(" ")[0].lower() not in browser.name.lower() + )) + configured_browser = None + for browser in similar_browsers: + if os.path.exists(browser.test_path): + configured_browser = browser + break + + print(webapp.web_browser, "-Browser not installed") + + WebAppManager.edit_webapp(WebAppManager, path, webapp.name, configured_browser, webapp.url, iconpath, webapp.category, + webapp.custom_parameters, webapp.codename, webapp.isolate_profile, webapp.navbar, webapp.privatewindow) + return "ok" + except: + return "error" + if __name__ == "__main__": download_favicon(sys.argv[1]) diff --git a/usr/lib/webapp-manager/webapp-manager.py b/usr/lib/webapp-manager/webapp-manager.py index dd77cae..02a094a 100755 --- a/usr/lib/webapp-manager/webapp-manager.py +++ b/usr/lib/webapp-manager/webapp-manager.py @@ -21,7 +21,8 @@ from gi.repository import Gtk, Gdk, Gio, XApp, GdkPixbuf # 3. Local application/library specific imports. -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 +from common import BROWSER_TYPE_FIREFOX, BROWSER_TYPE_FIREFOX_FLATPAK, BROWSER_TYPE_ZEN_FLATPAK, BROWSER_TYPE_FIREFOX_SNAP, ICONS_DIR +from common import _async, idle, WebAppManager, download_favicon, export_webapps, import_webapps setproctitle.setproctitle("webapp-manager") @@ -37,6 +38,9 @@ CATEGORY_ID, CATEGORY_NAME = range(2) BROWSER_OBJ, BROWSER_NAME = range(2) +# Gladefiles +MAIN_WINDOW_GLADEFILE = "/usr/share/webapp-manager/webapp-manager.ui" +SHORTCUTS_GLADEFILE = "/usr/share/webapp-manager/shortcuts.ui" class MyApplication(Gtk.Application): # Main initialization routine @@ -67,10 +71,9 @@ def __init__(self, application): self.icon_theme = Gtk.IconTheme.get_default() # Set the Glade file - gladefile = "/usr/share/webapp-manager/webapp-manager.ui" self.builder = Gtk.Builder() self.builder.set_translation_domain(APP) - self.builder.add_from_file(gladefile) + self.builder.add_from_file(MAIN_WINDOW_GLADEFILE) self.window = self.builder.get_object("main_window") self.window.set_title(_("Web Apps")) self.window.set_icon_name("webapp-manager") @@ -124,6 +127,20 @@ def __init__(self, application): self.window.add_accel_group(accel_group) menu = self.builder.get_object("main_menu") item = Gtk.ImageMenuItem() + item.set_image(Gtk.Image.new_from_icon_name("document-send-symbolic", Gtk.IconSize.MENU)) + item.set_label(_("Export")) + item.connect("activate", lambda widget: self.ei_select_location("export")) + key, mod = Gtk.accelerator_parse("E") + item.add_accelerator("activate", accel_group, key, mod, Gtk.AccelFlags.VISIBLE) + menu.append(item) + item = Gtk.ImageMenuItem() + item.set_image(Gtk.Image.new_from_icon_name("document-open-symbolic", Gtk.IconSize.MENU)) + item.set_label(_("Import")) + item.connect("activate", lambda widget: self.ei_select_location("import")) + key, mod = Gtk.accelerator_parse("I") + item.add_accelerator("activate", accel_group, key, mod, Gtk.AccelFlags.VISIBLE) + menu.append(item) + item = Gtk.ImageMenuItem() item.set_image( Gtk.Image.new_from_icon_name("preferences-desktop-keyboard-shortcuts-symbolic", Gtk.IconSize.MENU)) item.set_label(_("Keyboard Shortcuts")) @@ -223,10 +240,9 @@ def data_func_surface(self, column, cell, model, iter_, *args): cell.set_property("surface", surface) def open_keyboard_shortcuts(self, widget): - gladefile = "/usr/share/webapp-manager/shortcuts.ui" builder = Gtk.Builder() builder.set_translation_domain(APP) - builder.add_from_file(gladefile) + builder.add_from_file(SHORTCUTS_GLADEFILE) window = builder.get_object("shortcuts-webappmanager") window.set_title(_("Web Apps")) window.show() @@ -537,8 +553,59 @@ def load_webapps(self): self.stack.set_visible_child_name("main_page") self.headerbar.set_subtitle(_("Run websites as if they were apps")) + # "ei" means export and import feature + def ei_select_location(self, task): + # Open the file chooser dialog + if task == "export": + buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_SAVE, Gtk.ResponseType.OK) + title = _("Export WebApps - Select file location") + dialog = Gtk.FileChooserDialog(title, self.window, Gtk.FileChooserAction.SAVE, buttons) + else: + buttons = (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL, Gtk.STOCK_OPEN, Gtk.ResponseType.OK) + title = _("Import WebApps - Select the archive") + dialog = Gtk.FileChooserDialog(title, self.window, Gtk.FileChooserAction.OPEN, buttons) + + filter = Gtk.FileFilter() + filter.set_name(".tar.gz") + filter.add_pattern("*.tar.gz") + dialog.add_filter(filter) + response = dialog.run() + if response == Gtk.ResponseType.OK: + path = dialog.get_filename() + if path != "": + if task == "export": + path += ".tar.gz" + export_webapps(self.show_ei_result, path) + else: + import_webapps(self.show_ei_result, path) + dialog.destroy() + + def show_ei_result(self, result, task, path): + # Displays a success or failure message when the process is complete. + self.load_webapps() + if result == "ok" and task == "export": + # This dialog box gives users the option to open the containing directory. + title = _("Export completed!") + button_text = _("Open Containing Folder") + dialog = Gtk.Dialog(title, self.window, None, (button_text, 10, Gtk.STOCK_OK, Gtk.ResponseType.OK)) + dialog.get_content_area().add(Gtk.Label(label=_("WebApps have been exported successfully."))) + dialog.show_all() + result = dialog.run() + if result == 10: + # Open Containing Folder + os.system("xdg-open " + os.path.dirname(path)) + else: + if result == "ok" and task == "import": + message = _("Import completed!") + elif result != "ok" and task == "import": + message = _("Import failed!") + elif result != "ok" and task == "export": + message = _("Export failed!") + + dialog = Gtk.MessageDialog(text=message, message_type=Gtk.MessageType.INFO, buttons=Gtk.ButtonsType.OK) + dialog.run() + dialog.destroy() if __name__ == "__main__": application = MyApplication("org.x.webapp-manager", Gio.ApplicationFlags.FLAGS_NONE) - application.run() - + application.run() \ No newline at end of file diff --git a/usr/share/webapp-manager/shortcuts.ui b/usr/share/webapp-manager/shortcuts.ui index 31f5389..ae69779 100644 --- a/usr/share/webapp-manager/shortcuts.ui +++ b/usr/share/webapp-manager/shortcuts.ui @@ -53,6 +53,20 @@ Go Back + + + 1 + <ctrl><shift>E + Export + + + + + 1 + <ctrl><shift>I + Import + + 1 diff --git a/webapp-manager.pot b/webapp-manager.pot index 679fb64..875679d 100644 --- a/webapp-manager.pot +++ b/webapp-manager.pot @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2022-06-21 10:58+0200\n" +"POT-Creation-Date: 2025-06-13 22:42+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -17,119 +17,161 @@ msgstr "" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" -#: usr/lib/webapp-manager/common.py:208 usr/lib/webapp-manager/common.py:319 +#: usr/lib/webapp-manager/common.py:268 usr/lib/webapp-manager/common.py:437 msgid "Web App" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:73 -#: usr/lib/webapp-manager/webapp-manager.py:231 -#: usr/lib/webapp-manager/webapp-manager.py:238 generate_desktop_files:25 +#: usr/lib/webapp-manager/webapp-manager.py:78 +#: usr/lib/webapp-manager/webapp-manager.py:247 +#: usr/lib/webapp-manager/webapp-manager.py:254 generate_desktop_files:25 #: generate_desktop_files:39 usr/share/webapp-manager/webapp-manager.ui.h:1 #: usr/share/webapp-manager/shortcuts.ui.h:1 msgid "Web Apps" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:129 +#: usr/lib/webapp-manager/webapp-manager.py:131 +#: usr/share/webapp-manager/shortcuts.ui.h:8 +msgid "Export" +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:138 +#: usr/share/webapp-manager/shortcuts.ui.h:9 +msgid "Import" +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:146 msgid "Keyboard Shortcuts" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:136 -#: usr/lib/webapp-manager/webapp-manager.py:237 -#: usr/share/webapp-manager/shortcuts.ui.h:8 +#: usr/lib/webapp-manager/webapp-manager.py:153 +#: usr/lib/webapp-manager/webapp-manager.py:253 +#: usr/share/webapp-manager/shortcuts.ui.h:10 msgid "About" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:141 -#: usr/share/webapp-manager/shortcuts.ui.h:10 +#: usr/lib/webapp-manager/webapp-manager.py:158 +#: usr/share/webapp-manager/shortcuts.ui.h:12 msgid "Quit" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:156 +#: usr/lib/webapp-manager/webapp-manager.py:173 msgid "Icon" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:160 +#: usr/lib/webapp-manager/webapp-manager.py:177 msgid "Name" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:165 +#: usr/lib/webapp-manager/webapp-manager.py:182 msgid "Browser" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:179 -msgid "Internet" +#: usr/lib/webapp-manager/webapp-manager.py:196 generate_desktop_files:48 +msgid "Web" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:180 generate_desktop_files:48 -msgid "Web" +#: usr/lib/webapp-manager/webapp-manager.py:197 +msgid "Internet" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:181 +#: usr/lib/webapp-manager/webapp-manager.py:198 msgid "Accessories" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:182 +#: usr/lib/webapp-manager/webapp-manager.py:199 msgid "Games" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:183 +#: usr/lib/webapp-manager/webapp-manager.py:200 msgid "Graphics" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:184 +#: usr/lib/webapp-manager/webapp-manager.py:201 msgid "Office" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:185 +#: usr/lib/webapp-manager/webapp-manager.py:202 msgid "Sound & Video" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:186 +#: usr/lib/webapp-manager/webapp-manager.py:203 msgid "Programming" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:187 +#: usr/lib/webapp-manager/webapp-manager.py:204 msgid "Education" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:209 +#: usr/lib/webapp-manager/webapp-manager.py:226 msgid "No supported browsers were detected." msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:239 -#: usr/lib/webapp-manager/webapp-manager.py:528 generate_desktop_files:25 +#: usr/lib/webapp-manager/webapp-manager.py:255 +#: usr/lib/webapp-manager/webapp-manager.py:554 generate_desktop_files:25 #: generate_desktop_files:39 msgid "Run websites as if they were apps" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:292 +#: usr/lib/webapp-manager/webapp-manager.py:306 #, python-format msgid "Delete '%s'" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:293 +#: usr/lib/webapp-manager/webapp-manager.py:307 #, python-format msgid "Are you sure you want to delete '%s'?" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:294 +#: usr/lib/webapp-manager/webapp-manager.py:308 msgid "This Web App will be permanently lost." msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:347 -#: usr/lib/webapp-manager/webapp-manager.py:379 -#: usr/lib/webapp-manager/webapp-manager.py:435 +#: usr/lib/webapp-manager/webapp-manager.py:362 +#: usr/lib/webapp-manager/webapp-manager.py:404 +#: usr/lib/webapp-manager/webapp-manager.py:460 msgid "Add a New Web App" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:369 +#: usr/lib/webapp-manager/webapp-manager.py:394 msgid "Edit Web App" msgstr "" -#: usr/lib/webapp-manager/webapp-manager.py:411 +#: usr/lib/webapp-manager/webapp-manager.py:436 msgid "Choose an icon" msgstr "" +#: usr/lib/webapp-manager/webapp-manager.py:561 +msgid "Export WebApps - Select file location" +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:565 +msgid "Import WebApps - Select the archive" +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:588 +msgid "Export completed!" +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:589 +msgid "Open Containing Folder" +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:591 +msgid "WebApps have been exported successfully." +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:599 +msgid "Import completed!" +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:601 +msgid "Import failed!" +msgstr "" + +#: usr/lib/webapp-manager/webapp-manager.py:603 +msgid "Export failed!" +msgstr "" + #: usr/share/webapp-manager/webapp-manager.ui.h:2 msgid "Manage your Web Apps" msgstr "" @@ -227,6 +269,6 @@ msgstr "" msgid "Go Back" msgstr "" -#: usr/share/webapp-manager/shortcuts.ui.h:9 +#: usr/share/webapp-manager/shortcuts.ui.h:11 msgid "Shortcuts" msgstr ""