-
+
{% if (data.sender_full_name){var sender = data.sender_full_name} else {var sender = data.sender} %}
{%= sender %}
diff --git a/frappe/installer.py b/frappe/installer.py
index 873e570119..19af33ae1a 100755
--- a/frappe/installer.py
+++ b/frappe/installer.py
@@ -71,7 +71,7 @@ def create_database_and_user(force, verbose):
frappe.db.close()
def create_list_settings_table():
- frappe.db.sql_ddl("""create table if not exists __ListSettings (
+ frappe.db.sql_ddl("""create table if not exists __UserSettings (
`user` VARCHAR(180) NOT NULL,
`doctype` VARCHAR(180) NOT NULL,
`data` TEXT,
diff --git a/frappe/model/db_query.py b/frappe/model/db_query.py
index d5be376913..5193e8faa7 100644
--- a/frappe/model/db_query.py
+++ b/frappe/model/db_query.py
@@ -11,7 +11,7 @@ import frappe.permissions
from frappe.utils import flt, cint, getdate, get_datetime, get_time, make_filter_tuple, get_filter, add_to_date
from frappe import _
from frappe.model import optional_fields
-from frappe.model.utils.list_settings import get_list_settings, update_list_settings
+from frappe.model.utils.user_settings import get_user_settings, update_user_settings
from datetime import datetime
class DatabaseQuery(object):
@@ -30,8 +30,8 @@ class DatabaseQuery(object):
limit_page_length=None, as_list=False, with_childnames=False, debug=False,
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_list_settings=False, save_list_settings_fields=False,
- update=None, add_total_row=None):
+ ignore_ifnull=False, save_user_settings=False, save_user_settings_fields=False,
+ update=None, add_total_row=None, user_settings=None):
if not ignore_permissions and not frappe.has_permission(self.doctype, "read", user=user):
raise frappe.PermissionError, self.doctype
@@ -72,8 +72,10 @@ class DatabaseQuery(object):
self.flags.ignore_permissions = ignore_permissions
self.user = user or frappe.session.user
self.update = update
- self.list_settings_fields = copy.deepcopy(self.fields)
+ self.user_settings_fields = copy.deepcopy(self.fields)
#self.debug = True
+ if user_settings:
+ self.user_settings = json.loads(user_settings)
if query:
result = self.run_custom_query(query)
@@ -83,9 +85,9 @@ class DatabaseQuery(object):
if with_comment_count and not as_list and self.doctype:
self.add_comment_count(result)
- if save_list_settings:
- self.save_list_settings_fields = save_list_settings_fields
- self.update_list_settings()
+ if save_user_settings:
+ self.save_user_settings_fields = save_user_settings_fields
+ self.update_user_settings()
return result
@@ -513,17 +515,17 @@ class DatabaseQuery(object):
if "_comments" in r:
r._comment_count = len(json.loads(r._comments or "[]"))
- def update_list_settings(self):
- # update list settings if new search
- list_settings = json.loads(get_list_settings(self.doctype) or '{}')
- list_settings['filters'] = self.filters
- list_settings['limit'] = self.limit_page_length
- list_settings['order_by'] = self.order_by
+ def update_user_settings(self):
+ # update user settings if new search
+ user_settings = json.loads(get_user_settings(self.doctype))
- if self.save_list_settings_fields:
- list_settings['fields'] = self.list_settings_fields
+ if hasattr(self, 'user_settings'):
+ user_settings.update(self.user_settings)
- update_list_settings(self.doctype, list_settings)
+ if self.save_user_settings_fields:
+ user_settings['fields'] = self.user_settings_fields
+
+ update_user_settings(self.doctype, user_settings)
def get_order_by(doctype, meta):
order_by = ""
diff --git a/frappe/model/utils/list_settings.py b/frappe/model/utils/list_settings.py
deleted file mode 100644
index 0f6b91359e..0000000000
--- a/frappe/model/utils/list_settings.py
+++ /dev/null
@@ -1,39 +0,0 @@
-import frappe, json
-
-def get_list_settings(doctype, for_update=False):
- list_settings = frappe.cache().hget('_list_settings',
- '{0}::{1}'.format(doctype, frappe.session.user))
-
- if list_settings is None:
- list_settings = frappe.db.sql('''select data from __ListSettings
- where user=%s and doctype=%s''', (frappe.session.user, doctype))
- list_settings = list_settings and list_settings[0][0] or '{}'
-
- if not for_update:
- update_list_settings(doctype, list_settings, True)
-
- return list_settings
-
-def update_list_settings(doctype, list_settings, for_update=False):
- '''update list settings in cache'''
-
- if for_update:
- current = json.loads(list_settings)
- else:
- current = json.loads(get_list_settings(doctype, for_update = True))
-
- if isinstance(current, basestring):
- # corrupt due to old code, remove this in a future release
- current = {}
-
- current.update(list_settings)
-
- frappe.cache().hset('_list_settings', '{0}::{1}'.format(doctype, frappe.session.user),
- json.dumps(current))
-
-def sync_list_settings():
- '''Sync from cache to database (called asynchronously via the browser)'''
- for key, data in frappe.cache().hgetall('_list_settings').iteritems():
- doctype, user = key.split('::')
- frappe.db.sql('''insert into __ListSettings (user, doctype, data) values (%s, %s, %s)
- on duplicate key update data=%s''', (user, doctype, data, data))
\ No newline at end of file
diff --git a/frappe/model/utils/user_settings.py b/frappe/model/utils/user_settings.py
new file mode 100644
index 0000000000..dd58c4b9a4
--- /dev/null
+++ b/frappe/model/utils/user_settings.py
@@ -0,0 +1,48 @@
+# Settings saved per user basis
+# such as page_limit, filters, last_view
+
+import frappe, json
+
+def get_user_settings(doctype, for_update=False):
+ user_settings = frappe.cache().hget('_user_settings',
+ '{0}::{1}'.format(doctype, frappe.session.user))
+
+ if user_settings is None:
+ user_settings = frappe.db.sql('''select data from __UserSettings
+ where user=%s and doctype=%s''', (frappe.session.user, doctype))
+ user_settings = user_settings and user_settings[0][0] or '{}'
+
+ if not for_update:
+ update_user_settings(doctype, user_settings, True)
+
+ return user_settings or '{}'
+
+def update_user_settings(doctype, user_settings, for_update=False):
+ '''update user settings in cache'''
+
+ if for_update:
+ current = json.loads(user_settings)
+ else:
+ current = json.loads(get_user_settings(doctype, for_update = True))
+
+ if isinstance(current, basestring):
+ # corrupt due to old code, remove this in a future release
+ current = {}
+
+ current.update(user_settings)
+
+ frappe.cache().hset('_user_settings', '{0}::{1}'.format(doctype, frappe.session.user),
+ json.dumps(current))
+
+def sync_user_settings():
+ '''Sync from cache to database (called asynchronously via the browser)'''
+ for key, data in frappe.cache().hgetall('_user_settings').iteritems():
+ doctype, user = key.split('::')
+ frappe.db.sql('''insert into __UserSettings (user, doctype, data) values (%s, %s, %s)
+ on duplicate key update data=%s''', (user, doctype, data, data))
+
+@frappe.whitelist()
+def save(doctype, user_settings):
+ user_settings = json.loads(user_settings or '{}')
+ update_user_settings(doctype, user_settings)
+ return user_settings
diff --git a/frappe/patches.txt b/frappe/patches.txt
index c7f7e06f98..e1e0a2dad4 100644
--- a/frappe/patches.txt
+++ b/frappe/patches.txt
@@ -163,5 +163,5 @@ execute:frappe.rename_doc('Country', 'Macedonia, Republic of', 'Macedonia', igno
execute:frappe.rename_doc('Country', 'Iran, Islamic Republic of', 'Iran', ignore_if_exists=True)
execute:frappe.rename_doc('Country', 'Tanzania, United Republic of', 'Tanzania', ignore_if_exists=True)
execute:frappe.rename_doc('Country', 'Syrian Arab Republic', 'Syria', ignore_if_exists=True)
+frappe.patches.v8_0.rename_listsettings_to_usersettings
frappe.patches.v7_2.update_communications
-
diff --git a/frappe/patches/v8_0/__init__.py b/frappe/patches/v8_0/__init__.py
new file mode 100644
index 0000000000..e69de29bb2
diff --git a/frappe/patches/v8_0/rename_listsettings_to_usersettings.py b/frappe/patches/v8_0/rename_listsettings_to_usersettings.py
new file mode 100644
index 0000000000..0db09f9dc2
--- /dev/null
+++ b/frappe/patches/v8_0/rename_listsettings_to_usersettings.py
@@ -0,0 +1,26 @@
+import frappe, json
+
+def execute():
+ for us in frappe.db.sql('''select user, doctype, data from __ListSettings''', as_dict=True):
+ try:
+ data = json.loads(us.data)
+ except:
+ continue
+
+ if 'List' in data:
+ continue
+
+ if 'limit' in data:
+ data['page_length'] = data['limit']
+ del data['limit']
+
+ new_data = dict(List=data)
+ new_data = json.dumps(new_data)
+
+ frappe.db.sql('''update __ListSettings
+ set data=%(new_data)s
+ where user=%(user)s
+ and doctype=%(doctype)s''',
+ {'new_data': new_data, 'user': us.user, 'doctype': us.doctype})
+
+ frappe.db.sql("RENAME TABLE __ListSettings to __UserSettings")
\ No newline at end of file
diff --git a/frappe/public/build.json b/frappe/public/build.json
index 2f62b67a8d..86f648fa76 100755
--- a/frappe/public/build.json
+++ b/frappe/public/build.json
@@ -112,6 +112,7 @@
"public/js/frappe/model/create_new.js",
"public/js/frappe/model/perm.js",
"public/js/frappe/model/workflow.js",
+ "public/js/frappe/model/user_settings.js",
"public/js/lib/md5.min.js",
"public/js/frappe/misc/user.js",
@@ -211,7 +212,9 @@
],
"js/list.min.js": [
"public/js/frappe/ui/listing.html",
- "public/js/frappe/ui/listing.js",
+
+ "public/js/frappe/ui/base_list.js",
+
"public/js/frappe/model/indicator.js",
"public/js/frappe/ui/filters/filters.js",
"public/js/frappe/ui/filters/edit_filter.html",
@@ -222,7 +225,9 @@
"public/js/frappe/ui/like.js",
"public/js/frappe/ui/liked_by.html",
"public/html/print_template.html",
- "public/js/frappe/list/doclistview.js",
+
+ "public/js/frappe/list/list_view.js",
+
"public/js/frappe/list/list_sidebar.js",
"public/js/frappe/list/list_sidebar.html",
"public/js/frappe/list/list_sidebar_stat.html",
@@ -232,14 +237,21 @@
"public/js/frappe/list/list_item_row_head.html",
"public/js/frappe/list/list_item_subject.html",
"public/js/frappe/list/list_permission_footer.html",
- "public/js/frappe/list/listview.js",
- "public/js/frappe/views/calendar.js",
- "public/js/frappe/list/blueimp-gallery.html",
- "public/js/frappe/list/image_view_item_row.html",
- "public/js/frappe/list/image_view_item_main_head.html",
+
+ "public/js/frappe/list/list_renderer.js",
+ "public/js/frappe/views/gantt/gantt_view.js",
+ "public/js/frappe/views/calendar/calendar.js",
+ "public/js/frappe/views/image/image_view.js",
+ "public/js/frappe/views/kanban/kanban_view.js",
+
"public/js/frappe/list/header_select_all_like_filter.html",
"public/js/frappe/list/item_assigned_to_comment_count.html",
"public/js/frappe/views/treeview.js",
+
+ "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/kanban/kanban_board.html",
"public/js/frappe/views/kanban/kanban_column.html",
"public/js/frappe/views/kanban/kanban_card.html"
diff --git a/frappe/public/css/desk.css b/frappe/public/css/desk.css
index d87fe1b0f5..4bf3cb0275 100644
--- a/frappe/public/css/desk.css
+++ b/frappe/public/css/desk.css
@@ -838,3 +838,29 @@ fieldset[disabled] .form-control {
.c3-tooltip td.value {
text-align: right;
}
+input[type="checkbox"] {
+ visibility: hidden;
+ position: relative;
+}
+input[type="checkbox"]:before {
+ position: absolute;
+ font-family: 'FontAwesome';
+ content: '\f096';
+ visibility: visible;
+ font-style: normal;
+ font-weight: normal;
+ font-variant: normal;
+ text-transform: none;
+ line-height: 14px;
+ display: inline-block;
+ font-size: 14px;
+ color: #d1d8dd;
+ -webkit-transition: 150ms color;
+ -o-transition: 150ms color;
+ transition: 150ms color;
+}
+input[type="checkbox"]:checked:before {
+ content: '\f14a';
+ font-size: 13px;
+ color: #3b99fc;
+}
diff --git a/frappe/public/css/form_grid.css b/frappe/public/css/form_grid.css
index b0614d1205..2b20fd89e9 100644
--- a/frappe/public/css/form_grid.css
+++ b/frappe/public/css/form_grid.css
@@ -76,6 +76,7 @@
.grid-body .editable-row .checkbox {
margin: 0px;
text-align: center;
+ margin-top: 9px;
}
.grid-body .editable-row textarea {
height: 38px !important;
diff --git a/frappe/public/css/list.css b/frappe/public/css/list.css
index 05e57a0674..fe52ee026c 100644
--- a/frappe/public/css/list.css
+++ b/frappe/public/css/list.css
@@ -110,6 +110,25 @@
border-top: 1px solid #d1d8dd;
}
.list-value {
+ display: table;
+ vertical-align: middle;
+}
+.list-value .list-row-checkbox,
+.list-value .liked-by,
+.list-value .list-id,
+.list-value .list-select-all {
+ display: table-cell;
+ vertical-align: middle;
+}
+.list-value .list-row-checkbox,
+.list-value .list-select-all {
+ margin: 0;
+ margin-right: 7px;
+}
+.list-value .liked-by {
+ padding-top: 2px;
+}
+.list-value .list-col-title {
vertical-align: middle;
}
.progress {
@@ -118,7 +137,7 @@
.doclist-row {
font-size: 12px;
}
-.doclist-row .likes-count {
+.likes-count {
display: inline-block;
width: 15px;
margin-left: -5px;
@@ -164,9 +183,6 @@
.listview-main-section .octicon-heart {
cursor: pointer;
}
-.list-row-head .octicon-heart {
- margin-right: 13px;
-}
.like-action.octicon-heart {
color: #ff5858;
}
@@ -198,55 +214,6 @@
width: 37px;
text-align: left;
}
-.image-view {
- float: left;
-}
-.image-view-subject {
- padding: inherit;
-}
-.image-view-col {
- padding-bottom: 5px;
- padding-top: 5px;
-}
-.image-view-col a {
- text-decoration: none !important;
-}
-table.field-info {
- opacity: 0;
- bottom: -20px;
- font-size: 8pt;
- color: white;
- background-color: #36414C;
- position: absolute;
- padding-bottom: 0px !important;
- border: 0px;
-}
-table.field-info tr td {
- border: none !important;
-}
-.zoom-view {
- top: 10px !important;
- right: 10px !important;
- width: 36px;
- height: 36px;
- opacity: 0;
- font-size: 16px;
- color: #36414C;
- position: absolute;
- padding: 0px !important;
- border-radius: 5px;
- border: 0px;
-}
-.image-field {
- background-size: contain;
- position: relative;
-}
-.image-field:hover .field-info {
- opacity: 0.7;
-}
-.image-field:hover .zoom-view {
- opacity: 0.6;
-}
.tag-row {
margin-top: 5px;
padding-left: 55px;
@@ -291,3 +258,99 @@ table.field-info tr td {
font-size: 11px;
max-width: 100px;
}
+.image-view-container {
+ display: flex;
+ flex-wrap: wrap;
+}
+.image-view-container .image-view-item {
+ flex: 0 0 25%;
+ padding: 15px;
+ border-bottom: 1px solid #EBEFF2;
+ border-right: 1px solid #EBEFF2;
+}
+.image-view-container .image-view-item:nth-child(4n) {
+ border-right: none;
+}
+.image-view-container .image-view-item:nth-last-child(-n + 4):nth-child(4n + 1),
+.image-view-container .image-view-item:nth-last-child(-n + 4):nth-child(4n + 1) ~ .image-view-item {
+ border-bottom: none;
+}
+.image-view-container .image-view-header {
+ margin-bottom: 10px;
+}
+.image-view-container .image-view-body:hover .zoom-view {
+ opacity: 0.7;
+}
+.image-view-container .image-view-body a {
+ text-decoration: none;
+}
+.image-view-container .image-field {
+ display: flex;
+ align-content: center;
+ align-items: center;
+ justify-content: center;
+ position: relative;
+ height: 200px;
+ padding: 15px;
+}
+.image-view-container .image-field img {
+ max-height: 100%;
+}
+.image-view-container .placeholder-text {
+ font-size: 72px;
+ color: #d1d8dd;
+}
+.image-view-container .zoom-view {
+ bottom: 10px !important;
+ right: 10px !important;
+ width: 36px;
+ height: 36px;
+ opacity: 0;
+ font-size: 16px;
+ color: #36414C;
+ position: absolute;
+}
+@media (max-width: 767px) {
+ .image-view-container .zoom-view {
+ opacity: 0.5;
+ }
+}
+@media (max-width: 991px) {
+ .image-view-container .image-view-item {
+ flex: 0 0 33.33333333%;
+ }
+ .image-view-container .image-view-item:nth-child(3n) {
+ border-right: none;
+ }
+ .image-view-container .image-view-item:nth-last-child(-n + 3):nth-child(3n + 1),
+ .image-view-container .image-view-item:nth-last-child(-n + 3):nth-child(3n + 1) ~ .image-view-item {
+ border-bottom: none;
+ }
+ .image-view-container .image-view-item:nth-child(4n) {
+ border-right: 1px solid #EBEFF2;
+ }
+ .image-view-container .image-view-item:nth-last-child(-n + 4):nth-child(4n + 1),
+ .image-view-container .image-view-item:nth-last-child(-n + 4):nth-child(4n + 1) ~ .image-view-item {
+ border-bottom: 1px solid #EBEFF2;
+ }
+}
+.pswp--svg .pswp__button,
+.pswp--svg .pswp__button--arrow--left:before,
+.pswp--svg .pswp__button--arrow--right:before {
+ background-image: url('/assets/frappe/images/default-skin.svg') !important;
+}
+.pswp--svg .pswp__button--arrow--left,
+.pswp--svg .pswp__button--arrow--right {
+ background: none !important;
+}
+.pswp__bg {
+ background-color: #fff !important;
+}
+.gantt .details-container .heading {
+ margin-bottom: 10px;
+ font-size: 12px;
+}
+.gantt .details-container .avatar-small {
+ width: 16px;
+ height: 16px;
+}
diff --git a/frappe/public/css/sidebar.css b/frappe/public/css/sidebar.css
index 1541244de3..5b7fa2e8fd 100644
--- a/frappe/public/css/sidebar.css
+++ b/frappe/public/css/sidebar.css
@@ -121,6 +121,9 @@ body[data-route^="Module"] .main-menu .form-sidebar {
.form-sidebar .form-viewers .shared-with-everyone .octicon {
color: #36414C !important;
}
+.form-sidebar .liked-by {
+ margin-left: -4px;
+}
.form-sidebar .liked-by .octicon-heart {
font-size: 16px;
cursor: pointer;
diff --git a/frappe/public/images/default-skin.svg b/frappe/public/images/default-skin.svg
new file mode 100755
index 0000000000..9d5f0c6a10
--- /dev/null
+++ b/frappe/public/images/default-skin.svg
@@ -0,0 +1 @@
+
default-skin 2
\ No newline at end of file
diff --git a/frappe/public/js/frappe/form/sidebar.js b/frappe/public/js/frappe/form/sidebar.js
index 165688ddc8..080e6cd898 100644
--- a/frappe/public/js/frappe/form/sidebar.js
+++ b/frappe/public/js/frappe/form/sidebar.js
@@ -128,7 +128,7 @@ frappe.ui.form.Sidebar = Class.extend({
make_like: function() {
this.like_wrapper = this.sidebar.find(".liked-by");
this.like_icon = this.sidebar.find(".liked-by .octicon-heart");
- this.like_count = this.sidebar.find(".liked-by .like-count");
+ this.like_count = this.sidebar.find(".liked-by .likes-count");
frappe.ui.setup_like_popover(this.sidebar.find(".liked-by-parent"), ".liked-by");
},
diff --git a/frappe/public/js/frappe/form/templates/form_sidebar.html b/frappe/public/js/frappe/form/templates/form_sidebar.html
index cf5454b56c..e44a55cedc 100644
--- a/frappe/public/js/frappe/form/templates/form_sidebar.html
+++ b/frappe/public/js/frappe/form/templates/form_sidebar.html
@@ -67,8 +67,8 @@
{% } %}
{% } %}
diff --git a/frappe/public/js/frappe/list/list_item_row.html b/frappe/public/js/frappe/list/list_item_row.html
index 1995a3f931..0db51d0a25 100644
--- a/frappe/public/js/frappe/list/list_item_row.html
+++ b/frappe/public/js/frappe/list/list_item_row.html
@@ -1,6 +1,6 @@
" + options + "
" + - "' + heading + '
\n\t\t\t\t' + line_1 + '
\n\t\t\t\t' + (line_2 ? '' + line_2 + '
' : '') + '\n\t\t\t