Merge branch 'staging' into develop

This commit is contained in:
Ameya Shenoy 2018-10-23 08:04:11 +00:00
commit aa4ef1b405
No known key found for this signature in database
GPG key ID: AC016A555657D0A3
19 changed files with 487 additions and 21 deletions

View file

@ -17,7 +17,7 @@ from faker import Faker
from .exceptions import *
from .utils.jinja import (get_jenv, get_template, render_template, get_email_from_template, get_jloader)
__version__ = '10.1.54'
__version__ = '10.1.55'
__title__ = "Frappe Framework"
local = Local()

View file

@ -126,6 +126,8 @@ def set_default(key, value, parent, parenttype="__default"):
defkey=%s and parent=%s""", (key, parent))
if value != None:
add_default(key, value, parent)
else:
_clear_cache(parent)
def add_default(key, value, parent, parenttype=None):
d = frappe.get_doc({

View file

@ -12,7 +12,7 @@ source_link = "https://github.com/frappe/frappe"
app_license = "MIT"
develop_version = '12.x.x-develop'
staging_version = '11.0.3-beta.12'
staging_version = '11.0.3-beta.13'
app_email = "info@frappe.io"

View file

@ -182,7 +182,7 @@ def get_user_permissions(user):
from frappe.core.doctype.user_permission.user_permission import get_user_permissions
return get_user_permissions(user)
def has_user_permission(doc, user=None, verbose=True):
def has_user_permission(doc, user=None, verbose=False):
'''Returns True if User is allowed to view considering User Permissions'''
from frappe.core.doctype.user_permission.user_permission import get_user_permissions
user_permissions = get_user_permissions(user)

View file

@ -1,4 +1,7 @@
import 'quill-mention/dist/quill.mention.min';
import Quill from 'quill';
import Mention from './quill-mention/quill.mention';
Quill.register('modules/mention', Mention);
frappe.ui.form.ControlComment = frappe.ui.form.ControlTextEditor.extend({
make_wrapper() {

View file

@ -0,0 +1,38 @@
/* eslint-disable */
import Quill from 'quill';
const Embed = Quill.import('blots/embed');
class MentionBlot extends Embed {
static create(data) {
const node = super.create();
const denotationChar = document.createElement('span');
denotationChar.className = 'ql-mention-denotation-char';
denotationChar.innerHTML = data.denotationChar;
node.appendChild(denotationChar);
node.innerHTML += data.value;
node.dataset.id = data.id;
node.dataset.value = data.value;
node.dataset.denotationChar = data.denotationChar;
if (data.link) {
node.dataset.link = data.link;
}
return node;
}
static value(domNode) {
return {
id: domNode.dataset.id,
value: domNode.dataset.value,
link: domNode.dataset.link || null,
denotationChar: domNode.dataset.denotationChar,
};
}
}
MentionBlot.blotName = 'mention';
MentionBlot.tagName = 'span';
MentionBlot.className = 'mention';
Quill.register(MentionBlot);

View file

@ -0,0 +1,10 @@
/* eslint-disable */
const Keys = {
TAB: 'Tab',
ENTER: 'Enter',
ESCAPE: 27,
UP: 'ArrowUp',
DOWN: 'ArrowDown',
};
export default Keys;

View file

@ -0,0 +1,40 @@
.ql-mention-list-container {
width: 270px;
border: 1px solid #F0F0F0;
border-radius: 4px;
background-color: #FFFFFF;
box-shadow: 0 2px 12px 0 rgba(30, 30, 30, 0.08);
}
.ql-mention-list {
list-style: none;
margin: 0;
padding: 0;
overflow: hidden;
}
.ql-mention-list-item {
cursor: pointer;
height: 44px;
line-height: 44px;
font-size: 16px;
padding: 0 20px;
vertical-align: middle;
}
.ql-mention-list-item.selected {
background-color: #D3E1EB;
text-decoration: none;
}
.mention {
height: 24px;
width: 65px;
border-radius: 6px;
background-color: #D3E1EB;
padding: 3px 0;
}
.mention>span {
margin: 0 3px;
}

View file

@ -0,0 +1,366 @@
/* eslint-disable */
import Quill from 'quill';
import Keys from './constants/keys';
import './quill.mention.css';
import './blots/mention';
class Mention {
constructor(quill, options) {
this.isOpen = false;
this.itemIndex = 0;
this.mentionCharPos = null;
this.cursorPos = null;
this.values = [];
this.suspendMouseEnter = false;
this.quill = quill;
this.options = {
source: null,
renderItem(item, searchTerm) {
return `${item.value}`;
},
mentionDenotationChars: ['@'],
allowedChars: /^[a-zA-Z0-9_]*$/,
minChars: 0,
maxChars: 31,
offsetTop: 2,
offsetLeft: 0,
isolateCharacter: false,
fixMentionsToQuill: false,
defaultMenuOrientation: 'bottom',
};
Object.assign(this.options, options);
this.mentionContainer = document.createElement('div');
this.mentionContainer.className = 'ql-mention-list-container';
this.mentionContainer.style.cssText = 'display: none; position: absolute;';
this.mentionContainer.onmousemove = this.onContainerMouseMove.bind(this);
if (this.options.fixMentionsToQuill) {
this.mentionContainer.style.width = 'auto';
}
this.mentionList = document.createElement('ul');
this.mentionList.className = 'ql-mention-list';
this.mentionContainer.appendChild(this.mentionList);
this.quill.container.appendChild(this.mentionContainer);
quill.on('text-change', this.onTextChange.bind(this));
quill.on('selection-change', this.onSelectionChange.bind(this));
quill.keyboard.addBinding({
key: Keys.TAB,
}, this.selectHandler.bind(this));
quill.keyboard.bindings[Keys.TAB].unshift(quill.keyboard.bindings[Keys.TAB].pop());
quill.keyboard.addBinding({
key: Keys.ENTER,
}, this.selectHandler.bind(this));
quill.keyboard.bindings[Keys.ENTER].unshift(quill.keyboard.bindings[Keys.ENTER].pop());
quill.keyboard.addBinding({
key: Keys.ESCAPE,
}, this.escapeHandler.bind(this));
quill.keyboard.addBinding({
key: Keys.UP,
}, this.upHandler.bind(this));
quill.keyboard.addBinding({
key: Keys.DOWN,
}, this.downHandler.bind(this));
}
selectHandler() {
if (this.isOpen) {
this.selectItem();
return false;
}
return true;
}
escapeHandler() {
if (this.isOpen) {
this.hideMentionList();
return false;
}
return true;
}
upHandler() {
if (this.isOpen) {
this.prevItem();
return false;
}
return true;
}
downHandler() {
if (this.isOpen) {
this.nextItem();
return false;
}
return true;
}
showMentionList() {
this.mentionContainer.style.visibility = 'hidden';
this.mentionContainer.style.display = '';
this.setMentionContainerPosition();
this.isOpen = true;
}
hideMentionList() {
this.mentionContainer.style.display = 'none';
this.isOpen = false;
}
highlightItem(scrollItemInView = true) {
for (let i = 0; i < this.mentionList.childNodes.length; i += 1) {
this.mentionList.childNodes[i].classList.remove('selected');
}
this.mentionList.childNodes[this.itemIndex].classList.add('selected');
if (scrollItemInView) {
const itemHeight = this.mentionList.childNodes[this.itemIndex].offsetHeight;
const itemPos = this.itemIndex * itemHeight;
const containerTop = this.mentionContainer.scrollTop;
const containerBottom = containerTop + this.mentionContainer.offsetHeight;
if (itemPos < containerTop) {
// Scroll up if the item is above the top of the container
this.mentionContainer.scrollTop = itemPos;
} else if (itemPos > (containerBottom - itemHeight)) {
// scroll down if any part of the element is below the bottom of the container
this.mentionContainer.scrollTop += (itemPos - containerBottom) + itemHeight;
}
}
}
getItemData() {
const itemLink = this.mentionList.childNodes[this.itemIndex].dataset.link;
return {
id: this.mentionList.childNodes[this.itemIndex].dataset.id,
value: itemLink ?
`<a href="${itemLink}" target="_blank">${this.mentionList.childNodes[this.itemIndex].dataset.value}` :
this.mentionList.childNodes[this.itemIndex].dataset.value,
link: itemLink || null,
denotationChar: this.mentionList.childNodes[this.itemIndex].dataset.denotationChar,
};
}
onContainerMouseMove() {
this.suspendMouseEnter = false;
}
selectItem() {
const data = this.getItemData();
this.quill
.deleteText(this.mentionCharPos, this.cursorPos - this.mentionCharPos, Quill.sources.API);
this.quill.insertEmbed(this.mentionCharPos, 'mention', data, Quill.sources.API);
this.quill.insertText(this.mentionCharPos + 1, ' ', Quill.sources.API);
this.quill.setSelection(this.mentionCharPos + 2, Quill.sources.API);
this.hideMentionList();
}
onItemMouseEnter(e) {
if (this.suspendMouseEnter) {
return;
}
const index = Number(e.target.dataset.index);
if (!Number.isNaN(index) && index !== this.itemIndex) {
this.itemIndex = index;
this.highlightItem(false);
}
}
onItemClick(e) {
e.stopImmediatePropagation();
e.preventDefault();
this.itemIndex = e.currentTarget.dataset.index;
this.highlightItem();
this.selectItem();
}
renderList(mentionChar, data, searchTerm) {
if (data && data.length > 0) {
this.values = data;
this.mentionList.innerHTML = '';
for (let i = 0; i < data.length; i += 1) {
const li = document.createElement('li');
li.className = 'ql-mention-list-item';
li.dataset.index = i;
li.dataset.id = data[i].id;
li.dataset.value = data[i].value;
li.dataset.denotationChar = mentionChar;
if (data[i].link) {
li.dataset.link = data[i].link;
}
li.innerHTML = this.options.renderItem(data[i], searchTerm);
li.onmouseenter = this.onItemMouseEnter.bind(this);
li.onclick = this.onItemClick.bind(this);
this.mentionList.appendChild(li);
}
this.itemIndex = 0;
this.highlightItem();
this.showMentionList();
} else {
this.hideMentionList();
}
}
nextItem() {
this.itemIndex = (this.itemIndex + 1) % this.values.length;
this.suspendMouseEnter = true;
this.highlightItem();
}
prevItem() {
this.itemIndex = ((this.itemIndex + this.values.length) - 1) % this.values.length;
this.suspendMouseEnter = true;
this.highlightItem();
}
hasValidChars(s) {
return this.options.allowedChars.test(s);
}
containerBottomIsNotVisible(topPos, containerPos) {
const mentionContainerBottom = topPos + this.mentionContainer.offsetHeight + containerPos.top;
return mentionContainerBottom > window.pageYOffset + window.innerHeight;
}
containerRightIsNotVisible(leftPos, containerPos) {
if (this.options.fixMentionsToQuill) {
return false;
}
const rightPos = leftPos + this.mentionContainer.offsetWidth + containerPos.left;
const browserWidth = window.pageXOffset + document.documentElement.clientWidth;
return rightPos > browserWidth;
}
setMentionContainerPosition() {
const containerPos = this.quill.container.getBoundingClientRect();
const mentionCharPos = this.quill.getBounds(this.mentionCharPos);
const containerHeight = this.mentionContainer.offsetHeight;
let topPos = this.options.offsetTop;
let leftPos = this.options.offsetLeft;
// handle horizontal positioning
if (this.options.fixMentionsToQuill) {
const rightPos = 0;
this.mentionContainer.style.right = `${rightPos}px`;
} else {
leftPos += mentionCharPos.left;
}
if (this.containerRightIsNotVisible(leftPos, containerPos)) {
const containerWidth = this.mentionContainer.offsetWidth + this.options.offsetLeft;
const quillWidth = containerPos.width;
leftPos = quillWidth - containerWidth;
}
// handle vertical positioning
if (this.options.defaultMenuOrientation === 'top') {
// Attempt to align the mention container with the top of the quill editor
if (this.options.fixMentionsToQuill) {
topPos = -1 * (containerHeight + this.options.offsetTop);
} else {
topPos = mentionCharPos.top - (containerHeight + this.options.offsetTop);
}
// default to bottom if the top is not visible
if (topPos + containerPos.top <= 0) {
let overMentionCharPos = this.options.offsetTop;
if (this.options.fixMentionsToQuill) {
overMentionCharPos += containerPos.height;
} else {
overMentionCharPos += mentionCharPos.bottom;
}
topPos = overMentionCharPos;
}
} else {
// Attempt to align the mention container with the bottom of the quill editor
if (this.options.fixMentionsToQuill) {
topPos += containerPos.height;
} else {
topPos += mentionCharPos.bottom;
}
// default to the top if the bottom is not visible
if (this.containerBottomIsNotVisible(topPos, containerPos)) {
let overMentionCharPos = this.options.offsetTop * -1;
if (!this.options.fixMentionsToQuill) {
overMentionCharPos += mentionCharPos.top;
}
topPos = overMentionCharPos - containerHeight;
}
}
this.mentionContainer.style.top = `${topPos}px`;
this.mentionContainer.style.left = `${leftPos}px`;
this.mentionContainer.style.visibility = 'visible';
}
onSomethingChange() {
const range = this.quill.getSelection();
if (range == null) return;
this.cursorPos = range.index;
const startPos = Math.max(0, this.cursorPos - this.options.maxChars);
const beforeCursorPos = this.quill.getText(startPos, this.cursorPos - startPos);
const mentionCharIndex = this.options.mentionDenotationChars.reduce((prev, cur) => {
const previousIndex = prev;
const mentionIndex = beforeCursorPos.lastIndexOf(cur);
return mentionIndex > previousIndex ? mentionIndex : previousIndex;
}, -1);
if (mentionCharIndex > -1) {
if (this.options.isolateCharacter && !(mentionCharIndex == 0 || !!beforeCursorPos[mentionCharIndex - 1].match(/\s/g))) {
this.hideMentionList();
return;
}
const mentionCharPos = this.cursorPos - (beforeCursorPos.length - mentionCharIndex);
this.mentionCharPos = mentionCharPos;
const textAfter = beforeCursorPos.substring(mentionCharIndex + 1);
if (textAfter.length >= this.options.minChars && this.hasValidChars(textAfter)) {
const mentionChar = beforeCursorPos[mentionCharIndex];
this.options.source(textAfter, this.renderList.bind(this, mentionChar), mentionChar);
} else {
this.hideMentionList();
}
} else {
this.hideMentionList();
}
}
onTextChange(delta, oldDelta, source) {
if (source === 'user') {
this.onSomethingChange();
}
}
onSelectionChange(range) {
if (range && range.length === 0) {
this.onSomethingChange();
} else {
this.hideMentionList();
}
}
}
Quill.register('modules/mention', Mention);
export default Mention;

View file

@ -1,8 +1,6 @@
import Quill from 'quill';
import { ImageDrop } from 'quill-image-drop-module';
// required for quill-mention
window.Quill = Quill;
Quill.register('modules/imageDrop', ImageDrop);

View file

@ -9,11 +9,11 @@ frappe.ui.open_dialogs = [];
frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup {
constructor(opts) {
super();
this.display = false;
this.is_dialog = true;
$.extend(this, { animate: true, size: null }, opts);
super();
this.make();
}

View file

@ -29,6 +29,14 @@ frappe.ui.keys.get_key = function(e) {
// add ctrl+ the key
key = 'shift+' + key;
}
if (e.altKey) {
// add alt+ the key
key = 'alt+' + key;
}
if (e.altKey && e.ctrlKey) {
// add alt+ctrl+ the key or single key e.g f1,f2,etc..
return key.toLowerCase();
}
return key.toLowerCase();
}
@ -101,7 +109,12 @@ frappe.ui.keys.key_map = {
39: 'right',
38: 'up',
40: 'down',
32: 'space'
32: 'space',
112: 'f1',
113: 'f2',
114: 'f3',
115: 'f4',
116: 'f5'
}
// keyCode map
@ -130,4 +143,4 @@ function close_grid_and_dialog() {
cur_dialog.cancel();
return false;
}
}
}

View file

@ -177,7 +177,7 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
this.previous_filters = this.get_filter_values();
// clear previous_filters after 3 seconds, to allow refresh for new data
setTimeout(() => this.previous_filters = null, 3000);
setTimeout(() => this.previous_filters = null, 10000);
if (f.on_change) {
f.on_change(this);

View file

@ -1,7 +1,7 @@
@import "variables.less";
@import (less) "quill/dist/quill.snow.css";
@import (less) "quill/dist/quill.bubble.css";
@import (less) "quill-mention/src/quill.mention.css";
@import (less) "../js/frappe/form/controls/quill-mention/quill.mention.css";
.ql-toolbar.ql-snow, .ql-container.ql-snow {
border-color: @border-color;

View file

@ -41,6 +41,7 @@ class TestScheduler(TestCase):
last_event = now_datetime().replace(hour=0, minute=0, second=0, microsecond=0)
next_event = last_event + relativedelta(hours=2)
frappe.flags.ran_schedulers = []
enqueue_applicable_events(frappe.local.site, next_event, last_event)
self.assertTrue("all" in frappe.flags.ran_schedulers)
self.assertTrue("hourly" in frappe.flags.ran_schedulers)
@ -56,6 +57,7 @@ class TestScheduler(TestCase):
next_event = now_datetime().replace(hour=0, minute=0, second=0, microsecond=0)
last_event = next_event - relativedelta(hours=2)
frappe.flags.ran_schedulers = []
enqueue_applicable_events(frappe.local.site, next_event, last_event)
self.assertTrue("all" in frappe.flags.ran_schedulers)
self.assertFalse("hourly" in frappe.flags.ran_schedulers)

View file

@ -93,7 +93,7 @@ def read_options_from_html(html):
toggle_visible_pdf(soup)
# use regex instead of soup-parser
for attr in ("margin-top", "margin-bottom", "margin-left", "margin-right", "page-size"):
for attr in ("margin-top", "margin-bottom", "margin-left", "margin-right", "page-size", "header-spacing"):
try:
pattern = re.compile(r"(\.print-format)([\S|\s][^}]*?)(" + str(attr) + r":)(.+)(mm;)")
match = pattern.findall(html)

View file

@ -28,7 +28,6 @@
"moment-timezone": "^0.5.21",
"quill": "2.0.0-dev.2",
"quill-image-drop-module": "^1.0.3",
"quill-mention": "https://github.com/netchampfaris/quill-mention",
"redis": "^2.8.0",
"showdown": "^1.8.6",
"socket.io": "^2.0.4",

View file

@ -53,7 +53,8 @@ function get_rollup_options_for_js(output_file, input_files) {
buble({
objectAssign: 'Object.assign',
transforms: {
dangerousForOf: true
dangerousForOf: true,
classes: false
},
exclude: [path.resolve(bench_path, '**/*.css'), path.resolve(bench_path, '**/*.less')]
}),

View file

@ -3036,12 +3036,6 @@ quill-image-drop-module@^1.0.3:
dependencies:
quill "^1.2.2"
"quill-mention@https://github.com/netchampfaris/quill-mention":
version "2.0.4"
resolved "https://github.com/netchampfaris/quill-mention#c85d60ee8047bd6b15e319f410423dbb3f06b2e6"
dependencies:
quill "^1.3.4"
quill@2.0.0-dev.2:
version "2.0.0-dev.2"
resolved "https://registry.yarnpkg.com/quill/-/quill-2.0.0-dev.2.tgz#0f8bc962da28e3ebbb856f246200e7d32e39fc9f"
@ -3054,7 +3048,7 @@ quill@2.0.0-dev.2:
parchment quilljs/parchment#487850f7eb030a6c4e750ba809e58b09444e0bdb
quill-delta "^3.6.2"
quill@^1.2.2, quill@^1.3.4:
quill@^1.2.2:
version "1.3.6"
resolved "https://registry.yarnpkg.com/quill/-/quill-1.3.6.tgz#99f4de1fee85925a0d7d4163b6d8328f23317a4d"
integrity sha512-K0mvhimWZN6s+9OQ249CH2IEPZ9JmkFuCQeHAOQax3EZ2nDJ3wfGh59mnlQaZV2i7u8eFarx6wAtvQKgShojug==