@@ -13,7 +13,7 @@ import os, sys, importlib, inspect, json | |||||
from .exceptions import * | from .exceptions import * | ||||
from .utils.jinja import get_jenv, get_template, render_template | from .utils.jinja import get_jenv, get_template, render_template | ||||
__version__ = '8.0.4' | |||||
__version__ = '8.0.5' | |||||
__title__ = "Frappe Framework" | __title__ = "Frappe Framework" | ||||
local = Local() | local = Local() | ||||
@@ -3,17 +3,6 @@ from frappe import _ | |||||
def get_data(): | def get_data(): | ||||
return [ | return [ | ||||
{ | |||||
"module_name": 'Core', | |||||
"type": 'list', | |||||
"label": _('Email Inbox'), | |||||
"_label": _('Email Inbox'), | |||||
"_id": 'Email Inbox', | |||||
"_doctype": 'Communication', | |||||
"icon": 'fa fa-envelope-o', | |||||
"color": '#589494', | |||||
"link": 'List/Communication/Inbox' | |||||
}, | |||||
{ | { | ||||
"module_name": "Desk", | "module_name": "Desk", | ||||
"label": _("Tools"), | "label": _("Tools"), | ||||
@@ -54,9 +43,21 @@ def get_data(): | |||||
"type": "module", | "type": "module", | ||||
"hidden": 1 | "hidden": 1 | ||||
}, | }, | ||||
{ | |||||
"module_name": 'Email Inbox', | |||||
"type": 'list', | |||||
"label": 'Email Inbox', | |||||
"_label": _('Email Inbox'), | |||||
"_id": 'Email Inbox', | |||||
"_doctype": 'Communication', | |||||
"icon": 'fa fa-envelope-o', | |||||
"color": '#589494', | |||||
"link": 'List/Communication/Inbox' | |||||
}, | |||||
{ | { | ||||
"module_name": "Core", | "module_name": "Core", | ||||
"label": _("Developer"), | |||||
"label": "Developer", | |||||
"_label": _("Developer"), | |||||
"color": "#589494", | "color": "#589494", | ||||
"icon": "octicon octicon-circuit-board", | "icon": "octicon octicon-circuit-board", | ||||
"type": "module", | "type": "module", | ||||
@@ -2,7 +2,8 @@ frappe.listview_settings['Communication'] = { | |||||
add_fields: [ | add_fields: [ | ||||
"sent_or_received","recipients", "subject", | "sent_or_received","recipients", "subject", | ||||
"communication_medium", "communication_type", | "communication_medium", "communication_type", | ||||
"sender", "seen", "reference_doctype", "reference_name" | |||||
"sender", "seen", "reference_doctype", "reference_name", | |||||
"has_attachment" | |||||
], | ], | ||||
filters: [["status", "=", "Open"]], | filters: [["status", "=", "Open"]], | ||||
@@ -70,7 +70,10 @@ def send_feedback_request(reference_doctype, reference_name, trigger="Manual", d | |||||
def get_feedback_request_details(reference_doctype, reference_name, trigger="Manual", request=None): | def get_feedback_request_details(reference_doctype, reference_name, trigger="Manual", request=None): | ||||
feedback_url = "" | feedback_url = "" | ||||
if not trigger and not request and not frappe.db.get_value("Feedback Trigger", { "document_type": reference_doctype }): | |||||
if not frappe.db.get_value(reference_doctype, reference_name): | |||||
# reference document is either deleted or renamed | |||||
return | |||||
elif not trigger and not request and not frappe.db.get_value("Feedback Trigger", { "document_type": reference_doctype }): | |||||
return | return | ||||
elif not trigger and request: | elif not trigger and request: | ||||
trigger = frappe.db.get_value("Feedback Request", request, "feedback_trigger") | trigger = frappe.db.get_value("Feedback Request", request, "feedback_trigger") | ||||
@@ -81,8 +84,8 @@ def get_feedback_request_details(reference_doctype, reference_name, trigger="Man | |||||
return | return | ||||
feedback_trigger = frappe.get_doc("Feedback Trigger", trigger) | feedback_trigger = frappe.get_doc("Feedback Trigger", trigger) | ||||
doc = frappe.get_doc(reference_doctype, reference_name) | |||||
doc = frappe.get_doc(reference_doctype, reference_name) | |||||
context = get_context(doc) | context = get_context(doc) | ||||
recipients = doc.get(feedback_trigger.email_fieldname, None) | recipients = doc.get(feedback_trigger.email_fieldname, None) | ||||
@@ -28,7 +28,8 @@ frappe.pages['modules'].on_page_load = function(wrapper) { | |||||
// render sidebar | // render sidebar | ||||
page.sidebar.html(frappe.render_template('modules_sidebar', | page.sidebar.html(frappe.render_template('modules_sidebar', | ||||
{modules: frappe.get_desktop_icons(true)})); | |||||
{modules: frappe.get_desktop_icons(true).sort( | |||||
function(a, b){ return (a._label > b._label) ? 1 : -1 })})); | |||||
// help click | // help click | ||||
page.main.on("click", '.module-section-link[data-type="help"]', function(event) { | page.main.on("click", '.module-section-link[data-type="help"]', function(event) { | ||||
@@ -247,6 +247,9 @@ class EmailAccount(Document): | |||||
email_sync_rule = self.build_email_sync_rule() | email_sync_rule = self.build_email_sync_rule() | ||||
email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) | email_server = self.get_incoming_server(in_receive=True, email_sync_rule=email_sync_rule) | ||||
if not email_server: | |||||
return | |||||
emails = email_server.get_messages() | emails = email_server.get_messages() | ||||
incoming_mails = emails.get("latest_messages") | incoming_mails = emails.get("latest_messages") | ||||
@@ -595,6 +598,9 @@ class EmailAccount(Document): | |||||
uid_list = { flag.get("uid", None): flag.get("action", "Read") for flag in flags } | uid_list = { flag.get("uid", None): flag.get("action", "Read") for flag in flags } | ||||
if flags and uid_list: | if flags and uid_list: | ||||
email_server = self.get_incoming_server() | email_server = self.get_incoming_server() | ||||
if not email_server: | |||||
return | |||||
email_server.update_flag(uid_list=uid_list) | email_server.update_flag(uid_list=uid_list) | ||||
# mark communication as read | # mark communication as read | ||||
@@ -152,12 +152,17 @@ def update_child_docs(old, new, meta): | |||||
def update_link_field_values(link_fields, old, new, doctype): | def update_link_field_values(link_fields, old, new, doctype): | ||||
for field in link_fields: | for field in link_fields: | ||||
if field['issingle']: | if field['issingle']: | ||||
single_doc = frappe.get_doc(field['parent']) | |||||
if single_doc.get(field['fieldname'])==old: | |||||
single_doc.set(field['fieldname'], new) | |||||
# update single docs using ORM rather then query | |||||
# as single docs also sometimes sets defaults! | |||||
single_doc.save(ignore_permissions=True) | |||||
try: | |||||
single_doc = frappe.get_doc(field['parent']) | |||||
if single_doc.get(field['fieldname'])==old: | |||||
single_doc.set(field['fieldname'], new) | |||||
# update single docs using ORM rather then query | |||||
# as single docs also sometimes sets defaults! | |||||
single_doc.save(ignore_permissions=True) | |||||
except ImportError: | |||||
# fails in patches where the doctype has been renamed | |||||
# or no longer exists | |||||
pass | |||||
else: | else: | ||||
# because the table hasn't been renamed yet! | # because the table hasn't been renamed yet! | ||||
parent = field['parent'] if field['parent']!=new else old | parent = field['parent'] if field['parent']!=new else old | ||||
@@ -174,3 +174,4 @@ frappe.patches.v8_0.deprecate_integration_broker | |||||
frappe.patches.v8_0.setup_email_inbox #2017-03-29 | frappe.patches.v8_0.setup_email_inbox #2017-03-29 | ||||
frappe.patches.v8_0.newsletter_childtable_migrate | frappe.patches.v8_0.newsletter_childtable_migrate | ||||
execute:frappe.db.sql("delete from `tabDesktop Icon` where module_name='Communication'") | execute:frappe.db.sql("delete from `tabDesktop Icon` where module_name='Communication'") | ||||
execute:frappe.db.sql("update `tabDesktop Icon` set type='list' where _doctype='Communication'") |
@@ -251,7 +251,6 @@ | |||||
"public/js/frappe/views/treeview.js", | "public/js/frappe/views/treeview.js", | ||||
"public/js/frappe/views/image/image_view_item_row.html", | "public/js/frappe/views/image/image_view_item_row.html", | ||||
"public/js/frappe/views/image/image_view_item_main_head.html", | |||||
"public/js/frappe/views/image/photoswipe_dom.html", | "public/js/frappe/views/image/photoswipe_dom.html", | ||||
"public/js/frappe/views/inbox/inbox_no_result.html", | "public/js/frappe/views/inbox/inbox_no_result.html", | ||||
@@ -152,14 +152,6 @@ | |||||
} | } | ||||
.filterable { | .filterable { | ||||
cursor: pointer; | cursor: pointer; | ||||
display: inline-block; | |||||
text-overflow: ellipsis; | |||||
} | |||||
.col-sm-2:not(.list-row-right) .filterable, | |||||
.col-sm-3:not(.list-row-right) .filterable { | |||||
max-width: 145px; | |||||
overflow: hidden; | |||||
width: 100%; | |||||
} | } | ||||
.doclist-row .label { | .doclist-row .label { | ||||
margin-right: 8px; | margin-right: 8px; | ||||
@@ -392,3 +384,73 @@ | |||||
.inbox-value { | .inbox-value { | ||||
padding-top: 2px; | padding-top: 2px; | ||||
} | } | ||||
.list-items { | |||||
width: 100%; | |||||
} | |||||
.list-item-container { | |||||
border-bottom: 1px solid #d1d8dd; | |||||
} | |||||
.list-item-container:last-child { | |||||
border-bottom: none; | |||||
} | |||||
.list-item { | |||||
display: flex; | |||||
align-items: center; | |||||
cursor: pointer; | |||||
height: 40px; | |||||
padding-left: 15px; | |||||
font-size: 12px; | |||||
} | |||||
.list-item:hover { | |||||
background-color: #F7FAFC; | |||||
} | |||||
@media (max-width: 767px) { | |||||
.list-item { | |||||
height: 50px; | |||||
padding-left: 10px; | |||||
font-size: 14px; | |||||
font-weight: normal; | |||||
} | |||||
} | |||||
.list-item--head { | |||||
background-color: #F7FAFC; | |||||
border-bottom: 1px solid #d1d8dd; | |||||
cursor: auto; | |||||
} | |||||
.list-item input[type=checkbox] { | |||||
margin: 0; | |||||
margin-right: 5px; | |||||
} | |||||
.list-item .liked-by, | |||||
.list-item .liked-by-filter-button { | |||||
display: inline-block; | |||||
width: 20px; | |||||
margin-right: 10px; | |||||
} | |||||
.list-item__content { | |||||
flex: 1; | |||||
margin-right: 15px; | |||||
display: flex; | |||||
align-items: center; | |||||
} | |||||
.list-item__content--flex-2 { | |||||
flex: 2; | |||||
} | |||||
.list-item__content--activity { | |||||
justify-content: flex-end; | |||||
margin-right: 5px; | |||||
} | |||||
.list-item__content--activity .list-row-modified, | |||||
.list-item__content--activity .avatar-small { | |||||
margin-right: 10px; | |||||
} | |||||
.list-item__content--indicator span::before { | |||||
height: 12px; | |||||
width: 12px; | |||||
} | |||||
.list-item__content--id { | |||||
justify-content: flex-end; | |||||
} | |||||
.frappe-timestamp { | |||||
white-space: nowrap; | |||||
} |
@@ -444,6 +444,10 @@ frappe.get_module = function(m, default_module) { | |||||
frappe.get_desktop_icons = function(show_hidden, show_global) { | frappe.get_desktop_icons = function(show_hidden, show_global) { | ||||
// filter valid icons | // filter valid icons | ||||
// hidden == hidden from desktop | |||||
// blocked == no view from modules either | |||||
var out = []; | var out = []; | ||||
var add_to_out = function(module) { | var add_to_out = function(module) { | ||||
@@ -1,8 +1,8 @@ | |||||
{% if (_checkbox) { %} | {% if (_checkbox) { %} | ||||
<input class="list-select-all" type="checkbox" | |||||
<input class="list-select-all hidden-xs" type="checkbox" | |||||
title="{%= __("Select All") %}"> | title="{%= __("Select All") %}"> | ||||
{% } %} | {% } %} | ||||
<span style="display: inline-block; width: 34px; vertical-align: middle;"> | |||||
<span class="liked-by-filter-button"> | |||||
<i class="fa-fw octicon octicon-heart text-extra-muted not-liked like-action list-liked-by-me" | <i class="fa-fw octicon octicon-heart text-extra-muted not-liked like-action list-liked-by-me" | ||||
title="{%= __("Likes") %}"></i> | title="{%= __("Likes") %}"></i> | ||||
</span> | </span> |
@@ -1,17 +1,15 @@ | |||||
<div class="hidden-xs"> | |||||
<span class="list-row-modified text-muted"> | |||||
{%= comment_when(data.modified, true) %} | |||||
</span> | |||||
{% if (data._assign_list.length) { %} | |||||
<span class="filterable" | |||||
data-filter="_assign,like,%{%= data._assign_list[data._assign_list.length - 1] %}%"> | |||||
{%= frappe.avatar(data._assign_list[data._assign_list.length - 1]) %}</span> | |||||
{% } else { %} | |||||
<span class="avatar avatar-small avatar-empty"></span> | |||||
{% } %} | |||||
<span class="list-comment-count small | |||||
{% if(!data._comment_count) { %} text-extra-muted {% } else { %} text-muted {% } %}"> | |||||
<i class="octicon octicon-comment-discussion"></i> | |||||
{%= (data._comment_count > 99 ? "99+" : data._comment_count) || 0 %} | |||||
</span> | |||||
</div> | |||||
<span class="list-row-modified text-muted"> | |||||
{%= comment_when(data.modified, true) %} | |||||
</span> | |||||
{% if (data._assign_list.length) { %} | |||||
<span class="filterable" | |||||
data-filter="_assign,like,%{%= data._assign_list[data._assign_list.length - 1] %}%"> | |||||
{%= frappe.avatar(data._assign_list[data._assign_list.length - 1]) %}</span> | |||||
{% } else { %} | |||||
<span class="avatar avatar-small avatar-empty"></span> | |||||
{% } %} | |||||
<span class="list-comment-count small | |||||
{% if(!data._comment_count) { %} text-extra-muted {% } else { %} text-muted {% } %}"> | |||||
<i class="octicon octicon-comment-discussion"></i> | |||||
{%= (data._comment_count > 99 ? "99+" : data._comment_count) || 0 %} | |||||
</span> |
@@ -1,49 +1,40 @@ | |||||
<div class="row"> | |||||
{% var total_cols=0; for (var i=0, l=columns.length; i < l; i++ ) { | |||||
var col = columns[i], value=data[col.fieldname]; total_cols += parseInt(col.colspan); %} | |||||
{% if (total_cols <= 12) { %} | |||||
<div class="col-sm-{%= col.colspan %} list-col ellipsis | |||||
{% if(col.type==="Subject") { | |||||
if (right_column) { %} | |||||
col-xs-9 | |||||
{% } else { %} | |||||
col-xs-12 | |||||
{% } %} | |||||
{% } else if(right_column && col.fieldname===right_column) { %} | |||||
col-xs-3 | |||||
{% } else { %} | |||||
hidden-xs | |||||
{% } %} | |||||
{% if(col.df && ["Int", "Float", "Currency", "Percent"].indexOf(col.df.fieldtype)!==-1) { %}text-right{% } %}" | |||||
{% if(col.type!=="Indicator" && col.title) { %}title="{%= col.title + ": " + value %}"{% } %}> | |||||
{% if(col.type!=="Indicator") { %}<span class="list-value">{% } %} | |||||
{% if (col.type==="Subject") { %} | |||||
{%= subject %} | |||||
{% } else if (col.type==="Indicator") { %} | |||||
{%= indicator %} | |||||
{% } else if (col.render) { %} | |||||
{%= col.render(data) %} | |||||
{% } else if (col.fieldtype==="Image") { %} | |||||
{% if(data[col.df.options]) { %} | |||||
<img src="{%= data[col.df.options] %}" style="max-height: 30px; max-width: 100%;"> | |||||
{% } else { %} | |||||
<div class="missing-image small"><span class="octicon octicon-circle-slash"></span></div> | |||||
{% } %} | |||||
{% } else if(col.fieldtype==="Select") { %} | |||||
<span class="filterable indicator {%= frappe.utils.guess_colour(value) %}" | |||||
data-filter="{%= col.fieldname %},=,{%= value %}">{%= __(value) %}</span> | |||||
{% } else if(col.fieldtype==="Link") { %} | |||||
<a class="filterable h6 text-muted grey" | |||||
data-filter="{%= col.fieldname %},=,{%= value %}">{%= value %}</a> | |||||
{% } else { %} | |||||
{% if(formatters && formatters[col.fieldname]) { %} | |||||
{{ formatters[col.fieldname](value, col.df, data) }} | |||||
{% } else { %} | |||||
{{ frappe.format(value, col.df, null, data) }} | |||||
{% } %} | |||||
{% } %} | |||||
{% if(col.type!=="Indicator") { %}</span>{% } %} | |||||
</div> | |||||
<div class="list-item__content ellipsis | |||||
{% if(col.type==="Subject") { %} | |||||
list-item__content--flex-2 | |||||
{% } else { %} | |||||
hidden-xs | |||||
{% } %} | |||||
{% if(col.df && ["Int", "Float", "Currency", "Percent"].indexOf(col.df.fieldtype)!==-1) { %}text-right{% } %}" | |||||
{% if(col.type!=="Indicator" && col.title) { %} | |||||
title="{%= col.title + ": " + value %}" | |||||
{% } %} | |||||
> | |||||
{% if (col.type==="Subject") { %} | |||||
{%= subject %} | |||||
{% } else if (col.type==="Indicator") { %} | |||||
{%= indicator %} | |||||
{% } else if (col.render) { %} | |||||
{%= col.render(data) %} | |||||
{% } else if (col.fieldtype==="Image") { %} | |||||
{% if(data[col.df.options]) { %} | |||||
<img src="{%= data[col.df.options] %}" style="max-height: 30px; max-width: 100%;"> | |||||
{% } else { %} | |||||
<div class="missing-image small"><span class="octicon octicon-circle-slash"></span></div> | |||||
{% } %} | |||||
{% } else if(col.fieldtype==="Select") { %} | |||||
<span class="filterable indicator {%= frappe.utils.guess_colour(value) %} ellipsis" | |||||
data-filter="{%= col.fieldname %},=,{%= value %}">{%= __(value) %}</span> | |||||
{% } else if(col.fieldtype==="Link") { %} | |||||
<a class="filterable text-muted grey ellipsis" | |||||
data-filter="{%= col.fieldname %},=,{%= value %}">{%= value %}</a> | |||||
{% } else { %} | |||||
<a class="filterable text-muted ellipsis" | |||||
data-filter="{%= col.fieldname %},=,{%= value %}"> | |||||
{% if(formatters && formatters[col.fieldname]) { %} | |||||
{{ formatters[col.fieldname](value, col.df, data) }} | |||||
{% } else { %} | |||||
{{ frappe.format(value, col.df, null, data) }} | |||||
{% } %} | {% } %} | ||||
</a> | |||||
{% } %} | {% } %} | ||||
</div> | |||||
</div> |
@@ -1,29 +1,15 @@ | |||||
<div class="row"> | |||||
{% var total_cols=0; for (var i=0, l=columns.length; i < l; i++ ) { | |||||
var col = columns[i]; total_cols += parseInt(col.colspan); %} | |||||
{% if (total_cols <= 12) { %} | |||||
<div class="col-sm-{%= col.colspan %} list-col ellipsis h6 text-muted | |||||
{% if(col.type==="Subject") { | |||||
if (right_column) { %} | |||||
col-xs-9 | |||||
{% } else { %} | |||||
col-xs-12 | |||||
{% } %} | |||||
{% } else if(right_column && col.fieldname===right_column) { %} | |||||
col-xs-3 | |||||
{% } else { %} | |||||
hidden-xs | |||||
{% } %} | |||||
{% if(col.df && ["Int", "Float", "Currency", "Percent"].indexOf(col.df.fieldtype)!==-1) { %}text-right{% } %}"> | |||||
<div class="list-item__content ellipsis text-muted | |||||
{% if(col.type==="Subject") { %} | |||||
list-item__content--flex-2 | |||||
{% } else { %} | |||||
hidden-xs | |||||
{% } %} | |||||
{% if(col.df && ["Int", "Float", "Currency", "Percent"].indexOf(col.df.fieldtype)!==-1) { %}text-right{% } %}" | |||||
> | |||||
<span class="list-value"> | |||||
{% if (col.type==="Subject") { %} | |||||
{%= frappe.render_template("header_select_all_like_filter", { _checkbox: _checkbox }) %} | |||||
{% } %} | |||||
<span class="list-col-title">{{ __(col.title) || __(col.label) || "" }}</span> | |||||
</span> | |||||
{% if (col.type==="Subject") { %} | |||||
{%= frappe.render_template("header_select_all_like_filter", { _checkbox: _checkbox }) %} | |||||
{% } %} | |||||
<span class="list-col-title ellipsis">{{ __(col.title) || __(col.label) || "" }}</span> | |||||
</div> | |||||
{% } %} | |||||
{% } %} | |||||
</div> | |||||
</div> |
@@ -1,31 +1,24 @@ | |||||
<div class="row doclist-row {% if (data._checkbox) { %} has-checkbox {% } %}"> | |||||
<div class="col-xs-10 | |||||
{% if (meta.title_field && !settings.hide_name_column) { %} | |||||
col-sm-8 | |||||
{% } else { %} | |||||
col-sm-10 | |||||
{% } %} list-row-left"> | |||||
<!-- title + columns --> | |||||
{%= main %} | |||||
</div> | |||||
<div class="list-item"> | |||||
{%= main %} | |||||
<!-- id --> | <!-- id --> | ||||
{% if (meta.title_field && !settings.hide_name_column) { | {% if (meta.title_field && !settings.hide_name_column) { | ||||
var is_different = data.name !== data[meta.title_field]; | |||||
%} | |||||
<div class="list-col col-sm-2 hidden-xs text-right ellipsis list-row-id"> | |||||
{% if (is_different) { %} | |||||
<a class="text-muted list-value" href="#Form/{%= data._doctype_encoded %}/{%= data._name_encoded %}"> | |||||
var is_different = data.name !== data[meta.title_field]; | |||||
%} | |||||
<div class="list-item__content list-item__content--id text-right hidden-xs hidden-sm"> | |||||
{% if (is_different) { %} | |||||
<a class="text-muted" href="#Form/{%= data._doctype_encoded %}/{%= data._name_encoded %}"> | |||||
{%= data.name %}</a> | {%= data.name %}</a> | ||||
{% } %} | |||||
{% } %} | |||||
</div> | </div> | ||||
{% } %} | {% } %} | ||||
<!-- comment --> | <!-- comment --> | ||||
<div class="list-col col-sm-2 col-xs-2 | |||||
text-right list-row-right" style="padding-left:0px"> | |||||
<div class="visible-xs list-row-indicator">{%= indicator_dot %}</div> | |||||
<!-- comments count and assigned to section --> | |||||
{%= frappe.render_template("item_assigned_to_comment_count", { data: data }) %} | |||||
<div class="list-item__content list-item__content--activity hidden-xs"> | |||||
<!-- comments count and assigned to section --> | |||||
{%= frappe.render_template("item_assigned_to_comment_count", { data: data }) %} | |||||
</div> | |||||
<div class="list-item__content list-item__content--indicator visible-xs text-right"> | |||||
{%= indicator_dot %} | |||||
</div> | </div> | ||||
</div> | </div> |
@@ -1,22 +1,11 @@ | |||||
<div class="list-row list-row-head" data-list-renderer="{{list.name}}"> | |||||
<div class="row doclist-row"> | |||||
<div class="col-xs-10 | |||||
{% if (list.meta.title_field && !list.settings.hide_name_column) { %} | |||||
col-sm-8 | |||||
{% } else { %} | |||||
col-sm-10 | |||||
{% } %} list-row-left"> | |||||
<!-- title + columns --> | |||||
{%= main %} | |||||
</div> | |||||
<div class="list-item list-item--head" data-list-renderer="{{list.name}}"> | |||||
<!-- title + columns --> | |||||
{%= main %} | |||||
<!-- id --> | |||||
{% if (list.meta.title_field && !list.settings.hide_name_column) { %} | |||||
<div class="list-col col-sm-2 hidden-xs text-right"> | |||||
</div> | |||||
{% } %} | |||||
<!-- comment --> | |||||
<div class="list-col col-sm-2 hidden-xs text-right list-row-right"></div> | |||||
</div> | |||||
<!-- id --> | |||||
{% if(list.meta.title_field && !list.settings.hide_name_column) { %} | |||||
<div class="list-item__content hidden-xs hidden-sm"></div> | |||||
{% } %} | |||||
<!-- comment --> | |||||
<div class="list-item__content list-item__content--activity hidden-xs text-right list-row-right"></div> | |||||
</div> | </div> |
@@ -1,5 +1,5 @@ | |||||
{% if (_checkbox) { %} | {% if (_checkbox) { %} | ||||
<input class="list-row-checkbox" type="checkbox" data-name="{{name}}"> | |||||
<input class="list-row-checkbox hidden-xs" type="checkbox" data-name="{{name}}"> | |||||
{% } %} | {% } %} | ||||
<span class="liked-by" data-liked-by=\'{{ JSON.stringify(_liked_by) }}\'> | <span class="liked-by" data-liked-by=\'{{ JSON.stringify(_liked_by) }}\'> | ||||
<i class="octicon octicon-heart | <i class="octicon octicon-heart | ||||
@@ -221,6 +221,12 @@ frappe.views.ListRenderer = Class.extend({ | |||||
return col; | return col; | ||||
}); | }); | ||||
} | } | ||||
// Remove duplicates | |||||
this.columns = this.columns.uniqBy(col => col.title); | |||||
// Limit number of columns to 4 | |||||
this.columns = this.columns.slice(0, 4); | |||||
}, | }, | ||||
add_column: function (df) { | add_column: function (df) { | ||||
// field width | // field width | ||||
@@ -284,7 +290,7 @@ frappe.views.ListRenderer = Class.extend({ | |||||
me.list_view.refresh(true); | me.list_view.refresh(true); | ||||
} | } | ||||
}); | }); | ||||
this.wrapper.on('click', '.list-row-left', function (e) { | |||||
this.wrapper.on('click', '.list-item', function (e) { | |||||
// don't open in case of checkbox, like, filterable | // don't open in case of checkbox, like, filterable | ||||
if ($(e.target).hasClass('filterable') | if ($(e.target).hasClass('filterable') | ||||
|| $(e.target).hasClass('octicon-heart') | || $(e.target).hasClass('octicon-heart') | ||||
@@ -300,11 +306,18 @@ frappe.views.ListRenderer = Class.extend({ | |||||
render_view: function (values) { | render_view: function (values) { | ||||
var me = this; | var me = this; | ||||
values.map(function (value) { | |||||
var row = $('<div class="list-row">') | |||||
.data("data", me.meta) | |||||
.appendTo(me.wrapper).get(0); | |||||
me.render_item(row, value); | |||||
var $list_items = $(` | |||||
<div class="list-items"> | |||||
</div> | |||||
`); | |||||
me.wrapper.append($list_items); | |||||
values.map(value => { | |||||
const $item = $(this.get_item_html(value)); | |||||
const $item_container = $('<div class="list-item-container">').append($item); | |||||
$list_items.append($item_container); | |||||
this.render_tags($item_container, value); | |||||
}); | }); | ||||
this.setup_filterable(); | this.setup_filterable(); | ||||
@@ -325,14 +338,16 @@ frappe.views.ListRenderer = Class.extend({ | |||||
// returns html for a data item, | // returns html for a data item, | ||||
// usually based on a template | // usually based on a template | ||||
get_item_html: function (data) { | get_item_html: function (data) { | ||||
var main = frappe.render_template('list_item_main', { | |||||
data: data, | |||||
columns: this.columns, | |||||
formatters: this.settings.formatters, | |||||
subject: this.get_subject_html(data, true), | |||||
indicator: this.get_indicator_html(data), | |||||
right_column: this.settings.right_column | |||||
}); | |||||
var main = this.columns.map(column => | |||||
frappe.render_template('list_item_main', { | |||||
data: data, | |||||
col: column, | |||||
value: data[column.fieldname], | |||||
formatters: this.settings.formatters, | |||||
subject: this.get_subject_html(data, true), | |||||
indicator: this.get_indicator_html(data), | |||||
}) | |||||
).join(""); | |||||
return frappe.render_template('list_item_row', { | return frappe.render_template('list_item_row', { | ||||
data: data, | data: data, | ||||
@@ -340,17 +355,17 @@ frappe.views.ListRenderer = Class.extend({ | |||||
settings: this.settings, | settings: this.settings, | ||||
meta: this.meta, | meta: this.meta, | ||||
indicator_dot: this.get_indicator_dot(data), | indicator_dot: this.get_indicator_dot(data), | ||||
right_column: this.settings.right_column | |||||
}) | }) | ||||
}, | }, | ||||
get_header_html: function () { | get_header_html: function () { | ||||
var main = frappe.render_template('list_item_main_head', { | |||||
columns: this.columns, | |||||
right_column: this.settings.right_column, | |||||
_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable) | |||||
&& !this.no_delete) | |||||
}); | |||||
var main = this.columns.map(column => | |||||
frappe.render_template('list_item_main_head', { | |||||
col: column, | |||||
_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable) | |||||
&& !this.no_delete) | |||||
}) | |||||
).join(""); | |||||
return frappe.render_template('list_item_row_head', { main: main, list: this }); | return frappe.render_template('list_item_row_head', { main: main, list: this }); | ||||
}, | }, | ||||
@@ -429,7 +444,7 @@ frappe.views.ListRenderer = Class.extend({ | |||||
data._submittable = frappe.model.is_submittable(this.doctype); | data._submittable = frappe.model.is_submittable(this.doctype); | ||||
var title_field = frappe.get_meta(this.doctype).title_field || 'name'; | var title_field = frappe.get_meta(this.doctype).title_field || 'name'; | ||||
data._title = strip_html(data[title_field]) || data.name; | |||||
data._title = strip_html(data[title_field] || data.name); | |||||
data._full_title = data._title; | data._full_title = data._title; | ||||
if (data._title.length > 35) { | if (data._title.length > 35) { | ||||
@@ -122,8 +122,10 @@ frappe.views.ListSidebar = Class.extend({ | |||||
var $dropdown = this.page.sidebar.find('.kanban-dropdown'); | var $dropdown = this.page.sidebar.find('.kanban-dropdown'); | ||||
var divider = false; | var divider = false; | ||||
var boards = frappe.get_meta(this.doctype).__kanban_boards; | |||||
var meta = frappe.get_meta(this.doctype); | |||||
var boards = meta && meta.__kanban_boards; | |||||
if (!boards) return; | if (!boards) return; | ||||
boards.forEach(function(board) { | boards.forEach(function(board) { | ||||
var route = ["List", board.reference_doctype, "Kanban", board.name].join('/'); | var route = ["List", board.reference_doctype, "Kanban", board.name].join('/'); | ||||
if(!divider) { | if(!divider) { | ||||
@@ -250,9 +250,9 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||||
}, | }, | ||||
init_headers: function () { | init_headers: function () { | ||||
this.page.main.find('.list-headers > .list-row-head').hide(); | |||||
this.page.main.find('.list-headers > .list-item--head').hide(); | |||||
this.list_header = this.page.main.find('.list-headers > ' | this.list_header = this.page.main.find('.list-headers > ' | ||||
+ '.list-row-head[data-list-renderer="' | |||||
+ '.list-item--head[data-list-renderer="' | |||||
+ this.list_renderer.name +'"]'); | + this.list_renderer.name +'"]'); | ||||
if(this.list_header.length > 0) { | if(this.list_header.length > 0) { | ||||
@@ -760,9 +760,9 @@ frappe.views.ListView = frappe.ui.BaseList.extend({ | |||||
// multi-select using shift key | // multi-select using shift key | ||||
var $this = $(this); | var $this = $(this); | ||||
if (event.shiftKey && $this.prop('checked')) { | if (event.shiftKey && $this.prop('checked')) { | ||||
var $end_row = $this.parents('.list-row'); | |||||
var $start_row = $end_row.prevAll('.list-row') | |||||
.find('.list-row-checkbox:checked').last().parents('.list-row'); | |||||
var $end_row = $this.parents('.list-item-container'); | |||||
var $start_row = $end_row.prevAll('.list-item-container') | |||||
.find('.list-row-checkbox:checked').last().parents('.list-item-container'); | |||||
if ($start_row) { | if ($start_row) { | ||||
$start_row.nextUntil($end_row).find('.list-row-checkbox').prop('checked', true); | $start_row.nextUntil($end_row).find('.list-row-checkbox').prop('checked', true); | ||||
} | } | ||||
@@ -107,7 +107,7 @@ frappe.views.GanttView = frappe.views.ListRenderer.extend({ | |||||
var $dropdown = $(dropdown) | var $dropdown = $(dropdown) | ||||
$dropdown.find(".dropdown-menu") | $dropdown.find(".dropdown-menu") | ||||
.append(dropdown_list); | .append(dropdown_list); | ||||
me.list_view.$page.find(`.list-row-head[data-list-renderer='Gantt'] .list-row-right`).css("margin-top", 0).html($dropdown) | |||||
me.list_view.$page.find(`[data-list-renderer='Gantt'] .list-row-right`).css("margin-right", "15px").html($dropdown) | |||||
$dropdown.on("click", ".option", function() { | $dropdown.on("click", ".option", function() { | ||||
var mode = $(this).data('value'); | var mode = $(this).data('value'); | ||||
me.gantt.change_view_mode(mode); | me.gantt.change_view_mode(mode); | ||||
@@ -47,9 +47,8 @@ frappe.views.ImageView = frappe.views.ListRenderer.extend({ | |||||
return null; | return null; | ||||
}, | }, | ||||
get_header_html: function () { | get_header_html: function () { | ||||
var main = frappe.render_template('image_view_item_main_head', { | |||||
columns: this.columns, | |||||
right_column: this.settings.right_column, | |||||
var main = frappe.render_template('list_item_main_head', { | |||||
col: { type: "Subject" }, | |||||
_checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable) | _checkbox: ((frappe.model.can_delete(this.doctype) || this.settings.selectable) | ||||
&& !this.no_delete) | && !this.no_delete) | ||||
}); | }); | ||||
@@ -1,7 +0,0 @@ | |||||
<div class="row"> | |||||
<div class="col-xs-12"> | |||||
<div class="list-value"> | |||||
{%= frappe.render_template("header_select_all_like_filter", { _checkbox: _checkbox }) %} | |||||
</div> | |||||
</div> | |||||
</div> |
@@ -341,7 +341,9 @@ frappe.provide("frappe.views"); | |||||
function setup_restore_columns() { | function setup_restore_columns() { | ||||
var cur_list = store.getState().cur_list; | var cur_list = store.getState().cur_list; | ||||
var columns = store.getState().columns; | var columns = store.getState().columns; | ||||
var list_row_right = cur_list.$page.find(`.list-row-head[data-list-renderer='Kanban'] .list-row-right`); | |||||
var list_row_right = | |||||
cur_list.$page.find(`[data-list-renderer='Kanban'] .list-row-right`) | |||||
.css('margin-right', '15px'); | |||||
list_row_right.empty(); | list_row_right.empty(); | ||||
var archived_columns = columns.filter(function (col) { | var archived_columns = columns.filter(function (col) { | ||||
@@ -364,7 +366,7 @@ frappe.provide("frappe.views"); | |||||
"<ul class='dropdown-menu'>" + options + "</ul>" + | "<ul class='dropdown-menu'>" + options + "</ul>" + | ||||
"</div>") | "</div>") | ||||
list_row_right.css("margin-top", 0).html($dropdown); | |||||
list_row_right.html($dropdown); | |||||
$dropdown.find(".dropdown-menu").on("click", "button.restore-column", function (e) { | $dropdown.find(".dropdown-menu").on("click", "button.restore-column", function (e) { | ||||
var column_title = $(this).data().column; | var column_title = $(this).data().column; | ||||
@@ -189,15 +189,6 @@ | |||||
.filterable { | .filterable { | ||||
cursor: pointer; | cursor: pointer; | ||||
display: inline-block; | |||||
text-overflow: ellipsis; | |||||
} | |||||
.col-sm-2:not(.list-row-right) .filterable, | |||||
.col-sm-3:not(.list-row-right) .filterable { | |||||
max-width: 145px; | |||||
overflow: hidden; | |||||
width: 100%; | |||||
} | } | ||||
@@ -240,7 +231,6 @@ | |||||
} | } | ||||
.like-action.octicon-heart { | .like-action.octicon-heart { | ||||
// color: #ffdb4c; | |||||
color: @heart-color; | color: @heart-color; | ||||
} | } | ||||
@@ -477,4 +467,92 @@ | |||||
.inbox-value { | .inbox-value { | ||||
padding-top: 2px; | padding-top: 2px; | ||||
} | |||||
// list view | |||||
.list-items { | |||||
width: 100%; | |||||
} | |||||
.list-item-container { | |||||
border-bottom: 1px solid @border-color; | |||||
&:last-child { | |||||
border-bottom: none; | |||||
} | |||||
} | |||||
.list-item { | |||||
display: flex; | |||||
align-items: center; | |||||
cursor: pointer; | |||||
height: 40px; | |||||
padding-left: 15px; | |||||
font-size: @text-medium; | |||||
&:hover { | |||||
background-color: @panel-bg; | |||||
} | |||||
@media (max-width: @screen-xs) { | |||||
height: 50px; | |||||
padding-left: 10px; | |||||
font-size: @text-regular; | |||||
font-weight: normal; | |||||
} | |||||
&--head { | |||||
background-color: @panel-bg; | |||||
border-bottom: 1px solid @border-color; | |||||
cursor: auto; | |||||
} | |||||
input[type=checkbox] { | |||||
margin: 0; | |||||
margin-right: 5px; | |||||
} | |||||
.liked-by, .liked-by-filter-button { | |||||
display: inline-block; | |||||
width: 20px; | |||||
margin-right: 10px; | |||||
} | |||||
} | |||||
.list-item__content { | |||||
flex: 1; | |||||
margin-right: 15px; | |||||
display: flex; | |||||
align-items: center; | |||||
&--flex-2 { | |||||
flex: 2; | |||||
} | |||||
&--activity { | |||||
justify-content: flex-end; | |||||
margin-right: 5px; | |||||
.list-row-modified, .avatar-small { | |||||
margin-right: 10px; | |||||
} | |||||
} | |||||
&--indicator span::before { | |||||
height: 12px; | |||||
width: 12px; | |||||
} | |||||
&--id { | |||||
justify-content: flex-end; | |||||
} | |||||
} | |||||
.frappe-timestamp { | |||||
white-space: nowrap; | |||||
} | } |
@@ -2,46 +2,53 @@ | |||||
{% block title %}{{ _("Feedback") }}{% endblock %} | {% block title %}{{ _("Feedback") }}{% endblock %} | ||||
{% block page_content %} | |||||
<div class="feedback"> | |||||
<p class='lead' id="feedback-msg"></p> | |||||
<div> | |||||
{{ _("Your rating: ") }} | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=1></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=2></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=3></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=4></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=5></i> | |||||
</div> | |||||
<div style='max-width: 500px;'> | |||||
<p>{{ _("Full Name") }}</p> | |||||
<input class="form-control fullname" type="text" placeholder="Your Full Name"> | |||||
<p>{{ _("Detailed feedback") }}</p> | |||||
<textarea class='form-control feedback-text' style='min-height: 300px;'></textarea> | |||||
</div> | |||||
<p><button class='btn btn-primary btn-sm btn-submit'>{{ _("Submit") }}</button></p> | |||||
{% if comment_list -%} | |||||
<div class="comments"> | |||||
<br><br> | |||||
<h3>{{ _("Communication") }}</h3> | |||||
{% include 'templates/includes/comments/comments.html' %} | |||||
</div> | |||||
{% endif %} | |||||
</div> | |||||
<div class="feedback-result" style="display: none"> | |||||
{% macro feedback_result(is_valid_request, subject, message='') %} | |||||
<div class="feedback-result" style="{{ 'display: none' if is_valid_request else '' }}"> | |||||
<div class='page-card'> | <div class='page-card'> | ||||
<div class='page-card-head'> | <div class='page-card-head'> | ||||
<span class='indicator darkgrey'>{{_("Thank You !!")}}</span> | |||||
<span class='indicator darkgrey'>{{ _(subject) }}</span> | |||||
</div> | </div> | ||||
<p id="feedback-result"></p> | |||||
<p id="feedback-result">{{ _(message) }}</p> | |||||
<div><a href='/' class='btn btn-primary btn-sm'>{{ _("Home") }}</a></div> | <div><a href='/' class='btn btn-primary btn-sm'>{{ _("Home") }}</a></div> | ||||
</div> | </div> | ||||
</div> | </div> | ||||
{% endmacro %} | |||||
{% block page_content %} | |||||
{% if is_valid_request %} | |||||
<div class="feedback"> | |||||
<p class='lead' id="feedback-msg"></p> | |||||
<div> | |||||
{{ _("Your rating: ") }} | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=1></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=2></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=3></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=4></i> | |||||
<i class='fa fa-fw fa-star-o star-icon' data-idx=5></i> | |||||
</div> | |||||
<div style='max-width: 500px;'> | |||||
<p>{{ _("Full Name") }}</p> | |||||
<input class="form-control fullname" type="text" placeholder="Your Full Name"> | |||||
<p>{{ _("Detailed feedback") }}</p> | |||||
<textarea class='form-control feedback-text' style='min-height: 300px;'></textarea> | |||||
</div> | |||||
<p><button class='btn btn-primary btn-sm btn-submit'>{{ _("Submit") }}</button></p> | |||||
{% if comment_list -%} | |||||
<div class="comments"> | |||||
<br><br> | |||||
<h3>{{ _("Communication") }}</h3> | |||||
{% include 'templates/includes/comments/comments.html' %} | |||||
</div> | |||||
{% endif %} | |||||
</div> | |||||
{{ feedback_result(is_valid_request, "Thank You") }} | |||||
{% else %} | |||||
{{ feedback_result(is_valid_request, "Invalid Input", error_message) }} | |||||
{% endif %} | |||||
<script> | <script> | ||||
window.feedback = { | window.feedback = { | ||||
@@ -7,8 +7,13 @@ def get_context(context): | |||||
reference_doctype = frappe.form_dict.get("reference_doctype") | reference_doctype = frappe.form_dict.get("reference_doctype") | ||||
reference_name = frappe.form_dict.get("reference_name") | reference_name = frappe.form_dict.get("reference_name") | ||||
if not all([reference_name, reference_doctype]): | |||||
return {} | |||||
if not all([reference_name, reference_doctype]) or \ | |||||
not frappe.db.get_value(reference_doctype, reference_name): | |||||
return { | |||||
"is_valid_request": False, | |||||
"error_message": "Invalid reference doctype and reference name" | |||||
} | |||||
communications = frappe.get_all("Communication", filters={ | communications = frappe.get_all("Communication", filters={ | ||||
"reference_doctype": reference_doctype, | "reference_doctype": reference_doctype, | ||||
@@ -20,14 +25,17 @@ def get_context(context): | |||||
"reference_doctype": reference_doctype, | "reference_doctype": reference_doctype, | ||||
"reference_name": reference_name, | "reference_name": reference_name, | ||||
"comment_list": communications, | "comment_list": communications, | ||||
"is_communication": True | |||||
"is_communication": True, | |||||
"is_valid_request": True | |||||
} | } | ||||
@frappe.whitelist(allow_guest=True) | @frappe.whitelist(allow_guest=True) | ||||
def accept(key, sender, reference_doctype, reference_name, feedback, rating, fullname): | def accept(key, sender, reference_doctype, reference_name, feedback, rating, fullname): | ||||
""" save the feedback in communication """ | """ save the feedback in communication """ | ||||
if not reference_doctype and not reference_name: | |||||
frappe.throw("Invalid Reference Doctype, Reference Name") | |||||
if not reference_doctype and not reference_name or \ | |||||
not frappe.db.get_value(reference_doctype, reference_name): | |||||
frappe.throw("Invalid reference doctype and reference name") | |||||
if not rating or not feedback: | if not rating or not feedback: | ||||
frappe.throw("Please give both Rating and Detailed Feedback") | frappe.throw("Please give both Rating and Detailed Feedback") | ||||