1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16:
17: require_once("AWLUtilities.php");
18:
19: 20: 21:
22: $BrowserCurrentRow = (object) array();
23:
24:
25:
26: 27: 28: 29: 30: 31:
32: class BrowserColumn
33: {
34: var $Field;
35: var ;
36: var $Format;
37: var $Sql;
38: var $Align;
39: var $Class;
40: var $Type;
41: var $Translatable;
42: var $Hook;
43: var $current_row;
44:
45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68:
69: function BrowserColumn( $field, $header="", $align="", $format="", $sql="", $class="", $datatype="", $hook=null ) {
70: $this->Field = $field;
71: $this->Sql = $sql;
72: $this->Header = $header;
73: $this->Format = $format;
74: $this->Class = $class;
75: $this->Align = $align;
76: $this->Type = $datatype;
77: $this->Translatable = false;
78: $this->Hook = $hook;
79: }
80:
81: 82: 83: 84: 85:
86: function GetTarget() {
87: if ( $this->Sql == "" ) return $this->Field;
88: return "$this->Sql AS $this->Field";
89: }
90:
91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103:
104: function ( $order_field, $order_direction, $browser_array_key=0, $forced_order=false ) {
105: global $c;
106: if ( $this->Align == "" ) $this->Align = "left";
107: $html = '<th class="'.$this->Align.'" '. ($this->Class == "" ? "" : "class=\"$this->Class\"") . '>';
108:
109: $direction = 'A';
110: $image = "";
111: if ( !$forced_order && $order_field == $this->Field ) {
112: if ( strtoupper( substr( $order_direction, 0, 1) ) == 'A' ) {
113: $image = 'down';
114: $direction = 'D';
115: }
116: else {
117: $image = 'up';
118: }
119: $image = "<img class=\"order\" src=\"$c->images/$image.gif\" alt=\"$image\" />";
120: }
121: if ( !isset($browser_array_key) || $browser_array_key == '' ) $browser_array_key = 0;
122: if ( !$forced_order ) $html .= '<a href="'.replace_uri_params( $_SERVER['REQUEST_URI'], array( "o[$browser_array_key]" => $this->Field, "d[$browser_array_key]" => $direction ) ).'" class="order">';
123: $html .= ($this->Header == "" ? $this->Field : $this->Header);
124: if ( !$forced_order ) $html .= "$image</a>";
125: $html .= "</th>\n";
126: return $html;
127: }
128:
129: function SetTranslatable() {
130: $this->Translatable = true;
131: }
132:
133: function RenderValue( $value, $extraclass = "" ) {
134: global $session;
135:
136: if ( $this->Type == 'date' || $this->Type == 'timestamp') {
137: $value = $session->FormattedDate( $value, $this->Type );
138: }
139:
140: if ( $this->Hook && function_exists($this->Hook) ) {
141: dbg_error_log( "Browser", ":Browser: Hook for $this->Hook on column $this->Field");
142: $value = call_user_func( $this->Hook, $value, $this->Field, $this->current_row );
143: }
144:
145: if ( $this->Translatable ) {
146: $value = translate($value);
147: }
148:
149: $value = str_replace( "\n", "<br />", $value );
150: if ( substr(strtolower($this->Format),0,3) == "<td" ) {
151: $html = sprintf($this->Format,$value);
152: }
153: else {
154:
155:
156: $class = $this->Align . ($this->Class == "" ? "" : " $this->Class") . ($extraclass == "" ? "" : " $extraclass");
157: if ( $class != "" ) $class = ' class="'.$class.'"';
158: $html = sprintf('<td%s>',$class);
159: $html .= ($this->Format == "" ? $value : sprintf($this->Format,$value,$value));
160: $html .= "</td>\n";
161: }
162: return $html;
163: }
164: }
165:
166:
167: 168: 169: 170: 171: 172: 173:
174: class Browser
175: {
176: var $Title;
177: var $SubTitle;
178: var $FieldNames;
179: var $Columns;
180: var $HiddenColumns;
181: var $Joins;
182: var $Where;
183: var $Distinct;
184: var $Union;
185: var $Order;
186: var $OrderField;
187: var $OrderDirection;
188: var $OrderBrowserKey;
189: var $ForcedOrder;
190: var $Grouping;
191: var $Limit;
192: var $Offset;
193: var $Query;
194: var $BeginRow;
195: var $CloseRow;
196: var $BeginRowArgs;
197: var ;
198: var ;
199: var ;
200: var $Totals;
201: var $TotalFuncs;
202: var ;
203: var $match_column;
204: var $match_value;
205: var $match_function;
206: var $DivOpen;
207: var $DivClose;
208:
209: 210: 211: 212: 213:
214: function Browser( $title = "" ) {
215: global $c;
216: $this->Title = $title;
217: $this->SubTitle = "";
218: $this->Distinct = "";
219: $this->Order = "";
220: $this->Limit = "";
221: $this->Offset = "";
222: $this->BeginRow = "<tr class=\"row%d\">\n";
223: $this->CloseRow = "</tr>\n";
224: $this->BeginRowArgs = array('#even');
225: $this->Totals = array();
226: $this->Columns = array();
227: $this->HiddenColumns = array();
228: $this->FieldNames = array();
229: $this->DivOpen = '<div id="browser">';
230: $this->DivClose = '</div>';
231: $this->ForcedOrder = false;
232: dbg_error_log( "Browser", ":Browser: New browser called $title");
233: }
234:
235: 236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251: 252: 253: 254: 255: 256: 257: 258: 259:
260: function AddColumn( $field, $header="", $align="", $format="", $sql="", $class="", $datatype="", $hook=null ) {
261: $this->Columns[] = new BrowserColumn( $field, $header, $align, $format, $sql, $class, $datatype, $hook );
262: $this->FieldNames[$field] = count($this->Columns) - 1;
263: }
264:
265: 266: 267: 268: 269: 270: 271: 272: 273: 274:
275: function AddHidden( $field, $sql="" ) {
276: $this->HiddenColumns[] = new BrowserColumn( $field, "", "", "", $sql );
277: $this->FieldNames[$field] = count($this->Columns) - 1;
278: }
279:
280: 281: 282: 283: 284: 285: 286: 287: 288:
289: function SetTitle( $new_title ) {
290: $this->Title = $new_title;
291: }
292:
293:
294: 295: 296: 297: 298: 299:
300: function Title( $new_title = null ) {
301: if ( isset($new_title) ) $this->Title = $new_title;
302: return $this->Title;
303: }
304:
305:
306: 307: 308: 309: 310:
311: function SetTranslatable( $column_list ) {
312: $top = count($this->Columns);
313: for( $i=0; $i < $top; $i++ ) {
314: dbg_error_log( "Browser", "Comparing %s with column name list", $this->Columns[$i]->Field);
315: if ( in_array($this->Columns[$i]->Field,$column_list) ) $this->Columns[$i]->SetTranslatable();
316: }
317: $top = count($this->HiddenColumns);
318: for( $i=0; $i < $top; $i++ ) {
319: dbg_error_log( "Browser", "Comparing %s with column name list", $this->HiddenColumns[$i]->Field);
320: if ( in_array($this->HiddenColumns[$i]->Field,$column_list) ) $this->HiddenColumns[$i]->SetTranslatable();
321: }
322: }
323:
324: 325: 326: 327: 328:
329: function SetSubTitle( $sub_title ) {
330: $this->SubTitle = $sub_title;
331: }
332:
333: 334: 335: 336: 337: 338:
339: function SetDiv( $open_div, $close_div ) {
340: $this->DivOpen = $open_div;
341: $this->DivClose = $close_div;
342: }
343:
344: 345: 346: 347: 348: 349: 350: 351: 352:
353: function SetJoins( $join_list ) {
354: $this->Joins = $join_list;
355: }
356:
357: 358: 359: 360: 361: 362: 363: 364: 365:
366: function SetUnion( $union_select ) {
367: $this->Union = $union_select;
368: }
369:
370: 371: 372: 373: 374: 375: 376:
377: function SetWhere( $where_clause ) {
378: $this->Where = $where_clause;
379: }
380:
381: 382: 383: 384: 385: 386: 387:
388: function SetDistinct( $distinct ) {
389: $this->Distinct = "DISTINCT ".$distinct;
390: }
391:
392: 393: 394: 395: 396: 397: 398:
399: function SetLimit( $limit_n ) {
400: $this->Limit = "LIMIT ".intval($limit_n);
401: }
402:
403: 404: 405: 406: 407: 408: 409:
410: function SetOffset( $offset_n ) {
411: $this->Offset = "OFFSET ".intval($offset_n);
412: }
413:
414: 415: 416: 417: 418: 419: 420: 421: 422:
423: function MoreWhere( $operator, $more_where ) {
424: if ( $this->Where == "" ) {
425: $this->Where = $more_where;
426: return;
427: }
428: $this->Where = "$this->Where $operator $more_where";
429: }
430:
431: 432: 433: 434: 435:
436: function AndWhere( $more_where ) {
437: $this->MoreWhere("AND",$more_where);
438: }
439:
440: 441: 442: 443: 444:
445: function OrWhere( $more_where ) {
446: $this->MoreWhere("OR",$more_where);
447: }
448:
449: function AddGrouping( $field, $browser_array_key=0 ) {
450: if ( $this->Grouping == "" )
451: $this->Grouping = "GROUP BY ";
452: else
453: $this->Grouping .= ", ";
454:
455: $this->Grouping .= clean_string($field);
456: }
457:
458:
459: 460: 461: 462: 463: 464: 465: 466: 467: 468: 469: 470: 471: 472: 473:
474: function AddOrder( $field, $direction, $browser_array_key=0, $secondary=0 ) {
475: $field = check_by_regex($field,'/^[^\'"!\\\\()\[\]|*\/{}&%@~;:?<>]+$/');
476: if ( ! isset($this->FieldNames[$field]) ) return;
477:
478: if ( !isset($this->Order) || $this->Order == "" )
479: $this->Order = "ORDER BY ";
480: else
481: $this->Order .= ", ";
482:
483: if ( $secondary == 0 ) {
484: $this->OrderField = $field;
485: $this->OrderBrowserKey = $browser_array_key;
486: }
487: $this->Order .= $field;
488:
489: if ( preg_match( '/^A/i', $direction) ) {
490: $this->Order .= " ASC";
491: if ( $secondary == 0)
492: $this->OrderDirection = 'A';
493: }
494: else {
495: $this->Order .= " DESC";
496: if ( $secondary == 0)
497: $this->OrderDirection = 'D';
498: }
499: }
500:
501:
502: 503: 504: 505: 506: 507:
508: function ForceOrder( $field, $direction ) {
509: $field = clean_string($field);
510: if ( ! isset($this->FieldNames[$field]) ) return;
511:
512: if ( $this->Order == "" )
513: $this->Order = "ORDER BY ";
514: else
515: $this->Order .= ", ";
516:
517: $this->Order .= $field;
518:
519: if ( preg_match( '/^A/i', $direction) ) {
520: $this->Order .= " ASC";
521: }
522: else {
523: $this->Order .= " DESC";
524: }
525:
526: $this->ForcedOrder = true;
527: }
528:
529:
530: 531: 532: 533: 534:
535: function SetOrdering( $default_fld=null, $default_dir='A' , $browser_array_key=0 ) {
536: if ( isset( $_GET['o'][$browser_array_key] ) && isset($_GET['d'][$browser_array_key] ) ) {
537: $this->AddOrder( $_GET['o'][$browser_array_key], $_GET['d'][$browser_array_key], $browser_array_key );
538: }
539: else {
540: if ( ! isset($default_fld) ) $default_fld = $this->Columns[0];
541: $this->AddOrder( $default_fld, $default_dir, $browser_array_key );
542: }
543: }
544:
545:
546: 547: 548: 549: 550: 551: 552: 553: 554: 555: 556:
557: function AddTotal( $column_name, $total_function = false ) {
558: $this->Totals[$column_name] = 0;
559: if ( $total_function != false ) {
560: $this->TotalFuncs[$column_name] = $total_function;
561: }
562: }
563:
564:
565: 566: 567: 568: 569:
570: function GetTotal( $column_name ) {
571: return $this->Totals[$column_name];
572: }
573:
574:
575: 576: 577: 578: 579: 580: 581: 582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596:
597: function RowFormat( $beginrow, $closerow, $rowargs )
598: {
599: $argc = func_num_args();
600: $this->BeginRow = func_get_arg(0);
601: $this->CloseRow = func_get_arg(1);
602:
603: $this->BeginRowArgs = array();
604: for( $i=2; $i < $argc; $i++ ) {
605: $this->BeginRowArgs[] = func_get_arg($i);
606: }
607: }
608:
609:
610: 611: 612: 613: 614: 615: 616: 617: 618: 619: 620: 621:
622: function ( $beginrow, $closerow, $rowargs )
623: {
624: $argc = func_num_args();
625: $this->BeginExtraRow = func_get_arg(0);
626: $this->CloseExtraRow = func_get_arg(1);
627:
628: $this->BeginExtraRowArgs = array();
629: for( $i=2; $i < $argc; $i++ ) {
630: $this->BeginExtraRowArgs[] = func_get_arg($i);
631: }
632: }
633:
634:
635: 636: 637: 638: 639: 640: 641: 642:
643: function DoQuery() {
644: $target_fields = "";
645: foreach( $this->Columns AS $k => $column ) {
646: if ( $target_fields != "" ) $target_fields .= ", ";
647: $target_fields .= $column->GetTarget();
648: }
649: if ( isset($this->HiddenColumns) ) {
650: foreach( $this->HiddenColumns AS $k => $column ) {
651: if ( $target_fields != "" ) $target_fields .= ", ";
652: $target_fields .= $column->GetTarget();
653: }
654: }
655: $where_clause = ((isset($this->Where) && $this->Where != "") ? "WHERE $this->Where" : "" );
656: $sql = sprintf( "SELECT %s %s FROM %s %s %s ", $this->Distinct, $target_fields,
657: $this->Joins, $where_clause, $this->Grouping );
658: if ( "$this->Union" != "" ) {
659: $sql .= "UNION $this->Union ";
660: }
661: $sql .= $this->Order . ' ' . $this->Limit . ' ' . $this->Offset;
662: $this->Query = new AwlQuery( $sql );
663: return $this->Query->Exec("Browse:$this->Title:DoQuery");
664: }
665:
666:
667: 668: 669: 670: 671:
672: function AddRow( $column_values ) {
673: if ( !isset($this->ExtraRows) || typeof($this->ExtraRows) != 'array' ) $this->ExtraRows = array();
674: $this->ExtraRows[] = &$column_values;
675: }
676:
677:
678: 679: 680: 681: 682: 683: 684:
685: function MatchedRow( $column, $value, $function ) {
686: $this->match_column = $column;
687: $this->match_value = $value;
688: $this->match_function = $function;
689: }
690:
691:
692: 693: 694: 695: 696: 697: 698: 699: 700:
701: function ValueReplacement($matches)
702: {
703:
704:
705:
706:
707: $field_name = $matches[1];
708: if ( !isset($this->current_row->{$field_name}) && substr($field_name,0,4) == "URL:" ) {
709: $field_name = substr($field_name,4);
710: $replacement = urlencode($this->current_row->{$field_name});
711: }
712: else {
713: $replacement = (isset($this->current_row->{$field_name}) ? $this->current_row->{$field_name} : '');
714: }
715: dbg_error_log( "Browser", ":ValueReplacement: Replacing %s with %s", $field_name, $replacement);
716: return $replacement;
717: }
718:
719:
720: 721: 722: 723: 724: 725: 726: 727: 728: 729:
730: function Render( $title_tag = null, $subtitle_tag = null ) {
731: global $c, $BrowserCurrentRow;
732:
733: if ( !isset($this->Query) ) $this->DoQuery();
734:
735: dbg_error_log( "Browser", ":Render: browser $this->Title");
736: $html = $this->DivOpen;
737: if ( $this->Title != "" ) {
738: if ( !isset($title_tag) ) $title_tag = 'h1';
739: $html .= "<$title_tag>$this->Title</$title_tag>\n";
740: }
741: if ( $this->SubTitle != "" ) {
742: if ( !isset($subtitle_tag) ) $subtitle_tag = 'h2';
743: $html .= "<$subtitle_tag>$this->SubTitle</$subtitle_tag>\n";
744: }
745:
746: $html .= "<table id=\"browse_table\">\n";
747: $html .= "<thead><tr class=\"header\">\n";
748: foreach( $this->Columns AS $k => $column ) {
749: $html .= $column->RenderHeader( $this->OrderField, $this->OrderDirection, $this->OrderBrowserKey, $this->ForcedOrder );
750: }
751: $html .= "</tr></thead>\n<tbody>";
752:
753: $rowanswers = array();
754: while( $BrowserCurrentRow = $this->Query->Fetch() ) {
755:
756:
757:
758: foreach( $this->BeginRowArgs AS $k => $fld ) {
759: if ( isset($BrowserCurrentRow->{$fld}) ) {
760: $rowanswers[$k] = $BrowserCurrentRow->{$fld};
761: }
762: else {
763: switch( $fld ) {
764: case '#even':
765: $rowanswers[$k] = ($this->Query->rownum() % 2);
766: break;
767: default:
768: $rowanswers[$k] = $fld;
769: }
770: }
771: }
772:
773: $row_html = vsprintf( preg_replace("/#@even@#/", ($this->Query->rownum() % 2), $this->BeginRow), $rowanswers);
774:
775: if ( isset($this->match_column) && isset($this->match_value) && $BrowserCurrentRow->{$this->match_column} == $this->match_value ) {
776: $row_html .= call_user_func( $this->match_function, $BrowserCurrentRow );
777: }
778: else {
779:
780: foreach( $this->Columns AS $k => $column ) {
781: $row_html .= $column->RenderValue( (isset($BrowserCurrentRow->{$column->Field})?$BrowserCurrentRow->{$column->Field}:'') );
782: if ( isset($this->Totals[$column->Field]) ) {
783: if ( isset($this->TotalFuncs[$column->Field]) && function_exists($this->TotalFuncs[$column->Field]) ) {
784:
785: $this->Totals[$column->Field] += $this->TotalFuncs[$column->Field]( $BrowserCurrentRow, $BrowserCurrentRow->{$column->Field} );
786: }
787: else {
788:
789: $this->Totals[$column->Field] += doubleval( preg_replace( '/[^0-9.-]/', '', $BrowserCurrentRow->{$column->Field} ));
790: }
791: }
792: }
793: }
794:
795:
796: $row_html .= preg_replace("/#@even@#/", ($this->Query->rownum() % 2), $this->CloseRow);
797: $this->current_row = $BrowserCurrentRow;
798: $html .= preg_replace_callback("/##([^#]+)##/", array( &$this, "ValueReplacement"), $row_html );
799: }
800:
801: if ( count($this->Totals) > 0 ) {
802: $BrowserCurrentRow = (object) "";
803: $row_html = "<tr class=\"totals\">\n";
804: foreach( $this->Columns AS $k => $column ) {
805: if ( isset($this->Totals[$column->Field]) ) {
806: $row_html .= $column->RenderValue( $this->Totals[$column->Field], "totals" );
807: }
808: else {
809: $row_html .= $column->RenderValue( "" );
810: }
811: }
812: $row_html .= "</tr>\n";
813: $this->current_row = $BrowserCurrentRow;
814: $html .= preg_replace_callback("/##([^#]+)##/", array( &$this, "ValueReplacement"), $row_html );
815: }
816:
817:
818: if ( count($this->ExtraRows) > 0 ) {
819: if ( !isset($this->BeginExtraRow) )
820: $this->BeginExtraRow = $this->BeginRow;
821: if ( !isset($this->CloseExtraRow) )
822: $this->CloseExtraRow = $this->CloseRow;
823: if ( !isset($this->BeginExtraRowArgs) )
824: $this->BeginExtraRowArgs = $this->BeginRowArgs;
825:
826: foreach( $this->ExtraRows AS $k => $v ) {
827: $BrowserCurrentRow = (object) $v;
828:
829: foreach( $this->BeginExtraRowArgs AS $k => $fld ) {
830: if ( isset( $BrowserCurrentRow->{$fld} ) ) {
831: $rowanswers[$k] = $BrowserCurrentRow->{$fld};
832: }
833: else {
834: switch( $fld ) {
835: case '#even':
836: $rowanswers[$k] = ($this->Query->rownum() % 2);
837: break;
838: default:
839: $rowanswers[$k] = $fld;
840: }
841: }
842: }
843:
844:
845: $row_html = vsprintf( preg_replace("/#@even@#/", ($this->Query->rownum() % 2), $this->BeginExtraRow), $rowanswers);
846:
847: if ( isset($this->match_column) && isset($this->match_value) && $BrowserCurrentRow->{$this->match_column} == $this->match_value ) {
848: $row_html .= call_user_func( $this->match_function, $BrowserCurrentRow );
849: }
850: else {
851:
852: foreach( $this->Columns AS $k => $column ) {
853: $row_html .= $column->RenderValue( (isset($BrowserCurrentRow->{$column->Field}) ? $BrowserCurrentRow->{$column->Field} : '') );
854: }
855: }
856:
857:
858: $row_html .= preg_replace("/#@even@#/", ($this->Query->rownum() % 2), $this->CloseExtraRow);
859: $this->current_row = $BrowserCurrentRow;
860: $html .= preg_replace_callback("/##([^#]+)##/", array( &$this, "ValueReplacement"), $row_html );
861: }
862: }
863:
864: $html .= "</tbody>\n</table>\n";
865: $html .= $this->DivClose;
866:
867: return $html;
868: }
869:
870: }
871: