1: <?php
2:
3: App::import('Lib', 'Base.ModelTraverser');
4: App::uses('ViewUtilHelper', 'ExtendedScaffold.View/Helper');
5:
6: class ListsHelper extends AppHelper {
7:
8: public $helpers = array(
9: 'Widgets.ControllerMenu',
10: 'Paginator',
11: 'Base.PaginatorUtil',
12: 'AccessControl.AccessControl',
13: 'ExtendedScaffold.ViewUtil',
14: 'Base.CakeLayers',
15: );
16: private $model;
17: private $controller;
18: private $showActions;
19:
20: private $options = array();
21:
22: private $defaultOptions = array(
23: 'paginatorPrevText' => '<< ',
24: 'paginatorNextText' => ' >>',
25: 'paginatorNumberSeparator' => ' | ',
26: 'paginatorHiddenDisabled' => true,
27: 'rowActionListOptions' => array()
28: );
29:
30: 31: 32: 33:
34: private $defaultField = array(
35: 'path' => null,
36: 'emptyValue' => ' ',
37: 'label' => null,
38: 'type' => null,
39: 'align' => null,
40: 'valueFunction' => null,
41: 'extraData' => null,
42: 'staticValue' => null,
43: 'mask' => null,
44: 'link' => null,
45: 'accessObjectType' => null,
46: 'accessObject' => null,
47: );
48:
49: public function listElement($fields, $rows, $options = array()) {
50: $this->_setup($options);
51: $b = $this->PaginatorUtil->filterForm();
52: $b .= $this->paginatorInfo();
53: $b .= $this->rowsTable($fields, $rows, $options);
54: $b .= $this->paginatorInfo();
55: return $b;
56: }
57:
58: public function rowsTable($fields, $rows, $options = array()) {
59: $this->_setup($options);
60: $fields = $this->_extractFields($fields);
61: $b = "<table";
62: foreach ($this->htmlAttributes as $key => $value) {
63: $b .= " $key=\"$value\"";
64: }
65: $b .= ">\n";
66: $b .= $this->_header($fields);
67: if (empty($rows)) {
68: $b .= "\t<tr><td colspan='100%' style='text-align: center'><em>Nenhum registro foi encontrado.</em></td></tr>\n";
69: } else {
70: $i = 0;
71: foreach ($rows as $row) {
72: $options['position'] = $i == 0 ? 'top' :
73: ($i == count($rows) - 1 ? 'bottom' : 'middle');
74: $b .= $this->_rowLine($fields, $row, $i++, $options);
75: }
76: }
77: $b .= "\n";
78: $b .= '</table>';
79:
80: return $b;
81: }
82:
83: private function ($fields) {
84: $columns = array();
85: if ($this->showOrderNumbers) {
86: $columns[] = '#';
87: }
88: foreach ($fields as $_field) {
89: $columns[] = $this->_fieldLabel($_field);
90: }
91: if ($this->showActions) {
92: $columns[] = __d('extended_scaffold','Actions');
93: }
94:
95: $b = "\t<thead><tr>\n";
96: for ($i = 0; $i < count($columns); ++$i) {
97: if ($i == 0) {
98: $class = 'left';
99: }
100: else if ($i == count($columns) - 1) {
101: $class = 'right';
102: }
103: else {
104: $class = 'center';
105: }
106:
107: $b.= "\t\t<th scope='col' class='$class'>\n";
108: $b .= $columns[$i];
109: $b.= "\t\t</th>\n";
110: }
111: $b .= "\t</tr></thead>\n";
112: return $b;
113: }
114:
115: public function rowLine($fields, $row, $rowIndex, $tableOptions = array(), $rowOptions = array()) {
116: $this->_setup($tableOptions);
117: return $this->_rowLine($this->_extractFields($fields), $row, $rowIndex, $rowOptions);
118: }
119:
120: public function _setup($options) {
121: if (!empty($options['controller'])) {
122: $this->controller = $this->CakeLayers->getController($options['controller']);
123: } else if (!empty($options['model'])) {
124: $this->controller = $this->CakeLayers->getControllerByModel($options['model']);
125: } else {
126: $this->controller = $this->CakeLayers->getController();
127: }
128:
129: if (isset($options['model'])) {
130: if ($options['model']) {
131: $this->model = $this->CakeLayers->getModel($options['model']);
132: } else {
133: $this->model = false;
134: }
135: } else {
136: $this->model = $this->CakeLayers->getControllerDefaultModel($this->controller->name);
137: }
138:
139: $this->firstColumnViewLink = isset($options['firstColumnViewLink']) ? $options['firstColumnViewLink'] : true;
140: $this->htmlAttributes = isset($options['htmlAttributes']) ? $options['htmlAttributes'] : array();
141: $this->showActions = (isset($options['showActions']) ? $options['showActions'] : true);
142: $this->showOrderNumbers = (isset($options['showOrderNumbers']) ? $options['showOrderNumbers'] : false);
143: $this->options = $options + $this->defaultOptions;
144: }
145:
146: public function paginatorInfo() {
147: $b = '<div class="paging">';
148: if ($this->Paginator->hasPrev() || !$this->options['paginatorHiddenDisabled']) {
149: $b .= "\t" . $this->Paginator->prev(
150: $this->options['paginatorPrevText'],
151: array(),
152: null,
153: array('class' => 'disabled')
154: ) . "\n";
155: $b .= $this->options['paginatorNumberSeparator'];
156: }
157: $b .= $this->Paginator->numbers(array('modulus' => 4, 'first' => 2, 'last' => 2, 'separator' => $this->options['paginatorNumberSeparator'])) . "\n";
158: if ($this->Paginator->hasNext() || !$this->options['paginatorHiddenDisabled']) {
159: $b .= $this->options['paginatorNumberSeparator'];
160: $b .= "\t" . $this->Paginator->next(
161: $this->options['paginatorNextText'],
162: array(),
163: null,
164: array('class' => 'disabled')
165: ) . "\n";
166: }
167:
168: $b .= "<span class='counter'>";
169: $b .= $this->Paginator->counter(array(
170: 'format' => __d('extended_scaffold','({:page}-{:pages}/{:count})')
171: ));
172: $b .= "</span>";
173: $b .= '</div>';
174:
175: return $b;
176: }
177:
178: private function ($fields) {
179: $result = array();
180:
181: foreach ($fields as $key => $value) {
182: $field = $this->_extractField($key, $value);
183: if ($this->_hasFieldAccess($field)) {
184: $result[] = $field;
185: }
186: }
187:
188: return $result;
189: }
190:
191: private function _hasFieldAccess($extractedField) {
192: if ($extractedField['accessObject']) {
193: return $extractedField['accessObjectType'] ?
194: AccessControlComponent::sessionUserHasAccess(
195: $extractedField['accessObject']
196: , $extractedField['accessObjectType']
197: ) :
198: AccessControlComponent::sessionUserHasAccess(
199: $extractedField['accessObject']
200: );
201: } else {
202: return true;
203: }
204: }
205:
206: private function ($key, $value) {
207: $field = is_array($value) ?
208: array_merge(array('name' => $key), $value) :
209: array('name' => $value);
210: $field = Hash::merge($this->defaultField, $field);
211: $field['association'] = $this->_fieldAssociation($field['name']);
212: list($field['path'], $model) = $this->_fieldPathModel($field['name']);
213: $field['type'] = $this->_fieldType($field['path'], $model);
214: return $field;
215: }
216:
217: private function _fieldPathModel($fieldName) {
218: $nameParts = explode('.', $fieldName);
219: if (count($nameParts) == 1) {
220: if ($this->model) {
221: $path = array(
222: $this->model->alias,
223: $nameParts[0]
224: );
225: $model = &$this->model;
226: } else {
227: $path = array($nameParts[0]);
228: $model = false;
229: }
230: } else {
231: $path = $nameParts;
232: $model = &$this->model->{$nameParts[0]};
233: }
234: return array($path, $model);
235: }
236:
237: 238: 239: 240: 241: 242:
243: private function _fieldType($path, $model) {
244: if ($model) {
245: $modelSchema = $model->schema();
246: if (!empty($modelSchema[$path[1]])) {
247: return $modelSchema[$path[1]]['type'];
248: } else if (!empty($model->virtualFieldsSchema[$path[1]]['type'])) {
249: return $model->virtualFieldsSchema[$path[1]]['type'];
250: }
251: } else {
252: return 'string';
253: }
254: }
255:
256: private function _fieldAssociation($field) {
257: $associations = $this->_modelAssociations();
258: if (!empty($associations['belongsTo'])) {
259: foreach ($associations['belongsTo'] as $_alias => $_details) {
260: if ($field === $_details['foreignKey']) {
261: return $_details + array('alias' => $_alias);
262: }
263: }
264: }
265:
266: return null;
267: }
268:
269: private function _rowLine($fields, $row, $rowIndex, $options = array()) {
270:
271: $class = array();
272: if ($rowIndex % 2 == 0) {
273: $class[] = 'altrow';
274: }
275:
276: if (!empty($options['position'])) {
277: $class[] = $options['position'];
278: }
279:
280: if (!empty($class)) {
281: $class = ' class="' . implode(' ', $class) . '"';
282: } else {
283: $class = '';
284: }
285:
286: $b = "\n\t<tr{$class}";
287:
288: if (!empty($options['htmlAttributes']) && is_array($options['htmlAttributes'])) {
289: foreach ($options['htmlAttributes'] as $key => $value) {
290: $b .= " $key='$value'";
291: }
292: }
293:
294: $b .= ">\n";
295:
296: $cells = array();
297:
298: if ($this->showOrderNumbers) {
299: $cells[] = array(
300: 'attributes' => array(
301: 'style' => 'text-align: center'
302: ),
303: 'content' => ($this->_currentPageFirstRowIndex() + $rowIndex + 1)
304: );
305: }
306:
307: $first = true;
308: foreach ($fields as $field) {
309: $cells[] = array(
310: 'attributes' => array(
311: 'style' => 'text-align: '.$this->_fieldValueDefaultAlign($field),
312: ),
313: 'content' => $this->_rowFieldValue($field, $row, $first)
314: );
315: $first = false;
316: }
317:
318: if ($this->showActions) {
319: $rowActionListOptions = $this->options['rowActionListOptions'];
320: if (!empty($this->options['model']) && empty($rowActionListOptions['model'])) {
321: $rowActionListOptions['model'] = $this->options['model'];
322: }
323: $rowActionListOptions['controller'] = $this->controller->name;
324: $cells[] = array(
325: 'attributes' => array(
326: 'class' => array('actions')
327: ),
328: 'content' => $this->ControllerMenu->instanceMenu($row, $rowActionListOptions)
329: );
330: }
331:
332: for ($i = 0; $i < count($cells); ++$i) {
333: if ($i == 0) {
334: $cells[$i]['attributes']['class'][] = 'left';
335: } else if ($i == count($cells) - 1) {
336: $cells[$i]['attributes']['class'][] = 'right';
337: } else {
338: $cells[$i]['attributes']['class'][] = 'center';
339: }
340:
341: $b .= "\t\t<td{$this->_parseAttributes($cells[$i]['attributes'])}>\n";
342: $b .= $cells[$i]['content'];
343: $b.= "\t\t</td>\n";
344: }
345: $b .= "\t</tr>\n";
346:
347: return $b;
348: }
349:
350: private function _fieldValueDefaultAlign($field) {
351: if (empty($field['align'])) {
352: if (empty($field['association'])) {
353: switch ($field['type']) {
354: case 'boolean':
355: return 'center';
356: break;
357: case 'integer';
358: case 'float';
359: return 'right';
360: break;
361:
362: case 'string':
363: default:
364: return 'left';
365: }
366: } else {
367: return 'center';
368: }
369: } else {
370: return $field['align'];
371: }
372: }
373:
374: private function _modelAssociations() {
375: if ($this->model) {
376: return $this->CakeLayers->getModelAssociations($this->model);
377: } else {
378: return array();
379: }
380: }
381:
382: public function _fieldLabel($field) {
383: $title = null;
384: if (!empty($field['label'])) {
385: $title = $field['label'];
386: }
387:
388: $key = $field['path'][count($field['path']) - 1];
389:
390: if ($this->model) {
391: if (!empty($this->params['paging'][$this->model->name])) {
392: if (empty($title)) {
393: return $this->Paginator->sort($key);
394: } else {
395: return $this->Paginator->sort($title, $key);
396: }
397: }
398: }
399: if (empty($title)) {
400: $title = Inflector::humanize($key);
401: }
402: return __d('extended_scaffold',$title, true);
403: }
404:
405: private function _rowFieldValue($field, $row, $firstField) {
406: $link = $value = null;
407:
408: if ($field['valueFunction']) {
409: $value = $this->_formatValueByFunction($field, $row);
410: } else if ($field['staticValue'] !== null) {
411: $value = $field['staticValue'];
412: } else {
413: if (!empty($field['association'])) {
414: if (!empty($row[$this->model->alias][$field['association']['foreignKey']])) {
415: $link = $this->_associationLinkUrl($field, $row);
416: }
417: } else {
418: if ($this->model && $firstField && $this->firstColumnViewLink) {
419: $link = $this->_selfLinkUrl($row);
420: }
421: }
422:
423: if ($this->model) {
424: $value = ModelTraverser::displayValue($this->model, $row, $field['path']);
425: }
426: else {
427: $value = ArrayUtil::arrayIndex($row, $field['path']);
428: }
429:
430: if (is_array($value)) {
431: $value = $this->_formatValueAsList($value);
432: } else {
433: switch ($field['type']) {
434: case ViewUtilHelper::VALUE_TYPE_BOOLEAN:
435: $value = $this->ViewUtil->yesNo($value);
436: break;
437:
438: default:
439: $value = $this->ViewUtil->autoFormat($value);
440: }
441: }
442: }
443:
444: if (empty($value)) {
445: return $field['emptyValue'];
446: } else if (empty($link)) {
447: return $value;
448: } else {
449: return $this->AccessControl->linkOrText($value, $link);
450: }
451: }
452:
453: 454: 455: 456: 457: 458:
459: private function _associationLinkUrl($field, $row) {
460: return array(
461: 'controller' => $field['association']['controller']
462: , 'action' => 'view'
463: , $row[$this->model->alias][$field['association']['foreignKey']]
464: );
465: }
466:
467: private function _selfLinkUrl($row) {
468: if (!$this->controller) {
469: return null;
470: }
471: $id = !empty($row[$this->model->alias][$this->model->primaryKey]) ?
472: $row[$this->model->alias][$this->model->primaryKey] :
473: $row[$this->model->primaryKey];
474: return array(
475: 'controller' => Inflector::underscore($this->controller->name),
476: 'action' => 'view',
477: $id,
478: );
479: }
480:
481: private function _formatValueAsList($value) {
482: if (empty($value)) {
483: return '';
484: } else {
485: $b = '<ul>';
486: foreach ($value as $item) {
487: $b .= '<li>' . $item . '</li>';
488: }
489:
490: $b .= '</ul>';
491: return $b;
492: }
493: }
494:
495: public function _formatValueByFunction($field, $row) {
496: return call_user_func($field['valueFunction'], $this->CakeLayers->getCurrentView(), $row, $field);
497: }
498:
499: private function _currentPageFirstRowIndex() {
500: if (isset($this->params['paging'][$this->model->name])) {
501: $paging = $this->params['paging'][$this->model->name];
502: return ($paging['page'] - 1) * ($paging['options']['limit']);
503: } else {
504: return 0;
505: }
506: }
507:
508: }
509:
510: ?>
511: