1: <?php
2:
3: /**
4: * Represents a column for a QHtmlTable. Different subclasses (see below) allow accessing and fetching the data
5: * for each cells in a variety of ways
6: *
7: * @property string $Name name of the column
8: * @property string $CssClass CSS class of the column. This will be applied to every cell in the column. Use ColStyper
9: * to set the class for the actual 'col' tag if using col tags.
10: * @property string $HeaderCssClass CSS class of the column's cells when it's rendered in a table header
11: * @property boolean $HtmlEntities if true, cell values will be converted using htmlentities()
12: * @property boolean $RenderAsHeader if true, all cells in the column will be rendered with a <<th>> tag instead of <<td>>
13: * @property integer $Id HTML id attribute to put in the col tag
14: * @property integer $Span HTML span attribute to put in the col tag
15: * @property-read QHtmlTableBase $ParentTable parent table of the column
16: * @property-write QHtmlTableBase $_ParentTable Parent table of this column
17: * @property-write callable $CellParamsCallback A callback to set the html parameters of a generated cell
18: * @property boolean $Visible Whether the column will be drawn. Defaults to true.
19: * @property-read QTagStyler $CellStyler The tag styler for the cells in the column
20: * @property-read QTagStyler $HeaderCellStyler The tag styler for the header cells in the column
21: * @property-read QTagStyler $ColStyler The tag styler for the col tag in the column
22: */
23: abstract class QAbstractHtmlTableColumn extends QBaseClass {
24: /** @var string */
25: protected $strName;
26: /** @var string */
27: protected $strCssClass = null;
28: /** @var string */
29: protected $strHeaderCssClass = null;
30: /** @var boolean */
31: protected $blnHtmlEntities = true;
32: /** @var boolean */
33: protected $blnRenderAsHeader = false;
34: /** @var QHtmlTableBase */
35: protected $objParentTable = null;
36: /** @var integer */
37: protected $intSpan = 1;
38: /** @var string optional id for column tag rendering and datatables */
39: protected $strId = null;
40: /** @var bool Easy way to hide a column without removing the column. */
41: protected $blnVisible = true;
42: /** @var Callable Callback to modify the html attributes of the generated cell. */
43: protected $cellParamsCallback = null;
44: /** @var QTagStyler Styles for each cell. Usually this should be done in css for efficient code generation. */
45: protected $objCellStyler;
46: /** @var QTagStyler Styles for each header cell. Usually this should be done in css for efficient code generation. */
47: protected $objHeaderCellStyler;
48: /** @var QTagStyler Styles for each col. Usually this should be done in css for efficient code generation. */
49: protected $objColStyler;
50:
51:
52:
53: /**
54: * @param string $strName Name of the column
55: */
56: public function __construct($strName) {
57: $this->strName = $strName;
58: }
59:
60: /**
61: *
62: * Render the header cell including opening and closing tags.
63: *
64: * This will be called by the data table if ShowHeader is on, and will only
65: * be called for the top line item.
66: *
67: */
68: public function RenderHeaderCell() {
69: if (!$this->blnVisible) return '';
70:
71: $cellValue = $this->FetchHeaderCellValue();
72: if ($this->blnHtmlEntities)
73: $cellValue = QApplication::HtmlEntities($cellValue);
74: if ($cellValue == '' && QApplication::IsBrowser(QBrowserType::InternetExplorer)) {
75: $cellValue = ' ';
76: }
77:
78: return QHtml::RenderTag('th', $this->GetHeaderCellParams(), $cellValue);
79: }
80:
81: /**
82: * Returns the text to print in the header cell, if one is to be drawn. Override if you want
83: * something other than the default.
84: */
85: public function FetchHeaderCellValue() {
86: return $this->strName;
87: }
88:
89: /**
90: * Returns an array of key/value pairs to insert as parameters in the header cell. Override and add
91: * more if you need them.
92: * @return array
93: */
94: public function GetHeaderCellParams () {
95: $aParams['scope'] = 'col';
96: if ($this->strHeaderCssClass) {
97: $aParams['class'] = $this->strHeaderCssClass;
98: }
99: if ($this->objHeaderCellStyler) {
100: $aParams = $this->objHeaderCellStyler->GetHtmlAttributes($aParams);
101: }
102: return $aParams;
103: }
104:
105: /**
106: * Render a cell.
107: * Called by data table for each cell. Override and call with $blnHeader = true if you want
108: * this individual cell to render with <<th>> tags instead of <<td>>.
109: *
110: * @param mixed $item
111: * @param boolean $blnAsHeader
112: *
113: * @return string
114: */
115: public function RenderCell($item, $blnAsHeader = false) {
116: if (!$this->blnVisible) return '';
117:
118: $cellValue = $this->FetchCellValue($item);
119: if ($this->blnHtmlEntities)
120: $cellValue = QApplication::HtmlEntities($cellValue);
121: if ($cellValue == '' && QApplication::IsBrowser(QBrowserType::InternetExplorer)) {
122: $cellValue = ' ';
123: }
124:
125: if ($blnAsHeader || $this->blnRenderAsHeader) {
126: $strTag = 'th';
127: } else {
128: $strTag = 'td';
129: }
130:
131: return QHtml::RenderTag($strTag, $this->GetCellParams($item), $cellValue);
132: }
133:
134: /**
135: * Return a key/val array of items to insert inside the cell tag.
136: * Handles class, style, and id already. Override to add additional items, like an onclick handler.
137: *
138: * @param mixed $item
139: *
140: * @return array
141: */
142: protected function GetCellParams ($item) {
143: $aParams = array();
144:
145: if ($strClass = $this->GetCellClass ($item)) {
146: $aParams['class'] = $strClass;
147: }
148:
149: if ($strId = $this->GetCellId ($item)) {
150: $aParams['id'] = $strId;
151: }
152:
153: if ($this->blnRenderAsHeader) {
154: // assume this means it is a row header
155: $aParams['scope'] = 'row';
156: }
157:
158: $strStyle = $this->GetCellStyle ($item);
159:
160: if ($this->objCellStyler) {
161: $aStyles = null;
162: if ($strStyle) {
163: $aStyles = explode (';', $strStyle);
164: }
165: $aParams = $this->objCellStyler->GetHtmlAttributes($aParams, $aStyles);
166: } elseif ($strStyle) {
167: $aParams['style'] = $strStyle;
168: }
169:
170: if ($this->cellParamsCallback) {
171: $a = call_user_func($this->cellParamsCallback, $item);
172: $aParams = array_merge ($aParams, $a);
173: }
174:
175: return $aParams;
176: }
177:
178: /**
179: * Return the class of the cell.
180: *
181: * @param mixed $item
182: *
183: * @return string
184: */
185: protected function GetCellClass ($item) {
186: if ($this->strCssClass) {
187: return $this->strCssClass;
188: }
189: return '';
190: }
191:
192: /**
193: * Return the id of the cell.
194: *
195: * @param mixed $item
196: *
197: * @return string
198: */
199: protected function GetCellId ($item) {
200: return '';
201: }
202:
203: /**
204: * Return the style string for the cell.
205: *
206: * @param mixed $item
207: *
208: * @return string
209: */
210: protected function GetCellStyle ($item) {
211: return '';
212: }
213:
214: /**
215: * Return the raw string that represents the cell value.
216: *
217: * @param mixed $item
218: */
219: abstract public function FetchCellValue($item);
220:
221: /**
222: * Render the column tag.
223: * This special tag can control specific features of columns, but is generally optional on a table.
224: *
225: * @return string
226: */
227: public function RenderColTag() {
228: return QHtml::RenderTag('col', $this->GetColParams(), null, true);
229: }
230:
231: /**
232: * Return a key/value array of parameters to put in the col tag.
233: * Override to add parameters.
234: */
235: protected function GetColParams () {
236: $aParams = array();
237: if ($this->intSpan > 1) {
238: $aParams['span'] = $this->intSpan;
239: }
240: if ($this->strId) {
241: $aParams['id'] = $this->strId;
242: }
243:
244: if ($this->objColStyler) {
245: $aParams = $this->objColStyler->GetHtmlAttributes($aParams);
246: }
247:
248: return $aParams;
249: }
250:
251: /**
252: * Prepare to serialize references to the form.
253: */
254: public function Sleep() {
255: $this->cellParamsCallback = QControl::SleepHelper($this->cellParamsCallback);
256: }
257:
258: /**
259: * The object has been unserialized, so fix up pointers to embedded objects.
260: * @param QForm $objForm
261: */
262: public function Wakeup(QForm $objForm) {
263: $this->cellParamsCallback = QControl::WakeupHelper($objForm, $this->cellParamsCallback);
264: }
265:
266: /**
267: * Override to check for post data in your column if needed.
268: */
269: public function ParsePostData() {
270: }
271:
272: /**
273: * PHP magic method
274: *
275: * @param string $strName
276: *
277: * @return bool|int|mixed|QHtmlTableBase|string
278: * @throws Exception
279: * @throws QCallerException
280: */
281: public function __get($strName) {
282: switch ($strName) {
283: case 'Name':
284: return $this->strName;
285: case 'CssClass':
286: return $this->strCssClass;
287: case 'HeaderCssClass':
288: return $this->strHeaderCssClass;
289: case 'HtmlEntities':
290: return $this->blnHtmlEntities;
291: case 'RenderAsHeader':
292: return $this->blnRenderAsHeader;
293: case 'ParentTable':
294: return $this->objParentTable;
295: case 'Span':
296: return $this->intSpan;
297: case 'Id':
298: return $this->strId;
299: case 'Visible':
300: return $this->blnVisible;
301: case 'CellStyler':
302: if (!$this->objCellStyler) {
303: $this->objCellStyler = new QTagStyler();
304: }
305: return $this->objCellStyler;
306: case 'HeaderCellStyler':
307: if (!$this->objHeaderCellStyler) {
308: $this->objHeaderCellStyler = new QTagStyler();
309: }
310: return $this->objHeaderCellStyler;
311: case 'ColStyler':
312: if (!$this->objColStyler) {
313: $this->objColStyler = new QTagStyler();
314: }
315: return $this->objColStyler;
316:
317:
318: default:
319: try {
320: return parent::__get($strName);
321: } catch (QCallerException $objExc) {
322: $objExc->IncrementOffset();
323: throw $objExc;
324: }
325: }
326: }
327:
328: /**
329: * PHP Magic method
330: *
331: * @param string $strName
332: * @param string $mixValue
333: *
334: * @return mixed|void
335: * @throws Exception
336: * @throws QCallerException
337: * @throws QInvalidCastException
338: */
339: public function __set($strName, $mixValue) {
340: switch ($strName) {
341: case "Name":
342: try {
343: $this->strName = QType::Cast($mixValue, QType::String);
344: break;
345: } catch (QInvalidCastException $objExc) {
346: $objExc->IncrementOffset();
347: throw $objExc;
348: }
349:
350: case "CssClass":
351: try {
352: $this->strCssClass = QType::Cast($mixValue, QType::String);
353: break;
354: } catch (QInvalidCastException $objExc) {
355: $objExc->IncrementOffset();
356: throw $objExc;
357: }
358:
359: case "HeaderCssClass":
360: try {
361: $this->strHeaderCssClass = QType::Cast($mixValue, QType::String);
362: break;
363: } catch (QInvalidCastException $objExc) {
364: $objExc->IncrementOffset();
365: throw $objExc;
366: }
367:
368: case "HtmlEntities":
369: try {
370: $this->blnHtmlEntities = QType::Cast($mixValue, QType::Boolean);
371: break;
372: } catch (QInvalidCastException $objExc) {
373: $objExc->IncrementOffset();
374: throw $objExc;
375: }
376:
377: case "RenderAsHeader":
378: try {
379: $this->blnRenderAsHeader = QType::Cast($mixValue, QType::Boolean);
380: break;
381: } catch (QInvalidCastException $objExc) {
382: $objExc->IncrementOffset();
383: throw $objExc;
384: }
385:
386: case "Span":
387: try {
388: $this->intSpan = QType::Cast($mixValue, QType::Integer);
389: if ($this->intSpan < 1) {
390: throw new Exception("Span must be 1 or greater.");
391: }
392: break;
393: } catch (QInvalidCastException $objExc) {
394: $objExc->IncrementOffset();
395: throw $objExc;
396: }
397:
398: case "Id":
399: try {
400: $this->strId = QType::Cast($mixValue, QType::String);
401: break;
402: } catch (QInvalidCastException $objExc) {
403: $objExc->IncrementOffset();
404: throw $objExc;
405: }
406:
407: case "Visible":
408: try {
409: $this->blnVisible = QType::Cast($mixValue, QType::Boolean);
410: break;
411: } catch (QInvalidCastException $objExc) {
412: $objExc->IncrementOffset();
413: throw $objExc;
414: }
415:
416: case "CellParamsCallback":
417: $this->cellParamsCallback = $mixValue;
418: break;
419:
420: case "_ParentTable":
421: try {
422: $this->objParentTable = QType::Cast($mixValue, 'QHtmlTableBase');
423: break;
424: } catch (QInvalidCastException $objExc) {
425: $objExc->IncrementOffset();
426: throw $objExc;
427: }
428:
429: default:
430: try {
431: parent::__set($strName, $mixValue);
432: break;
433: } catch (QCallerException $objExc) {
434: $objExc->IncrementOffset();
435: throw $objExc;
436: }
437: }
438: }
439: }
440:
441: /**
442: * An abstract column designed to work with QDataGrid and other tables that require more than basic columns.
443: * Supports post processing of cell contents for further formatting, and OrderBy clauses.
444: *
445: * @property mixed $OrderByClause order by info for sorting the column in ascending order. Used by subclasses.
446: * Most often this is a QQ::Clause, but can be any data needed.
447: * @property mixed $ReverseOrderByClause order by info for sorting the column in descending order.
448: * @property string $Format the default format to use for FetchCellValueFormatted(). Used by QDataTables plugin.
449: * For date columns it should be a format accepted by QDateTime::qFormat()
450: * @property-write string $PostMethod after the cell object is retrieved, call this method on the obtained object
451: * @property-write callback $PostCallback after the cell object is retrieved, call this callback on the obtained object.
452: * If $PostMethod is also set, this will be called after that method call.
453: */
454: abstract class QAbstractHtmlTableDataColumn extends QAbstractHtmlTableColumn {
455: /** @var mixed Order By information. Can be a QQ::Clause, or any kind of object depending on your need */
456: protected $objOrderByClause = null;
457: /** @var mixed */
458: protected $objReverseOrderByClause = null;
459: /** @var string */
460: protected $strFormat = null;
461: /** @var string */
462: protected $strPostMethod = null;
463: /** @var callback */
464: protected $objPostCallback = null;
465:
466: /**
467: * Return the raw string that represents the cell value.
468: * This version uses a combination of post processing strategies so that you can set
469: * column options to format the raw data. If no
470: * options are set, then $item will just pass through, or __toString() will be called
471: * if its an object. If none of these work for you, just override FetchCellObject and
472: * return your formatted string from there.
473: *
474: * @param mixed $item
475: *
476: * @return mixed|string
477: */
478: public function FetchCellValue($item) {
479: $cellValue = $this->FetchCellObject($item);
480:
481: if ($cellValue !== null && $this->strPostMethod) {
482: $strPostMethod = $this->strPostMethod;
483: assert ('is_callable([$cellValue, $strPostMethod])'); // Malformed post method, or the item is not an object
484: $cellValue = $cellValue->$strPostMethod();
485: }
486: if ($this->objPostCallback) {
487: $cellValue = call_user_func($this->objPostCallback, $cellValue);
488: }
489: if ($cellValue === null) {
490: return '';
491: }
492:
493: if ($cellValue instanceof QDateTime) {
494: return $cellValue->qFormat($this->strFormat);
495: }
496: if (is_object($cellValue)) {
497: $cellValue = $cellValue->__toString();
498: }
499: if ($this->strFormat)
500: return sprintf($this->strFormat, $cellValue);
501:
502: return $cellValue;
503: }
504:
505: /**
506: * Return the value of the cell. FetchCellValue will process this more if needed.
507: * Default returns an entire data row and relies on FetchCellValue to extract the needed data.
508: *
509: * @param mixed $item
510: */
511: abstract public function FetchCellObject($item);
512:
513: /**
514: * Fix up possible embedded reference to the form.
515: */
516: public function Sleep() {
517: $this->objPostCallback = QControl::SleepHelper($this->objPostCallback);
518: parent::Sleep();
519: }
520:
521: /**
522: * The object has been unserialized, so fix up pointers to embedded objects.
523: * @param QForm $objForm
524: */
525: public function Wakeup(QForm $objForm) {
526: parent::Wakeup($objForm);
527: $this->objPostCallback = QControl::WakeupHelper($objForm, $this->objPostCallback);
528: }
529:
530: /**
531: * PHP magic method
532: *
533: * @param string $strName
534: *
535: * @return bool|int|mixed|QHtmlTableBase|string
536: * @throws Exception
537: * @throws QCallerException
538: */
539: public function __get($strName) {
540: switch ($strName) {
541: case "OrderByClause":
542: return $this->objOrderByClause;
543: case "ReverseOrderByClause":
544: return $this->objReverseOrderByClause;
545: case "Format":
546: return $this->strFormat;
547:
548: default:
549: try {
550: return parent::__get($strName);
551: } catch (QCallerException $objExc) {
552: $objExc->IncrementOffset();
553: throw $objExc;
554: }
555: }
556: }
557:
558: /**
559: * PHP magic method
560: *
561: * @param string $strName
562: * @param string $mixValue
563: *
564: * @return mixed|void
565: * @throws Exception
566: * @throws QCallerException
567: * @throws QInvalidCastException
568: */
569: public function __set($strName, $mixValue) {
570: switch ($strName) {
571: case "OrderByClause":
572: try {
573: $this->objOrderByClause = $mixValue;
574: break;
575: } catch (QInvalidCastException $objExc) {
576: $objExc->IncrementOffset();
577: throw $objExc;
578: }
579:
580: case "ReverseOrderByClause":
581: try {
582: $this->objReverseOrderByClause = $mixValue;
583: break;
584: } catch (QInvalidCastException $objExc) {
585: $objExc->IncrementOffset();
586: throw $objExc;
587: }
588:
589: case "Format":
590: try {
591: $this->strFormat = QType::Cast($mixValue, QType::String);
592: break;
593: } catch (QInvalidCastException $objExc) {
594: $objExc->IncrementOffset();
595: throw $objExc;
596: }
597:
598: case "PostMethod":
599: try {
600: $this->strPostMethod = QType::Cast($mixValue, QType::String);
601: break;
602: } catch (QInvalidCastException $objExc) {
603: $objExc->IncrementOffset();
604: throw $objExc;
605: }
606:
607: case "PostCallback":
608: $this->objPostCallback = $mixValue;
609: break;
610:
611: default:
612: try {
613: parent::__set($strName, $mixValue);
614: break;
615: } catch (QCallerException $objExc) {
616: $objExc->IncrementOffset();
617: throw $objExc;
618: }
619: }
620: }
621: }
622:
623: /**
624: * Displays a property of an object, as in $object->Property
625: * If your DataSource is an array of objects, use this column to display a particular property of each object.
626: * Can search with depth to, as in $obj->Prop1->Prop2.
627: *
628: * @property string $Property the property to use when accessing the objects in the DataSource array. Can be a s
629: * series of properties separated with '->', i.e. 'Prop1->Prop2->Prop3' will find the Prop3 item inside the Prop2 object,
630: * inside the Prop1 object, inside the current object.
631: * @property boolean $NullSafe if true the value fetcher will check for nulls before accessing the properties
632: */
633: class QHtmlTablePropertyColumn extends QAbstractHtmlTableDataColumn {
634: protected $strProperty;
635: protected $strPropertiesArray;
636: protected $blnNullSafe = true;
637:
638: /**
639: * @param string $strName name of the column
640: * @param string|null $strProperty the property name to use when accessing the DataSource row object.
641: * Can be null, in which case object will have the ->__toString() function called on it.
642: * @param QQNode $objBaseNode if not null, the OrderBy and ReverseOrderBy clauses will be created using the property path and the given database node
643: */
644: public function __construct($strName, $strProperty, $objBaseNode = null) {
645: parent::__construct($strName);
646: $this->Property = $strProperty;
647:
648: if ($objBaseNode != null) {
649: foreach ($this->strPropertiesArray as $strProperty) {
650: $objBaseNode = $objBaseNode->$strProperty;
651: }
652:
653: $this->OrderByClause = QQ::OrderBy($objBaseNode);
654: $this->ReverseOrderByClause = QQ::OrderBy($objBaseNode, 'desc');
655: }
656: }
657:
658: public function FetchCellObject($item) {
659: if ($this->blnNullSafe && $item == null)
660: return null;
661: foreach ($this->strPropertiesArray as $strProperty) {
662: $item = $item->$strProperty;
663: if ($this->blnNullSafe && $item == null)
664: break;
665: }
666: return $item;
667: }
668:
669: /**
670: * PHP magic method
671: *
672: * @param string $strName
673: *
674: * @return bool|int|mixed|QHtmlTableBase|string
675: * @throws Exception
676: * @throws QCallerException
677: */
678: public function __get($strName) {
679: switch ($strName) {
680: case 'Property':
681: return $this->strProperty;
682: case 'NullSafe':
683: return $this->blnNullSafe;
684: default:
685: try {
686: return parent::__get($strName);
687: } catch (QCallerException $objExc) {
688: $objExc->IncrementOffset();
689: throw $objExc;
690: }
691: }
692: }
693:
694: /**
695: * PHP magic method
696: *
697: * @param string $strName
698: * @param string $mixValue
699: *
700: * @return mixed|void
701: * @throws Exception
702: * @throws QCallerException
703: * @throws QInvalidCastException
704: */
705: public function __set($strName, $mixValue) {
706: switch ($strName) {
707: case "Property":
708: try {
709: $this->strProperty = QType::Cast($mixValue, QType::String);
710: $this->strPropertiesArray = $this->strProperty ? explode('->', $this->strProperty) : array();
711: break;
712: } catch (QInvalidCastException $objExc) {
713: $objExc->IncrementOffset();
714: throw $objExc;
715: }
716:
717: case "NullSafe":
718: try {
719: $this->blnNullSafe = QType::Cast($mixValue, QType::Boolean);
720: break;
721: } catch (QInvalidCastException $objExc) {
722: $objExc->IncrementOffset();
723: throw $objExc;
724: }
725:
726: default:
727: try {
728: parent::__set($strName, $mixValue);
729: break;
730: } catch (QCallerException $objExc) {
731: $objExc->IncrementOffset();
732: throw $objExc;
733: }
734: }
735: }
736: }
737:
738: /**
739: * Class QHtmlTableNodeColumn
740: *
741: * A table column that displays the content of a database column represented by a QQNode object.
742: * The $objNodes can be a single node, or an array of nodes. If an array of nodes, the first
743: * node will be the display node, and the rest of the nodes will be used for sorting.
744: */
745: class QHtmlTableNodeColumn extends QHtmlTablePropertyColumn {
746: public function __construct($strName, $objNodes) {
747: if ($objNodes instanceof QQNode) {
748: $objNodes = [$objNodes];
749: }
750: elseif (empty($objNodes) || !is_array($objNodes) || !$objNodes[0] instanceof QQNode) {
751: throw new QCallerException('Pass either a QQNode or an array of QQNodes only');
752: }
753:
754: $objNode = $objNodes[0]; // First node is the data node, the rest are for sorting.
755:
756: if (!$objNode->_ParentNode) {
757: throw new QCallerException('First QQNode cannot be a Top Level Node');
758: }
759: if (($objNode instanceof QQReverseReferenceNode) && !$objNode->IsUnique()) {
760: throw new QCallerException('Content QQNode cannot go through any "To Many" association nodes.');
761: }
762:
763: $properties = array($objNode->_PropertyName);
764: while ($objNode = $objNode->_ParentNode) {
765: if (!($objNode instanceof QQNode))
766: throw new QCallerException('QQNode cannot go through any "To Many" association nodes.');
767: if (($objNode instanceof QQReverseReferenceNode) && !$objNode->IsUnique())
768: throw new QCallerException('QQNode cannot go through any "To Many" association nodes.');
769: if ($strPropName = $objNode->_PropertyName) {
770: $properties[] = $strPropName;
771: }
772: }
773: $properties = array_reverse($properties);
774: $strProp = implode ('->', $properties);
775: parent::__construct($strName, $strProp, null);
776:
777: // build sort nodes
778: foreach ($objNodes as $objNode) {
779: if ($objNode instanceof QQReverseReferenceNode) {
780: $objNode = $objNode->_PrimaryKeyNode;
781: }
782: $objSortNodes[] = $objNode;
783: $objReverseNodes[] = $objNode;
784: $objReverseNodes[] = false;
785: }
786:
787: $this->OrderByClause = QQ::OrderBy($objSortNodes);
788: $this->ReverseOrderByClause = QQ::OrderBy($objReverseNodes);
789: }
790: }
791:
792:
793: /**
794: * A type of column that should be used when the DataSource items are arrays
795: *
796: * @property int|string $Index the index or key to use when accessing the arrays in the DataSource array
797: *
798: */
799: class QHtmlTableIndexedColumn extends QAbstractHtmlTableDataColumn {
800: protected $mixIndex;
801:
802: /**
803: * @param string $strName name of the column
804: * @param int|string $mixIndex the index or key to use when accessing the DataSource row array
805: */
806: public function __construct($strName, $mixIndex) {
807: parent::__construct($strName);
808: $this->mixIndex = $mixIndex;
809: }
810:
811: public function FetchCellObject($item) {
812: if (isset ($item[$this->mixIndex])) {
813: return $item[$this->mixIndex];
814: } else {
815: return '';
816: }
817: }
818:
819: /**
820: * PHP magic method
821: *
822: * @param string $strName
823: *
824: * @return bool|int|mixed|QHtmlTableBase|string
825: * @throws Exception
826: * @throws QCallerException
827: */
828: public function __get($strName) {
829: switch ($strName) {
830: case 'Index':
831: return $this->mixIndex;
832: default:
833: try {
834: return parent::__get($strName);
835: } catch (QCallerException $objExc) {
836: $objExc->IncrementOffset();
837: throw $objExc;
838: }
839: }
840: }
841:
842: /**
843: * PHP magic method
844: *
845: * @param string $strName
846: * @param string $mixValue
847: *
848: * @return mixed|void
849: * @throws Exception
850: * @throws QCallerException
851: */
852: public function __set($strName, $mixValue) {
853: switch ($strName) {
854: case "Index":
855: $this->mixIndex = $mixValue;
856: break;
857:
858: default:
859: try {
860: parent::__set($strName, $mixValue);
861: break;
862: } catch (QCallerException $objExc) {
863: $objExc->IncrementOffset();
864: throw $objExc;
865: }
866: }
867: }
868: }
869:
870: /**
871: * A type of column that lets you use a PHP 'callable'. However, you CANNOT send a PHP closure to this,
872: * since closures are not serializable. You CAN do things like array($this, 'method'), or 'Class::StaticMethod'.
873: *
874: * @property int|string $Index the index or key to use when accessing the arrays in the DataSource array
875: *
876: */
877: class QHtmlTableCallableColumn extends QAbstractHtmlTableDataColumn {
878: /** @var callback */
879: protected $objCallable;
880: /** @var array extra parameters passed to closure */
881: protected $mixParams;
882:
883: /**
884: * @param string $strName name of the column
885: * @param callback $objCallable a callable object. It should take a single argument, the item
886: * of the array. Do NOT pass an actual Closure object, as they are not serializable. However,
887: * you can pass a callable, like array($this, 'method'), or an object that has the __invoke method defined,
888: * as long as its serializable. You can also pass static methods as a string, as in "Class::method"
889: * @param mixed $mixParams extra parameters to pass to the closure callback.
890: * will be called with the row of the DataSource as that single argument.
891: *
892: * @throws InvalidArgumentException
893: */
894: public function __construct($strName, callable $objCallable, $mixParams = null) {
895: parent::__construct($strName);
896: if ($objCallable instanceof Closure) {
897: throw new InvalidArgumentException('Cannot be a Closure.');
898: }
899: $this->objCallable = $objCallable;
900: $this->mixParams = $mixParams;
901: }
902:
903: public function FetchCellObject($item) {
904: if ($this->mixParams) {
905: return call_user_func($this->objCallable, $item, $this->mixParams);
906: } else {
907: return call_user_func($this->objCallable, $item);
908: }
909: }
910:
911: /**
912: * Fix up possible embedded reference to the form.
913: */
914: public function Sleep() {
915: $this->objCallable = QControl::SleepHelper($this->objCallable);
916: parent::Sleep();
917: }
918:
919: /**
920: * Restore serialized references.
921: * @param QForm $objForm
922: */
923: public function Wakeup(QForm $objForm) {
924: parent::Wakeup($objForm);
925: $this->objCallable = QControl::WakeupHelper($objForm, $this->objCallable);
926: }
927:
928: /**
929: * PHP magic method
930: *
931: * @param string $strName
932: *
933: * @return bool|callable|int|mixed|QHtmlTableBase|string
934: * @throws Exception
935: * @throws QCallerException
936: */
937: public function __get($strName) {
938: switch ($strName) {
939: case 'Callable':
940: return $this->objCallable;
941: default:
942: try {
943: return parent::__get($strName);
944: } catch (QCallerException $objExc) {
945: $objExc->IncrementOffset();
946: throw $objExc;
947: }
948: }
949: }
950:
951: /**
952: * PHP magic method
953: *
954: * @param string $strName
955: * @param string $mixValue
956: *
957: * @return mixed|void
958: * @throws Exception
959: * @throws QCallerException
960: * @throws QInvalidCastException
961: */
962: public function __set($strName, $mixValue) {
963: switch ($strName) {
964: case "Callable":
965: if (!is_callable($mixValue)) {
966: throw new QInvalidCastException("Callable must be a callable object");
967: }
968: $this->objCallable = $mixValue;
969: break;
970:
971: default:
972: try {
973: parent::__set($strName, $mixValue);
974: break;
975: } catch (QCallerException $objExc) {
976: $objExc->IncrementOffset();
977: throw $objExc;
978: }
979: }
980: }
981: }
982:
983: /**
984: *
985: * A column to display a virtual attribute from a database record.
986: *
987: * @property string $Attribute
988: */
989: class QVirtualAttributeColumn extends QAbstractHtmlTableDataColumn {
990: protected $strAttribute;
991:
992: public function __construct($strName, $strAttribute = null) {
993: parent::__construct($strName);
994: if ($strAttribute) {
995: $this->strAttribute = $strAttribute;
996: }
997:
998: $this->OrderByClause = QQ::OrderBy(QQ::Virtual($strAttribute));
999: $this->ReverseOrderByClause = QQ::OrderBy(QQ::Virtual($strAttribute), false);
1000:
1001: }
1002:
1003: public function FetchCellObject($item) {
1004: return $item->GetVirtualAttribute ($this->strAttribute);
1005: }
1006:
1007: /**
1008: * PHP magic method
1009: *
1010: * @param string $strName
1011: *
1012: * @return bool|int|mixed|null|QHtmlTableBase|string
1013: * @throws Exception
1014: * @throws QCallerException
1015: */
1016: public function __get($strName) {
1017: switch ($strName) {
1018: case 'Attribute':
1019: return $this->strAttribute;
1020: default:
1021: try {
1022: return parent::__get($strName);
1023: } catch (QCallerException $objExc) {
1024: $objExc->IncrementOffset();
1025: throw $objExc;
1026: }
1027: }
1028: }
1029:
1030: /**
1031: * PHP magic method
1032: *
1033: * @param string $strName
1034: * @param string $mixValue
1035: *
1036: * @return mixed|void
1037: * @throws Exception
1038: * @throws QCallerException
1039: * @throws QInvalidCastException
1040: */
1041: public function __set($strName, $mixValue) {
1042: switch ($strName) {
1043: case "Attribute":
1044: $this->strAttribute = QType::Cast ($mixValue, QType::String);
1045: break;
1046:
1047: default:
1048: try {
1049: parent::__set($strName, $mixValue);
1050: break;
1051: } catch (QCallerException $objExc) {
1052: $objExc->IncrementOffset();
1053: throw $objExc;
1054: }
1055: }
1056: }
1057:
1058: }
1059:
1060: /**
1061: *
1062: * A column of checkboxes.
1063: *
1064: * Prints checkboxes in a column, including the header. Override this class and implement whatever hooks you need. In
1065: * particular implement the CheckId hooks, and IsChecked hooks.
1066: *
1067: * To get the checkbox values to post values back to PHP, each checkbox must have an id of the form:
1068: *
1069: * QcontrolId_index
1070: *
1071: * This class does not detect and record changes in the checkbox list. You can detect changes from within
1072: * ParsePostData by calling $this->objForm->CheckableControlValue,
1073: * or use the QHtmlTableCheckBoxColumn_ClickEvent to detect a change to a checkbox.
1074: *
1075: * You will need to detect whether
1076: * the header check all box was clicked, or a regular box was clicked and respond accordingly. In response to a
1077: * click, you could store the array of ids of the checkboxes clicked in a session variable, the database, or
1078: * a cache variable. You would just give an id to each checkbox. This would cause internet traffic every time
1079: * a box is clicked.
1080: *
1081: * @property bool $ShowCheckAll
1082: *
1083: */
1084: class QHtmlTableCheckBoxColumn extends QAbstractHtmlTableDataColumn {
1085: protected $blnHtmlEntities = false; // turn off html entities
1086: protected $checkParamCallback = null;
1087: protected $blnShowCheckAll = false;
1088:
1089: /**
1090: * Returns a header cell with a checkbox. This could be used as a check all box. Override this and return
1091: * an empty string to turn it off.
1092: *
1093: * @return string
1094: */
1095: public function FetchHeaderCellValue() {
1096: if ($this->blnShowCheckAll) {
1097: $aParams = $this->GetCheckboxParams(null);
1098: $aParams['type'] = 'checkbox';
1099: return QHtml::RenderTag('input', $aParams, null, true);
1100: } else {
1101: return $this->Name;
1102: }
1103: }
1104:
1105: public function FetchCellObject($item) {
1106: $aParams = $this->GetCheckboxParams($item);
1107: $aParams['type'] = 'checkbox';
1108: return QHtml::RenderTag('input', $aParams, null, true);
1109: }
1110:
1111: /**
1112: * Returns an array of parameters to attach to the checkbox tag. Includes whether the
1113: * checkbox should appear as checked. Will try the callback first, and if not present,
1114: * will try overridden functions.
1115: *
1116: * @param mixed|null $item Null to indicate that we want the params for the header cell.
1117: * @return array
1118: */
1119: public function GetCheckboxParams ($item) {
1120: $aParams = array();
1121:
1122: if ($strId = $this->GetCheckboxId ($item)) {
1123: $aParams['id'] = $strId;
1124: }
1125:
1126: if ($this->IsChecked ($item)) {
1127: $aParams['checked'] = 'checked';
1128: }
1129:
1130: if ($strName = $this->GetCheckboxName ($item)) {
1131: $aParams['name'] = $strName; // name is not used by QCubed
1132: }
1133:
1134: $aParams['value'] = $this->GetCheckboxValue ($item); // note that value is required for html checkboxes, but is not used by QCubed
1135:
1136: if ($this->checkParamCallback) {
1137: $a = call_user_func($this->checkParamCallback, $item);
1138: $aParams = array_merge ($aParams, $a);
1139: }
1140:
1141: return $aParams;
1142: }
1143:
1144: /**
1145: * Optional callback to control the appearance of the checkboxes. You can use a callback, or subclass to do this.
1146: * If a callback, it should be of the form:
1147: * func($item)
1148: *
1149: * $item is either the line item, or null to indicate the header
1150: *
1151: * This should return the following values in an array to indicate what should be put as attributes for the checkbox tag:
1152: * id
1153: * name
1154: * value
1155: * checked (only return a value here if you want it checked. Otherwise, do not include in the array)
1156: *
1157: * See below for a description of what should be returned for each item.
1158: *
1159: * @param $callable
1160: */
1161: public function SetCheckParamCallback ($callable) {
1162: $this->checkParamCallback = $callable;
1163: }
1164:
1165: /**
1166: * Returns the id for the checkbox itself. This is used together with the check action to send the item
1167: * id to the action. Your id should look like:
1168: *
1169: *
1170: *
1171: * @param mixed|null $item Null to get the id for the header checkbox
1172: */
1173: protected function GetCheckboxId ($item) {
1174: return null;
1175: }
1176:
1177: /**
1178: * Return true if the checkbox should be drawn checked. Override this to provide the correct value.
1179: * @param mixed|null $item Null to get the id for the header checkbox
1180: * @return bool
1181: */
1182: protected function IsChecked ($item) {
1183: return false;
1184: }
1185:
1186: /**
1187: * Return the name attribute for the checkbox. If you return null, the checkbox will not get submitted to the form.
1188: * If you return a name, then that will be the key for the value submitted by the form. If you return a name
1189: * ending with brackets [], then this checkbox will be part of an array of values posted to that name.
1190: *
1191: * @param mixed|null $item Null to get the id for the header checkbox
1192: * @return null|string
1193: */
1194: protected function GetCheckboxName ($item) {
1195: return null;
1196: }
1197:
1198: /**
1199: * Return the value attribute of the checkbox tag. Checkboxes are required to have a value in html.
1200: * This value will be what is posted by form post.
1201: *
1202: * @param mixed|null $item Null to get the id for the header checkbox
1203: * @return string
1204: */
1205: protected function GetCheckboxValue ($item) {
1206: return "1"; // Means that if the checkbox is checked, the POST value corresponding to the name of the checkbox will be 1.
1207: }
1208:
1209: /**
1210: * Fix up possible embedded reference to the form.
1211: */
1212: public function Sleep() {
1213: $this->checkParamCallback = QControl::SleepHelper($this->checkParamCallback);
1214: parent::Sleep();
1215: }
1216:
1217: /**
1218: * Restore embedded objects.
1219: *
1220: * @param QForm $objForm
1221: */
1222: public function Wakeup(QForm $objForm) {
1223: parent::Wakeup($objForm);
1224: $this->checkParamCallback = QControl::WakeupHelper($objForm, $this->checkParamCallback);
1225: }
1226:
1227: /**
1228: * PHP magic method
1229: *
1230: * @param string $strName
1231: *
1232: * @return bool|int|mixed|QHtmlTableBase|string
1233: * @throws Exception
1234: * @throws QCallerException
1235: */
1236: public function __get($strName) {
1237: switch ($strName) {
1238: case 'ShowCheckAll':
1239: return $this->blnShowCheckAll;
1240: default:
1241: try {
1242: return parent::__get($strName);
1243: } catch (QCallerException $objExc) {
1244: $objExc->IncrementOffset();
1245: throw $objExc;
1246: }
1247: }
1248: }
1249:
1250: /**
1251: * PHP magic method
1252: *
1253: * @param string $strName
1254: * @param string $mixValue
1255: *
1256: * @return mixed|void
1257: * @throws Exception
1258: * @throws QCallerException
1259: * @throws QInvalidCastException
1260: */
1261: public function __set($strName, $mixValue) {
1262: switch ($strName) {
1263: case "ShowCheckAll":
1264: try {
1265: $this->blnShowCheckAll = QType::Cast($mixValue, QType::Boolean);
1266: break;
1267: } catch (QInvalidCastException $objExc) {
1268: $objExc->IncrementOffset();
1269: throw $objExc;
1270: }
1271:
1272: default:
1273: try {
1274: parent::__set($strName, $mixValue);
1275: break;
1276: } catch (QCallerException $objExc) {
1277: $objExc->IncrementOffset();
1278: throw $objExc;
1279: }
1280: }
1281: }
1282:
1283:
1284: }
1285:
1286:
1287: class QHtmlTableCheckBoxColumn_ClickEvent extends QClickEvent {
1288: const JsReturnParam = '{"row": $j(this).closest("tr")[0].rowIndex, "col": $j(this).parent()[0].cellIndex, "checked":this.checked, "id":this.id}'; // returns the array of cell info, and the new state of the checkbox
1289:
1290: public function __construct($intDelay = 0, $strCondition = null) {
1291: parent::__construct($intDelay, $strCondition, 'input[type="checkbox"]');
1292: }
1293: }
1294:
1295: /**
1296: * Class QHtmlTableLinkColumn
1297: *
1298: * A multi-purpose link column. This column lets you specify a column whose purpose is to show an anchor tag
1299: * with text, attributes and properties related to row item. It can handle row items that are objects or arrays,
1300: * and specify parameters or methods of objects, as well as offsets in arrays.
1301: *
1302: * You can specify the text of the link, the destination address, the html get variables, and the attributes
1303: * to the anchor tag in a variety of ways as follows:
1304: * - as a static string
1305: * - as a two member array callable, with the row item passed to the callable
1306: * - as an object property or string of properties (i.e. $item->prop1->prop2) by starting the string with "->" and
1307: * separating each property with a "->". If the property ends with "()", then it will be a method call instead.
1308: * The same can be accomplished by passing an array, with each item being a step in the property chain. This
1309: * is provided the row item is an object.
1310: * - as an index into an array, or a multi-index array (i.e. $item['index1']['index2']) by passing a string of the
1311: * form "[index1][index2]...". You can also pass an array that contains the indexes into the array. This is provided
1312: * the row item is an array.
1313: *
1314: * Other options:
1315: * - Specify null for $mixDestination, and no link will be created, just text. This is helpful for turning off the
1316: * link mode without having to create a completely different kind of column.
1317: * - Specify a QControlProxy for $mixDestination to draw it as a proxy control. In this case, $blnAsButton can be
1318: * used to draw the proxy as a button rather than a link.
1319: *
1320: * Examples:
1321: *
1322: * Create a column to edit a person, with "Edit" in the header, the name of the person as the label of each link, and give each
1323: * anchor a class of "link".
1324: * $objColumn = new QHtmlTableLinkColumn ("Edit", "->Name", "person_edit.php", ["intId"=>"->Id"], ["class"=>"link"]);
1325: *
1326: *
1327: * Create a similar column, but use a proxy instead, with the person id as the action parameter to the proxy and
1328: * drawing the proxy as a button.
1329: * $objProxy = new QControlProxy($this);
1330: * $objColumn = new QHtmlTableLinkColumn ("Edit", "Edit", $objProxy, "->Id", null, true);
1331: *
1332: * Create a "zoom" column for a table that uses an array of arrays as its source. Pass the 'id' index from the item
1333: * as the id to the destination link. Use the "title" index as the label for the link.
1334: * $objColumn = new QHtmlTableLinkColumn ("Zoom", "[title]", "zoom.php", ["intId"=>"[id]"]);
1335: *
1336: * Create a simple link column that just specifies a data attribute, and uses event delegation attached to the table to trap a click on the link.
1337: * Return the id of the item clicked to the action as the action parameter.
1338: * $objTable = new QHtmlTable ($this);
1339: * $objTable->CreateLinkColumn("", "->Name", "#", null, ["data-id"=>"->Id"]);
1340: * $objTable->AddAction(new QClickEvent(0, null, "a"), new QAjaxAction("myActionScript", null, null, '$j(this).data("id")'));
1341: *
1342: * @property bool $AsButton Only used if this is drawing a QControlProxy. Will draw the proxy as a button.
1343: * @property-write null|string|array $Text The text to display as the label of the anchor, a callable callback to get the text,
1344: * a string that represents a property chain or a multi-dimensional array, or an array that represents the same. Depends on
1345: * what time of row item is passed.
1346: * @property-write null|string|array|QControlProxy $Destination The text representing the destination of the anchor, a callable callback to get the destination,
1347: * a string that represents a property chain or a multi-dimensional array, or an array that represents the same,
1348: * or a QControlProxy. Depends on what time of row item is passed.
1349: * @property-write null|string|array $GetVars An array of key=>value pairs to use as the GET variables in the link URL,
1350: * or in the case of a QControlProxy, possibly a string to represent the action parameter. In either case, each item
1351: * can be a property chain, an array index list, or a callable callback as specified above.
1352: * @property-write null|array $TagAttributes An array of key=>value pairs to use as additional attributes in the tag.
1353: * For example, could be used to add a class or an id to each tag.
1354: */
1355: class QHtmlTableLinkColumn extends QAbstractHtmlTableDataColumn {
1356: /** @var bool */
1357: protected $blnHtmlEntities = false; // we are rendering a link so turn off entities
1358:
1359: /** @var string|array */
1360: protected $mixText;
1361: /** @var string|array|QControlProxy|null */
1362: protected $mixDestination;
1363: /** @var array|string|null */
1364: protected $getVars;
1365: /** @var array|null */
1366: protected $tagAttributes;
1367: /** @var bool */
1368: protected $blnAsButton;
1369:
1370: /**
1371: * QHtmlTableLinkColumn constructor.
1372: *
1373: * @param string $strName Column name to be displayed in the table header.
1374: * @param null|string|array|QQNode $mixText The text to display as the label of the anchor, a callable callback to get the text,
1375: * a string that represents a property chain or a multi-dimensional array, or an array that represents the same, or a QQNode representing the property.
1376: * Depends on what type of row item is passed.
1377: * @param null|string|array|QControlProxy $mixDestination The text representing the destination of the anchor, a callable callback to get the destination,
1378: * a string that represents a property chain or a multi-dimensional array, or an array that represents the same,
1379: * or a QControlProxy. Depends on what type of row item is passed.
1380: * @param null|string|array $getVars An array of key=>value pairs to use as the GET variables in the link URL,
1381: * or in the case of a QControlProxy, possibly a string to represent the action parameter. In either case, each item
1382: * can be a property chain, an array index list, a QQNode, or a callable callback as specified above. If the destination is a
1383: * QControlProxy, this would be what to use as the action parameter.
1384: * @param null|array $tagAttributes An array of key=>value pairs to use as additional attributes in the tag.
1385: * For example, could be used to add a class or an id to each tag.
1386: * @param bool $blnAsButton Only used if this is drawing a QControlProxy. Will draw the proxy as a button.
1387: */
1388: public function __construct($strName, $mixText, $mixDestination = null, $getVars = null, $tagAttributes = null, $blnAsButton = false) {
1389: parent::__construct($strName);
1390: $this->Text = $mixText;
1391: $this->Destination = $mixDestination;
1392: $this->GetVars = $getVars;
1393: $this->TagAttributes = $tagAttributes;
1394: $this->blnAsButton = $blnAsButton;
1395: }
1396:
1397: /**
1398: * Utility function to pre-process a value specifier. This will take a property list chain or an array index
1399: * chain and split it into an array representing the parts.
1400: *
1401: * @param mixed $mixSpec
1402: * @return mixed
1403: */
1404: protected static function SplitSpec ($mixSpec)
1405: {
1406: if (is_array($mixSpec)) {
1407: return $mixSpec; // already split
1408: } elseif (is_string($mixSpec)) {
1409: if (strpos($mixSpec, '->') === 0) {
1410: // It is an object property list ($item->param1->param2)
1411: $parts = explode('->', substr($mixSpec, 2));
1412: return $parts;
1413: } elseif ($mixSpec[0] == '[' && substr($mixSpec, -1) == ']') {
1414: // It is a list of array dereferences
1415: $parts = explode('][', $mixSpec, substr(1, strlen($mixSpec) - 2));
1416: return $parts;
1417: }
1418: else {
1419: return $mixSpec;
1420: }
1421: } else {
1422: return $mixSpec;
1423: }
1424: }
1425:
1426:
1427: /**
1428: * Utility function to post-process a value specifier. Will walk through an object property chain or an array
1429: * index chain and return the final value.
1430: *
1431: * @param mixed $mixSpec
1432: * @param mixed $item
1433: * @return string
1434: */
1435: protected static function GetObjectValue ($mixSpec, $item) {
1436: if (is_array($mixSpec)) {
1437: if (is_object($mixSpec[0]) && is_callable($mixSpec)) {
1438: // If its a callable array, then call it
1439: return call_user_func($mixSpec, $item);
1440: }
1441: elseif (is_object($item)) {
1442: // It is an object property list ($item->param1->param2 or $item->method()->method2()). Can mix these too.
1443: $value = $item;
1444: foreach ($mixSpec as $part) {
1445: // Evaluate as a function, or a param
1446: if (substr($part,-2) == '()') {
1447: // call as a method
1448: $value = $value->$part();
1449: } else {
1450: $value = $value->$part;
1451: }
1452: }
1453: return $value;
1454: }
1455: elseif (is_array($item)) {
1456: $value = $item;
1457: foreach ($mixSpec as $part) {
1458: $value = $value[$part];
1459: }
1460: return $value;
1461: }
1462: else {
1463: return $item; // We have no idea what this is, so return the item for possible further processing
1464: }
1465: }
1466: elseif ($mixSpec instanceof QQNode) {
1467: $properties = array($mixSpec->_PropertyName);
1468: $objNode = $mixSpec;
1469: while ($objNode = $objNode->_ParentNode) {
1470: if (!($objNode instanceof QQNode))
1471: throw new QCallerException('QQNode cannot go through any "To Many" association nodes.');
1472: if (($objNode instanceof QQReverseReferenceNode) && !$objNode->IsUnique())
1473: throw new QCallerException('QQNode cannot go through any "To Many" association nodes.');
1474: if ($strPropName = $objNode->_PropertyName) {
1475: $properties[] = $strPropName;
1476: }
1477: }
1478: $properties = array_reverse($properties);
1479: $value = $item;
1480: foreach ($properties as $prop) {
1481: $value = $value->$prop;
1482: }
1483: if (is_object($value)) {
1484: return $value->__toString();
1485: }
1486: else {
1487: return $value;
1488: }
1489: }
1490: return $mixSpec; // In this case, we return a static value
1491: }
1492:
1493: /**
1494: * Returns the initial text that will be the label of the link. This text can be further processed by using
1495: * the inherited PostCallback function and similar properties.
1496: *
1497: * @param mixed $item
1498: * @return string
1499: */
1500: public function FetchCellObject($item)
1501: {
1502: return static::GetObjectValue($this->mixText, $item);
1503: }
1504:
1505: /**
1506: * Returns the final string representing the content of the cell.
1507: *
1508: * @param mixed $item
1509: * @return string
1510: */
1511: public function FetchCellValue($item) {
1512: $strText = parent::FetchCellValue($item); // allow post processing of cell label
1513:
1514: $getVars = null;
1515: if ($this->getVars) {
1516: if (is_array($this->getVars)) {
1517: if (array_keys($this->getVars)[0] === 0) {
1518: // assume this is not associative array. Likely we are here to extract a property list.
1519: $getVars = static::GetObjectValue($this->getVars, $item);
1520: }
1521: else {
1522: // associative array, so likely these are Get variables to be assigned individually
1523: foreach ($this->getVars as $key => $val) {
1524: $getVars[$key] = static::GetObjectValue($val, $item);
1525: }
1526: }
1527: }
1528: elseif ($this->getVars instanceof QQNode) {
1529: $getVars = static::GetObjectValue($this->getVars, $item);
1530: }
1531: else {
1532: $getVars = $this->getVars; // could be a simple action parameter.
1533: }
1534: }
1535:
1536: $tagAttributes = [];
1537: if ($this->tagAttributes && is_array($this->tagAttributes)) {
1538: foreach ($this->tagAttributes as $key=>$val) {
1539: $tagAttributes[$key] = static::GetObjectValue($val, $item);
1540: }
1541: }
1542:
1543: if ($this->mixDestination === null) {
1544: return QApplication::HtmlEntities($strText);
1545: }
1546: elseif ($this->mixDestination instanceof QControlProxy) {
1547: if ($this->blnAsButton) {
1548: return $this->mixDestination->RenderAsButton($strText, $getVars, $tagAttributes);
1549: } else {
1550: return $this->mixDestination->RenderAsLink($strText, $getVars, $tagAttributes);
1551: }
1552: }
1553: else {
1554: $strDestination = static::GetObjectValue($this->mixDestination, $item);
1555: return QHtml::RenderLink(QHtml::MakeUrl($strDestination, $getVars), $strText, $tagAttributes);
1556: }
1557: }
1558:
1559: /**
1560: * Fix up possible embedded references to the form.
1561: */
1562: public function Sleep() {
1563: $this->mixText = QControl::SleepHelper($this->mixText);
1564: $this->mixDestination = QControl::SleepHelper($this->mixDestination);
1565: $this->getVars = QControl::SleepHelper($this->getVars);
1566: $this->tagAttributes = QControl::SleepHelper($this->tagAttributes);
1567: parent::Sleep();
1568: }
1569:
1570: /**
1571: * Restore embedded objects.
1572: *
1573: * @param QForm $objForm
1574: */
1575: public function Wakeup(QForm $objForm) {
1576: parent::Wakeup($objForm);
1577: $this->mixText = QControl::WakeupHelper($objForm, $this->mixText);
1578: $this->mixDestination = QControl::WakeupHelper($objForm, $this->mixDestination);
1579: $this->getVars = QControl::WakeupHelper($objForm, $this->getVars);
1580: $this->tagAttributes = QControl::WakeupHelper($objForm, $this->tagAttributes);
1581: }
1582:
1583:
1584: /**
1585: * PHP magic method
1586: *
1587: * @param string $strName
1588: *
1589: * @return bool|int|mixed|QHtmlTableBase|string
1590: * @throws Exception
1591: * @throws QCallerException
1592: */
1593: public function __get($strName) {
1594: switch ($strName) {
1595: case 'AsButton':
1596: return $this->blnAsButton;
1597: default:
1598: try {
1599: return parent::__get($strName);
1600: } catch (QCallerException $objExc) {
1601: $objExc->IncrementOffset();
1602: throw $objExc;
1603: }
1604: }
1605: }
1606:
1607: /**
1608: * PHP magic method
1609: *
1610: * @param string $strName
1611: * @param string $mixValue
1612: *
1613: * @return mixed|void
1614: * @throws Exception
1615: * @throws QCallerException
1616: * @throws QInvalidCastException
1617: */
1618: public function __set($strName, $mixValue) {
1619: switch ($strName) {
1620: case "AsButton":
1621: try {
1622: $this->blnAsButton = QType::Cast($mixValue, QType::Boolean);
1623: break;
1624: } catch (QInvalidCastException $objExc) {
1625: $objExc->IncrementOffset();
1626: throw $objExc;
1627: }
1628:
1629: case "Text":
1630: $this->mixText = self::SplitSpec($mixValue);
1631: break;
1632:
1633: case "Destination":
1634: if ($mixValue instanceof QControlProxy) {
1635: $this->mixDestination = $mixValue;
1636: } else {
1637: $this->mixDestination = self::SplitSpec($mixValue);
1638: }
1639: break;
1640:
1641: case "GetVars":
1642: try {
1643: if (is_null($mixValue)) {
1644: $this->getVars = null;
1645: }
1646: elseif (is_string($mixValue)) {
1647: $this->getVars = self::SplitSpec($mixValue); // a simple action parameter for a control proxy
1648: }
1649: elseif (is_array($mixValue)) {
1650: $this->getVars = [];
1651: foreach ($mixValue as $key=>$val) {
1652: $this->getVars[$key] = self::SplitSpec($val);
1653: }
1654: }
1655: elseif ($mixValue instanceof QQNode) {
1656: $this->getVars = $mixValue;
1657: }
1658: else {
1659: throw new Exception ("Invalid type");
1660: }
1661: break;
1662: } catch (QInvalidCastException $objExc) {
1663: $objExc->IncrementOffset();
1664: throw $objExc;
1665: }
1666:
1667: case "TagAttributes":
1668: try {
1669: if (is_null($mixValue)) {
1670: $this->tagAttributes = null;
1671: }
1672: elseif (is_array($mixValue)) {
1673: $this->tagAttributes = [];
1674: foreach ($mixValue as $key=>$val) {
1675: $this->tagAttributes[$key] = self::SplitSpec($val);
1676: }
1677: }
1678: else {
1679: throw new Exception ("Invalid type");
1680: }
1681: break;
1682: } catch (QInvalidCastException $objExc) {
1683: $objExc->IncrementOffset();
1684: throw $objExc;
1685: }
1686:
1687: default:
1688: try {
1689: parent::__set($strName, $mixValue);
1690: break;
1691: } catch (QCallerException $objExc) {
1692: $objExc->IncrementOffset();
1693: throw $objExc;
1694: }
1695: }
1696: }
1697:
1698:
1699:
1700: }
1701: