diff --git a/frappe/boot.py b/frappe/boot.py index fb1bd3d7a2..959d03c1a3 100644 --- a/frappe/boot.py +++ b/frappe/boot.py @@ -106,6 +106,7 @@ def get_bootinfo(): bootinfo.link_title_doctypes = get_link_title_doctypes() bootinfo.translated_doctypes = get_translated_doctypes() bootinfo.subscription_conf = add_subscription_conf() + bootinfo.marketplace_apps = get_marketplace_apps() return bootinfo @@ -442,6 +443,29 @@ def load_currency_docs(bootinfo): bootinfo.docs += currency_docs +def get_marketplace_apps(): + import requests + + apps = [] + cache_key = "frappe_marketplace_apps" + + def get_apps_from_fc(): + remote_site = frappe.conf.frappecloud_url or "frappecloud.com" + request_url = f"https://{remote_site}/api/method/press.api.marketplace.get_marketplace_apps" + request = requests.get(request_url, timeout=2.0) + return request.json()["message"] + + try: + apps = frappe.cache().get_value(cache_key, get_apps_from_fc, shared=True) + installed_apps = set(frappe.get_installed_apps()) + apps = [app for app in apps if app["name"] not in installed_apps] + except Exception: + # Don't retry for a day + frappe.cache().set_value(cache_key, apps, shared=True, expires_in_sec=24 * 60 * 60) + + return apps + + def add_subscription_conf(): try: return frappe.conf.subscription diff --git a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js index 5f01edf578..cd5a4615a3 100644 --- a/frappe/public/js/frappe/ui/toolbar/awesome_bar.js +++ b/frappe/public/js/frappe/ui/toolbar/awesome_bar.js @@ -111,6 +111,10 @@ frappe.search.AwesomeBar = class AwesomeBar { if (event.ctrlKey || event.metaKey) { frappe.open_in_new_tab = true; } + if (item.route[0].startsWith("https://")) { + window.open(item.route[0], "_blank"); + return; + } frappe.set_route(item.route); } $input.val(""); @@ -201,7 +205,8 @@ frappe.search.AwesomeBar = class AwesomeBar { frappe.search.utils.get_workspaces(txt), frappe.search.utils.get_dashboards(txt), frappe.search.utils.get_recent_pages(txt || ""), - frappe.search.utils.get_executables(txt) + frappe.search.utils.get_executables(txt), + frappe.search.utils.get_marketplace_apps(txt) ); if (txt.charAt(0) === "#") { options = frappe.tags.utils.get_tags(txt); diff --git a/frappe/public/js/frappe/ui/toolbar/search_utils.js b/frappe/public/js/frappe/ui/toolbar/search_utils.js index aaed003d58..e9f2a2e6f2 100644 --- a/frappe/public/js/frappe/ui/toolbar/search_utils.js +++ b/frappe/public/js/frappe/ui/toolbar/search_utils.js @@ -625,5 +625,28 @@ frappe.search.utils = { args: args, }); }, + get_marketplace_apps: function (keywords) { + var me = this; + var out = []; + frappe.boot.marketplace_apps.forEach(function (item) { + var level = me.fuzzy_search(keywords, item.title) * 0.8; + if (level > 0) { + var ret = { + label: __("Install {0} from Marketplace", [ + me.bolden_match_part(__(item.title), keywords), + ]), + value: __("Install {0} from Marketplace", [__(item.title)]), + index: level, + route: [ + `https://frappecloud.com/${item.route}?utm_source=awesomebar`, + item.name, + ], + }; + + out.push(ret); + } + }); + return out; + }, searchable_functions: [], };