Merge branch 'develop' into chart-link
This commit is contained in:
commit
87805fdfd8
44 changed files with 836 additions and 1349 deletions
|
|
@ -333,12 +333,20 @@ class CookieManager:
|
|||
# sid expires in 3 days
|
||||
expires = datetime.datetime.now() + datetime.timedelta(days=3)
|
||||
if frappe.session.sid:
|
||||
self.cookies["sid"] = {"value": frappe.session.sid, "expires": expires}
|
||||
self.set_cookie("sid", frappe.session.sid, expires=expires, httponly=True)
|
||||
if frappe.session.session_country:
|
||||
self.cookies["country"] = {"value": frappe.session.get("session_country")}
|
||||
self.set_cookie("country", frappe.session.session_country)
|
||||
|
||||
def set_cookie(self, key, value, expires=None):
|
||||
self.cookies[key] = {"value": value, "expires": expires}
|
||||
def set_cookie(self, key, value, expires=None, secure=False, httponly=False, samesite="Strict"):
|
||||
if not secure:
|
||||
secure = frappe.local.request.scheme == "https"
|
||||
self.cookies[key] = {
|
||||
"value": value,
|
||||
"expires": expires,
|
||||
"secure": secure,
|
||||
"httponly": httponly,
|
||||
"samesite": samesite
|
||||
}
|
||||
|
||||
def delete_cookie(self, to_delete):
|
||||
if not isinstance(to_delete, (list, tuple)):
|
||||
|
|
@ -349,7 +357,10 @@ class CookieManager:
|
|||
def flush_cookies(self, response):
|
||||
for key, opts in self.cookies.items():
|
||||
response.set_cookie(key, quote((opts.get("value") or "").encode('utf-8')),
|
||||
expires=opts.get("expires"))
|
||||
expires=opts.get("expires"),
|
||||
secure=opts.get("secure"),
|
||||
httponly=opts.get("httponly"),
|
||||
samesite=opts.get("samesite"))
|
||||
|
||||
# expires yesterday!
|
||||
expires = datetime.datetime.now() + datetime.timedelta(days=-1)
|
||||
|
|
|
|||
|
|
@ -16,6 +16,13 @@ def get_data():
|
|||
"description": _("Language, Date and Time settings"),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Global Defaults",
|
||||
"label": _("Global Defaults"),
|
||||
"description": _("Company, Fiscal Year and Currency defaults"),
|
||||
"hide_count": True
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Error Log",
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
{
|
||||
"hidden": 0,
|
||||
"label": "Core",
|
||||
"links": "[\n {\n \"description\": \"Language, Date and Time settings\",\n \"hide_count\": true,\n \"label\": \"System Settings\",\n \"name\": \"System Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error on automated events (scheduler).\",\n \"label\": \"Error Log\",\n \"name\": \"Error Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error during requests.\",\n \"label\": \"Error Snapshot\",\n \"name\": \"Error Snapshot\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Enable / Disable Domains\",\n \"hide_count\": true,\n \"label\": \"Domain Settings\",\n \"name\": \"Domain Settings\",\n \"type\": \"doctype\"\n }\n]"
|
||||
"links": "[\n {\n \"description\": \"Language, Date and Time settings\",\n \"hide_count\": true,\n \"label\": \"System Settings\",\n \"name\": \"System Settings\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Company, Fiscal Year and Currency defaults\",\n \"hide_count\": true,\n \"label\": \"Global Defaults\",\n \"name\": \"Global Defaults\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error on automated events (scheduler).\",\n \"label\": \"Error Log\",\n \"name\": \"Error Log\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Log of error during requests.\",\n \"label\": \"Error Snapshot\",\n \"name\": \"Error Snapshot\",\n \"type\": \"doctype\"\n },\n {\n \"description\": \"Enable / Disable Domains\",\n \"hide_count\": true,\n \"label\": \"Domain Settings\",\n \"name\": \"Domain Settings\",\n \"type\": \"doctype\"\n }\n]"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
|
|
@ -39,10 +39,11 @@
|
|||
"docstatus": 0,
|
||||
"doctype": "Desk Page",
|
||||
"extends_another_page": 0,
|
||||
"hide_custom": 0,
|
||||
"idx": 0,
|
||||
"is_standard": 1,
|
||||
"label": "Settings",
|
||||
"modified": "2020-04-01 11:24:40.636747",
|
||||
"modified": "2020-07-14 10:09:09.520557",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "Settings",
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ def prepare_to_notify(doc, print_html=None, print_format=None, attachments=None)
|
|||
:param print_html: Send given value as HTML attachment.
|
||||
:param print_format: Attach print format of parent document."""
|
||||
|
||||
view_link = frappe.utils.cint(frappe.db.get_value("Print Settings", "Print Settings", "attach_view_link"))
|
||||
view_link = frappe.utils.cint(frappe.db.get_value("System Settings", "System Settings", "attach_view_link"))
|
||||
|
||||
if print_format and view_link:
|
||||
doc.content += get_attach_link(doc, print_format)
|
||||
|
|
|
|||
|
|
@ -441,9 +441,8 @@ class ImportFile:
|
|||
|
||||
# if there are child doctypes, find the subsequent rows
|
||||
if len(doctypes) > 1:
|
||||
# subsequent rows either dont have any parent value set
|
||||
# or have the same value as the parent row
|
||||
# we include a row if either of conditions match
|
||||
# subsequent rows that have blank values in parent columns
|
||||
# are considered as child rows
|
||||
parent_column_indexes = self.header.get_column_indexes(self.doctype)
|
||||
parent_row_values = first_row.get_values(parent_column_indexes)
|
||||
|
||||
|
|
@ -454,11 +453,8 @@ class ImportFile:
|
|||
if all([v in INVALID_VALUES for v in row_values]):
|
||||
rows.append(row)
|
||||
continue
|
||||
# if the row has same values as parent row, it's a child row doc
|
||||
if row_values == parent_row_values:
|
||||
rows.append(row)
|
||||
continue
|
||||
# if any of those conditions dont match, it's the next doc
|
||||
# if we encounter a row which has values in parent columns,
|
||||
# then it is the next doc
|
||||
break
|
||||
|
||||
parent_doc = None
|
||||
|
|
@ -693,6 +689,9 @@ class Row:
|
|||
return value
|
||||
|
||||
def get_date(self, value, column):
|
||||
if isinstance(value, datetime):
|
||||
return value
|
||||
|
||||
date_format = column.date_format
|
||||
if date_format:
|
||||
try:
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@
|
|||
"column_break_18",
|
||||
"disable_standard_email_footer",
|
||||
"hide_footer_in_auto_email_reports",
|
||||
"attach_view_link",
|
||||
"chat",
|
||||
"enable_chat",
|
||||
"use_socketio_to_upload_file"
|
||||
|
|
@ -422,12 +423,18 @@
|
|||
"fieldname": "enable_onboarding",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Onboarding"
|
||||
},
|
||||
{
|
||||
"default": "1",
|
||||
"fieldname": "attach_view_link",
|
||||
"fieldtype": "Check",
|
||||
"label": "Send document Web View link in email"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-cog",
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-05-01 19:21:15.496065",
|
||||
"modified": "2020-07-02 16:13:00.166382",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "System Settings",
|
||||
|
|
|
|||
|
|
@ -5,10 +5,15 @@ frappe.ui.form.on('Dashboard', {
|
|||
refresh: function(frm) {
|
||||
frm.add_custom_button(__("Show Dashboard"), () => frappe.set_route('dashboard', frm.doc.name));
|
||||
|
||||
if (!frappe.boot.developer_mode) {
|
||||
frm.disable_form();
|
||||
}
|
||||
|
||||
frm.set_query("chart", "charts", function() {
|
||||
return {
|
||||
filters: {
|
||||
is_public: 1
|
||||
is_public: 1,
|
||||
is_standard: 1,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
@ -16,7 +21,8 @@ frappe.ui.form.on('Dashboard', {
|
|||
frm.set_query("card", "cards", function() {
|
||||
return {
|
||||
filters: {
|
||||
is_public: 1
|
||||
is_public: 1,
|
||||
is_standard: 1,
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"autoname": "field:dashboard_name",
|
||||
"creation": "2019-01-10 12:54:40.938705",
|
||||
"doctype": "DocType",
|
||||
|
|
@ -8,6 +9,8 @@
|
|||
"field_order": [
|
||||
"dashboard_name",
|
||||
"is_default",
|
||||
"is_standard",
|
||||
"module",
|
||||
"charts",
|
||||
"chart_options",
|
||||
"cards"
|
||||
|
|
@ -35,21 +38,35 @@
|
|||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"description": "Set Default Options for all charts on this Dashboard (Ex: \"colors\": [\"#d1d8dd\", \"#ff5858\"])",
|
||||
"fieldname": "chart_options",
|
||||
"fieldtype": "Code",
|
||||
"label": "Chart Options",
|
||||
"options": "JSON"
|
||||
"description": "Set Default Options for all charts on this Dashboard (Ex: \"colors\": [\"#d1d8dd\", \"#ff5858\"])",
|
||||
"fieldname": "chart_options",
|
||||
"fieldtype": "Code",
|
||||
"label": "Chart Options",
|
||||
"options": "JSON"
|
||||
},
|
||||
{
|
||||
"fieldname": "cards",
|
||||
"fieldtype": "Table",
|
||||
"label": "Cards",
|
||||
"options": "Number Card Link"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_standard",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Standard"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.is_standard",
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Link",
|
||||
"label": "Module",
|
||||
"mandatory_depends_on": "eval: doc.is_standard",
|
||||
"options": "Module Def"
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-04-29 13:26:37.362482",
|
||||
"modified": "2020-07-10 17:48:19.468813",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Dashboard",
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
from frappe.model.document import Document
|
||||
from frappe.modules.export_file import export_to_files
|
||||
import frappe
|
||||
from frappe import _
|
||||
import json
|
||||
|
|
@ -15,7 +16,23 @@ class Dashboard(Document):
|
|||
frappe.db.sql('''update
|
||||
tabDashboard set is_default = 0 where name != %s''', self.name)
|
||||
|
||||
if frappe.conf.developer_mode and self.is_standard:
|
||||
export_to_files(record_list=[['Dashboard', self.name, self.module + ' Dashboard']], record_module=self.module)
|
||||
|
||||
def validate(self):
|
||||
if not frappe.conf.developer_mode and self.is_standard:
|
||||
frappe.throw('Cannot edit Standard Dashboards')
|
||||
|
||||
if self.is_standard:
|
||||
non_standard_docs_map = {
|
||||
'Dashboard Chart': get_non_standard_charts_in_dashboard(self),
|
||||
'Number Card': get_non_standard_cards_in_dashboard(self)
|
||||
}
|
||||
|
||||
if non_standard_docs_map['Dashboard Chart'] or non_standard_docs_map['Number Card']:
|
||||
message = get_non_standard_warning_message(non_standard_docs_map)
|
||||
frappe.throw(message, title=_("Standard Not Set"), is_minimizable=True)
|
||||
|
||||
self.validate_custom_options()
|
||||
|
||||
def validate_custom_options(self):
|
||||
|
|
@ -48,3 +65,29 @@ def get_permitted_cards(dashboard_name):
|
|||
if frappe.has_permission('Number Card', doc=card.card):
|
||||
permitted_cards.append(card)
|
||||
return permitted_cards
|
||||
|
||||
def get_non_standard_charts_in_dashboard(dashboard):
|
||||
non_standard_charts = [doc.name for doc in frappe.get_list('Dashboard Chart', {'is_standard': 0})]
|
||||
return [chart_link.chart for chart_link in dashboard.charts if chart_link.chart in non_standard_charts]
|
||||
|
||||
def get_non_standard_cards_in_dashboard(dashboard):
|
||||
non_standard_cards = [doc.name for doc in frappe.get_list('Number Card', {'is_standard': 0})]
|
||||
return [card_link.card for card_link in dashboard.cards if card_link.card in non_standard_cards]
|
||||
|
||||
def get_non_standard_warning_message(non_standard_docs_map):
|
||||
message = _('''Please set the following documents in this Dashboard as standard first.''')
|
||||
|
||||
def get_html(docs, doctype):
|
||||
html = '<p>{}</p>'.format(frappe.bold(doctype))
|
||||
for doc in docs:
|
||||
html += '<div><a href="#Form/{doctype}/{doc}">{doc}</a></div>'.format(doctype=doctype, doc=doc)
|
||||
html += '<br>'
|
||||
return html
|
||||
|
||||
html = message + '<br>'
|
||||
|
||||
for doctype in non_standard_docs_map:
|
||||
if non_standard_docs_map[doctype]:
|
||||
html += get_html(non_standard_docs_map[doctype], doctype)
|
||||
|
||||
return html
|
||||
|
|
|
|||
|
|
@ -9,9 +9,24 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
frm.add_fetch('source', 'timeseries', 'timeseries');
|
||||
},
|
||||
|
||||
before_save: function(frm) {
|
||||
let dynamic_filters = JSON.parse(frm.doc.dynamic_filters_json || 'null');
|
||||
let static_filters = JSON.parse(frm.doc.filters_json || 'null');
|
||||
static_filters =
|
||||
frappe.dashboard_utils.remove_common_static_filter_values(static_filters, dynamic_filters);
|
||||
|
||||
frm.set_value('filters_json', JSON.stringify(static_filters));
|
||||
frm.trigger('show_filters');
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
frm.chart_filters = null;
|
||||
|
||||
if (!frappe.boot.developer_mode && frm.doc.is_standard) {
|
||||
frm.set_df_property('chart_options_section', 'hidden', 1);
|
||||
frm.disable_form();
|
||||
}
|
||||
|
||||
frm.add_custom_button('Add Chart to Dashboard', () => {
|
||||
const d = new frappe.ui.Dialog({
|
||||
title: __('Add to Dashboard'),
|
||||
|
|
@ -49,6 +64,8 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
});
|
||||
|
||||
frm.set_df_property("filters_section", "hidden", 1);
|
||||
frm.set_df_property("dynamic_filters_section", "hidden", 1);
|
||||
|
||||
frm.trigger('set_time_series');
|
||||
frm.set_query('document_type', function() {
|
||||
return {
|
||||
|
|
@ -66,6 +83,15 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
if (!frappe.boot.developer_mode) {
|
||||
frm.set_df_property("custom_options", "hidden", 1);
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
is_standard: function(frm) {
|
||||
if (frappe.boot.developer_mode && frm.doc.is_standard) {
|
||||
frm.trigger('render_dynamic_filters_table');
|
||||
} else {
|
||||
frm.set_df_property("dynamic_filters_section", "hidden", 1);
|
||||
}
|
||||
},
|
||||
|
||||
source: function(frm) {
|
||||
|
|
@ -111,6 +137,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
frm.set_value('based_on', '');
|
||||
frm.set_value('value_based_on', '');
|
||||
frm.set_value('filters_json', '[]');
|
||||
frm.set_value('dynamic_filters_json', '[]');
|
||||
frm.trigger('update_options');
|
||||
},
|
||||
|
||||
|
|
@ -119,6 +146,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
frm.set_value('y_axis', []);
|
||||
frm.set_df_property('x_field', 'options', []);
|
||||
frm.set_value('filters_json', '{}');
|
||||
frm.set_value('dynamic_filters_json', '{}');
|
||||
frm.trigger('set_chart_report_filters');
|
||||
},
|
||||
|
||||
|
|
@ -146,7 +174,7 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
},
|
||||
|
||||
set_chart_field_options: function(frm) {
|
||||
let filters = frm.doc.filters_json.length > 2? JSON.parse(frm.doc.filters_json): null;
|
||||
let filters = frm.doc.filters_json.length > 2 ? JSON.parse(frm.doc.filters_json) : null;
|
||||
frappe.xcall(
|
||||
'frappe.desk.query_report.run',
|
||||
{
|
||||
|
|
@ -240,11 +268,14 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
show_filters: function(frm) {
|
||||
frm.chart_filters = [];
|
||||
frappe.dashboard_utils.get_filters_for_chart_type(frm.doc).then(filters => {
|
||||
if (filters) {
|
||||
frm.chart_filters = filters;
|
||||
}
|
||||
if (filters) {
|
||||
frm.chart_filters = filters;
|
||||
}
|
||||
frm.trigger('render_filters_table');
|
||||
|
||||
frm.trigger('render_filters_table');
|
||||
if (frappe.boot.developer_mode && frm.doc.is_standard) {
|
||||
frm.trigger('render_dynamic_filters_table');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -257,8 +288,8 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
let table = $(`<table class="table table-bordered" style="cursor:pointer; margin:0px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 33%">${__('Filter')}</th>
|
||||
<th style="width: 33%">${__('Condition')}</th>
|
||||
<th style="width: 20%">${__('Filter')}</th>
|
||||
<th style="width: 20%">${__('Condition')}</th>
|
||||
<th>${__('Value')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
@ -378,4 +409,141 @@ frappe.ui.form.on('Dashboard Chart', {
|
|||
});
|
||||
},
|
||||
|
||||
render_dynamic_filters_table(frm) {
|
||||
frm.set_df_property("dynamic_filters_section", "hidden", 0);
|
||||
|
||||
let is_document_type = frm.doc.chart_type !== 'Report'
|
||||
&& frm.doc.chart_type !== 'Custom';
|
||||
|
||||
let wrapper = $(frm.get_field('dynamic_filters_json').wrapper).empty();
|
||||
|
||||
frm.dynamic_filter_table = $(`<table class="table table-bordered" style="cursor:pointer; margin:0px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 20%">${__('Filter')}</th>
|
||||
<th style="width: 20%">${__('Condition')}</th>
|
||||
<th>${__('Value')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>`).appendTo(wrapper);
|
||||
|
||||
frm.dynamic_filters = frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2
|
||||
? JSON.parse(frm.doc.dynamic_filters_json)
|
||||
: null;
|
||||
|
||||
frm.trigger('set_dynamic_filters_in_table');
|
||||
|
||||
let filters = JSON.parse(frm.doc.filters_json || '[]');
|
||||
let fields = [
|
||||
{
|
||||
fieldtype: 'HTML',
|
||||
fieldname: 'description',
|
||||
options:
|
||||
`<div>
|
||||
<p>Set dynamic filter values in JavaScript for the required fields here.
|
||||
</p>
|
||||
<p>Ex:
|
||||
<code>frappe.defaults.get_user_default("Company")</code>
|
||||
</p>
|
||||
</div>`
|
||||
}
|
||||
];
|
||||
|
||||
if (is_document_type) {
|
||||
if (frm.dynamic_filters) {
|
||||
filters = [...filters, ...frm.dynamic_filters];
|
||||
}
|
||||
filters.forEach(f => {
|
||||
for (let field of fields) {
|
||||
if (field.fieldname == f[0] + ':' + f[1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (f[2] == '=') {
|
||||
fields.push({
|
||||
label: `${f[1]} (${f[0]})`,
|
||||
fieldname: f[0] + ':' + f[1],
|
||||
fieldtype: 'Data',
|
||||
});
|
||||
}
|
||||
});
|
||||
} else {
|
||||
filters = {...frm.dynamic_filters, ...filters};
|
||||
for (let key of Object.keys(filters)) {
|
||||
fields.push({
|
||||
label: key,
|
||||
fieldname: key,
|
||||
fieldtype: 'Data',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
frm.dynamic_filter_table.on('click', () => {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __('Set Dynamic Filters'),
|
||||
fields: fields,
|
||||
primary_action: () => {
|
||||
let values = dialog.get_values();
|
||||
dialog.hide();
|
||||
let dynamic_filters = [];
|
||||
for (let key of Object.keys(values)) {
|
||||
if (is_document_type) {
|
||||
let [doctype, fieldname] = key.split(':');
|
||||
dynamic_filters.push([doctype, fieldname, '=', values[key]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_document_type) {
|
||||
frm.set_value('dynamic_filters_json', JSON.stringify(dynamic_filters));
|
||||
} else {
|
||||
frm.set_value('dynamic_filters_json', JSON.stringify(values));
|
||||
}
|
||||
frm.trigger('set_dynamic_filters_in_table');
|
||||
},
|
||||
primary_action_label: "Set"
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
dialog.set_values(frm.dynamic_filters);
|
||||
});
|
||||
},
|
||||
|
||||
set_dynamic_filters_in_table: function(frm) {
|
||||
frm.dynamic_filters = frm.doc.dynamic_filters_json && frm.doc.dynamic_filters_json.length > 2
|
||||
? JSON.parse(frm.doc.dynamic_filters_json)
|
||||
: null;
|
||||
|
||||
if (!frm.dynamic_filters) {
|
||||
const filter_row = $(`<tr><td colspan="3" class="text-muted text-center">
|
||||
${__("Click to Set Dynamic Filters")}</td></tr>`);
|
||||
frm.dynamic_filter_table.find('tbody').html(filter_row);
|
||||
} else {
|
||||
let filter_rows = '';
|
||||
if ($.isArray(frm.dynamic_filters)) {
|
||||
frm.dynamic_filters.forEach(filter => {
|
||||
filter_rows +=
|
||||
`<tr>
|
||||
<td>${filter[1]}</td>
|
||||
<td>${filter[2] || ""}</td>
|
||||
<td>${filter[3]}</td>
|
||||
</tr>`;
|
||||
});
|
||||
} else {
|
||||
let condition = '=';
|
||||
for (let [key, val] of Object.entries(frm.dynamic_filters)) {
|
||||
filter_rows +=
|
||||
`<tr>
|
||||
<td>${key}</td>
|
||||
<td>${condition}</td>
|
||||
<td>${val || ""}</td>
|
||||
</tr>`
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
frm.dynamic_filter_table.find('tbody').html(filter_rows);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -7,6 +7,8 @@
|
|||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"is_standard",
|
||||
"module",
|
||||
"chart_name",
|
||||
"chart_type",
|
||||
"report_name",
|
||||
|
|
@ -32,10 +34,12 @@
|
|||
"type",
|
||||
"filters_section",
|
||||
"filters_json",
|
||||
"dynamic_filters_section",
|
||||
"dynamic_filters_json",
|
||||
"chart_options_section",
|
||||
"color",
|
||||
"column_break_2",
|
||||
"custom_options",
|
||||
"column_break_2",
|
||||
"color",
|
||||
"section_break_10",
|
||||
"last_synced_on"
|
||||
],
|
||||
|
|
@ -237,6 +241,39 @@
|
|||
"fieldname": "heatmap_year",
|
||||
"fieldtype": "Select",
|
||||
"label": "Year"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_standard",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Standard",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.is_standard",
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Link",
|
||||
"label": "Module",
|
||||
"mandatory_depends_on": "eval: doc.is_standard",
|
||||
"options": "Module Def",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "dynamic_filters_json",
|
||||
"fieldtype": "Code",
|
||||
"label": "Dynamic Filters JSON",
|
||||
"options": "JSON",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "dynamic_filters_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Dynamic Filters",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ from frappe.utils import nowdate, add_to_date, getdate, get_last_day, formatdate
|
|||
from frappe.model.naming import append_number_if_name_exists
|
||||
from frappe.boot import get_allowed_reports
|
||||
from frappe.model.document import Document
|
||||
from frappe.modules.export_file import export_to_files
|
||||
|
||||
|
||||
def get_permission_query_conditions(user):
|
||||
|
|
@ -80,7 +81,9 @@ def get(chart_name = None, chart = None, no_cache = None, filters = None, from_d
|
|||
to_date = get_datetime(chart.to_date)
|
||||
|
||||
timegrain = time_interval or chart.time_interval
|
||||
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json) or []
|
||||
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
|
||||
if not filters:
|
||||
filters = []
|
||||
|
||||
# don't include cancelled documents
|
||||
filters.append([chart.document_type, 'docstatus', '<', 2, False])
|
||||
|
|
@ -347,8 +350,13 @@ class DashboardChart(Document):
|
|||
|
||||
def on_update(self):
|
||||
frappe.cache().delete_key('chart-data:{}'.format(self.name))
|
||||
if frappe.conf.developer_mode and self.is_standard:
|
||||
export_to_files(record_list=[['Dashboard Chart', self.name]], record_module=self.module)
|
||||
|
||||
|
||||
def validate(self):
|
||||
if not frappe.conf.developer_mode and self.is_standard:
|
||||
frappe.throw('Cannot edit Standard charts')
|
||||
if self.chart_type != 'Custom' and self.chart_type != 'Report':
|
||||
self.check_required_field()
|
||||
self.check_document_type()
|
||||
|
|
|
|||
|
|
@ -3,9 +3,32 @@
|
|||
|
||||
frappe.ui.form.on('Number Card', {
|
||||
refresh: function(frm) {
|
||||
if (!frappe.boot.developer_mode && frm.doc.is_standard) {
|
||||
frm.disable_form();
|
||||
}
|
||||
frm.set_df_property("filters_section", "hidden", 1);
|
||||
frm.set_df_property("dynamic_filters_section", "hidden", 1);
|
||||
frm.trigger('set_options');
|
||||
frm.trigger('render_filters_table');
|
||||
frm.trigger('render_dynamic_filters_table');
|
||||
},
|
||||
|
||||
before_save: function(frm) {
|
||||
let dynamic_filters = JSON.parse(frm.doc.dynamic_filters_json || 'null');
|
||||
let static_filters = JSON.parse(frm.doc.filters_json || 'null');
|
||||
static_filters =
|
||||
frappe.dashboard_utils.remove_common_static_filter_values(static_filters, dynamic_filters);
|
||||
|
||||
frm.set_value('filters_json', JSON.stringify(static_filters));
|
||||
frm.trigger('render_filters_table');
|
||||
},
|
||||
|
||||
is_standard: function(frm) {
|
||||
if (frappe.boot.developer_mode && frm.doc.is_standard) {
|
||||
frm.trigger('render_dynamic_filters_table');
|
||||
} else {
|
||||
frm.set_df_property("dynamic_filters_section", "hidden", 1);
|
||||
}
|
||||
},
|
||||
|
||||
document_type: function(frm) {
|
||||
|
|
@ -17,6 +40,7 @@ frappe.ui.form.on('Number Card', {
|
|||
};
|
||||
});
|
||||
frm.set_value('filters_json', '[]');
|
||||
frm.set_value('dynamic_filters_json', '[]');
|
||||
frm.set_value('aggregate_function_based_on', '');
|
||||
frm.trigger('set_options');
|
||||
},
|
||||
|
|
@ -47,7 +71,7 @@ frappe.ui.form.on('Number Card', {
|
|||
frm.set_df_property("filters_section", "hidden", 0);
|
||||
|
||||
let wrapper = $(frm.get_field('filters_json').wrapper).empty();
|
||||
frm.filter_table = $(`<table class="table table-bordered" style="cursor:pointer; margin:0px;">
|
||||
let table = $(`<table class="table table-bordered" style="cursor:pointer; margin:0px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 33%">${__('Filter')}</th>
|
||||
|
|
@ -60,9 +84,9 @@ frappe.ui.form.on('Number Card', {
|
|||
|
||||
frm.filters = JSON.parse(frm.doc.filters_json || '[]');
|
||||
|
||||
frm.trigger('set_filters_in_table');
|
||||
set_filters_in_table(frm.filters, table);
|
||||
|
||||
frm.filter_table.on('click', () => {
|
||||
table.on('click', () => {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __('Set Filters'),
|
||||
fields: [{
|
||||
|
|
@ -75,7 +99,8 @@ frappe.ui.form.on('Number Card', {
|
|||
this.hide();
|
||||
frm.filters = frm.filter_group.get_filters();
|
||||
frm.set_value('filters_json', JSON.stringify(frm.filters));
|
||||
frm.trigger('set_filters_in_table');
|
||||
set_filters_in_table(frm.filters, table);
|
||||
frm.trigger('render_dynamic_filters_table');
|
||||
}
|
||||
},
|
||||
primary_action_label: "Set"
|
||||
|
|
@ -97,23 +122,110 @@ frappe.ui.form.on('Number Card', {
|
|||
|
||||
},
|
||||
|
||||
set_filters_in_table: function(frm) {
|
||||
if (!frm.filters.length) {
|
||||
const filter_row = $(`<tr><td colspan="3" class="text-muted text-center">
|
||||
${__("Click to Set Filters")}</td></tr>`);
|
||||
frm.filter_table.find('tbody').html(filter_row);
|
||||
} else {
|
||||
let filter_rows = '';
|
||||
frm.filters.forEach(filter => {
|
||||
filter_rows +=
|
||||
`<tr>
|
||||
<td>${filter[1]}</td>
|
||||
<td>${filter[2] || ""}</td>
|
||||
<td>${filter[3]}</td>
|
||||
</tr>`;
|
||||
|
||||
});
|
||||
frm.filter_table.find('tbody').html(filter_rows);
|
||||
render_dynamic_filters_table: function(frm) {
|
||||
if (!frappe.boot.developer_mode || !frm.doc.is_standard) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
let wrapper = $(frm.get_field('dynamic_filters_json').wrapper).empty();
|
||||
|
||||
frm.set_df_property("dynamic_filters_section", "hidden", 0);
|
||||
|
||||
let table = $(`<table class="table table-bordered" style="cursor:pointer; margin:0px;">
|
||||
<thead>
|
||||
<tr>
|
||||
<th style="width: 20%">${__('Filter')}</th>
|
||||
<th style="width: 20%">${__('Condition')}</th>
|
||||
<th>${__('Value')}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>`).appendTo(wrapper);
|
||||
|
||||
frm.dynamic_filters = JSON.parse(frm.doc.dynamic_filters_json || '[]');
|
||||
|
||||
set_filters_in_table(frm.dynamic_filters, table);
|
||||
|
||||
let filters = JSON.parse(frm.doc.filters_json || '[]');
|
||||
let fields = [
|
||||
{
|
||||
fieldtype: 'HTML',
|
||||
fieldname: 'description',
|
||||
options:
|
||||
`<div>
|
||||
<p>Set dynamic filter values in JavaScript for the required fields here.
|
||||
</p>
|
||||
<p>Ex:
|
||||
<code>frappe.defaults.get_user_default("Company")</code>
|
||||
</p>
|
||||
</div>`
|
||||
}
|
||||
];
|
||||
|
||||
if (frm.dynamic_filters.length) {
|
||||
filters = [...filters, ...frm.dynamic_filters];
|
||||
}
|
||||
|
||||
filters.forEach(f => {
|
||||
for (let field of fields) {
|
||||
if (field.fieldname == f[0] + ':' + f[1]) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (f[2] == '=') {
|
||||
fields.push({
|
||||
label: `${f[1]} (${f[0]})`,
|
||||
fieldname: f[0] + ':' + f[1],
|
||||
fieldtype: 'Data',
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
table.on('click', () => {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __('Set Filters'),
|
||||
fields: fields,
|
||||
primary_action: () => {
|
||||
let values = dialog.get_values();
|
||||
if (values) {
|
||||
dialog.hide();
|
||||
let dynamic_filters = [];
|
||||
for (let key of Object.keys(values)) {
|
||||
let [doctype, fieldname] = key.split(':');
|
||||
dynamic_filters.push([doctype, fieldname, '=', values[key]]);
|
||||
}
|
||||
|
||||
frm.set_value('dynamic_filters_json', JSON.stringify(dynamic_filters));
|
||||
frm.dynamic_filters = dynamic_filters;
|
||||
set_filters_in_table(frm.dynamic_filters, table);
|
||||
}
|
||||
},
|
||||
primary_action_label: "Set"
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
dialog.set_values(filters);
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
function set_filters_in_table(filters, table) {
|
||||
if (!filters.length) {
|
||||
const filter_row = $(`<tr><td colspan="3" class="text-muted text-center">
|
||||
${__("Click to Set Filters")}</td></tr>`);
|
||||
table.find('tbody').html(filter_row);
|
||||
} else {
|
||||
let filter_rows = '';
|
||||
filters.forEach(filter => {
|
||||
filter_rows +=
|
||||
`<tr>
|
||||
<td>${filter[1]}</td>
|
||||
<td>${filter[2] || ""}</td>
|
||||
<td>${filter[3]}</td>
|
||||
</tr>`;
|
||||
|
||||
});
|
||||
table.find('tbody').html(filter_rows);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
{
|
||||
"actions": [],
|
||||
"allow_rename": 1,
|
||||
"creation": "2020-04-15 18:06:39.444683",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"is_standard",
|
||||
"module",
|
||||
"label",
|
||||
"function",
|
||||
"aggregate_function_based_on",
|
||||
|
|
@ -16,6 +19,9 @@
|
|||
"stats_time_interval",
|
||||
"filters_section",
|
||||
"filters_json",
|
||||
"dynamic_filters_section",
|
||||
"dynamic_filters_json",
|
||||
"section_break_16",
|
||||
"color"
|
||||
],
|
||||
"fields": [
|
||||
|
|
@ -95,10 +101,47 @@
|
|||
"fieldname": "stats_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Stats"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_standard",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Standard"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.is_standard",
|
||||
"fieldname": "module",
|
||||
"fieldtype": "Link",
|
||||
"label": "Module",
|
||||
"mandatory_depends_on": "eval: doc.is_standard",
|
||||
"options": "Module Def",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "dynamic_filters_json",
|
||||
"fieldtype": "Code",
|
||||
"label": "Dynamic Filters JSON",
|
||||
"options": "JSON",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_16",
|
||||
"fieldtype": "Section Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "dynamic_filters_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Dynamic Filters Section",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2020-05-06 19:47:57.753574",
|
||||
"modified": "2020-07-10 17:55:35.873222",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Number Card",
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import frappe
|
|||
from frappe.model.document import Document
|
||||
from frappe.utils import cint
|
||||
from frappe.model.naming import append_number_if_name_exists
|
||||
from frappe.modules.export_file import export_to_files
|
||||
|
||||
class NumberCard(Document):
|
||||
def autoname(self):
|
||||
|
|
@ -16,6 +17,10 @@ class NumberCard(Document):
|
|||
if frappe.db.exists("Number Card", self.name):
|
||||
self.name = append_number_if_name_exists('Number Card', self.name)
|
||||
|
||||
def on_update(self):
|
||||
if frappe.conf.developer_mode and self.is_standard:
|
||||
export_to_files(record_list=[['Number Card', self.name]], record_module=self.module)
|
||||
|
||||
def get_permission_query_conditions(user=None):
|
||||
if not user:
|
||||
user = frappe.session.user
|
||||
|
|
@ -67,6 +72,9 @@ def get_result(doc, to_date=None):
|
|||
|
||||
filters = frappe.parse_json(doc.filters_json)
|
||||
|
||||
if not filters:
|
||||
filters = []
|
||||
|
||||
if to_date:
|
||||
filters.append([doc.document_type, 'creation', '<', to_date, False])
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ from frappe.integrations.frappe_providers.frappecloud import frappecloud_migrato
|
|||
|
||||
def migrate_to(local_site, frappe_provider):
|
||||
if frappe_provider in ("frappe.cloud", "frappecloud.com"):
|
||||
return frappecloud_migrator(local_site, frappe_provider)
|
||||
return frappecloud_migrator(local_site)
|
||||
else:
|
||||
print("{} is not supported yet".format(frappe_provider))
|
||||
sys.exit(1)
|
||||
|
|
|
|||
|
|
@ -1,412 +1,29 @@
|
|||
# imports - standard imports
|
||||
import getpass
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
|
||||
# imports - third party imports
|
||||
import click
|
||||
from html2text import html2text
|
||||
import requests
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed
|
||||
from html2text import html2text
|
||||
|
||||
# imports - module imports
|
||||
import frappe
|
||||
import frappe.utils.backups
|
||||
from frappe.utils import get_installed_apps_info
|
||||
from frappe.utils.commands import render_table, add_line_after, add_line_before
|
||||
|
||||
|
||||
# TODO: check upgrade compatibility
|
||||
|
||||
|
||||
def render_actions_table():
|
||||
actions_table = [["#", "Action"]]
|
||||
actions = []
|
||||
|
||||
for n, action in enumerate(migrator_actions):
|
||||
actions_table.append([n+1, action["title"]])
|
||||
actions.append(action["fn"])
|
||||
|
||||
render_table(actions_table)
|
||||
return actions
|
||||
|
||||
|
||||
def render_site_table(sites_info):
|
||||
sites_table = [["#", "Site Name", "Status"]]
|
||||
available_sites = []
|
||||
|
||||
for n, site_data in enumerate(sites_info):
|
||||
name, status = site_data["name"], site_data["status"]
|
||||
if status in ("Active", "Broken"):
|
||||
sites_table.append([n + 1, name, status])
|
||||
available_sites.append(name)
|
||||
|
||||
render_table(sites_table)
|
||||
return available_sites
|
||||
|
||||
|
||||
def render_teams_table(teams):
|
||||
teams_table = [["#", "Team"]]
|
||||
|
||||
for n, team in enumerate(teams):
|
||||
teams_table.append([n+1, team])
|
||||
|
||||
render_table(teams_table)
|
||||
|
||||
|
||||
def render_plan_table(plans_list):
|
||||
plans_table = [["Plan", "CPU Time"]]
|
||||
visible_headers = ["name", "cpu_time_per_day"]
|
||||
|
||||
for plan in plans_list:
|
||||
plan, cpu_time = [plan[header] for header in visible_headers]
|
||||
plans_table.append([plan, "{} hour{}/day".format(cpu_time, "" if cpu_time < 2 else "s")])
|
||||
|
||||
render_table(plans_table)
|
||||
|
||||
|
||||
def render_group_table(app_groups):
|
||||
# title row
|
||||
app_groups_table = [["#", "App Group", "Apps"]]
|
||||
|
||||
# all rows
|
||||
for idx, app_group in enumerate(app_groups):
|
||||
apps_list = ", ".join(["{}:{}".format(app["scrubbed"], app["branch"]) for app in app_group["apps"]])
|
||||
row = [idx + 1, app_group["name"], apps_list]
|
||||
app_groups_table.append(row)
|
||||
|
||||
render_table(app_groups_table)
|
||||
|
||||
|
||||
def handle_request_failure(request=None, message=None, traceback=True, exit_code=1):
|
||||
message = message or "Request failed with error code {}".format(request.status_code)
|
||||
response = html2text(request.text) if traceback else ""
|
||||
|
||||
print("{0}{1}".format(message, "\n" + response))
|
||||
sys.exit(exit_code)
|
||||
|
||||
|
||||
@add_line_after
|
||||
def select_primary_action():
|
||||
actions = render_actions_table()
|
||||
idx = click.prompt("What do you want to do?", type=click.IntRange(1, len(actions))) - 1
|
||||
|
||||
return actions[idx]
|
||||
|
||||
|
||||
@add_line_after
|
||||
def select_site():
|
||||
get_all_sites_request = session.post(all_site_url, headers={
|
||||
"accept": "application/json",
|
||||
"accept-encoding": "gzip, deflate, br",
|
||||
"content-type": "application/json; charset=utf-8"
|
||||
})
|
||||
|
||||
if get_all_sites_request.ok:
|
||||
all_sites = get_all_sites_request.json()["message"]
|
||||
available_sites = render_site_table(all_sites)
|
||||
|
||||
while True:
|
||||
selected_site = click.prompt("Name of the site you want to restore to", type=str).strip()
|
||||
if selected_site in available_sites:
|
||||
return selected_site
|
||||
else:
|
||||
print("Site {} does not exist. Try again ❌".format(selected_site))
|
||||
else:
|
||||
print("Couldn't retrive sites list...Try again later")
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
@add_line_before
|
||||
def select_team(session):
|
||||
# get team options
|
||||
account_details_sc = session.post(account_details_url)
|
||||
if account_details_sc.ok:
|
||||
account_details = account_details_sc.json()["message"]
|
||||
available_teams = account_details["teams"]
|
||||
|
||||
# ask if they want to select, go ahead with if only one exists
|
||||
if len(available_teams) == 1:
|
||||
team = available_teams[0]
|
||||
else:
|
||||
render_teams_table(available_teams)
|
||||
idx = click.prompt("Select Team", type=click.IntRange(1, len(available_teams))) - 1
|
||||
team = available_teams[idx]
|
||||
|
||||
print("Team '{}' set for current session".format(team))
|
||||
|
||||
return team
|
||||
|
||||
|
||||
@retry(stop=stop_after_attempt(5))
|
||||
def get_new_site_options():
|
||||
site_options_sc = session.post(options_url)
|
||||
|
||||
if site_options_sc.ok:
|
||||
site_options = site_options_sc.json()["message"]
|
||||
return site_options
|
||||
else:
|
||||
print("Couldn't retrive New site information: {}".format(site_options_sc.status_code))
|
||||
|
||||
|
||||
def is_valid_subdomain(subdomain):
|
||||
if len(subdomain) < 5:
|
||||
print("Subdomain too short. Use 5 or more characters")
|
||||
return False
|
||||
matched = re.match("^[a-z0-9][a-z0-9-]*[a-z0-9]$", subdomain)
|
||||
if matched:
|
||||
return True
|
||||
print("Subdomain contains invalid characters. Use lowercase characters, numbers and hyphens")
|
||||
|
||||
|
||||
@retry(stop=stop_after_attempt(5))
|
||||
def is_subdomain_available(subdomain):
|
||||
res = session.post(site_exists_url, {"subdomain": subdomain})
|
||||
if res.ok:
|
||||
available = not res.json()["message"]
|
||||
if not available:
|
||||
print("Subdomain already exists! Try another one")
|
||||
|
||||
return available
|
||||
|
||||
|
||||
@add_line_after
|
||||
def choose_plan(plans_list):
|
||||
print("{} plans available".format(len(plans_list)))
|
||||
available_plans = [plan["name"] for plan in plans_list]
|
||||
render_plan_table(plans_list)
|
||||
|
||||
while True:
|
||||
input_plan = click.prompt("Select Plan").strip()
|
||||
if input_plan in available_plans:
|
||||
print("{} Plan selected ✅".format(input_plan))
|
||||
return input_plan
|
||||
else:
|
||||
print("Invalid Selection ❌")
|
||||
|
||||
|
||||
@add_line_after
|
||||
def check_app_compat(available_group):
|
||||
is_compat = True
|
||||
incompatible_apps, filtered_apps, branch_msgs = [], [], []
|
||||
existing_group = [(app["app_name"], app["branch"]) for app in get_installed_apps_info()]
|
||||
print("Checking availability of existing app group")
|
||||
|
||||
for (app, branch) in existing_group:
|
||||
info = [ (a["name"], a["branch"]) for a in available_group["apps"] if a["scrubbed"] == app ]
|
||||
if info:
|
||||
app_title, available_branch = info[0]
|
||||
|
||||
if branch != available_branch:
|
||||
print("⚠️ App {}:{} => {}".format(app, branch, available_branch))
|
||||
branch_msgs.append([app, branch, available_branch])
|
||||
filtered_apps.append(app_title)
|
||||
is_compat = False
|
||||
|
||||
else:
|
||||
print("✅ App {}:{}".format(app, branch))
|
||||
filtered_apps.append(app_title)
|
||||
|
||||
else:
|
||||
incompatible_apps.append(app)
|
||||
print("❌ App {}:{}".format(app, branch))
|
||||
is_compat = False
|
||||
|
||||
start_msg = "\nSelecting this group will "
|
||||
incompatible_apps = ("\n\nDrop the following apps:\n" + "\n".join(incompatible_apps)) if incompatible_apps else ""
|
||||
branch_change = ("\n\nUpgrade the following apps:\n" + "\n".join(["{}: {} => {}".format(*x) for x in branch_msgs])) if branch_msgs else ""
|
||||
changes = (incompatible_apps + branch_change) or "be perfect for you :)"
|
||||
warning_message = start_msg + changes
|
||||
print(warning_message)
|
||||
|
||||
return is_compat, filtered_apps
|
||||
|
||||
|
||||
@add_line_after
|
||||
def filter_apps(app_groups):
|
||||
render_group_table(app_groups)
|
||||
|
||||
while True:
|
||||
app_group_index = click.prompt("Select App Group Number", type=int) - 1
|
||||
try:
|
||||
if app_group_index == -1:
|
||||
raise IndexError
|
||||
selected_group = app_groups[app_group_index]
|
||||
except IndexError:
|
||||
print("Invalid Selection ❌")
|
||||
continue
|
||||
|
||||
is_compat, filtered_apps = check_app_compat(selected_group)
|
||||
|
||||
if is_compat or click.confirm("Continue anyway?"):
|
||||
print("App Group {} selected! ✅".format(selected_group["name"]))
|
||||
break
|
||||
|
||||
return selected_group["name"], filtered_apps
|
||||
|
||||
|
||||
@add_line_after
|
||||
def get_subdomain(domain):
|
||||
while True:
|
||||
subdomain = click.prompt("Enter subdomain").strip()
|
||||
if is_valid_subdomain(subdomain) and is_subdomain_available(subdomain):
|
||||
print("Site Domain: {}.{}".format(subdomain, domain))
|
||||
return subdomain
|
||||
|
||||
|
||||
@retry(stop=stop_after_attempt(2), wait=wait_fixed(5))
|
||||
def upload_backup_file(file_type, file_path):
|
||||
return session.post(files_url, data={}, files={
|
||||
"file": open(file_path, "rb"),
|
||||
"is_private": 1,
|
||||
"folder": "Home",
|
||||
"method": "press.api.site.upload_backup",
|
||||
"type": file_type
|
||||
})
|
||||
|
||||
|
||||
@add_line_after
|
||||
def upload_backup(local_site):
|
||||
# take backup
|
||||
files_session = {}
|
||||
print("Taking backup for site {}".format(local_site))
|
||||
odb = frappe.utils.backups.new_backup(ignore_files=False, force=True)
|
||||
|
||||
# upload files
|
||||
for x, (file_type, file_path) in enumerate([
|
||||
("database", odb.backup_path_db),
|
||||
("public", odb.backup_path_files),
|
||||
("private", odb.backup_path_private_files)
|
||||
]):
|
||||
file_name = file_path.split(os.sep)[-1]
|
||||
|
||||
print("Uploading {} file: {} ({}/3)".format(file_type, file_name, x+1))
|
||||
file_upload_response = upload_backup_file(file_type, file_path)
|
||||
|
||||
if file_upload_response.ok:
|
||||
files_session[file_type] = file_upload_response.json()["message"]
|
||||
else:
|
||||
print("Upload failed for: {}".format(file_path))
|
||||
|
||||
files_uploaded = { k: v["file_url"] for k, v in files_session.items() }
|
||||
print("Uploaded backup files! ✅")
|
||||
|
||||
return files_uploaded
|
||||
|
||||
|
||||
def new_site(local_site):
|
||||
# get new site options
|
||||
site_options = get_new_site_options()
|
||||
|
||||
# set preferences from site options
|
||||
subdomain = get_subdomain(site_options["domain"])
|
||||
plan = choose_plan(site_options["plans"])
|
||||
|
||||
app_groups = site_options["groups"]
|
||||
selected_group, filtered_apps = filter_apps(app_groups)
|
||||
files_uploaded = upload_backup(local_site)
|
||||
|
||||
# push to frappe_cloud
|
||||
payload = json.dumps({
|
||||
"site": {
|
||||
"apps": filtered_apps,
|
||||
"files": files_uploaded,
|
||||
"group": selected_group,
|
||||
"name": subdomain,
|
||||
"plan": plan
|
||||
}
|
||||
})
|
||||
|
||||
session.headers.update({"Content-Type": "application/json; charset=utf-8"})
|
||||
site_creation_request = session.post(upload_url, payload)
|
||||
|
||||
if site_creation_request.ok:
|
||||
site_url = site_creation_request.json()["message"]
|
||||
print("Your site {} is being migrated ✨".format(local_site))
|
||||
print("View your site dashboard at {}/dashboard/#/sites/{}".format(remote_site, site_url))
|
||||
print("Your site URL: {}".format(site_url))
|
||||
else:
|
||||
handle_request_failure(site_creation_request)
|
||||
|
||||
|
||||
def restore_site(local_site):
|
||||
# get list of existing sites they can restore
|
||||
selected_site = select_site()
|
||||
|
||||
# TODO: check if they can restore it
|
||||
|
||||
click.confirm("This is an irreversible action. Are you sure you want to continue?", abort=True)
|
||||
|
||||
# backup site
|
||||
files_uploaded = upload_backup(local_site)
|
||||
|
||||
# push to frappe_cloud
|
||||
payload = json.dumps({
|
||||
"name": selected_site,
|
||||
"files": files_uploaded
|
||||
})
|
||||
headers = {"Content-Type": "application/json; charset=utf-8"}
|
||||
site_restore_request = session.post(restore_site_url, payload, headers=headers)
|
||||
|
||||
if site_restore_request.ok:
|
||||
print("Your site {0} is being restored on {1} ✨".format(local_site, selected_site))
|
||||
print("View your site dashboard at {}/dashboard/#/sites/{}".format(remote_site, selected_site))
|
||||
print("Your site URL: {}".format(selected_site))
|
||||
else:
|
||||
handle_request_failure(site_restore_request)
|
||||
|
||||
|
||||
@add_line_after
|
||||
def create_session():
|
||||
print("Frappe Cloud credentials @ {}".format(remote_site))
|
||||
|
||||
# take user input from STDIN
|
||||
username = click.prompt("Username").strip()
|
||||
password = getpass.unix_getpass()
|
||||
|
||||
auth_credentials = {"usr": username, "pwd": password}
|
||||
|
||||
session = requests.Session()
|
||||
login_sc = session.post(login_url, auth_credentials)
|
||||
|
||||
if login_sc.ok:
|
||||
print("Authorization Successful! ✅")
|
||||
team = select_team(session)
|
||||
session.headers.update({
|
||||
"X-Press-Team": team,
|
||||
"Connection": "keep-alive"
|
||||
})
|
||||
return session
|
||||
else:
|
||||
handle_request_failure(message="Authorization Failed with Error Code {}".format(login_sc.status_code), traceback=False)
|
||||
|
||||
|
||||
def frappecloud_migrator(local_site, frappecloud_site):
|
||||
global login_url, upload_url, files_url, options_url, site_exists_url, restore_site_url, account_details_url, all_site_url
|
||||
global session, migrator_actions, remote_site
|
||||
|
||||
def frappecloud_migrator(local_site):
|
||||
print("Retreiving Site Migrator...")
|
||||
remote_site = frappe.conf.frappecloud_url or "frappecloud.com"
|
||||
request_url = "https://{}/api/method/press.api.script".format(remote_site)
|
||||
request = requests.get(request_url)
|
||||
|
||||
login_url = "https://{}/api/method/login".format(remote_site)
|
||||
upload_url = "https://{}/api/method/press.api.site.new".format(remote_site)
|
||||
files_url = "https://{}/api/method/upload_file".format(remote_site)
|
||||
options_url = "https://{}/api/method/press.api.site.options_for_new".format(remote_site)
|
||||
site_exists_url = "https://{}/api/method/press.api.site.exists".format(remote_site)
|
||||
account_details_url = "https://{}/api/method/press.api.account.get".format(remote_site)
|
||||
all_site_url = "https://{}/api/method/press.api.site.all".format(remote_site)
|
||||
restore_site_url = "https://{}/api/method/press.api.site.restore".format(remote_site)
|
||||
if request.status_code / 100 != 2:
|
||||
print("Request exitted with Status Code: {}\nPayload: {}".format(request.status_code, html2text(request.text)))
|
||||
click.secho("Some errors occurred while recovering the migration script. Please contact us @ Frappe Cloud if this issue persists", fg="yellow")
|
||||
return
|
||||
|
||||
migrator_actions = [
|
||||
{ "title": "Create a new site", "fn": new_site },
|
||||
{ "title": "Restore to an existing site", "fn": restore_site }
|
||||
]
|
||||
script_contents = request.json()["message"]
|
||||
|
||||
# get credentials + auth user + start session
|
||||
session = create_session()
|
||||
import tempfile
|
||||
import os
|
||||
import sys
|
||||
|
||||
# available actions defined in migrator_actions
|
||||
primary_action = select_primary_action()
|
||||
|
||||
primary_action(local_site)
|
||||
py = sys.executable
|
||||
script = tempfile.NamedTemporaryFile(mode="w")
|
||||
script.write(script_contents)
|
||||
print("Site Migrator stored at {}".format(script.name))
|
||||
os.execv(py, [py, script.name, local_site])
|
||||
|
|
|
|||
|
|
@ -45,7 +45,9 @@ def make_new_doc(doctype):
|
|||
doc = doc.get_valid_dict(sanitize=False)
|
||||
doc["doctype"] = doctype
|
||||
doc["__islocal"] = 1
|
||||
doc["__unsaved"] = 1
|
||||
|
||||
if not frappe.model.meta.is_single(doctype):
|
||||
doc["__unsaved"] = 1
|
||||
|
||||
return doc
|
||||
|
||||
|
|
|
|||
|
|
@ -203,7 +203,7 @@ class DatabaseQuery(object):
|
|||
def sanitize_fields(self):
|
||||
'''
|
||||
regex : ^.*[,();].*
|
||||
purpose : The regex will look for malicious patterns like `,`, '(', ')', ';' in each
|
||||
purpose : The regex will look for malicious patterns like `,`, '(', ')', '@', ;' in each
|
||||
field which may leads to sql injection.
|
||||
example :
|
||||
field = "`DocType`.`issingle`, version()"
|
||||
|
|
@ -211,11 +211,11 @@ class DatabaseQuery(object):
|
|||
the system will filter out this field.
|
||||
'''
|
||||
|
||||
sub_query_regex = re.compile("^.*[,();].*")
|
||||
blacklisted_keywords = ['select', 'create', 'insert', 'delete', 'drop', 'update', 'case']
|
||||
sub_query_regex = re.compile("^.*[,();@].*")
|
||||
blacklisted_keywords = ['select', 'create', 'insert', 'delete', 'drop', 'update', 'case', 'show']
|
||||
blacklisted_functions = ['concat', 'concat_ws', 'if', 'ifnull', 'nullif', 'coalesce',
|
||||
'connection_id', 'current_user', 'database', 'last_insert_id', 'session_user',
|
||||
'system_user', 'user', 'version']
|
||||
'system_user', 'user', 'version', 'global']
|
||||
|
||||
def _raise_exception():
|
||||
frappe.throw(_('Use of sub-query or function is restricted'), frappe.DataError)
|
||||
|
|
@ -238,6 +238,10 @@ class DatabaseQuery(object):
|
|||
if any("{0}(".format(keyword) in field.lower() for keyword in blacklisted_functions):
|
||||
_raise_exception()
|
||||
|
||||
if '@' in field.lower():
|
||||
# prevent access to global variables
|
||||
_raise_exception()
|
||||
|
||||
if re.compile(r"[0-9a-zA-Z]+\s*'").match(field):
|
||||
_raise_exception()
|
||||
|
||||
|
|
@ -854,4 +858,4 @@ def get_date_range(operator, value):
|
|||
|
||||
timespan = period_map[operator] + ' ' + timespan_map[value] if operator != 'timespan' else value
|
||||
|
||||
return get_timespan_date_range(timespan)
|
||||
return get_timespan_date_range(timespan)
|
||||
|
|
|
|||
|
|
@ -12,16 +12,17 @@ def export_doc(doc):
|
|||
|
||||
def export_to_files(record_list=None, record_module=None, verbose=0, create_init=None):
|
||||
"""
|
||||
Export record_list to files. record_list is a list of lists ([doctype],[docname] ) ,
|
||||
Export record_list to files. record_list is a list of lists ([doctype, docname, folder name],) ,
|
||||
"""
|
||||
if frappe.flags.in_import:
|
||||
return
|
||||
|
||||
if record_list:
|
||||
for record in record_list:
|
||||
write_document_file(frappe.get_doc(record[0], record[1]), record_module, create_init=create_init)
|
||||
folder_name = record[2] if len(record) == 3 else None
|
||||
write_document_file(frappe.get_doc(record[0], record[1]), record_module, create_init=create_init, folder_name=folder_name)
|
||||
|
||||
def write_document_file(doc, record_module=None, create_init=True):
|
||||
def write_document_file(doc, record_module=None, create_init=True, folder_name=None):
|
||||
newdoc = doc.as_dict(no_nulls=True)
|
||||
doc.run_method("before_export", newdoc)
|
||||
|
||||
|
|
@ -35,7 +36,10 @@ def write_document_file(doc, record_module=None, create_init=True):
|
|||
module = record_module or get_module_name(doc)
|
||||
|
||||
# create folder
|
||||
folder = create_folder(module, doc.doctype, doc.name, create_init)
|
||||
if folder_name:
|
||||
folder = create_folder(module, folder_name, doc.name, create_init)
|
||||
else:
|
||||
folder = create_folder(module, doc.doctype, doc.name, create_init)
|
||||
|
||||
# write the data file
|
||||
fname = scrub(doc.name)
|
||||
|
|
|
|||
|
|
@ -264,7 +264,7 @@ frappe.patches.v11_0.make_all_prepared_report_attachments_private #2019-11-26
|
|||
frappe.patches.v12_0.setup_email_linking
|
||||
frappe.patches.v12_0.fix_home_settings_for_all_users
|
||||
frappe.patches.v12_0.change_existing_dashboard_chart_filters
|
||||
frappe.patches.v12_0.set_correct_assign_value_in_docs
|
||||
frappe.patches.v12_0.set_correct_assign_value_in_docs #2020-07-13
|
||||
execute:frappe.delete_doc("Test Runner")
|
||||
execute:frappe.delete_doc_if_exists('DocType', 'Google Maps Settings')
|
||||
execute:frappe.db.set_default('desktop:home_page', 'workspace')
|
||||
|
|
@ -292,4 +292,5 @@ execute:frappe.delete_doc("DocType", "Onboarding Slide Field")
|
|||
execute:frappe.delete_doc("DocType", "Onboarding Slide Help Link")
|
||||
frappe.patches.v13_0.update_date_filters_in_user_settings
|
||||
frappe.patches.v13_0.update_duration_options
|
||||
frappe.patches.v13_0.replace_old_data_import # 2020-06-24
|
||||
frappe.patches.v13_0.replace_old_data_import # 2020-06-24
|
||||
frappe.patches.v13_0.create_custom_dashboards_cards_and_charts
|
||||
|
|
@ -9,8 +9,9 @@ def execute():
|
|||
FROM
|
||||
`tabToDo`
|
||||
WHERE
|
||||
COALESCE(reference_type, '') != '' and
|
||||
COALESCE(reference_name, '') != ''
|
||||
COALESCE(reference_type, '') != '' AND
|
||||
COALESCE(reference_name, '') != '' AND
|
||||
status != 'Cancelled'
|
||||
GROUP BY
|
||||
reference_type, reference_name
|
||||
'''
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
import frappe
|
||||
from frappe.model.naming import append_number_if_name_exists
|
||||
from frappe.utils.dashboard import get_dashboards_with_link
|
||||
|
||||
def execute():
|
||||
if not frappe.db.table_exists('Dashboard Chart')\
|
||||
or not frappe.db.table_exists('Number Card')\
|
||||
or not frappe.db.table_exists('Dashboard'):
|
||||
return
|
||||
|
||||
frappe.reload_doc('desk', 'doctype', 'dashboard_chart')
|
||||
frappe.reload_doc('desk', 'doctype', 'number_card')
|
||||
frappe.reload_doc('desk', 'doctype', 'dashboard')
|
||||
|
||||
modified_charts = get_modified_docs('Dashboard Chart')
|
||||
modified_cards = get_modified_docs('Number Card')
|
||||
modified_dashboards = [doc.name for doc in get_modified_docs('Dashboard')]
|
||||
|
||||
for chart in modified_charts:
|
||||
modified_dashboards += get_dashboards_with_link(chart.name, 'Dashboard Chart')
|
||||
rename_modified_doc(chart.name, 'Dashboard Chart')
|
||||
|
||||
for card in modified_cards:
|
||||
modified_dashboards += get_dashboards_with_link(card.name, 'Number Card')
|
||||
rename_modified_doc(card.name, 'Number Card')
|
||||
|
||||
modified_dashboards = list(set(modified_dashboards))
|
||||
|
||||
for dashboard in modified_dashboards:
|
||||
rename_modified_doc(dashboard, 'Dashboard')
|
||||
|
||||
def get_modified_docs(doctype):
|
||||
return frappe.get_all(doctype,
|
||||
filters = {
|
||||
'owner': 'Administrator',
|
||||
'modified_by': ['!=', 'Administrator']
|
||||
})
|
||||
|
||||
def rename_modified_doc(docname, doctype):
|
||||
new_name = docname + ' Custom'
|
||||
try:
|
||||
frappe.rename_doc(doctype, docname, new_name)
|
||||
except frappe.ValidationError:
|
||||
new_name = append_number_if_name_exists(doctype, new_name)
|
||||
frappe.rename_doc(doctype, docname, new_name)
|
||||
|
|
@ -6,11 +6,15 @@ import frappe
|
|||
|
||||
|
||||
def execute():
|
||||
if not frappe.db.exists("DocType", "Data Import Beta"):
|
||||
if not frappe.db.table_exists("Data Import"): return
|
||||
|
||||
meta = frappe.get_meta("Data Import")
|
||||
# if Data Import is the new one, return early
|
||||
if meta.fields[1].fieldname == "import_type":
|
||||
return
|
||||
|
||||
frappe.db.sql("DROP TABLE IF EXISTS `tabData Import Legacy`")
|
||||
frappe.rename_doc('DocType', 'Data Import', 'Data Import Legacy')
|
||||
frappe.rename_doc("DocType", "Data Import", "Data Import Legacy")
|
||||
frappe.db.commit()
|
||||
frappe.db.sql("DROP TABLE IF EXISTS `tabData Import`")
|
||||
frappe.rename_doc('DocType', 'Data Import Beta', 'Data Import')
|
||||
frappe.rename_doc("DocType", "Data Import Beta", "Data Import")
|
||||
|
|
|
|||
|
|
@ -1,932 +1,203 @@
|
|||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_rename": 0,
|
||||
"beta": 0,
|
||||
"actions": [],
|
||||
"creation": "2014-07-17 06:54:20.782907",
|
||||
"custom": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "DocType",
|
||||
"document_type": "System",
|
||||
"editable_grid": 0,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"pdf_settings",
|
||||
"send_print_as_pdf",
|
||||
"repeat_header_footer",
|
||||
"column_break_4",
|
||||
"pdf_page_size",
|
||||
"view_link_in_email",
|
||||
"with_letterhead",
|
||||
"allow_print_for_draft",
|
||||
"add_draft_heading",
|
||||
"column_break_10",
|
||||
"allow_page_break_inside_tables",
|
||||
"allow_print_for_cancelled",
|
||||
"server_printer",
|
||||
"enable_print_server",
|
||||
"server_ip",
|
||||
"printer_name",
|
||||
"port",
|
||||
"raw_printing_section",
|
||||
"enable_raw_printing",
|
||||
"print_style_section",
|
||||
"print_style",
|
||||
"print_style_preview",
|
||||
"section_break_8",
|
||||
"font",
|
||||
"font_size"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "pdf_settings",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "PDF Settings",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "PDF Settings"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"description": "Send Email Print Attachments as PDF (Recommended)",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "send_print_as_pdf",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Send Print as PDF",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Send Print as PDF"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "repeat_header_footer",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Repeat Header and Footer in PDF",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Repeat Header and Footer in PDF"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_4",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "A4",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "pdf_page_size",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "PDF Page Size",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "A4\nLetter",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"options": "A4\nLetter"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "view_link_in_email",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Page Settings",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Page Settings"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "with_letterhead",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print with letterhead",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Print with letterhead"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "allow_print_for_draft",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow Print for Draft",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Allow Print for Draft"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "attach_view_link",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Send document web view link in email",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_10",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "add_draft_heading",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Always add \"Draft\" Heading for printing draft documents",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Always add \"Draft\" Heading for printing draft documents"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"default": "0",
|
||||
"fieldname": "allow_page_break_inside_tables",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow page break inside tables",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Allow page break inside tables"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"default": "0",
|
||||
"fieldname": "allow_print_for_cancelled",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Allow Print for Cancelled",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Allow Print for Cancelled"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "server_printer",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Server",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Print Server"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"default": "0",
|
||||
"fieldname": "enable_print_server",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Enable Print Server",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Enable Print Server"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "localhost",
|
||||
"depends_on": "enable_print_server",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "server_ip",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Server IP",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Server IP"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "enable_print_server",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "printer_name",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Printer Name",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Printer Name"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "631",
|
||||
"depends_on": "enable_print_server",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "port",
|
||||
"fieldtype": "Int",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Port",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Port"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "raw_printing_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Raw Printing",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Raw Printing"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"default": "0",
|
||||
"fieldname": "enable_raw_printing",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Enable Raw Printing",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Enable Raw Printing"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "print_style_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Style",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Print Style"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Modern",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "print_style",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Style",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Style",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"options": "Print Style"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "print_style_preview",
|
||||
"fieldtype": "HTML",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Print Style Preview",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Print Style Preview"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_8",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Fonts",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Fonts"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Default",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "font",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Font",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Default\nArial\nHelvetica\nVerdana\nMonospace",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"options": "Default\nArial\nHelvetica\nVerdana\nMonospace"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "In points. Default is 9.",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "font_size",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Font Size",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
"label": "Font Size"
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
"hide_toolbar": 0,
|
||||
"icon": "fa fa-cog",
|
||||
"idx": 0,
|
||||
"in_create": 0,
|
||||
"is_submittable": 0,
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-04-10 14:12:31.081187",
|
||||
"links": [],
|
||||
"modified": "2020-07-02 16:14:47.470668",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Printing",
|
||||
"name": "Print Settings",
|
||||
"name_case": "",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"amend": 0,
|
||||
"cancel": 0,
|
||||
"create": 1,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "System Manager",
|
||||
"set_user_permissions": 0,
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"read_only": 0,
|
||||
"show_name_in_global_search": 0,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -101,15 +101,6 @@ frappe.Application = Class.extend({
|
|||
frappe.ui.startup_setup_dialog.show();
|
||||
}
|
||||
|
||||
// listen to csrf_update
|
||||
frappe.realtime.on("csrf_generated", function(data) {
|
||||
// handles the case when a user logs in again from another tab
|
||||
// and it leads to invalid request in the current tab
|
||||
if (data.csrf_token && data.sid===frappe.get_cookie("sid")) {
|
||||
frappe.csrf_token = data.csrf_token;
|
||||
}
|
||||
});
|
||||
|
||||
frappe.realtime.on("version-update", function() {
|
||||
var dialog = frappe.msgprint({
|
||||
message:__("The application has been updated to a new version, please refresh this page"),
|
||||
|
|
|
|||
|
|
@ -199,6 +199,8 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
|
|||
|
||||
get_input_value() {
|
||||
let value = this.quill ? this.quill.root.innerHTML : '';
|
||||
// hack to retain space sequence.
|
||||
value = value.replace(/(\s)(\s)/g, ' ');
|
||||
return value;
|
||||
},
|
||||
|
||||
|
|
|
|||
|
|
@ -842,6 +842,15 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
this.page.clear_primary_action();
|
||||
}
|
||||
|
||||
disable_form() {
|
||||
this.set_read_only();
|
||||
this.fields
|
||||
.forEach((field) => {
|
||||
this.set_df_property(field.df.fieldname, "read_only", "1");
|
||||
});
|
||||
this.disable_save();
|
||||
}
|
||||
|
||||
handle_save_fail(btn, on_error) {
|
||||
$(btn).prop('disabled', false);
|
||||
if (on_error) {
|
||||
|
|
@ -1604,6 +1613,7 @@ frappe.ui.form.Form = class FrappeForm {
|
|||
});
|
||||
|
||||
driver.defineSteps(steps);
|
||||
frappe.route.on('change', () => driver.reset());
|
||||
driver.start();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -109,9 +109,10 @@ frappe.views.BaseList = class BaseList {
|
|||
this.fields = this.fields.uniqBy(f => f[0] + f[1]);
|
||||
}
|
||||
|
||||
_add_field(fieldname) {
|
||||
_add_field(fieldname, doctype) {
|
||||
if (!fieldname) return;
|
||||
let doctype = this.doctype;
|
||||
|
||||
if (!doctype) doctype = this.doctype;
|
||||
|
||||
if (typeof fieldname === 'object') {
|
||||
// df is passed
|
||||
|
|
@ -120,6 +121,8 @@ frappe.views.BaseList = class BaseList {
|
|||
doctype = df.parent;
|
||||
}
|
||||
|
||||
if (!this.fields) this.fields = [];
|
||||
|
||||
const is_valid_field = frappe.model.std_fields_list.includes(fieldname)
|
||||
|| frappe.meta.has_field(doctype, fieldname)
|
||||
|| fieldname === '_seen';
|
||||
|
|
|
|||
|
|
@ -126,7 +126,7 @@ frappe.request.call = function(opts) {
|
|||
message: __('The resource you are looking for is not available')});
|
||||
},
|
||||
403: function(xhr) {
|
||||
if (frappe.get_cookie('sid')==='Guest') {
|
||||
if (frappe.session.user === 'Guest') {
|
||||
// session expired
|
||||
frappe.app.handle_session_expired();
|
||||
}
|
||||
|
|
@ -321,7 +321,7 @@ frappe.request.cleanup = function(opts, r) {
|
|||
if(r) {
|
||||
|
||||
// session expired? - Guest has no business here!
|
||||
if(r.session_expired || frappe.get_cookie("sid")==="Guest") {
|
||||
if (r.session_expired || frappe.session.user === "Guest") {
|
||||
frappe.app.handle_session_expired();
|
||||
return;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ frappe.dashboard_utils = {
|
|||
frappe.dom.eval(config);
|
||||
return frappe.dashboards.chart_sources[chart.source].filters;
|
||||
});
|
||||
} else if (chart.chart_type === 'Report') {
|
||||
} else if (chart.chart_type === 'Report' && chart.report_name) {
|
||||
return frappe.report_utils.get_report_filters(chart.report_name).then(filters => {
|
||||
return filters;
|
||||
});
|
||||
|
|
@ -97,6 +97,28 @@ frappe.dashboard_utils = {
|
|||
|
||||
get_year(date_str) {
|
||||
return date_str.substring(0, date_str.indexOf('-'));
|
||||
},
|
||||
|
||||
remove_common_static_filter_values(static_filters, dynamic_filters) {
|
||||
if (dynamic_filters) {
|
||||
if ($.isArray(static_filters)) {
|
||||
static_filters = static_filters.filter(static_filter => {
|
||||
for (let dynamic_filter of dynamic_filters) {
|
||||
if (static_filter[0] == dynamic_filter[0]
|
||||
&& static_filter[1] == dynamic_filter[1]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
});
|
||||
} else {
|
||||
for (let key of Object.keys(dynamic_filters)) {
|
||||
delete static_filters[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return static_filters;
|
||||
}
|
||||
|
||||
};
|
||||
|
|
@ -646,11 +646,13 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
|
|||
|
||||
set_fields() {
|
||||
if (this.report_name && this.report_doc.json.fields) {
|
||||
this.fields = this.report_doc.json.fields.slice();
|
||||
let fields = this.report_doc.json.fields.slice();
|
||||
fields.forEach(f => this._add_field(f[0], f[1]));
|
||||
return;
|
||||
} else if (this.view_user_settings.fields) {
|
||||
// get from user_settings
|
||||
this.fields = this.view_user_settings.fields;
|
||||
let fields = this.view_user_settings.fields;
|
||||
fields.forEach(f => this._add_field(f[0], f[1]));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -412,10 +412,13 @@ export default class ChartWidget extends Widget {
|
|||
}
|
||||
|
||||
dialog.show();
|
||||
//Set query report object so that it can be used while fetching filter values in the report
|
||||
frappe.query_report = new frappe.views.QueryReport({'filters': dialog.fields_list});
|
||||
frappe.query_reports[this.chart_doc.report_name].onload
|
||||
&& frappe.query_reports[this.chart_doc.report_name].onload(frappe.query_report);
|
||||
|
||||
if (this.chart_doc.chart_type == 'Report') {
|
||||
//Set query report object so that it can be used while fetching filter values in the report
|
||||
frappe.query_report = new frappe.views.QueryReport({'filters': dialog.fields_list});
|
||||
frappe.query_reports[this.chart_doc.report_name].onload
|
||||
&& frappe.query_reports[this.chart_doc.report_name].onload(frappe.query_report);
|
||||
}
|
||||
dialog.set_values(this.filters);
|
||||
}
|
||||
|
||||
|
|
@ -636,7 +639,7 @@ export default class ChartWidget extends Widget {
|
|||
|
||||
set_chart_filters() {
|
||||
let user_saved_filters = this.chart_settings.filters || null;
|
||||
let chart_saved_filters = JSON.parse(this.chart_doc.filters_json || "null");
|
||||
let chart_saved_filters = this.get_all_chart_filters();
|
||||
|
||||
if (this.chart_doc.chart_type == 'Report') {
|
||||
return frappe.dashboard_utils
|
||||
|
|
@ -652,6 +655,38 @@ export default class ChartWidget extends Widget {
|
|||
}
|
||||
}
|
||||
|
||||
get_all_chart_filters() {
|
||||
let filters = JSON.parse(this.chart_doc.filters_json || "null");
|
||||
let dynamic_filters = JSON.parse(this.chart_doc.dynamic_filters_json || "null");
|
||||
|
||||
if (!dynamic_filters) {
|
||||
return filters;
|
||||
}
|
||||
|
||||
if ($.isArray(dynamic_filters)) {
|
||||
dynamic_filters.forEach(f => {
|
||||
try {
|
||||
f[3] = eval(f[3]);
|
||||
} catch (e) {
|
||||
frappe.throw(__(`Invalid expression set in filter ${f[1]} (${f[0]})`));
|
||||
}
|
||||
});
|
||||
filters = [...filters, ...dynamic_filters];
|
||||
} else {
|
||||
for (let key of Object.keys(dynamic_filters)) {
|
||||
try {
|
||||
const val = eval(dynamic_filters[key]);
|
||||
dynamic_filters[key] = val;
|
||||
} catch (e) {
|
||||
frappe.throw(__(`Invalid expression set in filter ${key}`));
|
||||
}
|
||||
}
|
||||
Object.assign(filters, dynamic_filters);
|
||||
}
|
||||
|
||||
return filters;
|
||||
}
|
||||
|
||||
update_default_date_filters(report_filters, chart_filters) {
|
||||
report_filters.map(f => {
|
||||
if (['Date', 'DateRange'].includes(f.fieldtype) && f.default) {
|
||||
|
|
|
|||
|
|
@ -143,6 +143,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.frappe-rtl {
|
||||
.desk-body {
|
||||
padding-left: 0px;
|
||||
padding-right: calc(20rem + 15px);
|
||||
}
|
||||
}
|
||||
|
||||
.widget-group {
|
||||
margin-bottom: 25px;
|
||||
// -webkit-animation-name: slideInUp;
|
||||
|
|
|
|||
|
|
@ -9,10 +9,6 @@
|
|||
font-family: inherit;
|
||||
}
|
||||
|
||||
.ql-editor {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.ql-editor {
|
||||
font-family: @font-stack;
|
||||
line-height: 1.6;
|
||||
|
|
|
|||
|
|
@ -172,13 +172,6 @@ def generate_csrf_token():
|
|||
frappe.local.session.data.csrf_token = frappe.generate_hash()
|
||||
frappe.local.session_obj.update(force=True)
|
||||
|
||||
# send sid and csrf token to the user
|
||||
# handles the case when a user logs in again from another tab
|
||||
# and it leads to invalid request in the current tab
|
||||
frappe.publish_realtime(event="csrf_generated",
|
||||
message={"sid": frappe.local.session.sid, "csrf_token": frappe.local.session.data.csrf_token},
|
||||
user=frappe.session.user, after_commit=True)
|
||||
|
||||
class Session:
|
||||
def __init__(self, user, resume=False, full_name=None, user_type=None):
|
||||
self.sid = cstr(frappe.form_dict.get('sid') or
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
{%- set res = frappe.utils.get_thumbnail_base64_for_image(src) if src else false -%}
|
||||
{%- if res and res['base64'].startswith('data:') -%}
|
||||
<img src="{{ res['base64'] }}" class="image-with-blur {{ resolve_class(class) }}"
|
||||
alt="{{ alt or '' }}" width="{{ res['width'] }}" height="{{ res['height'] }}" data-src="{{ src or '' }}" />
|
||||
data-src="{{ src or '' }}" alt="{{ alt or '' }}"
|
||||
width="{{ res['width'] }}" height="{{ res['height'] }}"
|
||||
style="width: {{ res['width'] }}px; height: {{ res['height'] }}px;" />
|
||||
{%- else -%}
|
||||
<img src="{{ src or '' }}" class="{{ resolve_class(class) }}" alt="{{ alt or '' }}" />
|
||||
{%- endif -%}
|
||||
|
|
|
|||
|
|
@ -167,6 +167,9 @@ class TestReportview(unittest.TestCase):
|
|||
self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
|
||||
fields=["name", "1' UNION SELECT * FROM __Auth --"],limit_start=0, limit_page_length=1)
|
||||
|
||||
self.assertRaises(frappe.DataError, DatabaseQuery("DocType").execute,
|
||||
fields=["@@version"], limit_start=0, limit_page_length=1)
|
||||
|
||||
data = DatabaseQuery("DocType").execute(fields=["count(`name`) as count"],
|
||||
limit_start=0, limit_page_length=1)
|
||||
self.assertTrue('count' in data[0])
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ from __future__ import unicode_literals, print_function
|
|||
|
||||
from six.moves import input
|
||||
|
||||
import frappe, os, re
|
||||
import frappe, os, re, git
|
||||
from frappe.utils import touch_file, cstr
|
||||
|
||||
def make_boilerplate(dest, app_name):
|
||||
|
|
@ -98,7 +98,13 @@ def make_boilerplate(dest, app_name):
|
|||
with open(os.path.join(dest, hooks.app_name, hooks.app_name, "config", "docs.py"), "w") as f:
|
||||
f.write(frappe.as_unicode(docs_template.format(**hooks)))
|
||||
|
||||
print("'{app}' created at {path}".format(app=app_name, path=os.path.join(dest, app_name)))
|
||||
# initialize git repository
|
||||
app_directory = os.path.join(dest, hooks.app_name)
|
||||
app_repo = git.Repo.init(app_directory)
|
||||
app_repo.git.add(A=True)
|
||||
app_repo.index.commit("feat: Initialize App")
|
||||
|
||||
print("'{app}' created at {path}".format(app=app_name, path=app_directory))
|
||||
|
||||
|
||||
manifest_template = """include MANIFEST.in
|
||||
|
|
|
|||
|
|
@ -5,7 +5,9 @@ import frappe
|
|||
from frappe import _
|
||||
from functools import wraps
|
||||
from frappe.utils import add_to_date, cint, get_link_to_form
|
||||
from frappe.modules.import_file import import_doc
|
||||
from frappe.modules.import_file import import_file_by_path
|
||||
import os
|
||||
from os.path import join
|
||||
|
||||
|
||||
def cache_source(function):
|
||||
|
|
@ -74,6 +76,26 @@ def get_from_date_from_timespan(to_date, timespan):
|
|||
return add_to_date(to_date, years=years, months=months, days=days,
|
||||
as_datetime=True)
|
||||
|
||||
def get_dashboards_with_link(docname, doctype):
|
||||
dashboards = []
|
||||
links = []
|
||||
|
||||
if doctype == 'Dashboard Chart':
|
||||
links = frappe.get_all('Dashboard Chart Link',
|
||||
fields = ['parent'],
|
||||
filters = {
|
||||
'chart': docname
|
||||
})
|
||||
elif doctype == 'Number Card':
|
||||
links = frappe.get_all('Number Card Link',
|
||||
fields = ['parent'],
|
||||
filters = {
|
||||
'card': docname
|
||||
})
|
||||
|
||||
dashboards = [link.parent for link in links]
|
||||
return dashboards
|
||||
|
||||
def sync_dashboards(app=None):
|
||||
"""Import, overwrite fixtures from `[app]/fixtures`"""
|
||||
if not cint(frappe.db.get_single_value('System Settings', 'setup_complete')):
|
||||
|
|
@ -86,39 +108,23 @@ def sync_dashboards(app=None):
|
|||
for app_name in apps:
|
||||
print("Updating Dashboard for {app}".format(app=app_name))
|
||||
for module_name in frappe.local.app_modules.get(app_name) or []:
|
||||
config = get_config(app_name, module_name)
|
||||
if config:
|
||||
frappe.flags.in_import = True
|
||||
try:
|
||||
make_records(config.charts, "Dashboard Chart")
|
||||
make_records(config.number_cards, "Number Card")
|
||||
make_records(config.dashboards, "Dashboard")
|
||||
except Exception as e:
|
||||
frappe.log_error(e, _("Dashboard Import Error"))
|
||||
finally:
|
||||
frappe.flags.in_import = False
|
||||
frappe.flags.in_import = True
|
||||
make_records_in_module(app_name, module_name)
|
||||
frappe.flags.in_import = False
|
||||
|
||||
def make_records(config, doctype):
|
||||
if not config:
|
||||
return
|
||||
def make_records_in_module(app, module):
|
||||
dashboards_path = frappe.get_module_path(module, "{module}_dashboard".format(module=module))
|
||||
charts_path = frappe.get_module_path(module, "dashboard chart")
|
||||
cards_path = frappe.get_module_path(module, "number card")
|
||||
|
||||
try:
|
||||
for item in config:
|
||||
item["doctype"] = doctype
|
||||
import_doc(item)
|
||||
frappe.db.commit()
|
||||
except frappe.DuplicateEntryError:
|
||||
pass
|
||||
paths = [dashboards_path, charts_path, cards_path]
|
||||
for path in paths:
|
||||
make_records(path)
|
||||
|
||||
def get_config(app, module):
|
||||
try:
|
||||
module_dashboards = frappe.get_module('{app}.{module}.dashboard_fixtures'.format(app=app, module=module))
|
||||
if hasattr(module_dashboards, 'get_data'):
|
||||
return frappe._dict(module_dashboards.get_data())
|
||||
return None
|
||||
except ImportError:
|
||||
return None
|
||||
except Exception as e:
|
||||
print(_("Failed to import dashboard fixtures for module {module}").format(module=module))
|
||||
frappe.log_error(e, _("Dashboard Fixture Import Error"))
|
||||
return None
|
||||
def make_records(path, filters=None):
|
||||
if os.path.isdir(path):
|
||||
for fname in os.listdir(path):
|
||||
if os.path.isdir(join(path, fname)):
|
||||
if fname == '__pycache__':
|
||||
continue
|
||||
import_file_by_path("{path}/{fname}/{fname}.json".format(path=path, fname=fname))
|
||||
|
|
|
|||
|
|
@ -137,7 +137,7 @@ def read_options_from_html(html):
|
|||
except:
|
||||
pass
|
||||
|
||||
return soup.prettify(), options
|
||||
return str(soup), options
|
||||
|
||||
|
||||
def prepare_header_footer(soup):
|
||||
|
|
|
|||
|
|
@ -183,10 +183,6 @@ $.extend(frappe, {
|
|||
.html('<div class="content"><i class="'+icon+' text-muted"></i><br>'
|
||||
+text+'</div>').appendTo(document.body);
|
||||
},
|
||||
get_sid: function() {
|
||||
var sid = frappe.get_cookie("sid");
|
||||
return sid && sid !== "Guest";
|
||||
},
|
||||
send_message: function(opts, btn) {
|
||||
return frappe.call({
|
||||
type: "POST",
|
||||
|
|
@ -212,8 +208,7 @@ $.extend(frappe, {
|
|||
});
|
||||
},
|
||||
render_user: function() {
|
||||
var sid = frappe.get_cookie("sid");
|
||||
if(sid && sid!=="Guest") {
|
||||
if (frappe.is_user_logged_in()) {
|
||||
$(".btn-login-area").toggle(false);
|
||||
$(".logged-in").toggle(true);
|
||||
$(".full-name").html(frappe.get_cookie("full_name"));
|
||||
|
|
@ -323,7 +318,7 @@ $.extend(frappe, {
|
|||
return $(".navbar .search, .sidebar .search");
|
||||
},
|
||||
is_user_logged_in: function() {
|
||||
return frappe.get_cookie("sid") && frappe.get_cookie("sid") !== "Guest";
|
||||
return frappe.get_cookie("user_id") !== "Guest" && frappe.session.user !== "Guest";
|
||||
},
|
||||
add_switch_to_desk: function() {
|
||||
$('.switch-to-desk').removeClass('hidden');
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/website",
|
||||
"idx": 0,
|
||||
"is_complete": 0,
|
||||
"modified": "2020-07-04 13:51:57.535269",
|
||||
"modified": "2020-07-08 14:06:24.785922",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Website",
|
||||
"name": "Website",
|
||||
|
|
@ -33,7 +33,6 @@
|
|||
}
|
||||
],
|
||||
"subtitle": "Blogs, Website View Tracking, and more.",
|
||||
"success_message": "Yayy! Your website is all set up!",
|
||||
"title": "Let's Set Up Your Website",
|
||||
"user_can_dismiss": 1
|
||||
"success_message": "Your website is all set up!",
|
||||
"title": "Let's Set Up Your Website."
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,6 @@ semantic-version==2.8.4
|
|||
six==1.14.0
|
||||
sqlparse==0.2.4
|
||||
stripe==2.40.0
|
||||
tenacity==6.2.0
|
||||
terminaltables==3.1.0
|
||||
unittest-xml-reporting==2.5.2
|
||||
urllib3==1.25.8
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue