Merge branch 'develop' of https://github.com/frappe/frappe into bulk-workflow-approval
This commit is contained in:
commit
61cc0d7918
23 changed files with 272 additions and 117 deletions
|
|
@ -108,7 +108,7 @@
|
|||
"no_copy": 0,
|
||||
"oldfieldname": "fieldtype",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
|
||||
"options": "Attach\nAttach Image\nBarcode\nButton\nCheck\nCode\nColor\nColumn Break\nCurrency\nData\nDate\nDatetime\nDynamic Link\nFloat\nFold\nGeolocation\nHeading\nHTML\nHTML Editor\nImage\nInt\nLink\nLong Text\nMarkdown Editor\nPassword\nPercent\nRead Only\nRating\nSection Break\nSelect\nSmall Text\nTable\nTable MultiSelect\nText\nText Editor\nTime\nSignature",
|
||||
"permlevel": 0,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
|
|
@ -1710,7 +1710,7 @@
|
|||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2019-04-08 12:19:53.415372",
|
||||
"modified": "2019-05-28 12:19:53.415372",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Core",
|
||||
"name": "DocField",
|
||||
|
|
|
|||
|
|
@ -178,10 +178,11 @@ class Database(object):
|
|||
frappe.errprint(("Execution time: {0} sec").format(round(time_end - time_start, 2)))
|
||||
|
||||
except Exception as e:
|
||||
if(frappe.db.db_type == 'postgres'):
|
||||
if frappe.conf.db_type == 'postgres':
|
||||
self.rollback()
|
||||
|
||||
if frappe.db.db_type == 'mariadb' and self.is_syntax_error(e):
|
||||
elif self.is_syntax_error(e):
|
||||
# only for mariadb
|
||||
frappe.errprint('Syntax error in query:')
|
||||
frappe.errprint(query)
|
||||
|
||||
|
|
@ -929,12 +930,26 @@ class Database(object):
|
|||
if values:
|
||||
query = frappe.safe_decode(self._cursor.mogrify(query, values))
|
||||
if query.strip().lower().split()[0] in ('insert', 'delete', 'update', 'alter'):
|
||||
# ([`\"']?) Captures ', " or ` at the begining of the table name (if provided)
|
||||
# single_word_regex is designed to match following patterns
|
||||
# `tabXxx`, tabXxx and "tabXxx"
|
||||
|
||||
# multi_word_regex is designed to match following patterns
|
||||
# `tabXxx Xxx` and "tabXxx Xxx"
|
||||
|
||||
# ([`"]?) Captures " or ` at the begining of the table name (if provided)
|
||||
# \1 matches the first captured group (quote character) at the end of the table name
|
||||
# multi word table name must have surrounding quotes.
|
||||
|
||||
# (tab([A-Z]\w+)( [A-Z]\w+)*) Captures table names that start with "tab"
|
||||
# and are continued with multiple words that start with a captital letter
|
||||
# e.g. 'tabXxx' or 'tabXxx Xxx' or 'tabXxx Xxx Xxx' and so on
|
||||
# \1 matches the first captured group (quote character) at the end of the table name
|
||||
tables = [groups[1] for groups in re.findall(r'([`"\']?)(tab([A-Z]\w+)( [A-Z]\w+)*)\1', query)]
|
||||
|
||||
single_word_regex = r'([`"]?)(tab([A-Z]\w+))\1'
|
||||
multi_word_regex = r'([`"])(tab([A-Z]\w+)( [A-Z]\w+)+)\1'
|
||||
tables = []
|
||||
for regex in (single_word_regex, multi_word_regex):
|
||||
tables += [groups[1] for groups in re.findall(regex, query)]
|
||||
|
||||
if frappe.flags.touched_tables is None:
|
||||
frappe.flags.touched_tables = set()
|
||||
frappe.flags.touched_tables.update(tables)
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ def get_contact_list(txt, page_length=20):
|
|||
out = frappe.db.sql("""select email_id as value,
|
||||
concat(first_name, ifnull(concat(' ',last_name), '' )) as description
|
||||
from tabContact
|
||||
where name like %(txt)s
|
||||
where name like %(txt)s or email_id like %(txt)s
|
||||
%(condition)s
|
||||
limit %(page_length)s""", {
|
||||
'txt': '%' + txt + '%',
|
||||
|
|
|
|||
|
|
@ -242,6 +242,7 @@ frappe.patches.v12_0.replace_null_values_in_tables
|
|||
frappe.patches.v12_0.reset_home_settings
|
||||
frappe.patches.v12_0.update_print_format_type
|
||||
frappe.patches.v11_0.remove_doctype_user_permissions_for_page_and_report #2019-05-01
|
||||
frappe.patches.v11_0.apply_customization_to_custom_doctype
|
||||
frappe.patches.v12_0.remove_feedback_rating
|
||||
frappe.patches.v12_0.move_form_attachments_to_attachments_folder
|
||||
frappe.patches.v12_0.move_timeline_links_to_dynamic_links
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
import frappe
|
||||
from frappe.utils import cint
|
||||
|
||||
# This patch aims to apply & delete all the customization
|
||||
# on custom doctypes done through customize form
|
||||
|
||||
# This is required because customize form in now blocked
|
||||
# for custom doctypes and user may not be able to
|
||||
# see previous customization
|
||||
|
||||
def execute():
|
||||
custom_doctypes = frappe.get_all('DocType', filters={
|
||||
'custom': 1
|
||||
})
|
||||
|
||||
for doctype in custom_doctypes:
|
||||
property_setters = frappe.get_all('Property Setter', filters={
|
||||
'doc_type': doctype.name,
|
||||
'doctype_or_field': 'DocField'
|
||||
}, fields=['name', 'property', 'value', 'property_type', 'field_name'])
|
||||
|
||||
custom_fields = frappe.get_all('Custom Field',
|
||||
filters={'dt': doctype.name},
|
||||
fields=['*']
|
||||
)
|
||||
|
||||
property_setter_map = {}
|
||||
|
||||
for prop in property_setters:
|
||||
property_setter_map[prop.field_name] = prop
|
||||
frappe.db.sql('DELETE FROM `tabProperty Setter` WHERE `name`=%s', prop.name)
|
||||
|
||||
meta = frappe.get_doc('DocType', doctype.name)
|
||||
|
||||
for df in meta.fields:
|
||||
ps = property_setter_map.get(df.fieldname, None)
|
||||
if ps:
|
||||
value = cint(ps.value) if ps.property_type == 'Int' else ps.value
|
||||
df.set(ps.property, value)
|
||||
|
||||
for cf in custom_fields:
|
||||
cf.pop('parenttype')
|
||||
cf.pop('parentfield')
|
||||
cf.pop('parent')
|
||||
cf.pop('name')
|
||||
field = meta.get_field(cf.fieldname)
|
||||
if field:
|
||||
field.update(cf)
|
||||
else:
|
||||
df = frappe.new_doc('DocField', meta, 'fields')
|
||||
df.update(cf)
|
||||
meta.fields.append(df)
|
||||
frappe.db.sql('DELETE FROM `tabCustom Field` WHERE name=%s', cf.name)
|
||||
|
||||
meta.save()
|
||||
|
|
@ -290,28 +290,31 @@ frappe.get_modal = function(title, content) {
|
|||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<div class="row">
|
||||
<div class="col-xs-7">
|
||||
<div class="flex justify-between">
|
||||
<div class="fill-width">
|
||||
<span class="indicator hidden"></span>
|
||||
<h4 class="modal-title" style="font-weight: bold;">${title}</h4>
|
||||
</div>
|
||||
<div class="col-xs-5">
|
||||
<div class="text-right buttons">
|
||||
<button type="button" class="btn btn-default btn-sm btn-modal-close"
|
||||
data-dismiss="modal">
|
||||
<h4 class="modal-title" style="font-weight: bold;">${title}</h4>
|
||||
</div>
|
||||
<div>
|
||||
<div class="text-right buttons">
|
||||
<button type="button" class="btn btn-default btn-sm btn-modal-minimize hide">
|
||||
<i class="octicon octicon-chevron-down" style="padding: 1px 0px;"></i>
|
||||
</button>
|
||||
<button type="button" class="btn btn-default btn-sm btn-modal-close" data-dismiss="modal">
|
||||
<i class="octicon octicon-x visible-xs" style="padding: 1px 0px;"></i>
|
||||
<span class="hidden-xs">${__("Close")}</span></button>
|
||||
<button type="button" class="btn btn-primary btn-sm hide">
|
||||
${__("Confirm")}</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body ui-front">${content}
|
||||
<span class="hidden-xs">${__("Close")}</span>
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm hide">
|
||||
${__("Confirm")}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body ui-front">${content}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>`)
|
||||
</div>`);
|
||||
};
|
||||
|
||||
frappe.is_online = function() {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
let me = this;
|
||||
|
||||
this.page_length = 20;
|
||||
this.start = 0;
|
||||
|
||||
let fields = [
|
||||
{
|
||||
|
|
@ -55,7 +56,13 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
},
|
||||
{ fieldtype: "Section Break" },
|
||||
{ fieldtype: "HTML", fieldname: "results_area" },
|
||||
{ fieldtype: "Button", fieldname: "make_new", label: __('Create a new ' + me.doctype) }
|
||||
{ fieldtype: "Button", fieldname: "more_btn", label: __("More"),
|
||||
click: function(){
|
||||
me.start += 20;
|
||||
frappe.flags.auto_scroll = true;
|
||||
me.get_results();
|
||||
}
|
||||
}
|
||||
]);
|
||||
|
||||
let doctype_plural = !this.doctype.endsWith('y') ? this.doctype + 's'
|
||||
|
|
@ -65,25 +72,30 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
title: __("Select {0}", [(this.doctype=='[Select]') ? __("value") : __(doctype_plural)]),
|
||||
fields: fields,
|
||||
primary_action_label: __("Get Items"),
|
||||
secondary_action_label: __("Make {0}", [me.doctype]),
|
||||
primary_action: function() {
|
||||
me.action(me.get_checked_values(), me.args);
|
||||
},
|
||||
secondary_action: function(e) {
|
||||
// If user wants to close the modal
|
||||
if (e) {
|
||||
frappe.route_options = {};
|
||||
|
||||
Object.keys(me.setters).forEach(function(setter) {
|
||||
frappe.route_options[setter] = me.dialog.fields_dict[setter].get_value() || undefined;
|
||||
});
|
||||
|
||||
frappe.new_doc(me.doctype, true);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.$parent = $(this.dialog.body);
|
||||
this.$wrapper = this.dialog.fields_dict.results_area.$wrapper.append(`<div class="results"
|
||||
style="border: 1px solid #d1d8dd; border-radius: 3px; height: 300px; overflow: auto;"></div>`);
|
||||
this.$results = this.$wrapper.find('.results');
|
||||
this.$make_new_btn = this.dialog.fields_dict.make_new.$wrapper;
|
||||
|
||||
this.$placeholder = $(`<div class="multiselect-empty-state">
|
||||
<span class="text-center" style="margin-top: -40px;">
|
||||
<i class="fa fa-2x fa-tags text-extra-muted"></i>
|
||||
<p class="text-extra-muted">No ${this.doctype} found</p>
|
||||
<button class="btn btn-default btn-xs text-muted" data-fieldtype="Button"
|
||||
data-fieldname="make_new" placeholder="" value="">Make a new ${this.doctype}</button>
|
||||
</span>
|
||||
</div>`);
|
||||
this.$results = this.$wrapper.find('.results');
|
||||
this.$results.append(this.make_list_row());
|
||||
|
||||
this.args = {};
|
||||
|
||||
|
|
@ -94,6 +106,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
|
||||
bind_events: function() {
|
||||
let me = this;
|
||||
|
||||
this.$results.on('click', '.list-item-container', function (e) {
|
||||
if (!$(e.target).is(':checkbox') && !$(e.target).is('a')) {
|
||||
$(this).find(':checkbox').trigger('click');
|
||||
|
|
@ -105,10 +118,12 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
});
|
||||
|
||||
this.$parent.find('.input-with-feedback').on('change', (e) => {
|
||||
frappe.flags.auto_scroll = false;
|
||||
this.get_results();
|
||||
});
|
||||
|
||||
this.$parent.find('[data-fieldname="date_range"]').on('blur', (e) => {
|
||||
frappe.flags.auto_scroll = false;
|
||||
this.get_results();
|
||||
});
|
||||
|
||||
|
|
@ -116,17 +131,10 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
var $this = $(this);
|
||||
clearTimeout($this.data('timeout'));
|
||||
$this.data('timeout', setTimeout(function() {
|
||||
frappe.flags.auto_scroll = false;
|
||||
me.get_results();
|
||||
}, 300));
|
||||
});
|
||||
|
||||
this.$parent.on('click', '.btn[data-fieldname="make_new"]', (e) => {
|
||||
frappe.route_options = {};
|
||||
Object.keys(this.setters).forEach(function(setter) {
|
||||
frappe.route_options[setter] = me.dialog.fields_dict[setter].get_value() || undefined;
|
||||
});
|
||||
frappe.new_doc(this.doctype, true);
|
||||
});
|
||||
},
|
||||
|
||||
get_checked_values: function() {
|
||||
|
|
@ -170,22 +178,28 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
|
||||
render_result_list: function(results, more = 0) {
|
||||
var me = this;
|
||||
this.$results.empty();
|
||||
if(results.length === 0) {
|
||||
this.$make_new_btn.addClass('hide');
|
||||
this.$results.append(me.$placeholder);
|
||||
return;
|
||||
}
|
||||
this.$make_new_btn.removeClass('hide');
|
||||
|
||||
this.$results.append(this.make_list_row());
|
||||
var more_btn = me.dialog.fields_dict.more_btn.$wrapper;
|
||||
|
||||
// Make empty result set if filter is set
|
||||
if (!frappe.flags.auto_scroll) {
|
||||
this.$results.empty();
|
||||
}
|
||||
|
||||
if(results.length === 0) {
|
||||
this.$results.empty();
|
||||
more_btn.hide();
|
||||
return;
|
||||
} else if(more) {
|
||||
more_btn.show();
|
||||
}
|
||||
|
||||
results.forEach((result) => {
|
||||
me.$results.append(me.make_list_row(result));
|
||||
})
|
||||
if (more) {
|
||||
let message = __("Only {0} entries shown. Please filter for more specific results.", [this.page_length]);
|
||||
me.$results.append($(`<div class="text-muted small" style="text-align: center;
|
||||
margin: 10px;">${message}</div>`));
|
||||
});
|
||||
|
||||
if (frappe.flags.auto_scroll) {
|
||||
this.$results.animate({scrollTop: me.$results.prop('scrollHeight')}, 500);
|
||||
}
|
||||
},
|
||||
|
||||
|
|
@ -208,6 +222,7 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
txt: me.dialog.fields_dict["search_term"].get_value(),
|
||||
filters: filters,
|
||||
filter_fields: Object.keys(me.setters).concat([me.date_field]),
|
||||
start: this.start,
|
||||
page_length: this.page_length + 1,
|
||||
query: this.get_query ? this.get_query().query : '',
|
||||
as_dict: 1
|
||||
|
|
@ -219,8 +234,8 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
args: args,
|
||||
callback: function(r) {
|
||||
let results = [], more = 0;
|
||||
if(r.values.length) {
|
||||
if(r.values.length > me.page_length){
|
||||
if (r.values.length) {
|
||||
if (r.values.length > me.page_length) {
|
||||
r.values.pop();
|
||||
more = 1;
|
||||
}
|
||||
|
|
@ -241,7 +256,9 @@ frappe.ui.form.MultiSelectDialog = Class.extend({
|
|||
});
|
||||
|
||||
// Preselect oldest entry
|
||||
results[0].checked = 1
|
||||
if (me.start < 1) {
|
||||
results[0].checked = 1;
|
||||
}
|
||||
}
|
||||
me.render_result_list(results, more);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -185,7 +185,10 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
|
|||
throw "saving";
|
||||
}
|
||||
|
||||
frappe.ui.form.remove_old_form_route();
|
||||
// ensure we remove new docs routes ONLY
|
||||
if ( frm.is_new() ) {
|
||||
frappe.ui.form.remove_old_form_route();
|
||||
}
|
||||
frappe.ui.form.is_saving = true;
|
||||
|
||||
return frappe.call({
|
||||
|
|
@ -224,14 +227,9 @@ frappe.ui.form.save = function (frm, action, callback, btn) {
|
|||
}
|
||||
|
||||
frappe.ui.form.remove_old_form_route = () => {
|
||||
let index = -1;
|
||||
let current_route = frappe.get_route();
|
||||
frappe.route_history.map((arr, i) => {
|
||||
if (arr.join("/") === current_route.join("/")) {
|
||||
index = i;
|
||||
}
|
||||
});
|
||||
frappe.route_history.splice(index, 1);
|
||||
let current_route = frappe.get_route().join("/");
|
||||
frappe.route_history = frappe.route_history
|
||||
.filter((route) => route.join("/") !== current_route);
|
||||
}
|
||||
|
||||
frappe.ui.form.update_calling_link = (newdoc) => {
|
||||
|
|
|
|||
|
|
@ -195,7 +195,11 @@ $(window).on('hashchange', function() {
|
|||
|
||||
// hide open dialog
|
||||
if(window.cur_dialog && cur_dialog.hide_on_page_refresh) {
|
||||
cur_dialog.hide();
|
||||
if (!cur_dialog.minimizable) {
|
||||
cur_dialog.hide();
|
||||
} else if (!cur_dialog.is_minimized) {
|
||||
cur_dialog.toggle_minimize();
|
||||
}
|
||||
}
|
||||
|
||||
frappe.route();
|
||||
|
|
|
|||
|
|
@ -61,6 +61,10 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup {
|
|||
this.get_close_btn().html(this.secondary_action_label || this.action.secondary.label);
|
||||
}
|
||||
|
||||
if (this.minimizable) {
|
||||
this.get_minimize_btn().removeClass('hide').on('click', () => this.toggle_minimize());
|
||||
}
|
||||
|
||||
var me = this;
|
||||
this.$wrapper
|
||||
.on("hide.bs.modal", function() {
|
||||
|
|
@ -101,6 +105,10 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup {
|
|||
return this.$wrapper.find(".modal-header .btn-primary");
|
||||
}
|
||||
|
||||
get_minimize_btn() {
|
||||
return this.$wrapper.find(".modal-header .btn-modal-minimize");
|
||||
}
|
||||
|
||||
set_message(text) {
|
||||
this.$message.removeClass('hide');
|
||||
this.$body.addClass('hide');
|
||||
|
|
@ -179,6 +187,13 @@ frappe.ui.Dialog = class Dialog extends frappe.ui.FieldGroup {
|
|||
cancel() {
|
||||
this.get_close_btn().trigger("click");
|
||||
}
|
||||
toggle_minimize() {
|
||||
let modal = this.$wrapper.closest('.modal').toggleClass('modal-minimize');
|
||||
modal.attr('tabindex') ? modal.removeAttr('tabindex') : modal.attr('tabindex', -1);
|
||||
this.get_minimize_btn().find('i').toggleClass('octicon-chevron-down').toggleClass('octicon-chevron-up');
|
||||
this.is_minimized = !this.is_minimized;
|
||||
this.on_minimize_toggle && this.on_minimize_toggle(this.is_minimized);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -175,9 +175,10 @@ frappe.ui.LinkPreview = class {
|
|||
animation: false,
|
||||
});
|
||||
|
||||
if(!this.is_link) {
|
||||
this.element.data('bs.popover').tip().addClass('control-field-popover');
|
||||
}
|
||||
const $popover = this.element.data('bs.popover').tip();
|
||||
|
||||
$popover.addClass('link-preview-popover');
|
||||
$popover.toggleClass('control-field-popover', this.is_link);
|
||||
|
||||
this.$links.push(this.element);
|
||||
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ Object.assign(frappe.energy_points, {
|
|||
},
|
||||
get_history_log_message(log) {
|
||||
const owner_name = frappe.user.full_name(log.owner).bold();
|
||||
const user = frappe.user.full_name(log.user).bold();
|
||||
const ref_doc = log.reference_name;
|
||||
|
||||
if (log.type === 'Appreciation') {
|
||||
|
|
@ -42,7 +41,7 @@ Object.assign(frappe.energy_points, {
|
|||
return __('{0} criticized on {1}', [owner_name, ref_doc]);
|
||||
}
|
||||
if (log.type === 'Revert') {
|
||||
return __('{0} reverted {1}', [user, log.revert_of]);
|
||||
return __('{0} reverted {1}', [owner_name, log.revert_of]);
|
||||
}
|
||||
return __('via automatic rule {0} on {1}', [log.rule.bold(), ref_doc]);
|
||||
},
|
||||
|
|
@ -57,8 +56,7 @@ Object.assign(frappe.energy_points, {
|
|||
return __('{0} criticized {1}', [owner_name, user]);
|
||||
}
|
||||
if (log.type === 'Revert') {
|
||||
return __('{0} reverted {1}', [user,
|
||||
frappe.utils.get_form_link('Energy Point Log', log.revert_of, true)]);
|
||||
return __('{0} reverted {1}', [owner_name, log.revert_of]);
|
||||
}
|
||||
return __('gained by {0} via automatic rule {1}', [user, log.rule.bold()]);
|
||||
},
|
||||
|
|
|
|||
|
|
@ -60,7 +60,11 @@ frappe.views.Container = Class.extend({
|
|||
|
||||
// hide dialog
|
||||
if(window.cur_dialog && cur_dialog.display && !cur_dialog.keep_open) {
|
||||
cur_dialog.hide();
|
||||
if (!cur_dialog.minimizable) {
|
||||
cur_dialog.hide();
|
||||
} else if (!cur_dialog.is_minimized) {
|
||||
cur_dialog.toggle_minimize();
|
||||
}
|
||||
}
|
||||
|
||||
// hide current
|
||||
|
|
|
|||
|
|
@ -464,27 +464,6 @@ li.user-progress {
|
|||
border-radius: 0px;
|
||||
}
|
||||
|
||||
// like pop-over
|
||||
.liked-by-popover {
|
||||
.popover-content {
|
||||
padding: 0px;
|
||||
overflow: scroll;
|
||||
max-height: 150px;
|
||||
}
|
||||
min-width: 100px;
|
||||
ul {
|
||||
margin: 0px;
|
||||
li {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: @btn-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.screenshot {
|
||||
border: 1px solid @border-color;
|
||||
box-shadow: 1px 1px 7px rgba(0,0,0,0.15);
|
||||
|
|
@ -1074,6 +1053,27 @@ img.img-loading:after {
|
|||
}
|
||||
}
|
||||
|
||||
// like pop-over
|
||||
.liked-by-popover {
|
||||
.popover-content {
|
||||
padding: 0px;
|
||||
overflow: scroll;
|
||||
max-height: 150px;
|
||||
}
|
||||
min-width: 100px;
|
||||
ul {
|
||||
margin: 0px;
|
||||
li {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
background: @btn-bg;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
body.full-width {
|
||||
@media (min-width: @screen-md) {
|
||||
.container {
|
||||
|
|
@ -1087,3 +1087,21 @@ body.full-width {
|
|||
.whitespace-nowrap {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.modal-minimize {
|
||||
position: initial;
|
||||
.modal-backdrop {
|
||||
display: none;
|
||||
}
|
||||
.modal-dialog {
|
||||
z-index: 101;
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
margin: 0;
|
||||
max-width: 500px;
|
||||
}
|
||||
.modal-body {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
|
@ -80,3 +80,11 @@
|
|||
margin-top: 7.5px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
||||
.indicator.blink {
|
||||
animation: blink 1s linear infinite;
|
||||
}
|
||||
|
||||
@keyframes blink {
|
||||
50% { opacity: 0.5; }
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
.popover {
|
||||
.link-preview-popover {
|
||||
border-radius: 0;
|
||||
max-width: 100%;
|
||||
.popover-content {
|
||||
|
|
|
|||
|
|
@ -131,26 +131,35 @@ def get_energy_points(user):
|
|||
@frappe.whitelist()
|
||||
def get_user_energy_and_review_points(user=None, from_date=None, as_dict=True):
|
||||
conditions = ''
|
||||
values = []
|
||||
given_points_condition = ''
|
||||
values = frappe._dict()
|
||||
if user:
|
||||
conditions = 'WHERE `user` = %s'
|
||||
values.append(user)
|
||||
conditions = 'WHERE `user` = %(user)s'
|
||||
values.user = user
|
||||
if from_date:
|
||||
conditions += 'WHERE' if not conditions else 'AND'
|
||||
conditions += ' `creation` >= %s'
|
||||
values.append(from_date)
|
||||
given_points_condition += "AND `creation` >= %(from_date)s"
|
||||
conditions += " `creation` >= %(from_date)s OR `type`='Review'"
|
||||
values.from_date = from_date
|
||||
|
||||
points_list = frappe.db.sql("""
|
||||
SELECT
|
||||
SUM(CASE WHEN `type`!= 'Review' THEN `points` ELSE 0 END) as energy_points,
|
||||
SUM(CASE WHEN `type`='Review' THEN `points` ELSE 0 END) as review_points,
|
||||
SUM(CASE WHEN `type`='Review' and `points` < 0 THEN ABS(`points`) ELSE 0 END) as given_points,
|
||||
SUM(CASE WHEN `type` != 'Review' THEN `points` ELSE 0 END) AS energy_points,
|
||||
SUM(CASE WHEN `type` = 'Review' THEN `points` ELSE 0 END) AS review_points,
|
||||
SUM(CASE
|
||||
WHEN `type`='Review' AND `points` < 0 {given_points_condition}
|
||||
THEN ABS(`points`)
|
||||
ELSE 0
|
||||
END) as given_points,
|
||||
`user`
|
||||
FROM `tabEnergy Point Log`
|
||||
{conditions}
|
||||
GROUP BY `user`
|
||||
ORDER BY `energy_points` DESC
|
||||
""".format(conditions=conditions), values=tuple(values), as_dict=1)
|
||||
""".format(
|
||||
conditions=conditions,
|
||||
given_points_condition=given_points_condition
|
||||
), values=values, as_dict=1)
|
||||
|
||||
if not as_dict:
|
||||
return points_list
|
||||
|
|
|
|||
|
|
@ -48,6 +48,12 @@ class TestDB(unittest.TestCase):
|
|||
todo.save()
|
||||
self.assertIn('tabToDo', frappe.flags.touched_tables)
|
||||
|
||||
if frappe.db.db_type != "postgres":
|
||||
frappe.flags.touched_tables = set()
|
||||
frappe.db.sql("UPDATE tabToDo SET description = 'Updated Description'")
|
||||
self.assertNotIn('tabToDo SET', frappe.flags.touched_tables)
|
||||
self.assertIn('tabToDo', frappe.flags.touched_tables)
|
||||
|
||||
frappe.flags.touched_tables = set()
|
||||
todo.delete()
|
||||
self.assertIn('tabToDo', frappe.flags.touched_tables)
|
||||
|
|
|
|||
|
|
@ -507,7 +507,7 @@ def extract_messages_from_code(code, is_py=False):
|
|||
:param is_py: include messages in triple quotes e.g. `_('''message''')`"""
|
||||
try:
|
||||
code = frappe.as_unicode(render_include(code))
|
||||
except (TemplateError, ImportError, InvalidIncludePath):
|
||||
except (TemplateError, ImportError, InvalidIncludePath, IOError):
|
||||
# Exception will occur when it encounters John Resig's microtemplating code
|
||||
pass
|
||||
|
||||
|
|
|
|||
|
|
@ -99,7 +99,10 @@ def get_page_info_from_doctypes(path=None):
|
|||
values = []
|
||||
controller = get_controller(doctype)
|
||||
meta = frappe.get_meta(doctype)
|
||||
condition_field = meta.is_published_field or controller.website.condition_field
|
||||
|
||||
condition_field = (meta.is_published_field or
|
||||
# custom doctypes dont have controllers and no website attribute
|
||||
(controller.website.condition_field if not meta.custom else None))
|
||||
|
||||
if condition_field:
|
||||
condition ="where {0}=1".format(condition_field)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
"cookie": "^0.3.1",
|
||||
"express": "^4.16.2",
|
||||
"fast-deep-equal": "^2.0.1",
|
||||
"frappe-datatable": "^1.13.1",
|
||||
"frappe-datatable": "^1.13.2",
|
||||
"frappe-gantt": "^0.1.0",
|
||||
"fuse.js": "^3.2.0",
|
||||
"highlight.js": "^9.12.0",
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ rauth>=0.6.2
|
|||
requests
|
||||
redis==2.10.6
|
||||
selenium
|
||||
babel
|
||||
babel==2.6.0
|
||||
ipython
|
||||
html2text==2016.9.19
|
||||
email_reply_parser
|
||||
|
|
|
|||
|
|
@ -1753,10 +1753,10 @@ fragment-cache@^0.2.1:
|
|||
dependencies:
|
||||
map-cache "^0.2.2"
|
||||
|
||||
frappe-datatable@^1.13.1:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.13.1.tgz#dbfa27fe735832cea54a0b35e3d3bfb839778c8d"
|
||||
integrity sha512-FOC8dpsOSI+KnF5sBrYja9b2Y+3qUvYy/H6108QchKSvXYvuWVr/uuLk2N5xlz38PWU3d7n5lK0dkx+zXTKJ0w==
|
||||
frappe-datatable@^1.13.2:
|
||||
version "1.13.2"
|
||||
resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.13.2.tgz#8b36c7cfc0ea660fc72eea8b1ae3c5dcc2a7d67d"
|
||||
integrity sha512-4PyPDX22K4e4S3WGlLQx3oyxIW+ENsbGiN9L6aUpmjU+fOCC7J/FfSwGKdua2f+4yD+2ObpkyJYazBl3inAeCA==
|
||||
dependencies:
|
||||
hyperlist "^1.0.0-beta"
|
||||
lodash "^4.17.5"
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue