Andrew's Web Libraries (AWL)
Loading...
Searching...
No Matches
classBrowser.php
1<?php
16
17require_once("AWLUtilities.php");
18
22$BrowserCurrentRow = (object) array();
23
24
25
33{
34 var $Field;
35 var $Header;
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
69 function __construct( $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
86 function GetTarget() {
87 if ( $this->Sql == "" ) return $this->Field;
88 return "$this->Sql AS $this->Field";
89 }
90
104 function RenderHeader( $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 ) {
141 if ( function_exists($this->Hook) ) {
142 dbg_error_log( "Browser", ":Browser: Hook for $this->Hook on column $this->Field");
143 $value = call_user_func( $this->Hook, $value, $this->Field, $this->current_row );
144 } else {
145 dbg_error_log( "Browser", ":Browser: Hook for $this->Hook on column $this->Field doesn't exist");
146 }
147 }
148
149 if ( $this->Translatable ) {
150 $value = translate($value);
151 }
152
153 $value = str_replace( "\n", "<br />", $value );
154 if ( substr(strtolower($this->Format),0,3) == "<td" ) {
155 $html = sprintf($this->Format,$value);
156 }
157 else {
158 // These quite probably don't work. The CSS standard for multiple classes is 'class="a b c"' but is lightly
159 // implemented according to some web references. Perhaps modern browsers are better?
160 $class = $this->Align . ($this->Class == "" ? "" : " $this->Class") . ($extraclass == "" ? "" : " $extraclass");
161 if ( $class != "" ) $class = ' class="'.$class.'"';
162 $html = sprintf('<td%s>',$class);
163 $html .= ($this->Format == "" ? $value : sprintf($this->Format,$value,$value));
164 $html .= "</td>\n";
165 }
166 return $html;
167 }
168}
169
170
179{
180 var $Title;
181 var $SubTitle;
182 var $FieldNames;
183 var $Columns;
184 var $HiddenColumns;
185 var $Joins;
186 var $Where;
187 var $Distinct;
188 var $Union;
189 var $Order;
190 var $OrderField;
191 var $OrderDirection;
192 var $OrderBrowserKey;
193 var $ForcedOrder;
194 var $Grouping;
195 var $Limit;
196 var $Offset;
197 var $Query;
198 var $BeginRow;
199 var $CloseRow;
200 var $BeginRowArgs;
201 var $BeginExtraRow;
202 var $CloseExtraRow;
203 var $BeginExtraRowArgs;
204 var $Totals;
205 var $TotalFuncs;
206 var $ExtraRows;
207 var $match_column;
208 var $match_value;
209 var $match_function;
210 var $DivOpen;
211 var $DivClose;
212 var $current_row;
213
219 function __construct( $title = "" ) {
220 global $c;
221 $this->Title = $title;
222 $this->SubTitle = "";
223 $this->Distinct = "";
224 $this->Order = "";
225 $this->Limit = "";
226 $this->Offset = "";
227 $this->BeginRow = "<tr class=\"row%d\">\n";
228 $this->CloseRow = "</tr>\n";
229 $this->BeginRowArgs = array('#even');
230 $this->Totals = array();
231 $this->Columns = array();
232 $this->HiddenColumns = array();
233 $this->FieldNames = array();
234 $this->DivOpen = '<div id="browser">';
235 $this->DivClose = '</div>';
236 $this->ForcedOrder = false;
237 dbg_error_log( "Browser", ":Browser: New browser called $title");
238 }
239
265 function AddColumn( $field, $header="", $align="", $format="", $sql="", $class="", $datatype="", $hook=null ) {
266 $this->Columns[] = new BrowserColumn( $field, $header, $align, $format, $sql, $class, $datatype, $hook );
267 $this->FieldNames[$field] = count($this->Columns) - 1;
268 }
269
280 function AddHidden( $field, $sql="" ) {
281 $this->HiddenColumns[] = new BrowserColumn( $field, "", "", "", $sql );
282 $this->FieldNames[$field] = count($this->Columns) - 1;
283 }
284
294 function SetTitle( $new_title ) {
295 $this->Title = $new_title;
296 }
297
298
305 function Title( $new_title = null ) {
306 if ( isset($new_title) ) $this->Title = $new_title;
307 return $this->Title;
308 }
309
310
316 function SetTranslatable( $column_list ) {
317 $top = count($this->Columns);
318 for( $i=0; $i < $top; $i++ ) {
319 dbg_error_log( "Browser", "Comparing %s with column name list", $this->Columns[$i]->Field);
320 if ( in_array($this->Columns[$i]->Field,$column_list) ) $this->Columns[$i]->SetTranslatable();
321 }
322 $top = count($this->HiddenColumns);
323 for( $i=0; $i < $top; $i++ ) {
324 dbg_error_log( "Browser", "Comparing %s with column name list", $this->HiddenColumns[$i]->Field);
325 if ( in_array($this->HiddenColumns[$i]->Field,$column_list) ) $this->HiddenColumns[$i]->SetTranslatable();
326 }
327 }
328
334 function SetSubTitle( $sub_title ) {
335 $this->SubTitle = $sub_title;
336 }
337
344 function SetDiv( $open_div, $close_div ) {
345 $this->DivOpen = $open_div;
346 $this->DivClose = $close_div;
347 }
348
358 function SetJoins( $join_list ) {
359 $this->Joins = $join_list;
360 }
361
371 function SetUnion( $union_select ) {
372 $this->Union = $union_select;
373 }
374
382 function SetWhere( $where_clause ) {
383 $this->Where = $where_clause;
384 }
385
393 function SetDistinct( $distinct ) {
394 $this->Distinct = "DISTINCT ".$distinct;
395 }
396
404 function SetLimit( $limit_n ) {
405 $this->Limit = "LIMIT ".intval($limit_n);
406 }
407
415 function SetOffset( $offset_n ) {
416 $this->Offset = "OFFSET ".intval($offset_n);
417 }
418
428 function MoreWhere( $operator, $more_where ) {
429 if ( $this->Where == "" ) {
430 $this->Where = $more_where;
431 return;
432 }
433 $this->Where = "$this->Where $operator $more_where";
434 }
435
441 function AndWhere( $more_where ) {
442 $this->MoreWhere("AND",$more_where);
443 }
444
450 function OrWhere( $more_where ) {
451 $this->MoreWhere("OR",$more_where);
452 }
453
454 function AddGrouping( $field, $browser_array_key=0 ) {
455 if ( $this->Grouping == "" )
456 $this->Grouping = "GROUP BY ";
457 else
458 $this->Grouping .= ", ";
459
460 $this->Grouping .= clean_string($field);
461 }
462
463
479 function AddOrder( $field, $direction, $browser_array_key=0, $secondary=0 ) {
480 $field = check_by_regex($field,'/^[^\'"!\\\\()\‍[\‍]|*\/{}&%@~;:?<>]+$/');
481 if ( ! isset($this->FieldNames[$field]) ) return;
482
483 if ( !isset($this->Order) || $this->Order == "" )
484 $this->Order = "ORDER BY ";
485 else
486 $this->Order .= ", ";
487
488 if ( $secondary == 0 ) {
489 $this->OrderField = $field;
490 $this->OrderBrowserKey = $browser_array_key;
491 }
492 $this->Order .= $field;
493
494 if ( preg_match( '/^A/i', $direction) ) {
495 $this->Order .= " ASC";
496 if ( $secondary == 0)
497 $this->OrderDirection = 'A';
498 }
499 else {
500 $this->Order .= " DESC";
501 if ( $secondary == 0)
502 $this->OrderDirection = 'D';
503 }
504 }
505
506
513 function ForceOrder( $field, $direction ) {
514 $field = clean_string($field);
515 if ( ! isset($this->FieldNames[$field]) ) return;
516
517 if ( $this->Order == "" )
518 $this->Order = "ORDER BY ";
519 else
520 $this->Order .= ", ";
521
522 $this->Order .= $field;
523
524 if ( preg_match( '/^A/i', $direction) ) {
525 $this->Order .= " ASC";
526 }
527 else {
528 $this->Order .= " DESC";
529 }
530
531 $this->ForcedOrder = true;
532 }
533
534
540 function SetOrdering( $default_fld=null, $default_dir='A' , $browser_array_key=0 ) {
541 if ( isset( $_GET['o'][$browser_array_key] ) && isset($_GET['d'][$browser_array_key] ) ) {
542 $this->AddOrder( $_GET['o'][$browser_array_key], $_GET['d'][$browser_array_key], $browser_array_key );
543 }
544 else {
545 if ( ! isset($default_fld) ) $default_fld = $this->Columns[0];
546 $this->AddOrder( $default_fld, $default_dir, $browser_array_key );
547 }
548 }
549
550
562 function AddTotal( $column_name, $total_function = false ) {
563 $this->Totals[$column_name] = 0;
564 if ( $total_function != false ) {
565 $this->TotalFuncs[$column_name] = $total_function;
566 }
567 }
568
569
575 function GetTotal( $column_name ) {
576 return $this->Totals[$column_name];
577 }
578
579
602 function RowFormat( $beginrow, $closerow, $rowargs )
603 {
604 $argc = func_num_args();
605 $this->BeginRow = func_get_arg(0);
606 $this->CloseRow = func_get_arg(1);
607
608 $this->BeginRowArgs = array();
609 for( $i=2; $i < $argc; $i++ ) {
610 $this->BeginRowArgs[] = func_get_arg($i);
611 }
612 }
613
614
627 function ExtraRowFormat( $beginrow, $closerow, $rowargs )
628 {
629 $argc = func_num_args();
630 $this->BeginExtraRow = func_get_arg(0);
631 $this->CloseExtraRow = func_get_arg(1);
632
633 $this->BeginExtraRowArgs = array();
634 for( $i=2; $i < $argc; $i++ ) {
635 $this->BeginExtraRowArgs[] = func_get_arg($i);
636 }
637 }
638
639
648 function DoQuery() {
649 $target_fields = "";
650 foreach( $this->Columns AS $k => $column ) {
651 if ( $target_fields != "" ) $target_fields .= ", ";
652 $target_fields .= $column->GetTarget();
653 }
654 if ( isset($this->HiddenColumns) ) {
655 foreach( $this->HiddenColumns AS $k => $column ) {
656 if ( $target_fields != "" ) $target_fields .= ", ";
657 $target_fields .= $column->GetTarget();
658 }
659 }
660 $where_clause = ((isset($this->Where) && $this->Where != "") ? "WHERE $this->Where" : "" );
661 $sql = sprintf( "SELECT %s %s FROM %s %s %s ", $this->Distinct, $target_fields,
662 $this->Joins, $where_clause, $this->Grouping );
663 if ( "$this->Union" != "" ) {
664 $sql .= "UNION $this->Union ";
665 }
666 $sql .= $this->Order . ' ' . $this->Limit . ' ' . $this->Offset;
667 $this->Query = new AwlQuery( $sql );
668 return $this->Query->Exec("Browse:$this->Title:DoQuery");
669 }
670
671
677 function AddRow( $column_values ) {
678 if ( !isset($this->ExtraRows) || typeof($this->ExtraRows) != 'array' ) $this->ExtraRows = array();
679 $this->ExtraRows[] = &$column_values;
680 }
681
682
690 function MatchedRow( $column, $value, $function ) {
691 $this->match_column = $column;
692 $this->match_value = $value;
693 $this->match_function = $function;
694 }
695
696
706 function ValueReplacement($matches)
707 {
708 // as usual: $matches[0] is the complete match
709 // $matches[1] the match for the first subpattern
710 // enclosed in '##...##' and so on
711
712 $field_name = $matches[1];
713 if ( !isset($this->current_row->{$field_name}) && substr($field_name,0,4) == "URL:" ) {
714 $field_name = substr($field_name,4);
715 $replacement = urlencode($this->current_row->{$field_name});
716 }
717 else {
718 $replacement = (isset($this->current_row->{$field_name}) ? $this->current_row->{$field_name} : '');
719 }
720 dbg_error_log( "Browser", ":ValueReplacement: Replacing %s with %s", $field_name, $replacement);
721 return $replacement;
722 }
723
724
735 function Render( $title_tag = null, $subtitle_tag = null ) {
736 global $c, $BrowserCurrentRow;
737
738 if ( !isset($this->Query) ) $this->DoQuery(); // Ensure the query gets run before we render!
739
740 dbg_error_log( "Browser", ":Render: browser $this->Title");
741 $html = $this->DivOpen;
742 if ( $this->Title != "" ) {
743 if ( !isset($title_tag) ) $title_tag = 'h1';
744 $html .= "<$title_tag>$this->Title</$title_tag>\n";
745 }
746 if ( $this->SubTitle != "" ) {
747 if ( !isset($subtitle_tag) ) $subtitle_tag = 'h2';
748 $html .= "<$subtitle_tag>$this->SubTitle</$subtitle_tag>\n";
749 }
750
751 $html .= "<table id=\"browse_table\">\n";
752 $html .= "<thead><tr class=\"header\">\n";
753 foreach( $this->Columns AS $k => $column ) {
754 $html .= $column->RenderHeader( $this->OrderField, $this->OrderDirection, $this->OrderBrowserKey, $this->ForcedOrder );
755 }
756 $html .= "</tr></thead>\n<tbody>";
757
758 $rowanswers = array();
759 while( $BrowserCurrentRow = $this->Query->Fetch() ) {
760
761 // Work out the answers to any stuff that may be being substituted into the row start
763 foreach( $this->BeginRowArgs AS $k => $fld ) {
764 if ( isset($BrowserCurrentRow->{$fld}) ) {
765 $rowanswers[$k] = $BrowserCurrentRow->{$fld};
766 }
767 else {
768 switch( $fld ) {
769 case '#even':
770 $rowanswers[$k] = ($this->Query->rownum() % 2);
771 break;
772 default:
773 $rowanswers[$k] = $fld;
774 }
775 }
776 }
777 // Start the row
778 $row_html = vsprintf( preg_replace("/#@even@#/", ($this->Query->rownum() % 2), $this->BeginRow), $rowanswers);
779
780 if ( isset($this->match_column) && isset($this->match_value) && $BrowserCurrentRow->{$this->match_column} == $this->match_value ) {
781 $row_html .= call_user_func( $this->match_function, $BrowserCurrentRow );
782 }
783 else {
784 // Each column
785 foreach( $this->Columns AS $k => $column ) {
786 $row_html .= $column->RenderValue( (isset($BrowserCurrentRow->{$column->Field})?$BrowserCurrentRow->{$column->Field}:'') );
787 if ( isset($this->Totals[$column->Field]) ) {
788 if ( isset($this->TotalFuncs[$column->Field]) && function_exists($this->TotalFuncs[$column->Field]) ) {
789 // Run the amount through the callback function $floatval = my_function( $row, $fieldval );
790 $this->Totals[$column->Field] += $this->TotalFuncs[$column->Field]( $BrowserCurrentRow, $BrowserCurrentRow->{$column->Field} );
791 }
792 else {
793 // Just add the amount
794 $this->Totals[$column->Field] += doubleval( preg_replace( '/[^0-9.-]/', '', $BrowserCurrentRow->{$column->Field} ));
795 }
796 }
797 }
798 }
799
800 // Finish the row
801 $row_html .= preg_replace("/#@even@#/", ($this->Query->rownum() % 2), $this->CloseRow);
802 $this->current_row = $BrowserCurrentRow;
803 $html .= preg_replace_callback("/##([^#]+)##/", array( &$this, "ValueReplacement"), $row_html );
804 }
805
806 if ( count($this->Totals) > 0 ) {
807 $BrowserCurrentRow = (object) "";
808 $row_html = "<tr class=\"totals\">\n";
809 foreach( $this->Columns AS $k => $column ) {
810 if ( isset($this->Totals[$column->Field]) ) {
811 $row_html .= $column->RenderValue( $this->Totals[$column->Field], "totals" );
812 }
813 else {
814 $row_html .= $column->RenderValue( "" );
815 }
816 }
817 $row_html .= "</tr>\n";
818 $this->current_row = $BrowserCurrentRow;
819 $html .= preg_replace_callback("/##([^#]+)##/", array( &$this, "ValueReplacement"), $row_html );
820 }
821
822
823 if ( is_array($this->ExtraRows) && count($this->ExtraRows) > 0 ) {
824 if ( !isset($this->BeginExtraRow) )
825 $this->BeginExtraRow = $this->BeginRow;
826 if ( !isset($this->CloseExtraRow) )
827 $this->CloseExtraRow = $this->CloseRow;
828 if ( !isset($this->BeginExtraRowArgs) )
829 $this->BeginExtraRowArgs = $this->BeginRowArgs;
830
831 foreach( $this->ExtraRows AS $k => $v ) {
832 $BrowserCurrentRow = (object) $v;
833 // Work out the answers to any stuff that may be being substituted into the row start
834 foreach( $this->BeginExtraRowArgs AS $k => $fld ) {
835 if ( isset( $BrowserCurrentRow->{$fld} ) ) {
836 $rowanswers[$k] = $BrowserCurrentRow->{$fld};
837 }
838 else {
839 switch( $fld ) {
840 case '#even':
841 $rowanswers[$k] = ($this->Query->rownum() % 2);
842 break;
843 default:
844 $rowanswers[$k] = $fld;
845 }
846 }
847 }
848
849 // Start the row
850 $row_html = vsprintf( preg_replace("/#@even@#/", ($this->Query->rownum() % 2), $this->BeginExtraRow), $rowanswers);
851
852 if ( isset($this->match_column) && isset($this->match_value) && $BrowserCurrentRow->{$this->match_column} == $this->match_value ) {
853 $row_html .= call_user_func( $this->match_function, $BrowserCurrentRow );
854 }
855 else {
856 // Each column
857 foreach( $this->Columns AS $k => $column ) {
858 $row_html .= $column->RenderValue( (isset($BrowserCurrentRow->{$column->Field}) ? $BrowserCurrentRow->{$column->Field} : '') );
859 }
860 }
861
862 // Finish the row
863 $row_html .= preg_replace("/#@even@#/", ($this->Query->rownum() % 2), $this->CloseExtraRow);
864 $this->current_row = $BrowserCurrentRow;
865 $html .= preg_replace_callback("/##([^#]+)##/", array( &$this, "ValueReplacement"), $row_html );
866 }
867 }
868
869 $html .= "</tbody>\n</table>\n";
870 $html .= $this->DivClose;
871
872 return $html;
873 }
874
875}
RenderHeader( $order_field, $order_direction, $browser_array_key=0, $forced_order=false)
__construct( $field, $header="", $align="", $format="", $sql="", $class="", $datatype="", $hook=null)
AddHidden( $field, $sql="")
SetDistinct( $distinct)
OrWhere( $more_where)
AddOrder( $field, $direction, $browser_array_key=0, $secondary=0)
SetUnion( $union_select)
SetJoins( $join_list)
SetSubTitle( $sub_title)
SetTranslatable( $column_list)
SetTitle( $new_title)
SetDiv( $open_div, $close_div)
SetLimit( $limit_n)
AndWhere( $more_where)
AddColumn( $field, $header="", $align="", $format="", $sql="", $class="", $datatype="", $hook=null)
SetWhere( $where_clause)
__construct( $title="")
MoreWhere( $operator, $more_where)
SetOffset( $offset_n)
Title( $new_title=null)