Browse Source

Merge pull request #4221 from rmehta/fix-text-editor-in-firefox

[fix] text editor in firefox
version-14
Rushabh Mehta 7 years ago
committed by GitHub
parent
commit
404f0ab07a
10 changed files with 110 additions and 47 deletions
  1. +2
    -2
      frappe/__init__.py
  2. +6
    -4
      frappe/core/doctype/communication/communication.py
  3. +0
    -3
      frappe/core/doctype/communication/email.py
  4. +23
    -0
      frappe/desk/doctype/todo/test_todo.js
  5. +9
    -9
      frappe/desk/doctype/todo/todo.json
  6. +0
    -1
      frappe/email/doctype/email_account/email_account.py
  7. +46
    -22
      frappe/model/document.py
  8. +22
    -4
      frappe/public/js/frappe/form/controls/text_editor.js
  9. +1
    -1
      frappe/public/js/frappe/socketio_client.js
  10. +1
    -1
      frappe/public/js/legacy/form.js

+ 2
- 2
frappe/__init__.py View File

@@ -602,7 +602,7 @@ def set_value(doctype, docname, fieldname, value=None):
import frappe.client
return frappe.client.set_value(doctype, docname, fieldname, value)

def get_doc(arg1, arg2=None):
def get_doc(*args, **kwargs):
"""Return a `frappe.model.document.Document` object of the given type and name.

:param arg1: DocType name as string **or** document JSON.
@@ -619,7 +619,7 @@ def get_doc(arg1, arg2=None):

"""
import frappe.model.document
return frappe.model.document.get_doc(arg1, arg2)
return frappe.model.document.get_doc(*args, **kwargs)

def get_last_doc(doctype):
"""Get last created document of this type."""


+ 6
- 4
frappe/core/doctype/communication/communication.py View File

@@ -25,7 +25,7 @@ class Communication(Document):
"""create email flag queue"""
if self.communication_type == "Communication" and self.communication_medium == "Email" \
and self.sent_or_received == "Received" and self.uid and self.uid != -1:
email_flag_queue = frappe.db.get_value("Email Flag Queue", {
"communication": self.name,
"is_completed": 0})
@@ -69,7 +69,7 @@ class Communication(Document):
def after_insert(self):
if not (self.reference_doctype and self.reference_name):
return
if self.reference_doctype == "Communication" and self.sent_or_received == "Sent":
frappe.db.set_value("Communication", self.reference_name, "status", "Replied")

@@ -264,7 +264,7 @@ def has_permission(doc, ptype, user):
if (doc.reference_doctype == "Communication" and doc.reference_name == doc.name) \
or (doc.timeline_doctype == "Communication" and doc.timeline_name == doc.name):
return
if doc.reference_doctype and doc.reference_name:
if frappe.has_permission(doc.reference_doctype, ptype="read", doc=doc.reference_name):
return True
@@ -277,7 +277,9 @@ def get_permission_query_conditions_for_communication(user):

if not user: user = frappe.session.user

if "Super Email User" in frappe.get_roles(user):
roles = frappe.get_roles(user)

if "Super Email User" in roles or "System Manager" in roles:
return None
else:
accounts = frappe.get_all("User Email", filters={ "parent": user },


+ 0
- 3
frappe/core/doctype/communication/email.py View File

@@ -166,9 +166,6 @@ def _notify(doc, print_html=None, print_format=None, attachments=None,

def update_parent_status(doc):
"""Update status of parent document based on who is replying."""
if doc.communication_type != "Communication":
return

parent = doc.get_parent_doc()
if not parent:
return


+ 23
- 0
frappe/desk/doctype/todo/test_todo.js View File

@@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line

QUnit.test("test: ToDo", function (assert) {
let done = assert.async();

// number of asserts
assert.expect(1);

frappe.run_serially([
// insert a new ToDo
() => frappe.tests.make('ToDo', [
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);

});

+ 9
- 9
frappe/desk/doctype/todo/todo.json View File

@@ -112,20 +112,18 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "color",
"fieldtype": "Color",
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Color",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -142,18 +140,20 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"fieldname": "color",
"fieldtype": "Color",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Color",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@@ -544,7 +544,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-09-05 12:54:58.044162",
"modified": "2017-09-30 13:57:29.398598",
"modified_by": "Administrator",
"module": "Desk",
"name": "ToDo",


+ 0
- 1
frappe/email/doctype/email_account/email_account.py View File

@@ -273,7 +273,6 @@ class EmailAccount(Document):
"uid_reindexed": uid_reindexed
}
communication = self.insert_communication(msg, args=args)
#self.notify_update()

except SentEmailInInbox:
frappe.db.rollback()


+ 46
- 22
frappe/model/document.py View File

@@ -20,13 +20,13 @@ from frappe.integrations.doctype.webhook import run_webhooks
# once_only validation
# methods

def get_doc(arg1, arg2=None):
def get_doc(*args, **kwargs):
"""returns a frappe.model.Document object.

:param arg1: Document dict or DocType name.
:param arg2: [optional] document name.

There are two ways to call `get_doc`
There are multiple ways to call `get_doc`

# will fetch the latest user object (with child table) from the database
user = get_doc("User", "test@example.com")
@@ -39,23 +39,39 @@ def get_doc(arg1, arg2=None):
{"role": "System Manager"}
]
})

# create new object with keyword arguments
user = get_doc(doctype='User', email_id='test@example.com')
"""
if isinstance(arg1, BaseDocument):
return arg1
elif isinstance(arg1, string_types):
doctype = arg1
else:
doctype = arg1.get("doctype")
if args:
if isinstance(args[0], BaseDocument):
# already a document
return args[0]
elif isinstance(args[0], string_types):
doctype = args[0]

elif isinstance(args[0], dict):
# passed a dict
kwargs = args[0]

else:
raise ValueError('First non keyword argument must be a string or dict')

if kwargs:
if 'doctype' in kwargs:
doctype = kwargs['doctype']
else:
raise ValueError('"doctype" is a required key')

controller = get_controller(doctype)
if controller:
return controller(arg1, arg2)
return controller(*args, **kwargs)

raise ImportError(arg1)
raise ImportError(doctype)

class Document(BaseDocument):
"""All controllers inherit from `Document`."""
def __init__(self, arg1, arg2=None):
def __init__(self, *args, **kwargs):
"""Constructor.

:param arg1: DocType name as string or document **dict**
@@ -68,29 +84,37 @@ class Document(BaseDocument):
self._default_new_docs = {}
self.flags = frappe._dict()

if arg1 and isinstance(arg1, string_types):
if not arg2:
if args and args[0] and isinstance(args[0], string_types):
# first arugment is doctype
if len(args)==1:
# single
self.doctype = self.name = arg1
self.doctype = self.name = args[0]
else:
self.doctype = arg1
if isinstance(arg2, dict):
self.doctype = args[0]
if isinstance(args[1], dict):
# filter
self.name = frappe.db.get_value(arg1, arg2, "name")
self.name = frappe.db.get_value(args[0], args[1], "name")
if self.name is None:
frappe.throw(_("{0} {1} not found").format(_(arg1), arg2), frappe.DoesNotExistError)
frappe.throw(_("{0} {1} not found").format(_(args[0]), args[1]),
frappe.DoesNotExistError)
else:
self.name = arg2
self.name = args[1]

self.load_from_db()
return

if args and args[0] and isinstance(args[0], dict):
# first argument is a dict
kwargs = args[0]

elif isinstance(arg1, dict):
super(Document, self).__init__(arg1)
if kwargs:
# init base document
super(Document, self).__init__(kwargs)
self.init_valid_columns()

else:
# incorrect arguments. let's not proceed.
raise frappe.DataError("Document({0}, {1})".format(arg1, arg2))
raise ValueError('Illegal arguments')

def reload(self):
"""Reload document from database"""


+ 22
- 4
frappe/public/js/frappe/form/controls/text_editor.js View File

@@ -6,6 +6,11 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
this.setup_drag_drop();
this.setup_image_dialog();
this.setting_count = 0;

$(document).on('form-refresh', () => {
// reset last keystroke when a new form is loaded
this.last_keystroke_on = null;
})
},
render_camera_button: (context) => {
var ui = $.summernote.ui;
@@ -74,7 +79,7 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
me.parse_validate_and_set_in_model(value);
},
onKeydown: function(e) {
me._last_change_on = new Date();
me.last_keystroke_on = new Date();
var key = frappe.ui.keys.get_key(e);
// prevent 'New DocType (Ctrl + B)' shortcut in editor
if(['ctrl+b', 'meta+b'].indexOf(key) !== -1) {
@@ -205,20 +210,30 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({

if(this.setting_count > 2) {
// we don't understand how the internal triggers work,
// so if someone is setting the value third time, then quit
// so if someone is setting the value third time in 500ms,
// then quit
return;
}

this.setting_count += 1;

let time_since_last_keystroke = moment() - moment(this._last_change_on);
let time_since_last_keystroke = moment() - moment(this.last_keystroke_on);

if(!this._last_change_on || (time_since_last_keystroke > 3000)) {
if(!this.last_keystroke_on || (time_since_last_keystroke > 3000)) {
// if 3 seconds have passed since the last keystroke and
// we have not set any value in the last 1 second, do this
setTimeout(() => this.setting_count = 0, 500);
this.editor.summernote('code', value || '');
this.last_keystroke_on = null;
} else {
// user is probably still in the middle of typing
// so lets not mess up the html by re-updating it
// keep checking every second if our 3 second barrier
// has been completed, so that we can refresh the html
this._setting_value = setInterval(() => {
if(time_since_last_keystroke > 3000) {
// 3 seconds done! lets refresh
// safe to update
if(this.last_value !== this.get_input_value()) {
// if not already in sync, reset
this.editor.summernote('code', this.last_value || '');
@@ -226,6 +241,9 @@ frappe.ui.form.ControlTextEditor = frappe.ui.form.ControlCode.extend({
clearInterval(this._setting_value);
this._setting_value = null;
this.setting_count = 0;

// clear timestamp of last keystroke
this.last_keystroke_on = null;
}
}, 1000);
}


+ 1
- 1
frappe/public/js/frappe/socketio_client.js View File

@@ -73,7 +73,7 @@ frappe.socketio = {
frappe.socketio.doc_subscribe(frm.doctype, frm.docname);
});

$(document).on("form_refresh", function(e, frm) {
$(document).on("form-refresh", function(e, frm) {
if (frm.is_new()) {
return;
}


+ 1
- 1
frappe/public/js/legacy/form.js View File

@@ -497,7 +497,7 @@ _f.Frm.prototype.render_form = function(is_a_different_doc) {

// trigger global trigger
// to use this
$(document).trigger('form_refresh', [this]);
$(document).trigger('form-refresh', [this]);

// fields
this.refresh_fields();


Loading…
Cancel
Save