diff --git a/frappe/core/page/modules_setup/includes/module_icons.html b/frappe/core/page/modules_setup/includes/module_icons.html
index fd57ef32b3..12a23feecc 100644
--- a/frappe/core/page/modules_setup/includes/module_icons.html
+++ b/frappe/core/page/modules_setup/includes/module_icons.html
@@ -3,7 +3,7 @@
{{ _("Global Settings: Users will only be able to choose checked icons") }}
diff --git a/frappe/core/page/modules_setup/modules_setup.js b/frappe/core/page/modules_setup/modules_setup.js
index 1f7cb358a1..e9952c1a18 100644
--- a/frappe/core/page/modules_setup/modules_setup.js
+++ b/frappe/core/page/modules_setup/modules_setup.js
@@ -37,7 +37,7 @@ frappe.pages['modules_setup'].on_page_load = function(wrapper) {
// save action
page.set_primary_action('Save', function() {
var hidden_list = [];
- page.wrapper.find('input[type="checkbox"]').each(function() {
+ page.wrapper.find('input.module-select').each(function() {
if(!$(this).is(":checked")) {
hidden_list.push($(this).attr('data-module'));
}
@@ -64,6 +64,11 @@ frappe.pages['modules_setup'].on_page_load = function(wrapper) {
frappe.set_route('applications');
});
}
+
+ // setup select all
+ $('.check-all').on('click', function() {
+ $(wrapper).find('input.module-select').prop('checked', $(this).prop('checked'));
+ });
}
frappe.pages['modules_setup'].on_page_show = function(wrapper) {
diff --git a/frappe/core/page/modules_setup/modules_setup.json b/frappe/core/page/modules_setup/modules_setup.json
index fbd6eed143..1aea188463 100644
--- a/frappe/core/page/modules_setup/modules_setup.json
+++ b/frappe/core/page/modules_setup/modules_setup.json
@@ -3,7 +3,7 @@
"creation": "2012-10-04 18:45:29",
"docstatus": 0,
"doctype": "Page",
- "icon": "icon-cog",
+ "icon": "fa fa-cog",
"idx": 1,
"modified": "2016-02-26 00:21:05.501007",
"modified_by": "Administrator",
diff --git a/frappe/core/page/modules_setup/modules_setup.py b/frappe/core/page/modules_setup/modules_setup.py
index 10b41f9e90..a913824a82 100644
--- a/frappe/core/page/modules_setup/modules_setup.py
+++ b/frappe/core/page/modules_setup/modules_setup.py
@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe.desk.doctype.desktop_icon.desktop_icon import set_hidden_list, get_desktop_icons
from frappe.utils.user import UserPermissions
+from frappe import _
@frappe.whitelist()
def update(hidden_list, user=None):
@@ -13,7 +14,7 @@ def update(hidden_list, user=None):
frappe.only_for('System Manager')
set_hidden_list(hidden_list, user)
- frappe.msgprint(frappe._('Updated'))
+ frappe.msgprint(frappe._('Updated'), indicator='green', title=_('Success'), alert=True)
def get_context(context):
context.icons = get_user_icons(frappe.session.user)
diff --git a/frappe/core/page/permission_manager/permission_manager.js b/frappe/core/page/permission_manager/permission_manager.js
index bb5fddd5be..7d13d3a3a1 100644
--- a/frappe/core/page/permission_manager/permission_manager.js
+++ b/frappe/core/page/permission_manager/permission_manager.js
@@ -2,7 +2,7 @@ frappe.pages['permission-manager'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: __('Role Permissions Manager'),
- icon: "icon-lock",
+ icon: "fa fa-lock",
single_column: true
});
@@ -305,7 +305,7 @@ frappe.PermissionEngine = Class.extend({
},
add_delete_button: function(row, d) {
var me = this;
- $("
")
+ $("
")
.appendTo($("
").appendTo(row))
.attr("data-name", d.name)
.attr("data-doctype", d.parent)
@@ -362,7 +362,7 @@ frappe.PermissionEngine = Class.extend({
},
show_add_rule: function() {
var me = this;
- $(" "
+ $(" "
+__("Add A New Rule")+" ")
.appendTo($("").appendTo(this.body))
.click(function() {
@@ -491,7 +491,7 @@ frappe.PermissionEngine = Class.extend({
make_reset_button: function() {
var me = this;
$('\
- ' + __("Restore Original Permissions") + ' ')
+ ' + __("Restore Original Permissions") + '
')
.appendTo(this.body.find(".permission-toolbar"))
.on("click", function() {
me.get_standard_permissions(function(data) {
diff --git a/frappe/core/page/permission_manager/permission_manager.json b/frappe/core/page/permission_manager/permission_manager.json
index 9eb5b3a8d6..0af33ca03c 100644
--- a/frappe/core/page/permission_manager/permission_manager.json
+++ b/frappe/core/page/permission_manager/permission_manager.json
@@ -2,7 +2,7 @@
"creation": "2013-01-01 11:00:01.000000",
"docstatus": 0,
"doctype": "Page",
- "icon": "icon-lock",
+ "icon": "fa fa-lock",
"idx": 1,
"modified": "2013-07-11 14:43:43.000000",
"modified_by": "Administrator",
diff --git a/frappe/core/page/user_permissions/user_permissions.js b/frappe/core/page/user_permissions/user_permissions.js
index 56d5a07e2c..f3d1321f0a 100644
--- a/frappe/core/page/user_permissions/user_permissions.js
+++ b/frappe/core/page/user_permissions/user_permissions.js
@@ -2,7 +2,7 @@ frappe.pages['user-permissions'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
title: __("User Permissions Manager"),
- icon: "icon-shield",
+ icon: "fa fa-shield",
single_column: true
});
@@ -87,14 +87,14 @@ frappe.UserPermissions = Class.extend({
fieldname: "download",
label: __("Download"),
fieldtype: "Button",
- icon: "icon-download"
+ icon: "fa fa-download"
});
me.upload = me.wrapper.page.add_field({
fieldname: "upload",
label: __("Upload"),
fieldtype: "Button",
- icon: "icon-upload"
+ icon: "fa fa-upload"
});
}
@@ -269,7 +269,7 @@ frappe.UserPermissions = Class.extend({
},
add_delete_button: function(row, d) {
var me = this;
- $(" ")
+ $(" ")
.appendTo($(" ").appendTo(row))
.attr("data-name", d.name)
.attr("data-user", d.parent)
diff --git a/frappe/core/page/user_permissions/user_permissions.json b/frappe/core/page/user_permissions/user_permissions.json
index f4e5d95ae5..ab831e1919 100644
--- a/frappe/core/page/user_permissions/user_permissions.json
+++ b/frappe/core/page/user_permissions/user_permissions.json
@@ -3,7 +3,7 @@
"creation": "2013-01-01 18:50:55",
"docstatus": 0,
"doctype": "Page",
- "icon": "icon-user",
+ "icon": "fa fa-user",
"idx": 1,
"modified": "2014-05-28 16:53:43.103533",
"modified_by": "Administrator",
diff --git a/frappe/custom/doctype/custom_field/custom_field.json b/frappe/custom/doctype/custom_field/custom_field.json
index 65ef909133..af7d5a93d9 100644
--- a/frappe/custom/doctype/custom_field/custom_field.json
+++ b/frappe/custom/doctype/custom_field/custom_field.json
@@ -9,11 +9,13 @@
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
+ "editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
+ "columns": 0,
"fieldname": "dt",
"fieldtype": "Link",
"hidden": 0,
@@ -21,6 +23,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Document",
"length": 0,
"no_copy": 0,
@@ -31,6 +34,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
@@ -41,6 +45,7 @@
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
+ "columns": 0,
"fieldname": "label",
"fieldtype": "Data",
"hidden": 0,
@@ -48,6 +53,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Label",
"length": 0,
"no_copy": 1,
@@ -57,6 +63,7 @@
"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,
@@ -67,6 +74,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "label_help",
"fieldtype": "HTML",
"hidden": 0,
@@ -74,6 +82,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Label Help",
"length": 0,
"no_copy": 0,
@@ -82,6 +91,7 @@
"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,
@@ -92,6 +102,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "fieldname",
"fieldtype": "Data",
"hidden": 0,
@@ -99,6 +110,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Fieldname",
"length": 0,
"no_copy": 1,
@@ -108,6 +120,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -118,6 +131,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "",
"description": "Select the label after which you want to insert new field.",
"fieldname": "insert_after",
@@ -127,6 +141,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Insert After",
"length": 0,
"no_copy": 1,
@@ -136,6 +151,7 @@
"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,
@@ -146,6 +162,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_6",
"fieldtype": "Column Break",
"hidden": 0,
@@ -153,6 +170,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -160,6 +178,7 @@
"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,
@@ -170,6 +189,7 @@
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
+ "columns": 0,
"default": "Data",
"fieldname": "fieldtype",
"fieldtype": "Select",
@@ -178,6 +198,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Field Type",
"length": 0,
"no_copy": 0,
@@ -188,6 +209,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -198,6 +220,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:in_list([\"Float\", \"Currency\", \"Percent\"], doc.fieldtype)",
"description": "Set non-standard precision for a Float or Currency field",
"fieldname": "precision",
@@ -207,6 +230,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Precision",
"length": 0,
"no_copy": 0,
@@ -216,6 +240,7 @@
"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,
@@ -226,6 +251,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "options",
"fieldtype": "Text",
"hidden": 0,
@@ -233,6 +259,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Options",
"length": 0,
"no_copy": 0,
@@ -242,6 +269,7 @@
"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,
@@ -252,6 +280,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "options_help",
"fieldtype": "HTML",
"hidden": 0,
@@ -259,6 +288,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Options Help",
"length": 0,
"no_copy": 0,
@@ -267,6 +297,7 @@
"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,
@@ -277,6 +308,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break_11",
"fieldtype": "Section Break",
"hidden": 0,
@@ -284,6 +316,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -291,6 +324,7 @@
"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,
@@ -301,6 +335,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
"fieldname": "collapsible",
"fieldtype": "Check",
@@ -309,6 +344,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Collapsible",
"length": 0,
"no_copy": 0,
@@ -317,6 +353,7 @@
"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,
@@ -327,6 +364,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.fieldtype==\"Section Break\"",
"fieldname": "collapsible_depends_on",
"fieldtype": "Code",
@@ -335,6 +373,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Collapsible Depends On",
"length": 0,
"no_copy": 0,
@@ -343,6 +382,7 @@
"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,
@@ -353,6 +393,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "default",
"fieldtype": "Text",
"hidden": 0,
@@ -360,6 +401,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Default Value",
"length": 0,
"no_copy": 0,
@@ -369,6 +411,7 @@
"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,
@@ -379,6 +422,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "depends_on",
"fieldtype": "Code",
"hidden": 0,
@@ -386,6 +430,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Depends On",
"length": 255,
"no_copy": 0,
@@ -393,6 +438,7 @@
"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,
@@ -403,6 +449,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "description",
"fieldtype": "Text",
"hidden": 0,
@@ -410,6 +457,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Field Description",
"length": 0,
"no_copy": 0,
@@ -420,6 +468,7 @@
"print_hide_if_no_value": 0,
"print_width": "300px",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -431,6 +480,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "0",
"fieldname": "permlevel",
"fieldtype": "Int",
@@ -439,6 +489,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Permission Level",
"length": 0,
"no_copy": 0,
@@ -448,6 +499,7 @@
"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,
@@ -458,6 +510,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "width",
"fieldtype": "Data",
"hidden": 0,
@@ -465,6 +518,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Width",
"length": 0,
"no_copy": 0,
@@ -474,6 +528,7 @@
"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,
@@ -484,6 +539,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "properties",
"fieldtype": "Column Break",
"hidden": 0,
@@ -491,6 +547,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -500,6 +557,7 @@
"print_hide_if_no_value": 0,
"print_width": "50%",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -511,6 +569,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "reqd",
"fieldtype": "Check",
"hidden": 0,
@@ -518,6 +577,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Is Mandatory Field",
"length": 0,
"no_copy": 0,
@@ -527,6 +587,7 @@
"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,
@@ -537,6 +598,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "unique",
"fieldtype": "Check",
"hidden": 0,
@@ -544,6 +606,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Unique",
"length": 0,
"no_copy": 0,
@@ -552,6 +615,7 @@
"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,
@@ -562,6 +626,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "read_only",
"fieldtype": "Check",
"hidden": 0,
@@ -569,6 +634,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Read Only",
"length": 0,
"no_copy": 0,
@@ -576,6 +642,7 @@
"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,
@@ -586,6 +653,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.fieldtype===\"Link\"",
"fieldname": "ignore_user_permissions",
"fieldtype": "Check",
@@ -594,6 +662,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Ignore User Permissions",
"length": 0,
"no_copy": 0,
@@ -601,6 +670,7 @@
"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,
@@ -611,6 +681,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "hidden",
"fieldtype": "Check",
"hidden": 0,
@@ -618,6 +689,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Hidden",
"length": 0,
"no_copy": 0,
@@ -625,6 +697,7 @@
"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,
@@ -635,6 +708,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "print_hide",
"fieldtype": "Check",
"hidden": 0,
@@ -642,6 +716,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Hide",
"length": 0,
"no_copy": 0,
@@ -651,6 +726,7 @@
"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,
@@ -661,6 +737,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:[\"Int\", \"Float\", \"Currency\", \"Percent\"].indexOf(doc.fieldtype)!==-1",
"fieldname": "print_hide_if_no_value",
"fieldtype": "Check",
@@ -669,6 +746,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Hide If No Value",
"length": 0,
"no_copy": 0,
@@ -677,6 +755,7 @@
"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,
@@ -687,6 +766,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "print_width",
"fieldtype": "Data",
"hidden": 1,
@@ -694,6 +774,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Width",
"length": 0,
"no_copy": 1,
@@ -701,6 +782,7 @@
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -711,6 +793,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "no_copy",
"fieldtype": "Check",
"hidden": 0,
@@ -718,6 +801,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "No Copy",
"length": 0,
"no_copy": 0,
@@ -727,6 +811,7 @@
"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,
@@ -737,6 +822,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "allow_on_submit",
"fieldtype": "Check",
"hidden": 0,
@@ -744,6 +830,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Allow on Submit",
"length": 0,
"no_copy": 0,
@@ -753,6 +840,7 @@
"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,
@@ -763,6 +851,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "in_filter",
"fieldtype": "Check",
"hidden": 0,
@@ -770,6 +859,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "In Report Filter",
"length": 0,
"no_copy": 0,
@@ -779,6 +869,7 @@
"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,
@@ -789,6 +880,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "in_list_view",
"fieldtype": "Check",
"hidden": 0,
@@ -796,6 +888,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "In List View",
"length": 0,
"no_copy": 0,
@@ -803,6 +896,7 @@
"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,
@@ -813,6 +907,35 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
+ "fieldname": "in_filter_dash",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "In Standard Filter",
+ "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,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "report_hide",
"fieldtype": "Check",
"hidden": 0,
@@ -820,6 +943,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Report Hide",
"length": 0,
"no_copy": 0,
@@ -829,6 +953,7 @@
"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,
@@ -839,6 +964,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "search_index",
"fieldtype": "Check",
"hidden": 1,
@@ -846,6 +972,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Index",
"length": 0,
"no_copy": 1,
@@ -853,6 +980,7 @@
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -863,6 +991,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"description": "Don't HTML Encode HTML tags like <script> or just characters like < or >, as they could be intentionally used in this field",
"fieldname": "ignore_xss_filter",
"fieldtype": "Check",
@@ -871,6 +1000,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Ignore XSS Filter",
"length": 0,
"no_copy": 0,
@@ -879,6 +1009,7 @@
"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,
@@ -888,15 +1019,16 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-glass",
+ "icon": "fa fa-glass",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-05-14 09:22:58.789603",
+ "modified": "2016-11-07 05:23:16.370928",
"modified_by": "Administrator",
"module": "Custom",
"name": "Custom Field",
@@ -912,6 +1044,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -932,6 +1065,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
diff --git a/frappe/custom/doctype/custom_script/custom_script.json b/frappe/custom/doctype/custom_script/custom_script.json
index 4d16f3d81d..5346f15ae2 100644
--- a/frappe/custom/doctype/custom_script/custom_script.json
+++ b/frappe/custom/doctype/custom_script/custom_script.json
@@ -11,6 +11,7 @@
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
@@ -24,6 +25,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "DocType",
"length": 0,
"no_copy": 0,
@@ -34,6 +36,7 @@
"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,
@@ -53,6 +56,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Script Type",
"length": 0,
"no_copy": 0,
@@ -63,6 +67,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -81,6 +86,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Script",
"length": 0,
"no_copy": 0,
@@ -91,6 +97,7 @@
"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,
@@ -109,6 +116,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Sample",
"length": 0,
"no_copy": 0,
@@ -117,6 +125,7 @@
"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,
@@ -126,7 +135,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-glass",
+ "icon": "fa fa-glass",
"idx": 1,
"image_view": 0,
"in_create": 0,
@@ -135,7 +144,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-09-24 05:47:53.900418",
+ "modified": "2016-11-07 05:24:05.590423",
"modified_by": "Administrator",
"module": "Custom",
"name": "Custom Script",
@@ -151,6 +160,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -171,6 +181,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
diff --git a/frappe/custom/doctype/customize_form/customize_form.js b/frappe/custom/doctype/customize_form/customize_form.js
index 8ad40cf11c..91a323ade2 100644
--- a/frappe/custom/doctype/customize_form/customize_form.js
+++ b/frappe/custom/doctype/customize_form/customize_form.js
@@ -70,11 +70,11 @@ frappe.ui.form.on("Customize Form", {
frm.add_custom_button(__('Refresh Form'), function() {
frm.script_manager.trigger("doc_type");
- }, "icon-refresh", "btn-default");
+ }, "fa fa-refresh", "btn-default");
frm.add_custom_button(__('Reset to defaults'), function() {
frappe.customize_form.confirm(__('Remove all customizations?'), frm);
- }, "icon-eraser", "btn-default");
+ }, "fa fa-eraser", "btn-default");
if(frappe.boot.developer_mode) {
frm.add_custom_button(__('Export Customizations'), function() {
diff --git a/frappe/custom/doctype/customize_form/customize_form.json b/frappe/custom/doctype/customize_form/customize_form.json
index dd3e2ab791..f562b7b62f 100644
--- a/frappe/custom/doctype/customize_form/customize_form.json
+++ b/frappe/custom/doctype/customize_form/customize_form.json
@@ -536,7 +536,7 @@
],
"hide_heading": 0,
"hide_toolbar": 1,
- "icon": "icon-glass",
+ "icon": "fa fa-glass",
"idx": 1,
"image_view": 0,
"in_create": 0,
diff --git a/frappe/custom/doctype/customize_form/customize_form.py b/frappe/custom/doctype/customize_form/customize_form.py
index 3d8f80e204..39c0d226b9 100644
--- a/frappe/custom/doctype/customize_form/customize_form.py
+++ b/frappe/custom/doctype/customize_form/customize_form.py
@@ -43,6 +43,7 @@ docfield_properties = {
'ignore_user_permissions': 'Check',
'in_filter': 'Check',
'in_list_view': 'Check',
+ 'in_standard_filter': 'Check',
'hidden': 'Check',
'collapsible': 'Check',
'collapsible_depends_on': 'Data',
diff --git a/frappe/custom/doctype/customize_form_field/customize_form_field.json b/frappe/custom/doctype/customize_form_field/customize_form_field.json
index c3cee78931..f123cd1dd7 100644
--- a/frappe/custom/doctype/customize_form_field/customize_form_field.json
+++ b/frappe/custom/doctype/customize_form_field/customize_form_field.json
@@ -23,6 +23,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Label and Type",
"length": 0,
"no_copy": 0,
@@ -50,6 +51,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Label",
"length": 0,
"no_copy": 0,
@@ -79,6 +81,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Type",
"length": 0,
"no_copy": 0,
@@ -108,6 +111,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Name",
"length": 0,
"no_copy": 0,
@@ -136,6 +140,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Mandatory",
"length": 0,
"no_copy": 0,
@@ -166,6 +171,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Unique",
"length": 0,
"no_copy": 0,
@@ -193,6 +199,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "In List View",
"length": 0,
"no_copy": 0,
@@ -207,6 +214,33 @@
"set_only_once": 0,
"unique": 0
},
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "in_standard_filter",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "In Standard Filter",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -219,6 +253,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -247,6 +282,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Precision",
"length": 0,
"no_copy": 0,
@@ -276,6 +312,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Length",
"length": 0,
"no_copy": 0,
@@ -304,6 +341,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Options",
"length": 0,
"no_copy": 0,
@@ -332,6 +370,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Permissions",
"length": 0,
"no_copy": 0,
@@ -360,6 +399,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Depends On",
"length": 0,
"no_copy": 0,
@@ -389,6 +429,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Perm Level",
"length": 0,
"no_copy": 0,
@@ -417,6 +458,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Hidden",
"length": 0,
"no_copy": 0,
@@ -447,6 +489,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Read Only",
"length": 0,
"no_copy": 0,
@@ -475,6 +518,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Collapsible",
"length": 0,
"no_copy": 0,
@@ -503,6 +547,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Collapsible Depends On",
"length": 0,
"no_copy": 0,
@@ -530,6 +575,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -556,6 +602,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Ignore User Permissions",
"length": 0,
"no_copy": 0,
@@ -582,6 +629,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Allow on Submit",
"length": 0,
"no_copy": 0,
@@ -610,6 +658,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Report Hide",
"length": 0,
"no_copy": 0,
@@ -665,6 +714,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Display",
"length": 0,
"no_copy": 0,
@@ -692,6 +742,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Default",
"length": 0,
"no_copy": 0,
@@ -720,6 +771,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "In Filter",
"length": 0,
"no_copy": 0,
@@ -750,6 +802,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -776,6 +829,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
@@ -806,6 +860,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Hide",
"length": 0,
"no_copy": 0,
@@ -835,6 +890,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Hide If No Value",
"length": 0,
"no_copy": 0,
@@ -863,6 +919,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Width",
"length": 0,
"no_copy": 0,
@@ -893,6 +950,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Columns",
"length": 0,
"no_copy": 0,
@@ -919,6 +977,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Width",
"length": 0,
"no_copy": 0,
@@ -949,6 +1008,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Is Custom Field",
"length": 0,
"no_copy": 0,
diff --git a/frappe/custom/doctype/property_setter/property_setter.json b/frappe/custom/doctype/property_setter/property_setter.json
index 4970d25abb..dbe52a2bdb 100644
--- a/frappe/custom/doctype/property_setter/property_setter.json
+++ b/frappe/custom/doctype/property_setter/property_setter.json
@@ -2,29 +2,38 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2013-01-10 16:34:04",
"custom": 0,
"description": "Property Setter overrides a standard DocType or Field property",
"docstatus": 0,
"doctype": "DocType",
+ "document_type": "Setup",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "help",
"fieldtype": "HTML",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Help",
"length": 0,
"no_copy": 0,
"options": "Please don't update it as it can mess up your form. Use the Customize Form View and Custom Fields to set properties!
",
"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,
@@ -35,17 +44,22 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "sb0",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -56,20 +70,25 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.__islocal",
"fieldname": "doctype_or_field",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "DocType or Field",
"length": 0,
"no_copy": 0,
"options": "\nDocField\nDocType",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -80,19 +99,24 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"description": "New value to be set",
"fieldname": "value",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Set Value",
"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,
@@ -103,17 +127,22 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -124,19 +153,24 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "doc_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 1,
"label": "DocType",
"length": 0,
"no_copy": 0,
"options": "DocType",
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
@@ -147,20 +181,25 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.doctype_or_field=='DocField'",
"description": "ID (name) of the entity whose property is to be set",
"fieldname": "field_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 1,
"label": "Field Name",
"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": 1,
@@ -171,18 +210,23 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "property",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 1,
"label": "Property",
"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": 1,
"search_index": 1,
@@ -193,18 +237,23 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "property_type",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Property Type",
"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,
@@ -215,18 +264,23 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "default_value",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Default Value",
"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,
@@ -236,15 +290,16 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-glass",
+ "icon": "fa fa-glass",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2015-11-16 06:29:52.878049",
+ "modified": "2016-12-15 14:55:45.761422",
"modified_by": "Administrator",
"module": "Custom",
"name": "Property Setter",
@@ -260,6 +315,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -280,6 +336,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -291,7 +348,10 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
- "search_fields": "doc_type,property"
+ "search_fields": "doc_type,property",
+ "sort_order": "DESC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/frappe/data/Framework.sql b/frappe/data/Framework.sql
index 61aec9a4ea..d1d089d359 100644
--- a/frappe/data/Framework.sql
+++ b/frappe/data/Framework.sql
@@ -47,6 +47,7 @@ CREATE TABLE `tabDocField` (
`description` text,
`in_filter` int(1) NOT NULL DEFAULT 0,
`in_list_view` int(1) NOT NULL DEFAULT 0,
+ `in_standard_filter` int(1) NOT NULL DEFAULT 0,
`read_only` int(1) NOT NULL DEFAULT 0,
`precision` varchar(255) DEFAULT NULL,
`length` int(11) NOT NULL DEFAULT 0,
diff --git a/frappe/data/app_listing/knowledge_base.json b/frappe/data/app_listing/knowledge_base.json
index 6da4205267..b93c0238d4 100644
--- a/frappe/data/app_listing/knowledge_base.json
+++ b/frappe/data/app_listing/knowledge_base.json
@@ -1,7 +1,7 @@
{
"app_url": "https://github.com/frappe/knowledge_base",
"app_name": "knowledge_base",
- "app_icon": "icon-question-sign",
+ "app_icon": "fa fa-question-sign",
"app_color": "#4cd964",
"app_description": "Publish help articles by category on the portal",
"app_publisher": "Frappe",
diff --git a/frappe/data/app_listing/poll.json b/frappe/data/app_listing/poll.json
index e2e37a2c95..2e16376f34 100644
--- a/frappe/data/app_listing/poll.json
+++ b/frappe/data/app_listing/poll.json
@@ -1,7 +1,7 @@
{
"app_url": "https://github.com/frappe/poll",
"app_name": "poll",
- "app_icon": "icon-ok-sign",
+ "app_icon": "fa fa-ok-sign",
"app_color": "#4cd964",
"app_description": "Online poll for the website",
"app_publisher": "Frappe",
diff --git a/frappe/database.py b/frappe/database.py
index 474038377e..0a0bb766f5 100644
--- a/frappe/database.py
+++ b/frappe/database.py
@@ -382,7 +382,7 @@ class Database:
return self.get_value(doctype, filters, "*", as_dict=as_dict, cache=cache)
def get_value(self, doctype, filters=None, fieldname="name", ignore=None, as_dict=False,
- debug=False, cache=False):
+ debug=False, order_by=None, cache=False):
"""Returns a document property or list of properties.
:param doctype: DocType name.
@@ -391,6 +391,7 @@ class Database:
:param ignore: Don't raise exception if table, column is missing.
:param as_dict: Return values as dict.
:param debug: Print query in error log.
+ :param order_by: Column to order by
Example:
@@ -407,7 +408,7 @@ class Database:
frappe.db.get_value("System Settings", None, "date_format")
"""
- ret = self.get_values(doctype, filters, fieldname, ignore, as_dict, debug, cache=cache)
+ ret = self.get_values(doctype, filters, fieldname, ignore, as_dict, debug, order_by, cache=cache)
return ((len(ret[0]) > 1 or as_dict) and ret[0] or ret[0][0]) if ret else None
@@ -421,6 +422,7 @@ class Database:
:param ignore: Don't raise exception if table, column is missing.
:param as_dict: Return values as dict.
:param debug: Print query in error log.
+ :param order_by: Column to order by
Example:
@@ -509,7 +511,16 @@ class Database:
return r and [[i[1] for i in r]] or []
def get_singles_dict(self, doctype):
- """Get Single DocType as dict."""
+ """Get Single DocType as dict.
+
+ :param doctype: DocType of the single object whose value is requested
+
+ Example:
+
+ # Get coulmn and value of the single doctype Accounts Settings
+ account_settings = frappe.db.get_singles_dict("Accounts Settings")
+ """
+
return frappe._dict(self.sql("""select field, value from
tabSingles where doctype=%s""", doctype))
@@ -520,7 +531,17 @@ class Database:
return frappe.get_list(*args, **kwargs)
def get_single_value(self, doctype, fieldname, cache=False):
- """Get property of Single DocType. Cache locally by default"""
+ """Get property of Single DocType. Cache locally by default
+
+ :param doctype: DocType of the single object whose value is requested
+ :param fieldname: `fieldname` of the property whose value is requested
+
+ Example:
+
+ # Get the default value of the company from the Global Defaults doctype.
+ company = frappe.db.get_single_value('Global Defaults', 'default_company')
+ """
+
value = self.value_cache.setdefault(doctype, {}).get(fieldname)
if value:
return value
@@ -586,7 +607,7 @@ class Database:
:param dt: DocType name.
:param dn: Document name.
- :param field: Property / field name.
+ :param field: Property / field name or dictionary of values to be updated
:param value: Value to be updated.
:param modified: Use this as the `modified` timestamp.
:param modified_by: Set this user as `modified_by`.
@@ -598,28 +619,40 @@ class Database:
if not modified_by:
modified_by = frappe.session.user
+ to_update = {}
+ if update_modified:
+ to_update = {"modified": modified, "modified_by": modified_by}
+
+ if isinstance(field, dict):
+ to_update.update(field)
+ else:
+ to_update.update({field: val})
+
if dn and dt!=dn:
+ # with table
conditions, values = self.build_conditions(dn)
- values.update({"val": val, "modified": modified, "modified_by": modified_by})
+ values.update(to_update)
- if update_modified:
- self.sql("""update `tab{0}` set `{1}`=%(val)s, modified=%(modified)s, modified_by=%(modified_by)s where
- {2}""".format(dt, field, conditions), values, debug=debug)
- else:
- self.sql("""update `tab{0}` set `{1}`=%(val)s where
- {2}""".format(dt, field, conditions), values, debug=debug)
+ set_values = []
+ for key in to_update:
+ set_values.append('`{0}`=%({0})s'.format(key))
+ self.sql("""update `tab{0}`
+ set {1} where {2}""".format(dt, ', '.join(set_values), conditions),
+ values, debug=debug)
else:
- self.sql("delete from tabSingles where field=%s and doctype=%s", (field, dt))
- self.sql("insert into tabSingles(doctype, field, value) values (%s, %s, %s)",
- (dt, field, val), debug=debug)
-
- if update_modified and (field not in ("modified", "modified_by")):
- self.set_value(dt, dn, "modified", modified)
- self.set_value(dt, dn, "modified_by", modified_by)
-
+ # for singles
+ keys = to_update.keys()
+ self.sql('''
+ delete from tabSingles
+ where field in ({0}) and
+ doctype=%s'''.format(', '.join(['%s']*len(keys))),
+ keys + [dt], debug=debug)
+ for key, value in to_update.iteritems():
+ self.sql('''insert into tabSingles(doctype, field, value) values (%s, %s, %s)''',
+ (dt, key, value), debug=debug)
if dt in self.value_cache:
del self.value_cache[dt]
@@ -679,8 +712,7 @@ class Database:
return frappe.defaults.get_defaults(parent)
def begin(self):
- pass
- #self.sql("start transaction")
+ self.sql("start transaction")
def commit(self):
"""Commit current transaction. Calls SQL `COMMIT`."""
diff --git a/frappe/desk/doctype/desktop_icon/desktop_icon.json b/frappe/desk/doctype/desktop_icon/desktop_icon.json
index f473b8722f..8e18297fd2 100644
--- a/frappe/desk/doctype/desktop_icon/desktop_icon.json
+++ b/frappe/desk/doctype/desktop_icon/desktop_icon.json
@@ -2,16 +2,20 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2016-02-22 03:47:45.387068",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "module_name",
"fieldtype": "Data",
"hidden": 0,
@@ -19,6 +23,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Module Name",
"length": 0,
"no_copy": 0,
@@ -27,6 +32,7 @@
"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,
@@ -37,6 +43,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "label",
"fieldtype": "Data",
"hidden": 0,
@@ -44,6 +51,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Label",
"length": 0,
"no_copy": 0,
@@ -52,6 +60,7 @@
"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,
@@ -62,6 +71,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "standard",
"fieldtype": "Check",
"hidden": 0,
@@ -69,6 +79,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Standard",
"length": 0,
"no_copy": 0,
@@ -77,6 +88,7 @@
"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,
@@ -87,6 +99,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "custom",
"fieldtype": "Check",
"hidden": 0,
@@ -94,6 +107,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Custom",
"length": 0,
"no_copy": 0,
@@ -102,6 +116,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -112,6 +127,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
@@ -119,6 +135,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -126,6 +143,7 @@
"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,
@@ -136,6 +154,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "app",
"fieldtype": "Data",
"hidden": 0,
@@ -143,6 +162,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "App",
"length": 0,
"no_copy": 0,
@@ -151,6 +171,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -161,6 +182,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "hidden",
"fieldtype": "Check",
"hidden": 0,
@@ -168,6 +190,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Hidden",
"length": 0,
"no_copy": 0,
@@ -176,6 +199,7 @@
"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,
@@ -186,6 +210,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "blocked",
"fieldtype": "Check",
"hidden": 0,
@@ -193,6 +218,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Blocked",
"length": 0,
"no_copy": 0,
@@ -201,6 +227,7 @@
"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,
@@ -211,6 +238,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "force_show",
"fieldtype": "Check",
"hidden": 0,
@@ -218,6 +246,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Force Show",
"length": 0,
"no_copy": 0,
@@ -226,6 +255,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -236,6 +266,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break_7",
"fieldtype": "Section Break",
"hidden": 0,
@@ -243,6 +274,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -250,6 +282,7 @@
"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,
@@ -260,6 +293,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "type",
"fieldtype": "Select",
"hidden": 0,
@@ -267,6 +301,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Type",
"length": 0,
"no_copy": 0,
@@ -276,6 +311,7 @@
"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,
@@ -286,6 +322,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "_doctype",
"fieldtype": "Link",
"hidden": 0,
@@ -293,6 +330,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "_doctype",
"length": 0,
"no_copy": 0,
@@ -302,6 +340,7 @@
"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,
@@ -312,6 +351,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "link",
"fieldtype": "Small Text",
"hidden": 0,
@@ -319,6 +359,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Link",
"length": 0,
"no_copy": 0,
@@ -327,6 +368,7 @@
"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,
@@ -337,6 +379,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
@@ -344,6 +387,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -351,6 +395,7 @@
"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,
@@ -361,6 +406,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "color",
"fieldtype": "Data",
"hidden": 0,
@@ -368,6 +414,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Color",
"length": 0,
"no_copy": 0,
@@ -376,6 +423,7 @@
"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,
@@ -386,6 +434,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "icon",
"fieldtype": "Data",
"hidden": 0,
@@ -393,6 +442,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Icon",
"length": 0,
"no_copy": 0,
@@ -401,6 +451,7 @@
"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,
@@ -411,6 +462,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "reverse",
"fieldtype": "Check",
"hidden": 0,
@@ -418,6 +470,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Reverse Icon Color",
"length": 0,
"no_copy": 0,
@@ -426,6 +479,7 @@
"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,
@@ -436,6 +490,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "idx",
"fieldtype": "Int",
"hidden": 0,
@@ -443,6 +498,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Idx",
"length": 0,
"no_copy": 0,
@@ -451,6 +507,7 @@
"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,
@@ -461,13 +518,14 @@
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
+ "image_view": 0,
"in_create": 1,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-04-01 09:50:56.611440",
+ "modified": "2016-11-07 05:31:01.259347",
"modified_by": "Administrator",
"module": "Desk",
"name": "Desktop Icon",
@@ -484,6 +542,7 @@
"export": 1,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -495,9 +554,11 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 1,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
- "title_field": "module_name"
+ "title_field": "module_name",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/frappe/desk/doctype/event/event.json b/frappe/desk/doctype/event/event.json
index c58b463c78..b3b7e455e8 100644
--- a/frappe/desk/doctype/event/event.json
+++ b/frappe/desk/doctype/event/event.json
@@ -3,16 +3,20 @@
"allow_import": 1,
"allow_rename": 0,
"autoname": "EV.#####",
+ "beta": 0,
"creation": "2013-06-10 13:17:47",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "details",
"fieldtype": "Section Break",
"hidden": 0,
@@ -20,6 +24,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -28,6 +33,7 @@
"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,
@@ -38,6 +44,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "subject",
"fieldtype": "Data",
"hidden": 0,
@@ -45,6 +52,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Subject",
"length": 0,
"no_copy": 0,
@@ -52,6 +60,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -62,6 +71,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "event_type",
"fieldtype": "Select",
"hidden": 0,
@@ -69,6 +79,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Event Type",
"length": 0,
"no_copy": 0,
@@ -79,6 +90,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
@@ -89,6 +101,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "1",
"fieldname": "send_reminder",
"fieldtype": "Check",
@@ -97,6 +110,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Send an email reminder in the morning",
"length": 0,
"no_copy": 0,
@@ -104,6 +118,7 @@
"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,
@@ -114,6 +129,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
@@ -121,12 +137,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -137,6 +155,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "starts_on",
"fieldtype": "Datetime",
"hidden": 0,
@@ -144,6 +163,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Starts on",
"length": 0,
"no_copy": 0,
@@ -151,6 +171,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -161,6 +182,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "ends_on",
"fieldtype": "Datetime",
"hidden": 0,
@@ -168,6 +190,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Ends on",
"length": 0,
"no_copy": 0,
@@ -175,6 +198,7 @@
"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,
@@ -185,6 +209,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "all_day",
"fieldtype": "Check",
"hidden": 0,
@@ -192,6 +217,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "All Day",
"length": 0,
"no_copy": 0,
@@ -199,6 +225,7 @@
"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,
@@ -209,6 +236,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break_8",
"fieldtype": "Section Break",
"hidden": 0,
@@ -216,12 +244,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -232,6 +262,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "repeat_this_event",
"fieldtype": "Check",
"hidden": 0,
@@ -239,6 +270,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Repeat this Event",
"length": 0,
"no_copy": 0,
@@ -246,6 +278,7 @@
"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,
@@ -256,6 +289,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "repeat_this_event",
"fieldname": "section_break_11",
"fieldtype": "Section Break",
@@ -264,12 +298,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -280,6 +316,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "repeat_this_event",
"fieldname": "repeat_on",
"fieldtype": "Select",
@@ -288,6 +325,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Repeat On",
"length": 0,
"no_copy": 0,
@@ -296,6 +334,7 @@
"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,
@@ -306,6 +345,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "repeat_this_event",
"description": "Leave blank to repeat always",
"fieldname": "repeat_till",
@@ -315,6 +355,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Repeat Till",
"length": 0,
"no_copy": 0,
@@ -322,6 +363,7 @@
"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,
@@ -332,6 +374,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"hidden": 0,
@@ -339,12 +382,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -355,6 +400,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"",
"fieldname": "monday",
"fieldtype": "Check",
@@ -363,6 +409,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Monday",
"length": 0,
"no_copy": 0,
@@ -370,6 +417,7 @@
"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,
@@ -380,6 +428,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"",
"fieldname": "tuesday",
"fieldtype": "Check",
@@ -388,6 +437,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Tuesday",
"length": 0,
"no_copy": 0,
@@ -395,6 +445,7 @@
"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,
@@ -405,6 +456,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"",
"fieldname": "wednesday",
"fieldtype": "Check",
@@ -413,6 +465,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Wednesday",
"length": 0,
"no_copy": 0,
@@ -420,6 +473,7 @@
"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,
@@ -430,6 +484,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"",
"fieldname": "thursday",
"fieldtype": "Check",
@@ -438,6 +493,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Thursday",
"length": 0,
"no_copy": 0,
@@ -445,6 +501,7 @@
"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,
@@ -455,6 +512,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"",
"fieldname": "friday",
"fieldtype": "Check",
@@ -463,6 +521,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Friday",
"length": 0,
"no_copy": 0,
@@ -470,6 +529,7 @@
"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,
@@ -480,6 +540,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"",
"fieldname": "saturday",
"fieldtype": "Check",
@@ -488,6 +549,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Saturday",
"length": 0,
"no_copy": 0,
@@ -495,6 +557,7 @@
"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,
@@ -505,6 +568,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "eval:doc.repeat_this_event && doc.repeat_on===\"Every Day\"",
"fieldname": "sunday",
"fieldtype": "Check",
@@ -513,6 +577,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Sunday",
"length": 0,
"no_copy": 0,
@@ -520,6 +585,7 @@
"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,
@@ -530,6 +596,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
@@ -537,12 +604,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -553,6 +622,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
@@ -560,6 +630,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
@@ -570,6 +641,7 @@
"print_hide_if_no_value": 0,
"print_width": "300px",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -581,6 +653,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "participants",
"fieldtype": "Section Break",
"hidden": 0,
@@ -588,6 +661,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Participants",
"length": 0,
"no_copy": 0,
@@ -596,6 +670,7 @@
"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,
@@ -606,6 +681,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "groups",
"fieldtype": "Column Break",
"hidden": 0,
@@ -613,6 +689,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Groups",
"length": 0,
"no_copy": 0,
@@ -622,6 +699,7 @@
"print_hide_if_no_value": 0,
"print_width": "50%",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -633,6 +711,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "roles",
"fieldtype": "Table",
"hidden": 0,
@@ -640,6 +719,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Roles",
"length": 0,
"no_copy": 0,
@@ -650,6 +730,7 @@
"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,
@@ -660,6 +741,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "ref_type",
"fieldtype": "Link",
"hidden": 0,
@@ -667,6 +749,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Ref Type",
"length": 0,
"no_copy": 0,
@@ -677,6 +760,7 @@
"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,
@@ -687,6 +771,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "ref_name",
"fieldtype": "Dynamic Link",
"hidden": 0,
@@ -694,6 +779,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Ref Name",
"length": 0,
"no_copy": 0,
@@ -704,6 +790,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -713,15 +800,16 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-calendar",
+ "icon": "fa fa-calendar",
"idx": 1,
+ "image_view": 0,
"in_create": 1,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-03-25 06:09:03.205236",
+ "modified": "2016-11-07 05:31:17.333435",
"modified_by": "Administrator",
"module": "Desk",
"name": "Event",
@@ -737,6 +825,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -757,6 +846,7 @@
"export": 1,
"if_owner": 0,
"import": 1,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -768,6 +858,7 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 1,
"read_only_onload": 0,
"sort_order": "DESC",
diff --git a/frappe/desk/doctype/event/event.py b/frappe/desk/doctype/event/event.py
index 3f690db147..375c9d7b20 100644
--- a/frappe/desk/doctype/event/event.py
+++ b/frappe/desk/doctype/event/event.py
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import (getdate, cint, add_months, date_diff, add_days,
- nowdate, get_datetime_str, cstr, get_datetime)
+ nowdate, get_datetime_str, cstr, get_datetime, now_datetime)
from frappe.model.document import Document
from frappe.utils.user import get_enabled_system_users
@@ -13,6 +13,9 @@ weekdays = ["monday", "tuesday", "wednesday", "thursday", "friday", "saturday",
class Event(Document):
def validate(self):
+ if not self.starts_on:
+ self.starts_on = now_datetime()
+
if self.starts_on and self.ends_on and get_datetime(self.starts_on) > get_datetime(self.ends_on):
frappe.msgprint(frappe._("Event end must be after start"), raise_exception=True)
diff --git a/frappe/desk/doctype/note/note.json b/frappe/desk/doctype/note/note.json
index eb485d5371..21ad87d9e6 100644
--- a/frappe/desk/doctype/note/note.json
+++ b/frappe/desk/doctype/note/note.json
@@ -198,7 +198,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-file-text",
+ "icon": "fa fa-file-text",
"idx": 1,
"image_view": 0,
"in_create": 0,
diff --git a/frappe/desk/doctype/todo/test_todo.py b/frappe/desk/doctype/todo/test_todo.py
index 2bbe82b8d0..bfc1bee06b 100644
--- a/frappe/desk/doctype/todo/test_todo.py
+++ b/frappe/desk/doctype/todo/test_todo.py
@@ -9,4 +9,30 @@ import unittest
# test_records = frappe.get_test_records('ToDo')
class TestToDo(unittest.TestCase):
- pass
+ def test_fetch(self):
+ todo = frappe.get_doc(dict(doctype='ToDo', description='test todo',
+ assigned_by='Administrator')).insert()
+ self.assertEquals(todo.assigned_by_full_name,
+ frappe.db.get_value('User', todo.assigned_by, 'full_name'))
+
+ def test_fetch_setup(self):
+ frappe.db.sql('delete from tabToDo')
+
+ todo_meta = frappe.get_doc('DocType', 'ToDo')
+ todo_meta.get('fields', dict(fieldname='assigned_by_full_name'))[0].options = ''
+ todo_meta.save()
+
+ frappe.clear_cache(doctype='ToDo')
+
+ todo = frappe.get_doc(dict(doctype='ToDo', description='test todo',
+ assigned_by='Administrator')).insert()
+ self.assertFalse(todo.assigned_by_full_name)
+
+ todo_meta = frappe.get_doc('DocType', 'ToDo')
+ todo_meta.get('fields', dict(fieldname='assigned_by_full_name'))[0].options = 'assigned_by.full_name'
+ todo_meta.save()
+
+ todo.reload()
+
+ self.assertEquals(todo.assigned_by_full_name,
+ frappe.db.get_value('User', todo.assigned_by, 'full_name'))
diff --git a/frappe/desk/doctype/todo/todo.js b/frappe/desk/doctype/todo/todo.js
index 66d6e87ace..a50b9e038a 100644
--- a/frappe/desk/doctype/todo/todo.js
+++ b/frappe/desk/doctype/todo/todo.js
@@ -25,7 +25,7 @@ frappe.ui.form.on("ToDo", {
// back to list
frappe.set_route("List", "ToDo");
});
- }, "icon-ok", "btn-success");
+ }, "fa fa-check", "btn-success");
} else {
frm.add_custom_button(__("Re-open"), function() {
frm.set_value("status", "Open");
diff --git a/frappe/desk/doctype/todo/todo.json b/frappe/desk/doctype/todo/todo.json
index e79c6db77c..3c4b97f9e3 100644
--- a/frappe/desk/doctype/todo/todo.json
+++ b/frappe/desk/doctype/todo/todo.json
@@ -9,11 +9,14 @@
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "description_and_status",
"fieldtype": "Section Break",
"hidden": 0,
@@ -21,6 +24,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -28,6 +32,7 @@
"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,
@@ -38,6 +43,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "Open",
"fieldname": "status",
"fieldtype": "Select",
@@ -46,6 +52,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
@@ -54,6 +61,7 @@
"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,
@@ -64,6 +72,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "Medium",
"fieldname": "priority",
"fieldtype": "Select",
@@ -72,6 +81,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Priority",
"length": 0,
"no_copy": 0,
@@ -82,6 +92,7 @@
"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,
@@ -92,6 +103,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
@@ -99,12 +111,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -115,6 +129,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "date",
"fieldtype": "Date",
"hidden": 0,
@@ -122,6 +137,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Due Date",
"length": 0,
"no_copy": 0,
@@ -131,6 +147,7 @@
"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,
@@ -141,6 +158,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "owner",
"fieldtype": "Link",
"hidden": 0,
@@ -148,6 +166,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 1,
"label": "Allocated To",
"length": 0,
"no_copy": 0,
@@ -156,6 +175,7 @@
"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,
@@ -166,6 +186,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "description_section",
"fieldtype": "Section Break",
"hidden": 0,
@@ -173,6 +194,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -181,6 +203,7 @@
"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,
@@ -191,6 +214,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "description",
"fieldtype": "Text Editor",
"hidden": 0,
@@ -198,6 +222,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Description",
"length": 0,
"no_copy": 0,
@@ -208,6 +233,7 @@
"print_hide_if_no_value": 0,
"print_width": "300px",
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -219,6 +245,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
@@ -226,6 +253,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Reference",
"length": 0,
"no_copy": 0,
@@ -233,6 +261,7 @@
"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,
@@ -243,6 +272,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "reference_type",
"fieldtype": "Link",
"hidden": 0,
@@ -250,6 +280,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Reference Type",
"length": 0,
"no_copy": 0,
@@ -260,6 +291,7 @@
"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,
@@ -270,6 +302,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"hidden": 0,
@@ -277,6 +310,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Reference Name",
"length": 0,
"no_copy": 0,
@@ -287,6 +321,7 @@
"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,
@@ -297,6 +332,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "column_break_10",
"fieldtype": "Column Break",
"hidden": 0,
@@ -304,12 +340,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -320,6 +358,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "role",
"fieldtype": "Link",
"hidden": 0,
@@ -327,6 +366,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Role",
"length": 0,
"no_copy": 0,
@@ -337,6 +377,7 @@
"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,
@@ -347,6 +388,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "assigned_by",
"fieldtype": "Link",
"hidden": 0,
@@ -354,6 +396,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Assigned By",
"length": 0,
"no_copy": 0,
@@ -362,6 +405,7 @@
"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,
@@ -372,6 +416,36 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
+ "fieldname": "assigned_by_full_name",
+ "fieldtype": "Read Only",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Assigned By Full Name",
+ "length": 0,
+ "no_copy": 0,
+ "options": "assigned_by.full_name",
+ "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,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "sender",
"fieldtype": "Data",
"hidden": 1,
@@ -379,6 +453,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Sender",
"length": 0,
"no_copy": 0,
@@ -387,6 +462,7 @@
"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,
@@ -396,7 +472,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-check",
+ "icon": "fa fa-check",
"idx": 2,
"image_view": 0,
"in_create": 0,
@@ -405,7 +481,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-06-15 13:11:14.435351",
+ "modified": "2016-12-15 14:56:07.115129",
"modified_by": "Administrator",
"module": "Desk",
"name": "ToDo",
@@ -421,6 +497,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -441,6 +518,7 @@
"export": 1,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
diff --git a/frappe/desk/form/document_flow.py b/frappe/desk/form/document_flow.py
deleted file mode 100644
index dee0053ae2..0000000000
--- a/frappe/desk/form/document_flow.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
-# MIT License. See license.txt
-from __future__ import unicode_literals
-
-import frappe
-import json
-from frappe.desk.form.linked_with import get_linked_docs, get_linked_doctypes
-
-@frappe.whitelist()
-def get_document_completion_status(doctypes, frm_doctype, frm_docname):
- if isinstance(doctypes, basestring):
- doctypes = json.loads(doctypes)
-
- doc = frappe.get_doc(frm_doctype, frm_docname)
- linkinfo = get_linked_doctypes(frm_doctype)
-
- flow_completion = {}
-
- if hasattr(doc, "prev_link_mapper"):
- for doctype in doc.prev_link_mapper:
- fieldname = doc.prev_link_mapper[doctype]["fieldname"]
- lookup_doctype = doc.prev_link_mapper[doctype]["doctype"]
- limit = doc.prev_link_mapper[doctype].get("limit") or 1
- condition = make_condition(doc.prev_link_mapper[doctype].get("filters"))
-
- if condition:
- condition = "where {condition}".format(condition=condition)
- else:
- condition = ""
-
- result = frappe.db.sql_list("select {fieldname} from `tab{doctype}` \
- {condition} limit {limit}".format(fieldname=fieldname, doctype=lookup_doctype,
- condition=condition, limit=limit))
-
- if result:
- flow_completion[doctype] = True
-
- for doctype in doctypes:
- if doctype not in flow_completion:
- links = get_linked_docs(frm_doctype, frm_docname, linkinfo, for_doctype=doctype)
- if links:
- flow_completion[doctype] = True
-
- return flow_completion
-
-def make_condition(filters=None):
- condition = []
- if filters and isinstance(filters, list):
- for cond in filters:
- condition.append("`tab{0}`.{1} {2} '{3}'".format(*cond))
-
- return " and ".join(condition)
-
\ No newline at end of file
diff --git a/frappe/desk/form/linked_with.py b/frappe/desk/form/linked_with.py
index 0c3ec8d72d..0ff8888167 100644
--- a/frappe/desk/form/linked_with.py
+++ b/frappe/desk/form/linked_with.py
@@ -10,21 +10,10 @@ import frappe.desk.form.load
@frappe.whitelist()
def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
- key = "linked_with:{doctype}:{name}".format(doctype=doctype, name=name)
-
if isinstance(linkinfo, basestring):
# additional fields are added in linkinfo
linkinfo = json.loads(linkinfo)
- if for_doctype:
- key = "{key}:{for_doctype}".format(key=key, for_doctype=for_doctype)
-
- results = frappe.cache().get_value(key, user=True)
-
- if results:
- return results
-
- meta = frappe.desk.form.meta.get_meta(doctype)
results = {}
if not linkinfo:
@@ -58,7 +47,7 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
fields = ["`tab{dt}`.`{fn}`".format(dt=dt, fn=sf.strip()) for sf in fields if sf
and "`tab" not in sf]
-
+
try:
if link.get("filters"):
ret = frappe.get_list(doctype=dt, fields=fields, filters=link.get("filters"))
@@ -76,9 +65,9 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
# dynamic link
if link.get("doctype_fieldname"):
filters.append([link.get('child_doctype'), link.get("doctype_fieldname"), "=", doctype])
-
+
ret = frappe.get_list(doctype=dt, fields=fields, filters=filters)
-
+
else:
if link.get("fieldname"):
filters = [[dt, link.get("fieldname"), '=', name]]
@@ -98,8 +87,7 @@ def get_linked_docs(doctype, name, linkinfo=None, for_doctype=None):
if ret:
results[dt] = ret
-
- frappe.cache().set_value(key, results, user=True)
+
return results
@frappe.whitelist()
diff --git a/frappe/desk/form/load.py b/frappe/desk/form/load.py
index 94c7345406..40b100db2b 100644
--- a/frappe/desk/form/load.py
+++ b/frappe/desk/form/load.py
@@ -152,12 +152,16 @@ def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=
conditions = '''communication_type in ("Communication", "Comment")
and (
(reference_doctype=%(doctype)s and reference_name=%(name)s)
- or (timeline_doctype=%(doctype)s
- and timeline_name=%(name)s
- and communication_type="Comment"
- and comment_type in ("Created", "Updated", "Submitted", "Cancelled", "Deleted"))
- )
- and (comment_type is null or comment_type != 'Update')'''
+ or (
+ (timeline_doctype=%(doctype)s and timeline_name=%(name)s)
+ and (
+ communication_type="Communication"
+ or (
+ communication_type="Comment"
+ and comment_type in ("Created", "Updated", "Submitted", "Cancelled", "Deleted")
+ )))
+ )'''
+
if after:
# find after a particular date
@@ -174,7 +178,7 @@ def get_communication_data(doctype, name, start=0, limit=20, after=None, fields=
return communications
def get_assignments(dt, dn):
- cl = frappe.db.sql("""select owner, description from `tabToDo`
+ cl = frappe.db.sql("""select name, owner, description from `tabToDo`
where reference_type=%(doctype)s and reference_name=%(name)s and status="Open"
order by modified desc limit 5""", {
"doctype": dt,
diff --git a/frappe/desk/moduleview.py b/frappe/desk/moduleview.py
index d8596a2894..acdb481ad3 100644
--- a/frappe/desk/moduleview.py
+++ b/frappe/desk/moduleview.py
@@ -34,7 +34,7 @@ def get_data(module):
else:
add_custom_doctypes(data, doctype_info)
- add_section(data, _("Custom Reports"), "icon-list-alt",
+ add_section(data, _("Custom Reports"), "fa fa-list-alt",
get_report_list(module))
data = combine_common_sections(data)
@@ -64,13 +64,13 @@ def build_standard_config(module, doctype_info):
data = []
- add_section(data, _("Documents"), "icon-star",
+ add_section(data, _("Documents"), "fa fa-star",
[d for d in doctype_info if d.document_type in ("Document", "Transaction")])
- add_section(data, _("Setup"), "icon-cog",
+ add_section(data, _("Setup"), "fa fa-cog",
[d for d in doctype_info if d.document_type in ("Master", "Setup", "")])
- add_section(data, _("Standard Reports"), "icon-list",
+ add_section(data, _("Standard Reports"), "fa fa-list",
get_report_list(module, is_standard="Yes"))
return data
@@ -87,10 +87,10 @@ def add_section(data, label, icon, items):
def add_custom_doctypes(data, doctype_info):
"""Adds Custom DocTypes to modules setup via `config/desktop.py`."""
- add_section(data, _("Documents"), "icon-star",
+ add_section(data, _("Documents"), "fa fa-star",
[d for d in doctype_info if (d.custom and d.document_type in ("Document", "Transaction"))])
- add_section(data, _("Setup"), "icon-cog",
+ add_section(data, _("Setup"), "fa fa-cog",
[d for d in doctype_info if (d.custom and d.document_type in ("Setup", "Master", ""))])
def get_doctype_info(module):
diff --git a/frappe/desk/page/activity/activity.js b/frappe/desk/page/activity/activity.js
index 553e8069ea..b71a08c357 100644
--- a/frappe/desk/page/activity/activity.js
+++ b/frappe/desk/page/activity/activity.js
@@ -69,7 +69,7 @@ frappe.pages['activity'].on_page_load = function(wrapper) {
if(frappe.boot.user.can_get_report.indexOf("Feed")!=-1) {
this.page.add_menu_item(__('Build Report'), function() {
frappe.set_route('Report', "Feed");
- }, 'icon-th')
+ }, 'fa fa-th')
}
this.page.add_menu_item(__('Show Likes'), function() {
@@ -120,7 +120,7 @@ frappe.activity.Feed = Class.extend({
data.by = frappe.user.full_name(data.owner);
data.avatar = frappe.avatar(data.owner);
- data.icon = "icon-flag";
+ data.icon = "fa fa-flag";
// color for comment
data.add_class = {
diff --git a/frappe/desk/page/activity/activity.json b/frappe/desk/page/activity/activity.json
index 73790bde28..aa195d9323 100644
--- a/frappe/desk/page/activity/activity.json
+++ b/frappe/desk/page/activity/activity.json
@@ -2,7 +2,7 @@
"creation": "2013-04-09 11:45:31.000000",
"docstatus": 0,
"doctype": "Page",
- "icon": "icon-play",
+ "icon": "fa fa-play",
"idx": 1,
"modified": "2013-07-11 14:40:20.000001",
"modified_by": "Administrator",
diff --git a/frappe/desk/page/applications/applications.js b/frappe/desk/page/applications/applications.js
index 3eb876f971..ad5a10138b 100644
--- a/frappe/desk/page/applications/applications.js
+++ b/frappe/desk/page/applications/applications.js
@@ -180,7 +180,7 @@ frappe.applications.Installer = Class.extend({
frappe.ui.make_app_page({
parent: this.parent,
title: __('App Installer'),
- icon: "icon-download",
+ icon: "fa fa-download",
single_column: true
});
diff --git a/frappe/desk/page/applications/applications.json b/frappe/desk/page/applications/applications.json
index 8ddab6db65..d420ebfdce 100644
--- a/frappe/desk/page/applications/applications.json
+++ b/frappe/desk/page/applications/applications.json
@@ -3,7 +3,7 @@
"creation": "2013-12-23 11:01:52",
"docstatus": 0,
"doctype": "Page",
- "icon": "icon-magic",
+ "icon": "fa fa-magic",
"idx": 1,
"modified": "2015-11-18 06:20:09.586810",
"modified_by": "Administrator",
diff --git a/frappe/desk/page/chat/chat_sidebar.html b/frappe/desk/page/chat/chat_sidebar.html
index 7abd41be76..5b77fd264f 100644
--- a/frappe/desk/page/chat/chat_sidebar.html
+++ b/frappe/desk/page/chat/chat_sidebar.html
@@ -1,7 +1,7 @@
{% for (var i=0, l= data.length; i < l; i++) { var contact = data[i]; %}
+
+
\ No newline at end of file
diff --git a/frappe/desk/page/modules/modules_sidebar_item.html b/frappe/desk/page/modules/modules_sidebar_item.html
index 5dfe6aa8b6..4e4b2c7720 100644
--- a/frappe/desk/page/modules/modules_sidebar_item.html
+++ b/frappe/desk/page/modules/modules_sidebar_item.html
@@ -1,7 +1,7 @@
diff --git a/frappe/desk/page/setup_wizard/setup_wizard.css b/frappe/desk/page/setup_wizard/setup_wizard.css
index 41eef343c0..c13372ce49 100644
--- a/frappe/desk/page/setup_wizard/setup_wizard.css
+++ b/frappe/desk/page/setup_wizard/setup_wizard.css
@@ -33,7 +33,7 @@
margin: -20px auto 20px;
}
-.setup-wizard-slide .icon-fixed-width {
+.setup-wizard-slide .fa-fw {
vertical-align: middle;
}
diff --git a/frappe/desk/page/setup_wizard/setup_wizard.js b/frappe/desk/page/setup_wizard/setup_wizard.js
index bd7e27ca0e..ec802ebb70 100644
--- a/frappe/desk/page/setup_wizard/setup_wizard.js
+++ b/frappe/desk/page/setup_wizard/setup_wizard.js
@@ -257,6 +257,7 @@ frappe.wiz.WizardSlide = Class.extend({
if(this.onload) {
this.onload(this);
}
+ this.focus_first_input();
},
set_init_values: function() {
@@ -288,29 +289,62 @@ frappe.wiz.WizardSlide = Class.extend({
// prev
if(this.id > 0) {
- this.$prev = this.$body.find('.prev-btn').removeClass("hide")
+ this.$prev = this.$body.find('.prev-btn')
+ .removeClass("hide")
+ .attr('tabIndex', 0)
.click(function() {
- frappe.set_route(me.wiz.page_name, me.id-1 + "");
+ me.prev();
})
.css({"margin-right": "10px"});
}
// next or complete
if(this.id+1 < this.wiz.slides.length) {
- this.$next = this.$body.find('.next-btn').removeClass("hide")
- .click(function() {
- if(me.set_values()) {
- frappe.set_route(me.wiz.page_name, me.id+1 + "");
- }
- });
+ this.$next = this.$body.find('.next-btn')
+ .removeClass("hide")
+ .attr('tabIndex', 0)
+ .click(this.next_or_complete.bind(this));
} else {
- this.$complete = this.$body.find('.complete-btn').removeClass("hide")
- .click(function() {
- if(me.set_values()) {
- me.wiz.on_complete(me.wiz);
- }
- });
+ this.$complete = this.$body.find('.complete-btn')
+ .removeClass("hide")
+ .attr('tabIndex', 0)
+ .click(this.next_or_complete.bind(this));
}
+
+ //setup mousefree navigation
+ this.$body.on('keypress', function(e) {
+ if(e.which === 13) {
+ $target = $(e.target);
+ if($target.hasClass('prev-btn')) {
+ me.prev();
+ } else if($target.hasClass('btn-attach')) {
+ //do nothing
+ } else {
+ me.next_or_complete();
+ e.preventDefault();
+ }
+ }
+ });
+ },
+ next_or_complete: function() {
+ if(this.set_values()) {
+ if(this.id+1 < this.wiz.slides.length) {
+ this.next();
+ } else {
+ this.wiz.on_complete(this.wiz);
+ }
+ }
+ },
+ focus_first_input: function() {
+ setTimeout(function() {
+ this.$body.find('.form-control').first().focus();
+ }.bind(this), 0);
+ },
+ next: function() {
+ frappe.set_route(this.wiz.page_name, this.id+1 + "");
+ },
+ prev: function() {
+ frappe.set_route(this.wiz.page_name, this.id-1 + "");
},
get_input: function(fn) {
return this.form.get_input(fn);
@@ -332,7 +366,7 @@ function load_frappe_slides() {
name: "welcome",
domains: ["all"],
title: __("Welcome"),
- icon: "icon-world",
+ icon: "fa fa-world",
help: __("Let's prepare the system for first use."),
fields: [
@@ -401,7 +435,7 @@ function load_frappe_slides() {
frappe.wiz.region = {
domains: ["all"],
title: __("Region"),
- icon: "icon-flag",
+ icon: "fa fa-flag",
help: __("Select your Country, Time Zone and Currency"),
fields: [
{ fieldname: "country", label: __("Country"), reqd:1,
@@ -512,7 +546,7 @@ function load_frappe_slides() {
frappe.wiz.user = {
domains: ["all"],
title: __("The First User: You"),
- icon: "icon-user",
+ icon: "fa fa-user",
fields: [
{"fieldname": "first_name", "label": __("First Name"), "fieldtype": "Data",
reqd:1},
diff --git a/frappe/desk/page/setup_wizard/setup_wizard_page.html b/frappe/desk/page/setup_wizard/setup_wizard_page.html
index 00673174ce..647542701f 100644
--- a/frappe/desk/page/setup_wizard/setup_wizard_page.html
+++ b/frappe/desk/page/setup_wizard/setup_wizard_page.html
@@ -1,7 +1,7 @@
{%= title %}
diff --git a/frappe/desk/reportview.py b/frappe/desk/reportview.py
index 1260a6be0a..9a9979a777 100644
--- a/frappe/desk/reportview.py
+++ b/frappe/desk/reportview.py
@@ -13,6 +13,7 @@ from frappe import _
def get():
args = get_form_params()
args.save_list_settings = True
+
data = compress(execute(**args), args = args)
return data
@@ -33,6 +34,7 @@ def get_form_params():
if isinstance(data.get("docstatus"), basestring):
data["docstatus"] = json.loads(data["docstatus"])
+
# queries must always be server side
data.query = None
@@ -60,7 +62,6 @@ def compress(data, args = {}):
"values": values
}
-
@frappe.whitelist()
def save_report():
"""save report"""
@@ -86,13 +87,22 @@ def export_query():
form_params["limit_page_length"] = None
form_params["as_list"] = True
doctype = form_params.doctype
+ add_totals_row = None
+
del form_params["doctype"]
+ if 'add_totals_row' in form_params and form_params['add_totals_row']=='1':
+ add_totals_row = 1
+ del form_params["add_totals_row"]
+
frappe.permissions.can_export(doctype, raise_exception=True)
db_query = DatabaseQuery(doctype)
ret = db_query.execute(**form_params)
+ if add_totals_row:
+ ret = append_totals_row(ret)
+
data = [['Sr'] + get_labels(db_query.fields, doctype)]
for i, row in enumerate(ret):
data.append([i+1] + list(row))
@@ -112,6 +122,21 @@ def export_query():
frappe.response['type'] = 'csv'
frappe.response['doctype'] = doctype
+def append_totals_row(data):
+ if not data:
+ return data
+ data = list(data)
+ totals = []
+ totals.extend([""]*len(data[0]))
+
+ for row in data:
+ for i in xrange(len(row)):
+ if isinstance(row[i], (float, int)):
+ totals[i] = (totals[i] or 0) + row[i]
+ data.append(totals)
+
+ return data
+
def get_labels(fields, doctype):
"""get column labels based on column names"""
labels = []
@@ -144,25 +169,77 @@ def delete_items():
frappe.delete_doc(doctype, d)
@frappe.whitelist()
-def get_stats(stats, doctype):
+def get_sidebar_stats(stats, doctype, filters=[]):
+ cat_tags = frappe.db.sql("""select tag.parent as category, tag.tag_name as tag
+ from `tabTag Doc Category` as docCat
+ INNER JOIN tabTag as tag on tag.parent = docCat.parent
+ where docCat.tagdoc=%s
+ ORDER BY tag.parent asc,tag.idx""",doctype,as_dict=1)
+
+ return {"defined_cat":cat_tags, "stats":get_stats(stats, doctype, filters)}
+
+@frappe.whitelist()
+def get_stats(stats, doctype, filters=[]):
"""get tag info"""
import json
tags = json.loads(stats)
+ if filters:
+ filters = json.loads(filters)
stats = {}
columns = frappe.db.get_table_columns(doctype)
for tag in tags:
if not tag in columns: continue
- tagcount = execute(doctype, fields=[tag, "count(*)"],
- filters=["ifnull(`%s`,'')!=''" % tag], group_by=tag, as_list=True)
+ tagcount = frappe.get_all(doctype, fields=[tag, "count(*)"],
+ #filters=["ifnull(`%s`,'')!=''" % tag], group_by=tag, as_list=True)
+ filters = filters + ["ifnull(`%s`,'')!=''" % tag], group_by = tag, as_list = True)
if tag=='_user_tags':
stats[tag] = scrub_user_tags(tagcount)
+ stats[tag].append(["No Tags", frappe.get_all(doctype,
+ fields=[tag, "count(*)"],
+ filters=filters +["({0} = ',' or {0} is null)".format(tag)], as_list=True)[0][1]])
else:
stats[tag] = tagcount
return stats
+@frappe.whitelist()
+def get_filter_dashboard_data(stats, doctype, filters=[]):
+ """get tags info"""
+ import json
+ tags = json.loads(stats)
+ if filters:
+ filters = json.loads(filters)
+ stats = {}
+
+ columns = frappe.db.get_table_columns(doctype)
+ for tag in tags:
+ if not tag["name"] in columns: continue
+ tagcount = []
+ if tag["type"] not in ['Date', 'Datetime']:
+ tagcount = frappe.get_all(doctype,
+ fields=[tag["name"], "count(*)"],
+ filters = filters + ["ifnull(`%s`,'')!=''" % tag["name"]],
+ group_by = tag["name"],
+ as_list = True)
+
+ if tag["type"] not in ['Check','Select','Date','Datetime','Int',
+ 'Float','Currency','Percent'] and tag['name'] not in ['docstatus']:
+ stats[tag["name"]] = list(tagcount)
+ if stats[tag["name"]]:
+ data =["No Data", frappe.get_all(doctype,
+ fields=[tag["name"], "count(*)"],
+ filters=filters + ["({0} = '' or {0} is null)".format(tag["name"])],
+ as_list=True)[0][1]]
+ if data and data[1]!=0:
+
+ stats[tag["name"]].append(data)
+ else:
+ stats[tag["name"]] = tagcount
+
+ return stats
+
def scrub_user_tags(tagcount):
"""rebuild tag list for tags"""
rdict = {}
diff --git a/frappe/desk/tags.py b/frappe/desk/tags.py
index 0f9cb3d3cb..b26cef82be 100644
--- a/frappe/desk/tags.py
+++ b/frappe/desk/tags.py
@@ -2,6 +2,7 @@
# MIT License. See license.txt
from __future__ import unicode_literals
+import json
"""
Server side functions for tagging.
@@ -48,18 +49,17 @@ def remove_tag(tag, dt, dn):
@frappe.whitelist()
-def get_tags(doctype, txt):
- tags = []
+def get_tags(doctype, txt, cat_tags):
+ tags = json.loads(cat_tags)
try:
- for _user_tags in frappe.db.sql_list("""select `_user_tags`
+ for _user_tags in frappe.db.sql_list("""select DISTINCT `_user_tags`
from `tab{0}`
where _user_tags like '%{1}%'
- limit 1""".format(frappe.db.escape(doctype), frappe.db.escape(txt))):
- tags.extend(_user_tags.split(","))
+ limit 50""".format(frappe.db.escape(doctype), frappe.db.escape(txt))):
+ tags.extend(_user_tags[1:].split(","))
except Exception, e:
if e.args[0]!=1054: raise
-
- return sorted(filter(lambda t: t and txt in t, list(set(tags))))
+ return sorted(filter(lambda t: t and txt.lower() in t.lower(), list(set(tags))))
class DocTags:
"""Tags for a particular doctype"""
diff --git a/frappe/desk/treeview.py b/frappe/desk/treeview.py
index fa2f3b65f5..6b0cef4ec3 100644
--- a/frappe/desk/treeview.py
+++ b/frappe/desk/treeview.py
@@ -2,7 +2,33 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
-import frappe
+import frappe, json
+from frappe import _
+
+@frappe.whitelist()
+def get_all_nodes(tree_method, tree_args, parent):
+ '''Recursively gets all data from tree nodes'''
+
+ tree_method = frappe.get_attr(tree_method)
+
+ if not tree_method in frappe.whitelisted:
+ frappe.throw(_("Not Permitted"), frappe.PermissionError)
+
+ frappe.local.form_dict = frappe._dict(json.loads(tree_args))
+ frappe.local.form_dict.parent = parent
+ data = tree_method()
+ out = [dict(parent=parent, data=data)]
+
+ to_check = [d.value for d in data if d.expandable]
+ while to_check:
+ frappe.local.form_dict.parent = to_check.pop()
+ data = tree_method()
+ out.append(dict(parent=frappe.local.form_dict.parent, data=data))
+ for d in data:
+ if d.expandable:
+ to_check.append(d.value)
+
+ return out
@frappe.whitelist()
def get_children():
diff --git a/frappe/docs/assets/img/00-login-to-idp.png b/frappe/docs/assets/img/00-login-to-idp.png
new file mode 100644
index 0000000000..8b60b0bc9e
Binary files /dev/null and b/frappe/docs/assets/img/00-login-to-idp.png differ
diff --git a/frappe/docs/assets/img/01-add-oauth-client-on-idp.png b/frappe/docs/assets/img/01-add-oauth-client-on-idp.png
new file mode 100644
index 0000000000..8bea510fda
Binary files /dev/null and b/frappe/docs/assets/img/01-add-oauth-client-on-idp.png differ
diff --git a/frappe/docs/assets/img/02-set-server-url-on-idp.png b/frappe/docs/assets/img/02-set-server-url-on-idp.png
new file mode 100644
index 0000000000..7a434332f8
Binary files /dev/null and b/frappe/docs/assets/img/02-set-server-url-on-idp.png differ
diff --git a/frappe/docs/assets/img/03-set-clientid-client-secret-server-on-app-server.png b/frappe/docs/assets/img/03-set-clientid-client-secret-server-on-app-server.png
new file mode 100644
index 0000000000..8c35f93421
Binary files /dev/null and b/frappe/docs/assets/img/03-set-clientid-client-secret-server-on-app-server.png differ
diff --git a/frappe/docs/assets/img/04-login-screen-on-app-server.png b/frappe/docs/assets/img/04-login-screen-on-app-server.png
new file mode 100644
index 0000000000..d06a51d9d0
Binary files /dev/null and b/frappe/docs/assets/img/04-login-screen-on-app-server.png differ
diff --git a/frappe/docs/assets/img/05-login-with-user-on-idp.png b/frappe/docs/assets/img/05-login-with-user-on-idp.png
new file mode 100644
index 0000000000..496aac16b9
Binary files /dev/null and b/frappe/docs/assets/img/05-login-with-user-on-idp.png differ
diff --git a/frappe/docs/assets/img/06-confirm-grant-access-on-idp.png b/frappe/docs/assets/img/06-confirm-grant-access-on-idp.png
new file mode 100644
index 0000000000..3ad305ab95
Binary files /dev/null and b/frappe/docs/assets/img/06-confirm-grant-access-on-idp.png differ
diff --git a/frappe/docs/assets/img/07-logged-in-as-website-user-with-id-from-idp.png b/frappe/docs/assets/img/07-logged-in-as-website-user-with-id-from-idp.png
new file mode 100644
index 0000000000..7750ed522a
Binary files /dev/null and b/frappe/docs/assets/img/07-logged-in-as-website-user-with-id-from-idp.png differ
diff --git a/frappe/docs/assets/img/app-development/__init__.py b/frappe/docs/assets/img/app-development/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/docs/assets/img/app-development/add_custom_button.png b/frappe/docs/assets/img/app-development/add_custom_button.png
new file mode 100644
index 0000000000..2a83476b04
Binary files /dev/null and b/frappe/docs/assets/img/app-development/add_custom_button.png differ
diff --git a/frappe/docs/assets/img/frappe-bird-white.png b/frappe/docs/assets/img/frappe-bird-white.png
new file mode 100644
index 0000000000..540a1d0398
Binary files /dev/null and b/frappe/docs/assets/img/frappe-bird-white.png differ
diff --git a/frappe/docs/assets/img/portals/__init__.py b/frappe/docs/assets/img/portals/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/docs/contents.py b/frappe/docs/contents.py
index 131d4102a3..c23737a3c0 100644
--- a/frappe/docs/contents.py
+++ b/frappe/docs/contents.py
@@ -6,4 +6,4 @@ import frappe
from frappe.website.utils import get_full_index
def get_context(context):
- context.full_index = get_full_index(extn = True)
+ context.full_index = get_full_index()
diff --git a/frappe/docs/user/en/bench/guides/configuring-https.md b/frappe/docs/user/en/bench/guides/configuring-https.md
index ebd8e52523..7e2740acf1 100755
--- a/frappe/docs/user/en/bench/guides/configuring-https.md
+++ b/frappe/docs/user/en/bench/guides/configuring-https.md
@@ -28,8 +28,8 @@ Also make sure that your private key is not world readable. Generally, it is own
Set the paths to the certificate and private key for your site
- bench set-ssl-certificate site1.local /etc/nginx/ssl/certificate_bundle.crt
- bench set-ssl-key site1.local /etc/nginx/ssl/private.key
+ bench set-ssl-certificate site1.local /etc/nginx/conf.d/ssl/certificate_bundle.crt
+ bench set-ssl-key site1.local /etc/nginx/conf.d/ssl/private.key
### Generate nginx config
@@ -37,7 +37,7 @@ Set the paths to the certificate and private key for your site
### Reload nginx
- service nginx reload
+ sudo service nginx reload
or
diff --git a/frappe/docs/user/en/bench/guides/setup-multitenancy.md b/frappe/docs/user/en/bench/guides/setup-multitenancy.md
index 4ff60c48c1..8f204cd9eb 100755
--- a/frappe/docs/user/en/bench/guides/setup-multitenancy.md
+++ b/frappe/docs/user/en/bench/guides/setup-multitenancy.md
@@ -1,5 +1,5 @@
Assuming that you've already got your first site running and you've performed
-the [production deployment steps](production-setup.html), this section explains how to host your second
+the [production deployment steps](setup-production.html), this section explains how to host your second
site (and more). Your first site is automatically set as default site. You can
change it with the command,
diff --git a/frappe/docs/user/en/guides/app-development/adding-custom-button-to-form.md b/frappe/docs/user/en/guides/app-development/adding-custom-button-to-form.md
new file mode 100644
index 0000000000..d76c673eff
--- /dev/null
+++ b/frappe/docs/user/en/guides/app-development/adding-custom-button-to-form.md
@@ -0,0 +1,27 @@
+To create a custom button on your form, you need to edit the javascript file associated to your doctype. For example, If you want to add a custom button to User form then you must edit `user.js`.
+
+In this file, you need to write a new method `add_custom_button` which should add a button to your form.
+
+#### Function Signature for `add_custom_button(...)`
+ frm.add_custom_button(__(buttonName), function(){
+ //perform desired action such as routing to new form or fetching etc.
+ }, __(groupName));
+
+#### Example-1: Adding a button to User form
+We should edit `frappe\core\doctype\user\user.js`
+
+ frappe.ui.form.on('User', {
+ refresh: function(frm) {
+ ...
+ frm.add_custom_button(__('Get User Email ID'), function(){
+ frappe.msgprint(frm.doc.email);
+ }, __("Utilities"));
+ ...
+ }
+ });
+
+You should be seeing a button on user form as shown below,
+
+
+
+
diff --git a/frappe/docs/user/en/guides/app-development/adding-module-icons-on-desktop.md b/frappe/docs/user/en/guides/app-development/adding-module-icons-on-desktop.md
index e6e0156bf6..44b014267d 100755
--- a/frappe/docs/user/en/guides/app-development/adding-module-icons-on-desktop.md
+++ b/frappe/docs/user/en/guides/app-development/adding-module-icons-on-desktop.md
@@ -19,7 +19,7 @@ In this file you will have to write the `get_data` method that will return a dic
return {
"To Do": {
"color": "#f1c40f",
- "icon": "icon-check",
+ "icon": "fa fa-check",
"icon": "octicon octicon-check",
"label": _("To Do"),
"link": "List/ToDo",
diff --git a/frappe/docs/user/en/guides/app-development/generating-docs.md b/frappe/docs/user/en/guides/app-development/generating-docs.md
index ca3d92a61d..8abd68f027 100755
--- a/frappe/docs/user/en/guides/app-development/generating-docs.md
+++ b/frappe/docs/user/en/guides/app-development/generating-docs.md
@@ -33,8 +33,8 @@ The first step is to setup the docs folder. For that you must create a new file
To generate the docs for the `current` version, go to the command line and write
- bench --site sitename make-docs app_name current
-
+ bench --site [site] build-docs [appname]
+
If you want to maintain versions of your docs, then you can add a version number instead of `current`
This will create a `/docs` folder in your app.
diff --git a/frappe/docs/user/en/guides/app-development/how-to-improve-a-standard-control.md b/frappe/docs/user/en/guides/app-development/how-to-improve-a-standard-control.md
index bfbf320612..e3c8ef82c0 100755
--- a/frappe/docs/user/en/guides/app-development/how-to-improve-a-standard-control.md
+++ b/frappe/docs/user/en/guides/app-development/how-to-improve-a-standard-control.md
@@ -42,7 +42,7 @@ _-At last, we think we would never stop talking!_
\
\
\
- \
+ \
\
').prependTo(this.input_area);
this.$input_area = $(this.input_area);
diff --git a/frappe/docs/user/en/guides/app-development/using-html-templates-in-javascript.md b/frappe/docs/user/en/guides/app-development/using-html-templates-in-javascript.md
index e1728ae86f..f6242babb7 100755
--- a/frappe/docs/user/en/guides/app-development/using-html-templates-in-javascript.md
+++ b/frappe/docs/user/en/guides/app-development/using-html-templates-in-javascript.md
@@ -15,7 +15,7 @@ To render a template,
From `erpnext/public/js/templates/address_list.js`
{% raw %}
- New Address
+ New Address
{% for(var i=0, l=addr_list.length; i
diff --git a/frappe/docs/user/en/guides/basics/site_config.md b/frappe/docs/user/en/guides/basics/site_config.md
index 983bf07563..4ed73239f1 100755
--- a/frappe/docs/user/en/guides/basics/site_config.md
+++ b/frappe/docs/user/en/guides/basics/site_config.md
@@ -16,6 +16,7 @@ Example:
- `db_name`: Database Name.
- `db_password`: Database password.
+- `encryption_key`: encryption_key for stored non user passwords.
### Optional Settings
diff --git a/frappe/docs/user/en/guides/integration/index.txt b/frappe/docs/user/en/guides/integration/index.txt
index 6bee5d8835..6d86f7389c 100755
--- a/frappe/docs/user/en/guides/integration/index.txt
+++ b/frappe/docs/user/en/guides/integration/index.txt
@@ -1,3 +1,4 @@
rest_api
how_to_setup_oauth
using_oauth
+openid_connect_and_frappe_social_login
diff --git a/frappe/docs/user/en/guides/integration/openid_connect_and_frappe_social_login.md b/frappe/docs/user/en/guides/integration/openid_connect_and_frappe_social_login.md
new file mode 100644
index 0000000000..b112e05b55
--- /dev/null
+++ b/frappe/docs/user/en/guides/integration/openid_connect_and_frappe_social_login.md
@@ -0,0 +1,72 @@
+# OpenID Connect and Frappe social login
+
+## OpenID Connect
+
+Frappe also uses Open ID connect essential standard for authenticating users. To get `id_token` with `access_token`, pass `openid` as the value for the scope parameter during authorization request.
+
+If the scope is `openid` the JSON response with `access_token` will also include a JSON Web Token (`id_token`) signed with `HS256` and `Client Secret`. The decoded `id_token` includes the `at_hash`.
+
+Example Bearer Token with scope `openid`
+
+```
+{
+ "token_type": "Bearer",
+ "id_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6Imp3dCJ9.eyJpc3MiOiJodHRwczovL21udGVjaG5pcXVlLmNvbSIsImF0X2hhc2giOiJOQlFXbExJUy1lQ1BXd1d4Y0EwaVpnIiwiYXVkIjoiYjg3NzJhZWQ1YyIsImV4cCI6MTQ3Nzk1NTYzMywic3ViIjoiNWFjNDE2NThkZjFiZTE1MjI4M2QxYTk0YjhmYzcwNDIifQ.1GRvhk5wNoR4GWoeQfleEDgtLS5nvj9nsO4xd8QE-Uk",
+ "access_token": "ZJD04ldyyvjuAngjgBrgHwxcOig4vW",
+ "scope": "openid",
+ "expires_in": 3600,
+ "refresh_token": "2pBTDTGhjzs2EWRkcNV1N67yw0nizS"
+}
+```
+
+## Frappe social login setup
+
+In this example there are 2 servers,
+
+### Primary Server
+This is the main server hosting all the users. e.g. `https://frappe.io`. To setup this as the main server, go to *Setup* > *Integrations* > *Social Login Keys* and enter `https://frappe.io` in the field `Frappe Server URL`. This URL repeats in all other Frappe servers who connect to this server to authenticate. Effectively, this is the main Identity Provider (IDP).
+
+Under this server add as many `OAuth Client`(s) as required. Because we are setting up one app server, add only one `OAuth Client`
+
+### Frappe App Server
+This is the client connecting to the IDP. Go to *Setup* > *Integrations* > *Social Login Keys* on this server and add appropriate values to `Frappe Client ID` and `Frappe Client Secret` (refer to client added in primary server). As mentioned before keep the `Frappe Server URL` as `https://frappe.io`
+
+Now you will see Frappe icon on the login page. Click on this icon to login with account created in primary server (IDP) `https://frappe.io`
+
+**Note**: If `Skip Authorization` is checked while registering a client, page to allow or deny the granting access to resource is not shown. This can be used if the apps are internal to one organization and seamless user experience is needed.
+
+## Steps
+
+### Part 1 : on Frappe Identity Provider (IDP)
+
+Login to IDP
+
+
+Add OAuth Client on IDP
+
+
+Set Server URL on IDP
+
+
+### Part 2 : on Frappe App Server
+
+Set `Frappe Client ID` and `Frappe Client Secret` on App server (refer the client set on IDP)
+
+
+**Note**: Frappe Server URL is the main server where identities from your organization are stored.
+
+Login Screen on App Server (login with frappe)
+
+
+### Part 3 : Redirected on IDP
+
+login with user on IDP
+
+
+Confirm Access on IDP
+
+
+### Part 4 : Back on App Server
+
+Logged in on app server with ID from IDP
+
diff --git a/frappe/docs/user/en/guides/reports-and-printing/how-to-make-script-reports.md b/frappe/docs/user/en/guides/reports-and-printing/how-to-make-script-reports.md
index 82b81ac51e..90c70272cc 100755
--- a/frappe/docs/user/en/guides/reports-and-printing/how-to-make-script-reports.md
+++ b/frappe/docs/user/en/guides/reports-and-printing/how-to-make-script-reports.md
@@ -4,13 +4,13 @@ You can create tabulated reports using server side scripts by creating a new Rep
> Note: You will need Administrator Permissions for this.
-Since these reports give you unrestricted access via Python scripts, they can only be created by Administrators. The script part of the report becomes a part of the repository of the application. If you have not created an app, [read this](/developers/guide).
+Since these reports give you unrestricted access via Python scripts, they can only be created by Administrators. The script part of the report becomes a part of the repository of the application. If you have not created an app, [read this](https://frappe.github.io/frappe/user/en/guides/app-development/).
> Note: You must be in [Developer Mode](https://frappe.github.io/frappe/user/en/guides/app-development/how-enable-developer-mode-in-frappe) to do this
### 1. Create a new Report
-
+
1. Set Report Type as "Script Report"
1. Set "Is Standard" as "Yes"
diff --git a/frappe/docs/user/en/guides/reports-and-printing/index.md b/frappe/docs/user/en/guides/reports-and-printing/index.md
index 8c464f691c..42a169f9f0 100755
--- a/frappe/docs/user/en/guides/reports-and-printing/index.md
+++ b/frappe/docs/user/en/guides/reports-and-printing/index.md
@@ -1 +1,3 @@
# Reports and Printing
+
+{index}
diff --git a/frappe/docs/user/en/tutorial/index.md b/frappe/docs/user/en/tutorial/index.md
index 56f5817fc5..43f04583e9 100755
--- a/frappe/docs/user/en/tutorial/index.md
+++ b/frappe/docs/user/en/tutorial/index.md
@@ -1,6 +1,6 @@
# Frappe Tutorial
-In this guide we will show you how to create an application from scratch using **Frappe**. Using the example of a Library Management System, we will cover:
+In this guide, we will show you how to create an application from scratch using **Frappe**. Using the example of a Library Management System, we will cover:
1. Installation
1. Making a New App
@@ -12,7 +12,7 @@ In this guide we will show you how to create an application from scratch using *
## Who is This For?
-This guide is intended for software developers who are familiar with how the web applications are built and served. Frappe Framework is built on Python and uses MariaDB database and for creating web views, HTML/CSS/Javascript is used. So it would be great if you are familiar with all these technologies. At minimum if you have never used Python before, you should take a quick tutorial before your use this Guide.
+This guide is intended for software developers who are familiar with how the web applications are built and served. Frappe Framework is built on Python and uses MariaDB database and for creating web views, HTML/CSS/Javascript is used. So it would be great if you are familiar with all these technologies. At the minimum, if you have never used Python before, you should take a quick tutorial before your use this Guide.
Frappe uses the git version control system on GitHub. It is also important that you are familiar with basic git and have an account on GitHub to manage your applications.
diff --git a/frappe/docs/user/fr/tutorial/app.md b/frappe/docs/user/fr/tutorial/app.md
index b52d71e97c..54c4fffd1e 100644
--- a/frappe/docs/user/fr/tutorial/app.md
+++ b/frappe/docs/user/fr/tutorial/app.md
@@ -5,9 +5,9 @@ la même facon que vous structurez une application Python standard. Pour le dép
vous pouvez facilement déployer votre application sur n'importe quelle machine.
Frappe fournit une interface WSGI et pendant vos développements vous pouvez utiliser le serveur Werkzeug embarqué. Pour le
-déploiemenent en production, nous recommandons d'utiliser nginx et gunicorn.
+déploiement en production, nous recommandons d'utiliser nginx et gunicorn.
-Frappe, c'est aussune une architecture multi-tenant ce qui signifie que vous pouvez lancer plusieurs sites sur une meme
+Frappe, c'est aussi une une architecture multi-tenant ce qui signifie que vous pouvez lancer plusieurs sites sur une même
configuration, chaque site utilisant ses propres applications et utilisateurs. La base de données de chaque site est indépendante.
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/before.md b/frappe/docs/user/fr/tutorial/before.md
index 775c395c38..9c73c934dc 100644
--- a/frappe/docs/user/fr/tutorial/before.md
+++ b/frappe/docs/user/fr/tutorial/before.md
@@ -1,19 +1,16 @@
# Avant de commencer
-Liste des outils, technologies utiles dans le développement d'applications avec Frappe.
+Liste des outils et technologies utiles dans le développement d'applications avec Frappe.
+
+Il y a un grand nombres de tutoriels en ligne et nous recommandons [CodeAcademy](http://www.codecademy.com/) ou vous trouverez beaucoup de ressources de qualité.
-Il y a un grand nombres de tutoriels en ligne et nous recommandons [CodeAcademy](http://www.codecademy.com/) ou vous
-trouverez beaucoup de ressources de qualité.
---
#### 1. Python
-La partie serveur de Frappe est codée en Python et c'est une bonne idée d'[apprendre rapidement Python](http://www.codecademy.com/tracks/python)
-avant de commencer à comprendre Frappe. [Le tutoriel sur docs.python.org](https://docs.python.org/2.7/tutorial/index.html)
-est aussi une excellente ressource pour apprendre Python. Notez que Frappe utilise Python 2.7.
+La partie serveur de Frappe est codée en Python et c'est une bonne idée d'[apprendre rapidement Python](http://www.codecademy.com/tracks/python) avant de commencer à comprendre Frappe. [Le tutoriel sur docs.python.org](https://docs.python.org/2.7/tutorial/index.html) est aussi une excellente ressource pour apprendre Python. Notez que Frappe utilise Python 2.7.
-Pour produire une application de qualité, vous devez inclure des tests automatiques. Vous pouvez comprendre les bases
-du développement par les tests [ici](http://code.tutsplus.com/tutorials/beginning-test-driven-development-in-python--net-30137).
+Pour produire une application de qualité, vous devez inclure des tests automatiques. Vous pouvez comprendre les bases du développement par les tests [ici](http://code.tutsplus.com/tutorials/beginning-test-driven-development-in-python--net-30137).
---
@@ -26,7 +23,7 @@ et comment faire des requêtes. Voici [une rapide introduction à MySQL](https:/
#### 3. HTML / CSS
-Pour construire des interfaces utilisateur, vous devez [apprendre les bases HTML / CSS](http://www.codecademy.com/tracks/web) et [connaître le framework CSS Boostrap](http://getbootstrap.com)
+Pour construire des interfaces utilisateur, vous devez [apprendre les bases HTML / CSS](http://www.codecademy.com/tracks/web) et [connaître le framework CSS Boostrap](http://getbootstrap.com).
---
@@ -44,9 +41,9 @@ Si vous voulez modifier les affichages, vous devez apprendre le [language de tem
#### 6. Git et GitHub
-[Apprenz à contrinuer à un projet open source en utilisant Git et GitHub](https://guides.github.com/activities/contributing-to-open-source/), deux outils fantastiques pour vous aider à gérer et partager votre code.
+[Apprenez à contribuer à un projet open source en utilisant Git et GitHub](https://guides.github.com/activities/contributing-to-open-source/), deux outils fantastiques pour vous aider à gérer et partager votre code.
---
-Quand vous êtes prêts, [essayez de développer une simple applicationa avec Frappe]({{ docs_base_url }}/user/fr/tutorial/app)
+Quand vous êtes prêts, [essayez de développer une application simple avec Frappe]({{ docs_base_url }}/user/fr/tutorial/app)
diff --git a/frappe/docs/user/fr/tutorial/bench.md b/frappe/docs/user/fr/tutorial/bench.md
index 65a82f29a2..c2902b3a87 100644
--- a/frappe/docs/user/fr/tutorial/bench.md
+++ b/frappe/docs/user/fr/tutorial/bench.md
@@ -1,13 +1,9 @@
# Installer le bench de Frappe
-La facon la plus rapide d'installer Frappe sur un système Unix est d'utiliser frappe-bench. Lisez les instructions détaillées sur
-comment installer et utiliser Frappe Bench.
+La façon la plus rapide d'installer Frappe sur un système Unix est d'utiliser frappe-bench. Lisez les instructions détaillées sur l'installation et l'utilisation de Frappe Bench : [https://github.com/frappe/bench](https://github.com/frappe/bench)
-> [https://github.com/frappe/bench](https://github.com/frappe/bench)
+Avec Frappe Bench vous pourrez configurer et héberger plusieurs sites et applications. Frappe Bench configure aussi un environnement virtuel Python (Virtualenv), vos applications seront donc isolées les unes des autres et vous n'aurez pas de conflits avec d'autres environnements de développement.
-Avec Frappe Bench vous pourrez configurer et héberger de multiples applications et sites. Frappe Bench configure aussi un Virtualenv
-Python donc vos applications seront isolées les unes des autres (et vous n'aurez pas de conflits avce d'autres environnements de developpement).
+La commande `bench` sera ajoutée pour vous aider dans le développement et la gestion de vos applications.
-La commande `bench` sera installé et vous aidera aidera dans le développement et la gestion des vos applications.
-
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/conclusion.md b/frappe/docs/user/fr/tutorial/conclusion.md
index 44590555f1..8428b362b2 100644
--- a/frappe/docs/user/fr/tutorial/conclusion.md
+++ b/frappe/docs/user/fr/tutorial/conclusion.md
@@ -1,10 +1,9 @@
# Conclusion
-
Nous espérons vous avoir donné un apercu de comment les applications sont développées avec Frappe. L'ojectif était de vous
montrer brievement, les différents aspects du développement d'une application en vous donnant un apércu général. Pour aller plus
loin dans les explications, consultez l'API.
-Pour obtenir de l'aider, rejoingnez la communauté sur le[chat sur Gitter](https://gitter.im/frappe/erpnext) ou sur le
+Pour obtenir de l'aider, rejoingnez la communauté sur le [chat sur Gitter](https://gitter.im/frappe/erpnext) ou sur le
[forum des développeurs](https://discuss.erpnext.com)
diff --git a/frappe/docs/user/fr/tutorial/controllers.md b/frappe/docs/user/fr/tutorial/controllers.md
index 963feec9e9..dd9f2ab69d 100644
--- a/frappe/docs/user/fr/tutorial/controllers.md
+++ b/frappe/docs/user/fr/tutorial/controllers.md
@@ -1,6 +1,6 @@
# Les controleurs
-La prochaine étape est d'ajouter quelques méthodes et évéements à nos modèles. Dans l'application, nous devons nous
+La prochaine étape est d'ajouter quelques méthodes et événements à nos modèles. Dans l'application, nous devons nous
assurer que si une opération est faite, l'article concerné doit être disponible en stock et que le membre qui souhaite
faire le prêt à un abonnement valide.
@@ -15,7 +15,7 @@ Ce fichier est le controleur qui gère les opérations de la librairie, vous pou
1. `on_cancel`
1. `on_trash` (avant qu'il ne soit sur le point d'être supprimé)
-Vous pouvez écrire des méthodes pour ces événement et elles seront appelées par le framework au bon moment.
+Vous pouvez écrire des méthodes pour ces événements et elles seront appelées par le framework au bon moment.
Voici pour finir le controleur:
@@ -42,13 +42,13 @@ Voici pour finir le controleur:
if not last_transaction or last_transaction[0].transaction_type!="Issue":
frappe.throw(_("Cannot return article not issued"))
-Dans ce scrit:
+Dans ce script:
1. Nous récuperons la dernière opération, avant la date de l'opération en cours en utilisant la méthode `frappe.get_list`
1. Si la dernière opération est quelque chose qui n'est pas attendu, alors nous levons une exception en utilisant `frappe.throw`
1. Nous utilisons la méthode `_("text")` pour identifier une chaine traduisible.
-Verifiez vos validations en créant de nouveux enregistrements.
+Vérifiez vos validations en créant de nouveaux enregistrements.
@@ -56,6 +56,6 @@ Verifiez vos validations en créant de nouveux enregistrements.
Pour debugger, gardez toujours votre console JS ouverte, vérifier les erreurs à la fois de Javascript mais aussi du serveur.
-Regardez aussi votre fenêtre de terminal pour les esceptions. Chaque **erreur 500 pour des problèmes internes** seront affichées dans le terminal du serveur en cours d'utilisation.
+Regardez aussi votre fenêtre de terminal pour les exceptions. Chaque **erreur 500 pour des problèmes internes** seront affichées dans le terminal du serveur en cours d'utilisation.
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/doctype-directory-structure.md b/frappe/docs/user/fr/tutorial/doctype-directory-structure.md
index 2fbfcb4c30..1bb7970182 100644
--- a/frappe/docs/user/fr/tutorial/doctype-directory-structure.md
+++ b/frappe/docs/user/fr/tutorial/doctype-directory-structure.md
@@ -29,4 +29,4 @@ Après avoir sauvegardé vos DocTypes, vérifiez que les modèles dans les fichi
│ │ ├── library_transaction.json
│ │ └── library_transaction.py
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/doctypes.md b/frappe/docs/user/fr/tutorial/doctypes.md
index 199fa92f7c..698e1e6c69 100644
--- a/frappe/docs/user/fr/tutorial/doctypes.md
+++ b/frappe/docs/user/fr/tutorial/doctypes.md
@@ -1,6 +1,6 @@
# DocType
-Après avoir créé les ^oles, créons des **DocTypes**
+Après avoir créé les roles, créons des **DocTypes**
Pour créer un nouveau **DocType**, rendez-vous sur:
@@ -50,7 +50,7 @@ sur chacune des lignes comme ci-dessous
#### Ajouter des permissins
Après avoir ajouté les champs, validez et ajoutez un nouveau rôle dans la section des règles de permissions. Pour le moment
-ajoutons les droits le lecture, ecriture, création et suppression au modèle **Librarian**. Frappe à une gestion fine des
+ajoutons les droits le lecture, écriture, création et suppression au modèle **Librarian**. Frappe à une gestion fine des
permissions sur les modèles. Vous pouvez aussi changer les permissions plus tard en utilisant le gestionnaire de permissions
dans la configuration.
@@ -101,5 +101,5 @@ Maintenant, connectez vous à MySQL et vérifiez la base de données créée:
Comme vous pouvez le voir, en plus de nos **DocFields**, d'autres colonnes ont été ajoutées dans notre table. Notez les
changement, la clé primaire sur, `name`, `owner`(l'utilisateur quia créer l'enregistrement), `creation` et `modified` (des timestamps pour enregistrer les dates de creation et de modification).
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/form-client-scripting.md b/frappe/docs/user/fr/tutorial/form-client-scripting.md
index 28786d1e39..f2f2e21894 100644
--- a/frappe/docs/user/fr/tutorial/form-client-scripting.md
+++ b/frappe/docs/user/fr/tutorial/form-client-scripting.md
@@ -13,7 +13,7 @@ faire, nous allons devoir écrire un gestionnaire d'évenements pour lorsque l'u
Pour commencer ce script, dans le repertoire `library_management/doctype/library_transaction`, créez un nouveau fichier
`library_transaction.js`. Ce fichier sera automatiquement executé lorsque le modèle `Library Transaction` est appelé par l'utilisateur.
-Donc, dans ce fichier nous pouvons lier des actions à des événemenents mais aussi écrire d'autres fonctiones.
+Donc, dans ce fichier nous pouvons lier des actions à des événemenents mais aussi écrire d'autres fonctions.
#### library_transaction.js
@@ -43,4 +43,4 @@ Donc, dans ce fichier nous pouvons lier des actions à des événemenents mais a
**Note:** Pour vérifier que votre script fonctionne, n'oubliez pas de recharger votre page avant de tester.
Les changements dans les scripts côté client ne sont pas automatiquement pris en compte quand vous êtes en mode développeur.
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/index.md b/frappe/docs/user/fr/tutorial/index.md
index a0bd012fb0..b518478c72 100644
--- a/frappe/docs/user/fr/tutorial/index.md
+++ b/frappe/docs/user/fr/tutorial/index.md
@@ -1,7 +1,7 @@
# Tutoriel Frappe
Dans ce guide nous allons vous montrer comment créer une application de A à Z en utilisant **Frappe**. Avec un
-exemple de gestion d'une librairie, nous allons aborder les sujets suivants:
+exemple de gestion de bibliothèque, nous allons aborder les sujets suivants:
1. Installation
1. Créer une nouvelle application
@@ -14,28 +14,27 @@ exemple de gestion d'une librairie, nous allons aborder les sujets suivants:
## A qui s'adresse ce tutoriel ?
Ce guide est à destination des développeurs familiers avec la création d'applications web. Le framework Frappe est développé
-avec Python, utilise le système de base de données MariaDB et database and HTML/CSS/Javascript pour le rendu des pages.
-Il est donc necessaire d'être familier avec ces technologies. Si vous n'avez jamais utilisé Python auparavant, vous devriez
+avec Python, utilise le système de base de données MariaDB et HTML/CSS/Javascript pour le rendu des pages.
+Il est donc nécessaire d'être familier avec ces technologies. Si vous n'avez jamais utilisé Python auparavant, vous devriez
suivre un tutoriel rapide avant de suivre ce guide.
-Frappe utilise le système de gestion de version git sur Github. Il est donc imporant que vous connaissiez les basiques de
+Frappe utilise le système de gestion de version git sur Github. Il est donc important que vous connaissiez les bases de
l'utilisation de git et que vous ayez un compte sur Github pour gérer vos applications.
## Exemple
-Dans ce guide, nous allons développer une simple application de **gestion de librairie**. Dans cette applications nous aurons
+Dans ce guide, nous allons développer une application simple de **gestion de bibliothèque**. Dans cette applications nous aurons
les modèles suivants:
-1. Article (un livre ou tout autre produit qui peut être emprunté)
-1. Library Member (membre de la librairie)
+1. Article (un livre ou tout autre objet qui peut être emprunté)
+1. Library Member (membre de la bibliothèque)
1. Library Transaction (prêt ou retour d'un article)
-1. Library Membership (Periode pendant laquelle un membre peut emprunter)
+1. Library Membership (période pendant laquelle un membre peut emprunter)
1. Library Management Setting (configuration générale)
-L'interace utilisateur (UI) pour le libraire sera **Frappe Desk**, un système de rendu d'interface où les formulaires sont
+L'interface utilisateur (UI) pour le bibliothécaire sera **Frappe Desk**, un système de rendu d'interface où les formulaires sont
automatiquement générés depuis les modèles en appliquant rôles et permissions.
-Nous allons aussi créer des vues pour la lirairie afin que les utilisateurs puissent parcourir les articles depuis un site
-internet.
+Nous allons aussi créer des vues pour la bibliothèque afin que les utilisateurs puissent parcourir la liste des livres depuis un site internet.
-{index}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/models.md b/frappe/docs/user/fr/tutorial/models.md
index 7f49a254a1..f67d356bf6 100644
--- a/frappe/docs/user/fr/tutorial/models.md
+++ b/frappe/docs/user/fr/tutorial/models.md
@@ -1,10 +1,10 @@
# Définir des modèles
-La prochaine étape est de définir les modèles que nous avons présenté en introduction. Dans Frappe, les modèles sont appelés
+La prochaine étape est de définir les modèles que nous avons présenté en introduction. Dans **Frappe**, les modèles sont appelés
des **DocTypes**. Vous pouvez définir de nouveaux **DocTypes** depuis l'interface. Les **DocTypes** sont faits de **DocField**
et de permissions appelées **DocPerms**.
-Quand un DocType est sauvegardé, une nouvelle table est créee dans la base de données. Cette table est nommée `tab[doctype]`.
+Quand un DocType est sauvegardé, une nouvelle table est créée dans la base de données. Cette table est nommée `tab[doctype]`.
Quand vous créez un **DocType**, un nouveau repertoire est créé dans le **Module**, un fichier JSON définissant le modèles
ainsin qu'un template de controleur sont automatiquement créés.
@@ -23,4 +23,4 @@ pour appliquer les changements. Vous devriez maintenant voir l'application "Deve
"developer_mode": 1
}
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/naming-and-linking.md b/frappe/docs/user/fr/tutorial/naming-and-linking.md
index cf4be0af57..a5f6c5df47 100644
--- a/frappe/docs/user/fr/tutorial/naming-and-linking.md
+++ b/frappe/docs/user/fr/tutorial/naming-and-linking.md
@@ -16,7 +16,7 @@ Les **DocTypes** peuvent être nommés de différentes facons:
1. Par le controlleur (code)
1. Par la console
-Cela peut être configuré par le champs **Autoname**. Pour le controlerur, laissez vide.
+Cela peut être configuré par le champs **Autoname**. Pour le controleur, laissez vide.
> **Search Fields**: Un **DocType** peut être nommé sur la base d'une serie mais nous devons toujours pouvoir le chercher par un nom.
Dans notre cas, l'arcicle peut etre cherché par un titre ou par l'auteur. Remplissons donc le champs **Search Fields**.
@@ -54,7 +54,7 @@ définir `library_member.first_name`
-### Completeter les Modeles
+### Completer les Modeles
De la même facon, vous pouvez compléter les autres modèles pour qu'au final le résultat soit:
@@ -74,6 +74,6 @@ De la même facon, vous pouvez compléter les autres modèles pour qu'au final l
-> Vérifiez que le modèles **Librarian** ait les permissions sur chaque **DocType**.
+> Vérifiez que le modèles **Librarian** aient les permissions sur chaque **DocType**.
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/new-app.md b/frappe/docs/user/fr/tutorial/new-app.md
index d3ae222f46..418a1643be 100644
--- a/frappe/docs/user/fr/tutorial/new-app.md
+++ b/frappe/docs/user/fr/tutorial/new-app.md
@@ -43,14 +43,14 @@ L'application sera créée dans un répertoire appelé `library_management` et a
├── requirements.txt
└── setup.py
-1. `config` configuration de l'applicationfolder contains application configuration info
+1. `config` contient les configurations de l'application
1. `desktop.py` est l'endroit ou les icones peuvent être ajoutées au bureau
1. `hooks.py` contient les définitions de la facon dont l'intégration avec l'environnement et les autres applications est faite.
-1. `library_management` (interne) est un **module** bootstrappé. Dans Frappe, un **module** est l'endroit ou sont les controlleurs et les modeles.
+1. `library_management` (interne) est un **module** bootstrappé. Dans Frappe, un **module** est l'endroit où sont les controlleurs et les modeles.
1. `modules.txt` contient la liste des **modules** dans l'application. Quand vous créez un nouveau module, c'est obligatoire de l'ajouter dans ce fichier.
1. `patches.txt` contient les patchs de migration. Ce sont des références Python utilisant la notation par point.
1. `templates` est le repertoire qui contient les templates des vues. Les templates pour **Login** et autres pages par défaut sont déjà contenus dans Frappe.
-1. `generators` est l'endroit ou sont stockés les templates des modèles. Chaque instance du modèle a une route web comme par exemple les **articles de blog** ou chaque article a une adresse unique. Dans Frappe, Jinj2 est utilisé pour les templates.
+1. `generators` est l'endroit où sont stockés les templates des modèles. Chaque instance du modèle a une route web comme par exemple les **articles de blog** ou chaque article a une adresse unique. Dans Frappe, Jinj2 est utilisé pour les templates.
1. `pages` contient les routes uniques pour les templates. Par exemple "/blog" ou "/article"
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/reports.md b/frappe/docs/user/fr/tutorial/reports.md
index ee93a0eb14..92b0ef4d45 100644
--- a/frappe/docs/user/fr/tutorial/reports.md
+++ b/frappe/docs/user/fr/tutorial/reports.md
@@ -4,4 +4,4 @@ Vous pouvez aussi cliquer sur le texte "Rapports" dans la barre latérale de gau
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/roles.md b/frappe/docs/user/fr/tutorial/roles.md
index 8ea3498166..ce5add6000 100644
--- a/frappe/docs/user/fr/tutorial/roles.md
+++ b/frappe/docs/user/fr/tutorial/roles.md
@@ -12,4 +12,4 @@ Pour créer un nouveau rôle, se rendre sur:
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/setting-up-the-site.md b/frappe/docs/user/fr/tutorial/setting-up-the-site.md
index 9c595e2974..985e124e0a 100644
--- a/frappe/docs/user/fr/tutorial/setting-up-the-site.md
+++ b/frappe/docs/user/fr/tutorial/setting-up-the-site.md
@@ -37,7 +37,7 @@ Un nouveau repertoires appelé `library` sera créé dans le repertoire `sites`.
### COnfiguration par défaut
-Dans le cas ou vous avez plusieurs sites, utilisez la commande `bench use [site_name]` pour définir le site par défaut.
+Dans le cas où vous avez plusieurs sites, utilisez la commande `bench use [site_name]` pour définir le site par défaut.
Exemple:
@@ -53,4 +53,4 @@ Exemple:
$ bench --site library install-app library_management
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/start.md b/frappe/docs/user/fr/tutorial/start.md
index bc71edd27e..608127c0d5 100644
--- a/frappe/docs/user/fr/tutorial/start.md
+++ b/frappe/docs/user/fr/tutorial/start.md
@@ -29,4 +29,4 @@ Une fois connecté, vous devriez voir le `Desk`, c'est à dire la page d'accueil
Comme vous pouvez le voir, Frappe fournit quelques applications comme un To Do, un gestionnaire de fichiers etc. Ces applications
peuvent être intégrées par la suite.
-{suite}
+{next}
diff --git a/frappe/docs/user/fr/tutorial/task-runner.md b/frappe/docs/user/fr/tutorial/task-runner.md
index 9bc2d4aa42..b455fb6af9 100644
--- a/frappe/docs/user/fr/tutorial/task-runner.md
+++ b/frappe/docs/user/fr/tutorial/task-runner.md
@@ -3,7 +3,7 @@
Finalement, une application a aussi à envoyer des emails de notifications ou d'autres taches planifiées. Dans Frappe, si
vous avez configuré le **bench**, la tâche / planificateur est configuré via Celery en utilisant les queues Redis.
-Pour ajouter un nouveau gestionnaire de tâches, ouvrez le fichier `hooks.py` et ajoutez un nouveau gestionnaire. Les gestionnaire
+Pour ajouter un nouveau gestionnaire de tâches, ouvrez le fichier `hooks.py` et ajoutez un nouveau gestionnaire. Les gestionnaires
par defaut sont `all`, `daily`, `weekly`, `monthly`. Le gestionanire `all` est appelé toutes les 3 minutes par defaut.
# Scheduled Tasks
@@ -69,9 +69,7 @@ ressemble:
articles_transacted.append(d.article)
-Nous pouvons placer ce code dans un n'importe quel module Python accessible. La route est définie dans `hooks.py`, donc
-
-so for nso besoins, nos placerons ce code dans `library_management/tasks.py`.
+Nous pouvons placer ce code dans un n'importe quel module Python accessible. La route est définie dans `hooks.py`, dans cet exemple nos placerons ce code dans `library_management/tasks.py`.
Note:
diff --git a/frappe/docs/user/fr/tutorial/users-and-records.md b/frappe/docs/user/fr/tutorial/users-and-records.md
index 20302ab91a..e0a714c9c4 100644
--- a/frappe/docs/user/fr/tutorial/users-and-records.md
+++ b/frappe/docs/user/fr/tutorial/users-and-records.md
@@ -1,11 +1,11 @@
# Créer des utilisateurs et des enregistrements
-Maintenant que nous avons définis des modèles, nous pouvons créér des enregistrements directemnt depuis l'interface. Vous
-n'avez pas à créer des vues ! Les vues dans Frappe sont automatiquements créees à partir des propriétés de vos **DocTypes**.
+Maintenant que nous avons définis des modèles, nous pouvons créér des enregistrements directement depuis l'interface. Vous
+n'avez pas à créer des vues ! Les vues dans Frappe sont automatiquements créées à partir des propriétés de vos **DocTypes**.
### 4.1 Créer des utilisateurs
-Afin de créer des enregistreents, nous avons tout d'abord besoin de créer un utilisateur. Rendez-vous sur:
+Afin de créer des enregistrements, nous avons tout d'abord besoin de créer un utilisateur. Rendez-vous sur:
> Setup > Users > User > New
@@ -14,11 +14,11 @@ Saisissez un nom, un prénom ainsi qu'un mot de passe à votre utilisateur pour
-Maintenant deconnectez-vous puis connectez vous avec l'utilisateur que vous venez de créer.
+Maintenant déconnectez-vous puis connectez-vous avec l'utilisateur que vous venez de créer.
### 4.2 Créer des enregistrements
-Vous allez désormais voirs une icones pour notre module de gestion de librairie. Cliquez sur cette icone et vous apercevrez
+Vous allez désormais voir une icone pour notre module de gestion de librairie. Cliquez sur cette icone et vous apercevrez
la page du module:
@@ -29,7 +29,7 @@ Définissons un nouvel Article:
-Le **DocType** que vous avons déinis est transformé en formulaire. Les règles de valdiation seront appliquées selons nos
+Le **DocType** que vous avons définis est transformé en formulaire. Les règles de validation seront appliquées selon nos
définitions. Remplissons le formulaire pour créer notre premier article.
diff --git a/frappe/docs/user/fr/tutorial/web-views.md b/frappe/docs/user/fr/tutorial/web-views.md
index c14e63a170..e96d2a5afc 100644
--- a/frappe/docs/user/fr/tutorial/web-views.md
+++ b/frappe/docs/user/fr/tutorial/web-views.md
@@ -1,4 +1,4 @@
-# es vues web
+# Les vues web
Frappe a deux principaux environnements, le **bureau** et **le web**. Le **bureau** est un environnement riche AJAX alors
que **le web** est une collection plus traditionnelle de fichers HTML pour la consultation publique. Les vues web peuvent
@@ -16,7 +16,7 @@ y a 2 principaux types de templates.
> Cette fonctionnalité est encore en développement.
-Jettons un oeuil aux vues standards:
+Jettons un oeil aux vues standards:
Si vous êtes connecté avec votre utilisateur de test, rendez-vous sur`/article` et vous devriez voir la liste des articles:
@@ -57,10 +57,10 @@ La mise à jour de la liste ressemble à ca !
Frappe permet l'inscription et inclut les inscriptions via Google, Facebook et Github. Quand un utilisateur s'inscrit via
le web, il n'a pas accès à l'interface du desk par defaut.
-> Pour autoriser les utilisateurs à accéder au Desk, ouvrez la configuration de l'utilisateur (Setup > User) et définissez
+> Pour autoriser les utilisateurs à accéder au `Desk`, ouvrez la configuration de l'utilisateur (Setup > User) et définissez
le type d'utilisatuer à "System User".
-Pour les utilisateurs qui n'ont pas accès au desk, nous pouvons définir une page d'accueil ou ils peuvent se connecter via
+Pour les utilisateurs qui n'ont pas accès au `Desk`, nous pouvons définir une page d'accueil ou ils peuvent se connecter via
`hooks.py` le tout en respectant les rôles.
Quand un membre de la librairie se connecte, il doit être redirigé vers la page `article` donc ouvrez le fichier `library_management/hooks.py` et ajoutez:
@@ -69,4 +69,4 @@ Quand un membre de la librairie se connecte, il doit être redirigé vers la pag
"Library Member": "article"
}
-{suite}
+{next}
diff --git a/frappe/email/__init__.py b/frappe/email/__init__.py
index e8159d249d..eeb7823172 100644
--- a/frappe/email/__init__.py
+++ b/frappe/email/__init__.py
@@ -4,30 +4,8 @@
from __future__ import unicode_literals
import frappe
-from frappe.email.email_body import get_email
-from frappe.email.smtp import send
-from frappe.utils import markdown
-
-def sendmail_md(recipients, sender=None, msg=None, subject=None, attachments=None, content=None,
- reply_to=None, cc=(), message_id=None, in_reply_to=None, retry=1):
- """send markdown email"""
- sendmail(recipients, sender, markdown(content or msg), subject, attachments,
- reply_to=reply_to, cc=cc, retry=retry)
-
-def sendmail(recipients, sender='', msg='', subject='[No Subject]', attachments=None, content=None,
- reply_to=None, cc=(), message_id=None, in_reply_to=None, retry=1):
- """send an html email as multipart with attachments and all"""
- mail = get_email(recipients, sender, content or msg, subject, attachments=attachments,
- reply_to=reply_to, cc=cc)
- if message_id:
- mail.set_message_id(message_id)
- if in_reply_to:
- mail.set_in_reply_to(in_reply_to)
-
- send(mail, retry=retry)
-
def sendmail_to_system_managers(subject, content):
- send(get_email(get_system_managers(), None, content, subject))
+ frappe.sendmail(recipients=get_system_managers(), subject=subject, content=content)
@frappe.whitelist()
def get_contact_list(doctype, fieldname, txt):
diff --git a/frappe/email/doctype/auto_email_report/auto_email_report.json b/frappe/email/doctype/auto_email_report/auto_email_report.json
index 2c8ecf191e..03a28175bd 100644
--- a/frappe/email/doctype/auto_email_report/auto_email_report.json
+++ b/frappe/email/doctype/auto_email_report/auto_email_report.json
@@ -10,6 +10,7 @@
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
@@ -23,6 +24,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Report",
"length": 0,
"no_copy": 0,
@@ -32,6 +34,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -51,6 +54,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Based on Permissions For User",
"length": 0,
"no_copy": 0,
@@ -60,6 +64,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -79,6 +84,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Enabled",
"length": 0,
"no_copy": 0,
@@ -87,6 +93,7 @@
"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,
@@ -106,6 +113,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Send only if there is any data",
"length": 0,
"no_copy": 0,
@@ -114,6 +122,7 @@
"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,
@@ -132,6 +141,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -139,6 +149,7 @@
"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,
@@ -157,6 +168,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Report Type",
"length": 0,
"no_copy": 0,
@@ -166,6 +178,7 @@
"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,
@@ -186,6 +199,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "No of Rows (Max 500)",
"length": 0,
"no_copy": 0,
@@ -194,6 +208,7 @@
"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,
@@ -213,6 +228,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Report Filters",
"length": 0,
"no_copy": 0,
@@ -221,6 +237,7 @@
"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,
@@ -239,6 +256,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Filters Display",
"length": 0,
"no_copy": 0,
@@ -247,6 +265,7 @@
"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,
@@ -265,6 +284,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Filters",
"length": 0,
"no_copy": 0,
@@ -273,6 +293,7 @@
"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,
@@ -291,6 +312,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Email Settings",
"length": 0,
"no_copy": 0,
@@ -299,6 +321,7 @@
"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,
@@ -317,6 +340,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Email To",
"length": 0,
"no_copy": 0,
@@ -325,6 +349,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -345,6 +370,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Day of Week",
"length": 0,
"no_copy": 0,
@@ -354,6 +380,7 @@
"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,
@@ -372,6 +399,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -379,6 +407,7 @@
"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,
@@ -397,6 +426,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Frequency",
"length": 0,
"no_copy": 0,
@@ -406,6 +436,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -424,6 +455,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Format",
"length": 0,
"no_copy": 0,
@@ -433,6 +465,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -451,6 +484,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Message",
"length": 0,
"no_copy": 0,
@@ -459,6 +493,7 @@
"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,
@@ -477,6 +512,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Message",
"length": 0,
"no_copy": 0,
@@ -485,6 +521,7 @@
"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,
@@ -502,7 +539,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-09-23 01:06:56.963190",
+ "modified": "2016-11-07 05:50:31.903959",
"modified_by": "Administrator",
"module": "Email",
"name": "Auto Email Report",
@@ -519,6 +556,7 @@
"export": 1,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
@@ -539,6 +577,7 @@
"export": 1,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
diff --git a/frappe/email/doctype/email_account/email_account.js b/frappe/email/doctype/email_account/email_account.js
index f379ca1499..d370144b66 100644
--- a/frappe/email/doctype/email_account/email_account.js
+++ b/frappe/email/doctype/email_account/email_account.js
@@ -15,6 +15,20 @@ frappe.email_defaults = {
"smtp_port": 587,
"use_tls": 1
},
+ "Sendgrid": {
+ "enable_outgoing": 0,
+ "enable_outgoing": 1,
+ "smtp_server": "smtp.sendgrid.net",
+ "smtp_port": 587,
+ "use_tls": 1
+ },
+ "SparkPost": {
+ "enable_incoming": 0,
+ "enable_outgoing": 1,
+ "smtp_server": "smtp.sparkpostmail.com",
+ "smtp_port": 587,
+ "use_tls": 1
+ },
"Yahoo Mail": {
"email_server": "pop.mail.yahoo.com",
"use_ssl": 1,
diff --git a/frappe/email/doctype/email_account/email_account.json b/frappe/email/doctype/email_account/email_account.json
index ac90fd77eb..3271f39a6a 100644
--- a/frappe/email/doctype/email_account/email_account.json
+++ b/frappe/email/doctype/email_account/email_account.json
@@ -3,22 +3,28 @@
"allow_import": 0,
"allow_rename": 1,
"autoname": "field:email_account_name",
+ "beta": 0,
"creation": "2014-09-11 12:04:34.163728",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Setup",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "email_settings",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -27,6 +33,7 @@
"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,
@@ -37,21 +44,25 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "service",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Service",
"length": 0,
"no_copy": 0,
- "options": "\nGMail\nYahoo Mail\nOutlook.com\nYandex.Mail",
+ "options": "\nGMail\nSendgrid\nSparkPost\nYahoo Mail\nOutlook.com\nYandex.Mail",
"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,
@@ -62,12 +73,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "email_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Email Id",
"length": 0,
"no_copy": 0,
@@ -77,6 +91,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -87,12 +102,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "login_id_is_different",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Login Id is Different",
"length": 0,
"no_copy": 0,
@@ -101,6 +119,7 @@
"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,
@@ -111,13 +130,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "login_id_is_different",
"fieldname": "login_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Login Id",
"length": 0,
"no_copy": 0,
@@ -126,6 +148,7 @@
"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,
@@ -136,12 +159,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "password",
"fieldtype": "Password",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Password",
"length": 0,
"no_copy": 0,
@@ -150,6 +176,7 @@
"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,
@@ -160,13 +187,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"description": "e.g. \"Support\", \"Sales\", \"Jerry Yang\"",
"fieldname": "email_account_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Email Account Name",
"length": 0,
"no_copy": 0,
@@ -175,6 +205,7 @@
"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,
@@ -185,12 +216,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "mailbox_settings",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -199,6 +233,7 @@
"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,
@@ -209,14 +244,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "",
"description": "Check this to pull emails from your mailbox",
"fieldname": "enable_incoming",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Enable Incoming",
"length": 0,
"no_copy": 0,
@@ -225,6 +263,7 @@
"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,
@@ -235,13 +274,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "",
"fieldname": "use_imap",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Use IMAP",
"length": 0,
"no_copy": 0,
@@ -250,6 +292,7 @@
"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,
@@ -260,14 +303,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_incoming",
"description": "e.g. pop.gmail.com / imap.gmail.com",
"fieldname": "email_server",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Email Server",
"length": 0,
"no_copy": 0,
@@ -276,6 +322,7 @@
"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,
@@ -286,13 +333,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_incoming",
"fieldname": "use_ssl",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Use SSL",
"length": 0,
"no_copy": 0,
@@ -301,6 +351,7 @@
"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,
@@ -311,6 +362,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "1",
"depends_on": "enable_incoming",
"description": "Ignore attachments over this size",
@@ -318,8 +370,10 @@
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Attachment Limit (MB)",
"length": 0,
"no_copy": 0,
@@ -328,6 +382,7 @@
"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,
@@ -338,14 +393,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_incoming",
"description": "Append as communication against this DocType (must have fields, \"Status\", \"Subject\")",
"fieldname": "append_to",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Append To",
"length": 0,
"no_copy": 0,
@@ -355,6 +413,7 @@
"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,
@@ -365,14 +424,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_incoming",
"description": "e.g. replies@yourcomany.com. All replies will come to this inbox.",
"fieldname": "default_incoming",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Default Incoming",
"length": 0,
"no_copy": 0,
@@ -381,6 +443,7 @@
"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,
@@ -391,13 +454,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_incoming",
"fieldname": "section_break_13",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -405,6 +471,7 @@
"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,
@@ -415,12 +482,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "notify_if_unreplied",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Notify if unreplied",
"length": 0,
"no_copy": 0,
@@ -429,6 +499,7 @@
"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,
@@ -439,14 +510,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "30",
"depends_on": "notify_if_unreplied",
"fieldname": "unreplied_for_mins",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Notify if unreplied for (in mins)",
"length": 0,
"no_copy": 0,
@@ -455,6 +529,7 @@
"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,
@@ -465,14 +540,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "notify_if_unreplied",
"description": "Email Ids",
"fieldname": "send_notification_to",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Send Notification to",
"length": 0,
"no_copy": 0,
@@ -481,6 +559,7 @@
"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,
@@ -491,12 +570,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "outgoing_mail_settings",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -505,6 +587,7 @@
"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,
@@ -515,14 +598,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "",
"description": "SMTP Settings for outgoing emails",
"fieldname": "enable_outgoing",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Enable Outgoing",
"length": 0,
"no_copy": 0,
@@ -531,6 +617,7 @@
"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,
@@ -541,14 +628,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_outgoing",
"description": "e.g. smtp.gmail.com",
"fieldname": "smtp_server",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "SMTP Server",
"length": 0,
"no_copy": 0,
@@ -557,6 +647,7 @@
"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,
@@ -567,13 +658,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_outgoing",
"fieldname": "use_tls",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Use TLS",
"length": 0,
"no_copy": 0,
@@ -582,6 +676,7 @@
"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,
@@ -592,14 +687,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_outgoing",
"description": "If non standard port (e.g. 587)",
"fieldname": "smtp_port",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Port",
"length": 0,
"no_copy": 0,
@@ -608,6 +706,7 @@
"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,
@@ -618,14 +717,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_outgoing",
"description": "Notifications and bulk mails will be sent from this outgoing server.",
"fieldname": "default_outgoing",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Default Outgoing",
"length": 0,
"no_copy": 0,
@@ -634,6 +736,7 @@
"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,
@@ -644,14 +747,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_outgoing",
"description": "Uses the Email ID mentioned in this Account as the Sender for all emails sent using this Account. ",
"fieldname": "always_use_account_email_id_as_sender",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Always use Account's Email ID as Sender",
"length": 0,
"no_copy": 0,
@@ -660,6 +766,7 @@
"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,
@@ -670,12 +777,44 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
+ "default": "1",
+ "fieldname": "send_unsubscribe_message",
+ "fieldtype": "Check",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Send unsubscribe message 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,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
"fieldname": "signature_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -684,6 +823,7 @@
"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,
@@ -694,13 +834,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "",
"fieldname": "add_signature",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Add Signature",
"length": 0,
"no_copy": 0,
@@ -709,6 +852,7 @@
"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,
@@ -719,13 +863,16 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "add_signature",
"fieldname": "signature",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Signature",
"length": 0,
"no_copy": 0,
@@ -734,6 +881,7 @@
"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,
@@ -744,12 +892,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "auto_reply",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -758,6 +909,7 @@
"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,
@@ -768,12 +920,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "enable_auto_reply",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Enable Auto Reply",
"length": 0,
"no_copy": 0,
@@ -782,6 +937,7 @@
"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,
@@ -792,14 +948,17 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"depends_on": "enable_auto_reply",
"description": "ProTip: Add Reference: {{ reference_doctype }} {{ reference_name }} to send document reference",
"fieldname": "auto_reply_message",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Auto Reply Message",
"length": 0,
"no_copy": 0,
@@ -808,6 +967,7 @@
"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,
@@ -818,12 +978,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "set_footer",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -832,6 +995,7 @@
"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,
@@ -842,12 +1006,15 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "footer",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Footer",
"length": 0,
"no_copy": 0,
@@ -856,6 +1023,7 @@
"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,
@@ -865,15 +1033,16 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-inbox",
+ "icon": "fa fa-inbox",
"idx": 0,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-02-08 02:39:33.273073",
+ "modified": "2016-12-06 01:34:36.359836",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Account",
@@ -890,6 +1059,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
@@ -901,8 +1071,10 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/frappe/email/doctype/email_account/email_account.py b/frappe/email/doctype/email_account/email_account.py
index 7e0398d20d..7e04c8f609 100755
--- a/frappe/email/doctype/email_account/email_account.py
+++ b/frappe/email/doctype/email_account/email_account.py
@@ -55,7 +55,7 @@ class EmailAccount(Document):
if not frappe.local.flags.in_install and not frappe.local.flags.in_patch:
if self.enable_incoming:
- self.get_server()
+ self.get_incoming_server()
if self.enable_outgoing:
self.check_smtp()
@@ -98,14 +98,14 @@ class EmailAccount(Document):
or self.email_id,
server = self.smtp_server,
port = cint(self.smtp_port),
- use_ssl = cint(self.use_tls)
+ use_tls = cint(self.use_tls)
)
if self.password:
server.password = self.get_password()
server.sess
- def get_server(self, in_receive=False):
- """Returns logged in POP3 connection object."""
+ def get_incoming_server(self, in_receive=False):
+ """Returns logged in POP3/IMAP connection object."""
args = frappe._dict({
"host": self.email_server,
@@ -184,7 +184,7 @@ class EmailAccount(Document):
if frappe.local.flags.in_test:
incoming_mails = test_mails
else:
- email_server = self.get_server(in_receive=True)
+ email_server = self.get_incoming_server(in_receive=True)
if not email_server:
return
@@ -225,6 +225,7 @@ class EmailAccount(Document):
"doctype": "Communication",
"subject": email.subject,
"content": email.content,
+ 'text_content': email.text_content,
"sent_or_received": "Received",
"sender_full_name": email.from_real_name,
"sender": email.from_email,
@@ -270,46 +271,57 @@ class EmailAccount(Document):
If no thread id is found and `append_to` is set for the email account,
it will create a new parent transaction (e.g. Issue)"""
- in_reply_to = (email.mail.get("In-Reply-To") or "").strip(" <>")
parent = None
- if self.append_to:
- # set subject_field and sender_field
- meta_module = frappe.get_meta_module(self.append_to)
- meta = frappe.get_meta(self.append_to)
- subject_field = getattr(meta_module, "subject_field", "subject")
- if not meta.get_field(subject_field):
- subject_field = None
- sender_field = getattr(meta_module, "sender_field", "sender")
- if not meta.get_field(sender_field):
- sender_field = None
+ parent = self.find_parent_from_in_reply_to(communication, email)
- if in_reply_to:
- if "@{0}".format(frappe.local.site) in in_reply_to:
+ if not parent:
+ self.set_sender_field_and_subject_field()
- # reply to a communication sent from the system
- in_reply_to, domain = in_reply_to.split("@", 1)
+ if not parent and self.append_to:
+ parent = self.find_parent_based_on_subject_and_sender(communication, email)
- if frappe.db.exists("Communication", in_reply_to):
- parent = frappe.get_doc("Communication", in_reply_to)
+ if not parent and self.append_to and self.append_to!="Communication":
+ parent = self.create_new_parent(communication, email)
- # set in_reply_to of current communication
- communication.in_reply_to = in_reply_to
+ if parent:
+ communication.reference_doctype = parent.doctype
+ communication.reference_name = parent.name
- if parent.reference_name:
- parent = frappe.get_doc(parent.reference_doctype,
- parent.reference_name)
+ # check if message is notification and disable notifications for this message
+ references = email.mail.get("References")
+ if references:
+ if "notification" in references:
+ communication.unread_notification_sent = 1
- if not parent and self.append_to and sender_field:
- if subject_field:
+ def set_sender_field_and_subject_field(self):
+ '''Identify the sender and subject fields from the `append_to` DocType'''
+ # set subject_field and sender_field
+ meta_module = frappe.get_meta_module(self.append_to)
+ meta = frappe.get_meta(self.append_to)
+
+ self.subject_field = getattr(meta_module, "subject_field", "subject")
+ if not meta.get_field(self.subject_field):
+ self.subject_field = None
+
+ self.sender_field = getattr(meta_module, "sender_field", "sender")
+ if not meta.get_field(self.sender_field):
+ self.sender_field = None
+
+ def find_parent_based_on_subject_and_sender(self, communication, email):
+ '''Find parent document based on subject and sender match'''
+ parent = None
+
+ if self.append_to and self.sender_field:
+ if self.subject_field:
# try and match by subject and sender
# if sent by same sender with same subject,
# append it to old coversation
- subject = strip(re.sub("^\s*(Re|RE)[^:]*:\s*", "", email.subject))
+ subject = strip(re.sub("(^\s*(Fw|FW|fwd)[^:]*:|\s*(Re|RE)[^:]*:\s*)*", "", email.subject))
parent = frappe.db.get_all(self.append_to, filters={
- sender_field: email.from_email,
- subject_field: ("like", "%{0}%".format(subject)),
+ self.sender_field: email.from_email,
+ self.subject_field: ("like", "%{0}%".format(subject)),
"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
}, fields="name")
@@ -318,48 +330,85 @@ class EmailAccount(Document):
# and subject is atleast 10 chars long
if not parent and len(subject) > 10 and is_system_user(email.from_email):
parent = frappe.db.get_all(self.append_to, filters={
- subject_field: ("like", "%{0}%".format(subject)),
+ self.subject_field: ("like", "%{0}%".format(subject)),
"creation": (">", (get_datetime() - relativedelta(days=10)).strftime(DATE_FORMAT))
}, fields="name")
if parent:
parent = frappe.get_doc(self.append_to, parent[0].name)
+ return parent
- if not parent and self.append_to and self.append_to!="Communication":
- # no parent found, but must be tagged
- # insert parent type doc
- parent = frappe.new_doc(self.append_to)
- if subject_field:
- parent.set(subject_field, email.subject)
+ def create_new_parent(self, communication, email):
+ '''If no parent found, create a new reference document'''
- if sender_field:
- parent.set(sender_field, email.from_email)
+ # no parent found, but must be tagged
+ # insert parent type doc
+ parent = frappe.new_doc(self.append_to)
- parent.flags.ignore_mandatory = True
+ if self.subject_field:
+ parent.set(self.subject_field, frappe.as_unicode(email.subject))
- try:
- parent.insert(ignore_permissions=True)
- except frappe.DuplicateEntryError:
- # try and find matching parent
- parent_name = frappe.db.get_value(self.append_to, {sender_field: email.from_email})
- if parent_name:
- parent.name = parent_name
- else:
- parent = None
+ if self.sender_field:
+ parent.set(self.sender_field, frappe.as_unicode(email.from_email))
- # NOTE if parent isn't found and there's no subject match, it is likely that it is a new conversation thread and hence is_first = True
- communication.is_first = True
+ parent.flags.ignore_mandatory = True
- if parent:
- communication.reference_doctype = parent.doctype
- communication.reference_name = parent.name
+ try:
+ parent.insert(ignore_permissions=True)
+ except frappe.DuplicateEntryError:
+ # try and find matching parent
+ parent_name = frappe.db.get_value(self.append_to, {self.sender_field: email.from_email})
+ if parent_name:
+ parent.name = parent_name
+ else:
+ parent = None
+
+ # NOTE if parent isn't found and there's no subject match, it is likely that it is a new conversation thread and hence is_first = True
+ communication.is_first = True
+
+ return parent
+
+ def find_parent_from_in_reply_to(self, communication, email):
+ '''Returns parent reference if embedded in In-Reply-To header
+
+ Message-ID is formatted as `{message_id}@{site}`'''
+ parent = None
+ in_reply_to = (email.mail.get("In-Reply-To") or "").strip(" <>")
+
+ if in_reply_to and "@{0}".format(frappe.local.site) in in_reply_to:
+ # reply to a communication sent from the system
+ email_queue = frappe.db.get_value('Email Queue', dict(message_id=in_reply_to), ['reference_doctype', 'reference_name'])
+ if email_queue:
+ parent_doctype, parent_name = email_queue
+ else:
+ reference, domain = in_reply_to.split("@", 1)
+ parent_doctype, parent_name = 'Communication', reference
+
+ if frappe.db.exists(parent_doctype, parent_name):
+ parent = frappe.get_doc(parent_doctype, parent_name)
+
+ # set in_reply_to of current communication
+ if parent_doctype=='Communication':
+ communication.in_reply_to = parent_name
+
+ if parent.reference_name:
+ # the true parent is the communication parent
+ parent = frappe.get_doc(parent.reference_doctype,
+ parent.reference_name)
+
+ return parent
def send_auto_reply(self, communication, email):
"""Send auto reply if set."""
if self.enable_auto_reply:
set_incoming_outgoing_accounts(communication)
+ if self.send_unsubscribe_message:
+ unsubscribe_message = _("Leave this conversation")
+ else:
+ unsubscribe_message = ""
+
frappe.sendmail(recipients = [email.from_email],
sender = self.email_id,
reply_to = communication.incoming_email_account,
@@ -368,9 +417,8 @@ class EmailAccount(Document):
frappe.get_template("templates/emails/auto_reply.html").render(communication.as_dict()),
reference_doctype = communication.reference_doctype,
reference_name = communication.reference_name,
- message_id = communication.name,
in_reply_to = email.mail.get("Message-Id"), # send back the Message-Id as In-Reply-To
- unsubscribe_message = _("Leave this conversation"))
+ unsubscribe_message = unsubscribe_message)
def get_unreplied_notification_emails(self):
"""Return list of emails listed"""
diff --git a/frappe/email/doctype/email_account/test_email_account.py b/frappe/email/doctype/email_account/test_email_account.py
index 57645a2b29..5513c7dc42 100644
--- a/frappe/email/doctype/email_account/test_email_account.py
+++ b/frappe/email/doctype/email_account/test_email_account.py
@@ -54,8 +54,8 @@ class TestEmailAccount(unittest.TestCase):
frappe.delete_doc("File", existing_file.name)
delete_file_from_filesystem(existing_file)
- with open(os.path.join(os.path.dirname(__file__), "test_mails", "incoming-2.raw"), "r") as f:
- test_mails = [f.read()]
+ with open(os.path.join(os.path.dirname(__file__), "test_mails", "incoming-2.raw"), "r") as testfile:
+ test_mails = [testfile.read()]
email_account = frappe.get_doc("Email Account", "_Test Email Account 1")
email_account.receive(test_mails=test_mails)
@@ -170,5 +170,30 @@ class TestEmailAccount(unittest.TestCase):
self.assertEquals(comm_list[0].reference_doctype, comm_list[1].reference_doctype)
self.assertEquals(comm_list[0].reference_name, comm_list[1].reference_name)
+ def test_threading_by_message_id(self):
+ frappe.db.sql("""delete from tabCommunication""")
+ frappe.db.sql("""delete from `tabEmail Queue`""")
+ # reference document for testing
+ event = frappe.get_doc(dict(doctype='Event', subject='test-message')).insert()
+ # send a mail against this
+ frappe.sendmail(recipients='test@example.com', subject='test message for threading',
+ message='testing', reference_doctype=event.doctype, reference_name=event.name)
+
+ last_mail = frappe.get_doc('Email Queue', dict(reference_name=event.name))
+
+ # get test mail with message-id as in-reply-to
+ with open(os.path.join(os.path.dirname(__file__), "test_mails", "reply-4.raw"), "r") as f:
+ test_mails = [f.read().replace('{{ message_id }}', last_mail.message_id)]
+
+ # pull the mail
+ email_account = frappe.get_doc("Email Account", "_Test Email Account 1")
+ email_account.receive(test_mails=test_mails)
+
+ comm_list = frappe.get_all("Communication", filters={"sender":"test_sender@example.com"},
+ fields=["name", "reference_doctype", "reference_name"])
+
+ # check if threaded correctly
+ self.assertEquals(comm_list[0].reference_doctype, event.doctype)
+ self.assertEquals(comm_list[0].reference_name, event.name)
diff --git a/frappe/email/doctype/email_account/test_mails/incoming-4.raw b/frappe/email/doctype/email_account/test_mails/incoming-4.raw
index 1854afb554..99374885cb 100644
--- a/frappe/email/doctype/email_account/test_mails/incoming-4.raw
+++ b/frappe/email/doctype/email_account/test_mails/incoming-4.raw
@@ -8,7 +8,7 @@ Received: from 23-59-23-10.perm.iinet.net.au ([23.59.23.10]:56280)
for test_receiver@example.com; Tue, 09 Feb 2016 14:53:22 +0800
From:
To:
-Subject: test email
+Subject: test email
Date: Tue, 9 Feb 2016 14:53:13 +0800
Message-ID: <000001d16306$8b9e5c60$a2db1520$@ia-group.com.au>
MIME-Version: 1.0
@@ -30,7 +30,7 @@ Content-Type: text/plain;
charset="us-ascii"
Content-Transfer-Encoding: 7bit
-4th test email
+4th test email
------=_NextPart_001_0006_01D16349.99C37120
diff --git a/frappe/email/doctype/email_account/test_mails/reply-4.raw b/frappe/email/doctype/email_account/test_mails/reply-4.raw
new file mode 100644
index 0000000000..7cc08c9f19
--- /dev/null
+++ b/frappe/email/doctype/email_account/test_mails/reply-4.raw
@@ -0,0 +1,75 @@
+From:
+Content-Type: multipart/alternative;
+ boundary="Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361"
+Message-Id: <07D687F6-10AA-4B9F-82DE-27753096164E@gmail.com>
+Mime-Version: 1.0 (Mac OS X Mail 9.3 \(3124\))
+X-Smtp-Server: 73CC8281-7E8F-4B47-8324-D5DA86EEDD4F
+Subject: Re: What did you work on today?
+Date: Thu, 10 Nov 2016 16:04:43 +0530
+X-Universally-Unique-Identifier: A4D9669F-179C-42D8-A3D3-AA6A8C49A6F2
+References: {{ message_id }}
+To: test_in@iwebnotes.com
+In-Reply-To: {{ message_id }}
+
+
+--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361
+Content-Transfer-Encoding: quoted-printable
+Content-Type: text/plain;
+ charset=us-ascii
+
+Testing another reply!
+
+> On 10-Nov-2016, at 3:20 PM, Frappe wrote:
+>=20
+> Please share what did you do today. If you reply by midnight, your =
+response will be recorded!
+>=20
+> This email was sent to rmehta@gmail.com
+> Unsubscribe from this list =
+
+> Sent via ERPNext
+
+
+--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361
+Content-Transfer-Encoding: 7bit
+Content-Type: text/html;
+ charset=us-ascii
+
+ Testing another reply!
+
+
+
+
+
What did you work on today?
+
+
+
+
Please share what did you do today. If you reply by midnight, your response will be recorded!
+
+
+
+
+
+
+
+
+
+
+--Apple-Mail=_29597CF7-20DD-4184-B3FA-85582C5C4361--
diff --git a/frappe/email/doctype/email_alert/email_alert.json b/frappe/email/doctype/email_alert/email_alert.json
index 6b34548265..e41d0019f9 100755
--- a/frappe/email/doctype/email_alert/email_alert.json
+++ b/frappe/email/doctype/email_alert/email_alert.json
@@ -33,6 +33,7 @@
"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,
@@ -59,6 +60,7 @@
"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,
@@ -86,6 +88,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -113,6 +116,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
@@ -140,6 +144,7 @@
"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,
@@ -169,6 +174,7 @@
"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,
@@ -195,6 +201,7 @@
"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,
@@ -222,6 +229,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
@@ -251,6 +259,7 @@
"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,
@@ -279,6 +288,7 @@
"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,
@@ -308,6 +318,7 @@
"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,
@@ -336,6 +347,7 @@
"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,
@@ -362,6 +374,7 @@
"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,
@@ -390,6 +403,7 @@
"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,
@@ -415,6 +429,7 @@
"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,
@@ -441,6 +456,7 @@
"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,
@@ -467,6 +483,7 @@
"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,
@@ -494,6 +511,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -520,6 +538,7 @@
"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,
@@ -548,6 +567,7 @@
"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,
@@ -575,6 +595,7 @@
"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,
@@ -602,6 +623,7 @@
"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,
@@ -629,6 +651,7 @@
"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,
@@ -638,7 +661,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-envelope",
+ "icon": "fa fa-envelope",
"idx": 0,
"image_view": 0,
"in_create": 0,
@@ -648,7 +671,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-11-03 01:29:48.678270",
+ "modified": "2016-11-07 05:21:43.253739",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Alert",
diff --git a/frappe/email/doctype/email_group_member/email_group_member.json b/frappe/email/doctype/email_group_member/email_group_member.json
index 52ee386fa1..6d6332c204 100644
--- a/frappe/email/doctype/email_group_member/email_group_member.json
+++ b/frappe/email/doctype/email_group_member/email_group_member.json
@@ -10,11 +10,13 @@
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "email_group",
"fieldtype": "Link",
"hidden": 0,
@@ -22,6 +24,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Email Group",
"length": 0,
"no_copy": 0,
@@ -31,6 +34,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -41,6 +45,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "email",
"fieldtype": "Data",
"hidden": 0,
@@ -48,6 +53,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Email",
"length": 0,
"no_copy": 0,
@@ -56,6 +62,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -66,6 +73,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "unsubscribed",
"fieldtype": "Check",
"hidden": 0,
@@ -73,6 +81,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Unsubscribed",
"length": 0,
"no_copy": 0,
@@ -81,6 +90,7 @@
"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,
@@ -98,7 +108,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-07-25 05:24:23.790234",
+ "modified": "2016-11-07 05:28:25.001760",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Group Member",
@@ -115,6 +125,7 @@
"export": 1,
"if_owner": 0,
"import": 1,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
diff --git a/frappe/email/doctype/email_queue/email_queue.js b/frappe/email/doctype/email_queue/email_queue.js
index 230582d0a8..acea015687 100644
--- a/frappe/email/doctype/email_queue/email_queue.js
+++ b/frappe/email/doctype/email_queue/email_queue.js
@@ -3,7 +3,7 @@
frappe.ui.form.on("Email Queue", {
refresh: function(frm) {
- if (frm.doc.status==="Not Sent") {
+ if (["Not Sent","Partially Sent"].indexOf(frm.doc.status)!=-1) {
frm.add_custom_button("Send Now", function() {
frappe.call({
method: 'frappe.email.doctype.email_queue.email_queue.send_now',
@@ -17,7 +17,7 @@ frappe.ui.form.on("Email Queue", {
});
}
- if (frm.doc.status==="Error") {
+ if (["Error","Partially Errored"].indexOf(frm.doc.status)!=-1) {
frm.add_custom_button("Retry Sending", function() {
frm.call({
method: "retry_sending",
diff --git a/frappe/email/doctype/email_queue/email_queue.json b/frappe/email/doctype/email_queue/email_queue.json
index 7463ca5cdc..980368baf9 100644
--- a/frappe/email/doctype/email_queue/email_queue.json
+++ b/frappe/email/doctype/email_queue/email_queue.json
@@ -1,308 +1,483 @@
{
- "allow_copy": 0,
- "allow_import": 0,
- "allow_rename": 0,
- "autoname": "hash",
- "beta": 0,
- "creation": "2012-08-02 15:17:28",
- "custom": 0,
- "description": "Email Queue records.",
- "docstatus": 0,
- "doctype": "DocType",
- "document_type": "System",
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "autoname": "hash",
+ "beta": 0,
+ "creation": "2012-08-02 15:17:28",
+ "custom": 0,
+ "description": "Email Queue records.",
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "System",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "sender",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Sender",
- "length": 0,
- "no_copy": 0,
- "options": "Email",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "sender",
+ "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": "Sender",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Email",
+ "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,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "recipient",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Recipient",
- "length": 0,
- "no_copy": 0,
- "options": "Email",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "recipient",
+ "fieldtype": "Table",
+ "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": "Recipient",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Email Queue Recipient",
+ "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,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "message",
- "fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Message",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "show_as_cc",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Show as cc",
+ "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,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "default": "Not Sent",
- "fieldname": "status",
- "fieldtype": "Select",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 1,
- "label": "Status",
- "length": 0,
- "no_copy": 0,
- "options": "\nNot Sent\nSending\nSent\nError\nExpired",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "message",
+ "fieldtype": "Code",
+ "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": "Message",
+ "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,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "error",
- "fieldtype": "Code",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Error",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Not Sent",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_global_search": 0,
+ "in_list_view": 1,
+ "in_standard_filter": 1,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nNot Sent\nSending\nSent\nError\nExpired",
+ "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,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "reference_doctype",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Reference DocType",
- "length": 0,
- "no_copy": 0,
- "options": "DocType",
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "error",
+ "fieldtype": "Code",
+ "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": "Error",
+ "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,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "reference_name",
- "fieldtype": "Data",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Reference DocName",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "message_id",
+ "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": "Message ID",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 1,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "communication",
- "fieldtype": "Link",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Communication",
- "length": 0,
- "no_copy": 0,
- "options": "Communication",
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 0,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 1,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "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": "Reference DocType",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "fieldname": "send_after",
- "fieldtype": "Datetime",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Send After",
- "length": 0,
- "no_copy": 1,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_name",
+ "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": "Reference DocName",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
- },
+ },
{
- "allow_on_submit": 0,
- "bold": 0,
- "collapsible": 0,
- "default": "1",
- "fieldname": "priority",
- "fieldtype": "Int",
- "hidden": 0,
- "ignore_user_permissions": 0,
- "ignore_xss_filter": 0,
- "in_filter": 0,
- "in_list_view": 0,
- "label": "Priority",
- "length": 0,
- "no_copy": 0,
- "permlevel": 0,
- "precision": "",
- "print_hide": 0,
- "print_hide_if_no_value": 0,
- "read_only": 1,
- "report_hide": 0,
- "reqd": 0,
- "search_index": 0,
- "set_only_once": 0,
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "communication",
+ "fieldtype": "Link",
+ "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": "Communication",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Communication",
+ "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": 1,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "send_after",
+ "fieldtype": "Datetime",
+ "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 After",
+ "length": 0,
+ "no_copy": 1,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "1",
+ "fieldname": "priority",
+ "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": "Priority",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "unsubscribe_param",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Unsubscribe Param",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "unsubscribe_method",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Unsubscribe Method",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "fieldname": "expose_recipients",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Expose Recipients",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
"unique": 0
}
- ],
- "hide_heading": 0,
- "hide_toolbar": 0,
- "icon": "icon-envelope",
- "idx": 1,
- "image_view": 0,
- "in_create": 1,
- "in_dialog": 0,
- "is_submittable": 0,
- "issingle": 0,
- "istable": 0,
- "max_attachments": 0,
- "modified": "2016-06-22 12:23:10.621244",
- "modified_by": "Administrator",
- "module": "Email",
- "name": "Email Queue",
- "owner": "Administrator",
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "icon": "fa fa-envelope",
+ "idx": 1,
+ "image_view": 0,
+ "in_create": 1,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 0,
+ "max_attachments": 0,
+ "modified": "2016-12-13 20:43:56.976928",
+ "modified_by": "Administrator",
+ "module": "Email",
+ "name": "Email Queue",
+ "owner": "Administrator",
"permissions": [
{
- "amend": 0,
- "apply_user_permissions": 0,
- "cancel": 0,
- "create": 0,
- "delete": 1,
- "email": 1,
- "export": 0,
- "if_owner": 0,
- "import": 0,
- "permlevel": 0,
- "print": 1,
- "read": 1,
- "report": 1,
- "role": "System Manager",
- "set_user_permissions": 0,
- "share": 0,
- "submit": 0,
+ "amend": 0,
+ "apply_user_permissions": 0,
+ "cancel": 0,
+ "create": 0,
+ "delete": 1,
+ "email": 1,
+ "export": 0,
+ "if_owner": 0,
+ "import": 0,
+ "is_custom": 0,
+ "permlevel": 0,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "System Manager",
+ "set_user_permissions": 0,
+ "share": 0,
+ "submit": 0,
"write": 0
}
- ],
- "quick_entry": 0,
- "read_only": 0,
- "read_only_onload": 0,
- "sort_order": "DESC",
+ ],
+ "quick_entry": 0,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_order": "DESC",
"track_seen": 0
}
\ No newline at end of file
diff --git a/frappe/email/doctype/email_queue/email_queue.py b/frappe/email/doctype/email_queue/email_queue.py
index b3fc2c29ef..0cbf9d8bc5 100644
--- a/frappe/email/doctype/email_queue/email_queue.py
+++ b/frappe/email/doctype/email_queue/email_queue.py
@@ -22,7 +22,7 @@ class EmailQueue(Document):
@frappe.whitelist()
def retry_sending(name):
doc = frappe.get_doc("Email Queue", name)
- if doc and doc.status == "Error":
+ if doc and (doc.status == "Error" or doc.status == "Partially Errored"):
doc.status = "Not Sent"
doc.save(ignore_permissions=True)
diff --git a/frappe/email/doctype/email_queue_recipient/__init__.py b/frappe/email/doctype/email_queue_recipient/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/email/doctype/email_queue_recipient/email_queue_recipient.json b/frappe/email/doctype/email_queue_recipient/email_queue_recipient.json
new file mode 100644
index 0000000000..68c91bcbab
--- /dev/null
+++ b/frappe/email/doctype/email_queue_recipient/email_queue_recipient.json
@@ -0,0 +1,122 @@
+{
+ "allow_copy": 0,
+ "allow_import": 0,
+ "allow_rename": 0,
+ "beta": 0,
+ "creation": "2016-12-08 12:01:07.993900",
+ "custom": 0,
+ "docstatus": 0,
+ "doctype": "DocType",
+ "document_type": "",
+ "editable_grid": 0,
+ "engine": "InnoDB",
+ "fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "recipient",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Recipient",
+ "length": 0,
+ "no_copy": 0,
+ "options": "Email",
+ "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,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "Not Sent",
+ "fieldname": "status",
+ "fieldtype": "Select",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 1,
+ "label": "Status",
+ "length": 0,
+ "no_copy": 0,
+ "options": "\nNot Sent\nSending\nSent\nError\nExpired",
+ "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,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "error",
+ "fieldtype": "Code",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "label": "Error",
+ "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,
+ "unique": 0
+ }
+ ],
+ "hide_heading": 0,
+ "hide_toolbar": 0,
+ "idx": 0,
+ "image_view": 0,
+ "in_create": 0,
+ "in_dialog": 0,
+ "is_submittable": 0,
+ "issingle": 0,
+ "istable": 1,
+ "max_attachments": 0,
+ "modified": "2016-12-08 14:05:33.578240",
+ "modified_by": "Administrator",
+ "module": "Email",
+ "name": "Email Queue Recipient",
+ "name_case": "",
+ "owner": "Administrator",
+ "permissions": [],
+ "quick_entry": 1,
+ "read_only": 0,
+ "read_only_onload": 0,
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "track_seen": 0
+}
\ No newline at end of file
diff --git a/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py b/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py
new file mode 100644
index 0000000000..42956a1180
--- /dev/null
+++ b/frappe/email/doctype/email_queue_recipient/email_queue_recipient.py
@@ -0,0 +1,10 @@
+# -*- coding: utf-8 -*-
+# Copyright (c) 2015, Frappe Technologies and contributors
+# For license information, please see license.txt
+
+from __future__ import unicode_literals
+import frappe
+from frappe.model.document import Document
+
+class EmailQueueRecipient(Document):
+ pass
diff --git a/frappe/email/doctype/email_unsubscribe/email_unsubscribe.json b/frappe/email/doctype/email_unsubscribe/email_unsubscribe.json
index 798c470319..01211abd82 100644
--- a/frappe/email/doctype/email_unsubscribe/email_unsubscribe.json
+++ b/frappe/email/doctype/email_unsubscribe/email_unsubscribe.json
@@ -9,11 +9,13 @@
"doctype": "DocType",
"document_type": "System",
"editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "email",
"fieldtype": "Data",
"hidden": 0,
@@ -21,6 +23,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Email",
"length": 0,
"no_copy": 0,
@@ -29,6 +32,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -39,6 +43,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "reference_doctype",
"fieldtype": "Link",
"hidden": 0,
@@ -46,6 +51,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Reference DocType",
"length": 0,
"no_copy": 0,
@@ -55,6 +61,7 @@
"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,
@@ -65,6 +72,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"hidden": 0,
@@ -72,6 +80,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Reference Name",
"length": 0,
"no_copy": 0,
@@ -81,6 +90,7 @@
"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,
@@ -91,6 +101,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "global_unsubscribe",
"fieldtype": "Check",
"hidden": 0,
@@ -98,6 +109,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Global Unsubscribe",
"length": 0,
"no_copy": 0,
@@ -106,6 +118,7 @@
"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,
@@ -123,7 +136,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-07-25 05:24:25.970291",
+ "modified": "2016-11-07 05:28:14.622378",
"modified_by": "Administrator",
"module": "Email",
"name": "Email Unsubscribe",
@@ -140,6 +153,7 @@
"export": 1,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
diff --git a/frappe/email/doctype/newsletter/newsletter.js b/frappe/email/doctype/newsletter/newsletter.js
index 2839b9a8ea..8e58b562e7 100644
--- a/frappe/email/doctype/newsletter/newsletter.js
+++ b/frappe/email/doctype/newsletter/newsletter.js
@@ -9,7 +9,7 @@ cur_frm.cscript.refresh = function(doc) {
return $c_obj(doc, 'send_emails', '', function(r) {
cur_frm.refresh();
});
- }, "icon-play", "btn-success");
+ }, "fa fa-play", "btn-success");
}
cur_frm.cscript.setup_dashboard();
diff --git a/frappe/email/doctype/newsletter/newsletter.json b/frappe/email/doctype/newsletter/newsletter.json
index 0790dad9f2..c64ec00bfe 100644
--- a/frappe/email/doctype/newsletter/newsletter.json
+++ b/frappe/email/doctype/newsletter/newsletter.json
@@ -11,6 +11,7 @@
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
@@ -24,6 +25,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 1,
"label": "Email Group",
"length": 0,
"no_copy": 0,
@@ -33,6 +35,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -51,6 +54,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Subject",
"length": 0,
"no_copy": 0,
@@ -58,6 +62,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -77,6 +82,7 @@
"ignore_xss_filter": 1,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Sender",
"length": 0,
"no_copy": 1,
@@ -84,6 +90,7 @@
"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,
@@ -102,6 +109,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Email Sent?",
"length": 0,
"no_copy": 1,
@@ -109,6 +117,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -127,6 +136,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -134,6 +144,7 @@
"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,
@@ -152,6 +163,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Message",
"length": 0,
"no_copy": 0,
@@ -159,6 +171,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -178,6 +191,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -185,6 +199,7 @@
"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,
@@ -204,6 +219,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Test Email Id",
"length": 0,
"no_copy": 0,
@@ -211,6 +227,7 @@
"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,
@@ -229,6 +246,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Test",
"length": 0,
"no_copy": 0,
@@ -237,6 +255,7 @@
"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,
@@ -246,7 +265,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-envelope",
+ "icon": "fa fa-envelope",
"idx": 1,
"image_view": 0,
"in_create": 0,
@@ -256,7 +275,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
- "modified": "2016-09-13 11:33:47.892910",
+ "modified": "2016-11-07 05:54:45.790992",
"modified_by": "Administrator",
"module": "Email",
"name": "Newsletter",
@@ -272,6 +291,7 @@
"export": 1,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 1,
"read": 1,
diff --git a/frappe/email/doctype/newsletter/test_newsletter.py b/frappe/email/doctype/newsletter/test_newsletter.py
index a379be27e1..6394d10f33 100644
--- a/frappe/email/doctype/newsletter/test_newsletter.py
+++ b/frappe/email/doctype/newsletter/test_newsletter.py
@@ -20,21 +20,25 @@ class TestNewsletter(unittest.TestCase):
def test_send(self):
self.send_newsletter()
- self.assertEquals(len(frappe.get_all("Email Queue")), 3)
+ self.assertEquals(len(frappe.get_all("Email Queue")), 1)
+ self.assertEquals(len(frappe.get_all("Email Queue Recipient")), 3)
def test_unsubscribe(self):
# test unsubscribe
self.send_newsletter()
-
+ from frappe.email.queue import flush
+ flush(from_test=True)
email = unquote(frappe.local.flags.signed_query_string.split("email=")[1].split("&")[0])
unsubscribe(email, "_Test Email Group")
self.send_newsletter()
- self.assertEquals(len(frappe.get_all("Email Queue")), 2)
+ self.assertEquals(len(frappe.get_all("Email Queue")), 1)
+ self.assertEquals(len(frappe.get_all("Email Queue Recipient")), 2)
def send_newsletter(self):
frappe.db.sql("delete from `tabEmail Queue`")
+ frappe.db.sql("delete from `tabEmail Queue Recipient`")
frappe.delete_doc("Newsletter", "_Test Newsletter")
newsletter = frappe.get_doc({
"doctype": "Newsletter",
diff --git a/frappe/email/doctype/standard_reply/standard_reply.js b/frappe/email/doctype/standard_reply/standard_reply.js
new file mode 100644
index 0000000000..a06fca1cba
--- /dev/null
+++ b/frappe/email/doctype/standard_reply/standard_reply.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2016, Frappe Technologies and contributors
+// For license information, please see license.txt
+
+frappe.ui.form.on('Standard Reply', {
+ refresh: function(frm) {
+
+ }
+});
diff --git a/frappe/email/doctype/standard_reply/standard_reply.json b/frappe/email/doctype/standard_reply/standard_reply.json
index a7486dba12..dac87add18 100644
--- a/frappe/email/doctype/standard_reply/standard_reply.json
+++ b/frappe/email/doctype/standard_reply/standard_reply.json
@@ -3,16 +3,20 @@
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:subject",
+ "beta": 0,
"creation": "2014-06-19 05:20:26.331041",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
+ "editable_grid": 0,
+ "engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "subject",
"fieldtype": "Data",
"hidden": 0,
@@ -20,6 +24,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Subject",
"length": 0,
"no_copy": 0,
@@ -27,6 +32,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -37,6 +43,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "response",
"fieldtype": "Text Editor",
"hidden": 0,
@@ -44,6 +51,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Response",
"length": 0,
"no_copy": 0,
@@ -51,6 +59,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -61,6 +70,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"default": "user",
"fieldname": "owner",
"fieldtype": "Link",
@@ -69,6 +79,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Owner",
"length": 0,
"no_copy": 0,
@@ -77,6 +88,7 @@
"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,
@@ -87,6 +99,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"hidden": 0,
@@ -94,6 +107,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -101,6 +115,7 @@
"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,
@@ -111,6 +126,7 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "standard_reply_help",
"fieldtype": "HTML",
"hidden": 0,
@@ -118,15 +134,17 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Standard Reply Help",
"length": 0,
"no_copy": 0,
- "options": "Standard Reply Example \n\nOrder Overdue\n\nTransaction {{ doc.name }} has exceeded Due Date. Please take necessary action.\n\nDetails\n\n- Customer: {{ doc.customer }}\n- Amount: {{ doc.grand_total }}\n \n\nHow to get fieldnames \n\nThe fieldnames you can use in your standard reply are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)
\n\nTemplating \n\nTemplates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.
\n",
+ "options": "Standard Reply Example \n\nOrder Overdue\n\nTransaction {{ name }} has exceeded Due Date. Please take necessary action.\n\nDetails\n\n- Customer: {{ customer }}\n- Amount: {{ grand_total }}\n \n\nHow to get fieldnames \n\nThe fieldnames you can use in your standard reply are the fields in the document from which you are sending the email. You can find out the fields of any documents via Setup > Customize Form View and selecting the document type (e.g. Sales Invoice)
\n\nTemplating \n\nTemplates are compiled using the Jinja Templating Langauge. To learn more about Jinja, read this documentation.
\n",
"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,
@@ -136,15 +154,16 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-comment",
+ "icon": "fa fa-comment",
"idx": 0,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-03-22 15:25:43.935671",
+ "modified": "2016-11-09 08:06:13.965074",
"modified_by": "Administrator",
"module": "Email",
"name": "Standard Reply",
@@ -161,6 +180,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
@@ -181,6 +201,7 @@
"export": 1,
"if_owner": 0,
"import": 1,
+ "is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
@@ -192,8 +213,10 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/frappe/email/email_body.py b/frappe/email/email_body.py
index 7568d49821..3f7fce0c31 100644
--- a/frappe/email/email_body.py
+++ b/frappe/email/email_body.py
@@ -6,15 +6,15 @@ import frappe
from frappe.utils.pdf import get_pdf
from frappe.email.smtp import get_outgoing_email_account
from frappe.utils import (get_url, scrub_urls, strip, expand_relative_urls, cint,
- split_emails, to_markdown, markdown, encode)
+ split_emails, to_markdown, markdown, encode, random_string)
import email.utils
def get_email(recipients, sender='', msg='', subject='[No Subject]',
text_content = None, footer=None, print_html=None, formatted=None, attachments=None,
- content=None, reply_to=None, cc=(), email_account=None):
+ content=None, reply_to=None, cc=[], email_account=None, expose_recipients=None):
"""send an html email as multipart with attachments and all"""
content = content or msg
- emailobj = EMail(sender, recipients, subject, reply_to=reply_to, cc=cc, email_account=email_account)
+ emailobj = EMail(sender, recipients, subject, reply_to=reply_to, cc=cc, email_account=email_account, expose_recipients=expose_recipients)
if not content.strip().startswith("<"):
content = markdown(content)
@@ -35,7 +35,7 @@ class EMail:
Also provides a clean way to add binary `FileData` attachments
Also sets all messages as multipart/alternative for cleaner reading in text-only clients
"""
- def __init__(self, sender='', recipients=(), subject='', alternative=0, reply_to=None, cc=(), email_account=None):
+ def __init__(self, sender='', recipients=(), subject='', alternative=0, reply_to=None, cc=(), email_account=None, expose_recipients=None):
from email.mime.multipart import MIMEMultipart
from email import Charset
Charset.add_charset('utf-8', Charset.QP, Charset.QP, 'utf-8')
@@ -51,6 +51,7 @@ class EMail:
self.reply_to = reply_to or sender
self.recipients = recipients
self.subject = subject
+ self.expose_recipients = expose_recipients
self.msg_root = MIMEMultipart('mixed')
self.msg_multipart = MIMEMultipart('alternative')
@@ -183,7 +184,11 @@ class EMail:
self.sender = email.utils.formataddr((sender_name or self.email_account.name, self.email_account.email_id))
def set_message_id(self, message_id):
- self.msg_root["Message-Id"] = "<{0}@{1}>".format(message_id, frappe.local.site)
+ if message_id:
+ self.msg_root["Message-Id"] = message_id
+ else:
+ self.msg_root["Message-Id"] = get_message_id()
+ self.msg_root["References"] = ''
def set_in_reply_to(self, in_reply_to):
"""Used to send the Message-Id of a received email back as In-Reply-To"""
@@ -194,10 +199,10 @@ class EMail:
headers = {
"Subject": strip(self.subject),
"From": self.sender,
- "To": ', '.join(self.recipients),
+ "To": ', '.join(self.recipients) if self.expose_recipients=="header" else "",
"Date": email.utils.formatdate(),
"Reply-To": self.reply_to if self.reply_to else None,
- "CC": ', '.join(self.cc) if self.cc else None,
+ "CC": ', '.join(self.cc) if self.cc and self.expose_recipients=="header" else None,
'X-Frappe-Site': get_url(),
}
@@ -239,6 +244,12 @@ def get_formatted_html(subject, message, footer=None, print_html=None, email_acc
return scrub_urls(rendered_email)
+def get_message_id():
+ '''Returns Message ID created from doctype and name'''
+ return "<{unique}@{site}>".format(
+ site=frappe.local.site,
+ unique=email.utils.make_msgid(random_string(10)).split('@')[0].split('<')[1])
+
def get_signature(email_account):
if email_account and email_account.add_signature and email_account.signature:
return " " + email_account.signature
diff --git a/frappe/email/queue.py b/frappe/email/queue.py
index 93d0f00ffc..aed9ae8b0f 100755
--- a/frappe/email/queue.py
+++ b/frappe/email/queue.py
@@ -18,8 +18,8 @@ class EmailLimitCrossedError(frappe.ValidationError): pass
def send(recipients=None, sender=None, subject=None, message=None, reference_doctype=None,
reference_name=None, unsubscribe_method=None, unsubscribe_params=None, unsubscribe_message=None,
- attachments=None, reply_to=None, cc=(), show_as_cc=(), message_id=None, in_reply_to=None, send_after=None,
- expose_recipients=False, send_priority=1, communication=None):
+ attachments=None, reply_to=None, cc=[], message_id=None, in_reply_to=None, send_after=None,
+ expose_recipients=None, send_priority=1, communication=None, now=False):
"""Add email to sending queue (Email Queue)
:param recipients: List of recipients.
@@ -33,15 +33,15 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
:param unsubscribe_params: additional params for unsubscribed links. default are name, doctype, email
:param attachments: Attachments to be sent.
:param reply_to: Reply to be captured here (default inbox)
- :param message_id: Used for threading. If a reply is received to this email, Message-Id is sent back as In-Reply-To in received email.
:param in_reply_to: Used to send the Message-Id of a received email back as In-Reply-To.
:param send_after: Send this email after the given datetime. If value is in integer, then `send_after` will be the automatically set to no of days from current date.
:param communication: Communication link to be set in Email Queue record
+ :param now: Send immediately (don't send in the background)
"""
if not unsubscribe_method:
unsubscribe_method = "/api/method/frappe.email.queue.unsubscribe"
- if not recipients:
+ if not recipients and not cc:
return
if isinstance(recipients, basestring):
@@ -74,56 +74,40 @@ def send(recipients=None, sender=None, subject=None, message=None, reference_doc
recipients = [r for r in list(set(recipients)) if r and r not in unsubscribed]
- for email in recipients:
- email_content = formatted
- email_text_context = text_content
+ email_content = formatted
+ email_text_context = text_content
- if reference_doctype:
- unsubscribe_link = get_unsubscribe_link(
- reference_doctype=reference_doctype,
- reference_name=reference_name,
- email=email,
- recipients=recipients,
- expose_recipients=expose_recipients,
- unsubscribe_method=unsubscribe_method,
- unsubscribe_params=unsubscribe_params,
- unsubscribe_message=unsubscribe_message,
- show_as_cc=show_as_cc
- )
+ if reference_doctype and (unsubscribe_message or reference_doctype=="Newsletter"):
+ unsubscribe_link = get_unsubscribe_message(unsubscribe_message, expose_recipients)
+ email_content = email_content.replace("", unsubscribe_link.html)
+ email_text_context += unsubscribe_link.text
- email_content = email_content.replace("", unsubscribe_link.html)
- email_text_context += unsubscribe_link.text
+ # add to queue
+ email_queue = add(recipients, sender, subject, email_content, email_text_context, reference_doctype,
+ reference_name, attachments, reply_to, cc, message_id, in_reply_to, send_after, send_priority, email_account=email_account, communication=communication,
+ unsubscribe_method=unsubscribe_method, unsubscribe_params=unsubscribe_params, expose_recipients=expose_recipients)
+ if now:
+ send_one(email_queue.name, now=True)
- # show as cc
- cc_message = ""
- if email in show_as_cc:
- cc_message = _("This email was sent to you as CC")
- email_content = email_content.replace("", cc_message)
- email_text_context = cc_message + "\n" + email_text_context
-
- # add to queue
- add(email, sender, subject, email_content, email_text_context, reference_doctype,
- reference_name, attachments, reply_to, cc, message_id, in_reply_to, send_after, send_priority, email_account=email_account, communication=communication)
-
-def add(email, sender, subject, formatted, text_content=None,
+def add(recipients, sender, subject, formatted, text_content=None,
reference_doctype=None, reference_name=None, attachments=None, reply_to=None,
- cc=(), message_id=None, in_reply_to=None, send_after=None, send_priority=1, email_account=None, communication=None):
+ cc=[], message_id=None, in_reply_to=None, send_after=None, send_priority=1, email_account=None,
+ communication=None, unsubscribe_method=None, unsubscribe_params=None, expose_recipients=None):
"""Add to Email Queue"""
e = frappe.new_doc('Email Queue')
- e.recipient = email
e.priority = send_priority
try:
- mail = get_email(email, sender=sender, formatted=formatted, subject=subject,
- text_content=text_content, attachments=attachments, reply_to=reply_to, cc=cc, email_account=email_account)
-
- if message_id:
- mail.set_message_id(message_id)
+ mail = get_email(recipients, sender=sender, formatted=formatted, subject=subject,
+ text_content=text_content, attachments=attachments, reply_to=reply_to,
+ cc=cc, email_account=email_account, expose_recipients=expose_recipients)
+ mail.set_message_id(message_id)
if in_reply_to:
mail.set_in_reply_to(in_reply_to)
+ e.message_id = mail.msg_root["Message-Id"].strip(" <>")
e.message = cstr(mail.as_string())
e.sender = mail.sender
@@ -131,11 +115,20 @@ def add(email, sender, subject, formatted, text_content=None,
# bad email id - don't add to queue
return
+ e.set("recipient", [])
+ for r in recipients + cc:
+ e.append("recipient",{"recipient":r})
e.reference_doctype = reference_doctype
e.reference_name = reference_name
+ e.unsubscribe_method = unsubscribe_method
+ e.unsubscribe_params = unsubscribe_params
+ e.expose_recipients = expose_recipients
e.communication = communication
e.send_after = send_after
- e.db_insert()
+ e.show_as_cc = ",".join(cc)
+ e.insert(ignore_permissions=True)
+
+ return e
def check_email_limit(recipients):
# if using settings from site_config.json, check email limit
@@ -146,14 +139,17 @@ def check_email_limit(recipients):
and getattr(smtp_server.email_account, "from_site_config", False)
or frappe.flags.in_test):
- # get count of mails sent this month
- this_month = get_emails_sent_this_month()
-
- monthly_email_limit = frappe.conf.get('limits', {}).get('emails') or 500
+ monthly_email_limit = frappe.conf.get('limits', {}).get('emails')
if frappe.flags.in_test:
monthly_email_limit = 500
+ if not monthly_email_limit:
+ return
+
+ # get count of mails sent this month
+ this_month = get_emails_sent_this_month()
+
if (this_month + len(recipients)) > monthly_email_limit:
throw(_("Cannot send this email. You have crossed the sending limit of {0} emails for this month.").format(monthly_email_limit),
EmailLimitCrossedError)
@@ -162,43 +158,23 @@ def get_emails_sent_this_month():
return frappe.db.sql("""select count(name) from `tabEmail Queue` where
status='Sent' and MONTH(creation)=MONTH(CURDATE())""")[0][0]
-def get_unsubscribe_link(reference_doctype, reference_name,
- email, recipients, expose_recipients, show_as_cc,
- unsubscribe_method, unsubscribe_params, unsubscribe_message):
-
- email_sent_to = recipients if expose_recipients else [email]
- email_sent_cc = ", ".join([e for e in email_sent_to if e in show_as_cc])
- email_sent_to = ", ".join([e for e in email_sent_to if e not in show_as_cc])
-
- if email_sent_cc:
- email_sent_message = _("This email was sent to {0} and copied to {1}").format(email_sent_to, email_sent_cc)
- else:
- email_sent_message = _("This email was sent to {0}").format(email_sent_to)
-
+def get_unsubscribe_message(unsubscribe_message, expose_recipients):
if not unsubscribe_message:
unsubscribe_message = _("Unsubscribe from this list")
- unsubscribe_url = get_unsubcribed_url(reference_doctype, reference_name, email,
- unsubscribe_method, unsubscribe_params)
-
html = """""".format(
- unsubscribe_url = unsubscribe_url,
- email=email_sent_message,
- unsubscribe_message=unsubscribe_message
- )
-
- text = "\n{email}\n\n{unsubscribe_message}: {unsubscribe_url}".format(
- email=email_sent_message,
- unsubscribe_message=unsubscribe_message,
- unsubscribe_url=unsubscribe_url
- )
+ """.format(unsubscribe_message=unsubscribe_message)
+ if expose_recipients == "footer":
+ text = "\n"
+ else:
+ text = ""
+ text += "\n\n{unsubscribe_message}: ".format(unsubscribe_message=unsubscribe_message)
return frappe._dict({
"html": html,
@@ -263,7 +239,7 @@ def flush(from_test=False):
email = cache.lpop('cache_email_queue')
if email:
- send_one(email, smtpserver, auto_commit)
+ send_one(email, smtpserver, auto_commit, from_test=from_test)
# NOTE: removing commit here because we pass auto_commit
# finally:
@@ -273,7 +249,7 @@ def make_cache_queue():
cache = frappe.cache()
emails = frappe.db.sql('''select name from `tabEmail Queue`
- where status='Not Sent' and (send_after is null or send_after < %(now)s)
+ where (status='Not Sent' or status='Partially Sent') and (send_after is null or send_after < %(now)s)
order by priority desc, creation asc
limit 500''', { 'now': now_datetime() })
@@ -282,17 +258,25 @@ def make_cache_queue():
for e in emails:
cache.rpush('cache_email_queue', e[0])
-def send_one(email, smtpserver=None, auto_commit=True, now=False):
+def send_one(email, smtpserver=None, auto_commit=True, now=False, from_test=False):
'''Send Email Queue with given smtpserver'''
email = frappe.db.sql('''select name, status, communication,
- message, sender, recipient, reference_doctype
+ message, sender, reference_doctype, reference_name, unsubscribe_param, unsubscribe_method, expose_recipients, show_as_cc
from `tabEmail Queue` where name=%s for update''', email, as_dict=True)[0]
- if email.status != 'Not Sent':
+
+ recipients_list = frappe.db.sql('''select name, recipient, status from `tabEmail Queue Recipient` where parent=%s''',email.name,as_dict=1)
+
+ if frappe.are_emails_muted():
+ frappe.msgprint(_("Emails are muted"))
+ return
+
+ if email.status not in ('Not Sent','Partially Sent') :
# rollback to release lock and return
frappe.db.rollback()
return
+
frappe.db.sql("""update `tabEmail Queue` set status='Sending', modified=%s where name=%s""",
(now_datetime(), email.name), auto_commit=auto_commit)
@@ -300,14 +284,32 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False):
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
try:
- if auto_commit:
+ if not frappe.flags.in_test:
if not smtpserver: smtpserver = SMTPServer()
smtpserver.setup_email_account(email.reference_doctype)
- smtpserver.sess.sendmail(email.sender, email.recipient, encode(email.message))
- frappe.db.sql("""update `tabEmail Queue` set status='Sent', modified=%s where name=%s""",
- (now_datetime(), email.name), auto_commit=auto_commit)
+ for recipient in recipients_list:
+ if recipient.status != "Not Sent":
+ continue
+ message = prepare_message(email, recipient.recipient, recipients_list)
+ if not frappe.flags.in_test:
+ smtpserver.sess.sendmail(email.sender, recipient.recipient, encode(message))
+
+ recipient.status = "Sent"
+ frappe.db.sql("""update `tabEmail Queue Recipient` set status='Sent', modified=%s where name=%s""",
+ (now_datetime(), recipient.name), auto_commit=auto_commit)
+
+ #if all are sent set status
+ if any("Sent" == s.status for s in recipients_list):
+ frappe.db.sql("""update `tabEmail Queue` set status='Sent', modified=%s where name=%s""",
+ (now_datetime(), email.name), auto_commit=auto_commit)
+ else:
+ frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s
+ where name=%s""", ("No recipients to send to", email.name), auto_commit=auto_commit)
+ if frappe.flags.in_test:
+ frappe.flags.sent_mail = message
+ return
if email.communication:
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
@@ -318,8 +320,13 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False):
JobTimeoutException):
# bad connection/timeout, retry later
- frappe.db.sql("""update `tabEmail Queue` set status='Not Sent', modified=%s where name=%s""",
- (now_datetime(), email.name), auto_commit=auto_commit)
+
+ if any("Sent" == s.status for s in recipients_list):
+ frappe.db.sql("""update `tabEmail Queue` set status='Partially Sent', modified=%s where name=%s""",
+ (now_datetime(), email.name), auto_commit=auto_commit)
+ else:
+ frappe.db.sql("""update `tabEmail Queue` set status='Not Sent', modified=%s where name=%s""",
+ (now_datetime(), email.name), auto_commit=auto_commit)
if email.communication:
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
@@ -330,8 +337,12 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False):
except Exception, e:
frappe.db.rollback()
- frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s
- where name=%s""", (unicode(e), email.name), auto_commit=auto_commit)
+ if any("Sent" == s.status for s in recipients_list):
+ frappe.db.sql("""update `tabEmail Queue` set status='Partially Errored', error=%s where name=%s""",
+ (unicode(e), email.name), auto_commit=auto_commit)
+ else:
+ frappe.db.sql("""update `tabEmail Queue` set status='Error', error=%s
+where name=%s""", (unicode(e), email.name), auto_commit=auto_commit)
if email.communication:
frappe.get_doc('Communication', email.communication).set_delivery_status(commit=auto_commit)
@@ -343,10 +354,38 @@ def send_one(email, smtpserver=None, auto_commit=True, now=False):
# log to Error Log
log('frappe.email.queue.flush', unicode(e))
-def clear_outbox():
- """Remove mails older than 31 days in Outbox. Called daily via scheduler."""
- frappe.db.sql("""delete from `tabEmail Queue` where
- datediff(now(), creation) > 31""")
+def prepare_message(email, recipient, recipients_list):
+ message = email.message
+ if email.reference_doctype: # is missing the check for unsubscribe message but will not add as there will be no unsubscribe url
+ unsubscribe_url = get_unsubcribed_url(email.reference_doctype, email.reference_name, recipient,
+ email.unsubscribe_method, email.unsubscribe_params)
+ message = message.replace("", unsubscribe_url)
- frappe.db.sql("""update `tabEmail Queue` set status='Expired'
- where datediff(curdate(), creation) > 7 and status='Not Sent'""")
+ if email.expose_recipients == "header":
+ pass
+ else:
+ if email.expose_recipients == "footer":
+ if isinstance(email.show_as_cc, basestring):
+ email.show_as_cc = email.show_as_cc.split(",")
+ email_sent_to = [r.recipient for r in recipients_list]
+ email_sent_cc = ", ".join([e for e in email_sent_to if e in email.show_as_cc])
+ email_sent_to = ", ".join([e for e in email_sent_to if e not in email.show_as_cc])
+
+ if email_sent_cc:
+ email_sent_message = _("This email was sent to {0} and copied to {1}").format(email_sent_to,email_sent_cc)
+ else:
+ email_sent_message = _("This email was sent to {0}").format(email_sent_to)
+ message = message.replace("", email_sent_message)
+
+ message = message.replace("", recipient)
+ return message
+
+def clear_outbox():
+ """Remove low priority older than 31 days in Outbox and expire mails not sent for 7 days.
+
+ Called daily via scheduler."""
+ frappe.db.sql("""delete q, r from `tabEmail Queue` as q, `tabEmail Queue Recipient` as r where q.name = r.parent and q.priority=0 and
+ datediff(now(), q.modified) > 31""")
+
+ frappe.db.sql("""update `tabEmail Queue` as q, `tabEmail Queue Recipient` as r set q.status='Expired', r.status='Expired'
+ where q.name = r.parent and datediff(curdate(), q.modified) > 7 and q.status='Not Sent' and r.status='Not Sent'""")
diff --git a/frappe/email/receive.py b/frappe/email/receive.py
index 39b970497f..7bceaab1be 100644
--- a/frappe/email/receive.py
+++ b/frappe/email/receive.py
@@ -262,6 +262,7 @@ class Email:
self.set_content_and_type()
self.set_subject()
self.set_from()
+ self.message_id = (self.mail.get('Message-ID') or "").strip(" <>")
if self.mail["Date"]:
utc = email.utils.mktime_tz(email.utils.parsedate_tz(self.mail["Date"]))
diff --git a/frappe/email/smtp.py b/frappe/email/smtp.py
index 31b0acc856..3de1830b69 100644
--- a/frappe/email/smtp.py
+++ b/frappe/email/smtp.py
@@ -11,15 +11,7 @@ from frappe.utils import cint
from frappe import _
def send(email, append_to=None, retry=1):
- """send the message or add it to Outbox Email"""
- if frappe.flags.in_test:
- frappe.flags.sent_mail = email.as_string()
- return
-
- if frappe.are_emails_muted():
- frappe.msgprint(_("Emails are muted"))
- return
-
+ """Deprecated: Send the message or add it to Outbox Email"""
def _send(retry):
try:
smtpserver = SMTPServer(append_to=append_to)
@@ -79,7 +71,7 @@ def get_default_outgoing_email_account(raise_exception_not_set=True):
{
"mail_server": "smtp.example.com",
"mail_port": 587,
- "use_ssl": 1,
+ "use_tls": 1,
"mail_login": "emails@example.com",
"mail_password": "Super.Secret.Password",
"auto_email_id": "emails@example.com",
@@ -97,7 +89,9 @@ def get_default_outgoing_email_account(raise_exception_not_set=True):
email_account.update({
"smtp_server": frappe.conf.get("mail_server"),
"smtp_port": frappe.conf.get("mail_port"),
- "use_tls": cint(frappe.conf.get("use_ssl") or 0),
+
+ # legacy: use_ssl was used in site_config instead of use_tls, but meant the same thing
+ "use_tls": cint(frappe.conf.get("use_tls") or 0) or cint(frappe.conf.get("use_ssl") or 0),
"login_id": frappe.conf.get("mail_login"),
"email_id": frappe.conf.get("auto_email_id") or frappe.conf.get("mail_login") or 'notifications@example.com',
"password": frappe.conf.get("mail_password"),
@@ -123,7 +117,7 @@ def _get_email_account(filters):
return frappe.get_doc("Email Account", name) if name else None
class SMTPServer:
- def __init__(self, login=None, password=None, server=None, port=None, use_ssl=None, append_to=None):
+ def __init__(self, login=None, password=None, server=None, port=None, use_tls=None, append_to=None):
# get defaults from mail settings
self._sess = None
@@ -132,7 +126,7 @@ class SMTPServer:
if server:
self.server = server
self.port = port
- self.use_ssl = cint(use_ssl)
+ self.use_tls = cint(use_tls)
self.login = login
self.password = password
@@ -146,7 +140,7 @@ class SMTPServer:
self.login = getattr(self.email_account, "login_id", None) or self.email_account.email_id
self.password = self.email_account.password
self.port = self.email_account.smtp_port
- self.use_ssl = self.email_account.use_tls
+ self.use_tls = self.email_account.use_tls
self.sender = self.email_account.email_id
self.always_use_account_email_id_as_sender = cint(self.email_account.get("always_use_account_email_id_as_sender"))
@@ -163,7 +157,7 @@ class SMTPServer:
raise frappe.OutgoingEmailError, err_msg
try:
- if self.use_ssl and not self.port:
+ if self.use_tls and not self.port:
self.port = 587
self._sess = smtplib.SMTP((self.server or "").encode('utf-8'),
@@ -174,7 +168,7 @@ class SMTPServer:
frappe.msgprint(err_msg)
raise frappe.OutgoingEmailError, err_msg
- if self.use_ssl:
+ if self.use_tls:
self._sess.ehlo()
self._sess.starttls()
self._sess.ehlo()
diff --git a/frappe/exceptions.py b/frappe/exceptions.py
index 2bd5e94851..c09ebd26e0 100644
--- a/frappe/exceptions.py
+++ b/frappe/exceptions.py
@@ -62,3 +62,4 @@ class AppNotInstalledError(ValidationError): pass
class IncorrectSitePath(NotFound): pass
class ImplicitCommitError(ValidationError): pass
class RetryBackgroundJobError(Exception): pass
+class DocumentLockedError(ValidationError): pass
diff --git a/frappe/frappeclient.py b/frappe/frappeclient.py
index 855564e8fc..10c5f505ba 100644
--- a/frappe/frappeclient.py
+++ b/frappe/frappeclient.py
@@ -2,6 +2,13 @@ import requests
import json
import frappe
+'''
+FrappeClient is a library that helps you connect with other frappe systems
+
+
+
+'''
+
class AuthError(Exception):
pass
@@ -13,7 +20,7 @@ class FrappeClient(object):
self.verify = verify
self.session = requests.session()
self.url = url
- self.login(username, password)
+ self._login(username, password)
def __enter__(self):
return self
@@ -21,7 +28,8 @@ class FrappeClient(object):
def __exit__(self, *args, **kwargs):
self.logout()
- def login(self, username, password):
+ def _login(self, username, password):
+ '''Login/start a sesion. Called internally on init'''
r = self.session.post(self.url, data={
'cmd': 'login',
'usr': username,
@@ -34,6 +42,7 @@ class FrappeClient(object):
raise AuthError
def logout(self):
+ '''Logout session'''
self.session.get(self.url, params={
'cmd': 'logout',
}, verify=self.verify)
@@ -54,41 +63,65 @@ class FrappeClient(object):
return self.post_process(res)
def insert(self, doc):
+ '''Insert a document to the remote server
+
+ :param doc: A dict or Document object to be inserted remotely'''
res = self.session.post(self.url + "/api/resource/" + doc.get("doctype"),
data={"data":frappe.as_json(doc)}, verify=self.verify)
return self.post_process(res)
def insert_many(self, docs):
+ '''Insert multiple documents to the remote server
+
+ :param docs: List of dict or Document objects to be inserted in one request'''
return self.post_request({
"cmd": "frappe.client.insert_many",
"docs": frappe.as_json(docs)
})
def update(self, doc):
+ '''Update a remote document
+
+ :param doc: dict or Document object to be updated remotely. `name` is mandatory for this'''
url = self.url + "/api/resource/" + doc.get("doctype") + "/" + doc.get("name")
res = self.session.put(url, data={"data":frappe.as_json(doc)}, verify=self.verify)
return self.post_process(res)
def bulk_update(self, docs):
+ '''Bulk update documents remotely
+
+ :param docs: List of dict or Document objects to be updated remotely (by `name`)'''
return self.post_request({
"cmd": "frappe.client.bulk_update",
"docs": frappe.as_json(docs)
})
def delete(self, doctype, name):
+ '''Delete remote document by name
+
+ :param doctype: `doctype` to be deleted
+ :param name: `name` of document to be deleted'''
return self.post_request({
"cmd": "frappe.model.delete_doc",
"doctype": doctype,
"name": name
})
- def submit(self, doclist):
+ def submit(self, doc):
+ '''Submit remote document
+
+ :param doc: dict or Document object to be submitted remotely'''
return self.post_request({
"cmd": "frappe.client.submit",
- "doclist": frappe.as_json(doclist)
+ "doc": frappe.as_json(doc)
})
def get_value(self, doctype, fieldname=None, filters=None):
+ '''Returns a value form a document
+
+ :param doctype: DocType to be queried
+ :param fieldname: Field to be returned (default `name`)
+ :param filters: dict or string for identifying the record'''
return self.get_request({
"cmd": "frappe.client.get_value",
"doctype": doctype,
@@ -97,6 +130,12 @@ class FrappeClient(object):
})
def set_value(self, doctype, docname, fieldname, value):
+ '''Set a value in a remote document
+
+ :param doctype: DocType of the document to be updated
+ :param docname: name of the document to be updated
+ :param fieldname: fieldname of the document to be updated
+ :param value: value to be updated'''
return self.post_request({
"cmd": "frappe.client.set_value",
"doctype": doctype,
@@ -106,6 +145,10 @@ class FrappeClient(object):
})
def cancel(self, doctype, name):
+ '''Cancel a remote document
+
+ :param doctype: DocType of the document to be cancelled
+ :param name: name of the document to be cancelled'''
return self.post_request({
"cmd": "frappe.client.cancel",
"doctype": doctype,
@@ -113,6 +156,12 @@ class FrappeClient(object):
})
def get_doc(self, doctype, name="", filters=None, fields=None):
+ '''Returns a single remote document
+
+ :param doctype: DocType of the document to be returned
+ :param name: (optional) `name` of the document to be returned
+ :param filters: (optional) Filter by this dict if name is not set
+ :param fields: (optional) Fields to be returned, will return everythign if not set'''
params = {}
if filters:
params["filters"] = json.dumps(filters)
@@ -125,6 +174,11 @@ class FrappeClient(object):
return self.post_process(res)
def rename_doc(self, doctype, old_name, new_name):
+ '''Rename remote document
+
+ :param doctype: DocType of the document to be renamed
+ :param old_name: Current `name` of the document to be renamed
+ :param new_name: New `name` to be set'''
params = {
"cmd": "frappe.client.rename_doc",
"doctype": doctype,
diff --git a/frappe/geo/doctype/country/country.json b/frappe/geo/doctype/country/country.json
index 118361bbd2..16ae4ef4fd 100644
--- a/frappe/geo/doctype/country/country.json
+++ b/frappe/geo/doctype/country/country.json
@@ -112,7 +112,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-globe",
+ "icon": "fa fa-globe",
"idx": 1,
"image_view": 0,
"in_create": 0,
diff --git a/frappe/geo/doctype/currency/currency.json b/frappe/geo/doctype/currency/currency.json
index 48dbf6f717..e604a5da04 100644
--- a/frappe/geo/doctype/currency/currency.json
+++ b/frappe/geo/doctype/currency/currency.json
@@ -183,7 +183,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-bitcoin",
+ "icon": "fa fa-bitcoin",
"idx": 1,
"in_create": 0,
"in_dialog": 0,
diff --git a/frappe/hooks.py b/frappe/hooks.py
index 1eecca0b4b..a79e377763 100755
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -48,7 +48,8 @@ web_include_css = [
]
website_route_rules = [
{"from_route": "/blog", "to_route": "Blog Post"},
- {"from_route": "/blog/", "to_route": "Blog Post"}
+ {"from_route": "/blog/", "to_route": "Blog Post"},
+ {"from_route": "/kb/", "to_route": "Help Article"}
]
write_file_keys = ["file_url", "file_name"]
@@ -57,7 +58,8 @@ notification_config = "frappe.core.notifications.get_notification_config"
before_tests = "frappe.utils.install.before_tests"
-website_generators = ["Web Page", "Blog Post", "Blog Category", "Web Form"]
+website_generators = ["Web Page", "Blog Post", "Blog Category", "Web Form",
+ "Help Article"]
email_append_to = ["Event", "ToDo", "Communication"]
@@ -116,6 +118,7 @@ scheduler_events = {
"frappe.email.queue.flush",
"frappe.email.doctype.email_account.email_account.pull",
"frappe.email.doctype.email_account.email_account.notify_unreplied",
+ "frappe.oauth.delete_oauth2_data"
],
"hourly": [
"frappe.model.utils.link_count.update_link_count",
diff --git a/frappe/integration_broker/doctype/integration_request/integration_request.json b/frappe/integration_broker/doctype/integration_request/integration_request.json
index e32450d9bb..dbe8d19ad6 100644
--- a/frappe/integration_broker/doctype/integration_request/integration_request.json
+++ b/frappe/integration_broker/doctype/integration_request/integration_request.json
@@ -23,6 +23,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Integration Type",
"length": 0,
"no_copy": 0,
@@ -32,6 +33,7 @@
"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,
@@ -50,6 +52,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Integration Request Service",
"length": 0,
"no_copy": 0,
@@ -59,6 +62,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -78,6 +82,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
@@ -87,6 +92,7 @@
"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,
@@ -105,6 +111,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Data",
"length": 0,
"no_copy": 0,
@@ -113,6 +120,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -131,6 +139,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Output",
"length": 0,
"no_copy": 0,
@@ -139,6 +148,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -157,6 +167,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Error",
"length": 0,
"no_copy": 0,
@@ -165,6 +176,65 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_doctype",
+ "fieldtype": "Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference Doctype",
+ "length": 0,
+ "no_copy": 0,
+ "options": "DocType",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "reference_docname",
+ "fieldtype": "Dynamic Link",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Reference Docname",
+ "length": 0,
+ "no_copy": 0,
+ "options": "reference_doctype",
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -182,7 +252,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-10-13 05:01:14.913553",
+ "modified": "2016-11-10 18:04:27.500673",
"modified_by": "Administrator",
"module": "Integration Broker",
"name": "Integration Request",
diff --git a/frappe/integration_broker/doctype/integration_service/integration_service.py b/frappe/integration_broker/doctype/integration_service/integration_service.py
index c8f5c96f8b..81ed105543 100644
--- a/frappe/integration_broker/doctype/integration_service/integration_service.py
+++ b/frappe/integration_broker/doctype/integration_service/integration_service.py
@@ -64,14 +64,16 @@ class IntegrationService(Document):
pass
def create_request(self, data, integration_type, service_name, name=None):
- if not isinstance(data, basestring):
- data = json.dumps(data)
+ if isinstance(data, basestring):
+ data = json.loads(data)
integration_request = frappe.get_doc({
"doctype": "Integration Request",
"integration_type": integration_type,
"integration_request_service": service_name,
- "data": data
+ "reference_doctype": data.get("reference_doctype"),
+ "reference_docname": data.get("reference_docname"),
+ "data": json.dumps(data)
})
if name:
diff --git a/frappe/integration_broker/doctype/oauth_authorization_code/oauth_authorization_code.json b/frappe/integration_broker/doctype/oauth_authorization_code/oauth_authorization_code.json
index 35ee594063..48aadf501e 100644
--- a/frappe/integration_broker/doctype/oauth_authorization_code/oauth_authorization_code.json
+++ b/frappe/integration_broker/doctype/oauth_authorization_code/oauth_authorization_code.json
@@ -24,6 +24,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Client",
"length": 0,
"no_copy": 0,
@@ -32,6 +33,7 @@
"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,
@@ -50,6 +52,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
@@ -58,6 +61,7 @@
"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,
@@ -76,6 +80,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Scopes",
"length": 0,
"no_copy": 0,
@@ -83,6 +88,7 @@
"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,
@@ -101,6 +107,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Authorization Code",
"length": 0,
"no_copy": 0,
@@ -108,6 +115,7 @@
"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,
@@ -126,6 +134,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Expiration time",
"length": 0,
"no_copy": 0,
@@ -133,6 +142,7 @@
"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,
@@ -151,6 +161,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Redirect URI Bound To Auth Code",
"length": 0,
"no_copy": 0,
@@ -159,6 +170,7 @@
"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,
@@ -177,6 +189,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 0,
"label": "Validity",
"length": 0,
"no_copy": 0,
@@ -186,6 +199,7 @@
"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,
@@ -203,7 +217,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-10-20 00:13:53.224437",
+ "modified": "2016-11-07 18:31:54.470173",
"modified_by": "Administrator",
"module": "Integration Broker",
"name": "OAuth Authorization Code",
@@ -214,7 +228,7 @@
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
- "create": 1,
+ "create": 0,
"delete": 1,
"email": 1,
"export": 1,
@@ -229,7 +243,7 @@
"set_user_permissions": 0,
"share": 1,
"submit": 0,
- "write": 1
+ "write": 0
}
],
"quick_entry": 0,
diff --git a/frappe/integration_broker/doctype/oauth_bearer_token/oauth_bearer_token.json b/frappe/integration_broker/doctype/oauth_bearer_token/oauth_bearer_token.json
index 029b8754fe..cc0cea719b 100644
--- a/frappe/integration_broker/doctype/oauth_bearer_token/oauth_bearer_token.json
+++ b/frappe/integration_broker/doctype/oauth_bearer_token/oauth_bearer_token.json
@@ -24,6 +24,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Client",
"length": 0,
"no_copy": 0,
@@ -32,6 +33,7 @@
"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,
@@ -50,6 +52,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
@@ -58,6 +61,7 @@
"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,
@@ -76,6 +80,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Scopes",
"length": 0,
"no_copy": 0,
@@ -83,6 +88,7 @@
"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,
@@ -101,6 +107,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Access Token",
"length": 0,
"no_copy": 0,
@@ -108,6 +115,7 @@
"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,
@@ -126,6 +134,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Refresh Token",
"length": 0,
"no_copy": 0,
@@ -133,6 +142,7 @@
"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,
@@ -151,6 +161,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Expiration time",
"length": 0,
"no_copy": 0,
@@ -158,6 +169,7 @@
"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,
@@ -176,6 +188,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Expires In",
"length": 0,
"no_copy": 0,
@@ -184,6 +197,7 @@
"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,
@@ -202,6 +216,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 1,
"label": "Status",
"length": 0,
"no_copy": 0,
@@ -211,6 +226,7 @@
"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,
@@ -228,7 +244,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-10-20 00:13:41.747141",
+ "modified": "2016-11-07 18:31:32.243853",
"modified_by": "Administrator",
"module": "Integration Broker",
"name": "OAuth Bearer Token",
@@ -239,7 +255,7 @@
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
- "create": 1,
+ "create": 0,
"delete": 1,
"email": 1,
"export": 1,
@@ -254,7 +270,7 @@
"set_user_permissions": 0,
"share": 1,
"submit": 0,
- "write": 1
+ "write": 0
}
],
"quick_entry": 0,
diff --git a/frappe/integration_broker/doctype/oauth_client/oauth_client.json b/frappe/integration_broker/doctype/oauth_client/oauth_client.json
index 83ec6bebdd..2edeb8e746 100644
--- a/frappe/integration_broker/doctype/oauth_client/oauth_client.json
+++ b/frappe/integration_broker/doctype/oauth_client/oauth_client.json
@@ -12,6 +12,34 @@
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "default": "",
+ "fieldname": "client_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "App Client ID",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 1,
+ "remember_last_selected_value": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
{
"allow_on_submit": 0,
"bold": 0,
@@ -24,6 +52,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "App Name",
"length": 0,
"no_copy": 0,
@@ -32,6 +61,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -50,6 +80,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
@@ -59,6 +90,7 @@
"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,
@@ -77,6 +109,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -84,6 +117,7 @@
"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,
@@ -95,21 +129,23 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
- "default": "",
- "fieldname": "client_id",
+ "fieldname": "client_secret",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
- "label": "App Client ID",
+ "in_standard_filter": 0,
+ "label": "App Client Secret",
"length": 0,
"no_copy": 0,
"permlevel": 0,
+ "precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
@@ -129,6 +165,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Skip Authorization",
"length": 0,
"no_copy": 0,
@@ -137,6 +174,7 @@
"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,
@@ -156,6 +194,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "",
"length": 0,
"no_copy": 0,
@@ -164,6 +203,7 @@
"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,
@@ -175,6 +215,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
+ "default": "all openid",
"description": "A list of resources which the Client App will have access to after the user allows it. e.g. project",
"fieldname": "scopes",
"fieldtype": "Text",
@@ -183,6 +224,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Scopes",
"length": 0,
"no_copy": 0,
@@ -190,6 +232,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -208,6 +251,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -215,6 +259,7 @@
"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,
@@ -234,6 +279,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Redirect URIs",
"length": 0,
"no_copy": 0,
@@ -241,6 +287,7 @@
"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,
@@ -259,6 +306,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Default Redirect URI",
"length": 0,
"no_copy": 0,
@@ -267,6 +315,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -286,6 +335,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": " Advanced Settings",
"length": 0,
"no_copy": 0,
@@ -294,6 +344,7 @@
"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,
@@ -312,6 +363,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Grant Type",
"length": 0,
"no_copy": 0,
@@ -320,6 +372,7 @@
"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,
@@ -338,6 +391,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -345,6 +399,7 @@
"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,
@@ -364,6 +419,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "Response Type",
"length": 0,
"no_copy": 0,
@@ -372,6 +428,7 @@
"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,
@@ -389,7 +446,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-10-20 00:32:11.993940",
+ "modified": "2016-11-07 18:53:59.549740",
"modified_by": "Administrator",
"module": "Integration Broker",
"name": "OAuth Client",
diff --git a/frappe/integration_broker/doctype/oauth_client/oauth_client.py b/frappe/integration_broker/doctype/oauth_client/oauth_client.py
index 3493832064..ebf2f64547 100644
--- a/frappe/integration_broker/doctype/oauth_client/oauth_client.py
+++ b/frappe/integration_broker/doctype/oauth_client/oauth_client.py
@@ -9,3 +9,5 @@ from frappe.model.document import Document
class OAuthClient(Document):
def validate(self):
self.client_id = self.name
+ if not self.client_secret:
+ self.client_secret = frappe.generate_hash(length=10)
diff --git a/frappe/integration_broker/oauth2.py b/frappe/integration_broker/oauth2.py
index ed48b83a4c..3b79c850af 100644
--- a/frappe/integration_broker/oauth2.py
+++ b/frappe/integration_broker/oauth2.py
@@ -3,8 +3,10 @@ import frappe, json
from frappe.oauth import OAuthWebRequestValidator, WebApplicationServer
from oauthlib.oauth2 import FatalClientError, OAuth2Error
from urllib import quote, urlencode
+from werkzeug import url_fix
from urlparse import urlparse
from frappe.integrations.doctype.oauth_provider_settings.oauth_provider_settings import get_oauth_settings
+from frappe import _
def get_oauth_server():
if not getattr(frappe.local, 'oauth_server', None):
@@ -25,7 +27,7 @@ def get_urlparams_from_kwargs(param_kwargs):
@frappe.whitelist()
def approve(*args, **kwargs):
r = frappe.request
- uri = r.url
+ uri = url_fix(r.url.replace("+"," "))
http_method = r.method
body = r.get_data()
headers = r.headers
@@ -62,7 +64,7 @@ def authorize(*args, **kwargs):
elif frappe.session['user']!='Guest':
try:
r = frappe.request
- uri = r.url
+ uri = url_fix(r.url)
http_method = r.method
body = r.get_data()
headers = r.headers
@@ -96,14 +98,42 @@ def authorize(*args, **kwargs):
def get_token(*args, **kwargs):
r = frappe.request
- uri = r.url
+ uri = url_fix(r.url)
http_method = r.method
body = r.form
headers = r.headers
+
+ #Check whether frappe server URL is set
+ frappe_server_url = frappe.db.get_value("Social Login Keys", None, "frappe_server_url") or None
+ if not frappe_server_url:
+ frappe.throw(_("Define Frappe Server URL in Social Login Keys"))
try:
headers, body, status = get_oauth_server().create_token_response(uri, http_method, body, headers, frappe.flags.oauth_credentials)
- frappe.local.response = frappe._dict(json.loads(body))
+ out = frappe._dict(json.loads(body))
+ if not out.error and "openid" in out.scope:
+ token_user = frappe.db.get_value("OAuth Bearer Token", out.access_token, "user")
+ token_client = frappe.db.get_value("OAuth Bearer Token", out.access_token, "client")
+ client_secret = frappe.db.get_value("OAuth Client", token_client, "client_secret")
+ if token_user in ["Guest", "Administrator"]:
+ frappe.throw(_("Logged in as Guest or Administrator"))
+ import hashlib
+ id_token_header = {
+ "typ":"jwt",
+ "alg":"HS256"
+ }
+ id_token = {
+ "aud": token_client,
+ "exp": int((frappe.db.get_value("OAuth Bearer Token", out.access_token, "expiration_time") - frappe.utils.datetime.datetime(1970, 1, 1)).total_seconds()),
+ "sub": frappe.db.get_value("User", token_user, "frappe_userid"),
+ "iss": frappe_server_url,
+ "at_hash": frappe.oauth.calculate_at_hash(out.access_token, hashlib.sha256)
+ }
+ import jwt
+ id_token_encoded = jwt.encode(id_token, client_secret, algorithm='HS256', headers=id_token_header)
+ out.update({"id_token":id_token_encoded})
+ frappe.local.response = out
+
except FatalClientError as e:
return e
@@ -111,7 +141,7 @@ def get_token(*args, **kwargs):
@frappe.whitelist(allow_guest=True)
def revoke_token(*args, **kwargs):
r = frappe.request
- uri = r.url
+ uri = url_fix(r.url)
http_method = r.method
body = r.form
headers = r.headers
@@ -122,4 +152,38 @@ def revoke_token(*args, **kwargs):
if status == 200:
return "success"
else:
- return "bad request"
\ No newline at end of file
+ return "bad request"
+
+@frappe.whitelist()
+def openid_profile(*args, **kwargs):
+ picture = None
+ first_name, last_name, avatar, name, frappe_userid = frappe.db.get_value("User", frappe.session.user, ["first_name", "last_name", "user_image", "name", "frappe_userid"])
+ request_url = urlparse(frappe.request.url)
+
+ if avatar:
+ if validate_url(avatar):
+ picture = avatar
+ else:
+ picture = request_url.scheme + "://" + request_url.netloc + avatar
+
+ user_profile = frappe._dict({
+ "sub": frappe_userid,
+ "name": " ".join(filter(None, [first_name, last_name])),
+ "given_name": first_name,
+ "family_name": last_name,
+ "email": name,
+ "picture": picture
+ })
+
+ frappe.local.response = user_profile
+
+def validate_url(url_string):
+ from urlparse import urlparse
+ try:
+ result = urlparse(url_string)
+ if result.scheme and result.scheme in ["http", "https", "ftp", "ftps"]:
+ return True
+ else:
+ return False
+ except:
+ return False
\ No newline at end of file
diff --git a/frappe/integrations/doctype/paypal_settings/paypal_settings.py b/frappe/integrations/doctype/paypal_settings/paypal_settings.py
index 2702cb7f2f..33f14843b1 100644
--- a/frappe/integrations/doctype/paypal_settings/paypal_settings.py
+++ b/frappe/integrations/doctype/paypal_settings/paypal_settings.py
@@ -2,15 +2,6 @@
# Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
-import json
-from frappe import _
-from frappe.utils import get_url, call_hook_method
-from urllib import urlencode
-from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService
-import urllib
-
"""
# Integrating PayPal
@@ -63,28 +54,44 @@ More Details:
"""
+from __future__ import unicode_literals
+import frappe
+import json
+from frappe import _
+from frappe.utils import get_url, call_hook_method, cint
+from urllib import urlencode
+from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService
+import urllib
+
class PayPalSettings(IntegrationService):
service_name = "PayPal"
-
+
supported_currencies = ["AUD", "BRL", "CAD", "CZK", "DKK", "EUR", "HKD", "HUF", "ILS", "JPY", "MYR", "MXN",
"TWD", "NZD", "NOK", "PHP", "PLN", "GBP", "RUB", "SGD", "SEK", "CHF", "THB", "TRY", "USD"]
-
+
+ def __setup__(self):
+ setattr(self, "use_sandbox", 0)
+
+ def setup_sandbox_env(self, token):
+ data = json.loads(frappe.db.get_value("Integration Request", token, "data"))
+ setattr(self, "use_sandbox", cint(frappe._dict(data).use_sandbox) or 0)
+
def validate(self):
if not self.flags.ignore_mandatory:
self.validate_paypal_credentails()
-
+
def on_update(self):
pass
-
+
def enable(self):
call_hook_method('payment_gateway_enabled', gateway=self.service_name)
if not self.flags.ignore_mandatory:
self.validate_paypal_credentails()
-
+
def validate_transaction_currency(self, currency):
if currency not in self.supported_currencies:
frappe.throw(_("Please select another payment method. {0} does not support transactions in currency '{1}'").format(self.service_name, currency))
-
+
def get_paypal_params_and_url(self):
params = {
"USER": self.api_username,
@@ -94,7 +101,14 @@ class PayPalSettings(IntegrationService):
"METHOD": "GetPalDetails"
}
- api_url = "https://api-3t.sandbox.paypal.com/nvp" if self.paypal_sandbox else "https://api-3t.paypal.com/nvp"
+ if hasattr(self, "use_sandbox") and self.use_sandbox:
+ params.update({
+ "USER": frappe.conf.sandbox_api_username,
+ "PWD": frappe.conf.sandbox_api_password,
+ "SIGNATURE": frappe.conf.sandbox_signature
+ })
+
+ api_url = "https://api-3t.sandbox.paypal.com/nvp" if (self.paypal_sandbox or self.use_sandbox) else "https://api-3t.paypal.com/nvp"
return params, api_url
@@ -110,11 +124,13 @@ class PayPalSettings(IntegrationService):
except Exception:
frappe.throw(_("Invalid payment gateway credentials"))
-
+
def get_payment_url(self, **kwargs):
+ setattr(self, "use_sandbox", cint(kwargs.get("use_sandbox", 0)))
+
response = self.execute_set_express_checkout(kwargs["amount"], kwargs["currency"])
- if self.paypal_sandbox:
+ if self.paypal_sandbox or self.use_sandbox:
return_url = "https://www.sandbox.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}"
else:
return_url = "https://www.paypal.com/cgi-bin/webscr?cmd=_express-checkout&token={0}"
@@ -153,13 +169,13 @@ def get_service_details():
Steps to configure Service
- Get PayPal api credentials from link:
+ Get PayPal api credentials from link:
https://developer.paypal.com/docs/classic/api/apiCredentials/
- Setup credentials on PayPal settings doctype.
+ Setup credentials on PayPal settings doctype.
Click on
PayPal Settings
top right corner
@@ -186,78 +202,92 @@ def get_service_details():
@frappe.whitelist(allow_guest=True, xss_safe=True)
def get_express_checkout_details(token):
- doc = frappe.get_doc("PayPal Settings")
- params, url = doc.get_paypal_params_and_url()
- params.update({
- "METHOD": "GetExpressCheckoutDetails",
- "TOKEN": token
- })
+ try:
+ doc = frappe.get_doc("PayPal Settings")
+ doc.setup_sandbox_env(token)
- response = doc.post_request(url, data=params)
+ params, url = doc.get_paypal_params_and_url()
+ params.update({
+ "METHOD": "GetExpressCheckoutDetails",
+ "TOKEN": token
+ })
- if response.get("ACK")[0] != "Success":
- frappe.respond_as_web_page(_("Something went wrong"),
- _("Looks like something went wrong during the transaction. Since we haven't confirmed the payment, Paypal will automatically refund you this amount. If it doesn't, please send us an email and mention the Correlation ID: {0}.").format(response.get("CORRELATIONID", [None])[0]),
- success=False,
- http_status_code=frappe.ValidationError.http_status_code)
+ response = doc.post_request(url, data=params)
- return
+ if response.get("ACK")[0] != "Success":
+ frappe.respond_as_web_page(_("Something went wrong"),
+ _("Looks like something went wrong during the transaction. Since we haven't confirmed the payment, Paypal will automatically refund you this amount. If it doesn't, please send us an email and mention the Correlation ID: {0}.").format(response.get("CORRELATIONID", [None])[0]),
+ success=False,
+ http_status_code=frappe.ValidationError.http_status_code)
- update_integration_request_status(token, {
- "payerid": response.get("PAYERID")[0],
- "payer_email": response.get("EMAIL")[0]
- }, "Authorized")
+ return
- frappe.local.response["type"] = "redirect"
- frappe.local.response["location"] = get_url( \
- "/api/method/frappe.integrations.doctype.paypal_settings.paypal_settings.confirm_payment?token={0}".format(token))
+ update_integration_request_status(token, {
+ "payerid": response.get("PAYERID")[0],
+ "payer_email": response.get("EMAIL")[0]
+ }, "Authorized")
+
+ frappe.local.response["type"] = "redirect"
+ frappe.local.response["location"] = get_url( \
+ "/api/method/frappe.integrations.doctype.paypal_settings.paypal_settings.confirm_payment?token={0}".format(token))
+
+ except Exception:
+ frappe.log_error(frappe.get_traceback())
@frappe.whitelist(allow_guest=True, xss_safe=True)
def confirm_payment(token):
- redirect = True
- status_changed_to, redirect_to = None, None
- doc = frappe.get_doc("PayPal Settings")
- integration_request = frappe.get_doc("Integration Request", token)
- data = json.loads(integration_request.data)
-
- redirect_to = data.get('redirect_to') or None
- redirect_message = data.get('redirect_message') or None
-
- params, url = doc.get_paypal_params_and_url()
- params.update({
- "METHOD": "DoExpressCheckoutPayment",
- "PAYERID": data.get("payerid"),
- "TOKEN": token,
- "PAYMENTREQUEST_0_PAYMENTACTION": "SALE",
- "PAYMENTREQUEST_0_AMT": data.get("amount"),
- "PAYMENTREQUEST_0_CURRENCYCODE": data.get("currency").upper()
- })
+ try:
+ redirect = True
+ status_changed_to, redirect_to = None, None
- response = doc.post_request(url, data=params)
+ doc = frappe.get_doc("PayPal Settings")
+ doc.setup_sandbox_env(token)
- if response.get("ACK")[0] == "Success":
- update_integration_request_status(token, {
- "transaction_id": response.get("PAYMENTINFO_0_TRANSACTIONID")[0],
- "correlation_id": response.get("CORRELATIONID")[0]
- }, "Completed")
+ integration_request = frappe.get_doc("Integration Request", token)
+ data = json.loads(integration_request.data)
- if data.get("reference_doctype") and data.get("reference_docname"):
- redirect_url = frappe.get_doc(data.get("reference_doctype"), data.get("reference_docname")).run_method("on_payment_authorized", "Completed")
+ redirect_to = data.get('redirect_to') or None
+ redirect_message = data.get('redirect_message') or None
- if not redirect_url:
- redirect_url = '/integrations/payment-success'
- else:
- redirect_url = "/integrations/payment-failed"
+ params, url = doc.get_paypal_params_and_url()
+ params.update({
+ "METHOD": "DoExpressCheckoutPayment",
+ "PAYERID": data.get("payerid"),
+ "TOKEN": token,
+ "PAYMENTREQUEST_0_PAYMENTACTION": "SALE",
+ "PAYMENTREQUEST_0_AMT": data.get("amount"),
+ "PAYMENTREQUEST_0_CURRENCYCODE": data.get("currency").upper()
+ })
- if redirect_to:
- redirect_url += '?' + urllib.urlencode({'redirect_to': redirect_to})
- if redirect_message:
- redirect_url += '&' + urllib.urlencode({'redirect_message': redirect_message})
+ response = doc.post_request(url, data=params)
- # this is done so that functions called via hooks can update flags.redirect_to
- if redirect:
- frappe.local.response["type"] = "redirect"
- frappe.local.response["location"] = get_url(redirect_to)
+ if response.get("ACK")[0] == "Success":
+ update_integration_request_status(token, {
+ "transaction_id": response.get("PAYMENTINFO_0_TRANSACTIONID")[0],
+ "correlation_id": response.get("CORRELATIONID")[0]
+ }, "Completed")
+
+ if data.get("reference_doctype") and data.get("reference_docname"):
+ redirect_url = frappe.get_doc(data.get("reference_doctype"), data.get("reference_docname")).run_method("on_payment_authorized", "Completed")
+ frappe.db.commit()
+
+ if not redirect_url:
+ redirect_url = '/integrations/payment-success'
+ else:
+ redirect_url = "/integrations/payment-failed"
+
+ if redirect_to:
+ redirect_url += '?' + urllib.urlencode({'redirect_to': redirect_to})
+ if redirect_message:
+ redirect_url += '&' + urllib.urlencode({'redirect_message': redirect_message})
+
+ # this is done so that functions called via hooks can update flags.redirect_to
+ if redirect:
+ frappe.local.response["type"] = "redirect"
+ frappe.local.response["location"] = get_url(redirect_url)
+
+ except Exception:
+ frappe.log_error(frappe.get_traceback())
def update_integration_request_status(token, data, status, error=False):
frappe.get_doc("Integration Request", token).update_status(data, status)
diff --git a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py
index 5437bcfd17..e9877636ae 100644
--- a/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py
+++ b/frappe/integrations/doctype/razorpay_settings/razorpay_settings.py
@@ -2,13 +2,6 @@
# Copyright (c) 2015, Frappe Technologies and contributors
# For license information, please see license.txt
-from __future__ import unicode_literals
-import frappe
-from frappe.utils import get_url, call_hook_method
-from frappe import _
-import urllib, json
-from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService
-
"""
# Integrating RazorPay
@@ -58,6 +51,13 @@ For razorpay payment status is Authorized
"""
+from __future__ import unicode_literals
+import frappe
+from frappe.utils import get_url, call_hook_method, cint
+from frappe import _
+import urllib, json
+from frappe.integration_broker.doctype.integration_service.integration_service import IntegrationService
+
class RazorpaySettings(IntegrationService):
service_name = "Razorpay"
supported_currencies = ["INR"]
@@ -117,8 +117,9 @@ class RazorpaySettings(IntegrationService):
The money is deducted from the customer’s account, but will not be transferred to the merchant’s account
until it is explicitly captured by merchant.
"""
- settings = self.get_settings()
data = json.loads(self.integration_request.data)
+
+ settings = self.get_settings(data)
redirect_to = data.get('notes', {}).get('redirect_to') or None
redirect_message = data.get('notes', {}).get('redirect_message') or None
@@ -167,12 +168,20 @@ class RazorpaySettings(IntegrationService):
"status": status
}
- def get_settings(self):
- return frappe._dict({
+ def get_settings(self, data):
+ settings = frappe._dict({
"api_key": self.api_key,
"api_secret": self.get_password(fieldname="api_secret", raise_exception=False)
})
+ if cint(data.get('notes', {}).get('use_sandbox')):
+ settings.update({
+ "api_key": frappe.conf.sandbox_api_key,
+ "api_secret": frappe.conf.sandbox_api_secret,
+ })
+
+ return settings
+
def capture_payment(is_sandbox=False, sanbox_response=None):
"""
Verifies the purchase as complete by the merchant.
@@ -190,8 +199,10 @@ def capture_payment(is_sandbox=False, sanbox_response=None):
resp = sanbox_response
else:
data = json.loads(doc.data)
+ settings = controller.get_settings(data)
+
resp = controller.post_request("https://api.razorpay.com/v1/payments/{0}/capture".format(data.get("razorpay_payment_id")),
- auth=(controller.api_key, controller.get_password("api_secret")), data={"amount": data.get("amount")})
+ auth=(settings.api_key, settings.api_secret), data={"amount": data.get("amount")})
if resp.get("status") == "captured":
frappe.db.set_value("Integration Request", doc.name, "status", "Completed")
@@ -212,7 +223,6 @@ def get_checkout_url(**kwargs):
success=False,
http_status_code=frappe.ValidationError.http_status_code)
-
@frappe.whitelist()
def get_service_details():
return """
diff --git a/frappe/integrations/doctype/social_login_keys/social_login_keys.json b/frappe/integrations/doctype/social_login_keys/social_login_keys.json
index fdb469c65c..d35cdac6bd 100644
--- a/frappe/integrations/doctype/social_login_keys/social_login_keys.json
+++ b/frappe/integrations/doctype/social_login_keys/social_login_keys.json
@@ -2,26 +2,33 @@
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
+ "beta": 0,
"creation": "2014-03-04 08:29:52",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "System",
+ "editable_grid": 0,
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "facebook",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Facebook",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -33,16 +40,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "facebook_client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Facebook Client ID",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -54,16 +66,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "facebook_client_secret",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Facebook Client Secret",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -75,16 +92,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "google",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Google",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -96,16 +118,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "google_client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Google Client ID",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -117,16 +144,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "google_client_secret",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Google Client Secret",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -138,16 +170,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "github",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "GitHub",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -159,16 +196,21 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "github_client_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "GitHub Client ID",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -180,16 +222,129 @@
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
+ "columns": 0,
"fieldname": "github_client_secret",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "GitHub Client Secret",
+ "length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "frappe",
+ "fieldtype": "Section Break",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Frappe",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "frappe_client_id",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Frappe Client ID",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "frappe_client_secret",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Frappe Client Secret",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
+ "read_only": 0,
+ "report_hide": 0,
+ "reqd": 0,
+ "search_index": 0,
+ "set_only_once": 0,
+ "unique": 0
+ },
+ {
+ "allow_on_submit": 0,
+ "bold": 0,
+ "collapsible": 0,
+ "columns": 0,
+ "fieldname": "frappe_server_url",
+ "fieldtype": "Data",
+ "hidden": 0,
+ "ignore_user_permissions": 0,
+ "ignore_xss_filter": 0,
+ "in_filter": 0,
+ "in_list_view": 0,
+ "in_standard_filter": 0,
+ "label": "Frappe Server URL",
+ "length": 0,
+ "no_copy": 0,
+ "permlevel": 0,
+ "precision": "",
+ "print_hide": 0,
+ "print_hide_if_no_value": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
@@ -202,12 +357,14 @@
"hide_toolbar": 0,
"icon": "icon-signin",
"idx": 1,
+ "image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
- "modified": "2015-08-05 08:14:52.667728",
+ "max_attachments": 0,
+ "modified": "2016-10-29 13:36:35.121599",
"modified_by": "Administrator",
"module": "Integrations",
"name": "Social Login Keys",
@@ -223,6 +380,7 @@
"export": 0,
"if_owner": 0,
"import": 0,
+ "is_custom": 0,
"permlevel": 0,
"print": 0,
"read": 1,
@@ -234,6 +392,9 @@
"write": 1
}
],
+ "quick_entry": 0,
"read_only": 0,
- "read_only_onload": 0
+ "read_only_onload": 0,
+ "sort_order": "ASC",
+ "track_seen": 0
}
\ No newline at end of file
diff --git a/frappe/integrations/doctype/social_login_keys/social_login_keys.py b/frappe/integrations/doctype/social_login_keys/social_login_keys.py
index f447a4421d..5a4f662a99 100644
--- a/frappe/integrations/doctype/social_login_keys/social_login_keys.py
+++ b/frappe/integrations/doctype/social_login_keys/social_login_keys.py
@@ -7,6 +7,20 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
+from frappe import _
class SocialLoginKeys(Document):
- pass
\ No newline at end of file
+ def validate(self):
+ self.validate_frappe_server_url()
+
+ def validate_frappe_server_url(self):
+ if self.frappe_server_url:
+ if self.frappe_server_url.endswith('/'):
+ self.frappe_server_url = self.frappe_server_url[:-1]
+ import requests
+ try:
+ r = requests.get(self.frappe_server_url + "/api/method/frappe.handler.version", timeout=5)
+ except:
+ frappe.throw(_("Unable to make request to the Frappe Server URL"))
+ if r.status_code != 200:
+ frappe.throw(_("Check Frappe Server URL"))
diff --git a/frappe/model/base_document.py b/frappe/model/base_document.py
index 6db4ca70d9..ae01396150 100644
--- a/frappe/model/base_document.py
+++ b/frappe/model/base_document.py
@@ -190,12 +190,11 @@ class BaseDocument(object):
df = self.meta.get_field(fieldname)
if df:
if df.fieldtype=="Check":
- if (not isinstance(d[fieldname], int) or d[fieldname] > 1):
- d[fieldname] = 1 if cint(d[fieldname]) else 0
+ if d[fieldname]==None:
+ d[fieldname] = 0
- # get the default value if none, for insert / update
- elif d[fieldname]==None:
- d[fieldname] = df.get('default')
+ elif (not isinstance(d[fieldname], int) or d[fieldname] > 1):
+ d[fieldname] = 1 if cint(d[fieldname]) else 0
elif df.fieldtype=="Int" and not isinstance(d[fieldname], int):
d[fieldname] = cint(d[fieldname])
@@ -286,6 +285,7 @@ class BaseDocument(object):
self.created_by = self.modifield_by = frappe.session.user
d = self.get_valid_dict()
+
columns = d.keys()
try:
frappe.db.sql("""insert into `tab{doctype}`
@@ -360,8 +360,21 @@ class BaseDocument(object):
# this is used to preserve traceback
raise frappe.UniqueValidationError, (self.doctype, self.name, e), traceback
- def db_set(self, fieldname, value, update_modified=True):
- self.set(fieldname, value)
+ def db_set(self, fieldname, value=None, update_modified=True):
+ '''Set a value in the document object, update the timestamp and update the database.
+
+ WARNING: This method does not trigger controller validations and should
+ be used very carefully.
+
+ :param fieldname: fieldname of the property to be updated, or a {"field":"value"} dictionary
+ :param value: value of the property to be updated
+ :param update_modified: default True. updates the `modified` and `modified_by` properties
+ '''
+ if isinstance(fieldname, dict):
+ self.update(fieldname)
+ else:
+ self.set(fieldname, value)
+
if update_modified and (self.doctype, self.name) not in frappe.flags.currently_saving:
# don't update modified timestamp if called from post save methods
# like on_update or on_submit
@@ -426,6 +439,7 @@ class BaseDocument(object):
return missing
def get_invalid_links(self, is_submittable=False):
+ '''Returns list of invalid links and also updates fetch values if not set'''
def get_msg(df, docname):
if self.parentfield:
return "{} #{}: {}: {}".format(_("Row"), self.idx, _(df.label), docname)
@@ -434,6 +448,7 @@ class BaseDocument(object):
invalid_links = []
cancelled_links = []
+
for df in (self.meta.get_link_fields()
+ self.meta.get("fields", {"fieldtype":"Dynamic Link"})):
docname = self.get(df.fieldname)
@@ -449,15 +464,38 @@ class BaseDocument(object):
frappe.throw(_("{0} must be set first").format(self.meta.get_label(df.options)))
# MySQL is case insensitive. Preserve case of the original docname in the Link Field.
- value = frappe.db.get_value(doctype, docname, "name", cache=True)
- if frappe.get_meta(doctype).issingle:
- value = doctype
- setattr(self, df.fieldname, value)
+ # get a map of values ot fetch along with this link query
+ # that are mapped as link_fieldname.source_fieldname in Options of
+ # Readonly or Data or Text type fields
+ fields_to_fetch = [
+ _df for _df in self.meta.get_fields_to_fetch(df.fieldname)
+ if not self.get(_df.fieldname)
+ ]
+
+ if not fields_to_fetch:
+ # cache a single value type
+ values = frappe._dict(name=frappe.db.get_value(doctype, docname,
+ 'name', cache=True))
+ else:
+ values_to_fetch = ['name'] + [_df.options.split('.')[-1]
+ for _df in fields_to_fetch]
+
+ # don't cache if fetching other values too
+ values = frappe.db.get_value(doctype, docname,
+ values_to_fetch, as_dict=True)
+
+ if frappe.get_meta(doctype).issingle:
+ values.name = doctype
+
+ setattr(self, df.fieldname, values.name)
+
+ for _df in fields_to_fetch:
+ setattr(self, _df.fieldname, values[_df.options.split('.')[-1]])
notify_link_count(doctype, docname)
- if not value:
+ if not values.name:
invalid_links.append((df.fieldname, docname, get_msg(df, docname)))
elif (df.fieldname != "amended_from"
diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py
index 3c38be9170..d64eef4976 100644
--- a/frappe/model/db_query.py
+++ b/frappe/model/db_query.py
@@ -8,7 +8,7 @@ import frappe, json, copy
import frappe.defaults
import frappe.share
import frappe.permissions
-from frappe.utils import flt, cint, getdate, get_datetime, get_time, make_filter_tuple, get_filter
+from frappe.utils import flt, cint, getdate, get_datetime, get_time, make_filter_tuple, get_filter, add_to_date
from frappe import _
from frappe.model import optional_fields
from frappe.model.utils.list_settings import get_list_settings, update_list_settings
@@ -291,7 +291,13 @@ class DatabaseQuery(object):
if df and df.fieldtype in ("Check", "Float", "Int", "Currency", "Percent"):
can_be_null = False
- if df and df.fieldtype=="Date":
+ if f.operator=='Between' and \
+ (f.fieldname in ('creation', 'modified') or (df and (df.fieldtype=="Date" or df.fieldtype=="Datetime"))):
+ value = "'%s' AND '%s'" % (
+ get_datetime(f.value[0]).strftime("%Y-%m-%d %H:%M:%S.%f"),
+ add_to_date(get_datetime(f.value[1]),days=1).strftime("%Y-%m-%d %H:%M:%S.%f"))
+ fallback = "'0000-00-00 00:00:00'"
+ elif df and df.fieldtype=="Date":
value = getdate(f.value).strftime("%Y-%m-%d")
fallback = "'0000-00-00'"
@@ -317,7 +323,7 @@ class DatabaseQuery(object):
fallback = 0
# put it inside double quotes
- if isinstance(value, basestring):
+ if isinstance(value, basestring) and not f.operator=='Between':
value = '"{0}"'.format(frappe.db.escape(value, percent=False))
if (self.ignore_ifnull
diff --git a/frappe/model/document.py b/frappe/model/document.py
index 72050cad5c..af6310895e 100644
--- a/frappe/model/document.py
+++ b/frappe/model/document.py
@@ -3,14 +3,17 @@
from __future__ import unicode_literals
import frappe
+import time
from frappe import _, msgprint
-from frappe.utils import flt, cstr, now, get_datetime_str
+from frappe.utils import flt, cstr, now, get_datetime_str, file_lock
from frappe.utils.background_jobs import enqueue
from frappe.model.base_document import BaseDocument, get_controller
from frappe.model.naming import set_new_name
from werkzeug.exceptions import NotFound, Forbidden
import hashlib, json
from frappe.model import optional_fields
+from frappe.utils.file_manager import save_url
+
# once_only validation
# methods
@@ -156,23 +159,6 @@ class Document(BaseDocument):
frappe.msgprint(msg)
raise frappe.PermissionError(msg)
- def lock(self):
- '''Will set docstatus to 3 + the current docstatus and mark it as queued
-
- 3 = queued for saving
- 4 = queued for submission
- 5 = queued for cancellation
- '''
- self.db_set('docstatus', 3 + self.docstatus, update_modified = False)
-
- def unlock(self):
- '''set the original docstatus at the time it was locked in the controller'''
- current_docstatus = self.db_get('docstatus') - 4
- if current_docstatus < 0:
- current_docstatus = 0
-
- self.db_set('docstatus', current_docstatus, update_modified = False)
-
def insert(self, ignore_permissions=None):
"""Insert the document in the database (as a new document).
This will check for user permissions and execute `before_insert`,
@@ -219,6 +205,10 @@ class Document(BaseDocument):
self.run_method("after_insert")
self.flags.in_insert = True
+
+ if self.get("amended_from"):
+ self.copy_attachments_from_amended_from()
+
self.run_post_save_methods()
self.flags.in_insert = False
@@ -280,6 +270,16 @@ class Document(BaseDocument):
return self
+ def copy_attachments_from_amended_from(self):
+ '''Copy attachments from `amended_from`'''
+ from frappe.desk.form.load import get_attachments
+
+ #loop through attachments
+ for attach_item in get_attachments(self.doctype, self.amended_from):
+
+ #save attachments to new doc
+ save_url(attach_item.file_url, attach_item.file_name, self.doctype, self.name, "Home/Attachments")
+
def update_children(self):
'''update child tables'''
for df in self.meta.get_table_fields():
@@ -498,8 +498,10 @@ class Document(BaseDocument):
self._action = "save"
if not self.get('__islocal'):
if self.meta.issingle:
- modified = frappe.db.get_value(self.doctype, self.name, "modified")
- if cstr(modified) and cstr(modified) != cstr(self._original_modified):
+ modified = frappe.db.sql('''select value from tabSingles
+ where doctype=%s and field='modified' for update''', self.doctype)
+ modified = modified and modified[0][0]
+ if modified and modified != cstr(self._original_modified):
conflict = True
else:
tmp = frappe.db.sql("""select modified, docstatus from `tab{0}`
@@ -534,13 +536,7 @@ class Document(BaseDocument):
- Submit (1) > Submit (1)
- Submit (1) > Cancel (2)
- If docstatus is > 2, it will throw exception as document is deemed queued
"""
-
- if self.docstatus > 2:
- frappe.throw(_('This document is currently queued for execution. Please try again'),
- title=_('Document Queued'), indicator='red')
-
if not self.docstatus:
self.docstatus = 0
if docstatus==0:
@@ -796,27 +792,6 @@ class Document(BaseDocument):
def clear_cache(self):
frappe.cache().hdel("last_modified", self.doctype)
- self.clear_linked_with_cache()
-
- def clear_linked_with_cache(self):
- cache = frappe.cache()
- def _clear_cache(d):
- for df in (d.meta.get_link_fields() + d.meta.get_dynamic_link_fields()):
- if d.get(df.fieldname):
- doctype = df.options if df.fieldtype=="Link" else d.get(df.options)
- name = d.get(df.fieldname)
-
- if df.fieldtype=="Dynamic Link":
- # clear linked doctypes list
- cache.hdel("linked_doctypes", doctype)
-
- # for all users, delete linked with cache and per doctype linked with cache
- cache.delete_value("user:*:linked_with:{doctype}:{name}".format(doctype=doctype, name=name))
- cache.delete_value("user:*:linked_with:{doctype}:{name}:*".format(doctype=doctype, name=name))
-
- _clear_cache(self)
- for d in self.get_all_children():
- _clear_cache(d)
def reset_seen(self):
'''Clear _seen property and set current user as seen'''
@@ -1018,28 +993,46 @@ class Document(BaseDocument):
def queue_action(self, action, **kwargs):
'''Run an action in background. If the action has an inner function,
like _submit for submit, it will call that instead'''
- if action in ('save', 'submit', 'cancel'):
- # set docstatus explicitly again due to inconsistent action
- self.docstatus = {'save':0, 'submit':1, 'cancel': 2}[action]
- else:
- raise 'Action must be one of save, submit, cancel'
-
# call _submit instead of submit, so you can override submit to call
# run_delayed based on some action
# See: Stock Reconciliation
if hasattr(self, '_' + action):
action = '_' + action
+ if file_lock.lock_exists(self.get_signature()):
+ frappe.throw(_('This document is currently queued for execution. Please try again'),
+ title=_('Document Queued'), indicator='red')
+
self.lock()
- frappe.db.commit()
enqueue('frappe.model.document.execute_action', doctype=self.doctype, name=self.name,
action=action, **kwargs)
+ def lock(self, timeout=None):
+ '''Creates a lock file for the given document. If timeout is set,
+ it will retry every 1 second for acquiring the lock again
+
+ :param timeout: Timeout in seconds, default 0'''
+ signature = self.get_signature()
+ if file_lock.lock_exists(signature):
+ lock_exists = True
+ if timeout:
+ for i in range(timeout):
+ time.sleep(1)
+ if not file_lock.lock_exists(signature):
+ lock_exists = False
+ break
+ if lock_exists:
+ raise frappe.DocumentLockedError
+ file_lock.create_lock(signature)
+
+ def unlock(self):
+ '''Delete the lock file for this document'''
+ file_lock.delete_lock(self.get_signature())
+
def execute_action(doctype, name, action, **kwargs):
'''Execute an action on a document (called by background worker)'''
doc = frappe.get_doc(doctype, name)
doc.unlock()
- frappe.db.commit()
try:
getattr(doc, action)(**kwargs)
except Exception:
diff --git a/frappe/model/mapper.py b/frappe/model/mapper.py
index ff227ab174..d8384eee09 100644
--- a/frappe/model/mapper.py
+++ b/frappe/model/mapper.py
@@ -7,6 +7,25 @@ from frappe import _
from frappe.utils import cstr
from frappe.model import default_fields
+@frappe.whitelist()
+def make_mapped_doc(method, source_name, selected_children=None):
+ '''Returns the mapped document calling the given mapper method.
+ Sets selected_children as flags for the `get_mapped_doc` method.
+
+ Called from `open_mapped_doc` from create_new.js'''
+ method = frappe.get_attr(method)
+
+ if method not in frappe.whitelisted:
+ raise frappe.PermissionError
+
+ if selected_children:
+ selected_children = json.loads(selected_children)
+
+ frappe.flags.selected_children = selected_children or None
+
+ return method(source_name)
+
+
def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None,
postprocess=None, ignore_permissions=False, ignore_child_tables=False):
@@ -51,6 +70,13 @@ def get_mapped_doc(from_doctype, from_docname, table_maps, target_doc=None,
if not table_map["condition"](source_d):
continue
+ # if children are selected (checked from UI) for this table type,
+ # and this record is not in the selected children, then continue
+ if (frappe.flags.selected_children
+ and (df.fieldname in frappe.flags.selected_children)
+ and source_d.name not in frappe.flags.selected_children[df.fieldname]):
+ continue
+
target_child_doctype = table_map["doctype"]
target_parentfield = target_doc.get_parentfield_of_doctype(target_child_doctype)
diff --git a/frappe/model/meta.py b/frappe/model/meta.py
index 3435126147..5b7fa083ed 100644
--- a/frappe/model/meta.py
+++ b/frappe/model/meta.py
@@ -59,7 +59,10 @@ class Meta(Document):
def __init__(self, doctype):
self._fields = {}
- super(Meta, self).__init__("DocType", doctype)
+ if isinstance(doctype, Document):
+ super(Meta, self).__init__(doctype.as_dict())
+ else:
+ super(Meta, self).__init__("DocType", doctype)
self.process()
def load_from_db(self):
@@ -155,6 +158,32 @@ class Meta(Document):
return search_fields
+ def get_fields_to_fetch(self, link_fieldname=None):
+ '''Returns a list of docfield objects for fields whose values
+ are to be fetched and updated for a particular link field
+
+ These fields are of type Data, Link, Text, Readonly and their
+ options property is set as `link_fieldname`.`source_fieldname`'''
+
+ out = []
+
+ if not link_fieldname:
+ link_fields = [df.fieldname for df in self.get_link_fields()]
+
+ for df in self.fields:
+ if df.fieldtype in ('Data', 'Read Only', 'Text', 'Small Text',
+ 'Text Editor', 'Code') and df.options:
+ if link_fieldname:
+ if df.options.startswith(link_fieldname + '.'):
+ out.append(df)
+ else:
+ if '.' in df.options:
+ fieldname = df.options.split('.', 1)[0]
+ if fieldname in link_fields:
+ out.append(df)
+
+ return out
+
def get_list_fields(self):
list_fields = ["name"] + [d.fieldname \
for d in self.fields if (d.in_list_view and d.fieldtype in type_map)]
@@ -424,9 +453,6 @@ def clear_cache(doctype=None):
for name in groups:
cache.hdel(name, dt)
- # also clear linked_with list cache
- cache.delete_keys("user:*:linked_with:{doctype}:".format(doctype=doctype))
-
if doctype:
clear_single(doctype)
diff --git a/frappe/modules/utils.py b/frappe/modules/utils.py
index 35ff2b4691..fb1f369a04 100644
--- a/frappe/modules/utils.py
+++ b/frappe/modules/utils.py
@@ -170,8 +170,7 @@ def load_doctype_module(doctype, module=None, prefix="", suffix=""):
if key not in doctype_python_modules:
doctype_python_modules[key] = frappe.get_module(module_name)
except ImportError:
- print 'Module import failed for {0} ({1})'.format(doctype, module_name)
- raise
+ raise ImportError, 'Module import failed for {0} ({1})'.format(doctype, module_name)
return doctype_python_modules[key]
diff --git a/frappe/oauth.py b/frappe/oauth.py
index 22e1f17562..89f00c41a6 100644
--- a/frappe/oauth.py
+++ b/frappe/oauth.py
@@ -1,8 +1,9 @@
import frappe, urllib
+from frappe import _
from urlparse import parse_qs, urlparse
from oauthlib.oauth2.rfc6749.tokens import BearerToken
-from oauthlib.oauth2.rfc6749.grant_types import AuthorizationCodeGrant, ImplicitGrant, ResourceOwnerPasswordCredentialsGrant, ClientCredentialsGrant, RefreshTokenGrant
+from oauthlib.oauth2.rfc6749.grant_types import AuthorizationCodeGrant, ImplicitGrant, ResourceOwnerPasswordCredentialsGrant, ClientCredentialsGrant, RefreshTokenGrant, OpenIDConnectAuthCode
from oauthlib.oauth2 import RequestValidator
from oauthlib.oauth2.rfc6749.endpoints.authorization import AuthorizationEndpoint
from oauthlib.oauth2.rfc6749.endpoints.token import TokenEndpoint
@@ -10,6 +11,9 @@ from oauthlib.oauth2.rfc6749.endpoints.resource import ResourceEndpoint
from oauthlib.oauth2.rfc6749.endpoints.revocation import RevocationEndpoint
from oauthlib.common import Request
+def get_url_delimiter(separator_character=" "):
+ return separator_character
+
class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoint,
RevocationEndpoint):
@@ -32,10 +36,19 @@ class WebApplicationServer(AuthorizationEndpoint, TokenEndpoint, ResourceEndpoin
"""
auth_grant = AuthorizationCodeGrant(request_validator)
refresh_grant = RefreshTokenGrant(request_validator)
+ openid_connect_auth = OpenIDConnectAuthCode(request_validator)
bearer = BearerToken(request_validator, token_generator,
token_expires_in, refresh_token_generator)
AuthorizationEndpoint.__init__(self, default_response_type='code',
- response_types={'code': auth_grant},
+ response_types={
+ 'code': auth_grant,
+ 'code+token': openid_connect_auth,
+ 'code+id_token': openid_connect_auth,
+ 'code+token+id_token': openid_connect_auth,
+ 'code token': openid_connect_auth,
+ 'code id_token': openid_connect_auth,
+ 'code token id_token': openid_connect_auth,
+ },
default_token_type=bearer)
TokenEndpoint.__init__(self, default_grant_type='authorization_code',
grant_types={
@@ -64,7 +77,7 @@ class OAuthWebRequestValidator(RequestValidator):
# Is the client allowed to use the supplied redirect_uri? i.e. has
# the client previously registered this EXACT redirect uri.
- redirect_uris = frappe.db.get_value("OAuth Client", client_id, 'redirect_uris').split(';')
+ redirect_uris = frappe.db.get_value("OAuth Client", client_id, 'redirect_uris').split(get_url_delimiter())
if redirect_uri in redirect_uris:
return True
@@ -80,7 +93,7 @@ class OAuthWebRequestValidator(RequestValidator):
def validate_scopes(self, client_id, scopes, client, request, *args, **kwargs):
# Is the client allowed to access the requested scopes?
- client_scopes = frappe.db.get_value("OAuth Client", client_id, 'scopes').split(';')
+ client_scopes = frappe.db.get_value("OAuth Client", client_id, 'scopes').split(get_url_delimiter())
are_scopes_valid = True
@@ -92,7 +105,7 @@ class OAuthWebRequestValidator(RequestValidator):
def get_default_scopes(self, client_id, request, *args, **kwargs):
# Scopes a client will authorize for if none are supplied in the
# authorization request.
- scopes = frappe.db.get_value("OAuth Client", client_id, 'scopes').split(';')
+ scopes = frappe.db.get_value("OAuth Client", client_id, 'scopes').split(get_url_delimiter())
request.scopes = scopes #Apparently this is possible.
return scopes
@@ -100,8 +113,11 @@ class OAuthWebRequestValidator(RequestValidator):
# Clients should only be allowed to use one type of response type, the
# one associated with their one allowed grant type.
# In this case it must be "code".
+ allowed_response_types = [client.response_type.lower(),
+ "code token", "code id_token", "code token id_token",
+ "code+token", "code+id_token", "code+token id_token"]
- return (client.response_type.lower() == response_type)
+ return (response_type in allowed_response_types)
# Post-authorization
@@ -111,7 +127,7 @@ class OAuthWebRequestValidator(RequestValidator):
cookie_dict = get_cookie_dict_from_headers(request)
oac = frappe.new_doc('OAuth Authorization Code')
- oac.scopes = ';'.join(request.scopes)
+ oac.scopes = get_url_delimiter().join(request.scopes)
oac.redirect_uri_bound_to_authorization_code = request.redirect_uri
oac.client = client_id
oac.user = urllib.unquote(cookie_dict['user_id'])
@@ -130,8 +146,10 @@ class OAuthWebRequestValidator(RequestValidator):
#Extract token, instantiate OAuth Bearer Token and use clientid from there.
if frappe.form_dict.has_key("refresh_token"):
oc = frappe.get_doc("OAuth Client", frappe.db.get_value("OAuth Bearer Token", {"refresh_token": frappe.form_dict["refresh_token"]}, 'client'))
- else:
+ elif frappe.form_dict.has_key("token"):
oc = frappe.get_doc("OAuth Client", frappe.db.get_value("OAuth Bearer Token", frappe.form_dict["token"], 'client'))
+ else:
+ oc = frappe.get_doc("OAuth Client", frappe.db.get_value("OAuth Bearer Token", frappe.get_request_header("Authorization").split(" ")[1], 'client'))
try:
request.client = request.client or oc.as_dict()
except Exception, e:
@@ -159,7 +177,7 @@ class OAuthWebRequestValidator(RequestValidator):
checkcodes.append(vcode["name"])
if code in checkcodes:
- request.scopes = frappe.db.get_value("OAuth Authorization Code", code, 'scopes').split(';')
+ request.scopes = frappe.db.get_value("OAuth Authorization Code", code, 'scopes').split(get_url_delimiter())
request.user = frappe.db.get_value("OAuth Authorization Code", code, 'user')
return True
else:
@@ -185,7 +203,7 @@ class OAuthWebRequestValidator(RequestValidator):
otoken = frappe.new_doc("OAuth Bearer Token")
otoken.client = request.client['name']
otoken.user = request.user
- otoken.scopes = ";".join(request.scopes)
+ otoken.scopes = get_url_delimiter().join(request.scopes)
otoken.access_token = token['access_token']
otoken.refresh_token = token['refresh_token']
otoken.expires_in = token['expires_in']
@@ -209,7 +227,7 @@ class OAuthWebRequestValidator(RequestValidator):
otoken = frappe.get_doc("OAuth Bearer Token", token) #{"access_token": str(token)})
is_token_valid = (frappe.utils.datetime.datetime.now() < otoken.expiration_time) \
and otoken.status != "Revoked"
- client_scopes = frappe.db.get_value("OAuth Client", otoken.client, 'scopes').split(';')
+ client_scopes = frappe.db.get_value("OAuth Client", otoken.client, 'scopes').split(get_url_delimiter())
are_scopes_valid = True
for scp in scopes:
are_scopes_valid = are_scopes_valid and True if scp in client_scopes else False
@@ -261,7 +279,6 @@ class OAuthWebRequestValidator(RequestValidator):
# - Resource Owner Password Credentials Grant (also indirectly)
# - Refresh Token Grant
# """
- # raise NotImplementedError('Subclasses must implement this method.')
otoken = frappe.get_doc("OAuth Bearer Token", {"refresh_token": refresh_token, "status": "Active"})
@@ -270,7 +287,101 @@ class OAuthWebRequestValidator(RequestValidator):
else:
return True
- #TODO: Validate scopes.
+ # OpenID Connect
+ def get_id_token(self, token, token_handler, request):
+ """
+ In the OpenID Connect workflows when an ID Token is requested this method is called.
+ Subclasses should implement the construction, signing and optional encryption of the
+ ID Token as described in the OpenID Connect spec.
+
+ In addition to the standard OAuth2 request properties, the request may also contain
+ these OIDC specific properties which are useful to this method:
+
+ - nonce, if workflow is implicit or hybrid and it was provided
+ - claims, if provided to the original Authorization Code request
+
+ The token parameter is a dict which may contain an ``access_token`` entry, in which
+ case the resulting ID Token *should* include a calculated ``at_hash`` claim.
+
+ Similarly, when the request parameter has a ``code`` property defined, the ID Token
+ *should* include a calculated ``c_hash`` claim.
+
+ http://openid.net/specs/openid-connect-core-1_0.html (sections `3.1.3.6`_, `3.2.2.10`_, `3.3.2.11`_)
+
+ .. _`3.1.3.6`: http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
+ .. _`3.2.2.10`: http://openid.net/specs/openid-connect-core-1_0.html#ImplicitIDToken
+ .. _`3.3.2.11`: http://openid.net/specs/openid-connect-core-1_0.html#HybridIDToken
+
+ :param token: A Bearer token dict
+ :param token_handler: the token handler (BearerToken class)
+ :param request: the HTTP Request (oauthlib.common.Request)
+ :return: The ID Token (a JWS signed JWT)
+ """
+ # the request.scope should be used by the get_id_token() method to determine which claims to include in the resulting id_token
+
+ def validate_silent_authorization(self, request):
+ """Ensure the logged in user has authorized silent OpenID authorization.
+
+ Silent OpenID authorization allows access tokens and id tokens to be
+ granted to clients without any user prompt or interaction.
+
+ :param request: The HTTP Request (oauthlib.common.Request)
+ :rtype: True or False
+
+ Method is used by:
+ - OpenIDConnectAuthCode
+ - OpenIDConnectImplicit
+ - OpenIDConnectHybrid
+ """
+ if request.prompt == "login":
+ False
+ else:
+ True
+
+ def validate_silent_login(self, request):
+ """Ensure session user has authorized silent OpenID login.
+
+ If no user is logged in or has not authorized silent login, this
+ method should return False.
+
+ If the user is logged in but associated with multiple accounts and
+ not selected which one to link to the token then this method should
+ raise an oauthlib.oauth2.AccountSelectionRequired error.
+
+ :param request: The HTTP Request (oauthlib.common.Request)
+ :rtype: True or False
+
+ Method is used by:
+ - OpenIDConnectAuthCode
+ - OpenIDConnectImplicit
+ - OpenIDConnectHybrid
+ """
+ if frappe.session.user == "Guest" or request.prompt.lower() == "login":
+ return False
+ else:
+ return True
+
+ def validate_user_match(self, id_token_hint, scopes, claims, request):
+ """Ensure client supplied user id hint matches session user.
+
+ If the sub claim or id_token_hint is supplied then the session
+ user must match the given ID.
+
+ :param id_token_hint: User identifier string.
+ :param scopes: List of OAuth 2 scopes and OpenID claims (strings).
+ :param claims: OpenID Connect claims dict.
+ :param request: The HTTP Request (oauthlib.common.Request)
+ :rtype: True or False
+
+ Method is used by:
+ - OpenIDConnectAuthCode
+ - OpenIDConnectImplicit
+ - OpenIDConnectHybrid
+ """
+ if id_token_hint and id_token_hint == frappe.get_value("User", frappe.session.user, "frappe_userid"):
+ return True
+ else:
+ return False
def get_cookie_dict_from_headers(r):
if r.headers.get('Cookie'):
@@ -280,3 +391,39 @@ def get_cookie_dict_from_headers(r):
return cookie_dict
else:
return {}
+
+def calculate_at_hash(access_token, hash_alg):
+ """Helper method for calculating an access token
+ hash, as described in http://openid.net/specs/openid-connect-core-1_0.html#CodeIDToken
+ Its value is the base64url encoding of the left-most half of the hash of the octets
+ of the ASCII representation of the access_token value, where the hash algorithm
+ used is the hash algorithm used in the alg Header Parameter of the ID Token's JOSE
+ Header. For instance, if the alg is RS256, hash the access_token value with SHA-256,
+ then take the left-most 128 bits and base64url encode them. The at_hash value is a
+ case sensitive string.
+ Args:
+ access_token (str): An access token string.
+ hash_alg (callable): A callable returning a hash object, e.g. hashlib.sha256
+ """
+ hash_digest = hash_alg(access_token.encode('utf-8')).digest()
+ cut_at = int(len(hash_digest) / 2)
+ truncated = hash_digest[:cut_at]
+ from jwt.utils import base64url_encode
+ at_hash = base64url_encode(truncated)
+ return at_hash.decode('utf-8')
+
+def delete_oauth2_data():
+ # Delete Invalid Authorization Code and Revoked Token
+ commit_code, commit_token = False, False
+ code_list = frappe.get_all("OAuth Authorization Code", filters={"validity":"Invalid"})
+ token_list = frappe.get_all("OAuth Bearer Token", filters={"status":"Revoked"})
+ if len(code_list) > 0:
+ commit_code = True
+ if len(token_list) > 0:
+ commit_token = True
+ for code in code_list:
+ frappe.delete_doc("OAuth Authorization Code", code["name"])
+ for token in token_list:
+ frappe.delete_doc("OAuth Bearer Token", token["name"])
+ if commit_code or commit_token:
+ frappe.db.commit()
diff --git a/frappe/patches.txt b/frappe/patches.txt
index 6c26f4aa4c..fd2e2a2cec 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -146,4 +146,6 @@ execute:frappe.db.set_default('language', '')
frappe.patches.v7_1.refactor_integration_broker
frappe.patches.v7_1.set_backup_limit
frappe.patches.v7_1.disabled_print_settings_for_custom_print_format
+frappe.patches.v7_2.set_doctype_engine
+frappe.patches.v7_2.merge_knowledge_base
frappe.patches.v7_0.update_report_builder_json
diff --git a/frappe/patches/v7_2/__init__.py b/frappe/patches/v7_2/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/patches/v7_2/merge_knowledge_base.py b/frappe/patches/v7_2/merge_knowledge_base.py
new file mode 100644
index 0000000000..2eb52acfd6
--- /dev/null
+++ b/frappe/patches/v7_2/merge_knowledge_base.py
@@ -0,0 +1,29 @@
+import frappe
+
+from frappe.patches.v7_0.re_route import update_routes
+from frappe.installer import remove_from_installed_apps
+
+def execute():
+ if 'knowledge_base' in frappe.get_installed_apps():
+ frappe.reload_doc('website', 'doctype', 'help_category')
+ frappe.reload_doc('website', 'doctype', 'help_article')
+ update_routes(['Help Category', 'Help Article'])
+ remove_from_installed_apps('knowledge_base')
+
+ # remove desktop icon
+ desktop_icon_name = frappe.db.get_value('Desktop Icon',
+ dict(module_name='Knowledge Base', type='module'))
+ if desktop_icon_name:
+ frappe.delete_doc('Desktop Icon', desktop_icon_name)
+
+ # remove module def
+ if frappe.db.exists('Module Def', 'Knowledge Base'):
+ frappe.delete_doc('Module Def', 'Knowledge Base')
+
+ # set missing routes
+ for doctype in ('Help Category', 'Help Article'):
+ for d in frappe.get_all(doctype, fields=['name', 'route']):
+ if not d.route:
+ doc = frappe.get_doc(doctype, d.name)
+ doc.set_route()
+ doc.db_update()
\ No newline at end of file
diff --git a/frappe/patches/v7_2/set_doctype_engine.py b/frappe/patches/v7_2/set_doctype_engine.py
new file mode 100644
index 0000000000..fa914003f3
--- /dev/null
+++ b/frappe/patches/v7_2/set_doctype_engine.py
@@ -0,0 +1,6 @@
+import frappe
+
+def execute():
+ for t in frappe.db.sql('show table status'):
+ if t[0].startswith('tab'):
+ frappe.db.sql('update tabDocType set engine=%s where name=%s', (t[1], t[0][3:]))
\ No newline at end of file
diff --git a/frappe/print/doctype/letter_head/letter_head.json b/frappe/print/doctype/letter_head/letter_head.json
index 432cc20067..706c7c0729 100644
--- a/frappe/print/doctype/letter_head/letter_head.json
+++ b/frappe/print/doctype/letter_head/letter_head.json
@@ -146,7 +146,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-font",
+ "icon": "fa fa-font",
"idx": 1,
"in_create": 0,
"in_dialog": 0,
diff --git a/frappe/print/doctype/print_format/print_format.json b/frappe/print/doctype/print_format/print_format.json
index a8e202f71e..efe9c93556 100644
--- a/frappe/print/doctype/print_format/print_format.json
+++ b/frappe/print/doctype/print_format/print_format.json
@@ -24,6 +24,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 1,
+ "in_standard_filter": 1,
"label": "DocType",
"length": 0,
"no_copy": 0,
@@ -32,6 +33,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
@@ -50,6 +52,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Module",
"length": 0,
"no_copy": 0,
@@ -59,6 +62,7 @@
"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,
@@ -77,6 +81,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 0,
@@ -84,6 +89,7 @@
"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,
@@ -102,12 +108,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -127,6 +135,7 @@
"ignore_xss_filter": 0,
"in_filter": 1,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Standard",
"length": 0,
"no_copy": 1,
@@ -137,6 +146,7 @@
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
+ "remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
@@ -155,6 +165,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Custom Format",
"length": 0,
"no_copy": 0,
@@ -163,6 +174,7 @@
"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,
@@ -182,12 +194,14 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"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,
@@ -209,6 +223,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Format Type",
"length": 0,
"no_copy": 0,
@@ -217,6 +232,7 @@
"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,
@@ -236,6 +252,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "HTML",
"length": 0,
"no_copy": 0,
@@ -246,6 +263,7 @@
"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,
@@ -265,6 +283,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Style Settings",
"length": 0,
"no_copy": 0,
@@ -273,6 +292,7 @@
"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,
@@ -292,6 +312,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Align Labels to the Left",
"length": 0,
"no_copy": 0,
@@ -300,6 +321,7 @@
"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,
@@ -319,6 +341,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Show Section Headings",
"length": 0,
"no_copy": 0,
@@ -327,6 +350,7 @@
"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,
@@ -346,6 +370,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Show Line Breaks after Sections",
"length": 0,
"no_copy": 0,
@@ -354,6 +379,7 @@
"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,
@@ -372,6 +398,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -379,6 +406,7 @@
"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,
@@ -399,6 +427,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Font",
"length": 0,
"no_copy": 0,
@@ -408,6 +437,7 @@
"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,
@@ -426,6 +456,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -433,6 +464,7 @@
"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,
@@ -451,6 +483,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Custom CSS",
"length": 0,
"no_copy": 0,
@@ -459,6 +492,7 @@
"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,
@@ -477,6 +511,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Custom HTML Help",
"length": 0,
"no_copy": 0,
@@ -486,6 +521,7 @@
"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,
@@ -505,6 +541,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -512,6 +549,7 @@
"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,
@@ -531,6 +569,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Format Help",
"length": 0,
"no_copy": 0,
@@ -539,6 +578,7 @@
"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,
@@ -557,6 +597,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Format Data",
"length": 0,
"no_copy": 0,
@@ -565,6 +606,7 @@
"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,
@@ -583,6 +625,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_list_view": 0,
+ "in_standard_filter": 0,
"label": "Print Format Builder",
"length": 0,
"no_copy": 0,
@@ -591,6 +634,7 @@
"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,
@@ -600,7 +644,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-print",
+ "icon": "fa fa-print",
"idx": 1,
"image_view": 0,
"in_create": 0,
@@ -609,7 +653,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
- "modified": "2016-10-10 06:06:34.530193",
+ "modified": "2016-11-07 05:23:46.983533",
"modified_by": "Administrator",
"module": "Print",
"name": "Print Format",
diff --git a/frappe/print/doctype/print_settings/print_settings.json b/frappe/print/doctype/print_settings/print_settings.json
index d6d31a62bd..569dfd5386 100644
--- a/frappe/print/doctype/print_settings/print_settings.json
+++ b/frappe/print/doctype/print_settings/print_settings.json
@@ -485,7 +485,7 @@
],
"hide_heading": 0,
"hide_toolbar": 0,
- "icon": "icon-cog",
+ "icon": "fa fa-cog",
"idx": 0,
"image_view": 0,
"in_create": 0,
diff --git a/frappe/print/page/print_format_builder/print_format_builder_sidebar.html b/frappe/print/page/print_format_builder/print_format_builder_sidebar.html
index 38152191db..5c288319dc 100644
--- a/frappe/print/page/print_format_builder/print_format_builder_sidebar.html
+++ b/frappe/print/page/print_format_builder/print_format_builder_sidebar.html
@@ -5,7 +5,7 @@
{% if(data.communication_type==="Communication" || (data.communication_type==="Comment" && data.comment_type==="Comment")) { %}
-
-
+
+
{%= data.content_html %}
{% } else if(in_list(["Assignment Completed", "Assigned", "Shared", "Unshared"], data.comment_type)) { %}
{% } else { %}
-
+
{% if (data.comment_type == "Like") { %}
{% if (data.timeline_doctype===data.frm.doc.doctype && data.timeline_name===data.frm.doc.name) { %}
@@ -130,12 +130,12 @@
{% if(data.attachments && data.attachments.length) { %}
{% $.each(data.attachments, function(i, a) { %}
-
+
diff --git a/frappe/public/js/frappe/form/formatters.js b/frappe/public/js/frappe/form/formatters.js
index 44c3c3f74e..2ab935c9a6 100644
--- a/frappe/public/js/frappe/form/formatters.js
+++ b/frappe/public/js/frappe/form/formatters.js
@@ -59,7 +59,7 @@ frappe.form.formatters = {
if(value) {
return '
';
} else {
- return '';
+ return '';
}
},
Link: function(value, docfield, options, doc) {
@@ -80,6 +80,9 @@ frappe.form.formatters = {
if(!value) {
return "";
}
+ if(value[0] == "'" && value[value.length -1] == "'") {
+ return value.substring(1, value.length - 1);
+ }
if(docfield && docfield.link_onclick) {
return repl('
%(value)s ',
{onclick: docfield.link_onclick.replace(/"/g, '"'), value:value});
@@ -110,7 +113,7 @@ frappe.form.formatters = {
if(frappe.boot.sysdefaults.time_zone) {
m = m.tz(frappe.boot.sysdefaults.time_zone);
}
- return m.format('MMMM Do YYYY, h:mm a z');
+ return m.format(frappe.boot.sysdefaults.date_format.toUpperCase() + ', h:mm a z');
} else {
return "";
}
@@ -183,7 +186,7 @@ frappe.form.formatters = {
return repl("
\
- %(value)s ", {
+
%(value)s", {
value: value,
style: workflow_state.style.toLowerCase(),
icon: workflow_state.icon
diff --git a/frappe/public/js/frappe/form/grid.js b/frappe/public/js/frappe/form/grid.js
index cecba0264d..58e122bf62 100644
--- a/frappe/public/js/frappe/form/grid.js
+++ b/frappe/public/js/frappe/form/grid.js
@@ -58,10 +58,44 @@ frappe.ui.form.Grid = Class.extend({
this.custom_buttons = {};
this.grid_buttons = this.wrapper.find('.grid-buttons');
+ this.remove_rows_button = this.grid_buttons.find('.grid-remove-rows')
this.setup_allow_bulk_edit();
+ this.setup_check();
},
+ setup_check: function() {
+ var me = this;
+ this.wrapper.on('click', '.grid-row-check', function(e) {
+ $check = $(this);
+ if($check.parents('.grid-heading-row:first').length!==0) {
+ // select all?
+ $check.parents('.form-grid:first').find('.grid-row-check').prop('checked', $check.prop('checked'));
+ } else {
+ var docname = $check.parents('.grid-row:first').attr('data-name');
+ me.grid_rows_by_docname[docname].select($check.prop('checked'));
+ }
+ me.refresh_remove_rows_button();
+ });
+
+ this.remove_rows_button.on('click', function() {
+ me.get_selected().forEach(function(docname) {
+ me.grid_rows_by_docname[docname].remove();
+ });
+ setTimeout(function() { me.refresh_remove_rows_button(); }, 100);
+ });
+ },
+ select_row: function(name) {
+ me.grid_rows_by_docname[name].select();
+ },
+ refresh_remove_rows_button: function() {
+ this.remove_rows_button.toggleClass('hide',
+ this.wrapper.find('.grid-body .grid-row-check:checked:first').length ? false : true);
+ },
+ get_selected: function() {
+ return (this.grid_rows || []).map(function(row) { return row.doc.__checked ? row.doc.name : null; })
+ .filter(function(d) { return d; });
+ },
make_head: function() {
// labels
if(!this.header_row) {
@@ -143,6 +177,8 @@ frappe.ui.form.Grid = Class.extend({
this.last_docname = this.frm.docname;
frappe.utils.scroll_to(_scroll_y);
}
+
+ this.refresh_remove_rows_button();
},
setup_toolbar: function() {
if(this.is_editable()) {
@@ -216,7 +252,6 @@ frappe.ui.form.Grid = Class.extend({
return;
}
-
new Sortable($rows.get(0), {
group: {name: 'row'},
handle: ".sortable-handle",
@@ -320,7 +355,7 @@ frappe.ui.form.Grid = Class.extend({
}
setTimeout(function() {
me.grid_rows[idx].row
- .find('input,textarea,select').filter(':visible:first').focus();
+ .find('input[type="Text"],textarea,select').filter(':visible:first').focus();
}, 100);
},
@@ -545,6 +580,7 @@ frappe.ui.form.GridRow = Class.extend({
init: function(opts) {
this.on_grid_fields_dict = {};
this.on_grid_fields = [];
+ this.row_check_html = '
';
this.columns = {};
this.columns_list = [];
$.extend(this, opts);
@@ -552,9 +588,13 @@ frappe.ui.form.GridRow = Class.extend({
},
make: function() {
var me = this;
+
this.wrapper = $('
').appendTo(this.parent).data("grid_row", this);
this.row = $('
').appendTo(this.wrapper)
- .on("click", function() {
+ .on("click", function(e) {
+ if($(e.target).hasClass('grid-row-check') || $(e.target).hasClass('row-index') || $(e.target).parent().hasClass('row-index')) {
+ return;
+ }
if(me.grid.allow_on_grid_editing() && me.grid.is_editable()) {
// pass
} else {
@@ -563,6 +603,11 @@ frappe.ui.form.GridRow = Class.extend({
}
});
+ // no checkboxes if too small
+ if(this.is_too_small()) {
+ this.row_check_html = '';
+ }
+
if(this.grid.template && !this.grid.meta.editable_grid) {
this.render_template();
} else {
@@ -582,10 +627,17 @@ frappe.ui.form.GridRow = Class.extend({
this.wrapper
.attr('data-name', this.doc.name)
.attr("data-idx", this.doc.idx)
- .find(".row-index, .grid-form-row-index").html(this.doc.idx)
+ .find(".row-index span, .grid-form-row-index").html(this.doc.idx)
}
},
+ select: function(checked) {
+ this.doc.__checked = checked ? 1 : 0;
+ },
+ refresh_check: function() {
+ this.wrapper.find('.grid-row-check').prop('checked', this.doc ? !!this.doc.__checked : false);
+ this.grid.refresh_remove_rows_button();
+ },
remove: function() {
if(this.grid.is_editable()) {
if(this.get_open_form()) {
@@ -629,6 +681,8 @@ frappe.ui.form.GridRow = Class.extend({
}
},
render_template: function() {
+ this.set_row_index();
+
if(this.row_display) {
this.row_display.remove();
}
@@ -638,9 +692,9 @@ frappe.ui.form.GridRow = Class.extend({
if(this.doc) {
if(!this.row_index) {
this.row_index = $('
').appendTo(this.row);
+ margin-right: -20px;">'+this.row_check_html+'
').appendTo(this.row);
}
- this.row_index.html(this.doc.idx);
+ this.row_index.find('span').html(this.doc.idx);
}
this.row_display = $('
'+
@@ -657,15 +711,23 @@ frappe.ui.form.GridRow = Class.extend({
// index (1, 2, 3 etc)
if(!this.row_index) {
- this.row_index = $('
' + (this.doc ? this.doc.idx : " ")+ '
')
+ var txt = (this.doc ? this.doc.idx : " ");
+ this.row_index = $('
' +
+ this.row_check_html +
+ ' ' + txt + '
')
.appendTo(this.row)
- .on('click', function() { me.toggle_view(); });
+ .on('click', function(e) {
+ if(!$(e.target).hasClass('grid-row-check')) {
+ me.toggle_view();
+ }
+ });
} else {
- this.row_index.html(this.doc ? this.doc.idx : " ");
+ this.row_index.find('span').html(txt);
}
this.setup_columns();
this.add_open_form_button();
+ this.refresh_check();
if(this.doc) {
$(this.frm.wrapper).trigger("grid-row-render", [this]);
@@ -676,6 +738,10 @@ frappe.ui.form.GridRow = Class.extend({
this.row.toggleClass('editable-row', this.grid.is_editable());
},
+ is_too_small: function() {
+ return this.row.width() < 400;
+ },
+
add_open_form_button: function() {
var me = this;
if(this.doc) {
@@ -686,7 +752,7 @@ frappe.ui.form.GridRow = Class.extend({
.appendTo($('
').appendTo(this.row))
.on('click', function() { me.toggle_view(); return false; });
- if(this.row.width() < 400) {
+ if(this.is_too_small()) {
// narrow
this.open_form_button.css({'margin-right': '-2px'});
}
@@ -742,13 +808,13 @@ frappe.ui.form.GridRow = Class.extend({
out = me.toggle_editable_row();
var col = this;
setTimeout(function() {
- $(col).find(':input:first').focus();
+ $(col).find('input[type="Text"]:first').focus();
}, 500);
return out;
});
$col.field_area = $('
').appendTo($col).toggle(false);
- $col.static_area = $('
').appendTo($col).html(txt);
+ $col.static_area = $('
').appendTo($col).html(txt);
$col.df = df;
$col.column_index = ci;
@@ -836,7 +902,13 @@ frappe.ui.form.GridRow = Class.extend({
field.$input.on('keydown', function(e) {
var values = me.frm.doc[me.grid.df.fieldname];
var fieldname = $(this).attr('data-fieldname');
+ var fieldtype = $(this).attr('data-fieldtype');
+
// TAB
+ if(in_list(['Text', 'Small Text'], fieldtype)) {
+ return;
+ }
+
if(e.which==TAB) {
// last column
if(me.grid.wrapper.find('input:enabled:last').get(0)===this) {
@@ -1104,7 +1176,7 @@ frappe.ui.form.GridRowForm = Class.extend({
var me = this;
setTimeout(function() {
if(me.row.frm.doc.docstatus===0) {
- var first = me.form_area.find(":input:first");
+ var first = me.form_area.find("input:first");
if(first.length && !in_list(["Date", "Datetime", "Time"], first.attr("data-fieldtype"))) {
try {
first.get(0).focus();
diff --git a/frappe/public/js/frappe/form/layout.js b/frappe/public/js/frappe/form/layout.js
index 5fc8d8d2fe..f81ba69e36 100644
--- a/frappe/public/js/frappe/form/layout.js
+++ b/frappe/public/js/frappe/form/layout.js
@@ -461,7 +461,7 @@ frappe.ui.form.Section = Class.extend({
this.make_head();
}
if(this.df.description) {
- $('
' + __(this.df.description) + '
')
+ $('
' + __(this.df.description) + '
')
.appendTo(this.wrapper);
}
}
diff --git a/frappe/public/js/frappe/form/link_selector.js b/frappe/public/js/frappe/form/link_selector.js
index f17a125b73..58f9ee2f74 100644
--- a/frappe/public/js/frappe/form/link_selector.js
+++ b/frappe/public/js/frappe/form/link_selector.js
@@ -99,11 +99,11 @@ frappe.ui.form.LinkSelector = Class.extend({
})
})
} else {
- $('
').appendTo(parent).find(".new-doc").click(function() {
+ $('
' + __("No Results") + ' '
+ + (frappe.model.can_create(me.doctype) ?
+ (''
+ + __("Make a new {0}", [__(me.doctype)]) + " ") : '')
+ + '
').appendTo(parent).find(".new-doc").click(function() {
me.target.new_doc();
});
}
diff --git a/frappe/public/js/frappe/form/linked_with.js b/frappe/public/js/frappe/form/linked_with.js
index 44679c8b4d..449ec26edd 100644
--- a/frappe/public/js/frappe/form/linked_with.js
+++ b/frappe/public/js/frappe/form/linked_with.js
@@ -12,7 +12,7 @@ frappe.ui.form.LinkedWith = Class.extend({
if(!this.dialog)
this.make_dialog();
- this.dialog.fields_dict.list.$wrapper.html('
'
+ this.dialog.fields_dict.list.$wrapper.html('
'
+ __("Loading") + '...
');
this.dialog.show();
diff --git a/frappe/public/js/frappe/form/save.js b/frappe/public/js/frappe/form/save.js
index cbdda43402..65e708067a 100644
--- a/frappe/public/js/frappe/form/save.js
+++ b/frappe/public/js/frappe/form/save.js
@@ -103,12 +103,6 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
if(frm.doc.docstatus==2) return true; // don't check for cancel
$.each(frappe.model.get_all_docs(frm.doc), function(i, doc) {
-
- if(doc.parent && doc.__unedited) {
- frappe.model.remove_from_locals(doc.doctype, doc.name);
- return;
- }
-
var error_fields = [];
var folded = false;
@@ -141,7 +135,7 @@ frappe.ui.form.save = function(frm, action, callback, btn) {
if(error_fields.length)
msgprint(__('Mandatory fields required in {0}', [(doc.parenttype
? (__(frappe.meta.docfield_map[doc.parenttype][doc.parentfield].label) + ' ('+ __("Table") + ')')
- : __(doc.doctype))]) + '\n' + error_fields.join('\n'));
+ : __(doc.doctype))]) + '
' + error_fields.join(' ') + " ");
});
return !has_errors;
diff --git a/frappe/public/js/frappe/form/script_manager.js b/frappe/public/js/frappe/form/script_manager.js
index 98bac6d9fa..128880bd17 100644
--- a/frappe/public/js/frappe/form/script_manager.js
+++ b/frappe/public/js/frappe/form/script_manager.js
@@ -102,7 +102,8 @@ frappe.ui.form.ScriptManager = Class.extend({
}
function setup_add_fetch(df) {
- if((df.fieldtype==="Read Only" || df.read_only==1)
+ if((in_list(['Data', 'Read Only', 'Text', 'Small Text',
+ 'Text Editor', 'Code'], df.fieldtype) || df.read_only==1)
&& df.options && df.options.indexOf(".")!=-1) {
var parts = df.options.split(".");
me.frm.add_fetch(parts[0], parts[1], df.fieldname);
diff --git a/frappe/public/js/frappe/form/templates/form_sidebar.html b/frappe/public/js/frappe/form/templates/form_sidebar.html
index 7233397ffe..f3ed783e89 100644
--- a/frappe/public/js/frappe/form/templates/form_sidebar.html
+++ b/frappe/public/js/frappe/form/templates/form_sidebar.html
@@ -70,3 +70,7 @@
{% if(frappe.get_form_sidebar_extension) { %}
{{ frappe.get_form_sidebar_extension() }}
{% } %}
+
\ No newline at end of file
diff --git a/frappe/public/js/frappe/form/templates/grid_body.html b/frappe/public/js/frappe/form/templates/grid_body.html
index 7a2adac38f..d74667d4df 100644
--- a/frappe/public/js/frappe/form/templates/grid_body.html
+++ b/frappe/public/js/frappe/form/templates/grid_body.html
@@ -7,6 +7,8 @@