Xataface  2.0alpha2
Xataface Application Framework
 All Data Structures Namespaces Files Functions Variables Groups Pages
RelatedList.php
Go to the documentation of this file.
1 <?php
2 /*-------------------------------------------------------------------------------
3  * Xataface Web Application Framework
4  * Copyright (C) 2005-2008 Web Lite Solutions Corp (shannah@sfu.ca)
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License
8  * as published by the Free Software Foundation; either version 2
9  * of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  *-------------------------------------------------------------------------------
20  */
21 
22 /******************************************************************************
23  * File: Dataface/ResultList.php
24  * Author: Steve Hannah
25  * Created: September 3, 2005
26  * Description:
27  * Handles creation and display of a result list from an SQL database.
28  *
29  *****************************************************************************/
30 
31  import( 'Dataface/Table.php');
32  import( 'Dataface/QueryBuilder.php');
33  import( 'Dataface/LinkTool.php');
34 
35 
37 
41  var $_db;
42  var $_table;
43  var $_record;
44  var $_start;
45  var $_limit;
46  var $_where;
47  var $hideActions=false;
48  var $noLinks=false;
49 
50 
51  function Dataface_RelatedList( &$record, $relname, $db=''){
52  if ( !is_a($record, 'Dataface_Record') ){
53  throw new Exception("In Dataface_RelatedList constructor, the first argument is expected to be an object of type 'Dataface_Record' but received '".get_class($record));
54  }
55  $this->_record =& $record;
56  $this->_tablename = $this->_record->_table->tablename;
57  $this->_db = $db;
58  $this->_relationship_name = $relname;
59 
60 
61  $this->_table =& $this->_record->_table;
62  $this->_relationship =& $this->_table->getRelationship($relname);
63 
64  $this->_start = isset($_REQUEST['-related:start']) ? $_REQUEST['-related:start'] : 0;
65  $this->_limit = isset($_REQUEST['-related:limit']) ? $_REQUEST['-related:limit'] : 30;
66 
68  $query =& $app->getQuery();
69  if ( isset($query['-related:search']) ){
70  $rwhere = array();
71  foreach ($this->_relationship->fields() as $rfield){
72  //list($garbage,$rfield) = explode('.', $rfield);
73  $rwhere[] = '`'.str_replace('.','`.`',$rfield).'` LIKE \'%'.addslashes($query['-related:search']).'%\'';
74  }
75  $rwhere = implode(' OR ', $rwhere);
76  } else {
77  $rwhere = 0;
78  }
79  $this->_where = $rwhere;
80 
81 
82  }
83 
84  function _forwardButtonHtml(){
85  $numRecords = $this->_record->numRelatedRecords( $this->_relationship_name, $this->_where );
86  if ( $this->_start + $this->_limit >= $numRecords ) return '';
87  $query = array('-related:start'=> $this->_start+$this->_limit, '-related:limit'=>$this->_limit);
88  $link = Dataface_LinkTool::buildLink($query);
89  $out = '<a href="'.$link.'" title="Next '.$this->_limit.' Results"><img src="'.DATAFACE_URL.'/images/go-next.png" alt="Next" /></a>';
90  if ( ($this->_start+(2*$this->_limit)) < $numRecords ){
91  $query['-related:start'] = $numRecords - ( ($numRecords - $this->_start) % $this->_limit) -1;
92  $link = Dataface_LinkTool::buildLink($query);
93  $out .= '<a href="'.$link.'" title="Last"><img src="'.DATAFACE_URL.'/images/go-last.png" alt="Last" /></a>';
94  }
95  return $out;
96  }
97 
98  function _backButtonHtml(){
99  if ( $this->_start <= 0 ) return '';
100  $query = array('-related:start'=> max( 0, $this->_start-$this->_limit), '-related:limit'=>$this->_limit);
101  $link = Dataface_LinkTool::buildLink($query);
102  $out = '<a href="'.$link.'" title="Previous '.$this->_limit.' Results"><img src="'.DATAFACE_URL.'/images/go-previous.png" alt="Previous" /></a>';
103 
104  if ( ($this->_start-$this->_limit) > 0 ){
105  $query['-related:start'] = 0;
106  $out = '<a href="'.Dataface_LinkTool::buildLink($query).'" title="First"><img src="'.DATAFACE_URL.'/images/go-first.png" alt="First" /></a>'.$out;
107  }
108 
109  return $out;
110 
111  }
112 
113  function renderCell(&$record, $fieldname){
114  $del =& $record->_table->getDelegate();
115  if ( isset($del) and method_exists($del, $fieldname.'__renderCell') ){
116  $method = $fieldname.'__renderCell';
117  return $del->$method($record);
118  //return call_user_func(array(&$del, $fieldname.'__renderCell'), $record);
119  }
120  return null;
121  }
122 
123 
124 
125  function toHtml(){
126  $context = array();
127  $context['relatedList'] = $this;
129  $context['app'] =& $app;
130 
131  $query =& $app->getQuery();
132  $context['query'] =& $query;
133 
134  if ( isset( $query['-related:sort']) ){
135  $sortcols = explode(',', trim($query['-related:sort']));
136  $sort_columns = array();
137  foreach ($sortcols as $sortcol){
138  $sortcol = trim($sortcol);
139  if (strlen($sortcol) === 0 ) continue;
140  $sortcol = explode(' ', $sortcol);
141  if ( count($sortcol) > 1 ){
142  $sort_columns[$sortcol[0]] = strtolower($sortcol[1]);
143  } else {
144  $sort_columns[$sortcol[0]] = 'asc';
145  }
146  break;
147  }
148  unset($sortcols); // this was just a temp array so we get rid of it here
149  } else {
150  $sort_columns = array();
151  }
152  $context['sort_columns'] =& $sort_columns;
153 
154  $sort_columns_arr = array();
155  foreach ( $sort_columns as $colkey=>$colorder) {
156  $sort_columns_arr[] = '`'.$colkey.'`'. $colorder;
157  }
158  if ( count($sort_columns_arr) > 0 ){
159  $sort_columns_str = implode(', ',$sort_columns_arr);
160  } else {
161  $sort_columns_str = 0;
162  }
163 
164 
165 
166  unset($query);
167 
168 
169  $skinTool =& Dataface_SkinTool::getInstance();
170  $context['skinTool'] =& $skinTool;
171 
172  $resultController =& $skinTool->getResultController();
173  $context['resultController'] =& $resultController;
174 
175  $s =& $this->_table;
176  $r =& $this->_relationship->_schema;
177  $fkeys = $this->_relationship->getForeignKeyValues();
178  $default_order_column = $this->_relationship->getOrderColumn();
179 
180  //echo "Def order col = $default_order_column";
181  ob_start();
182  df_display(array('redirectUrl'=>$_SERVER['PHP_SELF'].'?'.$_SERVER['QUERY_STRING']), 'Dataface_MoveUpForm.html');
183  $moveUpForm = ob_get_contents();
184  ob_end_clean();
185  $context['moveUpForm'] = $moveUpForm;
186 
187 
188 
189 
190 
191  $records =& $this->_record->getRelatedRecords($this->_relationship_name, true, $this->_start, $this->_limit, $this->_where);
192 
193  if ( PEAR::isError($records) ){
194  $records->addUserInfo("Error retrieving records from relationship ".$this->_relationship_name);
195  return $records;
196  }
197  $context['records'] =& $records;
198 
199  //echo "<br/><b>Now Showing</b> ".($this->_start+1)." to ".(min($this->_start + $this->_limit, $this->_record->numRelatedRecords($this->_relationship_name)));
200  $perms = $this->_record->getPermissions(array('relationship'=>$this->_relationship_name));
201  $context['perms'] = $perms;
202  $context['record_editable'] = Dataface_PermissionsTool::edit($this->_record);
203  $context['can_add_new_related_record'] = @$perms['add new related record'];
204  $context['can_add_existing_related_record'] = @$perms['add existing related record'];
205 
206  if ( !$this->hideActions and ($context['record_editable'] or @$perms['add new related record'] or @$perms['add existing related record']) ){
207  $query = array('-action'=>'new_related_record');
208  $link = Dataface_LinkTool::buildLink($query);
209  $context['new_related_record_query'] = $query;
210  $context['new_related_record_link'] = $link;
211 
212  $domainTable = $this->_relationship->getDomainTable();
213  //$context['domainTable'] =& $domainTable;
214  $importTablename = $domainTable;
215  if ( !PEAR::isError($domainTable) ){
216  //This relationship is many-to-many so we can add existing records to it.
217  $query2 = array('-action'=>'existing_related_record');
218  $context['existing_related_record_query'] = $query2;
219  $link2 = Dataface_LinkTool::buildLink($query2);
220  $context['existing_related_record_link'] = $link2;
221 
222  $destTables = $this->_relationship->getDestinationTables();
223  $context['destTables'] =& $destTables;
224  $importTablename = $destTables[0]->tablename;
225  $context['importTablename'] = $importTablename;
226  }
227  if ( !PEAR::isError($importTablename) ){
228  $importTable =& Dataface_Table::loadTable($importTablename);
229  $context['importTable'] =& $importTable;
230  $query3 = array('-action'=>'import');
231  $context['import_related_records_query'] =& $query3;
232  $link3 = Dataface_LinkTool::buildLink($query3);
233  $context['import_related_records_link'] = $link3;
234  }
235 
236 
237 
238  }
239 
240  $imgIcon = DATAFACE_URL.'/images/search_icon.gif';
241  $searchSrc = DATAFACE_URL.'/js/Dataface/RelatedList/search.js';
242  $relname = $this->_relationship_name;
243  $context['relationship_label'] = $this->_relationship->getLabel();
244  $context['relname'] = $relname;
245  $context['relationship_name'] = $this->_relationship_name;
246  $context['searchSrc'] = $searchSrc;
247  $context['imgIcon'] = $imgIcon;
248 
249 
250  if ( !$this->hideActions ){
251  $num_related_records = $this->_record->numRelatedRecords($this->_relationship_name, $this->_where);
252  $now_showing_start = $this->_start+1;
253  $now_showing_finish = min($this->_start + $this->_limit, $this->_record->numRelatedRecords($this->_relationship_name, $this->_where));
254 
255  $stats_context = array(
256  'num_related_records'=>$num_related_records,
257  'now_showing_start'=>$now_showing_start,
258  'now_showing_finish'=>$now_showing_finish,
259  'relationship_name'=>$this->_relationship_name,
260  'limit_field'=>$resultController->limitField('related:'),
261  'back_link'=>$this->_backButtonHtml(),
262  'next_link'=>$this->_forwardButtonHtml()
263 
264  );
265 
266  import('Dataface/ActionTool.php');
268  $actions = $at->getActions(array(
269  'category'=>'related_list_actions'
270  )
271  );
272 
273  $context['related_list_actions'] = $actions;
274  foreach ($stats_context as $k=>$v) $context[$k] = $v;
275 
276 
277 
278 
279  }
280 
281 
282  import('Dataface/ActionTool.php');
284  $selected_actions = $at->getActions(array('category'=>'selected_related_result_actions'));
285  $context['selected_actions'] = $selected_actions;
286 
287  if ( $this->_relationship->_schema['list']['type'] == 'treetable' ){
288  import('Dataface/TreeTable.php');
289  $treetable = new Dataface_TreeTable($this->_record, $this->_relationship->getName());
290  $context['treetable'] = $treetable->toHtml();
291  } else {
292  echo $moveUpForm;
293  if ( !$this->hideActions and $this->_where ){
294 
295  $filterQuery =& $app->getQuery();
296  $context['filterQuery'] =& $filterQuery;
297 
298  }
299  if ( count($records) > 0 ){
300 
301  ob_start();
302  echo '
303  <table class="listing relatedList relatedList--'.$this->_tablename.' relatedList--'.$this->_tablename.'--'.$this->_relationship_name.'" id="relatedList">
304  <thead>
305  <tr>';
306 
307  if ( count($selected_actions)>0){
308  echo '<th>';
309  if ( !$this->hideActions ){
310  echo '<input type="checkbox" onchange="toggleSelectedRows(this,\'relatedList\');">';
311  }
312  echo '</th>';
313  }
314  $cols = array_keys(current($records));
315 
316 
317 
318  $col_tables = array();
319  $table_keys = array();
320  $localFields = $this->_record->table()->fields();
321  $usedColumns = array();
322  foreach ($cols as $key ){
323  if ( $key == $default_order_column ) continue;
324  if ( is_int($key) ) continue;
325  if ( isset($sort_columns[$key]) ){
326  $class = 'sorted-column-'.$sort_columns[$key];
327  $query = array();
328  $qs_columns = $sort_columns;
329  unset($qs_columns[$key]);
330  $sort_query = $key.' '.($sort_columns[$key] == 'desc' ? 'asc' : 'desc');
331  foreach ( $qs_columns as $qcolkey=> $qcolvalue){
332  $sort_query .= ', '.$qcolkey.' '.$qcolvalue;
333  }
334  } else {
335  $class = 'unsorted-column';
336  $sort_query = $key.' asc';
337  foreach ( $sort_columns as $scolkey=>$scolvalue){
338  $sort_query .= ', '.$scolkey.' '.$scolvalue;
339  }
340 
341  }
342  $sq = array('-related:sort'=>$sort_query);
343  $link = Dataface_LinkTool::buildLink($sq);
344 
345  $fullpath = $this->_relationship_name.'.'.$key;
346 
347  $field =& $s->getField($fullpath);
348  if ( isset( $this->_relationship->_schema['visibility'][$key]) and $this->_relationship->_schema['visibility'][$key] == 'hidden' ) continue;
349  if ( $field['visibility']['list'] != 'visible') continue;
350  if ( $s->isBlob($fullpath) or $s->isPassword($fullpath) ) continue;
351  if ( isset($localFields[$key]) ) continue;
352  if ( PEAR::isError($field) ){
353  $field->addUserInfo("Error getting field info for field $key in RelatedList::toHtml() ");
354  return $field;
355  }
356  $usedColumns[] = $key;
357 
358  $label = $field['widget']['label'];
359  if ( isset($field['column']) and @$field['column']['label'] ){
360  $label = $field['column']['label'];
361  }
362 
363  $legend = '';
364  if ( @$field['column'] and @$field['column']['legend'] ){
365  $legend = '<span class="column-legend">'.htmlspecialchars($field['column']['legend']).'</span>';
366 
367  }
368  if (!$this->noLinks ){
369  echo '<th><a href="'.htmlspecialchars($link).'">'.htmlspecialchars($field['widget']['label'])."</a> $legend</th>\n";
370  } else {
371  echo '<th>'.$field['widget']['label'].'</th>';
372  }
373  if ( !isset($col_tables[$key]) ) $col_tables[$key] = $field['tablename'];
374  if (!isset($table_keys[$col_tables[$key]]) ){
375  $table_table =& Dataface_Table::loadTable($field['tablename']);
376  $table_keys[$col_tables[$key]] = array_keys($table_table->keys());
377  unset($table_table);
378  }
379  unset($field);
380 
381 
382  }
383  echo "</tr>
384  </thead>
385  <tbody id=\"relatedList-body\">
386  ";
387 
388  $limit = min( $this->_limit, $this->_record->numRelatedRecords($this->_relationship_name, $this->_where)-$this->_start);
389  $relatedTable = $this->_relationship->getDomainTable();
390  if ( PEAR::isError($relatedTable) ){
391  $relatedTable = reset($r['selected_tables']);
392  }
393  $relatedTable = Dataface_Table::loadTable($relatedTable);
394 
395  $relatedKeys = array_keys($relatedTable->keys());
396  foreach (array_keys($relatedKeys) as $i){
397  $relatedKeys[$i] = $this->_relationship_name.".".$relatedKeys[$i];
398  }
399 
400  $fullpaths = array();
401  $fields_index=array();
402  foreach( $usedColumns as $key ){
403  $fullpaths[$key] = $this->_relationship_name.'.'.$key;
404  $fields_index[$key] =& $s->getField($fullpaths[$key]);
405 
406 
407  }
408 
409  $evenRow = false;
410 
411 
412  for ( $i=$this->_start; $i<($this->_start+$limit); $i++){
413  $rowClass = $evenRow ? 'even' : 'odd';
414  $evenRow = !$evenRow;
415 
416  if ( $default_order_column and @$perms['reorder_related_records'] ){
417  $style = 'cursor:move';
418  // A variable that will be used below in javascript to decide
419  // whether to make the table sortable or not
420  $sortable_js = 'true';
421  } else {
422  $style = '';
423  $sortable_js = 'false';
424  }
425  $context['sortable_js'] = $sortable_js;
426 
427 
428  unset($rrec);
429  $rrec = $this->_record->getRelatedRecord($this->_relationship_name, $i,$this->_where, $sort_columns_str);//new Dataface_RelatedRecord($this->_record, $this->_relationship_name, $this->_record->getValues($fullpaths, $i, 0, $sort_columns_str));
430  $rrecid = $rrec->getId();
431 
432  echo "<tr class=\"listing $rowClass\" style=\"$style\" id=\"row_$rrecid\">";
433  if ( count($selected_actions)>0){
434  echo '
435  <td class="'.$rowClass.' viewableColumn" nowrap>';
436  if ( !$this->hideActions ){
437  echo '<input xf-record-id="'.htmlspecialchars($rrecid).'" class="rowSelectorCheckbox" id="rowSelectorCheckbox:'.htmlspecialchars($rrecid).'" type="checkbox">';
438  }
439  echo '</td>';
440  }
441 
442 
443  $link_queries=array();
444  foreach ($usedColumns as $key){
445  if ( is_int($key) ) continue;
446 
447  $fullpath = $fullpaths[$key];
448  unset($field);
449  $field =& $fields_index[$key];//$s->getField($fullpath);
450  $srcRecord =& $rrec->toRecord($field['tablename']);
451  $link = $srcRecord->getURL('-action=browse&-portal-context='.urlencode($rrecid));
452  $srcRecordId = $srcRecord->getId();
453 
454  //$val = $this->_record->preview($fullpath, $i,255, $this->_where, $sort_columns_str);
455  $val = strip_tags($rrec->display($key));
456  $title = "";
457 
458  if ( $key == $default_order_column ){
459  unset($field);
460  unset($srcRecord);
461  continue;
462 
463  } else {
464  if ($val != 'NO ACCESS'){
465  $accessClass = 'viewableColumn';
466  } else {
467 
468  $accessClass = '';
469  }
470  $cellClass = 'resultListCell resultListCell--'.$key;
471  $cellClass .= ' '.$srcRecord->table()->getType($key);
472  $renderVal = $this->renderCell($srcRecord, $field['Field']);
473  if ( isset($renderVal) ) $val = $renderVal;
474  else if ( $link and !@$field['noLinkFromListView'] and !$this->noLinks and $rrec->checkPermission('link', array('field'=>$key))) $val = "<a href=\"".htmlspecialchars($link)."\" title=\"". htmlspecialchars($title)."\" data-xf-related-record-id=\"".htmlspecialchars($srcRecordId)."\" class=\"xf-related-record-link\">".$val."</a>";
475  echo "<td class=\"$cellClass $rowClass $accessClass\">$val</td>\n";
476  unset($srcRecord);
477  }
478 
479  }
480  echo "</tr>\n";
481  }
482 
483  echo "</tbody>
484  </table>";
485 
486  $related_table_html = ob_get_contents();
487  $context['related_table_html'] = $related_table_html;
488  ob_end_clean();
489 
490  if ( !$this->hideActions ){
491  ob_start();
492 
493  echo '<form id="result_list_selected_items_form" method="post">';
495  $q =& $app->getQuery();
496  foreach ( $q as $key=>$val){
497  if ( strlen($key)>1 and $key{0} == '-' and $key{1} == '-' ){
498  continue;
499  }
500  echo '<input type="hidden" name="'.$key.'" value="'.htmlspecialchars($val).'">';
501  }
502  echo '<input type="hidden" name="--selected-ids" id="--selected-ids">';
503  echo '<input type="hidden" name="-from" id="-from" value="'.$query['-action'].'">';
504  echo '</form>';
505  $selected_actions_form = ob_get_contents();
506  $context['selected_actions_form'] = $selected_actions_form;
507  ob_end_clean();
508 
509 
510 
511 
512  // This bit of javascript goes through all of the columns and removes all columns that
513  // don't have any accessible information for this query. (i.e. any columns for which
514  // each row's value is 'NO ACCESS' is removed
515  $prototype_url = DATAFACE_URL.'/js/scriptaculous/lib/prototype.js';
516  $context['prototype_url'] = $prototype_url;
517  $scriptaculous_url =DATAFACE_URL.'/js/scriptaculous/src/scriptaculous.js';
518  $context['scriptaculous_url'] = $scriptaculous_url;
519  $effects_url = DATAFACE_URL.'/js/scriptaculous/src/effects.js';
520  $context['effects_url'] = $effects_url;
521  $dragdrop_url = DATAFACE_URL.'/js/scriptaculous/src/dragdrop.js';
522  $context['dragdrop_url'] = $dragdrop_url;
523  $thisRecordID = $this->_record->getId();
524  $context['thisRecordID'] = $thisRecordID;
525 
526  }
527 
528  }
529 
530 
531 
532  }
533 
534  ob_start();
535  df_display($context, 'xataface/RelatedList/list.html');
536  $out = ob_get_contents();
537  ob_end_clean();
538 
539  return $out;
540  }
541 
542 
543 
544  }
545