|
1 # |
|
2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net> |
|
3 # All Rights Reserved. |
|
4 # |
|
5 # This software is subject to the provisions of the Zope Public License, |
|
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
|
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
|
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
|
10 # FOR A PARTICULAR PURPOSE. |
|
11 # |
|
12 |
|
13 __docformat__ = 'restructuredtext' |
|
14 |
|
15 # import standard library |
|
16 |
|
17 # import interfaces |
|
18 from pyams_skin.interfaces.container import ITable, ITableElementEditor |
|
19 from pyams_skin.layer import IPyAMSLayer |
|
20 from z3c.table.interfaces import IColumn |
|
21 from zope.container.interfaces import IContained |
|
22 |
|
23 # import packages |
|
24 from pyams_template.template import get_view_template, template_config |
|
25 from pyams_utils.adapter import ContextRequestViewAdapter, adapter_config |
|
26 from pyams_utils.url import absolute_url |
|
27 from pyramid.url import resource_url |
|
28 from z3c.table.column import Column, GetAttrColumn |
|
29 from z3c.table.table import Table |
|
30 from zope.interface import implementer, Interface |
|
31 |
|
32 from pyams_skin import _ |
|
33 |
|
34 |
|
35 @adapter_config(context=(IContained, IPyAMSLayer, ITable), provides=ITableElementEditor) |
|
36 class DefaultElementEditorAdapter(ContextRequestViewAdapter): |
|
37 """Default contained element editor""" |
|
38 |
|
39 view_name = 'properties.html' |
|
40 |
|
41 @property |
|
42 def url(self): |
|
43 return resource_url(self.context, self.request, self.view_name) |
|
44 |
|
45 modal_target = True |
|
46 |
|
47 |
|
48 def get_element_editor(table, element): |
|
49 """Get editor for selected element""" |
|
50 registry = table.request.registry |
|
51 return registry.queryMultiAdapter((element, table.request, table), ITableElementEditor) |
|
52 |
|
53 |
|
54 @template_config(template='templates/table.pt', layer=IPyAMSLayer) |
|
55 @implementer(ITable) |
|
56 class BaseTable(Table): |
|
57 """Custom table""" |
|
58 |
|
59 id = _("TableID") |
|
60 title = _("Container elements") |
|
61 |
|
62 cssClasses = {'table': 'table table-bordered table-striped table-hover table-tight datatable'} |
|
63 |
|
64 @property |
|
65 def data_attributes(self): |
|
66 return {'tr': {'id': lambda x: '{0}::{1}'.format(self.id, x.__name__), |
|
67 'data-ams-element-name': lambda x: x.__name__, |
|
68 'data-ams-url': lambda x: getattr(get_element_editor(self, x), 'url', ''), |
|
69 'data-toggle': lambda x: 'modal' if getattr(get_element_editor(self, x), 'modal_target', None) else None}} |
|
70 |
|
71 batchSize = 10000 |
|
72 startBatchingAt = 10000 |
|
73 |
|
74 def getBatchSize(self): |
|
75 return int(self.request.params.get(self.prefix + '-batchSize', self.batchSize)) |
|
76 |
|
77 def getBatchStart(self): |
|
78 return int(self.request.params.get(self.prefix + '-batchStart', self.batchStart)) |
|
79 |
|
80 def getSortOn(self): |
|
81 return self.request.params.get(self.prefix + '-sortOn', self.sortOn) |
|
82 |
|
83 def getSortOrder(self): |
|
84 return self.request.params.get(self.prefix + '-sortOrder', self.sortOrder) |
|
85 |
|
86 @staticmethod |
|
87 def check_data_attribute(attribute, source): |
|
88 if isinstance(attribute, str): |
|
89 return attribute |
|
90 elif callable(attribute): |
|
91 return attribute(source) |
|
92 else: |
|
93 return str(attribute) |
|
94 |
|
95 def get_data_attributes(self, element, source, column=None): |
|
96 attrs = self.data_attributes.get(element) |
|
97 if attrs: |
|
98 result = '' |
|
99 for key, value in attrs.items(): |
|
100 checked_value = self.check_data_attribute(value, source) |
|
101 if checked_value is not None: |
|
102 result += "{0}='{1}'".format(key, checked_value) |
|
103 return result |
|
104 else: |
|
105 return '' |
|
106 |
|
107 render = get_view_template() |
|
108 |
|
109 def renderTable(self): |
|
110 return super(BaseTable, self).renderTable() \ |
|
111 .replace('<table', '<table %s' % self.get_data_attributes('table', self)) |
|
112 |
|
113 def renderRow(self, row, cssClass=None): |
|
114 return super(BaseTable, self).renderRow(row, cssClass) \ |
|
115 .replace('<tr', '<tr %s' % self.get_data_attributes('tr', row[0][0])) |
|
116 |
|
117 def renderCell(self, item, column, colspan=0): |
|
118 return super(BaseTable, self).renderCell(item, column, colspan) \ |
|
119 .replace('<td', '<td %s' % self.get_data_attributes('td', item, column)) |
|
120 |
|
121 |
|
122 @adapter_config(name='name', context=(Interface, IPyAMSLayer, BaseTable), provides=IColumn) |
|
123 class NameColumn(GetAttrColumn): |
|
124 """Container column""" |
|
125 |
|
126 header = _("Name") |
|
127 attrName = '__name__' |
|
128 |
|
129 |
|
130 class ActionColumn(Column): |
|
131 """Base action icon column""" |
|
132 |
|
133 header = '' |
|
134 icon_class = 'fa fa-fw fa-search' |
|
135 icon_hint = _("Search") |
|
136 cssClasses = {'th': 'action', |
|
137 'td': 'action'} |
|
138 url = "#" |
|
139 target = '#content' |
|
140 modal_target = False |
|
141 |
|
142 def renderCell(self, item): |
|
143 translate = self.request.localizer.translate |
|
144 return '''<a class="hint" title="{title}" href="{url}" |
|
145 data-ams-target="{target}" {modal} data-ams-hint-gravity="se"> |
|
146 <i class="{icon_class}"></i> |
|
147 </a>'''.format(title=translate(self.icon_hint), |
|
148 url=self.get_url(item), |
|
149 target=self.target, |
|
150 modal='data-toggle="modal"' if self.modal_target else '', |
|
151 icon_class=self.icon_class) |
|
152 |
|
153 def get_url(self, item): |
|
154 return absolute_url(item, self.request, self.url) |