@@ -3,6 +3,7 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
import os | |||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
from frappe.build import html_to_js_template | from frappe.build import html_to_js_template | ||||
from frappe.model.utils import render_include | from frappe.model.utils import render_include | ||||
@@ -49,15 +50,10 @@ class Page(Document): | |||||
from frappe.core.doctype.doctype.doctype import make_module_and_roles | from frappe.core.doctype.doctype.doctype import make_module_and_roles | ||||
make_module_and_roles(self, "roles") | make_module_and_roles(self, "roles") | ||||
if not frappe.flags.in_import and getattr(conf,'developer_mode', 0) and self.standard=='Yes': | |||||
from frappe.modules.export_file import export_to_files | |||||
from frappe.modules import get_module_path, scrub | |||||
import os | |||||
export_to_files(record_list=[['Page', self.name]]) | |||||
# write files | |||||
path = os.path.join(get_module_path(self.module), 'page', scrub(self.name), scrub(self.name)) | |||||
from frappe.modules.utils import export_module_json | |||||
path = export_module_json(self, self.standard=='Yes', self.module) | |||||
if path: | |||||
# js | # js | ||||
if not os.path.exists(path + '.js'): | if not os.path.exists(path + '.js'): | ||||
with open(path + '.js', 'w') as f: | with open(path + '.js', 'w') as f: | ||||
@@ -72,6 +72,21 @@ frappe.ui.form.on("Customize Form", { | |||||
frm.add_custom_button(__('Reset to defaults'), function() { | frm.add_custom_button(__('Reset to defaults'), function() { | ||||
frappe.customize_form.confirm(__('Remove all customizations?'), frm); | frappe.customize_form.confirm(__('Remove all customizations?'), frm); | ||||
}, "icon-eraser", "btn-default"); | }, "icon-eraser", "btn-default"); | ||||
if(frappe.boot.developer_mode) { | |||||
frm.add_custom_button(__('Export Customizations'), function() { | |||||
frappe.prompt({fieldtype:'Link', fieldname:'module', options:'Module Def', | |||||
label: __('Module to Export')}, function(data) { | |||||
frappe.call({ | |||||
method: 'frappe.modules.utils.export_customizations', | |||||
args: { | |||||
doctype: frm.doc.doc_type, | |||||
module: data.module | |||||
} | |||||
}); | |||||
}); | |||||
}) | |||||
} | |||||
} | } | ||||
// sort order select | // sort order select | ||||
@@ -49,6 +49,7 @@ frappe.ui.form.on("Email Alert", { | |||||
}, | }, | ||||
refresh: function(frm) { | refresh: function(frm) { | ||||
frappe.email_alert.setup_fieldname_select(frm); | frappe.email_alert.setup_fieldname_select(frm); | ||||
frm.get_field("is_standard").toggle(frappe.boot.developer_mode); | |||||
frm.trigger('event'); | frm.trigger('event'); | ||||
}, | }, | ||||
document_type: function(frm) { | document_type: function(frm) { | ||||
@@ -1,7 +1,7 @@ | |||||
{ | { | ||||
"allow_copy": 0, | "allow_copy": 0, | ||||
"allow_import": 0, | "allow_import": 0, | ||||
"allow_rename": 0, | |||||
"allow_rename": 1, | |||||
"autoname": "", | "autoname": "", | ||||
"beta": 0, | "beta": 0, | ||||
"creation": "2014-07-11 17:18:09.923399", | "creation": "2014-07-11 17:18:09.923399", | ||||
@@ -114,6 +114,60 @@ | |||||
"set_only_once": 0, | "set_only_once": 0, | ||||
"unique": 0 | "unique": 0 | ||||
}, | }, | ||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"fieldname": "is_standard", | |||||
"fieldtype": "Check", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Is Standard", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_if_no_value": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | |||||
"allow_on_submit": 0, | |||||
"bold": 0, | |||||
"collapsible": 0, | |||||
"columns": 0, | |||||
"depends_on": "is_standard", | |||||
"fieldname": "module", | |||||
"fieldtype": "Link", | |||||
"hidden": 0, | |||||
"ignore_user_permissions": 0, | |||||
"ignore_xss_filter": 0, | |||||
"in_filter": 0, | |||||
"in_list_view": 0, | |||||
"label": "Module", | |||||
"length": 0, | |||||
"no_copy": 0, | |||||
"options": "Module Def", | |||||
"permlevel": 0, | |||||
"precision": "", | |||||
"print_hide": 0, | |||||
"print_hide_if_no_value": 0, | |||||
"read_only": 0, | |||||
"report_hide": 0, | |||||
"reqd": 0, | |||||
"search_index": 0, | |||||
"set_only_once": 0, | |||||
"unique": 0 | |||||
}, | |||||
{ | { | ||||
"allow_on_submit": 0, | "allow_on_submit": 0, | ||||
"bold": 0, | "bold": 0, | ||||
@@ -154,7 +208,7 @@ | |||||
"label": "Send Alert On", | "label": "Send Alert On", | ||||
"length": 0, | "length": 0, | ||||
"no_copy": 0, | "no_copy": 0, | ||||
"options": "\nNew\nSave\nSubmit\nCancel\nDays After\nDays Before\nValue Change", | |||||
"options": "\nNew\nSave\nSubmit\nCancel\nDays After\nDays Before\nValue Change\nCustom", | |||||
"permlevel": 0, | "permlevel": 0, | ||||
"print_hide": 0, | "print_hide": 0, | ||||
"print_hide_if_no_value": 0, | "print_hide_if_no_value": 0, | ||||
@@ -429,6 +483,8 @@ | |||||
"bold": 0, | "bold": 0, | ||||
"collapsible": 0, | "collapsible": 0, | ||||
"columns": 0, | "columns": 0, | ||||
"default": "Add your message here", | |||||
"depends_on": "is_standard", | |||||
"fieldname": "message", | "fieldname": "message", | ||||
"fieldtype": "Text", | "fieldtype": "Text", | ||||
"hidden": 0, | "hidden": 0, | ||||
@@ -444,7 +500,7 @@ | |||||
"print_hide_if_no_value": 0, | "print_hide_if_no_value": 0, | ||||
"read_only": 0, | "read_only": 0, | ||||
"report_hide": 0, | "report_hide": 0, | ||||
"reqd": 1, | |||||
"reqd": 0, | |||||
"search_index": 0, | "search_index": 0, | ||||
"set_only_once": 0, | "set_only_once": 0, | ||||
"unique": 0 | "unique": 0 | ||||
@@ -540,7 +596,7 @@ | |||||
"istable": 0, | "istable": 0, | ||||
"max_attachments": 0, | "max_attachments": 0, | ||||
"menu_index": 0, | "menu_index": 0, | ||||
"modified": "2016-09-16 06:44:25.239921", | |||||
"modified": "2016-10-02 12:36:31.442369", | |||||
"modified_by": "Administrator", | "modified_by": "Administrator", | ||||
"module": "Email", | "module": "Email", | ||||
"name": "Email Alert", | "name": "Email Alert", | ||||
@@ -3,11 +3,13 @@ | |||||
from __future__ import unicode_literals | from __future__ import unicode_literals | ||||
import frappe | import frappe | ||||
import json | |||||
import json, os | |||||
from frappe import _ | from frappe import _ | ||||
from frappe.model.document import Document | from frappe.model.document import Document | ||||
from frappe.utils import validate_email_add, nowdate | from frappe.utils import validate_email_add, nowdate | ||||
from frappe.utils.jinja import validate_template | from frappe.utils.jinja import validate_template | ||||
from frappe.modules.utils import export_module_json, get_doc_module | |||||
from markdown2 import markdown | |||||
class EmailAlert(Document): | class EmailAlert(Document): | ||||
def autoname(self): | def autoname(self): | ||||
@@ -26,6 +28,26 @@ class EmailAlert(Document): | |||||
self.validate_forbidden_types() | self.validate_forbidden_types() | ||||
self.validate_condition() | self.validate_condition() | ||||
def on_update(self): | |||||
path = export_module_json(self, self.is_standard, self.module) | |||||
if path: | |||||
# js | |||||
if not os.path.exists(path + '.md') and not os.path.exists(path + '.html'): | |||||
with open(path + '.md', 'w') as f: | |||||
f.write(self.message) | |||||
# py | |||||
if not os.path.exists(path + '.py'): | |||||
with open(path + '.py', 'w') as f: | |||||
f.write("""from __future__ import unicode_literals | |||||
import frappe | |||||
def get_context(context): | |||||
# do your magic here | |||||
pass | |||||
""") | |||||
def validate_condition(self): | def validate_condition(self): | ||||
temp_doc = frappe.new_doc(self.document_type) | temp_doc = frappe.new_doc(self.document_type) | ||||
if self.condition: | if self.condition: | ||||
@@ -63,6 +85,67 @@ class EmailAlert(Document): | |||||
return docs | return docs | ||||
def send(self, doc): | |||||
'''Build recipients and send email alert''' | |||||
context = get_context(doc) | |||||
for recipient in self.recipients: | |||||
recipients = [] | |||||
if recipient.condition: | |||||
if not eval(recipient.condition, context): | |||||
continue | |||||
if recipient.email_by_document_field: | |||||
if validate_email_add(doc.get(recipient.email_by_document_field)): | |||||
recipients.append(doc.get(recipient.email_by_document_field)) | |||||
# else: | |||||
# print "invalid email" | |||||
if recipient.cc: | |||||
recipient.cc = recipient.cc.replace(",", "\n") | |||||
recipients = recipients + recipient.cc.split("\n") | |||||
if not recipients: | |||||
return | |||||
subject = self.subject | |||||
context = {"doc": doc, "alert": self, "comments": None} | |||||
if self.is_standard: | |||||
self.load_standard_properties(context) | |||||
if doc.get("_comments"): | |||||
context["comments"] = json.loads(doc.get("_comments")) | |||||
if "{" in subject: | |||||
subject = frappe.render_template(self.subject, context) | |||||
frappe.sendmail(recipients=recipients, subject=subject, | |||||
message= frappe.render_template(self.message, context), | |||||
reference_doctype = doc.doctype, | |||||
reference_name = doc.name, | |||||
attachments = [frappe.attach_print(doc.doctype, doc.name)] if self.attach_print else None) | |||||
def load_standard_properties(self, context): | |||||
module = get_doc_module(self.module, self.doctype, self.name) | |||||
if module: | |||||
if hasattr(module, 'get_context'): | |||||
out = module.get_context(context) | |||||
if out: context.update(out) | |||||
def load_template(extn): | |||||
template_path = os.path.join(os.path.dirname(module.__file__), | |||||
frappe.scrub(self.name) + extn) | |||||
if os.path.exists(template_path): | |||||
with open(template_path, 'r') as f: | |||||
self.message = f.read() | |||||
return True | |||||
# get template | |||||
if not load_template('.html'): | |||||
if load_template('.md'): | |||||
self.message = markdown(self.message) | |||||
@frappe.whitelist() | @frappe.whitelist() | ||||
def get_documents_for_today(email_alert): | def get_documents_for_today(email_alert): | ||||
email_alert = frappe.get_doc('Email Alert', email_alert) | email_alert = frappe.get_doc('Email Alert', email_alert) | ||||
@@ -121,41 +204,12 @@ def evaluate_alert(doc, alert, event): | |||||
doc.name, alert.value_changed): | doc.name, alert.value_changed): | ||||
return # value not changed | return # value not changed | ||||
for recipient in alert.recipients: | |||||
recipients = [] | |||||
if recipient.condition: | |||||
if not eval(recipient.condition, context): | |||||
continue | |||||
if recipient.email_by_document_field: | |||||
if validate_email_add(doc.get(recipient.email_by_document_field)): | |||||
recipients.append(doc.get(recipient.email_by_document_field)) | |||||
# else: | |||||
# print "invalid email" | |||||
if recipient.cc: | |||||
recipient.cc = recipient.cc.replace(",", "\n") | |||||
recipients = recipients + recipient.cc.split("\n") | |||||
if not recipients: | |||||
return | |||||
subject = alert.subject | |||||
if event != "Value Change" and not doc.is_new(): | if event != "Value Change" and not doc.is_new(): | ||||
# reload the doc for the latest values & comments, | # reload the doc for the latest values & comments, | ||||
# except for validate type event. | # except for validate type event. | ||||
doc = frappe.get_doc(doc.doctype, doc.name) | doc = frappe.get_doc(doc.doctype, doc.name) | ||||
context = {"doc": doc, "alert": alert, "comments": None} | |||||
if doc.get("_comments"): | |||||
context["comments"] = json.loads(doc.get("_comments")) | |||||
if "{" in subject: | |||||
subject = frappe.render_template(alert.subject, context) | |||||
frappe.sendmail(recipients=recipients, subject=subject, | |||||
message= frappe.render_template(alert.message, context), reference_doctype = doc.doctype, reference_name = doc.name, | |||||
attachments = [frappe.attach_print(doc.doctype, doc.name)] if alert.attach_print else None) | |||||
alert.send(doc) | |||||
def get_context(doc): | def get_context(doc): | ||||
return {"doc": doc, "nowdate": nowdate} | return {"doc": doc, "nowdate": nowdate} | ||||
@@ -13,6 +13,7 @@ from frappe.desk.notifications import clear_notifications | |||||
from frappe.website import render | from frappe.website import render | ||||
from frappe.desk.doctype.desktop_icon.desktop_icon import sync_desktop_icons | from frappe.desk.doctype.desktop_icon.desktop_icon import sync_desktop_icons | ||||
from frappe.core.doctype.language.language import sync_languages | from frappe.core.doctype.language.language import sync_languages | ||||
from frappe.modules.utils import sync_customizations | |||||
import frappe.utils.help | import frappe.utils.help | ||||
def migrate(verbose=True, rebuild_website=False): | def migrate(verbose=True, rebuild_website=False): | ||||
@@ -31,6 +32,7 @@ def migrate(verbose=True, rebuild_website=False): | |||||
frappe.model.sync.sync_all(verbose=verbose) | frappe.model.sync.sync_all(verbose=verbose) | ||||
frappe.translate.clear_cache() | frappe.translate.clear_cache() | ||||
sync_fixtures() | sync_fixtures() | ||||
sync_customizations() | |||||
sync_desktop_icons() | sync_desktop_icons() | ||||
sync_languages() | sync_languages() | ||||
@@ -1,109 +1 @@ | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# MIT License. See license.txt | |||||
from __future__ import unicode_literals | |||||
""" | |||||
Utilities for using modules | |||||
""" | |||||
import frappe, os | |||||
import frappe.utils | |||||
from frappe import _ | |||||
lower_case_files_for = ['DocType', 'Page', 'Report', | |||||
"Workflow", 'Module Def', 'Desktop Item', 'Workflow State', 'Workflow Action', 'Print Format', | |||||
"Website Theme", 'Web Form'] | |||||
def scrub(txt): | |||||
return frappe.scrub(txt) | |||||
def scrub_dt_dn(dt, dn): | |||||
"""Returns in lowercase and code friendly names of doctype and name for certain types""" | |||||
ndt, ndn = dt, dn | |||||
if dt in lower_case_files_for: | |||||
ndt, ndn = scrub(dt), scrub(dn) | |||||
return ndt, ndn | |||||
def get_module_path(module): | |||||
"""Returns path of the given module""" | |||||
return frappe.get_module_path(module) | |||||
def get_doc_path(module, doctype, name): | |||||
dt, dn = scrub_dt_dn(doctype, name) | |||||
return os.path.join(get_module_path(module), dt, dn) | |||||
def reload_doc(module, dt=None, dn=None, force=True): | |||||
from frappe.modules.import_file import import_files | |||||
return import_files(module, dt, dn, force=force) | |||||
def export_doc(doctype, name, module=None): | |||||
"""Write a doc to standard path.""" | |||||
from frappe.modules.export_file import write_document_file | |||||
print doctype, name | |||||
if not module: module = frappe.db.get_value('DocType', name, 'module') | |||||
write_document_file(frappe.get_doc(doctype, name), module) | |||||
def get_doctype_module(doctype): | |||||
"""Returns **Module Def** name of given doctype.""" | |||||
def make_modules_dict(): | |||||
return dict(frappe.db.sql("select name, module from tabDocType")) | |||||
return frappe.cache().get_value("doctype_modules", make_modules_dict)[doctype] | |||||
doctype_python_modules = {} | |||||
def load_doctype_module(doctype, module=None, prefix="", suffix=""): | |||||
"""Returns the module object for given doctype.""" | |||||
if not module: | |||||
module = get_doctype_module(doctype) | |||||
app = get_module_app(module) | |||||
key = (app, doctype, prefix, suffix) | |||||
module_name = get_module_name(doctype, module, prefix, suffix) | |||||
try: | |||||
if key not in doctype_python_modules: | |||||
doctype_python_modules[key] = frappe.get_module(module_name) | |||||
except ImportError: | |||||
print 'Module import failed for {0} ({1})'.format(doctype, module_name) | |||||
raise | |||||
return doctype_python_modules[key] | |||||
def get_module_name(doctype, module, prefix="", suffix="", app=None): | |||||
return '{app}.{module}.doctype.{doctype}.{prefix}{doctype}{suffix}'.format(\ | |||||
app = scrub(app or get_module_app(module)), | |||||
module = scrub(module), | |||||
doctype = scrub(doctype), | |||||
prefix=prefix, | |||||
suffix=suffix) | |||||
def get_module_app(module): | |||||
return frappe.local.module_app[scrub(module)] | |||||
def get_app_publisher(module): | |||||
app = frappe.local.module_app[scrub(module)] | |||||
if not app: | |||||
frappe.throw(_("App not found")) | |||||
app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0] | |||||
return app_publisher | |||||
def make_boilerplate(template, doc, opts=None): | |||||
target_path = get_doc_path(doc.module, doc.doctype, doc.name) | |||||
template_name = template.replace("controller", scrub(doc.name)) | |||||
target_file_path = os.path.join(target_path, template_name) | |||||
app_publisher = get_app_publisher(doc.module) | |||||
if not os.path.exists(target_file_path): | |||||
if not opts: | |||||
opts = {} | |||||
with open(target_file_path, 'w') as target: | |||||
with open(os.path.join(get_module_path("core"), "doctype", scrub(doc.doctype), | |||||
"boilerplate", template), 'r') as source: | |||||
target.write(frappe.utils.encode( | |||||
frappe.utils.cstr(source.read()).format(app_publisher=app_publisher, | |||||
classname=doc.name.replace(" ", ""), doctype=doc.name, **opts) | |||||
)) | |||||
from .utils import * |
@@ -0,0 +1,200 @@ | |||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors | |||||
# MIT License. See license.txt | |||||
from __future__ import unicode_literals | |||||
""" | |||||
Utilities for using modules | |||||
""" | |||||
import frappe, os, json | |||||
import frappe.utils | |||||
from frappe import _ | |||||
lower_case_files_for = ['DocType', 'Page', 'Report', | |||||
"Workflow", 'Module Def', 'Desktop Item', 'Workflow State', 'Workflow Action', 'Print Format', | |||||
"Website Theme", 'Web Form', 'Email Alert'] | |||||
def export_module_json(doc, is_standard, module): | |||||
"""Make a folder for the given doc and add its json file (make it a standard | |||||
object that will be synced)""" | |||||
if (not frappe.flags.in_import and getattr(frappe.get_conf(),'developer_mode', 0) | |||||
and is_standard): | |||||
from frappe.modules.export_file import export_to_files | |||||
from frappe.modules import get_module_path | |||||
# json | |||||
export_to_files(record_list=[[doc.doctype, doc.name]], record_module=module) | |||||
path = os.path.join(get_module_path(module), scrub(doc.doctype), | |||||
scrub(doc.name), scrub(doc.name)) | |||||
return path | |||||
def get_doc_module(module, doctype, name): | |||||
"""Get custom module for given document""" | |||||
module_name = "{app}.{module}.{doctype}.{name}.{name}".format( | |||||
app = frappe.local.module_app[scrub(module)], | |||||
doctype = scrub(doctype), | |||||
module = scrub(module), | |||||
name = scrub(name) | |||||
) | |||||
return frappe.get_module(module_name) | |||||
@frappe.whitelist() | |||||
def export_customizations(module, doctype): | |||||
"""Export Custom Field and Property Setter for the current document to the app folder. | |||||
This will be synced with bench migrate""" | |||||
if not frappe.get_conf().developer_mode: | |||||
raise 'Not developer mode' | |||||
custom = {'custom_fields': [], 'property_setters': [], 'doctype': doctype} | |||||
def add(_doctype): | |||||
custom['custom_fields'] += frappe.get_all('Custom Field', | |||||
fields='*', filters={'dt': _doctype}) | |||||
custom['property_setters'] += frappe.get_all('Property Setter', | |||||
fields='*', filters={'doc_type': _doctype}) | |||||
add(doctype) | |||||
# add custom fields and property setters for all child tables | |||||
for d in frappe.get_meta(doctype).get_table_fields(): | |||||
add(d.options) | |||||
folder_path = os.path.join(get_module_path(module), 'custom') | |||||
if not os.path.exists(folder_path): | |||||
os.makedirs(folder_path) | |||||
path = os.path.join(folder_path, scrub(doctype)+ '.json') | |||||
with open(path, 'w') as f: | |||||
f.write(frappe.as_json(custom)) | |||||
frappe.msgprint('Customizations exported to {0}'.format(path)) | |||||
def sync_customizations(): | |||||
'''Sync custom fields and property setters from custom folder in each app module''' | |||||
from frappe.core.doctype.doctype.doctype import validate_fields_for_doctype | |||||
for app_name in frappe.get_installed_apps(): | |||||
for module_name in frappe.local.app_modules.get(app_name) or []: | |||||
folder = frappe.get_app_path(app_name, module_name, 'custom') | |||||
if os.path.exists(folder): | |||||
for fname in os.listdir(folder): | |||||
with open(os.path.join(folder, fname), 'r') as f: | |||||
data = json.loads(f.read()) | |||||
doctype = data['doctype'] | |||||
if data['custom_fields']: | |||||
frappe.db.sql('delete from `tabCustom Field` where dt=%s', doctype) | |||||
for d in data['custom_fields']: | |||||
d['doctype'] = 'Custom Field' | |||||
doc = frappe.get_doc(d) | |||||
doc.db_insert() | |||||
if data['property_setters']: | |||||
frappe.db.sql('delete from `tabProperty Setter` where doc_type=%s', doctype) | |||||
for d in data['property_setters']: | |||||
d['doctype'] = 'Property Setter' | |||||
doc = frappe.get_doc(d) | |||||
doc.db_insert() | |||||
print 'Updating customizations for {0}'.format(doctype) | |||||
validate_fields_for_doctype(doctype) | |||||
def scrub(txt): | |||||
return frappe.scrub(txt) | |||||
def scrub_dt_dn(dt, dn): | |||||
"""Returns in lowercase and code friendly names of doctype and name for certain types""" | |||||
ndt, ndn = dt, dn | |||||
if dt in lower_case_files_for: | |||||
ndt, ndn = scrub(dt), scrub(dn) | |||||
return ndt, ndn | |||||
def get_module_path(module): | |||||
"""Returns path of the given module""" | |||||
return frappe.get_module_path(module) | |||||
def get_doc_path(module, doctype, name): | |||||
dt, dn = scrub_dt_dn(doctype, name) | |||||
return os.path.join(get_module_path(module), dt, dn) | |||||
def reload_doc(module, dt=None, dn=None, force=True): | |||||
from frappe.modules.import_file import import_files | |||||
return import_files(module, dt, dn, force=force) | |||||
def export_doc(doctype, name, module=None): | |||||
"""Write a doc to standard path.""" | |||||
from frappe.modules.export_file import write_document_file | |||||
print doctype, name | |||||
if not module: module = frappe.db.get_value('DocType', name, 'module') | |||||
write_document_file(frappe.get_doc(doctype, name), module) | |||||
def get_doctype_module(doctype): | |||||
"""Returns **Module Def** name of given doctype.""" | |||||
def make_modules_dict(): | |||||
return dict(frappe.db.sql("select name, module from tabDocType")) | |||||
return frappe.cache().get_value("doctype_modules", make_modules_dict)[doctype] | |||||
doctype_python_modules = {} | |||||
def load_doctype_module(doctype, module=None, prefix="", suffix=""): | |||||
"""Returns the module object for given doctype.""" | |||||
if not module: | |||||
module = get_doctype_module(doctype) | |||||
app = get_module_app(module) | |||||
key = (app, doctype, prefix, suffix) | |||||
module_name = get_module_name(doctype, module, prefix, suffix) | |||||
try: | |||||
if key not in doctype_python_modules: | |||||
doctype_python_modules[key] = frappe.get_module(module_name) | |||||
except ImportError: | |||||
print 'Module import failed for {0} ({1})'.format(doctype, module_name) | |||||
raise | |||||
return doctype_python_modules[key] | |||||
def get_module_name(doctype, module, prefix="", suffix="", app=None): | |||||
return '{app}.{module}.doctype.{doctype}.{prefix}{doctype}{suffix}'.format(\ | |||||
app = scrub(app or get_module_app(module)), | |||||
module = scrub(module), | |||||
doctype = scrub(doctype), | |||||
prefix=prefix, | |||||
suffix=suffix) | |||||
def get_module_app(module): | |||||
return frappe.local.module_app[scrub(module)] | |||||
def get_app_publisher(module): | |||||
app = frappe.local.module_app[scrub(module)] | |||||
if not app: | |||||
frappe.throw(_("App not found")) | |||||
app_publisher = frappe.get_hooks(hook="app_publisher", app_name=app)[0] | |||||
return app_publisher | |||||
def make_boilerplate(template, doc, opts=None): | |||||
target_path = get_doc_path(doc.module, doc.doctype, doc.name) | |||||
template_name = template.replace("controller", scrub(doc.name)) | |||||
target_file_path = os.path.join(target_path, template_name) | |||||
app_publisher = get_app_publisher(doc.module) | |||||
if not os.path.exists(target_file_path): | |||||
if not opts: | |||||
opts = {} | |||||
with open(target_file_path, 'w') as target: | |||||
with open(os.path.join(get_module_path("core"), "doctype", scrub(doc.doctype), | |||||
"boilerplate", template), 'r') as source: | |||||
target.write(frappe.utils.encode( | |||||
frappe.utils.cstr(source.read()).format(app_publisher=app_publisher, | |||||
classname=doc.name.replace(" ", ""), doctype=doc.name, **opts) | |||||
)) |
@@ -45,11 +45,9 @@ class PrintFormat(Document): | |||||
def export_doc(self): | def export_doc(self): | ||||
# export | # export | ||||
if self.standard == 'Yes' and (frappe.conf.get('developer_mode') or 0) == 1: | |||||
module = frappe.db.get_value("DocType", self.doc_type, "module") | |||||
from frappe.modules.export_file import export_to_files | |||||
export_to_files(record_list=[['Print Format', self.name]], | |||||
record_module= module) | |||||
from frappe.modules.utils import export_module_json | |||||
export_module_json(self, self.standard == 'Yes', | |||||
frappe.db.get_value("DocType", self.doc_type, "module")) | |||||
def on_trash(self): | def on_trash(self): | ||||
if self.doc_type: | if self.doc_type: | ||||
@@ -11,6 +11,7 @@ from frappe.website.utils import get_comment_list | |||||
from frappe.custom.doctype.customize_form.customize_form import docfield_properties | from frappe.custom.doctype.customize_form.customize_form import docfield_properties | ||||
from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller | from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller | ||||
from frappe.utils.file_manager import get_max_file_size | from frappe.utils.file_manager import get_max_file_size | ||||
from frappe.modules.utils import export_module_json, get_doc_module | |||||
class WebForm(WebsiteGenerator): | class WebForm(WebsiteGenerator): | ||||
website = frappe._dict( | website = frappe._dict( | ||||
@@ -65,16 +66,9 @@ class WebForm(WebsiteGenerator): | |||||
Writes the .txt for this page and if write_content is checked, | Writes the .txt for this page and if write_content is checked, | ||||
it will write out a .html file | it will write out a .html file | ||||
""" | """ | ||||
if not frappe.flags.in_import and getattr(frappe.get_conf(),'developer_mode', 0) and self.is_standard: | |||||
from frappe.modules.export_file import export_to_files | |||||
from frappe.modules import get_module_path | |||||
# json | |||||
export_to_files(record_list=[['Web Form', self.name]], record_module=self.module) | |||||
# write files | |||||
path = os.path.join(get_module_path(self.module), 'web_form', scrub(self.name), scrub(self.name)) | |||||
path = export_module_json(self, self.is_standard, self.module) | |||||
if path: | |||||
# js | # js | ||||
if not os.path.exists(path + '.js'): | if not os.path.exists(path + '.js'): | ||||
with open(path + '.js', 'w') as f: | with open(path + '.js', 'w') as f: | ||||
@@ -291,12 +285,7 @@ def get_context(context): | |||||
def set_web_form_module(self): | def set_web_form_module(self): | ||||
'''Get custom web form module if exists''' | '''Get custom web form module if exists''' | ||||
if self.is_standard: | if self.is_standard: | ||||
module_name = "{app}.{module}.web_form.{name}.{name}".format( | |||||
app = frappe.local.module_app[scrub(self.module)], | |||||
module = scrub(self.module), | |||||
name = scrub(self.name) | |||||
) | |||||
self.web_form_module = frappe.get_module(module_name) | |||||
self.web_form_module = get_doc_module(self.module, self.doctype, self.name) | |||||
else: | else: | ||||
self.web_form_module = None | self.web_form_module = None | ||||