def _get_home_page(): home_page = None get_website_user_home_page = frappe.get_hooks('get_website_user_home_page') if get_website_user_home_page: home_page = frappe.get_attr(get_website_user_home_page[-1])(frappe.session.user) if not home_page: role_home_page = frappe.get_hooks("role_home_page") if role_home_page: for role in frappe.get_roles(): if role in role_home_page: home_page = role_home_page[role][-1] break if not home_page: home_page = frappe.get_hooks("home_page") if home_page: home_page = home_page[-1] if not home_page: home_page = frappe.db.get_value("Website Settings", None, "home_page") or "login" home_page = home_page.strip('/') return home_page
def setup_complete(args): """Calls hooks for `setup_wizard_complete`, sets home page as `desktop` and clears cache. If wizard breaks, calls `setup_wizard_exception` hook""" args = process_args(args) try: if args.language and args.language != "english": set_default_language(args.language) frappe.clear_cache() # update system settings update_system_settings(args) for method in frappe.get_hooks("setup_wizard_complete"): frappe.get_attr(method)(args) # frappe.db.set_default('desktop:home_page', 'desktop') frappe.db.set_default('desktop:home_page', 'dashboard') frappe.db.commit() frappe.clear_cache() except: if args: traceback = frappe.get_traceback() for hook in frappe.get_hooks("setup_wizard_exception"): frappe.get_attr(hook)(traceback, args) raise else: for hook in frappe.get_hooks("setup_wizard_success"): frappe.get_attr(hook)(args)
def get_messages_from_include_files(app_name=None): """Extracts all translatable strings from Javascript app files""" messages = [] for file in (frappe.get_hooks("app_include_js", app_name=app_name) or []) + (frappe.get_hooks("web_include_js", app_name=app_name) or []): messages.extend(get_messages_from_file(os.path.join(frappe.local.sites_path, file))) return messages
def get_messages_from_include_files(app_name=None): """Returns messages from js files included at time of boot like desk.min.js for desk and web""" messages = [] for file in (frappe.get_hooks("app_include_js", app_name=app_name) or []) + (frappe.get_hooks("web_include_js", app_name=app_name) or []): messages.extend(get_messages_from_file(os.path.join(frappe.local.sites_path, file))) return messages
def get_bootinfo(): """build and return boot info""" frappe.set_user_lang(frappe.session.user) bootinfo = frappe._dict() hooks = frappe.get_hooks() doclist = [] # user get_user(bootinfo) # system info bootinfo['sysdefaults'] = frappe.defaults.get_defaults() bootinfo['server_date'] = frappe.utils.nowdate() if frappe.session['user'] != 'Guest': bootinfo['user_info'] = get_fullnames() bootinfo['sid'] = frappe.session['sid']; # home page bootinfo.modules = {} for app in frappe.get_installed_apps(): try: bootinfo.modules.update(frappe.get_attr(app + ".config.desktop.get_data")() or {}) except ImportError: pass except AttributeError: pass bootinfo.module_app = frappe.local.module_app bootinfo.hidden_modules = frappe.db.get_global("hidden_modules") bootinfo.doctype_icons = dict(frappe.db.sql("""select name, icon from tabDocType where ifnull(icon,'')!=''""")) bootinfo.single_types = frappe.db.sql_list("""select name from tabDocType where ifnull(issingle,0)=1""") add_home_page(bootinfo, doclist) bootinfo.page_info = get_allowed_pages() load_translations(bootinfo) add_timezone_info(bootinfo) load_conf_settings(bootinfo) load_print(bootinfo, doclist) doclist.extend(get_meta_bundle("Page")) bootinfo.home_folder = frappe.db.get_value("File", {"is_home_folder": 1}) # ipinfo if frappe.session['data'].get('ipinfo'): bootinfo['ipinfo'] = frappe.session['data']['ipinfo'] # add docs bootinfo['docs'] = doclist for method in hooks.boot_session or []: frappe.get_attr(method)(bootinfo) if bootinfo.lang: bootinfo.lang = unicode(bootinfo.lang) bootinfo['versions'] = {k: v['version'] for k, v in get_versions().items()} bootinfo.error_report_email = frappe.get_hooks("error_report_email") bootinfo.calendars = sorted(frappe.get_hooks("calendars")) return bootinfo
def migrate(verbose=True, rebuild_website=False): '''Migrate all apps to the latest version, will: - run before migrate hooks - run patches - sync doctypes (schema) - sync fixtures - sync desktop icons - sync web pages (from /www) - sync web pages (from /www) - run after migrate hooks ''' touched_tables_file = frappe.get_site_path('touched_tables.json') if os.path.exists(touched_tables_file): os.remove(touched_tables_file) try: frappe.flags.touched_tables = set() frappe.flags.in_migrate = True clear_global_cache() #run before_migrate hooks for app in frappe.get_installed_apps(): for fn in frappe.get_hooks('before_migrate', app_name=app): frappe.get_attr(fn)() # run patches frappe.modules.patch_handler.run_all() # sync frappe.model.sync.sync_all(verbose=verbose) frappe.translate.clear_cache() sync_fixtures() sync_customizations() sync_desktop_icons() sync_languages() frappe.get_doc('Portal Settings', 'Portal Settings').sync_menu() # syncs statics render.clear_cache() # add static pages to global search router.sync_global_search() #run after_migrate hooks for app in frappe.get_installed_apps(): for fn in frappe.get_hooks('after_migrate', app_name=app): frappe.get_attr(fn)() frappe.db.commit() clear_notifications() frappe.publish_realtime("version-update") frappe.flags.in_migrate = False finally: with open(touched_tables_file, 'w') as f: json.dump(list(frappe.flags.touched_tables), f, sort_keys=True, indent=4) frappe.flags.touched_tables.clear()
def get_website_user_home_page(user): home_page_method = frappe.get_hooks('get_website_user_home_page') if home_page_method: home_page = frappe.get_attr(home_page_method[-1])(user) return '/' + home_page.strip('/') elif frappe.get_hooks('website_user_home_page'): return '/' + frappe.get_hooks('website_user_home_page')[-1].strip('/') else: return '/me'
def get_bootinfo(): """build and return boot info""" frappe.set_user_lang(frappe.session.user) bootinfo = frappe._dict() hooks = frappe.get_hooks() doclist = [] # user get_user(bootinfo) # system info bootinfo.sysdefaults = frappe.defaults.get_defaults() bootinfo.server_date = frappe.utils.nowdate() if frappe.session['user'] != 'Guest': bootinfo.user_info = get_fullnames() bootinfo.sid = frappe.session['sid']; bootinfo.modules = {} bootinfo.module_list = [] load_desktop_icons(bootinfo) bootinfo.module_app = frappe.local.module_app bootinfo.single_types = frappe.db.sql_list("""select name from tabDocType where issingle=1""") add_home_page(bootinfo, doclist) bootinfo.page_info = get_allowed_pages() load_translations(bootinfo) add_timezone_info(bootinfo) load_conf_settings(bootinfo) load_print(bootinfo, doclist) doclist.extend(get_meta_bundle("Page")) bootinfo.home_folder = frappe.db.get_value("File", {"is_home_folder": 1}) # ipinfo if frappe.session.data.get('ipinfo'): bootinfo.ipinfo = frappe.session['data']['ipinfo'] # add docs bootinfo.docs = doclist for method in hooks.boot_session or []: frappe.get_attr(method)(bootinfo) if bootinfo.lang: bootinfo.lang = unicode(bootinfo.lang) bootinfo.versions = {k: v['version'] for k, v in get_versions().items()} bootinfo.error_report_email = frappe.get_hooks("error_report_email") bootinfo.calendars = sorted(frappe.get_hooks("calendars")) bootinfo.treeviews = frappe.get_hooks("treeviews") or [] bootinfo.email_accounts = frappe.get_all('User Emails', fields=['email_account', 'email_id'], filters=dict(parent=frappe.session.user), order_by='idx') bootinfo.lang_dict = get_lang_dict() return bootinfo
def get_website_settings(): hooks = frappe.get_hooks() context = frappe._dict({ 'top_bar_items': get_items('top_bar_items'), 'footer_items': get_items('footer_items'), "post_login": [ {"label": _("My Account"), "url": "/me"}, # {"class": "divider"}, {"label": _("Logout"), "url": "/?cmd=web_logout"} ] }) settings = frappe.get_single("Website Settings") for k in ["banner_html", "brand_html", "copyright", "twitter_share_via", "facebook_share", "google_plus_one", "twitter_share", "linked_in_share", "disable_signup", "hide_footer_signup", "head_html", "title_prefix", "navbar_search"]: if hasattr(settings, k): context[k] = settings.get(k) if settings.address: context["footer_address"] = settings.address for k in ["facebook_share", "google_plus_one", "twitter_share", "linked_in_share", "disable_signup"]: context[k] = int(context.get(k) or 0) if frappe.request: context.url = quote(str(get_request_site_address(full_address=True)), safe="/:") context.encoded_title = quote(encode(context.title or ""), str("")) for update_website_context in hooks.update_website_context or []: frappe.get_attr(update_website_context)(context) context.web_include_js = hooks.web_include_js or [] context.web_include_css = hooks.web_include_css or [] via_hooks = frappe.get_hooks("website_context") for key in via_hooks: context[key] = via_hooks[key] if key not in ("top_bar_items", "footer_items", "post_login") \ and isinstance(context[key], (list, tuple)): context[key] = context[key][-1] add_website_theme(context) if not context.get("favicon"): context["favicon"] = "/assets/frappe/images/favicon.png" if settings.favicon and settings.favicon != "attach_files:": context["favicon"] = settings.favicon return context
def build_context(context): """get_context method of doc or module is supposed to render content templates and push it into context""" context = frappe._dict(context) if not "url_prefix" in context: context.url_prefix = "" if context.url_prefix and context.url_prefix[-1]!='/': context.url_prefix += '/' context.update(get_website_settings()) context.update(frappe.local.conf.get("website_context") or {}) # provide doc if context.doc: context.update(context.doc.as_dict()) context.update(context.doc.website) if hasattr(context.doc, "get_context"): ret = context.doc.get_context(context) if ret: context.update(ret) for prop in ("no_cache", "no_sitemap"): if not prop in context: context[prop] = getattr(context.doc, prop, False) elif context.controller: # controller based context update_controller_context(context, context.controller) # controller context extensions context_controller_hooks = frappe.get_hooks("extend_website_page_controller_context") or {} for controller, extension in context_controller_hooks.items(): if isinstance(extension, list): for ext in extension: if controller == context.controller: update_controller_context(context, ext) else: update_controller_context(context, extension) add_metatags(context) if context.show_sidebar: context.no_cache = 1 add_sidebar_data(context) # determine templates to be used if not context.base_template_path: app_base = frappe.get_hooks("base_template") context.base_template_path = app_base[0] if app_base else "templates/base.html" return context
def migrate(verbose=True, rebuild_website=False): '''Migrate all apps to the latest version, will: - run before migrate hooks - run patches - sync doctypes (schema) - sync fixtures - sync desktop icons - sync web pages (from /www) - sync web pages (from /www) - run after migrate hooks ''' frappe.flags.in_migrate = True clear_global_cache() #run before_migrate hooks for app in frappe.get_installed_apps(): for fn in frappe.get_hooks('before_migrate', app_name=app): frappe.get_attr(fn)() # run patches frappe.modules.patch_handler.run_all() # sync frappe.model.sync.sync_all(verbose=verbose) frappe.translate.clear_cache() sync_fixtures() sync_customizations() sync_desktop_icons() sync_languages() frappe.get_doc('Portal Settings', 'Portal Settings').sync_menu() # syncs statics render.clear_cache() # add static pages to global search router.sync_global_search() #run after_migrate hooks for app in frappe.get_installed_apps(): for fn in frappe.get_hooks('after_migrate', app_name=app): frappe.get_attr(fn)() frappe.db.commit() clear_notifications() frappe.publish_realtime("version-update") frappe.flags.in_migrate = False
def application(request): frappe.local.request = request frappe.local.is_ajax = frappe.get_request_header("X-Requested-With")=="XMLHttpRequest" response = None try: rollback = True init_site(request) #wirte comment if "api_handler" in frappe.get_all_apps() and frappe.get_hooks("api_name", app_name="api_handler"): api_name = frappe.get_hooks("api_name", app_name="api_handler")[0] if frappe.local.conf.get('maintenance_mode'): raise frappe.SessionStopped make_form_dict(request) frappe.local.http_request = frappe.auth.HTTPRequest() if frappe.local.form_dict.cmd: response = frappe.handler.handle() elif frappe.request.path.startswith("/api/"): response = frappe.api.handle() elif frappe.request.path.startswith('/backups'): response = frappe.utils.response.download_backup(request.path) elif frappe.request.path.startswith('/private/files/'): response = frappe.utils.response.download_private_file(request.path) elif frappe.local.request.method in ('GET', 'HEAD'): response = frappe.website.render.render(request.path) #write comment elif api_name and frappe.request.path.startswith("/%s/"%api_name): response = api_handler.api.handle() else: raise NotFound except HTTPException, e: return e
def execute(): language = frappe.get_single("System Settings").language if language and language.startswith('en'): return frappe.local.lang = language all_domains = frappe.get_hooks("domains") for domain in all_domains: translated_domain = _(domain, lang=language) if frappe.db.exists("Domain", translated_domain): #if domain already exists merged translated_domain and domain merge = False if frappe.db.exists("Domain", domain): merge=True frappe.rename_doc("Domain", translated_domain, domain, ignore_permissions=True, merge=merge) domain_settings = frappe.get_single("Domain Settings") active_domains = [d.domain for d in domain_settings.active_domains] try: for domain in active_domains: domain = frappe.get_doc("Domain", domain) domain.setup_domain() if int(frappe.db.get_single_value('System Settings', 'setup_complete')): domain.setup_sidebar_items() domain.setup_desktop_icons() domain.set_default_portal_role() except frappe.LinkValidationError: pass
def send_login_mail(self, subject, template, add_args): """send mail with login details""" from frappe.utils.user import get_user_fullname from frappe.utils import get_url mail_titles = frappe.get_hooks().get("login_mail_title", []) title = frappe.db.get_default('company') or (mail_titles and mail_titles[0]) or "" full_name = get_user_fullname(frappe.session['user']) if full_name == "Guest": full_name = "Administrator" args = { 'first_name': self.first_name or self.last_name or "user", 'user': self.name, 'title': title, 'login_url': get_url(), 'user_fullname': full_name } args.update(add_args) sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None frappe.sendmail(recipients=self.email, sender=sender, subject=subject, message=frappe.get_template(template).render(args), as_bulk=self.flags.delay_emails)
def _get(): rules = frappe.get_hooks("website_route_rules") for d in frappe.get_all('DocType', 'name, route', dict(has_web_view=1)): if d.route: rules.append(dict(from_route = '/' + d.route.strip('/'), to_route=d.name)) return rules
def get_hook_method(hook_name, fallback=None): method = (frappe.get_hooks().get(hook_name)) if method: method = frappe.get_attr(method[0]) return method if fallback: return fallback
def get_versions(): """Get versions of all installed apps. Example: { "frappe": { "title": "Frappe Framework", "version": "5.0.0" } }""" versions = {} for app in frappe.get_installed_apps(sort=True): app_hooks = frappe.get_hooks(app_name=app) versions[app] = { "title": app_hooks.get("app_title")[0], "description": app_hooks.get("app_description")[0], "branch": get_app_branch(app) } if versions[app]['branch'] != 'master': branch_version = app_hooks.get('{0}_version'.format(versions[app]['branch'])) if branch_version: versions[app]['branch_version'] = branch_version[0] + ' ({0})'.format(get_app_last_commit_ref(app)) try: versions[app]["version"] = frappe.get_attr(app + ".__version__") except AttributeError: versions[app]["version"] = '0.0.1' return versions
def install_app(name, verbose=False, set_as_patched=True): frappe.flags.in_install_app = name frappe.clear_cache() app_hooks = frappe.get_hooks(app_name=name) installed_apps = frappe.get_installed_apps() if name not in frappe.get_all_apps(with_frappe=True): raise Exception("App not in apps.txt") if name in installed_apps: print "App Already Installed" frappe.msgprint(_("App Already Installed")) return if name != "frappe": frappe.only_for("System Manager") for before_install in app_hooks.before_install or []: frappe.get_attr(before_install)() if name != "frappe": add_module_defs(name) sync_for(name, force=True, sync_everything=True, verbose=verbose) add_to_installed_apps(name) if set_as_patched: set_all_patches_as_completed(name) for after_install in app_hooks.after_install or []: frappe.get_attr(after_install)() frappe.flags.in_install_app = False
def get_page_context_from_doctypes(): routes = frappe.cache().get_value("website_generator_routes") if not routes: routes = {} for app in frappe.get_installed_apps(): for doctype in frappe.get_hooks("website_generators", app_name = app): condition = "" route_column_name = "page_name" controller = get_controller(doctype) meta = frappe.get_meta(doctype) if meta.get_field("parent_website_route"): route_column_name = """concat(ifnull(parent_website_route, ""), if(ifnull(parent_website_route, "")="", "", "/"), page_name)""" if controller.website.condition_field: condition ="where {0}=1".format(controller.website.condition_field) for r in frappe.db.sql("""select {0} as route, name, modified from `tab{1}` {2}""".format(route_column_name, doctype, condition), as_dict=True): routes[r.route] = {"doctype": doctype, "name": r.name, "modified": r.modified} frappe.cache().set_value("website_generator_routes", routes) return routes
def send_login_mail(self, subject, template, add_args, now=None): """send mail with login details""" from frappe.utils.user import get_user_fullname from frappe.utils import get_url mail_titles = frappe.get_hooks().get("login_mail_title", []) title = frappe.db.get_default('company') or (mail_titles and mail_titles[0]) or "" full_name = get_user_fullname(frappe.session['user']) if full_name == "Guest": full_name = "Administrator" args = { 'first_name': self.first_name or self.last_name or "user", 'user': self.name, 'title': title, 'login_url': get_url(), 'user_fullname': full_name } args.update(add_args) sender = frappe.session.user not in STANDARD_USERS and get_formatted_email(frappe.session.user) or None frappe.sendmail(recipients=self.email, sender=sender, subject=subject, template=template, args=args, header=[subject, "green"], delayed=(not now) if now!=None else self.flags.delay_emails, retry=3)
def get_reply(self, query): self.query = query.lower() self.setup() self.pre_process() # basic replies if self.query.split()[0] in ("hello", "hi"): return _("Hello {0}").format(frappe.utils.get_fullname()) if self.query == "help": return help_text.format(frappe.utils.get_fullname()) # build using parsers replies = [] for parser in frappe.get_hooks('bot_parsers'): reply = None try: reply = frappe.get_attr(parser)(self, query).get_reply() except frappe.PermissionError: reply = _("Oops, you are not allowed to know that") if reply: replies.append(reply) if replies: return '\n\n'.join(replies) if not reply: return _("Don't know, ask 'help'")
def main(app=None, module=None, doctype=None, verbose=False, tests=(), force=False): frappe.flags.print_messages = verbose frappe.flags.in_test = True if not frappe.db: frappe.connect() # if not frappe.conf.get("db_name").startswith("test_"): # raise Exception, 'db_name must start with "test_"' # workaround! since there is no separate test db frappe.clear_cache() frappe.utils.scheduler.disable_scheduler() set_test_email_config() if verbose: print 'Running "before_tests" hooks' for fn in frappe.get_hooks("before_tests", app_name=app): frappe.get_attr(fn)() if doctype: ret = run_tests_for_doctype(doctype, verbose=verbose, tests=tests, force=force) elif module: ret = run_tests_for_module(module, verbose=verbose, tests=tests) else: ret = run_all_tests(app, verbose) frappe.db.commit() # workaround! since there is no separate test db frappe.clear_cache() return ret
def get_context(context): hooks = frappe.get_hooks() return { "build_version": str(os.path.getmtime(os.path.join(frappe.local.sites_path, "assets", "js", "frappe.min.js"))), "include_js": hooks["app_include_js"], "include_css": hooks["app_include_css"], }
def install_app(name): """Install app, if app is not installed in local environment, install it via git url in `frappe/data/app_listing/`""" frappe.only_for("System Manager") if name not in frappe.get_all_apps(True): if not frappe.conf.disallow_app_listing: get_app(name) frappe.cache().delete_value(["app_hooks"]) # reload sys.path import site reload(site) else: # will only come via direct API frappe.throw("Listing app not allowed") app_hooks = frappe.get_hooks(app_name=name) if app_hooks.get('hide_in_installer'): frappe.throw(_("You cannot install this app")) frappe.publish_realtime("install_app_progress", {"status": _("Installing App {0}").format(name)}, user=frappe.session.user, now=True) frappe.installer.install_app(name) frappe.publish_realtime("install_app_progress", {"status": _("{0} Installed").format(name)}, user=frappe.session.user, now=True)
def get_context(context): if (frappe.session.user == "Guest" or frappe.db.get_value("User", frappe.session.user, "user_type")=="Website User"): frappe.throw(_("You are not permitted to access this page."), frappe.PermissionError) hooks = frappe.get_hooks() boot = frappe.sessions.get() # this needs commit csrf_token = frappe.sessions.get_csrf_token() frappe.db.commit() boot_json = frappe.as_json(boot) # remove script tags from boot boot_json = re.sub("\<script\>[^<]*\</script\>", "", boot_json) return { "build_version": get_build_version(), "include_js": hooks["app_include_js"], "include_css": hooks["app_include_css"], "boot": boot if context.get("for_mobile") else boot_json, "csrf_token": csrf_token, "background_image": boot.user.background_image or boot.default_background_image, "google_analytics_id": frappe.conf.get("google_analytics_id") }
def restrict_roles_and_modules(self): '''Disable all restricted roles and set `restrict_to_domain` property in Module Def''' active_domains = frappe.get_active_domains() all_domains = (frappe.get_hooks('domains') or {}).keys() def remove_role(role): frappe.db.sql('delete from `tabHas Role` where role=%s', role) frappe.set_value('Role', role, 'disabled', 1) for domain in all_domains: data = frappe.get_domain_data(domain) if not frappe.db.get_value('Domain', domain): frappe.get_doc(dict(doctype='Domain', domain=domain)).insert() if 'modules' in data: for module in data.get('modules'): frappe.db.set_value('Module Def', module, 'restrict_to_domain', domain) if 'restricted_roles' in data: for role in data['restricted_roles']: if not frappe.db.get_value('Role', role): frappe.get_doc(dict(doctype='Role', role_name=role)).insert() frappe.db.set_value('Role', role, 'restrict_to_domain', domain) if domain not in active_domains: remove_role(role)
def get_app_list(): """Get list of all apps with properties, installed, category from hooks and `frappe/data/app_listing/` if an entry exists""" out = {} installed = frappe.get_installed_apps() for app in frappe.get_all_apps(True): app_hooks = frappe.get_hooks(app_name=app) if app not in installed and app_hooks.get('hide_in_installer'): continue out[app] = {} for key in ("app_name", "app_title", "app_description", "app_icon", "app_publisher", "app_version", "app_url", "app_color"): val = app_hooks.get(key) or [] out[app][key] = val[0] if len(val) else "" if app in installed: out[app]["installed"] = 1 for app_from_list in get_app_listing().values(): if app_from_list.app_name in out: out[app_from_list.app_name].update(app_from_list) else: if not frappe.conf.disallow_app_listing: out[app_from_list.app_name] = app_from_list return out
def get_jloader(): import frappe if not getattr(frappe.local, 'jloader', None): from jinja2 import ChoiceLoader, PackageLoader, PrefixLoader if frappe.local.flags.in_setup_help: apps = ['frappe'] else: apps = frappe.get_hooks('template_apps') if not apps: apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps(sort=True) apps.reverse() if not "frappe" in apps: apps.append('frappe') frappe.local.jloader = ChoiceLoader( # search for something like app/templates/... [PrefixLoader(dict( (app, PackageLoader(app, ".")) for app in apps ))] # search for something like templates/... + [PackageLoader(app, ".") for app in apps] ) return frappe.local.jloader
def build_sitemap_options(path): sitemap_options = frappe._dict(frappe.get_doc("Website Route", path).as_dict()) home_page = get_home_page() sitemap_config = frappe.get_doc("Website Template", sitemap_options.get("website_template")).as_dict() # get sitemap config fields too for fieldname in ("base_template_path", "template_path", "controller", "no_cache", "no_sitemap", "page_name_field", "condition_field"): sitemap_options[fieldname] = sitemap_config.get(fieldname) sitemap_options.doctype = sitemap_options.ref_doctype sitemap_options.title = sitemap_options.page_title sitemap_options.pathname = sitemap_options.name # establish hierarchy sitemap_options.parents = frappe.db.sql("""select name, page_title from `tabWebsite Route` where lft < %s and rgt > %s order by lft asc""", (sitemap_options.lft, sitemap_options.rgt), as_dict=True) if not sitemap_options.no_sidebar: sitemap_options.children = get_route_children(sitemap_options.pathname, home_page) if not sitemap_options.children and sitemap_options.parent_website_route \ and sitemap_options.parent_website_route!=home_page: sitemap_options.children = get_route_children(sitemap_options.parent_website_route, home_page) # determine templates to be used if not sitemap_options.base_template_path: app_base = frappe.get_hooks("base_template") sitemap_options.base_template_path = app_base[0] if app_base else "templates/base.html" return sitemap_options
def send_login_mail(self, subject, template, add_args, now=None): """send mail with login details""" from frappe.utils.user import get_user_fullname from frappe.utils import get_url mail_titles = frappe.get_hooks().get("login_mail_title", []) title = frappe.db.get_default("company") or (mail_titles and mail_titles[0]) or "" full_name = get_user_fullname(frappe.session["user"]) if full_name == "Guest": full_name = "Administrator" args = { "first_name": self.first_name or self.last_name or "user", "user": self.name, "title": title, "login_url": get_url(), "user_fullname": full_name, } args.update(add_args) sender = frappe.session.user not in STANDARD_USERS and get_formatted_email(frappe.session.user) or None frappe.sendmail( recipients=self.email, sender=sender, subject=subject, message=frappe.get_template(template).render(args), delayed=(not now) if now != None else self.flags.delay_emails, )
def main(app=None, module=None, doctype=None, verbose=False, tests=(), force=False, profile=False): frappe.flags.print_messages = verbose frappe.flags.in_test = True if not frappe.db: frappe.connect() # if not frappe.conf.get("db_name").startswith("test_"): # raise Exception, 'db_name must start with "test_"' # workaround! since there is no separate test db frappe.clear_cache() frappe.utils.scheduler.disable_scheduler() set_test_email_config() if verbose: print 'Running "before_tests" hooks' for fn in frappe.get_hooks("before_tests", app_name=app): frappe.get_attr(fn)() if doctype: ret = run_tests_for_doctype(doctype, verbose, tests, force, profile) elif module: ret = run_tests_for_module(module, verbose, tests, profile) else: ret = run_all_tests(app, verbose, profile) frappe.db.commit() # workaround! since there is no separate test db frappe.clear_cache() return ret
def get_custom_page_renderers(): custom_renderers = [] for renderer_path in frappe.get_hooks('page_renderer') or []: try: renderer = frappe.get_attr(renderer_path) if not hasattr(renderer, 'can_render'): click.echo( f'{renderer.__name__} does not have can_render method') continue if not hasattr(renderer, 'render'): click.echo( f'{renderer.__name__} does not have render method') continue custom_renderers.append(renderer) except Exception: click.echo( f'Failed to load page renderer. Import path: {renderer_path}' ) return custom_renderers
def get_footer(email_account, footer=None): """append a footer (signature)""" footer = footer or "" if email_account and email_account.footer: footer += '<div style="margin: 15px auto;">{0}</div>'.format( email_account.footer) footer += "<!--unsubscribe link here-->" company_address = frappe.db.get_default("email_footer_address") if company_address: footer += '<div style="margin: 15px auto; text-align: center; color: #8d99a6">{0}</div>'\ .format(company_address.replace("\n", "<br>")) if not cint(frappe.db.get_default("disable_standard_email_footer")): for default_mail_footer in frappe.get_hooks("default_mail_footer"): footer += '<div style="margin: 15px auto;">{0}</div>'.format( default_mail_footer) return footer
def _get_controller(): from frappe.model.document import Document from frappe.utils.nestedset import NestedSet module_name, custom = frappe.db.get_value("DocType", doctype, ("module", "custom"), cache=True) \ or ["Core", False] if custom: if frappe.db.field_exists("DocType", "is_tree"): is_tree = frappe.db.get_value("DocType", doctype, "is_tree", cache=True) else: is_tree = False _class = NestedSet if is_tree else Document else: class_overrides = frappe.get_hooks('override_doctype_class') if class_overrides and class_overrides.get(doctype): import_path = class_overrides[doctype][-1] module_path, classname = import_path.rsplit('.', 1) module = frappe.get_module(module_path) if not hasattr(module, classname): raise ImportError( '{0}: {1} does not exist in module {2}'.format( doctype, classname, module_path)) else: module = load_doctype_module(doctype, module_name) classname = doctype.replace(" ", "").replace("-", "") if hasattr(module, classname): _class = getattr(module, classname) if issubclass(_class, BaseDocument): _class = getattr(module, classname) else: raise ImportError(doctype) else: raise ImportError(doctype) return _class
def set_filters(jenv): import frappe from frappe.utils import global_date_format, cint, cstr, flt from frappe.website.utils import get_shade, with_leading_slash from markdown2 import markdown from json import dumps jenv.filters["global_date_format"] = global_date_format jenv.filters["markdown"] = markdown jenv.filters["json"] = dumps jenv.filters["get_shade"] = get_shade jenv.filters["len"] = len jenv.filters["int"] = cint jenv.filters["str"] = cstr jenv.filters["flt"] = flt jenv.filters["with_leading_slash"] = with_leading_slash # load jenv_filters from hooks.py for app in frappe.get_installed_apps(): for jenv_filter in (frappe.get_hooks(app_name=app).jenv_filter or []): filter_name, filter_function = jenv_filter.split(":") jenv.filters[filter_name] = frappe.get_attr(filter_function)
def clear_cache(path=None): '''Clear website caches :param path: (optional) for the given path''' for key in ('website_generator_routes', 'website_pages', 'website_full_index'): frappe.cache().delete_value(key) frappe.cache().delete_value("website_404") if path: frappe.cache().hdel('website_redirects', path) delete_page_cache(path) else: clear_sitemap() frappe.clear_cache("Guest") for key in ('portal_menu_items', 'home_page', 'website_route_rules', 'doctypes_with_web_view', 'website_redirects', 'page_context', 'website_page'): frappe.cache().delete_value(key) for method in frappe.get_hooks("website_clear_cache"): frappe.get_attr(method)(path)
def update_global_search_doctypes(): global_search_doctypes = [] show_message(1, _("Fetching default Global Search documents.")) installed_apps = [app for app in frappe.get_installed_apps() if app] active_domains = [domain for domain in frappe.get_active_domains() if domain] active_domains.append("Default") for app in installed_apps: search_doctypes = frappe.get_hooks(hook="global_search_doctypes", app_name=app) if not search_doctypes: continue for domain in active_domains: if search_doctypes.get(domain): global_search_doctypes.extend(search_doctypes.get(domain)) doctype_list = set([dt.name for dt in frappe.get_list("DocType")]) allowed_in_global_search = [] for dt in global_search_doctypes: if dt.get("index") is not None: allowed_in_global_search.insert(dt.get("index"), dt.get("doctype")) continue allowed_in_global_search.append(dt.get("doctype")) show_message(2, _("Setting up Global Search documents.")) global_search_settings = frappe.get_single("Global Search Settings") global_search_settings.allowed_in_global_search = [] for dt in allowed_in_global_search: if dt not in doctype_list: continue global_search_settings.append("allowed_in_global_search", { "document_type": dt }) global_search_settings.save(ignore_permissions=True) show_message(3, "Global Search Documents have been reset.")
def set_filters(jenv): import frappe from frappe.utils import global_date_format, cint, cstr, flt, markdown from frappe.website.utils import get_shade, abs_url jenv.filters["global_date_format"] = global_date_format jenv.filters["markdown"] = markdown jenv.filters["json"] = frappe.as_json jenv.filters["get_shade"] = get_shade jenv.filters["len"] = len jenv.filters["int"] = cint jenv.filters["str"] = cstr jenv.filters["flt"] = flt jenv.filters["abs_url"] = abs_url if frappe.flags.in_setup_help: return # load jenv_filters from hooks.py for app in frappe.get_installed_apps(): for jenv_filter in (frappe.get_hooks(app_name=app).jenv_filter or []): filter_name, filter_function = jenv_filter.split(":") jenv.filters[filter_name] = frappe.get_attr(filter_function)
def execute_cmd(cmd, from_async=False): """execute a request as python module""" for hook in frappe.get_hooks("override_whitelisted_methods", {}).get(cmd, []): # override using the first hook cmd = hook break try: method = get_attr(cmd) except: frappe.throw('Invalid method', frappe.NotFound) if from_async: method = method.queue is_whitelisted(method) ret = frappe.call(method, **frappe.form_dict) # returns with a message if ret: frappe.response['message'] = ret
def notifiy_stopped_entities_status(): """ Notify User about stopped entities """ filters = { "is_synced": "Stopped" } fields = ["name", "sync_doctype", "sync_docname", "sync_attempts"] entities = frappe.db.get_values("Sync Error Log", filters, fields, as_dict=True) if not entities: return recipients = frappe.get_hooks("error_report_email", app_name="erpnext") subject = "Magento >> ERP Sync Failed" template = "templates/emails/failed_synced.html" frappe.sendmail(recipients=recipients, subject=subject, message=frappe.get_template(template).render({"entities": entities})) # update notification sent status query = """ update `tabSync Error Log` set notification_sent='Yes' where name in ({names}) """.format( names=",".join(["'%s'"%entity.get("name") for entity in entities]) ) frappe.db.sql(query)
def make(self): """build into msg_root""" headers = { "Subject": strip(self.subject).encode("utf-8"), "From": self.sender.encode("utf-8"), "To": ', '.join(self.recipients).encode("utf-8"), "Date": email.utils.formatdate(), "Reply-To": self.reply_to.encode("utf-8") if self.reply_to else None, "CC": ', '.join(self.cc).encode("utf-8") if self.cc else None, b'X-Frappe-Site': get_url().encode('utf-8'), } # reset headers as values may be changed. for key, val in headers.iteritems(): if self.msg_root.has_key(key): del self.msg_root[key] self.msg_root[key] = val # call hook to enable apps to modify msg_root before sending for hook in frappe.get_hooks("make_email_body_message"): frappe.get_attr(hook)(self)
def send_welcome_mail_to_user(self): from frappe.utils import get_url link = self.reset_password() app_title = None method = frappe.get_hooks('get_site_info') if method: get_site_info = frappe.get_attr(method[0]) site_info = get_site_info({}) app_title = site_info.get('company', None) if app_title: subject = _("Welcome to {0}").format(app_title) else: subject = _("Complete Registration") self.send_login_mail(subject, "new_user", dict( link=link, site_url=get_url(), ))
def _get(): subscribed_documents = get_subscribed_documents() config = frappe._dict() hooks = frappe.get_hooks() if hooks: for notification_config in hooks.notification_config: nc = frappe.get_attr(notification_config)() for key in ("for_doctype", "for_module", "for_other", "targets"): config.setdefault(key, {}) if key == "for_doctype": if len(subscribed_documents) > 0: key_config = nc.get(key, {}) subscribed_docs_config = frappe._dict() for document in subscribed_documents: if key_config.get(document): subscribed_docs_config[document] = key_config.get(document) config[key].update(subscribed_docs_config) else: config[key].update(nc.get(key, {})) else: config[key].update(nc.get(key, {})) return config
def get_list_context(context=None): website_settings = frappe.get_doc("Website Settings") featured_blog = None if website_settings.featured_blog: featured_blog = frappe.get_doc("Blog Post", website_settings.featured_blog).as_dict() list_context = frappe._dict( template = "templates/includes/blog/blog.html", get_list = get_blog_list, hide_filters = True, children = get_children(), # show_search = True, title = _('Blog'), page_heading_template = frappe.get_hooks('blogs_page_heading_template') or 'website/doctype/blog_post/templates/blog_post_header.html', featured_blog = featured_blog, background_image = website_settings.blog_header or None ) category = sanitize_html(frappe.local.form_dict.blog_category or frappe.local.form_dict.category) if category: category_title = get_blog_category(category) list_context.sub_title = _("Posts filed under {0}").format(category_title) list_context.title = category_title elif frappe.local.form_dict.blogger: blogger = frappe.db.get_value("Blogger", {"name": frappe.local.form_dict.blogger}, "full_name") list_context.sub_title = _("Posts by {0}").format(blogger) list_context.title = blogger elif frappe.local.form_dict.txt: list_context.sub_title = _('Filtered by "{0}"').format(sanitize_html(frappe.local.form_dict.txt)) if list_context.sub_title: list_context.parents = [{"name": _("Home"), "route": "/"}, {"name": "Blog", "route": "/blog"}] else: list_context.parents = [{"name": _("Home"), "route": "/"}] list_context.update(frappe.get_doc("Blog Settings", "Blog Settings").as_dict(no_default_fields=True)) return list_context
def import_source_messages(): """Import messagse from apps listed in **Translator App** as **Source Message**""" frappe.db.sql("update `tabSource Message` set disabled=1") for app in frappe.db.sql_list("select name from `tabTranslator App`"): app_version = frappe.get_hooks(app_name='frappe')['app_version'][0] messages = get_messages_for_app(app) for message in messages: source_message = frappe.db.get_value("Source Message", {"message": message[1]}, ["name", "message", "position", "app_version"], as_dict=True) if source_message: d = frappe.get_doc("Source Message", source_message['name']) if source_message["position"] != message[0] or source_message["app_version"] != app_version: d.app_version = app_version d.position = message[0] d.app = app d.disabled = 0 else: d = frappe.new_doc("Source Message") d.position = message[0] d.message = message[1] d.app = app d.app_version = app_version d.save()
def get_footer(email_account, footer=None): """append a footer (signature)""" footer = footer or "" args = {} if email_account and email_account.footer: args.update({'email_account_footer': email_account.footer}) company_address = frappe.db.get_default("email_footer_address") if company_address: args.update({'company_address': company_address}) if not cint(frappe.db.get_default("disable_standard_email_footer")): args.update( {'default_mail_footer': frappe.get_hooks('default_mail_footer')}) footer += frappe.utils.jinja.get_email_from_template('email_footer', args)[0] return footer
def get_formatted_messages(): message_map = frappe._dict({}) for app in get_apps_to_be_translated(): messages = get_messages_for_app(app, False) # messages structure # [(position, source_text_1, context, line_no), (position, source_text_2)] for message_data in messages: position = message_data[0] message = message_data[1] context = message_data[2] or '' if len(message_data) > 2 else '' line_no = message_data[3] or 0 if len(message_data) == 4 else 0 position_dict = frappe._dict({ 'position': position, 'line_no': line_no, 'app': app, 'app_version': frappe.get_hooks(app_name=app).get('app_version', [''])[0] }) if not message_map.get((message, context)): message_map[(message, context)] = [position_dict] else: message_map[(message, context)].append(position_dict) return message_map
def get_messages_from_workflow(doctype=None, app_name=None): assert doctype or app_name, 'doctype or app_name should be provided' # translations for Workflows workflows = [] if doctype: workflows = frappe.get_all('Workflow', filters={'document_type': doctype}) else: fixtures = frappe.get_hooks('fixtures', app_name=app_name) or [] for fixture in fixtures: if isinstance(fixture, string_types) and fixture == 'Worflow': workflows = frappe.get_all('Workflow') break elif isinstance(fixture, dict) and fixture.get('dt', fixture.get('doctype')) == 'Workflow': workflows.extend(frappe.get_all('Workflow', filters=fixture.get('filters'))) messages = [] for w in workflows: states = frappe.db.sql( 'select distinct state from `tabWorkflow Document State` where parent=%s', (w['name'],), as_dict=True) messages.extend([('Workflow: ' + w['name'], state['state']) for state in states if is_translatable(state['state'])]) states = frappe.db.sql( 'select distinct message from `tabWorkflow Document State` where parent=%s and message is not null', (w['name'],), as_dict=True) messages.extend([("Workflow: " + w['name'], state['message']) for state in states if is_translatable(state['message'])]) actions = frappe.db.sql( 'select distinct action from `tabWorkflow Transition` where parent=%s', (w['name'],), as_dict=True) messages.extend([("Workflow: " + w['name'], action['action']) \ for action in actions if is_translatable(action['action'])]) return messages
def get_footer(email_account, footer=None): """append a footer (signature)""" footer = footer or "" args = {} if email_account and email_account.footer: args.update({"email_account_footer": email_account.footer}) sender_address = frappe.db.get_default("email_footer_address") if sender_address: args.update({"sender_address": sender_address}) if not cint(frappe.db.get_default("disable_standard_email_footer")): args.update( {"default_mail_footer": frappe.get_hooks("default_mail_footer")}) footer += frappe.utils.jinja.get_email_from_template("email_footer", args)[0] return footer
def reset_website_customer(): settings = get_shopping_cart_settings() #flag order_for feature as disabled if stopped by the sales team user frappe.session.data.order_for['enabled'] = False customer_name = frappe.session.data.order_for.get("customer_name") if frappe.session.data.order_for.get("customer_name"): del frappe.session.data.order_for["customer_name"] if frappe.session.data.order_for.get("customer_primary_contact_name"): del frappe.session.data.order_for["customer_primary_contact_name"] if settings.get("stop_order_for_behavior") == "Reload": url = "Reload" if settings.get("stop_order_for_behavior" ) == "Back to Customer Record" and customer_name: url = "/desk#Form/Customer/{}".format(customer_name) else: url = settings.get("stop_order_for_url", "") or "Reload" # Hook: Allows overriding the routing url after a user resets the website customer # # Signature: # override_stop_order_for_url(url) # # Args: # url: The current route # # Returns: # Hook expects a string or None to override the route hooks = frappe.get_hooks("override_stop_order_for_url") or [] for method in hooks: url = frappe.call(method, url=url) or url if not url: url = "Reload" return url
def confirm_payment(redirect_flow_id, reference_doctype, reference_docname): client = gocardless_initialization(reference_docname) try: redirect_flow = client.redirect_flows.complete( redirect_flow_id, params={"session_token": frappe.session.user} ) confirmation_url = redirect_flow.confirmation_url gocardless_success_page = frappe.get_hooks("gocardless_success_page") if gocardless_success_page: confirmation_url = frappe.get_attr(gocardless_success_page[-1])( reference_doctype, reference_docname ) data = { "mandate": redirect_flow.links.mandate, "customer": redirect_flow.links.customer, "redirect_to": confirmation_url, "redirect_message": "Mandate successfully created", "reference_doctype": reference_doctype, "reference_docname": reference_docname, } try: create_mandate(data) except Exception as e: frappe.log_error(e, "GoCardless Mandate Registration Error") gateway_controller = get_gateway_controller(reference_docname) frappe.get_doc("GoCardless Settings", gateway_controller).create_payment_request(data) return {"redirect_to": confirmation_url} except Exception as e: frappe.log_error(e, "GoCardless Payment Error") return {"redirect_to": "/integrations/payment-failed"}
def install_app(name, verbose=False, set_as_patched=True): frappe.flags.in_install_app = name frappe.clear_cache() app_hooks = frappe.get_hooks(app_name=name) installed_apps = frappe.get_installed_apps() if name not in frappe.get_all_apps(with_frappe=True): raise Exception("App not in apps.txt") if name in installed_apps: print "App Already Installed" frappe.msgprint("App {0} already installed".format(name)) return if name != "frappe": frappe.only_for("System Manager") for before_install in app_hooks.before_install or []: frappe.get_attr(before_install)() if name != "frappe": add_module_defs(name) sync_for(name, force=True, sync_everything=True, verbose=verbose) add_to_installed_apps(name) if set_as_patched: set_all_patches_as_completed(name) for after_install in app_hooks.after_install or []: frappe.get_attr(after_install)() print "Installing Fixtures..." sync_fixtures(name) frappe.flags.in_install_app = False
def init_request(request): frappe.local.request = request frappe.local.is_ajax = frappe.get_request_header( "X-Requested-With") == "XMLHttpRequest" site = _site or request.headers.get('X-Frappe-Site-Name') or get_site_name( request.host) frappe.init(site=site, sites_path=_sites_path) for fn in frappe.get_hooks("on_frappe_start"): frappe.get_attr(fn)() if not (frappe.local.conf and frappe.local.conf.db_name): # site does not exist raise NotFound if frappe.local.conf.get('maintenance_mode'): frappe.connect() raise frappe.SessionStopped('Session Stopped') make_form_dict(request) frappe.local.http_request = frappe.auth.HTTPRequest()
def make_controller_template(self): from frappe.modules import get_doc_path, get_module_path, scrub pypath = os.path.join( get_doc_path(self.module, self.doctype, self.name), scrub(self.name) + '.py') if not os.path.exists(pypath): # get app publisher for copyright app = frappe.local.module_app[frappe.scrub(self.module)] if not app: frappe.throw(_("App not found")) app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0] with open(pypath, 'w') as pyfile: with open( os.path.join(get_module_path("core"), "doctype", "doctype", "doctype_template.py"), 'r') as srcfile: pyfile.write(srcfile.read().format( app_publisher=app_publisher, classname=self.name.replace(" ", "")))
def send_welcome_mail_to_user(self): from frappe.utils import get_url link = self.reset_password() subject = None method = frappe.get_hooks("welcome_email") if method: subject = frappe.get_attr(method[-1])() if not subject: site_name = frappe.db.get_default("site_name") or frappe.get_conf().get("site_name") if site_name: subject = _("Welcome to {0}").format(site_name) else: subject = _("Complete Registration") self.send_login_mail( subject, "new_user", dict( link=link, site_url=get_url(), ), )
def install_app(name): """Install app, if app is not installed in local environment, install it via git url in `frappe/data/app_listing/`""" frappe.only_for("System Manager") if name not in frappe.get_all_apps(True): if not frappe.conf.disallow_app_listing: get_app(name) frappe.cache().delete_value(["app_hooks"]) # reload sys.path import site reload(site) else: # will only come via direct API frappe.throw(_("Listing app not allowed")) app_hooks = frappe.get_hooks(app_name=name) if app_hooks.get('hide_in_installer'): frappe.throw(_("You cannot install this app")) enqueue('frappe.desk.page.applications.applications.start_install', name=name) frappe.msgprint(_('Queued for install'))
def get_dashboard_data(self): '''Returns dashboard setup related to this doctype. This method will return the `data` property in the `[doctype]_dashboard.py` file in the doctype's folder, along with any overrides or extensions implemented in other Frappe applications via hooks. ''' data = frappe._dict() if not self.custom: try: module = load_doctype_module(self.name, suffix='_dashboard') if hasattr(module, 'get_data'): data = frappe._dict(module.get_data()) except ImportError: pass self.add_doctype_links(data) if not self.custom: for hook in frappe.get_hooks("override_doctype_dashboards", {}).get(self.name, []): data = frappe._dict(frappe.get_attr(hook)(data=data)) return data
def execute_cmd(cmd, from_async=False): """execute a request as python module""" for hook in frappe.get_hooks("override_whitelisted_methods", {}).get(cmd, []): # override using the first hook cmd = hook break try: method = get_attr(cmd) except: frappe.respond_as_web_page(title='Invalid Method', html='Method not found', indicator_color='red', http_status_code=404) return if from_async: method = method.queue is_whitelisted(method) return frappe.call(method, **frappe.form_dict)
def add_sidebar_and_breadcrumbs(context): '''Add sidebar and breadcrumbs to context''' from frappe.website.router import get_page_info_from_template if context.show_sidebar: context.no_cache = 1 add_sidebar_data(context) else: if context.basepath: hooks = frappe.get_hooks('look_for_sidebar_json') look_for_sidebar_json = hooks[0] if hooks else 0 sidebar_json_path = get_sidebar_json_path(context.basepath, look_for_sidebar_json) if sidebar_json_path: load_sidebar(context, sidebar_json_path) if context.add_breadcrumbs and not context.parents: if context.basepath: parent_path = os.path.dirname(context.path).rstrip('/') page_info = get_page_info_from_template(parent_path) if page_info: context.parents = [ dict(route=parent_path, title=page_info.title) ]
def build_sitemap_options(path): sitemap_options = frappe.doc("Website Route", path).fields home_page = get_home_page() sitemap_config = frappe.doc("Website Template", sitemap_options.get("website_template")).fields # get sitemap config fields too for fieldname in ("base_template_path", "template_path", "controller", "no_cache", "no_sitemap", "page_name_field", "condition_field"): sitemap_options[fieldname] = sitemap_config.get(fieldname) sitemap_options.doctype = sitemap_options.ref_doctype sitemap_options.title = sitemap_options.page_title sitemap_options.pathname = sitemap_options.name # establish hierarchy sitemap_options.parents = frappe.db.sql( """select name, page_title from `tabWebsite Route` where lft < %s and rgt > %s order by lft asc""", (sitemap_options.lft, sitemap_options.rgt), as_dict=True) if not sitemap_options.no_sidebar: set_sidebar_items(sitemap_options, sitemap_options.pathname, home_page) if not sitemap_options.children: set_sidebar_items(sitemap_options, sitemap_options.parent_website_route, home_page) # determine templates to be used if not sitemap_options.base_template_path: app_base = frappe.get_hooks("base_template") sitemap_options.base_template_path = app_base[ 0] if app_base else "templates/base.html" return sitemap_options