From 2e66bd044b1442f9676eb7329fdbb2eb8a45f03b Mon Sep 17 00:00:00 2001
From: Rushabh Mehta
Frappe also has a plug-in architecture that can be used to build plugins to ERPNext.
-Frappe Framework was designed to build ERPNext, open source +
Frappe Framework was designed to build ERPNext, open source ERP for managing small and medium sized businesses.
- + diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.js b/frappe/email/doctype/auto_email_report/auto_email_report.js index 962ca24b98..cc48901a37 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.js +++ b/frappe/email/doctype/auto_email_report/auto_email_report.js @@ -49,7 +49,7 @@ frappe.ui.form.on('Auto Email Report', { && frappe.query_reports[frm.doc.report].filters) { // make a table to show filters - var table = $(''+__('Filter')+' | '+__('Value')+' |
---|
' + __("Click table to edit") + '
').appendTo(wrapper); diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.json b/frappe/email/doctype/auto_email_report/auto_email_report.json index 432346bdd6..7578ffef67 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.json +++ b/frappe/email/doctype/auto_email_report/auto_email_report.json @@ -43,17 +43,43 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "report_type", - "fieldtype": "Read Only", + "fieldname": "user", + "fieldtype": "Link", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Report Type", + "label": "Based on Permissions For User", + "length": 0, + "no_copy": 0, + "options": "User", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "enabled", + "fieldtype": "Check", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "label": "Enabled", "length": 0, "no_copy": 0, - "options": "report.report_type", "permlevel": 0, "precision": "", "print_hide": 0, @@ -70,24 +96,22 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "user", - "fieldtype": "Link", + "fieldname": "column_break_4", + "fieldtype": "Column Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "For User", "length": 0, "no_copy": 0, - "options": "User", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -97,16 +121,17 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "enabled", - "fieldtype": "Check", + "fieldname": "report_type", + "fieldtype": "Read Only", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Enabled", + "label": "Report Type", "length": 0, "no_copy": 0, + "options": "report.report_type", "permlevel": 0, "precision": "", "print_hide": 0, @@ -228,17 +253,16 @@ "bold": 0, "collapsible": 0, "columns": 0, - "fieldname": "frequency", - "fieldtype": "Select", + "fieldname": "email_to", + "fieldtype": "Small Text", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Frequency", + "label": "Email To", "length": 0, "no_copy": 0, - "options": "Daily\nWeekly\nMonthly", "permlevel": 0, "precision": "", "print_hide": 0, @@ -279,6 +303,58 @@ "set_only_once": 0, "unique": 0 }, + { + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "columns": 0, + "fieldname": "column_break_13", + "fieldtype": "Column Break", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 0, + "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, + "fieldname": "frequency", + "fieldtype": "Select", + "hidden": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_filter": 0, + "in_list_view": 1, + "label": "Frequency", + "length": 0, + "no_copy": 0, + "options": "Daily\nWeekly\nMonthly", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "read_only": 0, + "report_hide": 0, + "reqd": 1, + "search_index": 0, + "set_only_once": 0, + "unique": 0 + }, { "allow_on_submit": 0, "bold": 0, @@ -309,16 +385,16 @@ { "allow_on_submit": 0, "bold": 0, - "collapsible": 0, + "collapsible": 1, "columns": 0, - "fieldname": "email_to", - "fieldtype": "Small Text", + "fieldname": "section_break_15", + "fieldtype": "Section Break", "hidden": 0, "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Email To", + "label": "Message", "length": 0, "no_copy": 0, "permlevel": 0, @@ -327,7 +403,7 @@ "print_hide_if_no_value": 0, "read_only": 0, "report_hide": 0, - "reqd": 1, + "reqd": 0, "search_index": 0, "set_only_once": 0, "unique": 0 @@ -344,7 +420,7 @@ "ignore_xss_filter": 0, "in_filter": 0, "in_list_view": 0, - "label": "Description", + "label": "Message", "length": 0, "no_copy": 0, "permlevel": 0, @@ -369,7 +445,7 @@ "issingle": 0, "istable": 0, "max_attachments": 0, - "modified": "2016-09-14 02:00:21.618956", + "modified": "2016-09-15 01:32:57.454880", "modified_by": "Administrator", "module": "Email", "name": "Auto Email Report", diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.py b/frappe/email/doctype/auto_email_report/auto_email_report.py index df8949cc63..fc6fe8d123 100644 --- a/frappe/email/doctype/auto_email_report/auto_email_report.py +++ b/frappe/email/doctype/auto_email_report/auto_email_report.py @@ -67,8 +67,13 @@ class AutoEmailReport(Document): def send(self): data = self.get_report_content() - message = '{0}
'.format(_('{0} generated on {1}').format(self.name, - frappe.utils.format_datetime(frappe.utils.now_datetime()))) + attachments = None + message = '{0}
'.format(_('{0} generated on {1}')\ + .format(frappe.bold(self.name), + frappe.utils.format_datetime(frappe.utils.now_datetime()))) + + if self.description: + message += 'Edit Auto Email Report Settings: {0}
'.format(frappe.utils.get_link_to_form('Auto Email Report', self.name)) + frappe.sendmail( recipients = self.email_to.split(), subject = self.name, diff --git a/frappe/integrations/paypal.py b/frappe/integrations/paypal.py index 01d47bf90d..d7c6fef91f 100644 --- a/frappe/integrations/paypal.py +++ b/frappe/integrations/paypal.py @@ -11,52 +11,49 @@ from frappe.utils import get_url, call_hook_method from frappe.integration_broker.integration_controller import IntegrationController """ -1. Get paypal controller, +# Integrating PayPal + +### 1. Validate Currency Support + +Example: + from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller + controller = get_integration_controller("PayPal", setup=False) + controller().validate_transaction_currency(currency) + +### 2. Redirect for payment -2. check whether transaction currency supported by payment gateway, -Controller().validate_transaction_currency(currency) +Example: -3. Get checkout url via controller or api, this will redirect you to payment page of particular gateway. + payment_details = { + "amount": 600, + "title": "Payment for bill : 111", + "description": "payment via cart", + "reference_doctype": "Payment Request", + "reference_docname": "PR0001", + "payer_email": "NuranVerkleij@example.com", + "payer_name": "Nuran Verkleij", + "order_id": "111", + "currency": "USD" + } -payment_details = { - "amount": 600, - "title": "Payment for bill : 111", - "description": "payment via cart", - "reference_doctype": "Payment Request", - "reference_docname": "PR0001", - "payer_email": "NuranVerkleij@example.com", - "payer_name": "Nuran Verkleij", - "order_id": "111", - "currency": "USD" -} + # redirect the user to this url + url = controller().get_payment_url(**payment_details) -Via API ---------- -Get payement url via get_checkout_url(**kwargs) -example: - from frappe.integration.paypal import get_checkout_url - get_checkout_url(**payment_details) +### 3. On Completion of Payment -Via Controller ---------------- -Get payment url via get_payment_url(**kwargs) +Write a method for `on_payment_authorized` in the reference doctype -example: - controller().get_payment_url(**payment_details) +Example: -4.To handle a callback of payment, you need to write `on_payment_authorized` -in reference document. + def on_payment_authorized(payment_status): + # your code to handle callback -example: - def on_payment_authorized(payment_satus): - "your code to handle callback" +##### Note: -parameter description ---------------------- -payment_satus - payment gateway will put payment status on callback. +payment_status - payment gateway will put payment status on callback. For paypal payment status parameter is one from: [Completed, Cancelled, Failed] """ @@ -83,27 +80,27 @@ class Controller(IntegrationController): "reqd": 1 } ] - + js = "assets/frappe/js/integrations/paypal.js" - + supported_currencies = ["AUD", "BRL", "CAD", "CZK", "DKK", "EUR", "HKD", "HUF", "ILS", "JPY", "MYR", "MXN", "TWD", "NZD", "NOK", "PHP", "PLN", "GBP", "RUB", "SGD", "SEK", "CHF", "THB", "TRY", "USD"] - + def enable(self, parameters, use_test_account=0): call_hook_method('payment_gateway_enabled', gateway=self.service_name) self.parameters = parameters self.validate_paypal_credentails(use_test_account) - + def get_settings(self): return frappe._dict(self.parameters) def validate_transaction_currency(self, currency): if currency not in self.supported_currencies: frappe.throw(_("Please select another payment method. {0} does not support transactions in currency '{1}'").format(self.service_name, currency)) - + def get_paypal_params_and_url(self, use_test_account): paypal_settings = frappe._dict(self.get_settings()) - + params = { "USER": paypal_settings.api_username, "PWD": paypal_settings.api_password, @@ -111,9 +108,9 @@ class Controller(IntegrationController): "VERSION": "98", "METHOD": "GetPalDetails" } - + api_url = "https://api-3t.sandbox.paypal.com/nvp" if use_test_account else "https://api-3t.paypal.com/nvp" - + return params, api_url def validate_paypal_credentails(self, use_test_account): @@ -134,7 +131,7 @@ class Controller(IntegrationController): self.parameters = json.loads(custom_settings_json) response = self.execute_set_express_checkout(kwargs["amount"], kwargs["currency"], use_test_account) - + if use_test_account: return_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}" else: @@ -144,9 +141,9 @@ class Controller(IntegrationController): "token": response.get("TOKEN")[0], "correlation_id": response.get("CORRELATIONID")[0] }) - + self.integration_request = self.create_request(kwargs, "Remote", self.service_name, response.get("TOKEN")[0]) - + return return_url.format(kwargs["token"]) def execute_set_express_checkout(self, amount, currency, use_test_account): @@ -159,7 +156,7 @@ class Controller(IntegrationController): "returnUrl": get_url("/api/method/frappe.integrations.paypal.get_express_checkout_details"), "cancelUrl": get_url("/payment-cancel") }) - + params = urlencode(params) response = self.post_request(url, data=params.encode("utf-8")) @@ -172,7 +169,7 @@ class Controller(IntegrationController): def get_express_checkout_details(token): use_test_account, custom_settings_json = frappe.db.get_value("Integration Service", "PayPal", ["use_test_account", "custom_settings_json"]) Controller.parameters = json.loads(custom_settings_json) - + params, url = Controller().get_paypal_params_and_url(use_test_account) params.update({ "METHOD": "GetExpressCheckoutDetails", @@ -188,12 +185,12 @@ def get_express_checkout_details(token): http_status_code=frappe.ValidationError.http_status_code) return - + update_integration_request_status(token, { "payerid": response.get("PAYERID")[0], "payer_email": response.get("EMAIL")[0] }, "Authorized") - + frappe.local.response["type"] = "redirect" frappe.local.response["location"] = get_url( \ "/api/method/frappe.integrations.paypal.confirm_payment?token={0}".format(token)) @@ -202,7 +199,7 @@ def get_express_checkout_details(token): def confirm_payment(token): redirect = True status_changed_to, redirect_to = None, None - + use_test_account = frappe.db.get_value("Integration Service", "PayPal", "use_test_account") integration_request = frappe.get_doc("Integration Request", token) diff --git a/frappe/integrations/razorpay.py b/frappe/integrations/razorpay.py index b71e2f504e..30485cfb53 100644 --- a/frappe/integrations/razorpay.py +++ b/frappe/integrations/razorpay.py @@ -10,52 +10,53 @@ from frappe.utils import get_url, call_hook_method from frappe.integration_broker.integration_controller import IntegrationController """ -1. Get razorpay controller, +# Integrating RazorPay + +### Validate Currency + +Example: + from frappe.integration_broker.doctype.integration_service.integration_service import get_integration_controller - controller = get_integration_controller("Razorpay", setup=False) -2. check whether transaction currency supported by payment gateway, -Controller().validate_transaction_currency(currency) + controller = get_integration_controller("Razorpay", setup=False) + controller().validate_transaction_currency(currency) -3. Get checkout url via controller or api, this will redirect you to payment page of particular gateway. +### 2. Redirect for payment -payment_details = { - "amount": 600, - "title": "Payment for bill : 111", - "description": "payment via cart", - "reference_doctype": "Payment Request", - "reference_docname": "PR0001", - "payer_email": "NuranVerkleij@example.com", - "payer_name": "Nuran Verkleij", - "order_id": "111", - "currency": "INR" -} +Example: -Via API ---------- -Get payement url via get_checkout_url(**kwargs) + payment_details = { + "amount": 600, + "title": "Payment for bill : 111", + "description": "payment via cart", + "reference_doctype": "Payment Request", + "reference_docname": "PR0001", + "payer_email": "NuranVerkleij@example.com", + "payer_name": "Nuran Verkleij", + "order_id": "111", + "currency": "INR" + } -example: from frappe.integration.razorpay import get_checkout_url - get_checkout_url(**payment_details) -Via Controller ---------------- -Get payment url via get_payment_url(**kwargs) + # Redirect the user to this url + url = controller().get_payment_url(**payment_details) + + +### 3. On Completion of Payment -example: - controller().get_payment_url(**payment_details) +Write a method for `on_payment_authorized` in the reference doctype -4.To handle a callback of payment, you need to write `on_payment_authorized` -in reference document. +Example: -example: - def on_payment_authorized(payment_satus): - "your code to handle callback" + def on_payment_authorized(payment_status): + # this method will be called when payment is complete -parameter description ---------------------- -payment_satus - payment gateway will put payment status on callback. For razorpay payment status is Authorized + +##### Notes: + +payment_status - payment gateway will put payment status on callback. +For razorpay payment status is Authorized """ @@ -75,7 +76,7 @@ class Controller(IntegrationController): 'reqd': 1 } ] - + # do also changes in razorpay.js scheduler job helper scheduled_jobs = [ { @@ -84,11 +85,11 @@ class Controller(IntegrationController): ] } ] - + js = "assets/frappe/js/integrations/razorpay.js" - + supported_currencies = ["INR"] - + def enable(self, parameters, use_test_account=0): call_hook_method('payment_gateway_enabled', gateway='Razorpay') self.parameters = parameters @@ -103,23 +104,23 @@ class Controller(IntegrationController): auth=(razorpay_settings.api_key, razorpay_settings.api_secret)) except Exception: frappe.throw(_("Seems API Key or API Secret is wrong !!!")) - + def validate_transaction_currency(self, currency): if currency not in self.supported_currencies: frappe.throw(_("Please select another payment method. {0} does not support transactions in currency '{1}'").format(self.service_name, currency)) - + def get_payment_url(self, **kwargs): return get_url("./integrations/razorpay_checkout?{0}".format(urllib.urlencode(kwargs))) - + def get_settings(self): if hasattr(self, "parameters"): return frappe._dict(self.parameters) custom_settings_json = frappe.db.get_value("Integration Service", "Razorpay", "custom_settings_json", debug=1) - + if custom_settings_json: return frappe._dict(json.loads(custom_settings_json)) - + def create_request(self, data): self.data = frappe._dict(data) @@ -133,29 +134,29 @@ class Controller(IntegrationController): "redirect_to": frappe.redirect_to_message(_('Server Error'), _("Seems issue with server's razorpay config. Don't worry, in case of failure amount will get refunded to your account.")), "status": 401 } - + def authorize_payment(self): """ An authorization is performed when user’s payment details are successfully authenticated by the bank. The money is deducted from the customer’s account, but will not be transferred to the merchant’s account until it is explicitly captured by merchant. """ - + settings = self.get_settings() - + if self.integration_request.status != "Authorized": resp = self.get_request("https://api.razorpay.com/v1/payments/{0}" .format(self.data.razorpay_payment_id), auth=(settings.api_key, settings.api_secret)) - + if resp.get("status") == "authorized": self.integration_request.db_set('status', 'Authorized', update_modified=False) self.flags.status_changed_to = "Authorized" - + if self.flags.status_changed_to == "Authorized": if self.data.reference_doctype and self.data.reference_docname: redirect_to = frappe.get_doc(self.data.reference_doctype, self.data.reference_docname).run_method("on_payment_authorized", self.flags.status_changed_to) - + return { "redirect_to": redirect_to or "payment-success", "status": 200 @@ -180,7 +181,7 @@ def capture_payment(is_sandbox=False, sanbox_response=None): data = json.loads(doc.data) resp = controller.post_request("https://api.razorpay.com/v1/payments/{0}/capture".format(data.get("razorpay_payment_id")), auth=(settings["api_key"], settings["api_secret"]), data={"amount": data.get("amount")}) - + if resp.get("status") == "captured": frappe.db.set_value("Integration Request", doc.name, "status", "Completed") diff --git a/frappe/public/css/docs.css b/frappe/public/css/docs.css index fac0b14e8f..cd13c69198 100644 --- a/frappe/public/css/docs.css +++ b/frappe/public/css/docs.css @@ -573,3 +573,9 @@ a.edit:visited, .page-content-wrapper > .row .col-sm-4 { display: none; } +.screenshot { + border: 2px solid #d1d8dd; + box-shadow: 1px 1px 7px rgba(0, 0, 0, 0.15); + margin: 15px 0px; + max-width: 100%; +} diff --git a/frappe/public/js/frappe/list/listview.js b/frappe/public/js/frappe/list/listview.js index 63d1e779c9..e68132b415 100644 --- a/frappe/public/js/frappe/list/listview.js +++ b/frappe/public/js/frappe/list/listview.js @@ -58,6 +58,10 @@ frappe.views.ListView = Class.extend({ add_field(this.meta.title_field); } + // endabled / disabled + if(frappe.meta.has_field(this.doctype, 'enabled')) { add_field('enabled'); }; + if(frappe.meta.has_field(this.doctype, 'disabled')) { add_field('disabled'); }; + // add workflow field (as priority) this.workflow_state_fieldname = frappe.workflow.get_state_fieldname(this.doctype); if(this.workflow_state_fieldname) { @@ -113,8 +117,8 @@ frappe.views.ListView = Class.extend({ this.columns.push(name_column); this.total_colspans = this.columns[0].colspan; - if(frappe.model.is_submittable(this.doctype) - || this.settings.get_indicator || this.workflow_state_fieldname) { + + if(frappe.has_indicator(this.doctype)) { // indicator this.columns.push({ colspan: this.settings.colwidths && this.settings.colwidths.indicator || 3, diff --git a/frappe/public/js/frappe/model/indicator.js b/frappe/public/js/frappe/model/indicator.js index b9060f1396..85905ece28 100644 --- a/frappe/public/js/frappe/model/indicator.js +++ b/frappe/public/js/frappe/model/indicator.js @@ -1,5 +1,17 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors +frappe.has_indicator = function(doctype) { + // returns true if indicator is present + if(frappe.model.is_submittable(this.doctype)) { + return true; + } else if(this.settings.get_indicator || this.workflow_state_fieldname) { + return true; + } else if(frappe.meta.has_field(doctype, 'enabled') || frappe.meta.has_field(doctype, 'disabled')) { + return true; + } + return false; +} + frappe.get_indicator = function(doc, doctype) { if(doc.__unsaved) { return [__("Not Saved"), "orange"]; @@ -38,10 +50,12 @@ frappe.get_indicator = function(doc, doctype) { } } + // draft if document is submittable if(is_submittable && doc.docstatus==0 && !settings.has_indicator_for_draft) { return [__("Draft"), "red", "docstatus,=,0"]; } + // cancelled if(is_submittable && doc.docstatus==2) { return [__("Cancelled"), "red", "docstatus,=,2"]; } @@ -51,11 +65,31 @@ frappe.get_indicator = function(doc, doctype) { if(indicator) return indicator; } + // if submittable if(is_submittable && doc.docstatus==1) { return [__("Submitted"), "blue", "docstatus,=,1"]; } + // based on status if(doc.status) { return [__(doc.status), frappe.utils.guess_colour(doc.status)]; } + + // based on enabled + if(frappe.meta.has_field(doctype, 'enabled')) { + if(doc.enabled) { + return [__('Enabled'), 'blue', 'enabled=1']; + } else { + return [__('Disabled'), 'grey', 'enabled=0']; + } + } + + // based on disabled + if(frappe.meta.has_field(doctype, 'disabled')) { + if(doc.disabled) { + return [__('Disabled'), 'grey', 'disabled=1']; + } else { + return [__('Enabled'), 'blue', 'disabled=0']; + } + } } diff --git a/frappe/public/less/docs.less b/frappe/public/less/docs.less index 0ba663eea9..a303f2b0f7 100644 --- a/frappe/public/less/docs.less +++ b/frappe/public/less/docs.less @@ -395,3 +395,11 @@ a.edit, a.edit:hover, a.edit:focus, a.edit:visited, .edit-container .icon { display: none; } } + +.screenshot { + border: 2px solid @border-color; + box-shadow: 1px 1px 7px rgba(0,0,0,0.15); + margin: 15px 0px; + max-width: 100%; +} + diff --git a/frappe/utils/autodoc.py b/frappe/utils/autodoc.py index 68663d8dc1..4a1412598e 100644 --- a/frappe/utils/autodoc.py +++ b/frappe/utils/autodoc.py @@ -8,6 +8,8 @@ frappe.utils.autodoc Inspect elements of a given module and return its objects """ +from __future__ import unicode_literals + import inspect, importlib, re, frappe from frappe.model.document import get_controller @@ -42,7 +44,7 @@ def automodule(name): return { "members": filter(None, attributes), - "docs": getattr(obj, "__doc__", "") + "docs": get_obj_doc(obj) } installed = None @@ -89,11 +91,11 @@ def get_class_info(class_obj, module_name): "type": "class", "bases": [b.__module__ + "." + b.__name__ for b in class_obj.__bases__], "members": filter(None, members), - "docs": parse(getattr(class_obj, "__doc__", "")) + "docs": parse(get_obj_doc(class_obj)) } def get_function_info(value): - docs = getattr(value, "__doc__") + docs = get_obj_doc(value) return { "name": value.__name__, "type": "function", @@ -108,8 +110,6 @@ def parse(docs): if not docs: return "" - docs = strip_leading_tabs(docs) - if ":param" in docs: out, title_set = [], False for line in docs.splitlines(): @@ -154,3 +154,10 @@ def strip_leading_tabs(docs): def automodel(doctype): """return doctype template""" pass + +def get_obj_doc(obj): + '''Return `__doc__` of the given object as unicode''' + doc = getattr(obj, "__doc__", "") or '' + if not isinstance(doc, unicode): + doc = unicode(doc, 'utf-8') + return doc \ No newline at end of file diff --git a/frappe/utils/setup_docs.py b/frappe/utils/setup_docs.py index a07210af9f..06ff3845f9 100644 --- a/frappe/utils/setup_docs.py +++ b/frappe/utils/setup_docs.py @@ -205,7 +205,7 @@ class setup_docs(object): context = {"name": self.app + "." + module_name} context.update(self.app_context) f.write(frappe.render_template("templates/autodoc/pymodule.html", - context)) + context).encode('utf-8')) self.update_index_txt(module_folder) @@ -272,12 +272,18 @@ class setup_docs(object): frappe.local.flags.home_page = "index" from frappe.website.router import get_pages, make_toc - pages = get_pages() + pages = get_pages(self.app) # clear the user, current folder in target shutil.rmtree(os.path.join(self.target, "user"), ignore_errors=True) shutil.rmtree(os.path.join(self.target, "current"), ignore_errors=True) + def raw_replacer(matchobj): + if '{% raw %}' in matchobj.group(0): + return matchobj.group(0) + else: + return '{% raw %}' + matchobj.group(0) + '{% endraw %}' + cnt = 0 for path, context in pages.iteritems(): print "Writing {0}".format(path) @@ -336,7 +342,7 @@ class setup_docs(object): context.base_template_path = "templates/autodoc/base_template.html" if '' in context.source:
- context.source = re.sub('\(.*)\
', '{% raw %}\g<1>{% endraw %}
', context.source)
+ context.source = re.sub('\(.*)\
', raw_replacer, context.source)
html = frappe.render_template(context.source, context)
diff --git a/frappe/website/router.py b/frappe/website/router.py
index 8dafca895b..f2bcc946fc 100644
--- a/frappe/website/router.py
+++ b/frappe/website/router.py
@@ -110,13 +110,17 @@ def get_page_info_from_doctypes(path=None):
return routes
-def get_pages():
+def get_pages(app=None):
'''Get all pages. Called for docs / sitemap'''
pages = {}
frappe.local.flags.in_get_all_pages = True
folders = frappe.local.flags.web_pages_folders or ('www', 'templates/pages')
- apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps()
+
+ if app:
+ apps = [app]
+ else:
+ apps = frappe.local.flags.web_pages_apps or frappe.get_installed_apps()
for app in apps:
app_path = frappe.get_app_path(app)