[print] tables & [feature] share #992

This commit is contained in:
Rushabh Mehta 2015-02-04 17:01:31 +05:30
parent c22af0798a
commit 8cde7bfc23
23 changed files with 497 additions and 97 deletions

View file

@ -198,6 +198,13 @@
"fieldtype": "Column Break",
"permlevel": 0
},
{
"fieldname": "share",
"fieldtype": "Check",
"label": "Share",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "print",
"fieldtype": "Check",
@ -216,7 +223,7 @@
"idx": 1,
"issingle": 0,
"istable": 1,
"modified": "2014-08-26 01:43:31.499363",
"modified": "2015-02-04 04:34:26.227213",
"modified_by": "Administrator",
"module": "Core",
"name": "DocPerm",

View file

View file

@ -0,0 +1,171 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "hash",
"creation": "2015-02-04 04:33:36.330477",
"custom": 0,
"description": "Internal record of document shares",
"docstatus": 0,
"doctype": "DocType",
"document_type": "System",
"fields": [
{
"allow_on_submit": 0,
"fieldname": "user",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "User",
"no_copy": 0,
"options": "User",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"fieldname": "share_doctype",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Document Type",
"no_copy": 0,
"options": "DocType",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"fieldname": "share_name",
"fieldtype": "Dynamic Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Document Name",
"no_copy": 0,
"options": "share_doctype",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 1,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"default": "0",
"fieldname": "read",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Read",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"default": "0",
"fieldname": "write",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
{
"allow_on_submit": 0,
"default": "0",
"fieldname": "share",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Share",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2015-02-04 04:44:55.131126",
"modified_by": "Administrator",
"module": "Core",
"name": "DocShare",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 0,
"export": 1,
"import": 1,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 1,
"role": "Administrator",
"set_user_permissions": 0,
"submit": 0,
"write": 1
}
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
}

View file

@ -0,0 +1,33 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
from frappe.utils import get_fullname
class DocShare(Document):
no_feed_on_delete = True
def validate(self):
if not frappe.has_permission(self.share_doctype, "share",
frappe.get_doc(self.share_doctype, self.share_name)):
frappe.throw(_("Not Allowed"), frappe.PermissionError)
self.cascade_permissions_downwards()
def cascade_permissions_downwards(self):
if self.share:
self.write = 1
if self.write:
self.read = 1
def after_insert(self):
self.add_comment(_("{0} shared this document with {0}").format(get_fullname(self.owner), get_fullname(self.user)))
def on_trash(self):
self.add_comment(_("{0} un-shared this document with {0}").format(get_fullname(self.owner), get_fullname(self.user)))
def on_doctype_update():
"""Add index in `tabDocShare` for `(user, share_doctype)`"""
frappe.db.add_index("DocShare", ["user", "share_doctype"])

View file

@ -0,0 +1,69 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# See license.txt
import frappe
import frappe.share
import unittest
class TestDocShare(unittest.TestCase):
def setUp(self):
self.user = "test@example.com"
self.event = frappe.get_doc({"doctype": "Event",
"subject": "test share event",
"starts_on": "2015-01-01 10:00:00",
"event_type": "Private"}).insert()
def tearDown(self):
frappe.set_user("Administrator")
self.event.delete()
def test_add(self):
# user not shared
self.assertTrue(self.event.name not in frappe.db.get_shared("Event", self.user))
frappe.share.add("Event", self.event.name, self.user)
self.assertTrue(self.event.name in frappe.db.get_shared("Event", self.user))
def test_doc_permission(self):
frappe.set_user(self.user)
self.assertFalse(self.event.has_permission())
frappe.set_user("Administrator")
frappe.share.add("Event", self.event.name, self.user)
frappe.set_user(self.user)
self.assertTrue(self.event.has_permission())
def test_share_permission(self):
frappe.share.add("Event", self.event.name, self.user, share=1)
frappe.set_user(self.user)
self.assertTrue(self.event.has_permission("share"))
# test cascade
self.assertTrue(self.event.has_permission("read"))
self.assertTrue(self.event.has_permission("write"))
def test_set_permission(self):
frappe.share.add("Event", self.event.name, self.user)
frappe.set_user(self.user)
self.assertFalse(self.event.has_permission("share"))
frappe.set_user("Administrator")
frappe.share.set_permission("Event", self.event.name, self.user, "share")
frappe.set_user(self.user)
self.assertTrue(self.event.has_permission("share"))
def test_permission_to_share(self):
frappe.set_user(self.user)
self.assertRaises(frappe.PermissionError, frappe.share.add, "Event", self.event.name, self.user)
frappe.set_user("Administrator")
frappe.share.add("Event", self.event.name, self.user, share=1)
# test not raises
frappe.set_user(self.user)
frappe.share.add("Event", self.event.name, "test1@example.com", share=1)

View file

@ -0,0 +1 @@
[]

View file

@ -14,7 +14,7 @@ from frappe.model.document import Document
from frappe.utils.file_manager import delete_file_data_content
class FileData(Document):
ignore_feed = True
no_feed_on_delete = True
def before_insert(self):
frappe.local.rollback_observers.append(self)

View file

@ -83,6 +83,7 @@ CREATE TABLE `tabDocPerm` (
`report` int(1) DEFAULT NULL,
`export` int(1) DEFAULT NULL,
`import` int(1) DEFAULT NULL,
`share` int(1) DEFAULT NULL,
`print` int(1) DEFAULT NULL,
`email` int(1) DEFAULT NULL,
`restrict` int(1) DEFAULT NULL,

View file

@ -598,6 +598,24 @@ class Database:
else:
return frappe.defaults.get_defaults(parent)
def get_shared(self, doctype, user=None, rights=None):
"""Get list of shared document names for given user and DocType.
:param doctype: DocType of which shared names are queried.
:param user: User for which shared names are queried.
:param rights: List of rights for which the document is shared. List of `read`, `write`, `share`"""
if not user:
user = frappe.session.user
if not rights:
rights = ["read"]
condition = " and ".join(["`{0}`=1".format(right) for right in rights])
return self.sql_list("select share_name from tabDocShare where user=%s and share_doctype=%s and {0}".format(condition),
(user, doctype))
def begin(self):
return # not required

View file

@ -7,6 +7,35 @@ from frappe import _
from frappe.utils import cint, flt, now, cstr, strip_html
from frappe.model import default_fields
from frappe.model.naming import set_new_name
from frappe.modules import load_doctype_module
_classes = {}
def get_controller(doctype):
"""Returns the **class** object of the given DocType.
For `custom` type, returns `frappe.model.document.Document`.
:param doctype: DocType name as string."""
if not doctype in _classes:
module_name, custom = frappe.db.get_value("DocType", doctype, ["module", "custom"]) \
or ["Core", False]
if custom:
_class = BaseDocument
else:
module = load_doctype_module(doctype, module_name)
classname = doctype.replace(" ", "").replace("-", "")
if hasattr(module, classname):
_class = getattr(module, classname)
if issubclass(_class, BaseDocument):
_class = getattr(module, classname)
else:
raise ImportError, doctype
else:
raise ImportError, doctype
_classes[doctype] = _class
return _classes[doctype]
class BaseDocument(object):
ignore_in_getter = ("doctype", "_meta", "meta", "_table_fields", "_valid_columns")
@ -15,6 +44,9 @@ class BaseDocument(object):
self.update(d)
self.dont_update_if_missing = []
if hasattr(self, "__setup__"):
self.__setup__()
@property
def meta(self):
if not hasattr(self, "_meta"):
@ -95,6 +127,9 @@ class BaseDocument(object):
self.__dict__[key] = []
value = self._init_child(value, key)
self.__dict__[key].append(value)
# reference parent document
value.parent_doc = self
return value
else:
raise ValueError, "Document attached to child table must be a dict or BaseDocument, not " + str(type(value))[1:-1]
@ -117,7 +152,7 @@ class BaseDocument(object):
value["doctype"] = self.get_table_field_doctype(key)
if not value["doctype"]:
raise AttributeError, key
value = BaseDocument(value)
value = get_controller(value["doctype"])(value)
value.init_valid_columns()
value.parent = self.name
@ -364,28 +399,30 @@ class BaseDocument(object):
return format_value(self.get(fieldname), self.meta.get_field(fieldname),
doc=doc or self, currency=currency)
def get_print_template(self, fieldname, parent_doc=None):
"""Returns print template for given fieldname if specified in controller
or parent controller.
def is_print_hide(self, fieldname, for_print=True):
"""Returns true if fieldname is to be hidden for print.
Templates must be specified as:
Print Hide can be set via the Print Format Builder or in the controller as a list
of hidden fields. Example
class MyDocType(Document):
class MyDoc(Document):
def __setup__(self):
self.print_templates = {
"[fieldname]": "templates/includes/template_name.html",
"[table fieldname].[fieldname]": "templates/includes/template_name.html"
}
self.print_hide = ["field1", "field2"]
:param fieldname: Field for which template is queried.
:param parent_doc: Parent Document, if child doc."""
src = self
if parent_doc:
src = parent_doc
fieldname = self.parentfield + "." + fieldname
if hasattr(src, "print_templates"):
return src.print_templates.get(fieldname)
:param fieldname: Fieldname to be checked if hidden.
"""
df = self.meta.get_field(fieldname)
return df and (df.get("__print_hide") or df.print_hide)
def in_format_data(self, fieldname):
"""Returns True if shown via Print Format::`format_data` property.
Called from within standard print format."""
doc = getattr(self, "parent_doc", self)
if hasattr(doc, "format_data_map"):
return fieldname in doc.format_data_map
else:
return True
def _filter(data, filters, limit=None):
"""pass filters as:

View file

@ -240,19 +240,36 @@ class DatabaseQuery(object):
self.match_filters = []
self.match_conditions = []
only_if_shared = False
if not self.tables: self.extract_tables()
meta = frappe.get_meta(self.doctype)
role_permissions = frappe.permissions.get_role_permissions(meta, user=self.user)
if not meta.istable and not role_permissions.get("read") and not getattr(self, "ignore_permissions", False):
frappe.throw(_("No permission to read {0}").format(self.doctype))
# apply user permissions?
if role_permissions.get("apply_user_permissions", {}).get("read"):
# get user permissions
user_permissions = frappe.defaults.get_user_permissions(self.user)
self.add_user_permissions(user_permissions,
user_permission_doctypes=role_permissions.get("user_permission_doctypes"))
self.shared = frappe.db.get_shared(self.doctype, self.user)
if not meta.istable and not role_permissions.get("read") and not getattr(self,
"ignore_permissions", False):
only_if_shared = True
if not self.shared:
frappe.throw(_("No permission to read {0}").format(self.doctype))
else:
self.conditions.append(self.get_share_condition())
else:
# share is an OR condition, if there is a role permission
if not only_if_shared and self.shared:
self.or_conditions.append(self.get_share_condition())
# apply user permissions?
if not role_permissions.get("apply_user_permissions", {}).get("read"):
# get user permissions
user_permissions = frappe.defaults.get_user_permissions(self.user)
self.add_user_permissions(user_permissions,
user_permission_doctypes=role_permissions.get("user_permission_doctypes"))
if as_condition:
conditions = ""
@ -269,6 +286,10 @@ class DatabaseQuery(object):
else:
return self.match_filters
def get_share_condition(self):
return """`tab{0}`.name in ({1})""".format(self.doctype, ", ".join(["%s"] * len(self.shared))) % \
[frappe.db.escape(s) for s in self.shared]
def add_user_permissions(self, user_permissions, user_permission_doctypes=None):
user_permission_doctypes = frappe.permissions.get_user_permission_doctypes(user_permission_doctypes,
user_permissions)

View file

@ -62,6 +62,7 @@ def delete_doc(doctype=None, name=None, force=0, ignore_doctypes=None, for_reloa
doc.run_method("on_trash")
delete_linked_todos(doc)
delete_shared(doc)
# check if links exist
if not force:
check_if_doc_is_linked(doc)
@ -166,7 +167,7 @@ def delete_linked_todos(doc):
def insert_feed(doc):
from frappe.utils import get_fullname
if frappe.flags.in_install or frappe.flags.in_import or doc.get("ignore_feed"):
if frappe.flags.in_install or frappe.flags.in_import or getattr(doc, "no_feed_on_delete", False):
return
frappe.get_doc({
@ -177,3 +178,7 @@ def insert_feed(doc):
"subject": _("Deleted"),
"full_name": get_fullname(doc.owner)
}).insert(ignore_permissions=True)
def delete_shared(doc):
delete_doc("DocShare", frappe.db.sql_list("""select name from `tabDocShare`
where share_doctype=%s and share_name=%s""", (doc.doctype, doc.name)))

View file

@ -5,8 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _, msgprint
from frappe.utils import flt, cint, cstr, now
from frappe.modules import load_doctype_module
from frappe.model.base_document import BaseDocument
from frappe.model.base_document import BaseDocument, get_controller
from frappe.model.naming import set_new_name
from werkzeug.exceptions import NotFound, Forbidden
import hashlib
@ -47,34 +46,6 @@ def get_doc(arg1, arg2=None):
raise ImportError, arg1
_classes = {}
def get_controller(doctype):
"""Returns the **class** object of the given DocType.
For `custom` type, returns `frappe.model.document.Document`.
:param doctype: DocType name as string."""
if not doctype in _classes:
module_name, custom = frappe.db.get_value("DocType", doctype, ["module", "custom"]) \
or ["Core", False]
if custom:
_class = Document
else:
module = load_doctype_module(doctype, module_name)
classname = doctype.replace(" ", "").replace("-", "")
if hasattr(module, classname):
_class = getattr(module, classname)
if issubclass(_class, Document):
_class = getattr(module, classname)
else:
raise ImportError, doctype
else:
raise ImportError, doctype
_classes[doctype] = _class
return _classes[doctype]
class Document(BaseDocument):
"""All controllers inherit from `Document`."""
def __init__(self, arg1, arg2=None):
@ -112,9 +83,6 @@ class Document(BaseDocument):
# incorrect arguments. let's not proceed.
raise frappe.DataError("Document({0}, {1})".format(arg1, arg2))
if hasattr(self, "__setup__"):
self.__setup__()
self.dont_update_if_missing = []
def load_from_db(self):
@ -157,7 +125,7 @@ class Document(BaseDocument):
if not self.has_permission(permtype):
self.raise_no_permission_to(permlabel or permtype)
def has_permission(self, permtype):
def has_permission(self, permtype="read"):
"""Call `frappe.has_permission` if `self.ignore_permissions`
is not set.

View file

@ -211,10 +211,6 @@ class Meta(Document):
return fields
def is_print_hide(self, fieldname):
df = self.get_field(fieldname)
return df and (df.get("__print_hide") or df.print_hide)
doctype_table_fields = [
frappe._dict({"fieldname": "fields", "options": "DocField"}),
frappe._dict({"fieldname": "permissions", "options": "DocPerm"})

View file

@ -7,7 +7,7 @@ from frappe import _, msgprint
from frappe.utils import cint
rights = ("read", "write", "create", "delete", "submit", "cancel", "amend",
"print", "email", "report", "import", "export", "set_user_permissions")
"print", "email", "report", "import", "export", "set_user_permissions", "share")
def check_admin_or_system_manager(user=None):
if not user: user = frappe.session.user
@ -33,9 +33,17 @@ def has_permission(doctype, ptype="read", doc=None, verbose=True, user=None):
if user=="Administrator":
return True
def false_if_not_shared():
if doc and ptype in ("read", "write", "share"):
shared = frappe.db.get_shared(meta.name, user, [ptype])
if doc.name in shared:
return True
return False
role_permissions = get_role_permissions(meta, user=user)
if not role_permissions.get(ptype):
return False
return false_if_not_shared()
if doc:
if isinstance(doc, basestring):
@ -44,10 +52,10 @@ def has_permission(doctype, ptype="read", doc=None, verbose=True, user=None):
if role_permissions["apply_user_permissions"].get(ptype):
if not user_has_permission(doc, verbose=verbose, user=user,
user_permission_doctypes=role_permissions.get("user_permission_doctypes")):
return False
return false_if_not_shared()
if not has_controller_permissions(doc, ptype, user=user):
return False
return false_if_not_shared()
return True
@ -74,6 +82,11 @@ def get_doc_permissions(doc, verbose=False, user=None):
if role_permissions["apply_user_permissions"].get(ptype):
role_permissions[ptype] = 0
# update share permissions
role_permissions.update(frappe.db.get_value("DocShare",
{"share_type": doc.doctype, "share_name": doc.name, "user": user},
["read", "write", "share"], as_dict=True))
return role_permissions
def get_role_permissions(meta, user=None):

View file

@ -19,8 +19,8 @@ a,
.badge,
.btn,
.ui-menu .ui-menu-item {
transition: 0.2s;
-webkit-transition: 0.2s;
transition: background-color 0.2s;
-webkit-transition: background-color 0.2s;
}
a.disabled,
a.disabled:hover {

View file

@ -82,6 +82,7 @@ frappe.ui.form.PrintPreview = Class.extend({
},
preview: function() {
var me = this;
this.wrapper.find(".btn-print-edit").toggle(this.get_print_format().name);
this.get_print_html(function(html) {
me.wrapper.find(".print-format").html(html);
});

View file

@ -217,7 +217,9 @@ _f.Frm.prototype.set_value = function(field, value, if_missing) {
_set(field, value)
} else if($.isPlainObject(field)) {
$.each(field, function(f, v) {
_set(f, v);
if(me.get_field(f)) {
_set(f, v);
}
})
}
}

View file

@ -26,8 +26,8 @@ a,
.badge,
.btn,
.ui-menu .ui-menu-item {
transition: 0.2s;
-webkit-transition: 0.2s;
transition: background-color 0.2s;
-webkit-transition: background-color 0.2s;
}
a.disabled, a.disabled:hover {

42
frappe/share.py Normal file
View file

@ -0,0 +1,42 @@
# Copyright (c) 2015, Web Notes Technologies Pvt. Ltd. and Contributors
# MIT License. See license.txt
from __future__ import unicode_literals
import frappe
def add(doctype, name, user=None, read=1, write=0, share=0):
"""Share the given document with a user."""
if not user:
user = frappe.session.user
return frappe.get_doc({
"doctype": "DocShare",
"user": user,
"share_doctype": doctype,
"share_name": name,
"read": read,
"write": write,
"share": share
}).insert(ignore_permissions=True)
def set_permission(doctype, name, user, permission_to, remove=False):
"""Set share right."""
share_name = frappe.db.get_value("DocShare", {"user": user, "share_name": name,
"share_doctype": doctype})
if not share_name:
if not remove:
share = add(doctype, name, user, **{permission_to: 1})
else:
# no share found, nothing to remove
share = {}
pass
else:
share = frappe.get_doc("DocShare", share_name)
share.set(permission_to, 0 if remove else 1)
share.save()
if not (share.read or share.write or share.share):
share.delete()
share = {}
return share

View file

@ -32,22 +32,13 @@
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
padding: 10px;
padding: 6px;
font-size: 16px;
margin-bottom: 10px;
}
.form-signin .form-control:focus {
z-index: 2;
}
input#login_email, input#signup_fullname {
margin-bottom: -1px;
border-bottom-right-radius: 0;
border-bottom-left-radius: 0;
}
input#login_password, input#signup_email {
margin-bottom: 10px;
border-top-left-radius: 0;
border-top-right-radius: 0;
}
.btn-social {
margin: 10px;

View file

@ -68,7 +68,7 @@ def get_html(doc, name=None, print_format=None, meta=None,
meta = frappe.get_meta(doc.doctype)
jenv = frappe.get_jenv()
format_data = {}
format_data, format_data_map = [], {}
# determine template
if print_format in ("Standard", standard_format):
@ -76,7 +76,16 @@ def get_html(doc, name=None, print_format=None, meta=None,
else:
print_format = frappe.get_doc("Print Format", print_format)
if print_format.format_data:
# set format data
format_data = json.loads(print_format.format_data)
for df in format_data:
format_data_map[df.get("fieldname")] = df
if "visible_columns" in df:
for _df in df.get("visible_columns"):
format_data_map[_df.get("fieldname")] = _df
doc.format_data_map = format_data_map
template = "standard"
else:
template = jenv.from_string(get_print_format(doc.doctype,
@ -180,7 +189,7 @@ def make_layout(doc, meta, format_data=None):
if df.fieldtype=="HTML" and df.options:
doc.set(df.fieldname, True) # show this field
if is_visible(df) and has_value(df, doc):
if is_visible(df, doc) and has_value(df, doc):
page[-1][-1].append(df)
# if table, add the row info in the field
@ -208,9 +217,16 @@ def make_layout(doc, meta, format_data=None):
layout = [filter(lambda s: any(filter(lambda c: any(c), s)), page) for page in layout]
return layout
def is_visible(df):
no_display = ("Section Break", "Column Break", "Button")
return (df.fieldtype not in no_display) and not df.get("__print_hide") and not df.print_hide
def is_visible(df, doc):
"""Returns True if docfield is visible in print layout and does not have print_hide set."""
if df.fieldtype in ("Section Break", "Column Break", "Button"):
return False
if hasattr(doc, "hide_in_print_layout"):
if df.fieldname in doc.hide_in_print_layout:
return False
return not doc.is_print_hide(df.fieldname)
def has_value(df, doc):
value = doc.get(df.fieldname)
@ -251,15 +267,22 @@ def get_print_style(style=None):
def get_visible_columns(data, table_meta, df):
"""Returns list of visible columns based on print_hide and if all columns have value."""
columns = []
doc = data[0] or frappe.new_doc(df.options)
def add_column(col_df):
return is_visible(col_df, doc) \
and column_has_value(data, col_df.get("fieldname"))
if df.get("visible_columns"):
# columns specified by column builder
for col_df in df.get("visible_columns"):
# load default docfield properties
newdf = table_meta.get_field(col_df.get("fieldname")).as_dict().copy()
newdf.update(col_df)
columns.append(newdf)
if add_column(newdf):
columns.append(newdf)
else:
for col_df in table_meta.fields:
if is_visible(col_df) and column_has_value(data, col_df.get("fieldname")):
if add_column(col_df):
columns.append(col_df)
return columns

View file

@ -76,9 +76,10 @@
{%- endif -%}
{%- endmacro -%}
{%- macro print_value(df, doc, parent_doc=None) -%}
{% if doc.get_print_template(df.fieldname, parent_doc) %}
{% include doc.get_print_template(df.fieldname, parent_doc) %}
{%- macro print_value(df, doc, meta, parent_doc=None) -%}
{% if doc.print_templates and
doc.print_templates.get(df.fieldname) %}
{% include doc.print_templates[df.fieldname] %}
{% elif df.fieldtype=="Check" %}
<i class="{{ 'icon-check' if doc[df.fieldname] else 'icon-check-empty' }}"></i>
{% elif df.fieldtype=="Image" %}