1 | |
---|
2 | /** |
---|
3 | * General use custom tags. |
---|
4 | * Some are taken from http://www.grails.org/Contribute+a+Tag#checkBoxList |
---|
5 | */ |
---|
6 | class CustomTagLib { |
---|
7 | static namespace = 'custom' |
---|
8 | |
---|
9 | def resources = { attrs -> |
---|
10 | ///@todo: should include our javascript and do setup here. |
---|
11 | } |
---|
12 | |
---|
13 | /** |
---|
14 | * Checkbox list that can be used as a more user-friendly alternative to a multiselect list box. |
---|
15 | * Usage: |
---|
16 | * To map the selected ids to corresponding domain objects, |
---|
17 | * an additional set method is required in the containing domain class: |
---|
18 | * // This additional setter is used to convert the checkBoxList string or string array |
---|
19 | * // of ids selected to the corresponding domain objects. |
---|
20 | * public void setAssetSubItemsFromCheckBoxList(ids) { |
---|
21 | * def idList = [] |
---|
22 | * if(ids instanceof String) { |
---|
23 | * if(ids.isInteger()) |
---|
24 | * idList << ids.toInteger() |
---|
25 | * } |
---|
26 | * else { |
---|
27 | * ids.each() { |
---|
28 | * if(it.isInteger()) |
---|
29 | * idList << it.toInteger() |
---|
30 | * } |
---|
31 | * } |
---|
32 | * this.assetSubItems = idList.collect { AssetSubItem.get( it ) } |
---|
33 | * } |
---|
34 | * |
---|
35 | * Then a line in the controller: |
---|
36 | * assetInstance.setAssetSubItemsFromCheckBoxList(params.assetSubItems) |
---|
37 | * |
---|
38 | * Fields: |
---|
39 | * name - the property name. |
---|
40 | * from - the list to select from. |
---|
41 | * value - the current value. |
---|
42 | * optionKey - the key to use. |
---|
43 | * sortBy - (optional) the attribute to sort the from list by. |
---|
44 | * displayFields - (optional) defaults to the objects toString() |
---|
45 | * displayFieldsSeparator - (optional) defaults to a space. |
---|
46 | * linkController - (optional, requires linkAction.) the controller to use for a link to the objects in the checkBoxList. |
---|
47 | * linkAction - (optional, requires linkController.) the action to use for a link to the objects in the checkBoxList. |
---|
48 | * |
---|
49 | * Example: |
---|
50 | * <!-- |
---|
51 | * <custom:checkBoxList name="assetSubItems" |
---|
52 | * from="${AssetSubItem.list()}" |
---|
53 | * value="${assetInstance?.assetSubItems.collect{it.id}}" |
---|
54 | * optionKey="id" |
---|
55 | * sortBy="description" |
---|
56 | * displayFields="['id', 'name']" |
---|
57 | * displayFieldsSeparator=', ' |
---|
58 | * linkController="assetSubItemDetailed" |
---|
59 | * linkAction="show"/> |
---|
60 | * --> |
---|
61 | * |
---|
62 | */ |
---|
63 | |
---|
64 | def checkBoxList = {attrs, body -> |
---|
65 | |
---|
66 | def from = attrs.from |
---|
67 | def value = attrs.value |
---|
68 | def cname = attrs.name |
---|
69 | def isChecked, ht, wd, style, html |
---|
70 | |
---|
71 | def sortBy = attrs.sortBy |
---|
72 | def displayFields = attrs.displayFields |
---|
73 | def displayFieldsSeparator = attrs.displayFieldsSeparator ?: ' ' |
---|
74 | def linkController = attrs.linkController |
---|
75 | def linkAction = attrs.linkAction |
---|
76 | |
---|
77 | def displayValue = " " |
---|
78 | |
---|
79 | // sets the style to override height and/or width if either of them |
---|
80 | // is specified, else the default from the CSS is taken |
---|
81 | style = "style='" |
---|
82 | if(attrs.height) |
---|
83 | style += "height:${attrs.height};" |
---|
84 | if(attrs.width) |
---|
85 | style += "width:${attrs.width};" |
---|
86 | if(style.length() == "style='".length()) |
---|
87 | style = "" |
---|
88 | else |
---|
89 | style += "'" // closing single quote |
---|
90 | |
---|
91 | html = "<ul class='CheckBoxList' " + style + ">" |
---|
92 | |
---|
93 | out << html |
---|
94 | |
---|
95 | if(sortBy) |
---|
96 | from.sort { p1, p2 -> p1[sortBy].compareToIgnoreCase(p2[sortBy]) } |
---|
97 | |
---|
98 | from.each { obj -> |
---|
99 | |
---|
100 | displayValue = " " |
---|
101 | |
---|
102 | if(linkController && linkAction) |
---|
103 | displayValue += "<a href=\"${createLink(controller: linkController, action: linkAction, id: obj.id).encodeAsHTML()}\">" |
---|
104 | |
---|
105 | if(displayFields) { |
---|
106 | displayValue += displayFields.collect { obj[it] }.join(displayFieldsSeparator) |
---|
107 | } |
---|
108 | else displayValue += obj // use the obj's default toString() |
---|
109 | |
---|
110 | if(linkController && linkAction) |
---|
111 | displayValue += "</a>" |
---|
112 | |
---|
113 | // if we wanted to select the checkbox using a click anywhere on the label (also hover effect) |
---|
114 | // but grails does not recognize index suffix in the name as an array: |
---|
115 | // cname = "${attrs.name}[${idx++}]" |
---|
116 | // and put this inside the li: <label for='$cname'>...</label> |
---|
117 | |
---|
118 | isChecked = (value?.contains(obj."${attrs.optionKey}"))? true: false |
---|
119 | |
---|
120 | out << "<li>" << checkBox(name:cname, value:obj."${attrs.optionKey}", checked: isChecked) << displayValue << "</li>" |
---|
121 | } |
---|
122 | |
---|
123 | out << "</ul>" |
---|
124 | |
---|
125 | } // checkBoxList |
---|
126 | |
---|
127 | def sortableColumnWithImg = { attrs, body -> |
---|
128 | def writer = out |
---|
129 | if(!attrs.property) |
---|
130 | throwTagError("Tag [sortableColumn] is missing required attribute [property]") |
---|
131 | |
---|
132 | // if(!attrs.title && !attrs.titleKey) |
---|
133 | // throwTagError("Tag [sortableColumn] is missing required attribute [title] or [titleKey]") |
---|
134 | |
---|
135 | def property = attrs.remove("property") |
---|
136 | def action = attrs.action ? attrs.remove("action") : (actionName ?: "list") |
---|
137 | |
---|
138 | def defaultOrder = attrs.remove("defaultOrder") |
---|
139 | if(defaultOrder != "desc") defaultOrder = "asc" |
---|
140 | |
---|
141 | // current sorting property and order |
---|
142 | def sort = params.sort |
---|
143 | def order = params.order |
---|
144 | |
---|
145 | // add sorting property and params to link params |
---|
146 | def linkParams = [:] |
---|
147 | if(params.id) linkParams.put("id",params.id) |
---|
148 | if(attrs.params) linkParams.putAll(attrs.remove("params")) |
---|
149 | linkParams.sort = property |
---|
150 | |
---|
151 | // determine and add sorting order for this column to link params |
---|
152 | attrs.class = (attrs.class ? "${attrs.class} sortable" : "sortable") |
---|
153 | if(property == sort) { |
---|
154 | attrs.class = attrs.class + " sorted " + order |
---|
155 | if(order == "asc") |
---|
156 | linkParams.order = "desc" |
---|
157 | else |
---|
158 | linkParams.order = "asc" |
---|
159 | } |
---|
160 | else |
---|
161 | linkParams.order = defaultOrder |
---|
162 | |
---|
163 | // determine column title |
---|
164 | // def title = attrs.remove("title") |
---|
165 | // def titleKey = attrs.remove("titleKey") |
---|
166 | // if(titleKey) { |
---|
167 | // if(!title) title = titleKey |
---|
168 | // def messageSource = grailsAttributes.getApplicationContext().getBean("messageSource") |
---|
169 | // def locale = RCU.getLocale(request) |
---|
170 | // |
---|
171 | // title = messageSource.getMessage(titleKey, null, title, locale) |
---|
172 | // } |
---|
173 | |
---|
174 | // Image. |
---|
175 | def img = "<img " |
---|
176 | def imgAttrs = [:] |
---|
177 | imgAttrs.src = attrs.remove("imgSrc") |
---|
178 | imgAttrs.alt = attrs.remove("imgAlt") |
---|
179 | imgAttrs.title = attrs.remove("imgTitle") |
---|
180 | imgAttrs.each { k, v -> |
---|
181 | if(v) |
---|
182 | img += "${k}=\"${v.encodeAsHTML()}\" " |
---|
183 | } |
---|
184 | img += "/>" |
---|
185 | |
---|
186 | writer << "<th " |
---|
187 | |
---|
188 | // process remaining attributes |
---|
189 | attrs.each { k, v -> |
---|
190 | writer << "${k}=\"${v.encodeAsHTML()}\" " |
---|
191 | } |
---|
192 | writer << ">${link(action:action, params:linkParams) { img } }" |
---|
193 | writer << "</th>" |
---|
194 | |
---|
195 | } // sortableColumnWithImg |
---|
196 | |
---|
197 | /** |
---|
198 | * Customised version of jasperButton as found in jaser plugin. |
---|
199 | * custom:jasperButtons is intended to be wrapped by g:jasperForm |
---|
200 | */ |
---|
201 | def jasperButtons = {attrs -> |
---|
202 | if(!attrs['format']){throw new Exception(message(code:"jasper.taglib.missingAttribute", args:'format'))} |
---|
203 | if(!attrs['jasper']){throw new Exception(message(code:"jasper.taglib.missingAttribute", args:'jasper'))} |
---|
204 | String jasper = attrs['jasper'] |
---|
205 | String buttonClass = attrs['class'] ?: "jasperButton" |
---|
206 | String format = attrs['format'].toUpperCase() |
---|
207 | String text = attrs['text'] |
---|
208 | String heightAttr = attrs['height'] ? ' height="' + attrs['height'] + '"' : '' // leading space on purpose |
---|
209 | String imgSrc = '' |
---|
210 | String delimiter = attrs['delimiter'] ?: "|" |
---|
211 | String delimiterBefore = attrs['delimiterBefore'] ?: delimiter |
---|
212 | String delimiterAfter = attrs['delimiterAfter'] ?: delimiter |
---|
213 | |
---|
214 | out << ''' |
---|
215 | <script type="text/javascript"> |
---|
216 | function submit_jasperForm(name, fmt) { |
---|
217 | var jasperForm = document.getElementsByName(name).item(0) |
---|
218 | jasperForm._format.value = fmt; |
---|
219 | jasperForm.submit(); |
---|
220 | return false; |
---|
221 | } |
---|
222 | </script> |
---|
223 | ''' |
---|
224 | |
---|
225 | out << delimiterBefore |
---|
226 | |
---|
227 | attrs['format'].toUpperCase().split(",").eachWithIndex { it, i -> |
---|
228 | if (i > 0) out << delimiter |
---|
229 | imgSrc = g.resource(plugin:"jasper", dir:'images/icons', file:"${it.trim()}.gif") |
---|
230 | def fmt = it.trim() |
---|
231 | out << """ |
---|
232 | <a href="#" class="${buttonClass}" title="${it.trim()}" onClick="return submit_jasperForm('${jasper}', '${fmt}')"> |
---|
233 | <img border="0" src="${imgSrc}"${heightAttr} /></a> |
---|
234 | """ |
---|
235 | } |
---|
236 | |
---|
237 | out << delimiterAfter |
---|
238 | } // jasperButtons |
---|
239 | |
---|
240 | } // end class |
---|