Merge branch 'develop' into desktop-fixes

This commit is contained in:
Faris Ansari 2019-04-01 10:18:57 +05:30 committed by GitHub
commit 0b574a34b5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
111 changed files with 10453 additions and 6853 deletions

View file

@ -137,6 +137,7 @@
"Cypress": true,
"cy": true,
"it": true,
"expect": true,
"context": true,
"before": true,
"beforeEach": true

View file

@ -0,0 +1,38 @@
context('Rating Control', () => {
beforeEach(() => {
cy.login('Administrator', 'qwe');
});
it('click on the star rating to record value', () => {
cy.visit('/desk');
cy.dialog('Rating', [{
'fieldname': 'rate',
'fieldtype': 'Rating',
}]).as('dialog');
cy.get('div.rating')
.children('i.fa')
.first()
.click()
.should('have.class', 'star-click');
cy.get('@dialog').then(dialog => {
var value = dialog.get_value('rate');
expect(value).to.equal(1);
});
});
it('hover on the star', () => {
cy.visit('/desk');
cy.dialog('Rating', [{
'fieldname': 'rate',
'fieldtype': 'Rating',
}]);
cy.get('div.rating')
.children('i.fa')
.first()
.invoke('trigger', 'mouseenter')
.should('have.class', 'star-hover')
.invoke('trigger', 'mouseleave')
.should('not.have.class', 'star-hover');
});
});

View file

@ -61,3 +61,17 @@ Cypress.Commands.add('new_form', (doctype) => {
Cypress.Commands.add('go_to_list', (doctype) => {
cy.visit(`/desk#List/${doctype}/List`);
});
Cypress.Commands.add('dialog', (title, fields) => {
cy.window().then(win => {
var d = new win.frappe.ui.Dialog({
title: title,
fields: fields,
primary_action: function(){
d.hide();
}
});
d.show();
return d;
});
});

View file

@ -24,7 +24,7 @@ if sys.version[0] == '2':
reload(sys)
sys.setdefaultencoding("utf-8")
__version__ = '11.1.16'
__version__ = '11.1.17'
__title__ = "Frappe Framework"
local = Local()

View file

@ -17,6 +17,7 @@ from frappe.utils.change_log import get_versions
from frappe.translate import get_lang_dict
from frappe.email.inbox import get_email_accounts
from frappe.core.doctype.feedback_trigger.feedback_trigger import get_enabled_feedback_trigger
from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled
def get_bootinfo():
"""build and return boot info"""
@ -78,6 +79,7 @@ def get_bootinfo():
bootinfo.gsuite_enabled = get_gsuite_status()
bootinfo.success_action = get_success_action()
bootinfo.update(get_email_accounts(user=frappe.session.user))
bootinfo.energy_points_enabled = is_energy_point_enabled()
return bootinfo

View file

@ -117,7 +117,10 @@ def make_asset_dirs(make_copy=False, restore=False):
os.unlink(target)
else:
shutil.rmtree(target)
os.symlink(source, target)
try:
os.symlink(source, target)
except OSError:
print('Cannot link {} to {}'.format(source, target))
else:
# warnings.warn('Source {source} does not exist.'.format(source = source))
pass

View file

@ -99,4 +99,15 @@ def get_data():
'idx': 15,
"description": "Build your profile and share posts with other users."
},
{
"module_name": 'leaderboard',
"category": "Places",
"label": _('Leaderboard'),
"icon": "octicon octicon-list-ordered",
"type": "link",
"link": "#social/users",
"color": '#FF4136',
'standard': 1,
'idx': 16
},
]

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -2,7 +2,7 @@ frappe.ui.form.on('User', {
before_load: function(frm) {
var update_tz_select = function(user_language) {
frm.set_df_property("time_zone", "options", [""].concat(frappe.all_timezones));
}
};
if(!frappe.all_timezones) {
frappe.call({
@ -25,7 +25,7 @@ frappe.ui.form.on('User', {
args: {
role_profile: frm.doc.role_profile_name
},
callback: function (data) {
callback: function(data) {
frm.set_value("roles", []);
$.each(data.message || [], function(i, v){
var d = frm.add_child("roles");
@ -60,6 +60,7 @@ frappe.ui.form.on('User', {
frm.reload_doc();
return;
}
if(doc.name===frappe.session.user && !doc.__unsaved
&& frappe.all_timezones
&& (doc.language || frappe.boot.user.language)
@ -78,7 +79,7 @@ frappe.ui.form.on('User', {
"user": doc.name
};
frappe.set_route('List', 'User Permission');
}, __("Permissions"))
}, __("Permissions"));
frm.add_custom_button(__('View Permitted Documents'),
() => frappe.set_route('query-report', 'Permitted Documents For User',
@ -93,7 +94,7 @@ frappe.ui.form.on('User', {
args: {
"user": frm.doc.name
}
})
});
}, __("Password"));
frm.add_custom_button(__("Reset OTP Secret"), function() {
@ -102,7 +103,7 @@ frappe.ui.form.on('User', {
args: {
"user": frm.doc.name
}
})
});
}, __("Password"));
frm.trigger('enabled');
@ -130,8 +131,8 @@ frappe.ui.form.on('User', {
}
if (!found){
frm.add_custom_button(__("Create User Email"), function() {
frm.events.create_user_email(frm)
})
frm.events.create_user_email(frm);
});
}
}
@ -146,7 +147,7 @@ frappe.ui.form.on('User', {
},
validate: function(frm) {
if(frm.roles_editor) {
frm.roles_editor.set_roles_in_table()
frm.roles_editor.set_roles_in_table();
}
},
enabled: function(frm) {
@ -173,18 +174,18 @@ frappe.ui.form.on('User', {
"awaiting_password": 1,
"enable_incoming": 1
};
frappe.model.with_doctype("Email Account", function (doc) {
frappe.model.with_doctype("Email Account", function(doc) {
var doc = frappe.model.get_new_doc("Email Account");
frappe.route_flags.linked_user = frm.doc.name;
frappe.route_flags.delete_user_from_locals = true;
frappe.set_route("Form", "Email Account", doc.name);
})
});
} else {
frappe.route_flags.create_user_account = frm.doc.name;
frappe.set_route("Form", "Email Account", r.message[0]["name"]);
}
}
})
});
},
generate_keys: function(frm){
frappe.call({
@ -197,9 +198,9 @@ frappe.ui.form.on('User', {
frappe.msgprint(__("Save API Secret: ") + r.message.api_secret);
}
}
})
});
}
})
});
function has_access_to_edit_user() {
return has_common(frappe.user_roles, get_roles_for_editing_user());
@ -239,10 +240,14 @@ frappe.ModuleEditor = Class.extend({
var module = $(this).attr('data-module');
if($(this).prop("checked")) {
// remove from block_modules
me.frm.doc.block_modules = $.map(me.frm.doc.block_modules || [], function(d) { if(d.module != module){ return d } });
me.frm.doc.block_modules = $.map(me.frm.doc.block_modules || [], function(d) {
if (d.module != module) {
return d;
}
});
} else {
me.frm.add_child("block_modules", {"module": module});
}
});
}
})
});

View file

@ -1086,4 +1086,4 @@ def generate_keys(user):
user_details.save()
return {"api_secret": api_secret}
frappe.throw(frappe._("Not Permitted"), frappe.PermissionError)
frappe.throw(frappe._("Not Permitted"), frappe.PermissionError)

View file

View file

@ -0,0 +1,27 @@
.chart-container {
border: 1px solid #d1d8dd;
border-radius: 4px;
margin: 15px 0;
padding: 15px;
}
.chart-actions {
position: absolute;
right: 30px;
top: 54px;
}
.chart-column-container {
position: relative;
}
.last-synced-text {
position: absolute;
top: 56px;
right: 60px;
font-size: 12px;
}
.dashboard-graph {
padding-top: 15px;
}

View file

@ -0,0 +1,234 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
frappe.pages['dashboard'].on_page_load = function(wrapper) {
frappe.ui.make_app_page({
parent: wrapper,
title: __("Dashboard"),
single_column: true
});
frappe.dashboard = new Dashboard(wrapper);
$(wrapper).bind('show', function() {
frappe.dashboard.show();
});
};
class Dashboard {
constructor(wrapper) {
this.wrapper = $(wrapper);
$(`<div class="dashboard">
<div class="dashboard-graph row"></div>
</div>`).appendTo(this.wrapper.find(".page-content").empty());
this.container = this.wrapper.find(".dashboard-graph");
this.page = wrapper.page;
}
show() {
this.route = frappe.get_route();
const current_dashboard_name = this.route.slice(-1)[0];
if(this.dashboard_name !== current_dashboard_name) {
this.dashboard_name = current_dashboard_name;
this.page.set_title(this.dashboard_name);
this.set_dropdown();
this.container.empty();
this.refresh();
}
this.charts = {};
}
refresh() {
this.get_dashboard_doc().then((doc) => {
this.dashboard_doc = doc;
this.charts = this.dashboard_doc.charts;
this.charts.map((chart) => {
let chart_container = $("<div></div>");
chart_container.appendTo(this.container);
frappe.model.with_doc("Dashboard Chart", chart.chart).then( chart_doc => {
let dashboard_chart = new DashboardChart(chart_doc, chart_container);
dashboard_chart.show();
});
});
});
}
get_dashboard_doc() {
return frappe.model.with_doc('Dashboard', this.dashboard_name);
}
set_dropdown() {
this.page.clear_menu();
frappe.db.get_list("Dashboard").then(dashboards => {
dashboards.map(dashboard => {
let name = dashboard.name;
if(name != this.dashboard_name){
this.page.add_menu_item(name, () => frappe.set_route("dashboard", name));
}
});
});
}
}
class DashboardChart {
constructor(chart_doc, chart_container) {
this.chart_doc = chart_doc;
this.container = chart_container;
}
show() {
this.get_settings().then(() => {
this.prepare_chart_object();
this.prepare_container();
this.prepare_chart_actions();
this.fetch(this.filters).then((data) => {
this.update_last_synced();
this.data = data;
this.render();
});
});
}
prepare_container() {
const column_width_map = {
"Half": "6",
"Full": "12",
};
let columns = column_width_map[this.chart_doc.width];
this.chart_container = $(`<div class="col-sm-${columns} chart-column-container">
<div class="chart-wrapper"></div>
</div>`);
this.chart_container.appendTo(this.container);
let last_synced_text = $(`<span class="text-muted last-synced-text"></span>`);
last_synced_text.prependTo(this.chart_container);
}
prepare_chart_actions() {
let actions = [
{
label: __("Set Filters"),
action: "set-filters",
handler: this.create_set_filters_dialog.bind(this)
},
{
label: __("Force Refresh"),
action: "force-refresh",
handler: () => {
this.fetch(this.filters, true).then(data => {
this.update_chart_object();
this.data = data;
this.render();
});
}
}
];
this.set_chart_actions(actions);
}
set_chart_actions(actions) {
this.chart_actions = $(`<div class="chart-actions btn-group dropdown pull-right">
<a class="dropdown-toggle" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="false"> <button class="btn btn-default btn-xs"><span class="caret"></span></button>
</a>
<ul class="dropdown-menu" style="max-height: 300px; overflow-y: auto;">
${actions.map(action => `<li><a data-action="${action.action}">${action.label}</a></li>`).join('')}
</ul>
</div>
`);
this.chart_actions.find("a[data-action]").each((i, o) => {
const action = o.dataset.action;
$(o).click(actions.find(a => a.action === action));
});
this.chart_actions.prependTo(this.chart_container);
}
fetch(filters, refresh=false) {
return frappe.xcall(
this.settings.method_path,
{
chart_name: this.chart_doc.name,
filters: filters,
refresh: refresh ? 1 : 0,
}
);
}
render() {
const chart_type_map = {
"Line": "line",
"Bar": "bar",
};
let chart_args = {
title: this.chart_doc.chart_name.bold(),
data: this.data,
type: chart_type_map[this.chart_doc.type],
colors: [this.chart_doc.color || "light-blue"],
axisOptions: {
xIsSeries: this.settings.is_time_series
},
};
if(!this.chart) {
this.chart = new Chart(this.chart_container.find(".chart-wrapper")[0], chart_args);
} else {
this.chart.update(this.data);
}
}
update_last_synced() {
let last_synced_text = __("Last synced {0}", [comment_when(this.chart_doc.last_synced_on)]);
this.container.find(".last-synced-text").html(last_synced_text);
}
update_chart_object() {
frappe.db.get_doc("Dashboard Chart", this.chart_doc.name).then(doc => {
this.chart_doc = doc;
this.prepare_chart_object();
this.update_last_synced();
});
}
prepare_chart_object() {
this.filters = JSON.parse(this.chart_doc.filters_json || '{}');
}
get_settings() {
return new Promise(resolve => frappe.db.get_value("Dashboard Chart Source", this.chart_doc.source, "config", e => {
this.settings = JSON.parse(e.config);
resolve();
}));
}
create_set_filters_dialog() {
const d = new frappe.ui.Dialog({
title: __('Set Filters'),
fields: this.settings.filters
});
d.set_values(this.filters);
d.show();
const set_filters = () => {
const values = d.get_values();
if (!Object.entries(this.filters).map(e => values[e[0]] === e[1]).every(Boolean)) {
frappe.db.set_value("Dashboard Chart", this.chart_doc.name, "filters_json", JSON.stringify(values)).then(() => {
this.fetch(values, true).then(data => {
this.update_chart_object();
this.data = data;
this.render();
});
});
}
d.hide();
};
this.settings.filters.map(field => field.onchange = e => {
if(e) {
d.set_primary_action(__('Save Filters'), set_filters);
}
});
}
}

View file

@ -0,0 +1,19 @@
{
"content": null,
"creation": "2019-01-08 19:19:48.073410",
"docstatus": 0,
"doctype": "Page",
"idx": 0,
"modified": "2019-01-08 19:19:48.073410",
"modified_by": "Administrator",
"module": "Core",
"name": "dashboard",
"owner": "Administrator",
"page_name": "Dashboard",
"roles": [],
"script": null,
"standard": "Yes",
"style": null,
"system_page": 0,
"title": "Dashboard"
}

View file

@ -0,0 +1,32 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import json
import frappe
def cache_source(function):
def wrapper(*args, **kwargs):
filters = json.loads(kwargs.get("filters", "{}"))
chart_name = kwargs.get("chart_name")
cache_key = json.dumps({
"name": chart_name,
"filters": filters
}, default=str)
if kwargs.get("refresh"):
results = generate_and_cache_results(chart_name, function, filters, cache_key)
else:
cached_results = frappe.cache().get_value(cache_key)
if cached_results:
results = json.loads(frappe.safe_decode(cached_results))
else:
results = generate_and_cache_results(chart_name, function, filters, cache_key)
return results
return wrapper
def generate_and_cache_results(chart_name, function, filters, cache_key):
results = function(frappe._dict(filters))
frappe.cache().set_value(cache_key, json.dumps(results, default=str))
frappe.db.set_value("Dashboard Chart", chart_name, "last_synced_on", frappe.utils.now())
return results

View file

@ -11,6 +11,7 @@ from frappe.core.doctype.doctype.doctype import (clear_permissions_cache,
validate_permissions_for_doctype)
from frappe.permissions import (reset_perms, get_linked_doctypes, get_all_perms,
setup_custom_perms, add_permission, update_permission_property)
from frappe.utils.user import get_users_with_role as _get_user_with_role
not_allowed_in_permission_manager = ["DocType", "Patch Log", "Module Def", "Transaction Log"]
@ -101,13 +102,7 @@ def reset(doctype):
@frappe.whitelist()
def get_users_with_role(role):
frappe.only_for("System Manager")
return [p[0] for p in frappe.db.sql("""select distinct tabUser.name
from `tabHas Role`, tabUser where
`tabHas Role`.role=%s
and tabUser.name != "Administrator"
and `tabHas Role`.parent = tabUser.name
and tabUser.enabled=1""", role)]
_get_user_with_role(role)
@frappe.whitelist()
def get_standard_permissions(doctype):

View file

@ -46,10 +46,6 @@
<div class="usage-info-section" style="margin: 30px;">
<h4>{{ __("Space") }}</h4>
{% var database_percent = ((limits.space_usage.database_size / limits.space) * 100); %}
{% var files_percent = ((limits.space_usage.files_size / limits.space) * 100); %}
{% var backup_percent = ((limits.space_usage.backup_size / limits.space) * 100); %}
<div class="progress" style="margin-bottom: 0;">
<div class="progress-bar" style="width: {%= database_percent %}%; background-color: #5e64ff"></div>
<div class="progress-bar" style="width: {%= files_percent %}%; background-color: #743ee2"></div>
@ -66,12 +62,7 @@
{{ __("Backup Size:") }} {%= limits.space_usage.backup_size %} MB
</span>
<p>
<span class="{%= ((limits.space - limits.space_usage.total) > 50) ? "" : "text-warning" %}">
<b>{%= flt(limits.space - limits.space_usage.total, 2) %} MB</b></span>
available out of
<span><b>{%= limits.space %} MB</b></span>
</p>
<p>{{ usage_message }}</p>
</div>
{% endif %}
</div>

View file

@ -15,7 +15,33 @@ frappe.pages['usage-info'].on_page_load = function(wrapper) {
return;
}
$(frappe.render_template("usage_info", usage_info)).appendTo(page.main);
let limits = usage_info.limits;
let database_percent = (limits.space_usage.database_size / limits.space) * 100;
let files_percent = (limits.space_usage.files_size / limits.space) * 100;
let backup_percent = (limits.space_usage.backup_size / limits.space) * 100;
let total_consumed = database_percent + files_percent + backup_percent;
let last_part = backup_percent;
if (total_consumed > 100) {
last_part = backup_percent - (total_consumed - 100);
}
backup_percent = last_part;
let usage_message = '';
if (limits.space_usage.total > limits.space) {
usage_message = __('You have used up all of the space allotted to you. Please buy more space in your subscription.');
} else {
let available = flt(limits.space - limits.space_usage.total, 2);
usage_message = __('{0} available out of {1}', [(available + ' MB').bold(), (limits.space + ' MB').bold()]);
}
$(frappe.render_template("usage_info", Object.assign(usage_info, {
database_percent,
files_percent,
backup_percent,
usage_message
}))).appendTo(page.main);
var btn_text = usage_info.limits.users == 1 ? __("Upgrade") : __("Renew / Upgrade");
$(page.main).find('.btn-primary').html(btn_text).on('click', () => {

File diff suppressed because it is too large Load diff

View file

@ -879,6 +879,10 @@ class Database(object):
# implemented in specific class
pass
@staticmethod
def is_column_missing(e):
return frappe.db.is_missing_column(e)
def get_descendants(self, doctype, name):
'''Return descendants of the current record'''
node_location_indexes = self.get_value(doctype, name, ('lft', 'rgt'))

View file

@ -46,6 +46,7 @@ class MariaDBDatabase(Database):
'Dynamic Link': ('varchar', self.VARCHAR_LEN),
'Password': ('varchar', self.VARCHAR_LEN),
'Select': ('varchar', self.VARCHAR_LEN),
'Rating': ('int', '1'),
'Read Only': ('varchar', self.VARCHAR_LEN),
'Attach': ('text', ''),
'Attach Image': ('text', ''),

View file

@ -51,6 +51,7 @@ class PostgresDatabase(Database):
'Dynamic Link': ('varchar', self.VARCHAR_LEN),
'Password': ('varchar', self.VARCHAR_LEN),
'Select': ('varchar', self.VARCHAR_LEN),
'Rating': ('smallint', None),
'Read Only': ('varchar', self.VARCHAR_LEN),
'Attach': ('text', ''),
'Attach Image': ('text', ''),

View file

@ -0,0 +1,8 @@
// Copyright (c) 2019, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Dashboard', {
refresh: function(frm) {
frm.add_custom_button(__("Show Dashboard"), () => frappe.set_route('dashboard', frm.doc.name));
}
});

View file

@ -0,0 +1,132 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:dashboard_name",
"beta": 0,
"creation": "2019-01-10 12:54:40.938705",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "dashboard_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Dashboard Name",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "charts",
"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": "Charts",
"length": 0,
"no_copy": 0,
"options": "Dashboard Chart Link",
"permlevel": 0,
"precision": "",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-12 15:01:12.183208",
"modified_by": "Administrator",
"module": "Desk",
"name": "Dashboard",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "dashboard_name",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
from frappe.model.document import Document
class Dashboard(Document):
pass

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
class TestDashboard(unittest.TestCase):
pass

View file

@ -0,0 +1,69 @@
// Copyright (c) 2019, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Dashboard Chart', {
refresh: function(frm) {
frm.set_df_property("filters_section", "hidden", 1);
frm.trigger("show_filters");
},
source: function(frm) {
frm.trigger("show_filters");
},
show_filters: function(frm) {
if (frm.doc.source) {
if (frm.filters) {
frm.trigger('render_filters_table');
} else {
frappe.db.get_value("Dashboard Chart Source", frm.doc.source, "config", e => {
frm.filters = JSON.parse(e.config).filters;
frm.trigger('render_filters_table');
});
}
}
},
render_filters_table: function(frm) {
frm.set_df_property("filters_section", "hidden", 0);
let fields = frm.filters;
let wrapper = $(frm.get_field('filters_json').wrapper).empty();
let table = $(`<table class="table table-bordered" style="cursor:pointer; margin:0px;">
<thead>
<tr>
<th>${__('Filter')}</th>
<th>${__('Value')}</th>
</tr>
</thead>
<tbody></tbody>
</table>`).appendTo(wrapper);
$(`<p class="text-muted small">${__("Click table to edit")}</p>`).appendTo(wrapper);
let filters = JSON.parse(frm.doc.filters_json || '{}');
fields.map( f => {
const filter_row = $(`<tr><td>${f.label}</td><td>${filters[f.fieldname] || ""}</td></tr>`);
table.find('tbody').append(filter_row);
});
table.on('click', () => {
let dialog = new frappe.ui.Dialog({
title: __('Set Filters'),
fields: fields,
primary_action: function() {
let values = this.get_values();
if(values) {
this.hide();
frm.set_value('filters_json', JSON.stringify(values));
frm.trigger('show_filters');
}
},
primary_action_label: "Set"
});
dialog.show();
dialog.set_values(filters);
});
}
});

View file

@ -0,0 +1,432 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:chart_name",
"beta": 0,
"creation": "2019-01-10 12:28:06.282875",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "chart_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Chart Name",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "source",
"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": "Chart Source",
"length": 0,
"no_copy": 0,
"options": "Dashboard Chart Source",
"permlevel": 0,
"precision": "",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "filters_section",
"fieldtype": "Section Break",
"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": "Filters",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "filters_json",
"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": "Filters JSON",
"length": 0,
"no_copy": 0,
"options": "JSON",
"permlevel": 0,
"precision": "",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "chart_options_section",
"fieldtype": "Section Break",
"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": "Chart Options",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "type",
"fieldtype": "Select",
"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": "Type",
"length": 0,
"no_copy": 0,
"options": "Line\nBar",
"permlevel": 0,
"precision": "",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "width",
"fieldtype": "Select",
"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": "Width",
"length": 0,
"no_copy": 0,
"options": "Half\nFull",
"permlevel": 0,
"precision": "",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"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": 0,
"in_standard_filter": 0,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"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_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,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_10",
"fieldtype": "Section Break",
"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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "last_synced_on",
"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": "Last Synced On",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-18 11:05:51.316816",
"modified_by": "Administrator",
"module": "Desk",
"name": "Dashboard Chart",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
from frappe.model.document import Document
class DashboardChart(Document):
pass

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
class TestDashboardChart(unittest.TestCase):
pass

View file

@ -0,0 +1,77 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2019-03-12 15:00:57.052684",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "chart",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Chart",
"length": 0,
"no_copy": 0,
"options": "Dashboard Chart",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-03-12 15:01:31.639414",
"modified_by": "Administrator",
"module": "Desk",
"name": "Dashboard Chart Link",
"name_case": "",
"owner": "Administrator",
"permissions": [],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,10 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, 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 DashboardChartLink(Document):
pass

View file

@ -0,0 +1,5 @@
// Copyright (c) 2019, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Dashboard Chart Source', {
});

View file

@ -0,0 +1,166 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:source_name",
"beta": 0,
"creation": "2019-02-06 07:55:29.579840",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "source_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Source Name",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "module",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Module",
"length": 0,
"no_copy": 0,
"options": "Module Def",
"permlevel": 0,
"precision": "",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "config",
"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": "Config",
"length": 0,
"no_copy": 0,
"options": "JSON",
"permlevel": 0,
"precision": "",
"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,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-15 14:57:17.696656",
"modified_by": "Administrator",
"module": "Desk",
"name": "Dashboard Chart Source",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,17 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.modules.export_file import export_to_files
class DashboardChartSource(Document):
def validate(self):
if frappe.session.user != "Administrator":
frappe.throw(_("Only Administrator is allowed to create Dashboard Chart Sources"))
def on_update(self):
export_to_files(record_list=[[self.doctype, self.name]], record_module=self.module, create_init=True)

View file

@ -0,0 +1,9 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
import unittest
class TestDashboardChartSource(unittest.TestCase):
pass

View file

@ -26,8 +26,27 @@ def set_list_settings(doctype, values):
doc.save()
@frappe.whitelist()
def get_user_assignments_and_count():
user_list = frappe.get_list("User", filters={"user_type": "System User"})
assignment_data = sorted([{"count":frappe.db.count('ToDo', filters = {'reference_type': 'Issue', 'owner': user['name'], 'status': 'Open'}),
"name": user['name']} for user in user_list], key=lambda k: k['count'], reverse = True)
return assignment_data
def get_user_assignments_and_count(doctype, current_filters):
subquery_condition = ''
if current_filters:
# get the subquery
subquery = frappe.get_all(doctype,
filters=current_filters, return_query = True)
subquery_condition = ' and `tabToDo`.reference_name in ({subquery})'.format(subquery = subquery)
todo_list = frappe.db.sql("""select `tabToDo`.owner as name, count(*) as count
from
`tabToDo`, `tabUser`
where
`tabToDo`.status='Open' and
`tabToDo`.owner = `tabUser`.name and
`tabUser`.user_type = 'System User'
{subquery_condition}
group by
`tabToDo`.owner
order by
count desc
limit 50""".format(subquery_condition = subquery_condition), as_dict=True)
return todo_list

View file

@ -275,14 +275,14 @@ def build_xlsx_data(columns, data, visible_idx):
if i in visible_idx:
row_data = []
if isinstance(row, list):
row_data = row
elif isinstance(row, dict) and row:
if isinstance(row, dict) and row:
for idx in range(len(data.columns)):
label = columns[idx]["label"]
fieldname = columns[idx]["fieldname"]
row_data.append(row.get(fieldname, row.get(label, "")))
else:
row_data = row
result.append(row_data)

View file

@ -2691,8 +2691,11 @@
},
"Yemen": {
"code": "ye",
"currency": "YER",
"currency_fraction": "Fils",
"currency_fraction_units": 100,
"smallest_currency_fraction_value": 0.01,
"currency_name": "Yemeni Rial",
"currency_symbol": "\ufdfc",
"number_format": "#,###.##",
"timezones": [

View file

@ -117,7 +117,8 @@ doc_events = {
"frappe.desk.notifications.clear_doctype_notifications",
"frappe.core.doctype.activity_log.feed.update_feed",
"frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions",
"frappe.automation.doctype.assignment_rule.assignment_rule.apply"
"frappe.automation.doctype.assignment_rule.assignment_rule.apply",
"frappe.social.doctype.energy_point_rule.energy_point_rule.process_energy_points"
],
"after_rename": "frappe.desk.notifications.clear_doctype_notifications",
"on_cancel": [
@ -129,13 +130,12 @@ doc_events = {
"frappe.workflow.doctype.workflow_action.workflow_action.process_workflow_actions"
],
"on_change": [
"frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_request",
"frappe.core.doctype.feedback_trigger.feedback_trigger.trigger_feedback_request"
]
},
"Email Group Member": {
"validate": "frappe.email.doctype.email_group.email_group.restrict_email_group"
},
}
}
scheduler_events = {
@ -174,8 +174,8 @@ scheduler_events = {
"frappe.core.doctype.feedback_request.feedback_request.delete_feedback_request",
"frappe.core.doctype.activity_log.activity_log.clear_authentication_logs",
"frappe.website.doctype.personal_data_deletion_request.personal_data_deletion_request.remove_unverified_record",
"frappe.desk.form.document_follow.send_daily_updates"
"frappe.desk.form.document_follow.send_daily_updates",
"frappe.social.doctype.energy_point_settings.energy_point_settings.allocate_review_points"
],
"daily_long": [
"frappe.integrations.doctype.dropbox_settings.dropbox_settings.take_backups_daily",

View file

@ -22,11 +22,11 @@ def create_gsuite_doc(doctype, docname, gs_template=None):
json_data = doc.as_dict()
filename = templ.document_name.format(**json_data)
r = run_gsuite_script('new', filename, templ.template_id, templ.destination_id, json_data)
response = run_gsuite_script('new', filename, templ.template_id, templ.destination_id, json_data)
_file = frappe.get_doc({
"doctype": "File",
"file_url": r['url'],
"file_url": response['url'],
"file_name": filename,
"attached_to_doctype": doctype,
"attached_to_name": docname,
@ -36,15 +36,15 @@ def create_gsuite_doc(doctype, docname, gs_template=None):
comment = frappe.get_doc(doctype, docname).add_comment("Attachment",
_("added {0}").format("<a href='{file_url}' target='_blank'>{file_name}</a>{icon}".format(**{
"icon": ' <i class="fa fa-lock text-warning"></i>' if filedata.is_private else "",
"file_url": filedata.file_url.replace("#", "%23") if filedata.file_name else filedata.file_url,
"file_name": filedata.file_name or filedata.file_url
"icon": ' <i class="fa fa-lock text-warning"></i>' if _file.is_private else "",
"file_url": _file.file_url.replace("#", "%23") if _file.file_name else _file.file_url,
"file_name": _file.file_name or _file.file_url
})))
return {
"name": filedata.name,
"file_name": filedata.file_name,
"file_url": filedata.file_url,
"is_private": filedata.is_private,
"name": _file.name,
"file_name": _file.file_name,
"file_url": _file.file_url,
"is_private": _file.is_private,
"comment": comment.as_dict() if comment else {}
}

View file

@ -27,6 +27,7 @@ data_fieldtypes = (
'Dynamic Link',
'Password',
'Select',
'Rating',
'Read Only',
'Attach',
'Attach Image',

View file

@ -60,15 +60,24 @@ def set_user_and_static_default_values(doc):
allowed_records = get_allowed_docs_for_doctype(doctype_user_permissions, df.parent)
user_default_value = get_user_default_value(df, defaults, doctype_user_permissions, allowed_records)
if user_default_value is not None:
doc.set(df.fieldname, user_default_value)
if user_default_value != None:
# do not set default if the field on which current field is dependent is not set
if is_dependent_field_set(df.depends_on, doc):
doc.set(df.fieldname, user_default_value)
else:
if df.fieldname != doc.meta.title_field:
static_default_value = get_static_default_value(df, doctype_user_permissions, allowed_records)
if static_default_value is not None:
if static_default_value != None and is_dependent_field_set(df.depends_on, doc):
doc.set(df.fieldname, static_default_value)
def is_dependent_field_set(fieldname, doc):
value_dict = doc.as_dict()
if not fieldname: return True
# to check if fieldname passed is valid
if fieldname not in value_dict: return True
return value_dict[fieldname]
def get_user_default_value(df, defaults, doctype_user_permissions, allowed_records):
# don't set defaults for "User" link field using User Permissions!
if df.fieldtype == "Link" and df.options != "User":

View file

@ -36,7 +36,7 @@ class DatabaseQuery(object):
ignore_permissions=False, user=None, with_comment_count=False,
join='left join', distinct=False, start=None, page_length=None, limit=None,
ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False,
update=None, add_total_row=None, user_settings=None, reference_doctype=None):
update=None, add_total_row=None, user_settings=None, reference_doctype=None, return_query=False):
if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user):
frappe.flags.error_message = _('Insufficient Permission for {0}').format(frappe.bold(self.doctype))
raise frappe.PermissionError(self.doctype)
@ -79,6 +79,7 @@ class DatabaseQuery(object):
self.user = user or frappe.session.user
self.update = update
self.user_settings_fields = copy.deepcopy(self.fields)
self.return_query = return_query
# for contextual user permission check
# to determine which user permission is applicable on link field of specific doctype
@ -91,6 +92,8 @@ class DatabaseQuery(object):
result = self.run_custom_query(query)
else:
result = self.build_and_run()
if return_query:
return result
if with_comment_count and not as_list and self.doctype:
self.add_comment_count(result)
@ -115,7 +118,10 @@ class DatabaseQuery(object):
query = """select %(fields)s from %(tables)s %(conditions)s
%(group_by)s %(order_by)s %(limit)s""" % args
return frappe.db.sql(query, as_dict=not self.as_list, debug=self.debug, update=self.update)
if self.return_query:
return query
else:
return frappe.db.sql(query, as_dict=not self.as_list, debug=self.debug, update=self.update)
def prepare_args(self):
self.parse_args()

View file

@ -45,6 +45,7 @@ type_map = {
'Dynamic Link': ('varchar', varchar_len),
'Password': ('varchar', varchar_len),
'Select': ('varchar', varchar_len),
'Rating': ('int', '1'),
'Read Only': ('varchar', varchar_len),
'Attach': ('text', ''),
'Attach Image': ('text', ''),

View file

@ -213,12 +213,12 @@ class Document(BaseDocument):
self.set_docstatus()
self.check_if_latest()
self.run_method("before_insert")
self._validate_links()
self.set_new_name()
self.set_parent_in_children()
self.validate_higher_perm_levels()
self.flags.in_insert = True
self._validate_links()
self.run_before_save_methods()
self._validate()
self.set_docstatus()
@ -1183,6 +1183,12 @@ class Document(BaseDocument):
self.set("__onload", frappe._dict())
self.get("__onload")[key] = value
def get_onload(self, key=None):
if not key:
return self.get("__onload", frappe._dict())
return self.get('__onload')[key]
def update_timeline_doc(self):
if frappe.flags.in_install or not self.meta.get("timeline_field"):
return

View file

@ -88,21 +88,21 @@ def set_name_by_naming_series(doc):
def make_autoname(key='', doctype='', doc=''):
"""
Creates an autoname from the given key:
Creates an autoname from the given key:
**Autoname rules:**
**Autoname rules:**
* The key is separated by '.'
* '####' represents a series. The string before this part becomes the prefix:
Example: ABC.#### creates a series ABC0001, ABC0002 etc
* 'MM' represents the current month
* 'YY' and 'YYYY' represent the current year
* The key is separated by '.'
* '####' represents a series. The string before this part becomes the prefix:
Example: ABC.#### creates a series ABC0001, ABC0002 etc
* 'MM' represents the current month
* 'YY' and 'YYYY' represent the current year
*Example:*
* DE/./.YY./.MM./.##### will create a series like
DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series
* DE/./.YY./.MM./.##### will create a series like
DE/09/01/0001 where 09 is the year, 01 is the month and 0001 is the series
"""
if key == "hash":
return frappe.generate_hash(doctype, 10)
@ -121,7 +121,6 @@ def parse_naming_series(parts, doctype='', doc=''):
n = ''
if isinstance(parts, string_types):
parts = parts.split('.')
series_set = False
today = now_datetime()
for e in parts:
@ -129,7 +128,7 @@ def parse_naming_series(parts, doctype='', doc=''):
if e.startswith('#'):
if not series_set:
digits = len(e)
part = getseries(n, digits, doctype)
part = getseries(n, digits)
series_set = True
elif e == 'YY':
part = today.strftime('%y')
@ -141,6 +140,9 @@ def parse_naming_series(parts, doctype='', doc=''):
part = today.strftime('%Y')
elif e == 'FY':
part = frappe.defaults.get_user_default("fiscal_year")
elif e.startswith('{') and doc:
e = e.replace('{', '').replace('}', '')
part = doc.get(e)
elif doc and doc.get(e):
part = doc.get(e)
else:
@ -152,7 +154,7 @@ def parse_naming_series(parts, doctype='', doc=''):
return n
def getseries(key, digits, doctype=''):
def getseries(key, digits):
# series created ?
current = frappe.db.sql("SELECT `current` FROM `tabSeries` WHERE `name`=%s FOR UPDATE", (key,))
if current and current[0][0] is not None:

View file

@ -68,7 +68,7 @@ def get_doc_files(files, start_path, force=0, sync_everything = False, verbose=F
"""walk and sync all doctypes and pages"""
# load in sequence - warning for devs
document_types = ['doctype', 'page', 'report', 'print_format',
document_types = ['doctype', 'page', 'report', 'dashboard_chart_source', 'print_format',
'website_theme', 'web_form', 'notification', 'print_style',
'data_migration_mapping', 'data_migration_plan']
for doctype in document_types:

View file

@ -11,4 +11,4 @@ Contacts
Data Migration
Chat
Social
Automation
Automation

View file

@ -6,12 +6,11 @@
"public/less/avatar.less",
"node_modules/highlight.js/styles/zenburn.css",
"public/less/form.less",
"public/less/controls.less",
"public/less/chat.less",
"public/less/form_grid.less",
"node_modules/frappe-datatable/dist/frappe-datatable.css"
],
"css/frappe-web-b4.css": [
"public/css/font-awesome.css",
"public/less/indicator.less",
"public/scss/website.scss"
],
@ -94,7 +93,8 @@
"public/js/frappe/form/controls/multiselect.js",
"public/js/frappe/form/controls/multicheck.js",
"public/js/frappe/form/controls/table_multiselect.js",
"public/js/frappe/form/controls/multiselect_pills.js"
"public/js/frappe/form/controls/multiselect_pills.js",
"public/js/frappe/form/controls/rating.js"
],
"js/dialog.min.js": [
"public/js/frappe/dom.js",
@ -265,8 +265,8 @@
"public/less/form_grid.less"
],
"js/form.min.js": [
"public/js/frappe/form/templates/print_layout.html",
"public/js/frappe/form/document_follow.js",
"public/js/frappe/form/templates/print_layout.html",
"public/js/frappe/form/templates/users_in_sidebar.html",
"public/js/frappe/form/templates/set_sharing.html",
"public/js/frappe/form/templates/form_sidebar.html",
@ -287,6 +287,7 @@
"public/js/frappe/form/form_sidebar.js",
"public/js/frappe/form/user_image.js",
"public/js/frappe/form/share.js",
"public/js/frappe/form/review.js",
"public/js/frappe/form/form_viewers.js",
"public/js/frappe/form/footer/attachment.html",
"public/js/frappe/form/footer/form_footer.html",
@ -375,7 +376,8 @@
"public/less/quill.less",
"public/less/datepicker.less",
"public/less/awesomplete.less",
"public/less/form_grid.less"
"public/less/form_grid.less",
"public/less/controls.less"
],
"js/print_format_v3.min.js": [
"public/js/legacy/layout.js",

View file

@ -45,6 +45,9 @@ frappe.Application = Class.extend({
this.make_nav_bar();
this.set_favicon();
this.setup_analytics();
this.setup_energy_point_listeners();
frappe.ui.keys.setup();
this.set_rtl();
@ -547,6 +550,12 @@ frappe.Application = Class.extend({
frappe.show_alert(message);
}
});
},
setup_energy_point_listeners() {
frappe.realtime.on('energy_point_alert', (message) => {
frappe.show_alert(message);
});
}
});

View file

@ -178,4 +178,4 @@ frappe.ui.form.ControlInput = frappe.ui.form.Control.extend({
$(this.disp_area).toggleClass("bold", !!(this.df.bold || this.df.reqd));
}
}
});
});

View file

@ -32,7 +32,8 @@ frappe.ui.form.ControlCode = frappe.ui.form.ControlText.extend({
'HTML': 'ace/mode/html',
'CSS': 'ace/mode/css',
'Markdown': 'ace/mode/markdown',
'SCSS': 'ace/mode/scss'
'SCSS': 'ace/mode/scss',
'JSON': 'ace/mode/json'
};
const language = this.df.options;

View file

@ -0,0 +1,62 @@
frappe.ui.form.ControlRating = frappe.ui.form.ControlInt.extend({
make_input() {
this._super();
const star_template = `
<div class="rating">
<i class="fa fa-fw fa-star" data-idx=1></i>
<i class="fa fa-fw fa-star" data-idx=2></i>
<i class="fa fa-fw fa-star" data-idx=3></i>
<i class="fa fa-fw fa-star" data-idx=4></i>
<i class="fa fa-fw fa-star" data-idx=5></i>
</div>
`;
$(this.input_area).html(star_template);
$(this.input_area).find('i').hover((ev) => {
const el = $(ev.currentTarget);
let star_value = el.data('idx');
el.parent().children('i.fa').each( function(e){
if (e < star_value) {
$(this).addClass('star-hover');
} else {
$(this).removeClass('star-hover');
}
});
}, (ev) => {
const el = $(ev.currentTarget);
el.parent().children('i.fa').each( function() {
$(this).removeClass('star-hover');
});
});
$(this.input_area).find('i').click((ev) => {
const el = $(ev.currentTarget);
let star_value = el.data('idx');
el.parent().children('i.fa').each( function(e) {
if (e < star_value){
$(this).addClass('star-click');
} else {
$(this).removeClass('star-click');
}
});
this.validate_and_set_in_model(star_value, ev);
if (this.doctype && this.docname) {
this.set_input(star_value);
}
});
},
get_value() {
return cint(this.value);
},
set_formatted_input(value) {
let el = $(this.input_area).find('i');
el.children('i.fa').prevObject.each( function(e) {
if (e < value) {
$(this).addClass('star-click');
} else {
$(this).removeClass('star-click');
}
});
}
});

View file

@ -20,6 +20,8 @@ frappe.ui.form.Sidebar = Class.extend({
this.make_attachments();
this.make_shared();
this.make_viewers();
this.make_review();
this.make_tags();
this.make_like();
this.make_follow();
@ -155,6 +157,7 @@ frappe.ui.form.Sidebar = Class.extend({
refresh_image: function() {
},
setup_ratings: function() {
var _ratings = this.frm.get_docinfo().rating || 0;
@ -164,4 +167,11 @@ frappe.ui.form.Sidebar = Class.extend({
this.ratings.find(".rating-icons").html(rating_icons);
}
},
make_review: function() {
this.frm.reviews = new frappe.ui.form.Review({
parent: this.sidebar.find(".form-attachments"),
frm: this.frm
});
}
});

View file

@ -52,6 +52,13 @@ frappe.form.formatters = {
Percent: function(value, docfield, options) {
return frappe.form.formatters._right(flt(value, 2) + "%", options)
},
Rating: function(value) {
return `<span class="rating">
${Array.from(new Array(5)).map((_, i) =>
`<i class="fa fa-fw fa-star ${i < (value || 0) ? "star-click": "" } star-icon" data-idx="${(i+1)}"></i>`
).join('')}
</span>`;
},
Currency: function (value, docfield, options, doc) {
var currency = frappe.meta.get_field_currency(docfield, doc);
var precision = docfield.precision || cint(frappe.boot.sysdefaults.currency_precision) || 2;

View file

@ -0,0 +1,190 @@
// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors
// MIT License. See license.txt
frappe.provide("frappe.ui.form");
frappe.ui.form.Review = class Review {
constructor({parent, frm}) {
this.parent = parent;
this.frm = frm;
this.fetch_energy_points()
.then(() => {
this.make_review_container();
this.add_review_button();
this.update_reviewers();
});
}
fetch_energy_points() {
return frappe.xcall('frappe.social.doctype.energy_point_log.energy_point_log.get_energy_points', {
user: frappe.session.user
}).then(data => {
this.points = data;
});
}
make_review_container() {
this.$wrapper = this.parent.append(`
<ul class="list-unstyled sidebar-menu">
<li class="divider"></li>
<li class="h6 shared-with-label">${__('Reviews')}</li>
<li class="review-list"></li>
</ul>
`);
this.review_list_wrapper = this.$wrapper.find('.review-list');
}
add_review_button() {
if (!this.points.review_points || !this.get_involved_users().length) return;
this.review_list_wrapper.append(`
<span class="avatar avatar-small avatar-empty btn-add-review" title="${__('Add Review')}">
<i class="octicon octicon-plus text-muted"></i>
</span>
`);
this.review_list_wrapper.find('.btn-add-review').click(() => this.show());
}
get_involved_users() {
const user_fields = this.frm.meta.fields
.filter(d => d.fieldtype === 'Link' && d.options === 'User')
.map(d => d.fieldname);
user_fields.push('owner');
let involved_users = user_fields.map(field => this.frm.doc[field]);
const docinfo = this.frm.get_docinfo();
involved_users = involved_users.concat(
docinfo.communications.map(d => d.sender && d.delivery_status==='sent'),
docinfo.comments.map(d => d.owner),
docinfo.versions.map(d => d.owner),
docinfo.assignments.map(d => d.owner)
);
return involved_users
.uniqBy(u => u)
.filter(user => user !== frappe.session.user)
.filter(Boolean);
}
show() {
const user_options = this.get_involved_users();
const doc_owner = this.frm.doc.owner;
const review_dialog = new frappe.ui.Dialog({
'title': __('Add Review'),
'fields': [{
fieldname: 'to_user',
fieldtype: 'Autocomplete',
label: __('To User'),
options: user_options,
default: user_options.includes(doc_owner) ? doc_owner : user_options[0],
description: __('Only users involved in the document are listed')
}, {
fieldname: 'review_type',
fieldtype: 'Select',
label: __('Action'),
options: [{
'label': __('Appreciate'),
'value': 'Appreciation'
}, {
'label': __('Criticize'),
'value': 'Criticism'
}],
default: 'Appreciation'
}, {
fieldname: 'points',
fieldtype: 'Int',
label: __('Points'),
reqd: 1,
description: __(`Currently you have ${this.points.review_points} review points`)
}, {
fieldtype: 'Small Text',
fieldname: 'reason',
reqd: 1,
label: __('Reason')
}],
primary_action: (values) => {
if (values.points > this.points.review_points) {
return frappe.msgprint(__('You do not have enough points'));
}
frappe.xcall('frappe.social.doctype.energy_point_log.energy_point_log.review', {
doc: {
doctype: this.frm.doc.doctype,
name: this.frm.doc.name,
},
to_user: values.to_user,
points: values.points,
review_type: values.review_type,
reason: values.reason
}).then(() => {
review_dialog.hide();
review_dialog.clear();
this.update_reviewers();
});
},
primary_action_label: __('Submit')
});
review_dialog.show();
}
update_reviewers() {
frappe.xcall('frappe.social.doctype.energy_point_log.energy_point_log.get_reviews', {
'doctype': this.frm.doc.doctype,
'docname': this.frm.doc.name,
}).then(review_logs => {
this.review_list_wrapper.find('.review-pill').remove();
review_logs.forEach(log => {
let review_pill = $(`
<span class="review-pill">
${frappe.avatar(log.owner)}
${frappe.utils.get_points(log.points)}
</span>
`);
this.review_list_wrapper.prepend(review_pill);
this.setup_detail_popover(review_pill, log);
});
});
}
setup_detail_popover(el, data) {
let subject = '';
let fullname = frappe.user.full_name(data.user);
let timestamp = `<span class="text-muted">${frappe.datetime.comment_when(data.creation)}</span>`;
let message_parts = [Math.abs(data.points), fullname, timestamp];
if (data.type === 'Appreciation') {
if (data.points == 1) {
subject = __('{0} appreciation point for {1} {2}', message_parts);
} else {
subject = __('{0} appreciation points for {1} {2}', message_parts);
}
} else {
if (data.points == -1) {
subject = __('{0} criticism point for {1} {2}', message_parts);
} else {
subject = __('{0} criticism points for {1} {2}', message_parts);
}
}
el.popover({
animation: true,
trigger: 'hover',
delay: 500,
placement: 'top',
template:`
<div class="review-popover popover">
<div class="arrow"></div>
<div class="popover-content"></div>
</div>
`,
content: () => {
return `
<div class="text-medium">
<div class="subject">
${subject}
</div>
<div class="body">
<div>${data.reason}</div>
</div>
</div>
`;
},
html: true,
container: 'body'
});
return el;
}
};

View file

@ -45,11 +45,15 @@
</ul>
</div>
</li>
<li class="assigned-to">
<li class="assigned-to" style="display: none">
<a class = "dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
{%= __("Assigned To") %} <span class="caret"></span>
</a>
<ul class="dropdown-menu assigned-dropdown" style="max-height: 300px; overflow-y: auto;">
<li><div class="list-loading text-center assigned-loading text-muted">
{%= (__("Loading") + "..." ) %}
</div>
</li>
</ul>
</li>
{% if(frappe.help.has_help(doctype)) { %}

View file

@ -26,7 +26,6 @@ frappe.views.ListSidebar = class ListSidebar {
this.setup_reports();
this.setup_list_filter();
this.setup_assigned_to();
this.setup_views();
this.setup_kanban_boards();
this.setup_calendar_view();
@ -37,6 +36,15 @@ frappe.views.ListSidebar = class ListSidebar {
if (limits.upgrade_url && limits.expiry && !frappe.flags.upgrade_dismissed) {
this.setup_upgrade_box();
}
if(this.doctype !== 'ToDo') {
$('.assigned-to').show();
}
$('.assigned-to').on('click', () => {
$('.assigned').remove();
this.setup_assigned_to();
});
}
setup_views() {
@ -217,14 +225,18 @@ frappe.views.ListSidebar = class ListSidebar {
}
setup_assigned_to() {
$('.assigned-loading').show();
let dropdown = this.page.sidebar.find('.assigned-dropdown');
if(this.doctype === 'ToDo') {
$('.assigned-to').remove();
}
frappe.call('frappe.desk.listview.get_user_assignments_and_count').then((data) => {
let current_user_count = data.message.find(user => user.name === frappe.session.user).count;
this.get_html_for_assigned(frappe.session.user, current_user_count).appendTo(dropdown);
let user_list = data.message.filter(user => !['Guest', frappe.session.user, 'Administrator'].includes(user.name));
let current_filters = this.list_view.get_filters_for_args();
frappe.call('frappe.desk.listview.get_user_assignments_and_count', {doctype: this.doctype, current_filters: current_filters}).then((data) => {
$('.assigned-loading').hide();
let current_user = data.message.find(user => user.name === frappe.session.user);
if(current_user) {
let current_user_count = current_user.count;
this.get_html_for_assigned(frappe.session.user, current_user_count).appendTo(dropdown);
}
let user_list = data.message.filter(user => !['Guest', frappe.session.user, 'Administrator'].includes(user.name) && user.count!==0 );
user_list.forEach((user) => {
this.get_html_for_assigned(user.name, user.count).appendTo(dropdown);
});

View file

@ -923,7 +923,7 @@ frappe.views.ListView = class ListView extends frappe.views.BaseList {
get_checked_items(only_docnames) {
const docnames = Array.from(this.$checks || [])
.map(check => $(check).data().name);
.map(check => cstr($(check).data().name));
if (only_docnames) return docnames;

View file

@ -9,7 +9,7 @@ frappe.user_info = function(uid) {
fullname: __("Bot"),
image: "/assets/frappe/images/ui/bot.png",
abbr: "B"
}
};
}
if(!(frappe.boot.user_info && frappe.boot.user_info[uid])) {
@ -24,7 +24,7 @@ frappe.user_info = function(uid) {
user_info.color = frappe.get_palette(user_info.fullname);
return user_info;
}
};
frappe.ui.set_user_background = function(src, selector, style) {
if(!selector) selector = "#page-desktop";
@ -47,7 +47,7 @@ frappe.ui.set_user_background = function(src, selector, style) {
background:background,
style: style==="Fill Screen" ? "background-size: cover;" : ""
}));
}
};
frappe.provide('frappe.user');

View file

@ -682,6 +682,11 @@ Object.assign(frappe.utils, {
} else {
return null;
}
},
get_points(points) {
return `<span class='bold' style="color: ${points >= 0 ? '#45A163': '#e42121'}">
${points > 0 ? '+': ''}${points}
</span>`;
}
});

View file

@ -83,11 +83,22 @@ export default {
},
update_primary_action(current_route) {
if (current_route === 'home') {
this.$root.page.set_title(__('Social'));
frappe.breadcrumbs.update();
this.$root.page.set_primary_action(__('Post'), () => {
frappe.social.post_dialog.show();
});
} else {
this.$root.page.clear_primary_action()
frappe.breadcrumbs.add({
type: 'Custom',
label: __('Social Home'),
route: '#social/home'
});
this.$root.page.clear_primary_action();
}
if (current_route === 'users') {
this.$root.page.set_title(__('Leaderboard'));
}
},
get_current_page() {

View file

@ -0,0 +1,91 @@
<template>
<div>
<ul class="log-list">
<li class="history-log" v-for="log in history_logs" :key="log.name">
<span v-html="frappe.utils.get_points(log.points)"></span>
<span v-html="log_body(log)"></span>
<span>&nbsp;-&nbsp;</span>
<span v-html="frappe.datetime.comment_when(log.creation)"></span>
</li>
<li v-if="!history_logs.length" class="history-log">
{{__('No logs found')}}
</li>
</ul>
</div>
</template>
<script>
export default {
props: ['user'],
data() {
return {
history_logs: []
}
},
created() {
frappe.db.get_list('Energy Point Log', {
filters: {
user: this.user,
type: ['!=', 'Review']
},
fields: ['*']
}).then(data => {
this.history_logs = data;
})
},
methods: {
log_body(log) {
const doc_link = frappe.utils.get_form_link(log.reference_doctype, log.reference_name, true)
const owner_name = frappe.user.full_name(log.owner).bold();
if (log.type === 'Appreciation') {
return __('{0} appreciated on {1}', [owner_name, doc_link])
}
if (log.type === 'Criticism') {
return __('{0} criticized on {1}', [owner_name, doc_link])
}
return __('via automatic rule {0} for {1}', [log.rule.bold(), doc_link])
}
}
}
</script>
<style lang="less">
@import "frappe/public/less/common";
.log-list {
padding: 15px;
padding-left: 0px;
position: relative;
}
.log-list:before {
content: " ";
border-left: 1px solid @border-color;
position: absolute;
top: 0px;
bottom: 0px;
left: 30px;
z-index: 0;
}
.history-log {
.text-muted;
.text-medium;
list-style: none;
padding: 10px;
padding-left: 50px;
display: flex;
span:nth-child(1) {
width: 40px;
text-align: right;
margin-right: 10px;
}
position: relative;
}
.history-log:before {
content: " ";
width: 7px;
height: 7px;
background-color: @border-color;
position: absolute;
border-radius: 50%;
left: 27px;
top: 16px;
}
</style>

View file

@ -2,7 +2,7 @@
<div class="flex flex-column">
<a class="route-link"
@click.prevent="go_to_user_list()">
{{ __('All Users') }}
{{ __('Leaderboard') }}
</a>
<div class="links" v-if="frequently_visited_list.length">
<div class="muted-title">

View file

@ -14,14 +14,14 @@
class="option"
:class="{'bold': show_list === 'user_posts'}"
@click="show_list = 'user_posts'">
<span>Posts</span>
<span>{{__('Posts')}}</span>
<span>({{ user_posts_count }})</span>
</div>
<div
class="option"
:class="{'bold': show_list === 'liked_posts'}"
@click="show_list = 'liked_posts'">
<span>Likes</span>
<span>{{__('Likes')}}</span>
<span>({{ liked_posts_count }})</span>
</div>
</div>

View file

@ -1,45 +1,99 @@
<template>
<div class="flex justify-center">
<div class="col-md-6">
<div class="flex justify-between padding search-bar">
<div class="flex col-md-6">
<button class="btn" @click="frappe.set_route('social', 'home')"> {{ __('Back') }}</button>
<input type="text" class="form-control" :placeholder="__('Search for a user...')" v-model="search_text">
</div>
</div>
<ul class="list-unstyled user-list">
<li
class="padding cursor-pointer flex user-card"
v-for="user in filtered_users" :key="user.name"
@click="go_to_profile_page(user.name)">
<span v-html="get_avatar(user.name)"></span>
<div class="user-details">
{{ user.fullname }}
<div class="text-muted text-medium" :class="{'italic': !user.bio}">{{ frappe.ellipsis(user.bio, 100) || 'No Bio'}}</div>
<div class="user-list-container">
<ul class="list-unstyled user-list">
<li class="user-card user-list-header text-medium">
<span class="user-details text-muted">
{{ __('User') }}
</span>
<span class="user-points text-muted">
{{ __('Energy Points') }}
</span>
<span class="user-points text-muted">
{{ __('Review Points') }}
</span>
<span class="user-points text-muted">
{{ __('Points Given') }}
</span>
</li>
<li
v-for="user in filtered_users"
:key="user.name"
@click="toggle_log(user.name)">
<div class="user-card">
<div class="user-details flex">
<span v-html="get_avatar(user.name)"></span>
<span>
<a @click="go_to_profile_page(user.name)">{{ user.fullname }}</a>
<div class="text-muted text-medium" :class="{'italic': !user.bio}">
{{ frappe.ellipsis(user.bio, 100) || 'No Bio'}}
</div>
</span>
</div>
</li>
<li class="text-muted" v-if="!filtered_users.length">{{__('No user found')}}</li>
</ul>
</div>
<span class="text-muted text-nowrap user-points">
{{ user.energy_points }}
</span>
<span class="text-muted text-nowrap user-points">
{{ user.review_points }}
</span>
<span class="text-muted text-nowrap user-points">
{{ user.given_points }}
</span>
</div>
<energy-point-history
v-if="show_log_for===user.name"
class="energy-point-history"
:user="user.name">
</energy-point-history>
</li>
<li class="user-card text-muted" v-if="!filtered_users.length">{{__('No user found')}}</li>
</ul>
</div>
</template>
<script>
import EnergyPointHistory from '../components/EnergyPointHistory.vue';
export default {
components: {
EnergyPointHistory
},
data() {
return {
users: [],
search_text: ''
filter_users_by: null,
sort_users_by: 'energy_points',
sort_order: 'desc',
show_log_for: null
}
},
computed: {
filtered_users() {
let filtered = this.users;
if (this.search_text !== '') {
filtered = filtered.filter(user => {
if (user.fullname.toLowerCase().includes(this.search_text.toLowerCase())) {
return true;
let filtered = this.users.slice();
if (this.filter_users_by) {
filtered = filtered.filter(user =>
user.fullname.toLowerCase().includes(this.filter_users_by.toLowerCase())
)
}
if (this.sort_users_by) {
filtered.sort((a, b) => {
const value_a = a[this.sort_users_by];
const value_b = b[this.sort_users_by];
let return_value = 0;
if (value_a > value_b) {
return_value = 1;
}
})
if (value_a < value_b) {
return_value = -1;
}
if (this.sort_order === 'desc') {
return_value = -return_value
}
return return_value
});
}
return filtered;
}
@ -50,6 +104,10 @@ export default {
// delete standard users from the list
standard_users.forEach(user => delete this.users[user]);
this.users = Object.values(this.users);
this.fetch_users_energy_points_and_update_users();
frappe.realtime.on('update_points', () => {
this.fetch_users_energy_points_and_update_users();
});
},
methods: {
get_avatar(user) {
@ -57,29 +115,60 @@ export default {
},
go_to_profile_page(user) {
frappe.set_route('social', 'profile', user)
},
fetch_users_energy_points_and_update_users() {
frappe.xcall(
'frappe.social.doctype.energy_point_log.energy_point_log.get_user_energy_and_review_points'
).then(data => {
let users = this.users.slice();
this.users = users.map(user => {
const points = data[user.name] || {};
user.energy_points = points.energy_points || 0;
user.review_points = points.review_points || 0;
user.given_points = points.given_points || 0;
return user;
});
});
},
toggle_log(user) {
if (this.show_log_for === user) {
this.show_log_for = null
} else {
this.show_log_for = user
}
}
}
}
</script>
<style lang="less" scoped>
@import "frappe/public/less/variables";
.user-list {
// similar to search bar height
margin-top: 75px;
border-left: 1px solid @border-color;
border-right: 1px solid @border-color;
.user-card {
&:hover {
border: 1px solid #d1d8dd;
}
border-radius: 5px;
display: flex;
cursor: pointer;
padding: 12px 15px;
border-bottom: 1px solid @border-color;
.user-details {
margin-left: 10px;
flex: 1;
.italic {
font-style: italic;
}
}
}
}
.user-points {
flex: 0 0 20%;
text-align: right;
align-self: center;
}
.user-list-header {
background-color: @light-bg;
}
.search-bar {
position: fixed;
position: sticky;
top: 0;
background: white;
height: 75px;
text-align: center;
@ -89,6 +178,12 @@ export default {
width: 100%;
left: 0;
}
.energy-point-history {
border-bottom: 1px solid @border-color;
max-height: 300px;
overflow: scroll;
background-color: @light-bg;
}
</style>

View file

@ -238,7 +238,7 @@ frappe.ui.Slides = class Slides {
.appendTo(this.container);
this.$body = $(`<div>`).addClass(`slide-container`)
.appendTo(this.container);
this.$footer = $(`<div>`).addClass(`footer`)
this.$footer = $(`<div>`).addClass(`slide-footer`)
.appendTo(this.container);
this.render_progress_dots();

View file

@ -20,15 +20,15 @@
<a class="dropdown-toggle" data-toggle="dropdown" href="#"
onclick="return false;">
{{ avatar }}
<span class="ellipsis toolbar-user-fullname hidden-xs hidden-sm">
<span class="ellipsis toolbar-user-fullname hidden-xs hidden-sm">
{%= frappe.user.full_name() %}</span>
<b class="caret hidden-xs hidden-sm"></b></a>
<b class="caret hidden-xs hidden-sm"></b></a>
<ul class="dropdown-menu" id="toolbar-user" role="menu">
<li><a href="#Form/User/{%= encodeURIComponent(frappe.session.user) %}">
{%= __("My Settings") %}</a></li>
{%= __("My Settings") %}</a></li>
<li class="navbar-reload">
<a href="#" onclick="return frappe.ui.toolbar.clear_cache();">
{%= __("Reload") %}</a></li>
{%= __("Reload") %}</a></li>
<li><a href="/index" target="_blank" rel="noopener noreferrer">
{%= __("View Website") %}</a></li>
<li><a href="#background_jobs">
@ -86,13 +86,13 @@
</div>
<div class="hidden-xs">
<form class="navbar-form navbar-right" role="search" onsubmit="return false;">
<div class="form-group form-group-sm ui-front">
<input id="navbar-search" type="text" class="form-control"
<form class="navbar-form navbar-right" role="search" onsubmit="return false;">
<div class="form-group form-group-sm ui-front">
<input id="navbar-search" type="text" class="form-control"
placeholder="{%= __("Search or type a command") %} {%= __("(Ctrl + G)") %}" aria-haspopup="true">
<span class="octicon octicon-search navbar-search-icon"></span>
</div>
</form>
</div>
</form>
</div>
</div>
</div>

View file

@ -859,13 +859,18 @@ frappe.views.ReportView = class ReportView extends frappe.views.ListView {
align,
compareValue: compareFn,
format: (value, row, column, data) => {
const d = row.reduce((acc, curr) => {
if (!curr.column.docfield) return acc;
acc[curr.column.docfield.fieldname] = curr.content;
return acc;
}, {});
let doc = null;
if (Array.isArray(row)) {
doc = row.reduce((acc, curr) => {
if (!curr.column.docfield) return acc;
acc[curr.column.docfield.fieldname] = curr.content;
return acc;
}, {});
} else {
doc = row;
}
return frappe.format(value, column.docfield, { always_show_decimals: true }, d);
return frappe.format(value, column.docfield, { always_show_decimals: true }, doc);
}
};
}

View file

@ -69,7 +69,7 @@
height: 108px;
.standard-image {
font-size: 72px;
font-size: 36px;
}
}

View file

@ -145,7 +145,7 @@ a.badge-hover& {
max-width: 400px;
.modal-content {
min-height: 140px;
min-height: 110px;
}
}
@ -317,6 +317,10 @@ a.no-decoration& {
font-size: @text-small;
}
.text-large {
font-size: @text-large;
}
.disable-click {
pointer-events: none;
}

View file

@ -82,3 +82,15 @@
margin-right: 5px;
margin-bottom: 5px;
}
.rating {
i {
color: @text-extra-muted;
}
.star-hover {
color: @text-muted;
}
.star-click {
color: @text-light;
}
}

View file

@ -956,8 +956,8 @@ input[type="checkbox"] {
.btn-primary {
font-weight: bold;
}
.footer {
margin: 15px 0;
.slide-footer {
margin: 15px 0px;
padding: 0px 7px;
.btn:not(:last-child) {
@ -1049,3 +1049,12 @@ img.img-loading:after {
font-family: 'Octicons';
content: "\f00b";
}
// popover
.popover {
border-radius: 4px;
}
.popover-content {
padding: 12px;
}

View file

@ -72,3 +72,7 @@
flex-shrink: 0;
justify-content: center;
}
.fill-width {
flex: 1
}

View file

@ -203,7 +203,7 @@ body[data-route^="Module"] .main-menu {
}
}
.form-sidebar .form-shared .share-doc-btn& {
.form-sidebar .form-shared .share-doc-btn, .btn-add-review {
&:hover,
&:focus,
&:active {
@ -376,3 +376,56 @@ body[data-route^="Module"] .main-menu {
.assignment-row {
margin-bottom: 5px;
}
.review-list {
display: flex;
flex-wrap: wrap;
.btn-add-review {
cursor: pointer;
.octicon {
position: relative;
left: 4.5px;
}
}
.review-pill {
width: 50px;
border: 1px solid @border-color;
border-radius: 4px;
display: flex;
margin: 0 10px 10px 0;
.avatar {
padding: 0px;
margin: 0px;
.avatar-frame {
border-radius: 3px 0 0 3px;
}
}
.standard-image {
border-left: 0;
}
span:nth-child(2) {
flex-basis: 50%;
text-align: center;
align-self: center;
font-size: @text-small;
}
cursor: pointer;
}
}
.review-popover {
max-width: 150px;
.popover-content {
padding: 0;
}
.subject {
border-bottom: 1px solid @border-color;
}
.subject, .body {
padding: 12px;
}
}

View file

@ -152,4 +152,4 @@ body[data-route*="social/profile"] {
.page-head {
display: none;
}
}
}

View file

@ -15,6 +15,7 @@
@light-yellow: #fffce7;
@extra-light-yellow: #fffdf4;
@text-extra-muted: @border-color;
@text-large: 16px;
@text-regular: 14px;
@text-medium: 12px;
@text-small: 10px;

View file

@ -66,6 +66,10 @@ img {
text-decoration: underline;
}
.text-extra-muted {
color: #d1d8dd !important;
}
.web-footer {
padding: 5rem 0;
min-height: 140px;
@ -86,4 +90,4 @@ img {
.indicator {
font-size: inherit;
}
}

View file

@ -0,0 +1,8 @@
// Copyright (c) 2018, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Energy Point Log', {
refresh: function() {
}
});

View file

@ -0,0 +1,317 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-06-21 14:58:55.913619",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "User",
"length": 0,
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "type",
"fieldtype": "Select",
"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": "Type",
"length": 0,
"no_copy": 0,
"options": "Auto\nAppreciation\nCriticism\nReview",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "rule",
"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": "Rule",
"length": 0,
"no_copy": 0,
"options": "Energy Point Rule",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 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 Name",
"length": 0,
"no_copy": 0,
"options": "",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "points",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Points",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "reason",
"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": "Reason",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_toolbar": 0,
"idx": 0,
"in_create": 1,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2019-03-30 12:25:27.821811",
"modified_by": "Administrator",
"module": "Social",
"name": "Energy Point Log",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "All",
"set_user_permissions": 0,
"share": 0,
"submit": 0,
"write": 0
}
],
"quick_entry": 0,
"read_only": 1,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "user",
"track_changes": 0,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,143 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
import json
from frappe.model.document import Document
from frappe.utils import cint, get_fullname
class EnergyPointLog(Document):
def validate(self):
if self.type in ['Appreciation', 'Criticism'] and self.user == self.owner:
frappe.throw(_('You cannot give review points to yourself'))
def after_insert(self):
message = get_alert_message(self)
if message:
frappe.publish_realtime('energy_point_alert', message=message, user=self.user)
frappe.cache().hdel('energy_points', self.user)
frappe.publish_realtime('update_points')
def get_alert_message(doc):
message = ''
owner_name = get_fullname(doc.owner)
doc_link = frappe.get_desk_link(doc.reference_doctype, doc.reference_name)
points = frappe.bold(doc.points)
if doc.type == 'Auto':
message=_('You gained {} points').format(points)
elif doc.type == 'Appreciation':
message = _('{} appreciated your work on {} with {} points'.format(
owner_name,
doc_link,
points
))
elif doc.type == 'Criticism':
message = _('{} criticized your work on {} with {} points'.format(
owner_name,
doc_link,
points
))
return message
def create_energy_points_log(ref_doctype, ref_name, doc):
doc = frappe._dict(doc)
log_exists = frappe.db.exists('Energy Point Log', {
'user': doc.user,
'rule': doc.rule,
'reference_doctype': ref_doctype,
'reference_name': ref_name
})
if log_exists:
return
_doc = frappe.new_doc('Energy Point Log')
_doc.reference_doctype = ref_doctype
_doc.reference_name = ref_name
_doc.update(doc)
_doc.insert(ignore_permissions=True)
return _doc
def create_review_points_log(user, points, reason=None, doctype=None, docname=None):
return frappe.get_doc({
'doctype': 'Energy Point Log',
'points': points,
'type': 'Review',
'user': user,
'reason': reason,
'reference_doctype': doctype,
'reference_name': docname
}).insert(ignore_permissions=True)
@frappe.whitelist()
def add_review_points(user, points):
frappe.only_for('System Manager')
create_review_points_log(user, points)
@frappe.whitelist()
def get_energy_points(user):
# points = frappe.cache().hget('energy_points', user,
# lambda: get_user_energy_and_review_points(user))
# TODO: cache properly
points = get_user_energy_and_review_points(user)
return frappe._dict(points.get(user, {}))
@frappe.whitelist()
def get_user_energy_and_review_points(user=None):
if user:
where_user = 'WHERE `user` = %s'
else:
where_user = ''
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,
`user`
FROM `tabEnergy Point Log`
{where_user}
GROUP BY `user`
""".format(where_user=where_user), values=user or (), as_dict=1)
dict_to_return = frappe._dict()
for d in points_list:
dict_to_return[d.pop('user')] = d
return dict_to_return
@frappe.whitelist()
def review(doc, points, to_user, reason, review_type='Appreciation'):
current_review_points = get_energy_points(frappe.session.user).review_points
doc = doc.as_dict() if hasattr(doc, 'as_dict') else frappe._dict(json.loads(doc))
points = abs(cint(points))
if current_review_points < points:
frappe.msgprint(_('You do not have enough review points'))
return
review_doc = create_energy_points_log(doc.doctype, doc.name, {
'type': review_type,
'reason': reason,
'points': points if review_type == 'Appreciation' else -points,
'user': to_user
})
# deduct review points from reviewer
create_review_points_log(
user=frappe.session.user,
points=-points,
reason=reason,
doctype=review_doc.doctype,
docname=review_doc.name
)
@frappe.whitelist()
def get_reviews(doctype, docname):
return frappe.get_all('Energy Point Log', filters={
'reference_doctype': doctype,
'reference_name': docname,
'type': ['in', ('Appreciation', 'Criticism')],
}, fields=['points', 'owner', 'type', 'user', 'reason', 'creation'])

View file

@ -0,0 +1,137 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
from .energy_point_log import get_energy_points as _get_energy_points, create_review_points_log, review
from frappe.utils.testutils import add_custom_field, clear_custom_fields
class TestEnergyPointLog(unittest.TestCase):
def tearDown(self):
frappe.set_user('Administrator')
frappe.db.sql('DELETE FROM `tabEnergy Point Log`')
frappe.db.sql('DELETE FROM `tabEnergy Point Rule`')
def test_user_energy_point(self):
frappe.set_user('test@example.com')
todo_point_rule = create_energy_point_rule_for_todo()
energy_point_of_user = get_points('test@example.com')
created_todo = create_a_todo()
created_todo.status = 'Closed'
created_todo.save()
points_after_closing_todo = get_points('test@example.com')
self.assertEquals(points_after_closing_todo, energy_point_of_user + todo_point_rule.points)
created_todo.save()
points_after_double_save = get_points('test@example.com')
# point should not be awarded more than once for same doc
self.assertEquals(points_after_double_save, energy_point_of_user + todo_point_rule.points)
def test_points_based_on_multiplier_field(self):
frappe.set_user('test@example.com')
add_custom_field('ToDo', 'multiplier', 'Float')
multiplier_value = 0.51
todo_point_rule = create_energy_point_rule_for_todo('multiplier')
energy_point_of_user = get_points('test@example.com')
created_todo = create_a_todo()
points_after_closing_todo = get_points('test@example.com')
created_todo.status = 'Closed'
created_todo.multiplier = multiplier_value
created_todo.save()
points_after_closing_todo = get_points('test@example.com')
self.assertEquals(points_after_closing_todo, energy_point_of_user + round(todo_point_rule.points * multiplier_value))
clear_custom_fields('ToDo')
def test_disabled_energy_points(self):
settings = frappe.get_single('Energy Point Settings')
settings.enabled = 0
settings.save()
frappe.set_user('test@example.com')
create_energy_point_rule_for_todo()
energy_point_of_user = get_points('test@example.com')
created_todo = create_a_todo()
created_todo.status = 'Closed'
created_todo.save()
points_after_closing_todo = get_points('test@example.com')
# no change in points
self.assertEquals(points_after_closing_todo, energy_point_of_user)
settings.enabled = 1
settings.save()
def test_review(self):
created_todo = create_a_todo()
review_points = 20
create_review_points_log('test2@example.com', review_points)
# reviewer
frappe.set_user('test2@example.com')
review_points_before_review = get_points('test2@example.com', 'review_points')
self.assertEquals(review_points_before_review, review_points)
# for appreciation
appreciation_points = 5
energy_points_before_review = get_points('test@example.com')
review(created_todo, appreciation_points, 'test@example.com', 'good job')
energy_points_after_review = get_points('test@example.com')
review_points_after_review = get_points('test2@example.com', 'review_points')
self.assertEquals(energy_points_after_review, energy_points_before_review + appreciation_points)
self.assertEquals(review_points_after_review, review_points_before_review - appreciation_points)
# for criticism
criticism_points = 2
energy_points_before_review = energy_points_after_review
review_points_before_review = review_points_after_review
review(created_todo, criticism_points, 'test@example.com', 'You could have done better.', 'Criticism')
energy_points_after_review = get_points('test@example.com')
review_points_after_review = get_points('test2@example.com', 'review_points')
self.assertEquals(energy_points_after_review, energy_points_before_review - criticism_points)
self.assertEquals(review_points_after_review, review_points_before_review - criticism_points)
def create_energy_point_rule_for_todo(multiplier_field=None):
name = 'ToDo Closed'
point_rule = frappe.db.get_all(
'Energy Point Rule',
{'name': name},
['*'],
limit=1
)
if point_rule: return point_rule[0]
return frappe.get_doc({
'doctype': 'Energy Point Rule',
'rule_name': name,
'points': 5,
'reference_doctype': 'ToDo',
'condition': 'doc.status == "Closed"',
'user_field': 'owner',
'multiplier_field': multiplier_field
}).insert(ignore_permissions=1)
def create_a_todo():
return frappe.get_doc({
'doctype': 'ToDo',
'description': 'Fix a bug',
}).insert()
def get_points(user, point_type='energy_points'):
return _get_energy_points(user).get(point_type) or 0

View file

@ -0,0 +1,30 @@
// Copyright (c) 2018, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Energy Point Rule', {
refresh: function(frm) {
frm.events.set_user_and_multiplier_field_options(frm);
},
reference_doctype(frm) {
frm.events.set_user_and_multiplier_field_options(frm);
},
set_user_and_multiplier_field_options(frm) {
const reference_doctype = frm.doc.reference_doctype;
if (!reference_doctype) return;
frappe.model.with_doctype(reference_doctype, () => {
const map_for_options = df => ({ label: df.label, value: df.fieldname });
const fields = frappe.meta.get_docfields(frm.doc.reference_doctype);
const user_fields = fields.filter(df => (df.fieldtype === 'Link' && df.options === 'User')
|| df.fieldtype === 'Data')
.map(map_for_options)
.concat([{label: __('Owner'), value: 'owner'}]);
const multiplier_fields = fields.filter(df => ['Int', 'Float'].includes(df.fieldtype))
.map(map_for_options);
frm.set_df_property('user_field', 'options', user_fields);
frm.set_df_property('multiplier_field', 'options', multiplier_fields);
});
}
});

View file

@ -0,0 +1,296 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:rule_name",
"beta": 0,
"creation": "2018-06-20 10:08:36.275253",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "enabled",
"fieldtype": "Check",
"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": "Enabled",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "rule_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": "Rule Name",
"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,
"translatable": 0,
"unique": 1
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "If the condition is satisfied user will be rewarded with the points\n",
"fetch_if_empty": 0,
"fieldname": "condition",
"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": "Condition",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "points",
"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": "Points",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The user from this field will be rewarded points",
"fetch_if_empty": 0,
"fieldname": "user_field",
"fieldtype": "Select",
"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": "User Field",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "multiplier_field",
"fieldtype": "Select",
"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": "Multiplier Field",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_toolbar": 1,
"idx": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-27 11:57:27.672792",
"modified_by": "Administrator",
"module": "Social",
"name": "Energy Point Rule",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,54 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2018, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.social.doctype.energy_point_settings.energy_point_settings import is_energy_point_enabled
from frappe.social.doctype.energy_point_log.energy_point_log import create_energy_points_log
class EnergyPointRule(Document):
def apply(self, doc):
if frappe.safe_eval(self.condition, None, {'doc': doc.as_dict()}):
multiplier = 1
if self.multiplier_field:
multiplier = doc.get(self.multiplier_field) or 1
points = round(self.points * multiplier)
reference_doctype = doc.doctype
reference_name = doc.name
user = doc.get(self.user_field)
rule = self.name
# incase of zero as result after roundoff
if not points: return
try:
create_energy_points_log(reference_doctype, reference_name, {
'points': points,
'user': user,
'rule': rule
})
except Exception as e:
frappe.log_error('apply_energy_point', e)
def process_energy_points(doc, state):
if frappe.flags.in_patch or frappe.flags.in_install or not is_energy_point_enabled():
return
# TODO: cache properly
# energy_point_doctypes = frappe.cache().get_value('energy_point_doctypes', get_energy_point_doctypes)
# if doc.doctype in energy_point_doctypes:
rules = frappe.get_all('Energy Point Rule', filters={
'reference_doctype': doc.doctype,
'enabled': 1
})
for d in rules:
frappe.get_doc('Energy Point Rule', d.name).apply(doc)
def get_energy_point_doctypes():
return [
d.reference_doctype for d in frappe.get_all('Energy Point Rule',
['reference_doctype'], {'enabled': 1})
]

View file

@ -0,0 +1,42 @@
// Copyright (c) 2019, Frappe Technologies and contributors
// For license information, please see license.txt
frappe.ui.form.on('Energy Point Settings', {
refresh: function(frm) {
frm.add_custom_button(__('Give Review Points'), show_review_points_dialog);
}
});
function show_review_points_dialog() {
const dialog = new frappe.ui.Dialog({
title: __('Give Review Points'),
fields: [
{
"label" : "User",
"fieldname": "user",
"fieldtype": "Link",
"options": "User",
"reqd": 1
},
{
"label" : "Points",
"fieldname": "points",
"fieldtype": "Int",
"reqd": 1
}
],
primary_action: function(values) {
frappe.xcall("frappe.social.doctype.energy_point_log.energy_point_log.add_review_points", {
user: values.user,
points: values.points,
}).then(() => {
frappe.show_alert({
message: __('Successfully Done'),
indicator: 'green'
});
}).finally(() => dialog.hide());
},
primary_action_label: __('Submit')
});
dialog.show();
}

View file

@ -0,0 +1,229 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2019-03-19 13:17:51.710241",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "enabled",
"fieldtype": "Check",
"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": "Enabled",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "enabled",
"fetch_if_empty": 0,
"fieldname": "section_break_2",
"fieldtype": "Section Break",
"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,
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "review_levels",
"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": "Review Levels",
"length": 0,
"no_copy": 0,
"options": "Review Level",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Weekly",
"fetch_if_empty": 0,
"fieldname": "point_allocation_periodicity",
"fieldtype": "Select",
"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": "Point Allocation Periodicity",
"length": 0,
"no_copy": 0,
"options": "Daily\nWeekly\nMonthly",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "last_point_allocation_date",
"fieldtype": "Date",
"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": "Last Point Allocation Date",
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_toolbar": 1,
"idx": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-26 19:10:14.087840",
"modified_by": "Administrator",
"module": "Social",
"name": "Energy Point Settings",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 0,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "ASC",
"track_changes": 1,
"track_seen": 0,
"track_views": 0
}

View file

@ -0,0 +1,56 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2019, Frappe Technologies and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe.social.doctype.energy_point_log.energy_point_log import create_review_points_log
from frappe.utils import add_to_date, today, getdate
class EnergyPointSettings(Document):
pass
def is_energy_point_enabled():
return frappe.get_cached_value('Energy Point Settings', None, 'enabled')
def allocate_review_points():
settings = frappe.get_single('Energy Point Settings')
if not can_allocate_today(settings.last_point_allocation_date,
settings.point_allocation_periodicity):
return
for level in settings.review_levels:
create_review_points(level)
settings.last_point_allocation_date = today()
settings.save(ignore_permissions=True)
def create_review_points(level):
users = get_users_with_role(level.role)
for user in users:
create_review_points_log(user, level.review_points)
def can_allocate_today(last_date, periodicity):
if not last_date:
return True
days_to_add = {
'Daily': 1,
'Weekly': 7,
'Monthly': 30
}.get(periodicity, 1)
next_allocation_date = add_to_date(last_date, days=days_to_add)
return getdate(next_allocation_date) <= getdate()
def get_users_with_role(role):
return [p[0] for p in frappe.db.sql("""SELECT DISTINCT `tabUser`.`name`
FROM `tabHas Role`, `tabUser`
WHERE `tabHas Role`.`role`=%s
AND `tabUser`.`name`!='Administrator'
AND `tabHas Role`.`parent`=`tabUser`.`name`
AND `tabUser`.`enabled`=1""", role)]

View file

@ -1,343 +1,343 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-09-25 11:39:04.533626",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2018-09-25 11:39:04.533626",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "content",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Content",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "content",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Content",
"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": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "comments",
"fieldtype": "Table",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "comments",
"length": 0,
"no_copy": 0,
"options": "Post Comment",
"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,
"translatable": 0,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "comments",
"fieldtype": "Table",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "comments",
"length": 0,
"no_copy": 0,
"options": "Post Comment",
"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,
"translatable": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "liked_by",
"fieldtype": "Small Text",
"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": "Liked By",
"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,
"translatable": 0,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "liked_by",
"fieldtype": "Small Text",
"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": "Liked By",
"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,
"translatable": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_pinned",
"fieldtype": "Check",
"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": "Is Pinned",
"length": 0,
"no_copy": 0,
"permlevel": 2,
"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,
"translatable": 0,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_pinned",
"fieldtype": "Check",
"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": "Is Pinned",
"length": 0,
"no_copy": 0,
"permlevel": 2,
"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,
"translatable": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_globally_pinned",
"fieldtype": "Check",
"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": "Is Globally Pinned",
"length": 0,
"no_copy": 0,
"permlevel": 1,
"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,
"translatable": 0,
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "is_globally_pinned",
"fieldtype": "Check",
"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": "Is Globally Pinned",
"length": 0,
"no_copy": 0,
"permlevel": 1,
"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,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-11 16:32:20.638805",
"modified_by": "Administrator",
"module": "Social",
"name": "Post",
"name_case": "",
"owner": "Administrator",
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-11 16:32:20.638805",
"modified_by": "Administrator",
"module": "Social",
"name": "Post",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
},
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 1,
"import": 0,
"permlevel": 2,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 1,
"import": 0,
"permlevel": 2,
"print": 1,
"read": 1,
"report": 1,
"role": "System Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
},
},
{
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 2,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 0,
"delete": 0,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 2,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 0
},
},
{
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 1,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 1,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "All",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0,
"track_views": 1
}

View file

@ -102,4 +102,4 @@ def set_seen(post_name):
'reference_doctype': 'Post',
'reference_name': post_name,
'viewed_by': frappe.session.user
}).insert(ignore_permissions=True)
}).insert(ignore_permissions=True)

Some files were not shown because too many files have changed in this diff Show more