Merge branch 'staging' into develop

This commit is contained in:
Saurabh 2018-12-18 10:20:24 +05:30
commit 2de5dfdbb3
18 changed files with 146 additions and 57 deletions

View file

@ -39,7 +39,7 @@ def get_message_urls(content):
urls.append(text)
return urls
def get_message_mentions(content):
mentions = [ ]
tokens = content.split(' ')
@ -68,12 +68,12 @@ def get_message_meta(content):
meta.content = content
meta.urls = get_message_urls(content)
meta.mentions = get_message_mentions(content)
return meta
def sanitize_message_content(content):
emojis = get_emojis()
tokens = content.split(' ')
for token in tokens:
if token.startswith(':') and token.endswith(':'):
@ -131,7 +131,7 @@ def get_new_chat_message(user, room, content, type = "Content"):
@frappe.whitelist(allow_guest = True)
def send(user, room, content, type = "Content"):
mess = get_new_chat_message(user, room, content, type)
frappe.publish_realtime('frappe.chat.message:create', mess, room = room,
after_commit = True)
@ -144,7 +144,7 @@ def seen(message, user = None):
room = mess.room
resp = dict(message = message, data = dict(seen = json.loads(mess._seen)))
frappe.publish_realtime('frappe.chat.message:update', resp, room = room, after_commit = True)
def history(room, fields = None, limit = 10, start = None, end = None):
@ -159,7 +159,7 @@ def history(room, fields = None, limit = 10, start = None, end = None):
],
order_by = 'creation'
)
if not fields or 'seen' in fields:
for m in mess:
m['seen'] = json.loads(m._seen) if m._seen else [ ]
@ -168,8 +168,26 @@ def history(room, fields = None, limit = 10, start = None, end = None):
for m in mess:
m['content'] = json.loads(m.content) if m.type in ["File"] else m.content
frappe.enqueue('frappe.chat.doctype.chat_message.chat_message.mark_messages_as_seen',
message_names=[m.name for m in mess], user=frappe.session.user)
return mess
def mark_messages_as_seen(message_names, user):
'''
Marks chat messages as seen, updates the _seen for each message
(should be run in background process)
'''
for name in message_names:
seen = frappe.db.get_value('Chat Message', name, '_seen') or '[]'
seen = json.loads(seen)
seen.append(user)
seen = json.dumps(seen)
frappe.db.set_value('Chat Message', name, '_seen', seen, update_modified=False)
frappe.db.commit()
@frappe.whitelist()
def get(name, rooms = None, fields = None):
rooms, fields = safe_json_loads(rooms, fields)

View file

@ -53,3 +53,18 @@ class TestReport(unittest.TestCase):
report = frappe.get_doc('Report', 'Test Report')
self.assertNotEquals(report.is_permitted(), True)
# test for the `_format` method if report data doesn't have sort_by parameter
def test_format_method(self):
if frappe.db.exists('Report', 'User Activity Report Without Sort'):
frappe.delete_doc('Report', 'User Activity Report Without Sort')
with open(os.path.join(os.path.dirname(__file__), 'user_activity_report_without_sort.json'), 'r') as f:
frappe.get_doc(json.loads(f.read())).insert()
report = frappe.get_doc('Report', 'User Activity Report Without Sort')
# this would raise an error without the fix added along with this test case
columns, data = report.get_data()
self.assertEqual(columns[0].get('label'), 'ID')
self.assertEqual(columns[1].get('label'), 'User Type')
self.assertTrue('Administrator' in [d[0] for d in data])
frappe.delete_doc('Report', 'User Activity Report Without Sort')

View file

@ -0,0 +1,17 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"is_standard": "No",
"javascript": null,
"json": "{\"filters\":[],\"columns\":[[\"name\",\"User\"],[\"user_type\",\"User\"],[\"first_name\",\"User\"],[\"last_name\",\"User\"],[\"last_active\",\"User\"],[\"role\",\"Has Role\"]],\"sort_order\":\"desc\",\"sort_by_next\":null,\"sort_order_next\":\"desc\"}",
"modified": "2018-12-17 18:27:07.728890",
"module": "Core",
"name": "User Activity Report Without Sort",
"query": null,
"ref_doctype": "User",
"report_name": "User Activity Report Without Sort",
"report_type": "Report Builder"
}

View file

@ -29,7 +29,7 @@ def get_notifications():
notification_count[name] = count
return {
"open_count_doctype": {},
"open_count_doctype": get_notifications_for_doctypes(config, notification_count),
"open_count_module": get_notifications_for_modules(config, notification_count),
"open_count_other": get_notifications_for_other(config, notification_count),
"targets": get_notifications_for_targets(config, notification_percent),

View file

@ -314,7 +314,7 @@ def add_total_row(result, columns, meta = None):
if fieldtype=="Link" and options == "Currency":
total_row[i] = result[0][i]
total_row[i] = result[0].get(fieldname) if isinstance(result[0], dict) else result[0][i]
for i in has_percent:
total_row[i] = flt(total_row[i]) / len(result)

View file

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

View file

@ -233,3 +233,4 @@ frappe.patches.v11_0.set_allow_self_approval_in_workflow
execute:frappe.db.sql('ALTER table `tabSeries` ADD PRIMARY KEY IF NOT EXISTS (name)')
frappe.patches.v11_0.migrate_report_settings_for_new_listview
frappe.patches.v11_0.delete_all_prepared_reports
frappe.patches.v11_0.fix_order_by_in_reports_json

View file

@ -0,0 +1,22 @@
import frappe, json
def execute():
reports_data = frappe.get_all('Report',
filters={'json': ['not like', '%%%"order_by": "`tab%%%'],
'report_type': 'Report Builder', 'is_standard': 'No'}, fields=['name'])
for d in reports_data:
doc = frappe.get_doc('Report', d.get('name'))
json_data = json.loads(doc.get('json'))
parts = []
if ('order_by' in json_data) and ('.' in json_data.get('order_by')):
parts = json_data.get('order_by').split('.')
sort_by = parts[1].split(' ')
json_data['order_by'] = '`tab{0}`.`{1}`'.format(doc.ref_doctype, sort_by[0])
json_data['order_by'] += ' {0}'.format(sort_by[1]) if len(sort_by) > 1 else ''
doc.json = json.dumps(json_data)
doc.save()

View file

@ -1984,18 +1984,27 @@ class extends Component {
}
}
if ( props.last_message )
let is_unread = false
if ( props.last_message ) {
item.timestamp = frappe.chat.pretty_datetime(props.last_message.creation)
is_unread = !props.last_message.seen.includes(frappe.session.user)
}
return (
h("li", null,
h("a", { class: props.active ? "active": "", onclick: () => props.click(props) },
h("a", { class: props.active ? "active": "", onclick: () => {
props.last_message.seen.push(frappe.session.user)
props.click(props)
} },
h("div", { class: "row" },
h("div", { class: "col-xs-9" },
h(frappe.Chat.Widget.MediaProfile, { ...item })
),
h("div", { class: "col-xs-3 text-right" },
h("div", { class: "text-muted", style: { "font-size": "9px" } }, item.timestamp)
[
h("div", { class: "text-muted", style: { "font-size": "9px" } }, item.timestamp),
is_unread ? h("span", { class: "indicator red" }) : null
]
),
)
)

View file

@ -974,16 +974,16 @@ frappe.views.QueryReport = class QueryReport extends frappe.views.BaseList {
toggle_nothing_to_show(flag) {
let message = __('Nothing to show');
if(this.prepared_report) {
message = __(`This is a background report.
Please set the appropriate filters and then generate a new one.`);
}
let message = this.prepared_report
? __('This is a background report. Please set the appropriate filters and then generate a new one.')
: __('Nothing to show')
this.toggle_message(flag, message);
if(flag){
if (flag && this.prepared_report) {
this.prepared_report_action = "New";
this.add_prepared_report_buttons();
}
this.add_prepared_report_buttons();
}
toggle_message(flag, message) {

View file

@ -2,8 +2,8 @@
// http://codeguide.co - @mdo (Author of Bootstrap)
@import "common.less";
@import "flex.less";
@import {reference} "common.less";
// Typography
@font-weight-bold: 700;
@ -55,7 +55,7 @@
margin: @fab-margin;
width: @fab-size;
height: @fab-size;
&.frappe-fab-lg
{
width: @fab-size-lg;
@ -80,10 +80,10 @@
{
position: fixed;
bottom: 0px;
right: 0px;
right: 0px;
margin: @chat-popper-margin;
z-index: @chat-popper-z-index;
& > .frappe-chat-popper-collapse
{
& > .panel
@ -175,7 +175,7 @@
right: 0px;
overflow: auto;
border-radius: 0px;
.panel-heading
{
border-radius: 0px;
@ -194,19 +194,19 @@
{
font-size: @chat-form-font-size;
}
.dropdown-menu
{
border-radius: @chat-form-menu-border-radius;
}
// Hints
.hint-list.list-group
{
margin: 0px;
max-height: @chat-form-list-group-height;
overflow-y: auto;
.hint-list-item.list-group-item:first-child, .hint-list-item.list-group-item:last-child
{
border-radius: 0px !important;
@ -269,7 +269,7 @@
{
margin-right: @chat-base-spacing;
}
.avatar
{
width: 32px; height: 32px;
@ -285,7 +285,7 @@
.chat-form
{
border-top: @chat-form-border;
.input-group-btn
{
.btn
@ -294,7 +294,7 @@
border-radius: 0px;
}
}
.form-control
{
line-height: 27px; // HACK: Makes input and placeholder centered within textarea. Also takes care of the input-btn
@ -305,7 +305,7 @@
padding-right: 0px;
overflow: hidden;
}
.fa
{
font-size: @chat-base-font-size-lg;
@ -317,7 +317,7 @@
.chat-list
{
height: 100%;
height: 100%;
// background: @chat-list-bg-color;
overflow-y: scroll;
@ -334,22 +334,22 @@
}
.cursor-pointer;
border: none !important;
padding: @chat-list-item-padding;
background: transparent;
.chat-bubble
{
max-width: @chat-bubble-max-width;
display: inline-block;
padding: @chat-bubble-padding;
border-radius: @chat-bubble-border-radius;
-webkit-box-shadow: @chat-bubble-box-shadow;
-moz-box-shadow: @chat-bubble-box-shadow;
box-shadow: @chat-bubble-box-shadow;
@media (max-width : 768px) {
min-width: @chat-bubble-min-width;
}
@ -372,7 +372,7 @@
}
}
}
&.chat-bubble-r
{
text-align: right;
@ -386,33 +386,33 @@
}
}
}
.chat-bubble-author
{
font-size: @chat-bubble-author-font-size;
a
{
.font-bold;
text-decoration: none !important;
}
}
.chat-bubble-content
{
margin-bottom: @chat-bubble-content-margin-bottom;
word-wrap: break-word;
}
.chat-bubble-meta
{
font-size: @chat-bubble-meta-font-size;
& > .chat-bubble-check
{
margin-left: @chat-base-spacing;
i
{
font-size: @chat-bubble-check-font-size;
@ -439,4 +439,4 @@
font-size: 10px;
padding: 5px;
// background-color: white;
}
}

View file

@ -11,6 +11,7 @@ frappe.ready(function() {
pathname: location.pathname,
});
data.web_form_name = frappe.web_form_name;
data.pathname = location.pathname;
btn.prop("disabled", true);
return $.ajax({
url:"/api/method/frappe.www.list.get",

View file

@ -1,11 +1,11 @@
{%- if metatags -%}
{%- for name in metatags %}
<meta {% if name == 'og:title' or name == "og:image" or name == "og:type" or name == "og:description" %} property="{{ name }}" {% else %} name="{{ name }}" {% endif %} content="{{ metatags[name]|striptags|escape }}" data-html-block="meta_block">
<meta {% if name == 'og:title' or name == "og:image" or name == "og:type" or name == "og:description" %} property="{{ name }}" {% else %} name="{{ name }}" {% endif %} content="{{ metatags[name]|striptags|escape }}">
{%- endfor -%}
<meta property="og:url" content="{{ frappe.utils.get_url(path) }}">
<meta itemprop="datePublished" content="{{ frappe.format_date(published_on) }}">
<meta itemprop="author" content="{{ blogger }}">
<meta itemprop="name" content="{{ name }}" data-html-block="meta_block">
<meta itemprop="description" content="{{ description }}" data-html-block="meta_block">
<meta itemprop="image" content="{{ frappe.utils.get_url(metatags["image"]) }}" data-html-block="meta_block">
<meta itemprop="name" content="{{ name }}">
<meta itemprop="description" content="{{ description }}">
<meta itemprop="image" content="{{ frappe.utils.get_url(metatags["image"]) }}">
{%- endif -%}

View file

@ -824,7 +824,8 @@ def get_filter(doctype, f):
if len(f) == 3:
f = (doctype, f[0], f[1], f[2])
elif len(f) > 4:
f = f[0:4]
elif len(f) != 4:
frappe.throw(frappe._("Filter must have 4 values (doctype, fieldname, operator, value): {0}").format(str(f)))

View file

@ -2,6 +2,10 @@
{%- block header -%} {{ header or "" }} {%- endblock -%}
{% block meta_block %}
{% include "templates/includes/meta_block.html" %}
{% endblock %}
{% block hero %}{{ hero or "" }}{% endblock %}
{% block breadcrumbs %}

View file

@ -24,7 +24,7 @@ def get_context(context, **dict_params):
context.update(get(**frappe.local.form_dict))
@frappe.whitelist(allow_guest=True)
def get(doctype, txt=None, limit_start=0, limit=20, **kwargs):
def get(doctype, txt=None, limit_start=0, limit=20, pathname=None, **kwargs):
"""Returns processed HTML page for a standard listing."""
limit_start = cint(limit_start)
raw_result = get_list_data(doctype, txt, limit_start, limit=limit + 1, **kwargs)
@ -54,7 +54,8 @@ def get(doctype, txt=None, limit_start=0, limit=20, **kwargs):
new_context.update(new_context.doc.as_dict())
if not frappe.flags.in_test:
new_context["pathname"] = frappe.local.request.path.strip("/ ")
pathname = pathname or frappe.local.request.path
new_context["pathname"] = pathname.strip("/ ")
new_context.update(list_context)
set_route(new_context)
rendered_row = frappe.render_template(row_template, new_context, is_path=True)

View file

@ -22,7 +22,7 @@
"awesomplete": "^1.1.2",
"cookie": "^0.3.1",
"express": "^4.16.2",
"frappe-datatable": "^1.7.0",
"frappe-datatable": "^1.7.1",
"frappe-gantt": "^0.1.0",
"fuse.js": "^3.2.0",
"highlight.js": "^9.12.0",

View file

@ -1517,10 +1517,10 @@ forwarded@~0.1.2:
resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.1.2.tgz#98c23dab1175657b8c0573e8ceccd91b0ff18c84"
integrity sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=
frappe-datatable@^1.7.0:
version "1.7.0"
resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.7.0.tgz#6cc950a69bdfd514fb61ab25b85fa68c3b33aee0"
integrity sha512-CcCIf36eJMqzobc4rMT75zCCFKjfghJAR9rXiAP6h2uWdDKhb0U+rDjiFPweS54lv19tpTYF7Q/1SSx0Ih+Mfg==
frappe-datatable@^1.7.1:
version "1.7.1"
resolved "https://registry.yarnpkg.com/frappe-datatable/-/frappe-datatable-1.7.1.tgz#5badb1138c7019e9dce50d6cbbd81c2fdc97e391"
integrity sha512-ePD4IDaLDCZCrchqT+TsPquOdnm72oYxh/KYMPfYBxUGSzWUoi2uRgzQD4MBR+uq1WxJhFQNoXe/fD3yOqNNUQ==
dependencies:
hyperlist "^1.0.0-beta"
lodash "^4.17.5"