Merge lp:~gregfr/phpdevshell/TablesAndFilters-v1.0.0-trunk into lp:~titan-phpdevshell/phpdevshell/main

Proposed by ignatia on 2013-05-14
Status: Needs review
Proposed branch: lp:~gregfr/phpdevshell/TablesAndFilters-v1.0.0-trunk
Merge into: lp:~titan-phpdevshell/phpdevshell/main
Diff against target: 1201 lines (+1134/-0)
11 files modified
TablesAndFilters/config/plugin.config.xml (+116/-0)
TablesAndFilters/controllers/index.php (+16/-0)
TablesAndFilters/includes/TaF_excerpt.class.php (+32/-0)
TablesAndFilters/includes/TaF_filteredQuery.class.php (+398/-0)
TablesAndFilters/includes/TaF_table.class.php (+302/-0)
TablesAndFilters/models/tableTest.query.php (+12/-0)
TablesAndFilters/scripts/tableTest.php (+158/-0)
TablesAndFilters/tests/taf.Test.php (+82/-0)
nbproject/private/private.properties (+2/-0)
nbproject/project.properties (+7/-0)
nbproject/project.xml (+9/-0)
To merge this branch: bzr merge lp:~gregfr/phpdevshell/TablesAndFilters-v1.0.0-trunk
Reviewer Review Type Date Requested Status
TitanKing 2013-05-14 Pending
Review via email: mp+163637@code.launchpad.net
To post a comment you must log in.

Unmerged revisions

2. By greg <greg@Silencer> on 2012-07-01

Added class alias to plugin.config.xml

1. By TitanKing on 2010-11-11

Created tables and filters branch.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'TablesAndFilters'
2=== added directory 'TablesAndFilters/config'
3=== added file 'TablesAndFilters/config/plugin.config.xml'
4--- TablesAndFilters/config/plugin.config.xml 1970-01-01 00:00:00 +0000
5+++ TablesAndFilters/config/plugin.config.xml 2013-05-14 05:06:25 +0000
6@@ -0,0 +1,116 @@
7+<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
8+<!-- Please see http://phpdevshell.org for documentation on plugin config xml files. -->
9+<config type="plugin">
10+
11+ <!-- Use a proper plugin name without using special characters. -->
12+ <name>TablesAndFilters</name>
13+
14+ <!-- Human readable version number of your plugin. -->
15+ <version>3.0</version>
16+
17+ <!-- a Short description of your plugin. -->
18+ <description>This plugin provides easy to use tables based on query results.</description>
19+
20+ <!-- If the plugin/script is modification by you, place the original authors names here. -->
21+ <founder>Greg Reitter</founder>
22+
23+ <!-- Name of the developer for this plugin. -->
24+ <author>Greg Reitter</author>
25+
26+ <!-- Email address of the developer for this plugin. -->
27+ <email>greg@phpdevshell.org</email>
28+
29+ <!-- Plugin developers web address. -->
30+ <homepage>http://www.phpdevshell.org</homepage>
31+
32+ <!-- Date the plugin was developed, modified etc, this is up to you. -->
33+ <date>July 2010</date>
34+
35+ <!-- Copyright notice you would like to amend to your plugin. -->
36+ <copyright>Copyright 2010 PHPDevShell.org All rights reserved.</copyright>
37+
38+ <!-- License this plugin is released under. -->
39+ <license>http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html GNU/LGPL</license>
40+ <!-- Code Version XML URL check. -->
41+ <!-- Version (current) below is used to check for new releases and has little to do with database version. -->
42+ <versionurl current="3000">http://version.phpdevshell.org/TablesAndFilters.xml</versionurl>
43+
44+ <!-- Detailed information and help for this plugin. -->
45+ <info>
46+ <![CDATA[
47+ <p>
48+ This plugin provides easy to use tables based on query results.
49+ </p>
50+ ]]>
51+ </info>
52+ <!-- Version here represents the database version that should be install. -->
53+ <!-- If your database version needs no update, this number can stay the same. -->
54+ <!-- Upgrades further down will only be executed up to this number. -->
55+ <!-- This is the current database version that will be installed. -->
56+ <!-- Below is a series of example upgrade procedures. -->
57+ <install version="3000">
58+ <!--
59+ [contains][All query, menu, hooks, settings installation tags.]
60+ [param][version][int][mandatory][The latest database version in numbers only.]
61+ [note][This is how the plugin manager will know to what version upgrade scripts should be executed.]
62+ [note][Always keep install maintained to the latest menu, query, hooks and setting versions.]
63+ -->
64+ <menus>
65+ <!--
66+ [contains][All types of menu items that needs to be installed.]
67+ [note][Tags inside menus can be nested and repeated.]
68+ -->
69+ <menu name="testTable" type="1" link="scripts/tableTest.php" hide="0" rank="last" newwindow="" plugin="TablesAndFilters" >
70+ <!--
71+ [contains][Menu items can be contained in itself, this will create a menu tree.]
72+ [param][name][string][not-mandatory][The name of the menu item, if empty the pluginName.menu.lang.php will be used.]
73+ [param][type][int][not-mandatory][There are 8 menu types, 1 is the default if left empty.]
74+ [1][Plugin script] normal plugin menu item in your plugin folder.
75+ [2][Link existing menu] item while staying in its own menu group when clicked.
76+ [3][Link existing menu] item while jumping to original scripts menu group when clicked.
77+ [4][External file] Include external PHP web applications into PHPDevShell.
78+ [5][HTTP URL] Normal url to outside web.
79+ [6][Empty Place Holder] This item will only serve as a unclickable menu place holder.
80+ [7][iFrame] Link url to both external url or onsite url.
81+ [8][Cronjob Menu Type] The same as a plugin script but is set as cronjob.
82+ [param][link][string][mandatory][The url, script location or symlink holder will be entered here depending on type.]
83+ [param][hide][int][not-mandatory][There are 4 hide types, 0 is the default if left empty.]
84+ [0] Do not hide menu item.
85+ [1] Hide menu item from both Menu System and Control Panel.
86+ [2] Hide menu item from Control Panel only.
87+ [3] Hide menu item from Menu System only.
88+ [4] Hide menu only when inactive.
89+ [param][rank][string][not-mandatory][If you want to ensure ranking positions, will auto rank if left empty.]
90+ [int] Can be ranked with integer.
91+ [last] Will be ranked last in a menu group.
92+ [first] Will be ranked first in a menu group.
93+ [param][newwindow][int][not-mandatory][To make item open in new window set to 1, will not open in new if left empty.]
94+ [param][plugin][string][not-mandatory][Plugin name, use to install menu item to a different plugins menu structure.]
95+ [param][alias][string][not-mandatory][When switching on friendly urls in the settings and renaming rename.htaccess in the root, sef url will use this alias.]
96+ [param][parentlink][string][not-mandatory][Use with [param][plugin] or without to install in different structure.]
97+ [param][symlink][string][not-mandatory][Url or location with [param][plugin] or without to link to another menu item duplicating its use.]
98+ [note][Symlink is mandatory for menu types 1,2,6]
99+ [param][template][string][not-mandatory][Set template to use with plugin, if template is unavailable it will be installed.]
100+ [param][template][string][not-mandatory][Set the height of an iframe menu type.]
101+ [note][Height is mandatory for menu types 7]
102+ [param][layout][string][not-mandatory][Set a custom template.tpl location for a certain script.]
103+ [param][noautopermission][int][not-mandatory][Set to 1 to not add the installer of the plugin to permit menu item access.]
104+ -->
105+ </menu>
106+ </menus>
107+ <classes>
108+ <class name="TaF_table" alias="TaF_table" plugin="TablesAndFilters" rank="last" />
109+ <class name="TaF_html_table@TaF_table" alias="TaF_table" plugin="TablesAndFilters" rank="last" />
110+
111+ <class name="TaF_filteredQuery" alias="TaF_filteredQuery" plugin="TablesAndFilters" rank="last" />
112+ <class name="TaF_html_filteredQuery@TaF_filteredQuery" alias="TaF_html_filteredQuery" plugin="TablesAndFilters" rank="last" />
113+ <class name="TaF_html_filteredTable@TaF_filteredQuery" alias="TaF_html_filteredTable" plugin="TablesAndFilters" rank="last" />
114+ <class name="TaF_html_queryFilter@TaF_filteredQuery" alias="TaF_html_filteredTable" plugin="TablesAndFilters" rank="last" />
115+ <class name="TaF_list_queryFilter@TaF_filteredQuery" alias="TaF_html_filteredTable" plugin="TablesAndFilters" rank="last" />
116+ <class name="TaF_queryFilter@TaF_filteredQuery" alias="TaF_html_filteredTable" plugin="TablesAndFilters" rank="last" />
117+ <class name="TaF_radio_queryFilter@TaF_filteredQuery" alias="TaF_html_filteredTable" plugin="TablesAndFilters" rank="last" />
118+ <class name="TaF_select_queryFilter@TaF_filteredQuery" alias="TaF_html_filteredTable" plugin="TablesAndFilters" rank="last" />
119+
120+ </classes>
121+ </install>
122+</config>
123
124=== added directory 'TablesAndFilters/controllers'
125=== added file 'TablesAndFilters/controllers/index.php'
126--- TablesAndFilters/controllers/index.php 1970-01-01 00:00:00 +0000
127+++ TablesAndFilters/controllers/index.php 2013-05-14 05:06:25 +0000
128@@ -0,0 +1,16 @@
129+<?php
130+/**
131+ * PHPDevShell is a RAD Framework aimed at developing administrative applications.
132+ *
133+ * @package PHPDevShell
134+ * @link http://www.phpdevshell.org
135+ * @copyright Copyright (C) 2007 Jason Schoeman, All rights reserved.
136+ * @license GNU/LGPL, see readme/licensed_under_lgpl or http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
137+ * @author Jason Schoeman, Contact: titan [at] phpdevshell [dot] org.
138+ *
139+ * Copyright notice: See readme/notice
140+ * By using PHPDevShell you agree to notice and license, if you dont agree to this notice/license you are not allowed to use PHPDevShell.
141+ */
142+// Directory listing not allowed.
143+exit('Access Denied!');
144+?>
145\ No newline at end of file
146
147=== added directory 'TablesAndFilters/includes'
148=== added file 'TablesAndFilters/includes/TaF_excerpt.class.php'
149--- TablesAndFilters/includes/TaF_excerpt.class.php 1970-01-01 00:00:00 +0000
150+++ TablesAndFilters/includes/TaF_excerpt.class.php 2013-05-14 05:06:25 +0000
151@@ -0,0 +1,32 @@
152+<?php
153+
154+
155+
156+ class TaF_excerpt extends PHPDS_dependant
157+ {
158+ protected $data = array();
159+ protected $strip = true;
160+
161+
162+ public function addArray(array $query_result)
163+ {
164+ //$strip = empty($options['strip']) ? $this->strip : true;
165+ foreach($query_result as $key => $value) {
166+ $this->data[$key] = $this->strip ? true : $value;
167+ }
168+ }
169+
170+ public function addResult($query_name)
171+ {
172+ $params = func_get_args();
173+ array_shift($params); // first parameter of this function is $query_name
174+ $query = $this->db->makeQuery($query_name);
175+ $result = $query->invoke($params);
176+ if (is_array($result)) return $this->addArray($result); else return false;
177+ }
178+
179+ public function dump()
180+ {
181+ print_r($this->data);
182+ }
183+ }
184\ No newline at end of file
185
186=== added file 'TablesAndFilters/includes/TaF_filteredQuery.class.php'
187--- TablesAndFilters/includes/TaF_filteredQuery.class.php 1970-01-01 00:00:00 +0000
188+++ TablesAndFilters/includes/TaF_filteredQuery.class.php 2013-05-14 05:06:25 +0000
189@@ -0,0 +1,398 @@
190+<?php
191+
192+
193+
194+ /**
195+ * FILTERED QUERIES AND TABLE - EXPERIMENTAL
196+ *
197+ * The basic idea is to take the winning couple PHPDS_query + PHPDS_table and add multilayers of user-provided constraints (ie filters) to it
198+ *
199+ * @author greg completly stolen ideas from Jason
200+ *
201+ */
202+
203+ class TaF_queryFilter extends PHPDS_dependant
204+ {
205+ protected $table; // TaF_html_filteredTable
206+ protected $sql = ''; // a snipet in include into the WHERE clause
207+
208+ protected $field = ''; // filters are usualy (but not always) in the form: "field operator value" (ie. "age > 18")
209+ protected $value = '';
210+ protected $operator = '=';
211+
212+ public function __construct(TaF_html_filteredTable $table, TaF_filteredQuery $dependance)
213+ {
214+ parent::__construct($dependance); // our dependancy is the query
215+ $this->table = $table;
216+
217+ $this->init();
218+ }
219+
220+ /**
221+ * Give the filter a chance to alter the given query to do its job
222+ *
223+ * @param PHPDS_query $query
224+ * @param unknown_type $mode
225+ * @return unknown_type
226+ */
227+ public function alterQuery(PHPDS_query $query, $mode)
228+ {
229+ $sql = $this->sql();
230+ if ($sql) $query->addWhere($this->sql, $mode);
231+ return true;
232+ }
233+
234+ /**
235+ * return a piece of sql to be added to the WHERE clause
236+ *
237+ * @param unknown_type $sql
238+ * @return unknown_type
239+ */
240+ public function sql($sql = null)
241+ {
242+ if (!empty($sql)) $this->sql = $sql;
243+ if (empty($this->sql) && !empty($this->field) && !empty($this->value)) $this->sql = $this->field.' '.$this->operator.' '.$this->value;
244+ return $this->sql;
245+ }
246+
247+ public function init()
248+ {
249+ return true;
250+ }
251+ }
252+
253+
254+
255+ /**
256+ * Add an html presentation layer to a filter
257+ *
258+ * @author greg
259+ *
260+ */
261+ class TaF_html_queryFilter extends TaF_queryFilter
262+ {
263+ protected $id = '';
264+ protected $label = '';
265+
266+ public function __construct(TaF_html_filteredTable $table, TaF_filteredQuery $dependance)
267+ {
268+ if (empty($this->id)) $this->id = $table->id().'_'.get_class($this);
269+ parent::__construct($table, $dependance);
270+ }
271+
272+ public function label($label = null)
273+ {
274+ if (!empty($label)) $this->label = $label;
275+ return $this->label;
276+ }
277+
278+ public function id()
279+ {
280+ return $this->id;
281+ }
282+
283+ /**
284+ * This should ouput some html to be added before the table
285+ *
286+ * @return unknown_type
287+ */
288+ public function toHTML()
289+ {
290+ return '<p>'.$this->sql.'</p>';
291+ }
292+
293+ /**
294+ * Support for dynamic reload: bind the javascript function in a way you want it to be called for reloading the table
295+ *
296+ * @return javascript code to add inside a <script> clause
297+ */
298+ public function toJSbind()
299+ {
300+ return '';
301+ }
302+
303+ /**
304+ *
305+ * This should return a pair key=value to add to the request to ensure the filtering
306+ *
307+ * @return string in the form " key=value " suitable for html request (such as GET or POST)
308+ */
309+ public function toJSdata()
310+ {
311+ return '';
312+ }
313+ }
314+
315+
316+
317+
318+
319+ /**
320+ * A filter based on a list of key=>value pairs, possibly from a secondary query
321+ *
322+ * @author greg
323+ *
324+ */
325+ class TaF_list_queryFilter extends TaF_html_queryFilter
326+ {
327+ protected $list = array();
328+ protected $listQuery;
329+
330+ public function init ()
331+ {
332+ if (!empty($this->listQuery)) {
333+ $dep = array_pop(func_get_args());
334+
335+ $this->list = $this->buildList();
336+ }
337+
338+ //if ($this->select($this->select) != $this->select)
339+ $select = isset($_POST[$this->id()]) ? $_POST[$this->id()] : $this->defValue();
340+ // $this->select(VeM_GetFromRequest($this->id, $this->defValue()));
341+ $this->value($select);
342+ //TODO: get parameter is a nicer way
343+ }
344+
345+ public function buildList()
346+ {
347+ return is_a($this->listQuery, PHPDS_query) ? $dep->db->invokeQuery($this->listQuery): array();
348+ }
349+
350+ public function value($value = null)
351+ {
352+ if (!is_null($value)) {
353+ if (is_array($this->list) && isset($this->list[$value])) $this->value = $value;
354+ }
355+ return $this->value;
356+ }
357+
358+ public function defValue()
359+ {
360+ if (!empty($this->value)) return $this->value;
361+
362+ $a = array_keys($this->list);
363+ $b = array_shift(array_keys($this->list));
364+ if (is_array($this->list)) return array_shift(array_keys($this->list));
365+ }
366+ }
367+
368+
369+
370+ /**
371+ *
372+ * @author greg
373+ *
374+ */
375+ class TaF_select_queryFilter extends TaF_list_queryFilter
376+ {
377+ public function toHTML()
378+ {
379+ $fct_name = $this->table->makeName('js_reload');
380+ $html = "<label for=\"{$this->id}\">".$this->label()."</label>";
381+ $html .= "<select id=\"{$this->id}\" name=\"{$this->id}\" onChange=\"$fct_name()\" >";
382+ foreach($this->list as $key => $value) $html .= "<option value=\"$key\"".($this->value == $key ? 'selected' : '').">$value</option>";
383+ $html .= '</select>';
384+
385+ return $html;
386+ }
387+
388+ /*public function toJSbind()
389+ {
390+ $js = "$('#{$this->id}').change(function() { alert('toto'); $fct_name });";
391+ return $js;
392+ }*/
393+
394+ /*public function toJSdata()
395+ {
396+ return "'&{$this->id}=' + $('#{$this->id}').val()";
397+ }*/
398+
399+ }
400+
401+
402+
403+
404+
405+
406+
407+ class TaF_radio_queryFilter extends TaF_list_queryFilter
408+
409+ {
410+ protected $classes = array('line' => 'filterline', 'input' => 'filterinput');
411+
412+ public function toHTML()
413+ {
414+ $html = '<div class="'.$this->classes['line'].'">';
415+ $html .= '<label>'.$this->label().'</label>';
416+ foreach($this->list as $key => $value) {
417+ $name = $this->id;
418+ $id = $name.'_'.$value;
419+ $html .= "<input type=\"radio\" id=\"$id\" name=\"$name\" class=\"{$this->classes['input']}\" value=\"$key\" ";
420+ if ($key == $this->value) $html .= 'checked ';
421+ $html .= "/><label for=\"$id\">$value</label>";
422+ }
423+ $html .= '</div>';
424+ return $html;
425+ }
426+
427+ public function toJSbind($fct_name = '')
428+ {
429+ return "$('input[name={$this->id}]').click(function() { $fct_name; });";
430+ }
431+
432+ public function toJSdata()
433+ {
434+ return "'&{$this->id}=' + $('input:radio[name={$this->id}]:checked').val()";
435+ }
436+ }
437+
438+
439+
440+
441+
442+
443+
444+ class TaF_filteredQuery extends PHPDS_query
445+ {
446+
447+ protected $filters = array();
448+ protected $mode = 'AND';
449+
450+ /*
451+ * filter can be a TaF_queryFilter object or a TaF_queryFilter-descendant class name
452+ */
453+ public function addFilter($filter, TaF_html_filteredTable $table)
454+ {
455+ if (is_a($filter, 'TaF_queryFilter')) $this->filters[] = $filter;
456+ else {
457+ $params = func_get_args();
458+ array_shift($params); // shift-off $filter class name
459+ $this->filters[] = $this->_factory($filter, $params);
460+ }
461+ return $this;
462+ }
463+
464+ /**
465+ * Construct the extra part of the query (WHERE ... GROUP BY ... ORDER BY...)
466+ * Doesn't change $this->sql but DOES alter the query (our local clone)
467+ *
468+ * @param array $parameters
469+ * @return string (sql)
470+ */
471+ public function extra_build($parameters = null)
472+ {
473+ foreach ($this->filters as $filter) $filter->alterQuery($this, $this->mode);
474+
475+ return parent::extra_build($parameters);
476+ }
477+ }
478+
479+
480+
481+
482+
483+
484+
485+ class TaF_html_filteredQuery extends TaF_filteredQuery
486+ {
487+ public function filtersAsHTML()
488+ {
489+ $html = '';
490+ foreach($this->filters as $filter) $html .= $filter->toHTML();
491+ return $html;
492+ }
493+
494+ public function filtersAsJSbind()
495+ {
496+ $js = '';
497+ foreach($this->filters as $filter) $js .= $filter->toJSbind()."\n";
498+ return $js;
499+ }
500+
501+ public function filtersAsJSdata()
502+ {
503+ $js = array();
504+ foreach($this->filters as $filter) $js[] = $filter->toJSdata();
505+ return implode(' + ', $js);
506+ }
507+
508+ }
509+
510+
511+
512+
513+
514+
515+
516+
517+
518+
519+
520+
521+ class TaF_html_filteredTable extends TaF_html_table
522+ {
523+ /*
524+ * filter can be a TaF_queryFilter object or a TaF_queryFilter-descendant class name
525+ */
526+ public function addFilter($filter)
527+ {
528+ if (is_a($this->query, 'TaF_html_filteredQuery')) $this->query->addFilter($filter, $this);
529+ }
530+
531+ public function makeName($action)
532+ {
533+ switch ($action) {
534+ case 'js_reload': return 'TaFReload_'.$this->id;
535+ }
536+ }
537+
538+ public function make_html($forceLoad = false)
539+ {
540+ // first fetch the data
541+ $data_html = parent::make_html($forceLoad);
542+
543+ // then add some headers
544+ $html = $this->make_tag('div', $this->id.'_container', array('class' => 'slidingTable'));
545+ $url = PU_BuildURL();
546+ $html .= $this->make_tag('form', $this->id.'_form', array('action' => $url));
547+ if (is_a($this->query, 'TaF_html_filteredQuery')) $html .= $this->filtersAsHTML($this); // this is actually calling the query's method "filtersAsHTML()", at least if it's not overriden by the table
548+ $html .= $data_html;
549+
550+ //and some footers
551+ $html .= $this->make_tag(); // _form
552+ $html .= $this->make_tag(); // _container
553+ if (is_a($this->query, 'TaF_html_filteredQuery')) $html .= $this->make_js($this);
554+
555+ return $html;
556+ }
557+
558+ public function make_js()
559+ {
560+ $fct_name = $this->makeName('js_reload');
561+
562+ if ($js_filters = $this->query->filtersASJSdata()) $js_filters .= ' + '.$js_filters;
563+
564+ $js = '<script type="text/javascript" language="javascript">';
565+ $js .= "\n//<![CDATA[\n";
566+ $js .= <<<EndOfJavascript
567+
568+
569+ function $fct_name(opt)
570+ {
571+ url = $('#{$this->id}_form').attr('action');
572+ params = $('#{$this->id}_form').serializeArray(); // so it's sent as POST
573+ if (opt) params[params.length] = opt;
574+ $('#{$this->id}_container').load(url + ' #{$this->id}_form', params);
575+ }
576+
577+
578+EndOfJavascript;
579+
580+ $js .= $this->query->filtersAsJSbind($fct_name);
581+
582+ $js .= "\n//]]>\n</script>";
583+
584+ return $js;
585+ }
586+ }
587+
588
589=== added file 'TablesAndFilters/includes/TaF_table.class.php'
590--- TablesAndFilters/includes/TaF_table.class.php 1970-01-01 00:00:00 +0000
591+++ TablesAndFilters/includes/TaF_table.class.php 2013-05-14 05:06:25 +0000
592@@ -0,0 +1,302 @@
593+<?php
594+
595+
596+
597+/**
598+ * Basic class to link a table (i.e. tabular data) to a query result
599+ *
600+ * Usage 1 (implict instanciation of the class given its name):
601+ * $params = array(
602+ * 'query' => 'my_query_class',
603+ * 'showHeaders' => true,
604+ * 'headers' => array('ref' => 'Reference', 'name' => 'User name')
605+ * );
606+ * $t = $this->factory('TaF_table', $params);
607+ *
608+ * Usage 2 (you already have instanciated the class, you pass the object):
609+ * $query = $this->db->makeQuery('my_query_class', $query_parameters);
610+ * $params = array(
611+ * 'query' => 'my_query_class',
612+ * 'showHeaders' => true,
613+ * 'headers' => array('ref' => 'Reference', 'name' => 'User name')
614+ * );
615+ * $t = $this->factory($query, $params);
616+ *
617+ * Note: all unknown method class are routed to the query so you can actually do something like: $t->sql('SELECT * FROM mytable');
618+ *
619+ * @author greg
620+ *
621+ */
622+class TaF_table extends PHPDS_dependant
623+{
624+ /**
625+ * These are the parameters used to customize the table:
626+ * - query (optional): name of the query to instanciate or query object to use; if blank a useless empty PHPDS_query is created
627+ * - query_parameters (optional): parameters to pass on to the query when it's actually invokated
628+ * @var unknown_type
629+ */
630+ protected $parameters; // array
631+
632+ protected $parent = 'query';
633+ protected $query; // PHPDS_query
634+ protected $data; // array
635+
636+ protected $request;
637+
638+ protected $headers;
639+ protected $classes;
640+
641+ protected static $defaults = array('showHeaders' => false);
642+
643+
644+ public function construct ($parameters = null)
645+ {
646+ $this->parameters = is_array($parameters) ? array_merge(self::$defaults, $parameters) : self::$defaults;
647+
648+ $query = empty($this->parameters['query']) ? 'PHPDS_query' : $this->parameters['query'];
649+ $this->query = (is_a($query, 'PHPDS_query')) ? clone $query : $this->db->makeQuery($query);
650+ // TODO: is "clone" enough???
651+
652+ if (!empty($this->parameters->sql)) $this->query->sql($this->parameters->sql);
653+ }
654+
655+
656+ /**
657+ * Use the query to load all data into memory
658+ *
659+ * Unless forced, if the data is already in memory, do nothing
660+ *
661+ * @version 1.1
662+ * @date 20100528 (greg) (v1.1) use the query parameters taken from the table parameters array, if any
663+ *
664+ * @param boolean $force
665+ * @return array the data
666+ */
667+ public function load($force = false)
668+ {
669+ if (empty($this->data) || $force) {
670+ $this->data = empty($this->parameters['query_parameters']) ? $this->query->invoke() : $this->query->invoke($this->parameters['query_parameters']);
671+
672+ $this->purge();
673+ }
674+
675+ return $this->data;
676+ }
677+
678+ public function make_request()
679+ {
680+ $this->request = $this->query->query();
681+ }
682+
683+ public function purge()
684+ {
685+ }
686+
687+ public function next()
688+ {
689+ }
690+
691+ public function count()
692+ {
693+ if (empty($data)) return $this->query->count();
694+ else return count($this->data);
695+ }
696+
697+ public function scan($line)
698+ {
699+ return $line;
700+
701+ }
702+
703+ /*public function __call($name, $arguments)
704+ {
705+ if (method_exists($this->query, $name)) return call_user_func_array(array($this->query, $name), $arguments);
706+ else trigger_error("Table's query doesn't have a \"$name\"() method.");
707+ }*/
708+
709+ public function data($data = null)
710+ {
711+ if (is_array($data)) $this->data = $data;
712+ return $this->data;
713+ }
714+}
715+
716+
717+/**
718+ * An easy way to turn a query into a flexible HTML table
719+ *
720+ * @author greg
721+ *
722+ */
723+class TaF_html_table extends TaF_table
724+{
725+ protected $alternate_styles = array('alt1', 'alt1', 'alt1', 'alt2', 'alt2', 'alt2');
726+ protected $tag_styles = array(
727+ 'table' => 'inside_table', 'tr'=> 'highlight', 'th' => 'head'
728+ );
729+ protected $id = '';
730+
731+ protected $tag_stack = array();
732+
733+ protected $empty_content = '&nbsp;'; // default value in case we have to value to display
734+
735+
736+ public function construct ($parameters = null)
737+ {
738+ if (empty($this->id)) $this->id = get_class($this);
739+ //call_user_func_array(array($this, 'parent::__construct'), func_get_args());
740+ parent::construct ($parameters);
741+ }
742+
743+ public function id()
744+ {
745+ return $this->id;
746+ }
747+
748+ /**
749+ * Creates an array with the key and the labels of the headers
750+ *
751+ * @date 20100428 (v1.0) (greg)
752+ * @version 1.0
753+ * @author greg
754+ * @param array $data an row of data
755+ * @return array
756+ */
757+ public function build_headers($data)
758+ {
759+ $result = array();
760+
761+ if (!empty($this->parameters['headers'])) {
762+ // field list is given as a parameter
763+ $result = $this->parameters['headers'];
764+ /*foreach ($this->parameters['headers'] as $key => $value) {
765+ $result .= $this->make_tag('th');
766+ $result .= $value;
767+ $result .= $this->make_tag();
768+ }*/
769+ } elseif (!empty($this->query->fields)) {
770+ // fields are explicitly listed
771+ foreach ($this->query->fields as $key => $value) {
772+ // $key is column field name (from the DB point of view, $value is what's displayed
773+ $result[$key] = $value;
774+ }
775+ } elseif (is_array($data)) {
776+ // we're on our own, so we try to get the field names from the database result
777+ $first_line = $data[array_shift(array_keys($data))];
778+ if (is_array($first_line)) {
779+ $keys = array_keys($first_line);
780+ $result = array_combine($keys, $keys);
781+ } else $result = array('#', '_');
782+ }
783+
784+ return $result;
785+ }
786+
787+ /**
788+ * Takes a header array and make an html string out of it
789+ *
790+ * @date 20100428 (v1.0) (greg)
791+ * @version 1.0
792+ * @author greg
793+ * @param array $headers
794+ * @return string
795+ */
796+ public function make_headers($headers)
797+ {
798+ $result = '';
799+ if (!empty($this->parameters['showHeaders'])) {
800+ $result .= $this->make_tag('thead');
801+ $result .= $this->make_tag('tr');
802+
803+ if(is_array($headers)) {
804+ foreach ($headers as $key => $value) {
805+ // $key is column field name (from the DB point of view, $value is what's displayed
806+ $result .= $this->make_tag('th');
807+ $result .= $value;
808+ $result .= $this->make_tag(); // th
809+ }
810+ }
811+ $result .= $this->make_tag(); //tr
812+ $result .= $this->make_tag(); //theader
813+ }
814+ return $result;
815+ }
816+
817+
818+ public function make_html($forceLoad = false)
819+ {
820+ $this->load($forceLoad);
821+ $result = $this->make_tag('table', $this->id.'_table');
822+
823+ if (is_array($this->data) && count($this->data) > 0) {
824+ $headers = $this->build_headers($this->data);
825+ $result .= $this->make_headers($headers);
826+
827+ $line_index = 0;
828+
829+ $result .= $this->make_tag('tbody');
830+ foreach($this->data as $idx => $line) {
831+ $style = $this->alternate_styles[$line_index % count($this->alternate_styles)];
832+ $result .= $this->make_tag('tr', '', array('class' => $style));
833+
834+ foreach($headers as $key => $value) $result .= $this->make_cell($key, $line_index, $line, $idx);
835+ $result .= $this->make_tag(); // tr
836+ $line_index++;
837+ }
838+ $result .= $this->make_tag(); // tbody
839+ }
840+
841+ $result .= $this->make_tag(); // table
842+
843+ return $result;
844+ }
845+
846+
847+ public function make_cell($key, $line_index, $line, $idx)
848+ {
849+ $result = $this->make_tag('td');
850+ $matches = array();
851+ if (preg_match('/.+ as (?<alias>.+)/', $key, $matches)) $key = $matches['alias'];
852+ if (is_array($line)) {
853+ $value = empty($line[$key]) ? '' : $line[$key];
854+ } else $value = $line;
855+ $content = $this->chew($key, $line_index, $line, $value, $idx);
856+ if (empty($content)) $content = $this->empty_content;
857+ $result .= $content;
858+ $result .= $this->make_tag();
859+ return $result;
860+ }
861+
862+ /**
863+ * Placeholder to give you the opportunity to alter a cell content before it's displayed
864+ *
865+ * @param string $column_key
866+ * @param integer $line_index
867+ * @param array $line
868+ * @param mixed $value
869+ * @return striing
870+ */
871+ public function chew($column_key, $line_index, $line, $value)
872+ {
873+ return $value;
874+ }
875+
876+ public function make_tag($tag = null, $id = '', array $attributes = array(), $single = false)
877+ {
878+ if (empty($tag)) {
879+ $html = '</'.array_pop($this->tag_stack).'>';
880+ } else {
881+ $class_tag = empty($this->tag_styles[$tag]) ? array() : array('class' => $this->tag_styles[$tag]);
882+ $attr = PU_BuildAttrString(array_merge($class_tag, $attributes));
883+ if ($id) $id = " id=\"$id\" ";
884+ if ($single) {
885+ $html ="<$tag$id$attr />";
886+ }else {
887+ array_push($this->tag_stack, $tag);
888+ $html ="<$tag$id$attr>";
889+ }
890+ }
891+
892+ return $html;
893+ }
894+}
895\ No newline at end of file
896
897=== added directory 'TablesAndFilters/models'
898=== added file 'TablesAndFilters/models/tableTest.query.php'
899--- TablesAndFilters/models/tableTest.query.php 1970-01-01 00:00:00 +0000
900+++ TablesAndFilters/models/tableTest.query.php 2013-05-14 05:06:25 +0000
901@@ -0,0 +1,12 @@
902+<?php
903+ //require_once 'includes/query.class.php';
904+
905+ class testTableQuery extends TaF_html_filteredQuery
906+ {
907+ protected $sql = 'SELECT SQL_CALC_FOUND_ROWS * FROM _db_core_menu_items';
908+ protected $keyField = 'user_name';
909+
910+ }
911+
912+
913+
914
915=== added directory 'TablesAndFilters/scripts'
916=== added file 'TablesAndFilters/scripts/tableTest.php'
917--- TablesAndFilters/scripts/tableTest.php 1970-01-01 00:00:00 +0000
918+++ TablesAndFilters/scripts/tableTest.php 2013-05-14 05:06:25 +0000
919@@ -0,0 +1,158 @@
920+<?php
921+
922+ //(is_object($security)) ? $this->security->load_security(true) : exit('Access Denied!'); ////////////////////////////////////////
923+
924+ ?>
925+
926+ <style type="text/css">
927+ .navigBtn {
928+ padding: 3px;
929+ outline: 1px solid black;
930+ }
931+ </style>
932+
933+ <?php
934+
935+
936+ class_exists('TaF_table');
937+ class_exists('TaF_filteredQuery');
938+ class_exists('PHPDS_user');
939+
940+
941+
942+
943+ class pagingFilter extends TaF_html_queryFilter
944+ {
945+ protected $pageSize = 10;
946+ protected $pageNumber = 1;
947+
948+ protected $foundRows = 0;
949+ protected $numRows = 0;
950+ protected $lastPage = 0;
951+
952+ public function init()
953+ {
954+ $pageNo = empty($_POST['pageNo']) ? 1 :intval($_POST['pageNo']);
955+ if ($pageNo > 0) $this->pageNumber = $pageNo;
956+
957+ $pageSize = empty($_POST['pageSize']) ? 10 : intval($_POST['pageSize']);
958+ if ($pageSize > 0) $this->pageSize = $pageSize;
959+ }
960+
961+ public function makeNavigBtn($label, $pageNo, $fct_name, $extra = '')
962+ {
963+ $html = '<button class="navigBtn" onClick="'.$fct_name.'({name:\'pageNo\',value:\''.$pageNo.'\'})"';
964+ $html .= ' type="button" value="'.$pageNo.'" name="'.$this->id().'" '.$extra.' >'.$label.'</button>';
965+
966+ return $html;
967+ }
968+
969+ public function toHTML()
970+ {
971+ $this->numRows = $this->count();
972+ $this->foundRows = mysql_result($this->db->connector->query('SELECT FOUND_ROWS()'), 0 ,0);
973+
974+ $this->lastPage = intval($this->foundRows / $this->pageSize) + 1;
975+
976+ $fct_name = $this->table->makeName('js_reload');
977+
978+ $html = '<p>Paging by '.$this->pageSize.' rows - '.time().'</p>';
979+ $html .= '<p>Rows on page '.$this->pageNumber.' (for a total of '.$this->foundRows.' rows on '.$this->lastPage.' pages)</p>';
980+
981+ $disabled = ($this->pageNumber < 2) ? 'disabled' : '';
982+ $html .= $this->makeNavigBtn('First', 1, $fct_name, $disabled);
983+ $html .= $this->makeNavigBtn('Prev', $this->pageNumber - 1, $fct_name, $disabled);
984+
985+ $disabled = ($this->pageNumber >= $this->lastPage) ? 'disabled' : '';
986+ $html .= $this->makeNavigBtn('Next', $this->pageNumber + 1, $fct_name, $disabled);
987+ $html .= $this->makeNavigBtn('Last', $this->lastPage, $fct_name, $disabled);
988+
989+ return $html;
990+ }
991+
992+ public function alterQuery(PHPDS_query $query, $mode)
993+ {
994+ if ($this->pageSize < 1) $this->pageSize = 1;
995+ if ($this->pageNumber < 1) $this->pageNumber = 1;
996+
997+ $query->limit($this->pageSize * ($this->pageNumber -1).', '.$this->pageSize);
998+ return true;
999+ }
1000+ }
1001+
1002+
1003+
1004+ class testTable extends TaF_html_filteredTable
1005+ {
1006+ /*public function _make_js()
1007+ {
1008+ $fct_name = 'TaFReload_'.$this->id;
1009+
1010+ if ($js_filters = $this->query->filtersASJSdata()) $js_filters .= ' + '.$js_filters;
1011+
1012+ $js = '<script type="text/javascript" language="javascript">';
1013+ $js .= "\n//<![CDATA[\n";
1014+ $js .= <<<EOS
1015+
1016+
1017+ function $fct_name(url, dir)
1018+ {
1019+ if (dir < 0) {
1020+ jQuery.get(url{$js_filters} + ' #{$this->id}_div', function (data, textStatus, XMLHttpRequest) {
1021+ $('#{$this->id}_container').prepend($(data).find('#testTable_div'));
1022+ previous = $('#{$this->id}_container div:last-child');
1023+ //previous.slideDown(function() { previous.remove(); });
1024+ });
1025+ } else {
1026+ jQuery.get(url{$js_filters} + ' #{$this->id}_div', function (data, textStatus, XMLHttpRequest) {
1027+ $('#{$this->id}_container').append($(data).find('#testTable_div'));
1028+ previous = $('#{$this->id}_container div:first-child');
1029+ previous.slideUp(function() { previous.remove(); });
1030+ });
1031+ }
1032+ }
1033+
1034+
1035+EOS;
1036+
1037+ $js .= '$(document).ready(function() {';
1038+ $js .= $this->query->filtersAsJSbind($fct_name.'();');
1039+ $js .=' });';
1040+
1041+ $js .= "\n//]]>\n</script>";
1042+
1043+ return $js;
1044+ }*/
1045+ }
1046+
1047+ class f1 extends TaF_select_queryFilter
1048+ {
1049+ protected $field = 'user_role';
1050+ protected $list = array(1 => 'one', 2 => 'two');
1051+ protected $value = 2;
1052+ }
1053+
1054+ class tableTestController extends PHPDS_controller
1055+ {
1056+
1057+ protected $params = array(
1058+ 'query' => 'testTableQuery',
1059+ 'showHeaders' => true
1060+ );
1061+
1062+ public function execute()
1063+ {
1064+ $t = $this->factory('testTable', $this->params);
1065+ /*$t->addFilter('pagingFilter');
1066+ $t->addFilter('f1');*/
1067+ echo '<tr><td>';
1068+ echo $t->make_html();
1069+ echo '</td></tr>';
1070+ }
1071+ }
1072+
1073+ return 'tableTestController';
1074+
1075+
1076+
1077+
1078
1079=== added directory 'TablesAndFilters/tests'
1080=== added file 'TablesAndFilters/tests/taf.Test.php'
1081--- TablesAndFilters/tests/taf.Test.php 1970-01-01 00:00:00 +0000
1082+++ TablesAndFilters/tests/taf.Test.php 2013-05-14 05:06:25 +0000
1083@@ -0,0 +1,82 @@
1084+<?php
1085+
1086+ require_once 'mock_connector.php';
1087+
1088+ require_once BASEPATH.'/plugins/TablesAndFilters/includes/TaF_table.class.php';
1089+ require_once BASEPATH.'/plugins/TablesAndFilters/includes/TaF_filteredQuery.class.php';
1090+ require_once BASEPATH.'/plugins/TablesAndFilters/includes/TaF_excerpt.class.php';
1091+
1092+ class TAF_testTable extends TaF_html_filteredTable
1093+ {
1094+
1095+ }
1096+
1097+ class testTableQuery extends TEST_stubQuery
1098+ {
1099+
1100+ }
1101+
1102+ class TAF_tableTest extends PHPUnit_Framework_TestCase
1103+ {
1104+ protected $query;
1105+ protected $stub;
1106+ protected $table;
1107+
1108+ protected function setUp()
1109+ {
1110+ $db = PHPDSlib::instance()->my_db();
1111+ $this->query = $db->factory('TEST_stubQuery');
1112+ $this->stub = $db->factory('TEST_mock_connector');
1113+ $this->query->connector($this->stub);
1114+ }
1115+
1116+ public function testDefaults()
1117+ {
1118+ $data_in = array(
1119+ array('col1' => 1, 'col2' => 'one', 'col3' => false, 'col4' => 'abc'),
1120+ );
1121+ $this->stub->data = $data_in;
1122+
1123+ $this->table = $this->query->factory('TAF_testTable');
1124+
1125+ $html = $this->table->make_html();
1126+ $this->assertEquals('<div id="TAF_testTable_container" class="slidingTable"><form id="TAF_testTable_form" action="http://TEST/test.php"><table id="TAF_testTable_table" class="inside_table"></table></form></div>', $html);
1127+ }
1128+
1129+
1130+ /**
1131+ * @dataProvider tableValueProvider
1132+ * @group TaF
1133+ */
1134+ public function testVariousData($data, $result)
1135+ {
1136+ $this->stub->data = $data;
1137+
1138+ $params = array(
1139+ 'query' => $this->query,
1140+ 'showHeaders' => true
1141+ );
1142+ $this->table = $this->query->factory('TAF_testTable', $params);
1143+ $html = $this->table->make_html(true);
1144+ $this->assertEquals($result, $html);
1145+ }
1146+
1147+ public function tableValueProvider()
1148+ {
1149+ return array(
1150+ array(
1151+ array(
1152+ array('col1' => 1, 'col2' => 'one', 'col3' => false, 'col4' => 'abc'),
1153+ ),
1154+ '<div id="TAF_testTable_container" class="slidingTable"><form id="TAF_testTable_form" action="http://TEST/test.php"><table id="TAF_testTable_table" class="inside_table"><thead><tr class="highlight"><th class="head">col1</th><th class="head">col2</th><th class="head">col3</th><th class="head">col4</th></tr></thead><tbody><tr class="alt1"><td>1</td><td>one</td><td>&nbsp;</td><td>abc</td></tr></tbody></table></form></div>'
1155+ ),
1156+ array(
1157+ array(
1158+ array('col1' => 1, 'col2' => 'one', 'col3' => false, 'col4' => 'abc'),
1159+ array('col1' => 2, 'col2' => 'two', 'col3' => true, 'col4' => 'def'),
1160+ ),
1161+ '<div id="TAF_testTable_container" class="slidingTable"><form id="TAF_testTable_form" action="http://TEST/test.php"><table id="TAF_testTable_table" class="inside_table"><thead><tr class="highlight"><th class="head">col1</th><th class="head">col2</th><th class="head">col3</th><th class="head">col4</th></tr></thead><tbody><tr class="alt1"><td>1</td><td>one</td><td>&nbsp;</td><td>abc</td></tr><tr class="alt1"><td>2</td><td>two</td><td>1</td><td>def</td></tr></tbody></table></form></div>'
1162+ ),
1163+ );
1164+ }
1165+ }
1166\ No newline at end of file
1167
1168=== added directory 'nbproject'
1169=== added directory 'nbproject/private'
1170=== added file 'nbproject/private/private.properties'
1171--- nbproject/private/private.properties 1970-01-01 00:00:00 +0000
1172+++ nbproject/private/private.properties 2013-05-14 05:06:25 +0000
1173@@ -0,0 +1,2 @@
1174+index.file=index.php
1175+url=http://localhost/TablesAndFilters/
1176
1177=== added file 'nbproject/project.properties'
1178--- nbproject/project.properties 1970-01-01 00:00:00 +0000
1179+++ nbproject/project.properties 2013-05-14 05:06:25 +0000
1180@@ -0,0 +1,7 @@
1181+include.path=${php.global.include.path}
1182+php.version=PHP_53
1183+source.encoding=UTF-8
1184+src.dir=.
1185+tags.asp=false
1186+tags.short=true
1187+web.root=.
1188
1189=== added file 'nbproject/project.xml'
1190--- nbproject/project.xml 1970-01-01 00:00:00 +0000
1191+++ nbproject/project.xml 2013-05-14 05:06:25 +0000
1192@@ -0,0 +1,9 @@
1193+<?xml version="1.0" encoding="UTF-8"?>
1194+<project xmlns="http://www.netbeans.org/ns/project/1">
1195+ <type>org.netbeans.modules.php.project</type>
1196+ <configuration>
1197+ <data xmlns="http://www.netbeans.org/ns/php-project/1">
1198+ <name>TablesAndFilters</name>
1199+ </data>
1200+ </configuration>
1201+</project>

Subscribers

People subscribed via source and target branches