diff --git a/frappe/chat/doctype/chat_message/chat_message.py b/frappe/chat/doctype/chat_message/chat_message.py
index 3c58a6c07d..06127b3148 100644
--- a/frappe/chat/doctype/chat_message/chat_message.py
+++ b/frappe/chat/doctype/chat_message/chat_message.py
@@ -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)
diff --git a/frappe/core/doctype/report/test_report.py b/frappe/core/doctype/report/test_report.py
index 1689d99716..5591035c08 100644
--- a/frappe/core/doctype/report/test_report.py
+++ b/frappe/core/doctype/report/test_report.py
@@ -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')
diff --git a/frappe/core/doctype/report/user_activity_report_without_sort.json b/frappe/core/doctype/report/user_activity_report_without_sort.json
new file mode 100644
index 0000000000..bb520a25e2
--- /dev/null
+++ b/frappe/core/doctype/report/user_activity_report_without_sort.json
@@ -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"
+ }
\ No newline at end of file
diff --git a/frappe/desk/notifications.py b/frappe/desk/notifications.py
index fa350d9d8d..b3100a43da 100644
--- a/frappe/desk/notifications.py
+++ b/frappe/desk/notifications.py
@@ -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),
diff --git a/frappe/desk/query_report.py b/frappe/desk/query_report.py
index 9e172754bd..6d43ae5eb7 100644
--- a/frappe/desk/query_report.py
+++ b/frappe/desk/query_report.py
@@ -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)
diff --git a/frappe/hooks.py b/frappe/hooks.py
index bb3458d35a..223819b013 100644
--- a/frappe/hooks.py
+++ b/frappe/hooks.py
@@ -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"
diff --git a/frappe/patches.txt b/frappe/patches.txt
index c057611ca9..1f134ccfa5 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -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
diff --git a/frappe/patches/v11_0/fix_order_by_in_reports_json.py b/frappe/patches/v11_0/fix_order_by_in_reports_json.py
new file mode 100644
index 0000000000..28558ef4fb
--- /dev/null
+++ b/frappe/patches/v11_0/fix_order_by_in_reports_json.py
@@ -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()
diff --git a/frappe/public/js/frappe/chat.js b/frappe/public/js/frappe/chat.js
index ea7bfd3664..0a444b08dc 100644
--- a/frappe/public/js/frappe/chat.js
+++ b/frappe/public/js/frappe/chat.js
@@ -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
+ ]
),
)
)
diff --git a/frappe/public/js/frappe/views/reports/query_report.js b/frappe/public/js/frappe/views/reports/query_report.js
index 26f346618a..3bd8a9b83c 100644
--- a/frappe/public/js/frappe/views/reports/query_report.js
+++ b/frappe/public/js/frappe/views/reports/query_report.js
@@ -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) {
diff --git a/frappe/public/less/chat.less b/frappe/public/less/chat.less
index e9ea77d843..74fd78c01b 100644
--- a/frappe/public/less/chat.less
+++ b/frappe/public/less/chat.less
@@ -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;
-}
\ No newline at end of file
+}
diff --git a/frappe/templates/includes/list/list.js b/frappe/templates/includes/list/list.js
index ee1006326d..6bd3a155f2 100644
--- a/frappe/templates/includes/list/list.js
+++ b/frappe/templates/includes/list/list.js
@@ -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",
diff --git a/frappe/templates/includes/meta_block.html b/frappe/templates/includes/meta_block.html
index 6f8a6a3a73..0ab3c2a4a7 100644
--- a/frappe/templates/includes/meta_block.html
+++ b/frappe/templates/includes/meta_block.html
@@ -1,11 +1,11 @@
{%- if metatags -%}
{%- for name in metatags %}
-
+
{%- endfor -%}
-
-
-
+
+
+
{%- endif -%}
diff --git a/frappe/utils/data.py b/frappe/utils/data.py
index 9a8cc772f1..08d46f84c6 100644
--- a/frappe/utils/data.py
+++ b/frappe/utils/data.py
@@ -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)))
diff --git a/frappe/website/doctype/web_page/templates/web_page.html b/frappe/website/doctype/web_page/templates/web_page.html
index ab0ec87650..0295c9ec1c 100644
--- a/frappe/website/doctype/web_page/templates/web_page.html
+++ b/frappe/website/doctype/web_page/templates/web_page.html
@@ -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 %}
diff --git a/frappe/www/list.py b/frappe/www/list.py
index 159983aa72..e0d8bd895a 100644
--- a/frappe/www/list.py
+++ b/frappe/www/list.py
@@ -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)
diff --git a/package.json b/package.json
index 153ca01ee0..778324f72d 100644
--- a/package.json
+++ b/package.json
@@ -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",
diff --git a/yarn.lock b/yarn.lock
index 3d2cfae642..bd2ffe9214 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -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"