Merge branch 'develop'
This commit is contained in:
commit
d2fb2a3867
24 changed files with 186 additions and 79 deletions
|
|
@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json
|
|||
from .exceptions import *
|
||||
from .utils.jinja import get_jenv, get_template, render_template
|
||||
|
||||
__version__ = "7.0.18"
|
||||
__version__ = "7.0.19"
|
||||
|
||||
local = Local()
|
||||
|
||||
|
|
|
|||
|
|
@ -55,6 +55,8 @@ def application(request):
|
|||
response = frappe.handler.handle()
|
||||
|
||||
elif frappe.request.path.startswith("/api/"):
|
||||
if frappe.local.form_dict.data is None:
|
||||
frappe.local.form_dict.data = request.get_data()
|
||||
response = frappe.api.handle()
|
||||
|
||||
elif frappe.request.path.startswith('/backups'):
|
||||
|
|
|
|||
|
|
@ -83,6 +83,29 @@ def insert(doc=None):
|
|||
doc = frappe.get_doc(doc).insert()
|
||||
return doc.as_dict()
|
||||
|
||||
@frappe.whitelist()
|
||||
def insert_many(docs=None):
|
||||
if isinstance(docs, basestring):
|
||||
docs = json.loads(docs)
|
||||
|
||||
out = []
|
||||
|
||||
if len(docs) > 200:
|
||||
frappe.throw(_('Only 200 inserts allowed in one request'))
|
||||
|
||||
for doc in docs:
|
||||
if doc.get("parent") and doc.get("parenttype"):
|
||||
# inserting a child record
|
||||
parent = frappe.get_doc(doc.get("parenttype"), doc.get("parent"))
|
||||
parent.append(doc.get("parentfield"), doc)
|
||||
parent.save()
|
||||
out.append(parent.name)
|
||||
else:
|
||||
doc = frappe.get_doc(doc).insert()
|
||||
out.append(doc.name)
|
||||
|
||||
return out
|
||||
|
||||
@frappe.whitelist()
|
||||
def save(doc):
|
||||
if isinstance(doc, basestring):
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import re, copy
|
||||
import MySQLdb
|
||||
import frappe
|
||||
from frappe import _
|
||||
|
|
@ -311,7 +311,7 @@ def validate_fields(meta):
|
|||
frappe.throw(_("Max width for type Currency is 100px in row {0}").format(d.idx))
|
||||
|
||||
def check_in_list_view(d):
|
||||
if d.in_list_view and (d.fieldtype in no_value_fields):
|
||||
if d.in_list_view and (d.fieldtype in not_allowed_in_list_view):
|
||||
frappe.throw(_("'In List View' not allowed for type {0} in row {1}").format(d.fieldtype, d.idx))
|
||||
|
||||
def check_dynamic_link_options(d):
|
||||
|
|
@ -441,6 +441,10 @@ def validate_fields(meta):
|
|||
frappe.throw(_("Timeline field must be a Link or Dynamic Link"), InvalidFieldNameError)
|
||||
|
||||
fields = meta.get("fields")
|
||||
not_allowed_in_list_view = list(copy.copy(no_value_fields))
|
||||
if meta.istable:
|
||||
not_allowed_in_list_view.remove('Button')
|
||||
|
||||
for d in fields:
|
||||
if not d.permlevel: d.permlevel = 0
|
||||
if not d.fieldname:
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ class User(Document):
|
|||
def onload(self):
|
||||
self.set_onload('all_modules',
|
||||
[m.module_name for m in frappe.db.get_all('Desktop Icon',
|
||||
fields=['module_name'], filters={'standard': 1})])
|
||||
fields=['module_name'], filters={'standard': 1}, order_by="module_name")])
|
||||
|
||||
def validate(self):
|
||||
self.check_demo()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,13 @@
|
|||
"db_name": "testdb",
|
||||
"db_password": "password",
|
||||
"mute_emails": true,
|
||||
|
||||
"limits": {
|
||||
"emails": 1500,
|
||||
"space": 0.157,
|
||||
"expiry": "2016-07-25",
|
||||
"users": 1
|
||||
}
|
||||
|
||||
"developer_mode": 1,
|
||||
"auto_cache_clear": true,
|
||||
|
|
|
|||
|
|
@ -21,6 +21,8 @@ frappe.ui.form.on('Bulk Update', {
|
|||
},
|
||||
document_type: function(frm) {
|
||||
// set field options
|
||||
if(!frm.doc.document_type) return;
|
||||
|
||||
frappe.model.with_doctype(frm.doc.document_type, function() {
|
||||
var options = $.map(frappe.get_meta(frm.doc.document_type).fields,
|
||||
function(d) {
|
||||
|
|
|
|||
|
|
@ -35,23 +35,15 @@ def check_user_tags(dt):
|
|||
DocTags(dt).setup()
|
||||
|
||||
@frappe.whitelist()
|
||||
def add_tag():
|
||||
def add_tag(tag, dt, dn, color=None):
|
||||
"adds a new tag to a record, and creates the Tag master"
|
||||
|
||||
f = frappe.local.form_dict
|
||||
tag, color = f.get('tag'), f.get('color')
|
||||
dt, dn = f.get('dt'), f.get('dn')
|
||||
|
||||
DocTags(dt).add(dn, tag)
|
||||
|
||||
return tag
|
||||
|
||||
@frappe.whitelist()
|
||||
def remove_tag():
|
||||
def remove_tag(tag, dt, dn):
|
||||
"removes tag from the record"
|
||||
f = frappe.local.form_dict
|
||||
tag, dt, dn = f.get('tag'), f.get('dt'), f.get('dn')
|
||||
|
||||
DocTags(dt).remove(dn, tag)
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
email_defaults = {
|
||||
"Gmail": {
|
||||
frappe.email_defaults = {
|
||||
"GMail": {
|
||||
"email_server": "pop.gmail.com",
|
||||
"use_ssl": 1,
|
||||
"enable_outgoing": 1,
|
||||
|
|
@ -33,8 +33,8 @@ email_defaults = {
|
|||
},
|
||||
};
|
||||
|
||||
email_defaults_imap = {
|
||||
"Gmail": {
|
||||
frappe.email_defaults_imap = {
|
||||
"GMail": {
|
||||
"email_server": "imap.gmail.com"
|
||||
},
|
||||
"Outlook.com": {
|
||||
|
|
@ -51,11 +51,12 @@ email_defaults_imap = {
|
|||
|
||||
frappe.ui.form.on("Email Account", {
|
||||
service: function(frm) {
|
||||
$.each(email_defaults[frm.doc.service], function(key, value) {
|
||||
console.log(frm.doc.service, frappe.email_defaults[frm.doc.service])
|
||||
$.each(frappe.email_defaults[frm.doc.service], function(key, value) {
|
||||
frm.set_value(key, value);
|
||||
})
|
||||
if (frm.doc.use_imap) {
|
||||
$.each(email_defaults_imap[frm.doc.service], function(key, value) {
|
||||
$.each(frappe.email_defaults_imap[frm.doc.service], function(key, value) {
|
||||
frm.set_value(key, value);
|
||||
});
|
||||
}
|
||||
|
|
@ -63,12 +64,12 @@ frappe.ui.form.on("Email Account", {
|
|||
},
|
||||
use_imap: function(frm) {
|
||||
if (frm.doc.use_imap) {
|
||||
$.each(email_defaults_imap[frm.doc.service], function(key, value) {
|
||||
$.each(frappe.email_defaults_imap[frm.doc.service], function(key, value) {
|
||||
frm.set_value(key, value);
|
||||
});
|
||||
}
|
||||
else{
|
||||
$.each(email_defaults[frm.doc.service], function(key, value) {
|
||||
$.each(frappe.email_defaults[frm.doc.service], function(key, value) {
|
||||
frm.set_value(key, value);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,6 +58,12 @@ class FrappeClient(object):
|
|||
data={"data":frappe.as_json(doc)}, verify=self.verify)
|
||||
return self.post_process(res)
|
||||
|
||||
def insert_many(self, docs):
|
||||
return self.post_request({
|
||||
"cmd": "frappe.client.insert_many",
|
||||
"docs": frappe.as_json(docs)
|
||||
})
|
||||
|
||||
def update(self, doc):
|
||||
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)
|
||||
|
|
|
|||
|
|
@ -361,13 +361,11 @@ def check_if_ready_for_barracuda():
|
|||
|
||||
def extract_sql_gzip(sql_gz_path):
|
||||
try:
|
||||
success = subprocess.check_output(['gzip', '-d', '-v', '-f', sql_gz_path])
|
||||
subprocess.check_call(['gzip', '-d', '-v', '-f', sql_gz_path])
|
||||
except:
|
||||
raise
|
||||
|
||||
path = sql_gz_path[:-3] if success else None
|
||||
|
||||
return path
|
||||
return sql_gz_path[:-3]
|
||||
|
||||
def extract_tar_files(site_name, file_path, folder_name):
|
||||
# Need to do frappe.init to maintain the site locals
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ def write_document_file(doc, record_module=None, create_init=None):
|
|||
for fieldname in frappe.model.default_fields:
|
||||
if fieldname in d:
|
||||
del d[fieldname]
|
||||
for fieldname in d.keys():
|
||||
if d[fieldname] == 0 or d[fieldname] == "":
|
||||
del d[fieldname]
|
||||
|
||||
module = record_module or get_module_name(doc)
|
||||
if create_init is None:
|
||||
|
|
|
|||
|
|
@ -104,8 +104,19 @@
|
|||
.grid-body .editable-row input[data-fieldtype="Currency"] {
|
||||
text-align: right;
|
||||
}
|
||||
.grid-body .grid-static-col[data-fieldtype="Button"] .field-area {
|
||||
margin-top: 5px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
.grid-body .grid-static-col[data-fieldtype="Button"] .field-area button {
|
||||
height: 27px;
|
||||
}
|
||||
.grid-body .grid-static-col[data-fieldtype="Code"] .static-area {
|
||||
margin-top: -10px;
|
||||
margin-top: -5px;
|
||||
}
|
||||
.grid-body .grid-static-col[data-fieldtype="Code"] .static-area pre {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
@media (max-width: 767px) {
|
||||
.grid-body .btn-open-row {
|
||||
|
|
|
|||
|
|
@ -238,6 +238,7 @@ table.field-info tr td {
|
|||
border: 0px;
|
||||
}
|
||||
.image-field {
|
||||
background-size: 100% 100% !important;
|
||||
position: relative;
|
||||
}
|
||||
.image-field:hover .field-info {
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ body[data-route^="Module"] .main-menu .form-sidebar {
|
|||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
border-radius: 6px;
|
||||
background-size: cover;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -291,12 +291,17 @@ frappe.ui.form.Grid = Class.extend({
|
|||
this.frm.script_manager.trigger(this.df.fieldname + "_add", d.doctype, d.name);
|
||||
this.refresh();
|
||||
|
||||
if(show && !this.allow_on_grid_editing()) {
|
||||
if(show) {
|
||||
if(idx) {
|
||||
// always open inserted rows
|
||||
this.wrapper.find("[data-idx='"+idx+"']").data("grid_row")
|
||||
.toggle_view(true, callback);
|
||||
} else {
|
||||
this.wrapper.find(".grid-row:last").data("grid_row").toggle_view(true, callback);
|
||||
if(!this.allow_on_grid_editing()) {
|
||||
// open last row only if on-grid-editing is disabled
|
||||
this.wrapper.find(".grid-row:last").data("grid_row")
|
||||
.toggle_view(true, callback);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -820,50 +825,52 @@ frappe.ui.form.GridRow = Class.extend({
|
|||
|
||||
set_arrow_keys: function(field) {
|
||||
var me = this;
|
||||
field.$input.on('keydown', function(e) {
|
||||
var values = me.frm.doc[me.grid.df.fieldname];
|
||||
var fieldname = $(this).attr('data-fieldname');
|
||||
// TAB
|
||||
if(e.which==TAB) {
|
||||
// last column
|
||||
if(me.grid.wrapper.find('input:enabled:last').get(0)===this) {
|
||||
setTimeout(function() {
|
||||
if(me.doc.idx === values.length) {
|
||||
// last row
|
||||
me.grid.add_new_row(null, null, true);
|
||||
me.grid.grid_rows[me.grid.grid_rows.length - 1].toggle_editable_row();
|
||||
me.grid.set_focus_on_row();
|
||||
} else {
|
||||
me.grid.grid_rows[me.doc.idx].toggle_editable_row();
|
||||
me.grid.set_focus_on_row(me.doc.idx+1);
|
||||
}
|
||||
}, 500);
|
||||
if(field.$input) {
|
||||
field.$input.on('keydown', function(e) {
|
||||
var values = me.frm.doc[me.grid.df.fieldname];
|
||||
var fieldname = $(this).attr('data-fieldname');
|
||||
// TAB
|
||||
if(e.which==TAB) {
|
||||
// last column
|
||||
if(me.grid.wrapper.find('input:enabled:last').get(0)===this) {
|
||||
setTimeout(function() {
|
||||
if(me.doc.idx === values.length) {
|
||||
// last row
|
||||
me.grid.add_new_row(null, null, true);
|
||||
me.grid.grid_rows[me.grid.grid_rows.length - 1].toggle_editable_row();
|
||||
me.grid.set_focus_on_row();
|
||||
} else {
|
||||
me.grid.grid_rows[me.doc.idx].toggle_editable_row();
|
||||
me.grid.set_focus_on_row(me.doc.idx+1);
|
||||
}
|
||||
}, 500);
|
||||
}
|
||||
} else if(e.which==UP_ARROW) {
|
||||
if(me.doc.idx > 1) {
|
||||
var prev = me.grid.grid_rows[me.doc.idx-2];
|
||||
prev.toggle_editable_row();
|
||||
setTimeout(function() {
|
||||
var input = prev.columns[fieldname].field.$input;
|
||||
if(input) {
|
||||
input.focus();
|
||||
}
|
||||
}, 400)
|
||||
}
|
||||
} else if(e.which==DOWN_ARROW) {
|
||||
if(me.doc.idx < values.length) {
|
||||
var next = me.grid.grid_rows[me.doc.idx];
|
||||
next.toggle_editable_row();
|
||||
setTimeout(function() {
|
||||
var input = next.columns[fieldname].field.$input;
|
||||
if(input) {
|
||||
input.focus();
|
||||
}
|
||||
}, 400)
|
||||
}
|
||||
}
|
||||
} else if(e.which==UP_ARROW) {
|
||||
if(me.doc.idx > 1) {
|
||||
var prev = me.grid.grid_rows[me.doc.idx-2];
|
||||
prev.toggle_editable_row();
|
||||
setTimeout(function() {
|
||||
var input = prev.columns[fieldname].field.$input;
|
||||
if(input) {
|
||||
input.focus();
|
||||
}
|
||||
}, 400)
|
||||
}
|
||||
} else if(e.which==DOWN_ARROW) {
|
||||
if(me.doc.idx < values.length) {
|
||||
var next = me.grid.grid_rows[me.doc.idx];
|
||||
next.toggle_editable_row();
|
||||
setTimeout(function() {
|
||||
var input = next.columns[fieldname].field.$input;
|
||||
if(input) {
|
||||
input.focus();
|
||||
}
|
||||
}, 400)
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
get_open_form: function() {
|
||||
|
|
|
|||
|
|
@ -136,8 +136,22 @@
|
|||
}
|
||||
}
|
||||
|
||||
.grid-static-col[data-fieldtype="Button"] .field-area {
|
||||
margin-top: 5px;
|
||||
margin-left: 5px;
|
||||
|
||||
button {
|
||||
height: 27px;
|
||||
}
|
||||
}
|
||||
|
||||
.grid-static-col[data-fieldtype="Code"] .static-area {
|
||||
margin-top: -10px;
|
||||
margin-top: -5px;
|
||||
|
||||
pre {
|
||||
background: none;
|
||||
border: none;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -301,6 +301,7 @@ table.field-info tr td {
|
|||
}
|
||||
|
||||
.image-field {
|
||||
background-size: 100% 100% !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ body[data-route^="Module"] .main-menu {
|
|||
height: 0;
|
||||
padding-bottom: 100%;
|
||||
border-radius: 6px;
|
||||
background-size: cover;
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: center center;
|
||||
}
|
||||
|
|
|
|||
33
frappe/tests/test_api.py
Normal file
33
frappe/tests/test_api.py
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# MIT License. See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest, frappe
|
||||
from frappe.utils import get_url
|
||||
|
||||
class TestAPI(unittest.TestCase):
|
||||
def test_insert_many(self):
|
||||
from frappe.frappeclient import FrappeClient
|
||||
|
||||
frappe.db.sql('delete from `tabToDo` where description like "Test API%"')
|
||||
frappe.db.commit()
|
||||
|
||||
host = get_url()
|
||||
|
||||
if not host.startswith('http'):
|
||||
host = 'http://' + host
|
||||
|
||||
if not host.endswith(':8000'):
|
||||
host = host + ':8000'
|
||||
|
||||
server = FrappeClient(host, "Administrator", "admin", verify=False)
|
||||
|
||||
server.insert_many([
|
||||
{"doctype": "ToDo", "description": "Test API 1"},
|
||||
{"doctype": "ToDo", "description": "Test API 2"},
|
||||
{"doctype": "ToDo", "description": "Test API 3"},
|
||||
])
|
||||
|
||||
self.assertTrue(frappe.db.get_value('ToDo', {'description': 'Test API 1'}))
|
||||
self.assertTrue(frappe.db.get_value('ToDo', {'description': 'Test API 2'}))
|
||||
self.assertTrue(frappe.db.get_value('ToDo', {'description': 'Test API 3'}))
|
||||
|
|
@ -11,7 +11,7 @@ class TestDB(unittest.TestCase):
|
|||
def test_get_value(self):
|
||||
self.assertEquals(frappe.db.get_value("User", {"name": ["=", "Administrator"]}), "Administrator")
|
||||
self.assertEquals(frappe.db.get_value("User", {"name": ["like", "Admin%"]}), "Administrator")
|
||||
self.assertEquals(frappe.db.get_value("User", {"name": ["!=", "Guest"]}), "Administrator")
|
||||
self.assertNotEquals(frappe.db.get_value("User", {"name": ["!=", "Guest"]}), "Guest")
|
||||
self.assertEquals(frappe.db.get_value("User", {"name": ["<", "B"]}), "Administrator")
|
||||
self.assertEquals(frappe.db.get_value("User", {"name": ["<=", "Administrator"]}), "Administrator")
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ def test_timeout():
|
|||
time.sleep(100)
|
||||
|
||||
class TestScheduler(TestCase):
|
||||
|
||||
def setUp(self):
|
||||
frappe.db.set_global('enabled_scheduler_events', "")
|
||||
|
||||
|
|
@ -63,10 +62,8 @@ class TestScheduler(TestCase):
|
|||
|
||||
def test_restrict_scheduler_events(self):
|
||||
frappe.set_user("Administrator")
|
||||
user = frappe.get_doc("User", "Administrator")
|
||||
dormant_date = add_days(today(), -5)
|
||||
user.last_active = dormant_date
|
||||
user.save()
|
||||
frappe.db.sql('update tabUser set last_active=%s', dormant_date)
|
||||
|
||||
restrict_scheduler_events_if_dormant()
|
||||
frappe.local.conf = _dict(frappe.get_site_config())
|
||||
|
|
|
|||
|
|
@ -114,6 +114,10 @@ def now_datetime():
|
|||
dt = convert_utc_to_user_timezone(datetime.datetime.utcnow())
|
||||
return dt.replace(tzinfo=None)
|
||||
|
||||
def get_eta(from_time, percent_complete):
|
||||
diff = time_diff(now_datetime(), from_time).total_seconds()
|
||||
return str(datetime.timedelta(seconds=(100 - percent_complete) / percent_complete * diff))
|
||||
|
||||
def _get_time_zone():
|
||||
return frappe.db.get_system_setting('time_zone') or 'Asia/Kolkata'
|
||||
|
||||
|
|
|
|||
|
|
@ -35,3 +35,4 @@ rq
|
|||
schedule
|
||||
cryptography
|
||||
zxcvbn
|
||||
psutil
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue