1: <?php
2: /**
3: * This file contains the QDataGridBase and QDataGridRow class.
4: *
5: * @package Controls
6: */
7:
8: /**
9: * Due to the fact that DataGrid's will perform php eval's on anything that is back-ticked within each column/row's
10: * DataGridColumn::HTML, we need to set up this special DataGridEvalHandleError error handler to correctly report
11: * errors that happen.
12: *
13: * @global string $__exc_dtg_errstr
14: * @param mixed $__exc_errno
15: * @param string $__exc_errstr
16: * @param string $__exc_errfile
17: * @param string $__exc_errline
18: */
19: function DataGridEvalHandleError($__exc_errno, $__exc_errstr, $__exc_errfile, $__exc_errline) {
20: $__exc_objBacktrace = debug_backtrace();
21: for ($__exc_intIndex = 0; $__exc_intIndex < count($__exc_objBacktrace); $__exc_intIndex++) {
22: $__exc_objItem = $__exc_objBacktrace[$__exc_intIndex];
23:
24: if ((strpos($__exc_errfile, "DataGrid.inc") !== false) &&
25: (strpos($__exc_objItem["file"], "DataGrid.inc") === false)) {
26: $__exc_errfile = $__exc_objItem["file"];
27: $__exc_errline = $__exc_objItem["line"];
28: } else if ((strpos($__exc_errfile, "Form.inc") !== false) &&
29: (strpos($__exc_objItem["file"], "Form.inc") === false)) {
30: $__exc_errfile = $__exc_objItem["file"];
31: $__exc_errline = $__exc_objItem["line"];
32: }
33: }
34:
35: global $__exc_dtg_errstr;
36: if (isset($__exc_dtg_errstr) && ($__exc_dtg_errstr))
37: $__exc_errstr = sprintf("%s\n%s", $__exc_dtg_errstr, $__exc_errstr);
38: QcubedHandleError($__exc_errno, $__exc_errstr, $__exc_errfile, $__exc_errline, null);
39: }
40:
41: /**
42: * @package Controls
43: *
44: * @property-write QDataGridLegacyRowStyle $Style
45: */
46: class QDataGridLegacyRow extends QControl {
47: /**
48: * Returns the HTML for a row complete with row-level styles when supplied with the HTML of the row
49: *
50: * @param string $strColumnsHtml The calculated inner HTML for the row
51: *
52: * @return string The HTML for the entire row with applied styles enclosed inside '<tr>' tag
53: */
54: public function GetHtml($strColumnsHtml) {
55: $strStyle = $this->GetStyleAttributes();
56: if (strlen($strStyle) > 0)
57: $strStyle = sprintf(' style="%s"', $strStyle);
58:
59: $strToReturn = sprintf('<tr id="%s" %s%s>%s</tr>', $this->strControlId, $this->GetAttributes(), $strStyle, $strColumnsHtml);
60: return $strToReturn;
61: }
62:
63: protected function GetControlHtml() { }
64: public function ParsePostData() {}
65: public function Validate() {}
66:
67: /////////////////////////
68: // Public Properties: SET
69: /////////////////////////
70: /**
71: * PHP magic method
72: *
73: * @param string $strName
74: * @param string $mixValue
75: *
76: * @return mixed
77: * @throws Exception|QCallerException|QInvalidCastException
78: */
79: public function __set($strName, $mixValue) {
80: switch ($strName) {
81: case "Style":
82: try {
83: /** @var QDataGridLegacyRowStyle $objStyle */
84: $objStyle = QType::Cast($mixValue, "QDataGridLegacyRowStyle");
85: $this->Override ($objStyle);
86: break;
87: } catch (QInvalidCastException $objExc) {
88: $objExc->IncrementOffset();
89: throw $objExc;
90: }
91: default:
92: try {
93: parent::__set($strName, $mixValue);
94: break;
95: } catch (QCallerException $objExc) {
96: $objExc->IncrementOffset();
97: throw $objExc;
98: }
99: }
100: }
101: }
102:
103: /**
104: * NOTE: Due to the use of "Eval" and HTML4 specific tags, this class is now deprecated and
105: * the name has been changed from QDataGrid to QDataGridLegacy.
106: *
107: * DataGrid control is used to display tabular information (e.g. lists)
108: *
109: * The control itself will display things based off of an array of objects that gets set as the "Data Source".
110: * It is particularly useful when combined with the Class::LoadArrayByXXX() functions or the Class::LoadAll()
111: * that is generated by the CodeGen framework, or when combined with custom Class ArrayLoaders that you define
112: * youself, but who's structure is based off of the CodeGen framework.
113: *
114: * The DataGrid essentially is a <table>. For each item in a datasource's Array, a row (<tr>) will be generated.
115: * You can define any number of DataGridColumns which will result in a <td> for each row.
116: * Within the DataGridColumn, you can specify the DataGridColumn::Html that should be displayed.
117: *
118: * The HTML looks for the special back-tick character "`", and will do PHP Eval's on antyhing within a pair of ` `.
119: * Moreover, the special variable $_ROW can be used to access the actual contents of that particular row's data
120: * in the main data source array.
121: *
122: * So, for example, supposed the following:
123: * $strSimpleArray = {"Blah", "Foo", "Baz", "Fun"};
124: * $dtgDataGrid = new DataGrid("dtgDataGrid");
125: * $dtgDataGrid->AddColumn("Column Heading", "Contents: `$_ROW`");
126: * $dtgDataGrid->DataSource = $strSimpleArray;
127: * This will generate a simple 4-row, 1-column table that contains the following:
128: * Column Heading
129: * --------------
130: * Contents: Blah
131: * Contents: Foo
132: * Contents: Baz
133: * Contents: Fun
134: * In this case, $_ROW is a string, itself, which is each item in the DataSource's array.
135: *
136: * Utilizing the back-tick and $_ROW feature, you can do infinitely more complicatd display functionality:
137: * $dtgDataGrid = new DataGrid("dtgDataGrid");
138: * $dtgDataGrid->AddColumn("Title", "<b>`$_ROW->Title`</b>");
139: * $dtgDataGrid->AddColumn("Calculated Result", "`DisplayResults($_ROW->Calculate())`");
140: * $dtgDataGrid->DataSource = Foo::LoadAll();
141: * This could then generate a table with much more data-rich information:
142: * Title Calculated Result
143: * ----------------- --------------------
144: * Some Title Here $28,298.24
145: * Foo Baz Bar $18,000.00
146: * Blah (None)
147: * In this case, $_ROW is actually a Foo object.
148: *
149: * IMPORTANT: Please note that while all properties can/should be set up only once within the form's
150: * !IsPostBack() clause, the DataSource **MUST** be set **EVERY TIME**. The contents of DataSource
151: * do NOT persist from postback to postback.
152: *
153: * The appearance of the datagrid control appears to be complicated, but keep in mind that it simply
154: * utlizes the cascading nature of how browsers render tables based on styles assigned to
155: * the <table>, <tr>, and <td>. In short:
156: * - Appearance properties defined to the DataGrid, itself, show up as HTML Attributes
157: * and CSS Styles within the <table> tag.
158: * - Appearance properties defined to a specific row's DataGridRowStyle will show up as
159: * HTML attributes within that specific row's <tr> tag.
160: * - Appearance properties defined to a DataGridColumn will show up as HTML attributes
161: * within that specific row's <td> tag.
162: *
163: * So, attributes will only show up if it is defined at that particular level. So if you define a background color
164: * for a DataGridRowStyle for a particular row, but not for a DataGridColumn or for the DataGrid in general, that
165: * background style will only show up in that row.
166: *
167: * And due to the cascaiding nature of how browsers render tables, any undefined appearance property will simply
168: * inherit from the parent (where a <td>'s parent is the <tr>, and the <tr>'s parent is the <table>,
169: * and any defined appearance property will simply override whatever was defined by the parent.
170: *
171: * Sorting
172: * =======
173: * Whether or not a column can be sorted depends on whether or not you define a SortByCommand (and
174: * subsequently a ReverseSortByComamnd) on the DataGridColumn itself. This SortByCommand is meant
175: * to be the SQL syntax used in an "ORDER BY" clause of a SQL query. This fits in really well
176: * with the CodeGen Framework's Class::LoadArrayByXXX() and Class::LoadAll() which takes "$strSortInfo"
177: * as an optional parameter.
178: * If a DataGrid is being sorted by a specific column, DataGrid::SortInfo will return to you the contents
179: * of DataGridColumn::SortByCommand (or ReverseSortByCommand if it's a reverse sort) for the specific
180: * column being sorted by. Therefore, you can set up your data source like:
181: * $dtgDataGrid->DataSource = Foo::LoadAll($dtgDataGrid->SortInfo);
182: *
183: * Pagination
184: * ==========
185: * Pagination can be toggled on and off with the DataGrid::Paginate flag. When enabling pagination, you
186: * must specify how many items, TOTAL, are in the full list (DataGrid::TotalItemCount). The DataGrid will
187: * then automatically calculate the SQL Limit information (as used in a "LIMIT" clause of a SQL query) to
188: * be used when querying a specific subset of the total list. As with sorting, this fits really well
189: * with the CodeGen Framework's LoadArray methods which takes $strLimitInfo" as an optional parameter.
190: * Moreover, the codegen also auto-generates CountBy methods for every LoadAll/LoadArray method it generates
191: * to assist with the DataGrid::TotalItemCount property:
192: * $dtgDataGrid->TotalItemCount = Foo::CountAll();
193: * $dtgDataGrid->DataSource = Foo::LoadAll($dtgDataGrid->SortInfo, $dtgDataGrid->LimitInfo);
194: *
195: * Due to a bug with PHP, you cannot set a property of a property. DataGrid's AlternateRowStyle, HeaderRowStyle and RowStyle
196: * are obviously instances of DataGridRowStyle objects which have properties in and of themselves.
197: * So unfortuantely, the following code will **NOT** work:
198: * $dtgDataGrid->RowStyle->BackColor = "blue";
199: * Instead, you will need to do the following:
200: * $objRowStyle = $dtgDataGrid->RowStyle;
201: * $objRowStyle->BackColor = "blue";
202: *
203: * @package Controls
204: * @property QDataGridLegacyRowStyle $AlternateRowStyle is the DataGridRowStyle object that defines how "alternating rows" should be displayed
205: * @property QDataGridLegacyRowStyle $HeaderRowStyle is the DataGridRowStyle object that defines how the "header row" should be displayed (attributes that get rendred in the header row's <tr>)
206: * @property QDataGridLegacyRowStyle $FilterRowStyle The row style for the filter row at the top
207: * @property QDataGridLegacyRowStyle $HeaderLinkStyle is the DataGridRowStyle object that defines how links, specifically, in the header row should be displayed. Basically, anything defined here will show up as html attributes and css styles within the '<a href="">' tag of the link, itself, in the header. Links in the header ONLY GET DISPLAYED when a column is sortable
208: * @property QDataGridLegacyRowStyle $RowStyle is the main or "default" DataGridRowStyle for the entire table. Any overriding row style (see "OverrideRowStyle(int, DataGridRowStyle)" below) or any appearance properties set in AlternateRowStyle or HeaderRowStyle will be applied in those specific situations. Any appearance properties NOT set in ovverrides, alternate, or header will simply default to what RowStyle has defined.
209: * @property integer $CellPadding refers the the HTML CellPadding attribute of the <table>. Not supported in HTML 5.
210: * @property integer $CellSpacing refers the the HTML CellSpacing attribute of the <table> Not supported in HTML 5.
211: * @property string $GridLines refers the the HTML rules attribute of the <table>. Not supported in HTML 5.
212: * @property boolean $ShowHeader is the flag of whether or not to show the Header row
213: * @property boolean $ShowFooter
214: * @property boolean $ShowFilter
215: * @property boolean $ShowFilterButton Should the filter button (on filter row) be shown?
216: * @property boolean $ShowFilterResetButton Should the reset filter button (on filter row) be shown?
217: * @property mixed $OrderByClause
218: * @property mixed $SortInfo
219: * @property-read integer $CurrentRowIndex is the current row index that is being rendered. Useful for render-helper functions that may get called when rendering the datagrid, itself
220: * @property integer $SortColumnIndex is the current column that is being "sorted by" (or -1 if none)
221: * @property integer $SortDirection specifies the direction of that sort, 0 for SortBy, and 1 for ReverseSortBy
222: * @property string $LabelForNoneFound Format string for the label to show on the paginator when no result is found
223: * @property string $LabelForOneFound Format string for the label to show on the paginator when one result is found
224: * @property string $LabelForMultipleFound Format string for the label to show on the paginator when more than one result is found
225: * @property string $LabelForPaginated Format string for the label to show on the paginator indicating current results window
226: * @property mixed $Owner
227: * @property mixed $FilterInfo
228: * @property mixed $Conditions
229: * @property-write QPaginatorBase $Paginator Paginator to go browse through results
230: * @property-write QPaginatorBase $PaginatorAlternate A second paginator to go browse through results (can be used at bottom)
231: * @property-write boolean $UseAjax Will the paginator use AJAX or not
232: * @property-write string $RowActionParameterHtml String which evaluates as the ActionParameter for row of the result.
233: * @property-read QButton $FilterButton Button to filter the results (if filtering is enabled)
234: * @property-read QWaitIcon $WaitIcon Wait icon (e.g. a spinner) to be shown while filtering is being done on the server
235: */
236: abstract class QDataGridLegacyBase extends QPaginatedControl {
237: // APPEARANCE
238: /** @var null|QDataGridLegacyRowStyle Row style for alternate rows */
239: protected $objAlternateRowStyle = null;
240: /** @var null|QDataGridLegacyRowStyle Style for the top row (not the filter row) */
241: protected $objHeaderRowStyle = null;
242: /** @var null|QDataGridLegacyRowStyle Style for the filter row (not the top row) */
243: protected $objFilterRowStyle = null;
244: /**
245: * @var null|QDataGridLegacyRowStyle[] List of styles for corresponding rows
246: * (in intRowNumber => objStyleObject style)
247: */
248: protected $objOverrideRowStyleArray = null;
249: /** @var null|QDataGridLegacyRowStyle Style object for the links header */
250: protected $objHeaderLinkStyle = null;
251: /** @var null|QDataGridLegacyRowStyle Row style for rown in the datagrid */
252: protected $objRowStyle = null;
253: /** @var string|QWaitIcon Wait icon for Ajax Actions */
254: protected $objWaitIcon = 'default';
255:
256: // LAYOUT
257: /** @var int CellPadding. Deprecated. */
258: protected $intCellPadding = -1;
259: /** @var int CellSpacing. Deprecated. */
260: protected $intCellSpacing = -1;
261: /** @var bool Show the header for the table? */
262: protected $blnShowHeader = true;
263: /** @var bool Determines if the filter row has to be shown or not */
264: protected $blnShowFilter = false;
265: /** @var bool Determines if the filter button has to be shown or not */
266: protected $blnShowFilterButton = true;
267: /** @var bool Determines if the reset button on the filter row has to be shown or not */
268: protected $blnShowFilterResetButton = true;
269: /** @var bool Determines of the footer has to be shown or not */
270: protected $blnShowFooter = false;
271: /** @var Deprecated. Rules attribute of table. Not supported in HTML 5. */
272: protected $strGridLines;
273:
274: // MISC
275: /** @var QDataGridLegacyColumn[] */
276: protected $objColumnArray;
277:
278: /** @var int Number of rows in the current render */
279: protected $intRowCount = 0;
280: /**
281: * @var int The index of the row being processed
282: * (This variable is used internally)
283: */
284: protected $intCurrentRowIndex;
285: /** @var int Column index by which we are sorting the results */
286: protected $intSortColumnIndex = -1;
287: /**
288: * @var int The index of column under consideration
289: * (This variable is used internally)
290: */
291: protected $intCurrentColumnId = 1;
292: /** @var int Sort is ascending (0) or descending (1) */
293: protected $intSortDirection = 0;
294:
295: /**
296: * @var string Format string for the label to show on the paginator when no result is found
297: * (Please see constructor for default values)
298: */
299: protected $strLabelForNoneFound;
300: /**
301: * @var string Format string for the label to show on the paginator when one result is found
302: * (Please see constructor for default values)
303: */
304: protected $strLabelForOneFound;
305: /**
306: * @var string Format string for the label to show on the paginator when more than one entry is found
307: * (Please see constructor for default values)
308: */
309: protected $strLabelForMultipleFound;
310: /**
311: * @var string Format string for the label to show on the paginator indicating current results window
312: * This format string is responsible for telling the user about the current chunk of
313: * results that he/she is viewing. It produces strings like:
314: * - Results: Viewing Cars 11-20 of 72
315: *
316: * Please see constructor for the way this format string is to be specified
317: */
318: protected $strLabelForPaginated;
319:
320: /**
321: * @var string String which evaluates as the ActionParameter for row of the result.
322: * This one is used for passing a string as an action parameter for the entire row
323: * and can be used inside an event handler which handles click on an entire row of the
324: * QDataGridLegacy. The event handler will recieve the value determined by the evaluation of
325: * this string.
326: *
327: */
328: protected $strRowActionParameterHtml;
329: /** @var QEvent[] List of events for which actions have to be taken*/
330: protected $objRowEventArray = array();
331: /** @var QAction[] List of actions to be taken (elements will correspond to the row event array) */
332: protected $objRowActionArray = array();
333:
334: /** @var null Owner table of this QDataGridLegacy (not being used right now) */
335: protected $objOwner = null;
336:
337: /** @var null|QButton Button for filtering results (is rendered on the filter row on top) */
338: protected $btnFilter = null;
339: /** @var array|QButton[] */
340: protected $btnFilterResetArray = array();
341: /** @var QControlProxy Proxy used for sorting QDataGridLegacy by a particular column */
342: protected $prxDatagridSorting;
343:
344: /**
345: * Add an action on a row of this QDataGridLegacy
346: *
347: * @param QEvent $objEvent Event object
348: * @param QAction $objAction Action object
349: */
350: public function AddRowAction($objEvent, $objAction) {
351: array_push($this->objRowEventArray, $objEvent);
352: array_push($this->objRowActionArray, $objAction);
353: }
354:
355: /**
356: * Constructor
357: *
358: * @param QControl|QControlBase|QForm $objParentObject Parent object
359: * @param null $strControlId Control ID (optional)
360: *
361: * @throws Exception|QCallerException
362: */
363: public function __construct($objParentObject, $strControlId = null) {
364: try {
365: parent::__construct($objParentObject, $strControlId);
366: } catch (QCallerException $objExc) {
367: $objExc->IncrementOffset();
368: throw $objExc;
369: }
370: $this->prxDatagridSorting = new QControlProxy($this);
371: $this->objRowStyle = new QDataGridLegacyRowStyle();
372: $this->objAlternateRowStyle = new QDataGridLegacyRowStyle();
373: $this->objHeaderRowStyle = new QDataGridLegacyRowStyle();
374: $this->objHeaderLinkStyle = new QDataGridLegacyRowStyle();
375: $this->objFilterRowStyle = new QDataGridLegacyRowStyle();
376:
377: // Labels
378: $this->strLabelForNoneFound = QApplication::Translate('<b>Results:</b> No %s found.');
379: $this->strLabelForOneFound = QApplication::Translate('<b>Results:</b> 1 %s found.');
380: $this->strLabelForMultipleFound = QApplication::Translate('<b>Results:</b> %s %s found.');
381: $this->strLabelForPaginated = QApplication::Translate('<b>Results:</b> Viewing %s %s-%s of %s.');
382:
383: $this->objColumnArray = array();
384:
385: // Setup Sorting Events
386: if ($this->blnUseAjax)
387: $this->prxDatagridSorting->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'Sort_Click'));
388: else
389: $this->prxDatagridSorting->AddAction(new QClickEvent(), new QServerControlAction($this, 'Sort_Click'));
390:
391: $this->prxDatagridSorting->AddAction(new QClickEvent(), new QTerminateAction());
392:
393: // The wait icon should only be created if the filter is shown
394: if($this->blnShowFilter) {
395: $this->objWaitIcon_Create();
396: }
397: }
398:
399: // Used to add a DataGridColumn to this DataGrid
400: /**
401: * Add a column to the QDataGridLegacy
402: *
403: * @param QDataGridLegacyColumn $objColumn The column to be added
404: */
405: public function AddColumn(QDataGridLegacyColumn $objColumn) {
406: $this->blnModified = true;
407: $this->objColumnArray[] = $objColumn;
408: }
409:
410: /**
411: * Add a column at a given index
412: *
413: * @param integer $intColumnIndex
414: * @param QDataGridLegacyColumn $objColumn
415: *
416: * @throws Exception|QCallerException|QIndexOutOfRangeException|QInvalidCastException
417: */
418: public function AddColumnAt($intColumnIndex, QDataGridLegacyColumn $objColumn) {
419: $this->blnModified = true;
420: try {
421: $intColumnIndex = QType::Cast($intColumnIndex, QType::Integer);
422: } catch (QInvalidCastException $objExc) {
423: $objExc->IncrementOffset();
424: throw $objExc;
425: }
426: if (($intColumnIndex < 0) ||
427: ($intColumnIndex > (count($this->objColumnArray))))
428: throw new QIndexOutOfRangeException($intColumnIndex, "AddColumnAt()");
429:
430: if ($intColumnIndex == 0) {
431: $this->objColumnArray = array_merge(array($objColumn), $this->objColumnArray);
432: } else {
433: $this->objColumnArray = array_merge(array_slice($this->objColumnArray, 0, $intColumnIndex),
434: array($objColumn),
435: array_slice($this->objColumnArray, $intColumnIndex));
436: }
437: }
438:
439: /**
440: * Removes a column from the datagrid given the index of the column
441: *
442: * @param integer $intColumnIndex The index of column which has to be removed
443: *
444: * @throws Exception
445: * @throws QCallerException
446: * @throws QIndexOutOfRangeException
447: * @throws QInvalidCastException
448: */
449: public function RemoveColumn($intColumnIndex) {
450: $this->blnModified = true;
451: try {
452: $intColumnIndex = QType::Cast($intColumnIndex, QType::Integer);
453: } catch (QInvalidCastException $objExc) {
454: $objExc->IncrementOffset();
455: throw $objExc;
456: }
457: if (($intColumnIndex < 0) ||
458: ($intColumnIndex > (count($this->objColumnArray) - 1)))
459: throw new QIndexOutOfRangeException($intColumnIndex, "RemoveColumn()");
460:
461: array_splice($this->objColumnArray, $intColumnIndex, 1);
462: }
463:
464: /**
465: * Removes a column from the datagrid (the first column that matches) given the name of the column
466: *
467: * @param string $strName Name of the column
468: */
469: public function RemoveColumnByName($strName) {
470: $this->blnModified = true;
471: for ($intIndex = 0; $intIndex < count($this->objColumnArray); $intIndex++)
472: if ($this->objColumnArray[$intIndex]->Name == $strName) {
473: array_splice($this->objColumnArray, $intIndex, 1);
474: return;
475: }
476: }
477:
478: /**
479: * Remove all column from the datagrid given the column name
480: *
481: * @param string $strName Name of the column
482: */
483: public function RemoveColumnsByName($strName) {
484: $this->blnModified = true;
485: for ($intIndex = 0; $intIndex < count($this->objColumnArray); $intIndex++)
486: if ($this->objColumnArray[$intIndex]->Name == $strName) {
487: array_splice($this->objColumnArray, $intIndex, 1);
488: $intIndex--;
489: }
490: }
491:
492: /**
493: * Remove all columns from the QDataGridLegacy
494: */
495: public function RemoveAllColumns() {
496: $this->blnModified = true;
497: $this->objColumnArray = array();
498: }
499:
500: /**
501: * Returns the array of all columns in the QDataGridLegacy
502: *
503: * @return array|QDataGridLegacyColumn[]
504: */
505: public function GetAllColumns() {
506: return $this->objColumnArray;
507: }
508:
509: /**
510: * Returns a column from the QDataGridLegacy given its index
511: *
512: * @param int $intColumnIndex Index of the column
513: *
514: * @return QDataGridLegacyColumn|null
515: */
516: public function GetColumn($intColumnIndex) {
517: if (array_key_exists($intColumnIndex, $this->objColumnArray)) {
518: return $this->objColumnArray[$intColumnIndex];
519: }
520:
521: return null;
522: }
523:
524: /**
525: * Returns a column from the QDataGridLegacy given its name
526: *
527: * @param string $strName Name of the column
528: *
529: * @return QDataGridLegacyColumn|null The found column
530: */
531: public function GetColumnByName($strName) {
532: if ($this->objColumnArray) {
533: foreach ($this->objColumnArray as $objColumn)
534: if ($objColumn->Name == $strName) {
535: return $objColumn;
536: }
537: }
538:
539: return null;
540: }
541:
542: /**
543: * Returns an array of columns of same name from the QDataGridLegacy
544: *
545: * @param string $strName Name of the column
546: *
547: * @return QDataGridLegacyColumn[] Array of found columns
548: */
549: public function GetColumnsByName($strName) {
550: $objColumnArrayToReturn = array();
551: if ($this->objColumnArray) {
552: foreach ($this->objColumnArray as $objColumn)
553: if ($objColumn->Name == $strName) {
554: array_push($objColumnArrayToReturn, $objColumn);
555: }
556: }
557:
558: return $objColumnArrayToReturn;
559: }
560:
561: /**
562: * Overrides the style of a QDataGridLegacy row given a style object
563: *
564: * If you want to override a SPECIFIC row's style, you can specify
565: * the RowIndex and the DataGridRowStyle with which to override
566: *
567: * @param int $intRowIndex Index of the row
568: * @param QDataGridLegacyRowStyle $objStyle Style object to be applied to the row
569: *
570: * @throws Exception|QCallerException|QInvalidCastException
571: */
572: public function OverrideRowStyle($intRowIndex, $objStyle) {
573: try {
574: $objStyle = QType::Cast($objStyle, "QDataGridLegacyRowStyle");
575: } catch (QInvalidCastException $objExc) {
576: $objExc->IncrementOffset();
577: throw $objExc;
578: }
579: $this->objOverrideRowStyleArray[$intRowIndex] = $objStyle;
580: }
581:
582: /**
583: * Wrapper around static function ParseHtml of this class
584: *
585: * @param QDataGridLegacyColumn $objColumn Column which is to be parsed
586: * @param mixed $objObject An element from DataSource which is interpreted as '$_ITEM' in template
587: *
588: * @return string
589: * @throws Exception|QCallerException
590: */
591: protected function ParseColumnHtml(QDataGridLegacyColumn $objColumn, $objObject) {
592: return QDataGridLegacyBase::ParseHtml($objColumn->Html, $this, $objColumn, $objObject);
593: }
594:
595: /**
596: * Parses the HTML written in the template and replaces the value of expressions and returns the HTML
597: * Used upon rendering to find backticks and perform PHP eval's
598: *
599: * @param string $strHtml Expression to be evaluated and converted into the final HTML
600: * @param QDataGridLegacyBase|QDataGridLegacy $objControl This object (the QDataGridLegacy)
601: * @param QDataGridLegacyColumn $objColumn Column which is to be parsed
602: * @param mixed $objObject An element from DataSource which is interpreted as '$_ITEM' in template
603: *
604: * @return string
605: * @throws Exception|QCallerException
606: */
607: public static function ParseHtml($strHtml, $objControl, $objColumn, $objObject) {
608: global $_FORM;
609:
610: /** @var mixed $_ITEM */
611: $_ITEM = $objObject;
612: /** @var QDataGridLegacyBase|QDataGridLegacy $_CONTROL */
613: $_CONTROL = $objControl;
614: /** @var QDataGridLegacyColumn $_COLUMN */
615: $_COLUMN = $objColumn;
616: $_OWNER = $objControl->Owner;
617:
618: $intPosition = 0;
619:
620: while (($intStartPosition = strpos($strHtml, '<?=', $intPosition)) !== false) {
621: $intEndPosition = strpos($strHtml, '?>', $intStartPosition);
622: if ($intEndPosition === false)
623: return $strHtml;
624: $strToken = substr($strHtml, $intStartPosition + 3, ($intEndPosition - $intStartPosition) - 3);
625: $strToken = trim($strToken);
626:
627: if ($strToken) {
628: // Because Eval doesn't utilize exception management, we need to do hack thru the PHP Error Handler
629: set_error_handler("DataGridEvalHandleError");
630: global $__exc_dtg_errstr;
631: $__exc_dtg_errstr = sprintf("Incorrectly formatted DataGrid HTML in %s: %s", $objControl->strControlId, $strHtml);
632:
633: try {
634: $strEvaledToken = eval(sprintf('return %s;', $strToken));
635: } catch (QCallerException $objExc) {
636: $objExc->DecrementOffset();
637: throw $objExc;
638: }
639:
640: // Restore the original error handler
641: set_error_handler("QcubedHandleError");
642: $__exc_dtg_errstr = null;
643: unset($__exc_dtg_errstr);
644: } else {
645: $strEvaledToken = '';
646: }
647: // fix for the not so magic __toString behaviour in PHP < 5.2.0
648: if (is_object($strEvaledToken)){
649: if($strEvaledToken instanceof QDateTime)
650: $strEvaledToken = $strEvaledToken->qFormat();
651: else
652: $strEvaledToken = $strEvaledToken->__toString();
653: }
654:
655: $strHtml = sprintf("%s%s%s",
656: substr($strHtml, 0, $intStartPosition),
657: $strEvaledToken,
658: substr($strHtml, $intEndPosition + 2));
659:
660: $intPosition = $intStartPosition + strlen($strEvaledToken);
661: }
662:
663: return $strHtml;
664: }
665:
666: /**
667: * This method does absolutely nothing and is here because of constraints (derived from an abstract class)
668: * This is because the Table, itself, should have no actions defined on it and should not be parsing anything
669: */
670: public function ParsePostData() {}
671:
672: /**
673: * Parse the _POST to see if the user is requesting a change in the sort column or page
674: * NOTE: This is an event handler (hence not all parameters of this method are used in its body)
675: *
676: * @param string $strFormId Form ID
677: * @param string $strControlId Control ID
678: * @param string $strParameter Action Parameter
679: *
680: * @throws Exception|QCallerException|QInvalidCastException
681: */
682: public function Sort_Click($strFormId, $strControlId, $strParameter) {
683: $this->blnModified = true;
684:
685: if (strlen($strParameter)) {
686: // Sorting
687: $intColumnIndex = QType::Cast($strParameter, QType::Integer);
688: $objColumn = $this->objColumnArray[$intColumnIndex];
689:
690: // First, reset pagination (if applicable)
691: if ($this->objPaginator) {
692: $this->PageNumber = 1;
693: }
694:
695: // First, make sure the Column is Sortable
696: if ($objColumn->OrderByClause) {
697: // It is
698:
699: // Are we currently sorting by this column?
700: if ($this->intSortColumnIndex == $intColumnIndex) {
701: // Yes we are currently sorting by this column
702:
703: // In Reverse?
704: if ($this->intSortDirection == 1) {
705: // Yep -- unreverse the sort
706: $this->intSortDirection = 0;
707: } else {
708: // Nope -- can we reverse?
709: if ($objColumn->ReverseOrderByClause) {
710: $this->intSortDirection = 1;
711: }
712: }
713: } else {
714: // Nope -- so let's set it to this column
715: $this->intSortColumnIndex = $intColumnIndex;
716: $this->intSortDirection = 0;
717: }
718: } else {
719: // It isn't -- clear all sort properties
720: $this->intSortDirection = 0;
721: $this->intSortColumnIndex = -1;
722: }
723: }
724: }
725:
726: /**
727: * Get the HTML for the paginator associated with this QDataGridLegacy
728: *
729: * @param QPaginator $objPaginator
730: *
731: * @return string HTML to be returned
732: */
733: protected function GetPaginatorRowHtml($objPaginator) {
734: $strToReturn = " <span class=\"paginator-control\">";
735: $strToReturn .= $objPaginator->Render(false);
736: $strToReturn .= "</span>\r\n <span class=\"paginator-results\">";
737: if ($this->TotalItemCount > 0) {
738: $intStart = (($this->PageNumber - 1) * $this->ItemsPerPage) + 1;
739: $intEnd = $intStart + count($this->DataSource) - 1;
740: $strToReturn .= sprintf($this->strLabelForPaginated,
741: $this->strNounPlural,
742: $intStart,
743: $intEnd,
744: $this->TotalItemCount);
745: } else {
746: $intCount = count($this->objDataSource);
747: if ($intCount == 0)
748: $strToReturn .= sprintf($this->strLabelForNoneFound, $this->strNounPlural);
749: else if ($intCount == 1)
750: $strToReturn .= sprintf($this->strLabelForOneFound, $this->strNoun);
751: else
752: $strToReturn .= sprintf($this->strLabelForMultipleFound, $intCount, $this->strNounPlural);
753: }
754:
755: $strToReturn .= "</span>\r\n";
756:
757: return $strToReturn;
758: }
759:
760: /**
761: * Returns the HTML for the row header if the result was sorted by that row
762: *
763: * @param QDataGridLegacyColumn $objColumn
764: *
765: * @return string HTML for the sorted header column
766: */
767: protected function GetHeaderSortedHtml(QDataGridLegacyColumn $objColumn) {
768: $strToReturn = sprintf('<span style="text-transform: uppercase;">%s</span>', $objColumn->Name);
769:
770: if ($this->intSortDirection == 0)
771: $strToReturn .= sprintf(' <img src="%s/sort_arrow.png" alt="Sorted" />', __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__);
772: else
773: $strToReturn .= sprintf(' <img src="%s/sort_arrow_reverse.png" alt="Reverse Sorted" />', __VIRTUAL_DIRECTORY__ . __IMAGE_ASSETS__);
774:
775: return $strToReturn;
776: }
777:
778: /**
779: * Returns the HTML for the header row
780: *
781: * @return string HTML for the header row
782: */
783: protected function GetHeaderRowHtml() {
784: $objHeaderStyle = $this->objRowStyle->ApplyOverride($this->objHeaderRowStyle);
785:
786: $strToReturn = sprintf(" <tr %s>\r\n", $objHeaderStyle->GetAttributes());
787: $intColumnIndex = 0;
788: if ($this->objColumnArray) foreach ($this->objColumnArray as $objColumn) {
789: if ($objColumn->OrderByClause) {
790: // This Column is Sortable
791: if ($intColumnIndex == $this->intSortColumnIndex)
792: $strName = $this->GetHeaderSortedHtml($objColumn);
793: else
794: $strName = $objColumn->Name;
795:
796: $this->mixActionParameter = $intColumnIndex;
797:
798: $strToReturn .= sprintf(" <th %s><a id=\"%s\" href=\"%s\" %s%s>%s</a></th>\r\n",
799: $this->objHeaderRowStyle->GetAttributes(),
800: $this->ControlId . "_col_" . $intColumnIndex,
801: QApplication::$RequestUri,
802: $this->prxDatagridSorting->RenderAsEvents($intColumnIndex, true, $this->ControlId . "_col_" . $intColumnIndex, false),
803: $this->objHeaderLinkStyle->GetAttributes(),
804: $strName);
805: }
806: elseif ($objColumn instanceof QCheckBoxLegacyColumn) {
807: $strCheck = $objColumn->chkSelectAll_Render(true);
808: $strToReturn .= sprintf(" <th %s>%s</th>\r\n", $this->objHeaderRowStyle->GetAttributes(), $strCheck);
809: }
810: else
811: $strToReturn .= sprintf(" <th %s>%s</th>\r\n", $this->objHeaderRowStyle->GetAttributes(), $objColumn->Name);
812: $intColumnIndex++;
813: }
814: $strToReturn .= " </tr>\r\n";
815:
816: return $strToReturn;
817: }
818:
819: /**
820: * @param $objObject
821: *
822: * @return string HTML of the row
823: * @throws Exception
824: * @throws QCallerException
825: */
826: protected function GetDataGridRowHtml($objObject) {
827: // Get the Default Style
828: $objStyle = $this->objRowStyle;
829:
830: // Iterate through the Columns
831: $strColumnsHtml = '';
832: foreach ($this->objColumnArray as $objColumn) {
833: try {
834: $strHtml = $this->ParseColumnHtml($objColumn, $objObject);
835:
836: if ($objColumn->HtmlEntities)
837: $strHtml = QApplication::HtmlEntities($strHtml);
838:
839: // For IE
840: if (QApplication::IsBrowser(QBrowserType::InternetExplorer) &&
841: ($strHtml == ''))
842: $strHtml = ' ';
843: } catch (QCallerException $objExc) {
844: $objExc->IncrementOffset();
845: throw $objExc;
846: }
847: $strColumnsHtml .= sprintf(" <td %s>%s</td>\r\n", $objColumn->GetAttributes(), $strHtml);
848: }
849:
850: // Apply AlternateRowStyle (if applicable)
851: if (($this->intCurrentRowIndex % 2) == 1)
852: $objStyle = $objStyle->ApplyOverride($this->objAlternateRowStyle);
853:
854: // Apply any Style Override (if applicable)
855: if ((is_array($this->objOverrideRowStyleArray)) &&
856: (array_key_exists($this->intCurrentRowIndex, $this->objOverrideRowStyleArray)) &&
857: (!is_null($this->objOverrideRowStyleArray[$this->intCurrentRowIndex])))
858: $objStyle = $objStyle->ApplyOverride($this->objOverrideRowStyleArray[$this->intCurrentRowIndex]);
859:
860: // Make the event string
861: $strTrId = sprintf("%srow%s", $this->strControlId, $this->intCurrentRowIndex);
862: $numEvents = count($this->objRowEventArray);
863: if ($numEvents > 0) {
864: $this->RemoveChildControl($strTrId, true);
865: $objRow = new QDataGridLegacyRow($this, $strTrId);
866: // add all the row actions to this proxy
867: for ($i = 0; $i < $numEvents; ++$i) {
868: $objRow->AddAction($this->objRowEventArray[$i], $this->objRowActionArray[$i]);
869: }
870: $objRow->Style = $objStyle;
871: // parse the action parameter
872: $objRow->ActionParameter = QDataGridLegacyBase::ParseHtml($this->strRowActionParameterHtml, $this, null, $objObject);
873: $strToReturn = $objRow->GetHtml($strColumnsHtml);
874: QApplication::ExecuteJavaScript($objRow->RenderActionScripts());
875: } else {
876: // If there are no events, don't create any row controls'
877: // Finish up
878: $strToReturn = sprintf('<tr id="%s" %s>%s</tr>', $strTrId, $objStyle->GetAttributes(), $strColumnsHtml);
879: }
880: $this->intCurrentRowIndex++;
881: return $strToReturn;
882: }
883:
884: /**
885: * Returns the footer row HTML
886: * NOTE: This function currently does nothing but is available for overriding
887: */
888: protected function GetFooterRowHtml() {}
889:
890: /**
891: * Returns the HTML for creating the QDataGridLegacy on user's browser
892: *
893: * @return string The HTML for the QDataGridLegacy
894: * @throws Exception|QCallerException
895: */
896: protected function GetControlHtml() {
897: return $this->RenderTag('table',
898: null,
899: null,
900: $this->GetInnerHtml());
901: }
902:
903: protected function GetInnerHtml() {
904: $this->DataBind();
905:
906: $strToReturn = '';
907:
908: // Paginator Row (if applicable)
909: if ($this->objPaginator) {
910: // TODO: caption is not the right tag here. It should be a nav or menu, but these are not allowed inside a table tag.
911: $strToReturn .= "<caption>\r\n" . $this->GetPaginatorRowHtml($this->objPaginator) . "</caption>\r\n";
912: }
913: // bottom paginator
914: //if ($this->objPaginatorAlternate)
915: // $strToReturn .= "<caption align=\"bottom\">\r\n" . $this->GetPaginatorRowHtml($this->objPaginatorAlternate) . "</caption>\r\n";
916:
917: // Header Row (if applicable)
918: if ($this->blnShowHeader)
919: {
920: $strToReturn .= "<thead>\r\n" . $this->GetHeaderRowHtml();
921:
922: // Filter Row (if applicable)
923: if ($this->blnShowFilter)
924: $strToReturn .= $this->GetFilterRowHtml();
925:
926: $strToReturn .= "</thead>\r\n";
927: }
928:
929: // Footer Row (if applicable)
930: if ($this->blnShowFooter)
931: $strToReturn .= "<tfoot>\r\n" . $this->GetFooterRowHtml() . "</tfoot>\r\n";
932:
933: // DataGrid Rows
934: $strToReturn .= "<tbody>\r\n";
935: $this->intCurrentRowIndex = 0;
936: if ($this->objDataSource)
937: foreach ($this->objDataSource as $objObject)
938: $strToReturn .= $this->GetDataGridRowHtml($objObject);
939: // Cleanup all the extra rows from the previous rendering
940: for ($i = $this->intCurrentRowIndex; $i < $this->intRowCount; ++$i) {
941: $strTrId = sprintf("%srow%s", $this->strControlId, $i);
942: $this->RemoveChildControl($strTrId, true);
943: }
944: $this->intRowCount = $this->intCurrentRowIndex;
945:
946: $strToReturn .= "</tbody>\r\n";
947:
948: // Finish Up
949: $this->objDataSource = null;
950: return $strToReturn;
951:
952: }
953:
954: /**
955: * Create the row used for datagrid filtering
956: *
957: * @return string $strToReturn of html table row
958: */
959: protected function GetFilterRowHtml() {
960: $objFilterStyle = $this->objRowStyle->ApplyOverride($this->objFilterRowStyle);
961: $strToReturn = sprintf(' <tr %s>'."\r\n", $objFilterStyle->GetAttributes());
962: if($this->objColumnArray !== null)
963: {
964: $blnResetButtonRendered = false;
965: for ($intIndex = 0; $intIndex < count($this->objColumnArray); $intIndex++)
966: {
967: $objColumn = $this->objColumnArray[$intIndex];
968:
969: $colContent = ' ';
970:
971: if ($objColumn->HasFilter())
972: {
973: // This Column is Filterable
974: $ctlFilter = $this->GetFilterControl($objColumn);
975:
976: if(null !== $ctlFilter)
977: //display the control
978: $colContent = $ctlFilter->Render(false);
979: }
980: if ($this->ShowFilterResetButton) {
981: if (!$blnResetButtonRendered && $intIndex == count($this->objColumnArray) -1) {
982: // no column has the reset button, but ShowFilterResetButton is true
983: // put the reset button in the last column
984: $objColumn->HasResetButton = true;
985: }
986: $btnReset = $this->GetResetButton($objColumn);
987: if (null !== $btnReset) {
988: $colContent .= $btnReset->Render(false);
989: $blnResetButtonRendered = true;
990: }
991: }
992:
993: //show the filter button in the last column (if set to)
994: if($intIndex == count($this->objColumnArray) -1)
995: {
996: $btnFilter = $this->FilterButton;
997: if (null !== $btnFilter) {
998: $colContent .= $btnFilter->Render(false);
999: }
1000: $colContent .= $this->objWaitIcon->Render(false);
1001: }
1002:
1003: $strToReturn .= sprintf(' <th %s>%s</th>'."\r\n",
1004: $this->objFilterRowStyle->GetAttributes(),
1005: $colContent);
1006: }
1007: }
1008: $strToReturn .= ' </tr>'."\r\n";
1009: return $strToReturn;
1010: }
1011:
1012: /**
1013: * Returns the filter control ID for a column
1014: * @param QDataGridLegacyColumn $objColumn
1015: *
1016: * @return string The column filter control ID
1017: */
1018: protected function GetColumnFilterControlId(QDataGridLegacyColumn $objColumn) {
1019: if ($objColumn->FilterColId === null)
1020: $objColumn->FilterColId = $this->intCurrentColumnId++;
1021: return 'ctl'.$this->ControlId.'flt'.$objColumn->FilterColId;
1022: }
1023:
1024: /**
1025: * Returns filter control for a given QDataGridLegacyColumn
1026: *
1027: * @param QDataGridLegacyColumn $objColumn The column whose filter control is needed
1028: *
1029: * @return QTextBox|QListBox
1030: */
1031: public function GetFilterControl(QDataGridLegacyColumn $objColumn) {
1032: $strControlId = $this->GetColumnFilterControlId($objColumn);
1033: //find/build the control
1034: if(($ctlFilter = $this->GetChildControl($strControlId)) === null)
1035: //create the control this first time
1036: $ctlFilter = $this->CreateFilterControl($strControlId, $objColumn);
1037: return $ctlFilter;
1038: }
1039:
1040: /**
1041: * Returns the reset button for the QDataGridLegacy which will be used in the filter row
1042: * (filter row is the row at the top for filtering data)
1043: *
1044: * @param QDataGridLegacyColumn $objColumn
1045: *
1046: * @return null|QButton
1047: */
1048: public function GetResetButton(QDataGridLegacyColumn $objColumn) {
1049: if (!$this->ShowFilterResetButton)
1050: return null;
1051: if (!$objColumn->HasResetButton)
1052: return null;
1053:
1054: $strControlId = $this->GetColumnFilterControlId($objColumn);
1055: if (!array_key_exists($strControlId, $this->btnFilterResetArray)) {
1056: return ($this->btnFilterResetArray[$strControlId] = $this->btnFilterReset_Create());
1057: }
1058: return $this->btnFilterResetArray[$strControlId];
1059: }
1060:
1061: /**
1062: * CreateControls used in the filter row and set their fiter values if available.
1063: * NOTE: this function, btnReset_Click and GetControlValue are the functions to override/change if you want to add new types
1064: *
1065: * @param string $strControlId id based on the column that the control is contained
1066: * @param QDataGridLegacyColumn $objColumn the QDataGridLegacyColumn that contains the filter data.
1067: *
1068: * @return QControl $control the input control used for filtering
1069: */
1070: protected function CreateFilterControl($strControlId, QDataGridLegacyColumn $objColumn) {
1071: //show the current filter in the control
1072: $value = $objColumn->GetActiveFilterValue();
1073:
1074: #region Create the control
1075: //create the appropriate kind of control
1076: $actionName = 'btnFilter_Click';
1077: $ctlFilter = null;
1078: switch($objColumn->FilterType)
1079: {
1080: default:
1081: case QFilterType::TextFilter:
1082: $ctlFilter = $this->filterTextBox_Create($strControlId, $objColumn->Name, $objColumn->FilterBoxSize, $value);
1083: break;
1084: case QFilterType::ListFilter:
1085: $ctlFilter = $this->filterListBox_Create($strControlId, $objColumn->Name, $objColumn->FilterList, $value);
1086: break;
1087: }
1088: #endregion
1089:
1090: if(null !== $ctlFilter)
1091: {
1092: //make sure hitting enter applies the filter
1093: if ($this->blnUseAjax)
1094: $ctlFilter->AddAction(new QEnterKeyEvent(), new QAjaxControlAction($this, $actionName, $this->objWaitIcon));
1095: else
1096: $ctlFilter->AddAction(new QEnterKeyEvent(), new QServerControlAction($this, $actionName));
1097:
1098: $ctlFilter->AddAction(new QEnterKeyEvent(), new QTerminateAction());
1099: }
1100:
1101: return $ctlFilter;
1102: }
1103:
1104: /**
1105: * Get the control's filter input for filtering
1106: *
1107: * @param string $strFilterType id based on the column that the control is contained
1108: * @param QLabel|QListBox|QControl $ctlControl the filter control to get the filter input
1109: *
1110: * @return string The input used for filtering
1111: */
1112: //this, btnReset_Click and CreateControl are the functions to override/change if you want to add new types
1113: protected function GetFilterControlValue($strFilterType, $ctlControl) {
1114: //depending on the control, the members used to store the value are different
1115: $strValue = null;
1116: switch($strFilterType) {
1117: default:
1118: case QFilterType::TextFilter:
1119: $strValue = $ctlControl->Text;
1120: if($strValue == '')
1121: $strValue = null;
1122: break;
1123: case QFilterType::ListFilter:
1124: $strValue = $ctlControl->SelectedValue;
1125: break;
1126: }
1127: return $strValue;
1128: }
1129:
1130: /**
1131: * This function creates a textbox suitable for the filter bar
1132: *
1133: * @param string $strControlId id based on the column that the control is contained
1134: * @param string $strControlName The name to give the textbox
1135: * @param int $columns The Columns setting to use for the textbox
1136: * @param string $strValue The text to fill the textbox with
1137: *
1138: * @return QTextBox the resulting textbox
1139: */
1140: protected function filterTextBox_Create($strControlId, $strControlName, $columns, $strValue) {
1141: $ctlFilterTextBox = new QTextBox($this, $strControlId);
1142: $ctlFilterTextBox->Name = $strControlName;
1143: $ctlFilterTextBox->Text = QType::Cast($strValue, QType::String);
1144: $ctlFilterTextBox->FontSize = $this->RowStyle->FontSize;
1145: $ctlFilterTextBox->Columns = $columns;
1146:
1147: return $ctlFilterTextBox;
1148: }
1149:
1150: /**
1151: * This function creates a listbox suitable for the filter bar
1152: *
1153: * @param string $strControlId id based on the column that the control is contained
1154: * @param string $strControlName The name to give the textbox
1155: * @param string[] $arrListValues A name=>value array of items to add to the list
1156: * @param string $strSelectedValue The value to start selected
1157: *
1158: * @return QListBox the resulting listbox
1159: */
1160: protected function filterListBox_Create($strControlId, $strControlName, $arrListValues, $strSelectedValue)
1161: {
1162: $ctlFilterListbox = new QListBox($this, $strControlId);
1163: $ctlFilterListbox->Name = $strControlName;
1164: $ctlFilterListbox->AddItem('-'.QApplication::Translate('Any').'-');
1165: $ctlFilterListbox->FontSize = $this->RowStyle->FontSize;
1166: $ctlFilterListbox->Width = 'auto';
1167:
1168: //Now fill up the advanced list
1169: foreach (array_keys($arrListValues) as $strFilterName) {
1170: $ctlFilterListbox->AddItem($strFilterName,$strFilterName);
1171: }
1172: $ctlFilterListbox->SelectedName = $strSelectedValue;
1173: return $ctlFilterListbox;
1174: }
1175:
1176:
1177: /**
1178: * Creates the reset button for the filter row
1179: *
1180: * @return QButton
1181: *
1182: */
1183: protected function btnFilterReset_Create() {
1184: $btnFilterReset = new QButton($this);
1185: $btnFilterReset->Text = QApplication::Translate('Reset');;
1186:
1187: if ($this->blnUseAjax)
1188: $btnFilterReset->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnFilterReset_Click', $this->objWaitIcon));
1189: else
1190: $btnFilterReset->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnFilterReset_Click'));
1191: $btnFilterReset->AddAction(new QClickEvent(), new QTerminateAction());
1192: return $btnFilterReset;
1193: }
1194:
1195:
1196: /**
1197: * Creates the Filter button for the filter row
1198: *
1199: * @return QButton
1200: */
1201: protected function btnFilter_Create()
1202: {
1203: $btnFilter = new QButton($this);
1204: $btnFilter->Name = QApplication::Translate('Filter');
1205: $btnFilter->Text = QApplication::Translate('Filter');
1206:
1207: if ($this->blnUseAjax)
1208: $btnFilter->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnFilter_Click', $this->objWaitIcon));
1209: else
1210: $btnFilter->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnFilter_Click'));
1211:
1212: $btnFilter->AddAction(new QClickEvent(), new QTerminateAction());
1213:
1214: $btnFilter->CausesValidation = false;
1215: return $btnFilter;
1216: }
1217:
1218:
1219: /**
1220: * Creates the objWaitIcon for this datagrid, since we don't want to use the default form one
1221: *
1222: * @return void
1223: *
1224: */
1225: protected function objWaitIcon_Create()
1226: {
1227: $this->objWaitIcon = new QWaitIcon($this);
1228: }
1229:
1230: /**
1231: * For each column, get its input filter value and set the columns filter with it.
1232: * NOTE: this is a regular event handler method
1233: *
1234: * @param string $strFormId Form ID of the form in which this data grid is defined
1235: * @param string $strControlId Control ID of the datagrid
1236: * @param string $strParameter Action parameter
1237: */
1238: public function btnFilter_Click($strFormId, $strControlId, $strParameter) {
1239: //set the filter commands
1240: foreach($this->objColumnArray as $objColumn)
1241: {
1242: if ($objColumn->HasFilter())
1243: {
1244: //a filter is defined for this column
1245: $ctlFilter = $this->GetChildControl($this->GetColumnFilterControlId($objColumn));
1246: if($ctlFilter !== null)
1247: {
1248: //we've found the control that has it's value
1249: $strValue = $this->GetFilterControlValue($objColumn->FilterType, $ctlFilter);
1250: $objColumn->SetActiveFilterState($strValue);
1251:
1252: }
1253: }
1254: }
1255: //reset to page 1
1256: if ($this->objPaginator)
1257: $this->PageNumber = 1;
1258:
1259: $this->DataBind();
1260:
1261: $this->MarkAsModified();
1262: }
1263:
1264: /**
1265: * Clear all filter column control input values.
1266: */
1267: public function ClearFilters()
1268: {
1269: foreach($this->objColumnArray as $objColumn)
1270: {
1271: if($objColumn->HasFilter())
1272: {
1273: //both modes clear in the same way
1274: /**
1275: * @var QLabel|QListBox|QControl $ctlFilter
1276: */
1277: $ctlFilter = $this->GetChildControl($this->GetColumnFilterControlId($objColumn));
1278: if ($ctlFilter !== null)
1279: {
1280: switch($objColumn->FilterType)
1281: {
1282: default:
1283: case QFilterType::TextFilter:
1284: $ctlFilter->Text = '';
1285: break;
1286: case QFilterType::ListFilter:
1287: $ctlFilter->SelectedIndex = 0;
1288: break;
1289: }
1290: $objColumn->ClearFilter();
1291: }
1292: }
1293: }
1294:
1295: //reset to page 1
1296: if ($this->objPaginator)
1297: $this->PageNumber = 1;
1298: $this->MarkAsModified();
1299: }
1300:
1301: /**
1302: * Click handler for the reset button
1303: * NOTE: This function, GetControlValue and CreateControl are the functions to override/change if you want to add new types
1304: *
1305: * @param string $strFormId Form ID
1306: * @param string $strControlId Control ID
1307: * @param string $strParameter Action Parameter
1308: */
1309: public function btnFilterReset_Click($strFormId, $strControlId, $strParameter) {
1310: $this->ClearFilters();
1311: }
1312:
1313: /**
1314: * Set Filter values (used to restore a previously saved state)
1315: *
1316: * @param array $filters array of filters indexed by column name
1317: * contain either a string or a filter object
1318: */
1319: public function SetFilters($filters) {
1320: foreach($this->objColumnArray as $col) {
1321: if (isset($filters[$col->Name])) {
1322: $value = $filters[$col->Name];
1323: $col->SetActiveFilterState($value);
1324: $objFilterControl = $this->GetFilterControl($col);
1325: switch($col->FilterType)
1326: {
1327: default:
1328: case QFilterType::TextFilter:
1329: $objFilterControl->Text = $value;
1330: break;
1331: case QFilterType::ListFilter:
1332: $objFilterControl->SelectedName = $value;
1333: break;
1334: }
1335:
1336: }
1337: }
1338: }
1339:
1340: /**
1341: * Get Filter values from each column (used to save a state)
1342: *
1343: * @return array $filters array of filters indexed by column name
1344: */
1345: public function GetFilters() {
1346: $filters = array();
1347: foreach($this->objColumnArray as $col) {
1348: $activeFilterState = $col->GetActiveFilterState();
1349: if ($activeFilterState !== null)
1350: $filters[$col->Name] = $activeFilterState;
1351: }
1352: return $filters;
1353: }
1354:
1355: /**
1356: * Returns the current state of the control to be able to restore it later.
1357: * @return mixed
1358: */
1359: public function GetState() {
1360: $state = array();
1361: if ($this->SortColumnIndex != -1) {
1362: $state["c"] = $this->GetColumn($this->SortColumnIndex)->Name;
1363: $state["d"] = $this->SortDirection;
1364: }
1365: if ($this->Paginator || $this->PaginatorAlternate) {
1366: $state["p"] = $this->PageNumber;
1367: }
1368: return $state;
1369: }
1370:
1371: /**
1372: * Restore the state of the control.
1373: * @param mixed $state Previously saved state as returned by GetState above.
1374: */
1375: public function PutState($state) {
1376: // use the name as the column key because columns might be added or removed for some reason
1377: if (isset ($state["c"])) {
1378: $a = $this->GetAllColumns();
1379: for ($i = 0; $i < count($a); $i++) {
1380: if ($a[$i]->Name == $state["c"]) {
1381: $this->SortColumnIndex = $i;
1382: break;
1383: }
1384: }
1385: }
1386: if (isset ($state["d"])) {
1387: $this->SortDirection = $state["d"];
1388: }
1389: if (isset ($state["p"]) &&
1390: ($this->Paginator || $this->PaginatorAlternate)) {
1391:
1392: $this->PageNumber = $state["p"];
1393: }
1394: }
1395:
1396:
1397: /////////////////////////
1398: // Public Properties: GET
1399: /////////////////////////
1400: /**
1401: * PHP magic method
1402: *
1403: * @param string $strName
1404: *
1405: * @return mixed
1406: * @throws Exception|QCallerException
1407: */
1408: public function __get($strName) {
1409: switch ($strName) {
1410: // APPEARANCE
1411: case "AlternateRowStyle": return $this->objAlternateRowStyle;
1412: case "HeaderRowStyle": return $this->objHeaderRowStyle;
1413: case "FilterRowStyle": return $this->objFilterRowStyle;
1414: case "HeaderLinkStyle": return $this->objHeaderLinkStyle;
1415: case "RowStyle": return $this->objRowStyle;
1416:
1417: // LAYOUT
1418: case "CellPadding": return $this->intCellPadding;
1419: case "CellSpacing": return $this->intCellSpacing;
1420: case "GridLines": return $this->strGridLines;
1421: case "ShowHeader": return $this->blnShowHeader;
1422: case "ShowFooter": return $this->blnShowFooter;
1423: case "ShowFilter": return $this->blnShowFilter;
1424: case "ShowFilterButton": return $this->blnShowFilterButton;
1425: case 'ShowFilterResetButton': return $this->blnShowFilterResetButton;
1426:
1427: // MISC
1428: case "OrderByClause":
1429: if ($this->intSortColumnIndex >= 0) {
1430: if ($this->intSortDirection == 0)
1431: return $this->objColumnArray[$this->intSortColumnIndex]->OrderByClause;
1432: else
1433: return $this->objColumnArray[$this->intSortColumnIndex]->ReverseOrderByClause;
1434: } else
1435: return null;
1436: case "SortInfo":
1437: if ($this->intSortColumnIndex >= 0) {
1438: if ($this->intSortDirection == 0) {
1439: $mixToReturn = $this->objColumnArray[$this->intSortColumnIndex]->SortByCommand;
1440: if ($mixToReturn instanceof QQOrderBy)
1441: return $mixToReturn->GetAsManualSql();
1442: else
1443: return $mixToReturn;
1444: } else {
1445: $mixToReturn = $this->objColumnArray[$this->intSortColumnIndex]->ReverseSortByCommand;
1446: if ($mixToReturn instanceof QQOrderBy)
1447: return $mixToReturn->GetAsManualSql();
1448: else
1449: return $mixToReturn;
1450: }
1451: } else
1452: return null;
1453:
1454: case "CurrentRowIndex": return $this->intCurrentRowIndex;
1455: case "SortColumnIndex": return $this->intSortColumnIndex;
1456: case "SortDirection": return $this->intSortDirection;
1457:
1458: case 'LabelForNoneFound': return $this->strLabelForNoneFound;
1459: case 'LabelForOneFound': return $this->strLabelForOneFound;
1460: case 'LabelForMultipleFound': return $this->strLabelForMultipleFound;
1461: case 'LabelForPaginated': return $this->strLabelForPaginated;
1462: case 'Owner' : return $this->objOwner;
1463:
1464: case "FilterInfo":
1465: $filterArray = array();
1466: foreach($this->objColumnArray as $col)
1467: {
1468: $colFilterInfo = $col->FilterInfo;
1469: if ($colFilterInfo) {
1470: $filterArray[] = $colFilterInfo;
1471: }
1472: }
1473: return $filterArray;
1474: case "Conditions":
1475: //Calculate the conditions to apply to the entire grid based on the column's filters
1476: $dtgConditions = QQ::All();
1477: foreach($this->objColumnArray as $objColumn)
1478: {
1479: $colCondition = null;
1480: //if there's a normal filter type, return the QQConditions related to it
1481: if ($objColumn->FilterType != QFilterType::None) {
1482: $colCondition = null;
1483: if ($objColumn->ActiveFilter !== null)
1484: $colCondition = $objColumn->ActiveFilter;
1485: }
1486:
1487: /*FilterConstant allows us to specify a custom QQuery that applies in addition to any
1488: user-specified filters. EG: If the user enters a Cost to filter on, also filter on
1489: object actually being sold*/
1490: if(null !== $colCondition && null !== $objColumn->FilterConstant)
1491: $colCondition = QQ::AndCondition($colCondition, $objColumn->FilterConstant);
1492:
1493: //now after all the above checks if the column has a condition to be specified
1494: //we add it to overall conditions. but if the column conditions are null we leave
1495: //overall conditions as is
1496: if($colCondition !== null) {
1497: //if there are no overall conditions yet change them to reflect the column condition
1498: if($dtgConditions == QQ::All())
1499: $dtgConditions = $colCondition;
1500: else
1501: //combine the overall conditions with the column conditions
1502: $dtgConditions = QQ::AndCondition($dtgConditions, $colCondition);
1503: }
1504: }
1505:
1506: return $dtgConditions;
1507:
1508: case "FilterButton":
1509: if (!$this->ShowFilterButton)
1510: return null;
1511: if (null !== $this->btnFilter)
1512: return $this->btnFilter;
1513: return ($this->btnFilter = $this->btnFilter_Create());
1514:
1515: case "WaitIcon": return $this->objWaitIcon;
1516:
1517: default:
1518: try {
1519: return parent::__get($strName);
1520: } catch (QCallerException $objExc) {
1521: $objExc->IncrementOffset();
1522: throw $objExc;
1523: }
1524: }
1525: }
1526:
1527:
1528: /////////////////////////
1529: // Public Properties: SET
1530: /////////////////////////
1531: /**
1532: * PHP magic method
1533: *
1534: * @param string $strName
1535: * @param string $mixValue
1536: *
1537: * @return mixed
1538: * @throws Exception
1539: * @throws QCallerException
1540: * @throws QInvalidCastException
1541: */
1542: public function __set($strName, $mixValue) {
1543: switch ($strName) {
1544: // APPEARANCE
1545: case "AlternateRowStyle":
1546: try {
1547: $this->objAlternateRowStyle = QType::Cast($mixValue, "QDataGridLegacyRowStyle");
1548: break;
1549: } catch (QInvalidCastException $objExc) {
1550: $objExc->IncrementOffset();
1551: throw $objExc;
1552: }
1553: case "HeaderRowStyle":
1554: try {
1555: $this->objHeaderRowStyle = QType::Cast($mixValue, "QDataGridLegacyRowStyle");
1556: break;
1557: } catch (QInvalidCastException $objExc) {
1558: $objExc->IncrementOffset();
1559: throw $objExc;
1560: }
1561: case "HeaderLinkStyle":
1562: try {
1563: $this->objHeaderLinkStyle = QType::Cast($mixValue, "QDataGridLegacyRowStyle");
1564: break;
1565: } catch (QInvalidCastException $objExc) {
1566: $objExc->IncrementOffset();
1567: throw $objExc;
1568: }
1569: case "FilterRowStyle":
1570: try {
1571: $this->objFilterRowStyle = QType::Cast($mixValue, "QDataGridLegacyRowStyle");
1572: break;
1573: } catch (QInvalidCastException $objExc) {
1574: $objExc->IncrementOffset();
1575: throw $objExc;
1576: }
1577: case "RowStyle":
1578: try {
1579: $this->objRowStyle = QType::Cast($mixValue, "QDataGridLegacyRowStyle");
1580: break;
1581: } catch (QInvalidCastException $objExc) {
1582: $objExc->IncrementOffset();
1583: throw $objExc;
1584: }
1585:
1586: // BEHAVIOR
1587: case "Paginator":
1588: //do whatever needs done
1589: try {
1590: $blnToReturn = parent::__set($strName, $mixValue);
1591: } catch (QInvalidCastException $objExc) {
1592: $objExc->IncrementOffset();
1593: throw $objExc;
1594: }
1595:
1596: //Now make sure it knows about our spinner
1597: $this->objPaginator->WaitIcon = $this->objWaitIcon;
1598: return $blnToReturn;
1599: break;
1600:
1601: case "PaginatorAlternate":
1602: //do whatever needs done
1603: try {
1604: $blnToReturn = parent::__set($strName, $mixValue);
1605: } catch (QInvalidCastException $objExc) {
1606: $objExc->IncrementOffset();
1607: throw $objExc;
1608: }
1609:
1610: //Now make sure it knows about our spinner
1611: $this->objPaginatorAlternate->WaitIcon = $this->objWaitIcon;
1612: return $blnToReturn;
1613: break;
1614: case "UseAjax":
1615: try {
1616: $blnToReturn = parent::__set($strName, $mixValue);
1617: } catch (QInvalidCastException $objExc) {
1618: $objExc->IncrementOffset();
1619: throw $objExc;
1620: }
1621:
1622: // Because we are switching to/from Ajax, we need to reset the events
1623: $this->prxDatagridSorting->RemoveAllActions(QClickEvent::EventName);
1624: if ($this->blnUseAjax)
1625: $this->prxDatagridSorting->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'Sort_Click', $this->objWaitIcon));
1626: else
1627: $this->prxDatagridSorting->AddAction(new QClickEvent(), new QServerControlAction($this, 'Sort_Click'));
1628:
1629: $this->prxDatagridSorting->AddAction(new QClickEvent(), new QTerminateAction());
1630:
1631: $actionName = 'btnFilter_Click';
1632: foreach($this->objColumnArray as $objColumn) {
1633: if ($objColumn->HasFilter())
1634: {
1635: $ctlFilter = $this->GetChildControl($this->GetColumnFilterControlId($objColumn));
1636: if ($ctlFilter !== null)
1637: {
1638: $ctlFilter->RemoveAllActions(QKeyDownEvent::EventName);
1639: if ($this->blnUseAjax)
1640: $ctlFilter->AddAction(new QEnterKeyEvent(), new QAjaxControlAction($this, $actionName, $this->objWaitIcon));
1641: else
1642: $ctlFilter->AddAction(new QEnterKeyEvent(), new QServerControlAction($this, $actionName));
1643:
1644: $ctlFilter->AddAction(new QEnterKeyEvent(), new QTerminateAction());
1645: }
1646: }
1647: }
1648:
1649: if (null !== $this->btnFilter) {
1650: $this->btnFilter->RemoveAllActions(QClickEvent::EventName);
1651: if ($this->blnUseAjax)
1652: $this->btnFilter->AddAction(new QClickEvent(), new QAjaxControlAction($this, $actionName, $this->objWaitIcon));
1653: else
1654: $this->btnFilter->AddAction(new QClickEvent(), new QServerControlAction($this, $actionName));
1655: $this->btnFilter->AddAction(new QClickEvent(), new QTerminateAction());
1656: }
1657:
1658: foreach ($this->btnFilterResetArray as $btnFilterReset) {
1659: $btnFilterReset->RemoveAllActions(QClickEvent::EventName);
1660: if ($this->blnUseAjax)
1661: $btnFilterReset->AddAction(new QClickEvent(), new QAjaxControlAction($this, 'btnFilterReset_Click', $this->objWaitIcon));
1662: else
1663: $btnFilterReset->AddAction(new QClickEvent(), new QServerControlAction($this, 'btnFilterReset_Click'));
1664: $btnFilterReset->AddAction(new QClickEvent(), new QTerminateAction());
1665: }
1666: return $blnToReturn;
1667:
1668: // LAYOUT
1669: case "CellPadding":
1670: try {
1671: $this->intCellPadding = QType::Cast($mixValue, QType::Integer);
1672: break;
1673: } catch (QInvalidCastException $objExc) {
1674: $objExc->IncrementOffset();
1675: throw $objExc;
1676: }
1677: case "CellSpacing":
1678: try {
1679: $this->intCellSpacing = QType::Cast($mixValue, QType::Integer);
1680: break;
1681: } catch (QInvalidCastException $objExc) {
1682: $objExc->IncrementOffset();
1683: throw $objExc;
1684: }
1685: /* TODO
1686: case "BorderSpacing":
1687: try {
1688: $this->SetCssStyle('border-spacing', $mixValue = QType::Cast($mixValue, QType::String), true);
1689: if ($mixValue == '0' && $mixValue == '0px') {
1690: $this->SetCssStyle('border-collapsed', 'collapse');
1691: } else {
1692: $this->SetCssStyle('border-collapsed', 'separate');
1693: }
1694: break;
1695: } catch (QInvalidCastException $objExc) {
1696: $objExc->IncrementOffset();
1697: throw $objExc;
1698: }
1699: */
1700: case "GridLines":
1701: try {
1702: $this->strGridLines = QType::Cast($mixValue, QType::String);
1703: $this->SetHtmlAttribute('rules', $this->strGridLines);
1704: break;
1705: } catch (QInvalidCastException $objExc) {
1706: $objExc->IncrementOffset();
1707: throw $objExc;
1708: }
1709: case "ShowHeader":
1710: try {
1711: $this->blnShowHeader = QType::Cast($mixValue, QType::Boolean);
1712: break;
1713: } catch (QInvalidCastException $objExc) {
1714: $objExc->IncrementOffset();
1715: throw $objExc;
1716: }
1717:
1718: case "ShowFooter":
1719: try {
1720: $this->blnShowFooter = QType::Cast($mixValue, QType::Boolean);
1721: break;
1722: } catch (QInvalidCastException $objExc) {
1723: $objExc->IncrementOffset();
1724: throw $objExc;
1725: }
1726: case "ShowFilter":
1727: try {
1728: $this->blnShowFilter = QType::Cast($mixValue, QType::Boolean);
1729: // If the filter is being shown, the table gets its own wait icon.
1730: // When the wait icon changes we need to refresh a lot of actions
1731: // that would otherwise use the default wait icon.
1732: if($this->blnShowFilter){
1733: $this->objWaitIcon_Create();
1734: } else{
1735: $this->objWaitIcon = "default";
1736: }
1737: break;
1738: } catch (QInvalidCastException $objExc) {
1739: $objExc->IncrementOffset();
1740: throw $objExc;
1741: }
1742: case "ShowFilterButton":
1743: try {
1744: $this->blnShowFilterButton = QType::Cast($mixValue, QType::Boolean);
1745: break;
1746: } catch (QInvalidCastException $objExc) {
1747: $objExc->IncrementOffset();
1748: throw $objExc;
1749: }
1750: case 'ShowFilterResetButton':
1751: try {
1752: $this->blnShowFilterResetButton = QType::Cast($mixValue, QType::Boolean);
1753: break;
1754: } catch (QInvalidCastException $objExc) {
1755: $objExc->IncrementOffset();
1756: throw $objExc;
1757: }
1758: // MISC
1759: case "SortColumnIndex":
1760: try {
1761: $this->intSortColumnIndex = QType::Cast($mixValue, QType::Integer);
1762: break;
1763: } catch (QInvalidCastException $objExc) {
1764: $objExc->IncrementOffset();
1765: throw $objExc;
1766: }
1767:
1768: case "SortDirection":
1769: if ($mixValue == 1)
1770: $this->intSortDirection = 1;
1771: else
1772: $this->intSortDirection = 0;
1773: break;
1774:
1775:
1776: case 'LabelForNoneFound':
1777: try {
1778: $this->strLabelForNoneFound = QType::Cast($mixValue, QType::String);
1779: break;
1780: } catch (QInvalidCastException $objExc) {
1781: $objExc->IncrementOffset();
1782: throw $objExc;
1783: }
1784:
1785: case 'LabelForOneFound':
1786: try {
1787: $this->strLabelForOneFound = QType::Cast($mixValue, QType::String);
1788: break;
1789: } catch (QInvalidCastException $objExc) {
1790: $objExc->IncrementOffset();
1791: throw $objExc;
1792: }
1793:
1794: case 'LabelForMultipleFound':
1795: try {
1796: $this->strLabelForMultipleFound = QType::Cast($mixValue, QType::String);
1797: break;
1798: } catch (QInvalidCastException $objExc) {
1799: $objExc->IncrementOffset();
1800: throw $objExc;
1801: }
1802:
1803: case 'LabelForPaginated':
1804: try {
1805: $this->strLabelForPaginated = QType::Cast($mixValue, QType::String);
1806: break;
1807: } catch (QInvalidCastException $objExc) {
1808: $objExc->IncrementOffset();
1809: throw $objExc;
1810: }
1811:
1812: case 'RowActionParameterHtml':
1813: try {
1814: $this->strRowActionParameterHtml = QType::Cast($mixValue, QType::String);
1815: break;
1816: } catch (QInvalidCastException $objExc) {
1817: $objExc->IncrementOffset();
1818: throw $objExc;
1819: }
1820:
1821: case 'Owner':
1822: try {
1823: $this->objOwner = $mixValue;
1824: break;
1825: } catch (QInvalidCastException $objExc) {
1826: $objExc->IncrementOffset();
1827: throw $objExc;
1828: }
1829:
1830: default:
1831: try {
1832: parent::__set($strName, $mixValue);
1833: break;
1834: } catch (QCallerException $objExc) {
1835: $objExc->IncrementOffset();
1836: throw $objExc;
1837: }
1838: }
1839: }
1840: }