source: extensions/GrumPluginClasses/classes/GPCRequestBuilder.class.inc.php @ 7310

Last change on this file since 7310 was 7310, checked in by grum, 14 years ago

fix bug on the rbuilder and migrate ajax call on the gpc ajax manager
fix bug:1945
add some functionnalities

File size: 39.5 KB
Line 
1<?php
2/* -----------------------------------------------------------------------------
3  class name: GCPRequestBuilder
4  class version  : 1.1.1
5  plugin version : 3.3.2
6  date           : 2010-09-08
7
8  ------------------------------------------------------------------------------
9  Author     : Grum
10    email    : grum@piwigo.org
11    website  : http://photos.grum.com
12    PWG user : http://forum.phpwebgallery.net/profile.php?id=3706
13
14    << May the Little SpaceFrog be with you ! >>
15  ------------------------------------------------------------------------------
16  *
17  * theses classes provides base functions to manage search pictures in the
18  * database
19  *
20  *
21  * HOW TO USE IT ?
22  * ===============
23  *
24  * when installing the plugin, you have to register the usage of the request
25  * builder
26  *
27  * 1/ Create a RBCallback class
28  *  - extends the GPCSearchCallback class ; the name of the extended class must
29  *    be "RBCallBack%" => replace the "%" by the plugin name
30  *     for example : 'ThePlugin' => 'RBCallBackThePlugin'
31  *
32  * 2/ In the plugin 'maintain.inc.php' file :
33  *  - function plugin_install, add :
34  *       GPCRequestBuilder::register('plugin name', 'path to the RBCallback classe');
35  *          for example : GPCRequestBuilder::register('ThePlugin', $piwigo_path.'plugins/ThePlugin/rbcallback_file_name.php');
36  *
37  *
38  *  - function plugin_uninstall, add :
39  *       GPCRequestBuilder::unregister('plugin name');
40  *          for example : GPCRequestBuilder::unregister('ThePlugin');
41  *
42  * 3/ In the plugin code, put somewhere
43  *     GPCRequestBuilder::loadJSandCSS();
44  *     => this will load specific JS and CSS in the page, by adding url in the
45  *        the header, so try to put this where you're used to prepare the header
46  *
47  * 4/ to display the request builder, just add the returned string in the html
48  *    page
49  *       $stringForTheTemplate=GPCRequestBuilder::displaySearchPage();
50  *
51  *
52  *
53  * HOW DOES THE REQUEST BUILDER WORKS ?
54  * ====================================
55  *
56  * the request builder works in 2 steps :
57  *  - first step  : build a cache, to associate all image id corresponding to
58  *                  the search criterion
59  *                  the cache is an association of request ID/image id
60  *  - second step : use the cache to retrieve images informations
61  *
62  ------------------------------------------------------------------------------
63  :: HISTORY
64
65| release | date       |
66| 1.0.0   | 2010/04/30 | * start coding
67|         |            |
68| 1.1.0   | 2010/09/08 | * add functionnalities to manage complex requests
69|         |            |
70| 1.1.1   | 2010/10/14 | * fix bug on the buildGroupRequest function
71|         |            |   . adding 'DISTINCT' keyword to the SQL requests
72|         |            |
73|         |            | * ajax management moved into the gpc_ajax.php file
74|         |            |
75|         |            | * fix bug on user level access to picture
76|         |            |
77|         |            |
78|         |            |
79|         |            |
80|         |            |
81
82  --------------------------------------------------------------------------- */
83
84include_once('GPCTables.class.inc.php');
85
86/**
87 *
88 * Preparing the cache
89 * -------------------
90 * To prepare the cache, the request builder use the following functions :
91 *  - getFrom
92 *  - getWhere
93 *  - getJoin
94 *  - getImageId
95 *
96 * Retrieving the results
97 * ----------------------
98 * To retrieve the image informations, the request builder uses the following
99 * functions :
100 *  - getSelect
101 *  - getFrom
102 *  - getJoin
103 *  - getFilter (in fact, the result of this function is stored while the cache
104 *               is builded, but it is used only when retrieving the results for
105 *               multirecord tables)
106 *  - formatData
107 *
108 *
109 * Example
110 * -------
111 * Consider the table "tableA" like this
112 *
113 *  - (*) imageId
114 *  - (*) localId
115 *  -     att1
116 *  -     att2
117 *  The primary key is the 'imageId'+'localId' attributes
118 *    => for one imageId, you can have ZERO or more than ONE record
119 *       when you register the class, you have to set the $multiRecord parameter
120 *       to 'y'
121 *
122 *  gatImageId returns : "tableA.imageId"
123 *  getSelect returns  : "tableA.att1, tableA.att2"
124 *  getFrom returns    : "tableA"
125 *  getWhere returns   : "tableA.localId= xxxx AND tableA.att1 = zzzz"
126 *  getJoin returns    : "tableA.imageId = pit.id"
127 *  getFilter returns  : "tableA.localId= xxxx"
128 *
129 */
130class GPCSearchCallback {
131
132  /**
133   * the getImageId returns the name of the image id attribute
134   * return String
135   */
136  static public function getImageId()
137  {
138    return("");
139  }
140
141  /**
142   * the getSelect function must return an attribute list separated with a comma
143   *
144   * "att1, att2, att3, att4"
145   *
146   * you can specifie tables names and aliases
147   *
148   * "table1.att1 AS alias1, table1.att2 AS alias2, table2.att3 AS alias3"
149   */
150  static public function getSelect($param="")
151  {
152    return("");
153  }
154
155  /**
156   * the getFrom function must return a tables list separated with a comma
157   *
158   * "table1, (table2 left join table3 on table2.key = table3.key), table4"
159   */
160  static public function getFrom($param="")
161  {
162    return("");
163  }
164
165  /**
166   * the getWhere function must return a ready to use where clause
167   *
168   * "(att1 = value0 OR att2 = value1) AND att4 LIKE value2 "
169   */
170  static public function getWhere($param="")
171  {
172    return("");
173  }
174
175  /**
176   * the getJoin function must return a ready to use sql statement allowing to
177   * join the IMAGES table (key : pit.id) with given conditions
178   *
179   * "att3 = pit.id "
180   */
181  static public function getJoin($param="")
182  {
183    return("");
184  }
185
186
187  /**
188   * the getFilter function must return a ready to use where clause
189   * this where clause is used to filter the cache when the used tables can
190   * return more than one result
191   *
192   * the filter can be empty, can be equal to the where clause, or can be equal
193   * to a sub part of the where clause
194   *
195   */
196  static public function getFilter($param="")
197  {
198    return(self::getWhere($param));
199  }
200
201  /**
202   * this function is called by the request builder, allowing to display plugin
203   * data with a specific format
204   *
205   * @param Array $attributes : array of ('attribute_name' => 'attribute_value')
206   * @return String : HTML formatted value
207   */
208  static public function formatData($attributes)
209  {
210    return(print_r($attributes, true));
211  }
212
213
214  /**
215   * this function is called by the request builder to make the search page, and
216   * must return the HTML & JS code of the dialogbox used to select criterion
217   *
218   * Notes :
219   *  - the dialogbox is a JS object with a public method 'show'
220   *  - when the method show is called, one parameter is given by the request
221   *    builder ; the parameter is an object defined as this :
222   *      {
223   *        cBuilder: an instance of the criteriaBuilder object used in the page,
224   *      }
225   *
226   *
227   *
228   *
229   * @param String $mode : can take 'admin' or 'public' values, allowing to
230   *                       return different interface if needed
231   * @return String : HTML formatted value
232   */
233  static public function getInterfaceContent($mode='admin')
234  {
235    return("");
236  }
237
238  /**
239   * this function returns the label displayed in the criterion menu
240   *
241   * @return String : label displayed in the criterions menu
242   */
243  static public function getInterfaceLabel()
244  {
245    return(l10n('gpc_rb_unknown_interface'));
246  }
247
248  /**
249   * this function returns the name of the dialog box class
250   *
251   * @return String : name of the dialogbox class
252   */
253  static public function getInterfaceDBClass()
254  {
255    return('');
256  }
257
258
259}
260
261
262
263class GPCRequestBuilder {
264
265  static public $pluginName = 'GPCRequestBuilder';
266  static public $version = '1.1.0';
267
268  static private $tables = Array();
269  static protected $tGlobalId=0;
270
271  /**
272   * register a plugin using GPCRequestBuilder
273   *
274   * @param String $pluginName : the plugin name
275   * @param String $fileName : the php filename where the callback function can
276   *                           be found
277   * @return Boolean : true if registering is Ok, otherwise false
278   */
279  static public function register($plugin, $fileName)
280  {
281    $config=Array();
282    if(GPCCore::loadConfig(self::$pluginName, $config))
283    {
284      $config['registered'][$plugin]=Array(
285        'name' => $plugin,
286        'fileName' => $fileName,
287        'date' => date("Y-m-d H:i:s"),
288        'version' => self::$version
289      );
290      return(GPCCore::saveConfig(self::$pluginName, $config));
291    }
292    return(false);
293  }
294
295  /**
296   * unregister a plugin using GPCRequestBuilder
297   *
298   * assume that if the plugin was not registerd before, unregistering returns
299   * a true value
300   *
301   * @param String $pluginName : the plugin name
302   * @return Boolean : true if registering is Ok, otherwise false
303   */
304  static public function unregister($plugin)
305  {
306    $config=Array();
307    if(GPCCore::loadConfig(self::$pluginName, $config))
308    {
309      if(array_key_exists('registered', $config))
310      {
311        if(array_key_exists($plugin, $config['registered']))
312        {
313          unset($config['registered'][$plugin]);
314          return(GPCCore::saveConfig(self::$pluginName, $config));
315        }
316      }
317    }
318    // assume if the plugin was not registered before, unregistering it is OK
319    return(true);
320  }
321
322  /**
323   * @return Array : list of registered plugins
324   */
325  static public function getRegistered()
326  {
327    $config=Array();
328    if(GPCCore::loadConfig(self::$pluginName, $config))
329    {
330      if(array_key_exists('registered', $config))
331      {
332        return($config['registered']);
333      }
334    }
335    return(Array());
336  }
337
338
339  /**
340   * initialise the class
341   *
342   * @param String $prefixeTable : the piwigo prefixe used on tables name
343   * @param String $pluginNameFile : the plugin name used for tables name
344   */
345  static public function init($prefixeTable, $pluginNameFile)
346  {
347    $list=Array('request', 'result_cache', 'temp');
348
349    for($i=0;$i<count($list);$i++)
350    {
351      self::$tables[$list[$i]]=$prefixeTable.$pluginNameFile.'_'.$list[$i];
352    }
353  }
354
355  /**
356   * create the tables needed by RequestBuilder (used during the gpc process install)
357   */
358  static public function createTables()
359  {
360    $tablesDef=array(
361"CREATE TABLE `".self::$tables['request']."` (
362  `id` int(10) unsigned NOT NULL auto_increment,
363  `user_id` int(10) unsigned NOT NULL,
364  `date` datetime NOT NULL,
365  `num_items` int(10) unsigned NOT NULL default '0',
366  `execution_time` float unsigned NOT NULL default '0',
367  `connected_plugin` char(255) NOT NULL,
368  `filter` text NOT NULL,
369  `parameters` text NOT NULL,
370  PRIMARY KEY  (`id`)
371)
372CHARACTER SET utf8 COLLATE utf8_general_ci",
373
374"CREATE TABLE `".self::$tables['result_cache']."` (
375  `id` int(10) unsigned NOT NULL,
376  `image_id` int(10) unsigned NOT NULL,
377  PRIMARY KEY  (`id`,`image_id`)
378)
379CHARACTER SET utf8 COLLATE utf8_general_ci",
380
381"CREATE TABLE `".self::$tables['temp']."` (
382  `requestId` char(30) NOT NULL,
383  `imageId` mediumint(8) unsigned NOT NULL,
384  PRIMARY KEY  (`requestId`,`imageId`)
385)
386CHARACTER SET utf8 COLLATE utf8_general_ci",
387  );
388
389    $tablef= new GPCTables(self::$tables);
390    $tablef->create($tablesDef);
391
392    return(true);
393  }
394
395  /**
396   * update the tables needed by RequestBuilder (used during the gpc process
397   * activation)
398   */
399  static public function updateTables($pluginPreviousRelease)
400  {
401    $tablef=new GPCTables(array(self::$tables['temp']));
402
403    switch($pluginPreviousRelease)
404    {
405      case '03.01.00':
406        $tablesCreate=array();
407        $tablesUpdate=array();
408
409        $tablesCreate[]=
410"CREATE TABLE `".self::$tables['temp']."` (
411  `requestId` char(30) NOT NULL,
412  `imageId` mediumint(8) unsigned NOT NULL,
413  PRIMARY KEY  (`requestId`,`imageId`)
414)
415CHARACTER SET utf8 COLLATE utf8_general_ci";
416
417        $tablesUpdate[self::$tables['request']]['filter']=
418"ADD COLUMN  `filter` text NOT NULL default '' ";
419
420
421
422        $tablef->create($tablesCreate);
423        $tablef->updateTablesFields($tablesUpdate);
424        // no break ! need to be updated like the next release
425        // break;
426      case '03.01.01':
427      case '03.02.00':
428      case '03.02.01':
429      case '03.03.00':
430      case '03.03.01':
431        $tablesUpdate=array();
432
433        $tablesUpdate[self::$tables['request']]['parameters']=
434"ADD COLUMN `parameters` TEXT NOT NULL AFTER `filter`";
435
436        $tablef->updateTablesFields($tablesUpdate);
437        // no break ! need to be updated like the next release
438        // break;
439    }
440
441    return(true);
442  }
443
444  /**
445   * delete the tables needed by RequestBuilder
446   */
447  static public function deleteTables()
448  {
449    $tablef= new GPCTables(self::$tables);
450    $tablef->drop();
451    return(true);
452  }
453
454
455  /**
456   * this function add and handler on the 'loc_end_page_header' to add request
457   * builder JS script & specific CSS on the page
458   *
459   * use it when the displayed page need an access to the criteriaBuilder GUI
460   *
461   */
462  static public function loadJSandCSS()
463  {
464    add_event_handler('loc_begin_page_header', array('GPCRequestBuilder', 'insertJSandCSSFiles'), 9);
465  }
466
467
468  /**
469   * insert JS a CSS file in header
470   *
471   * the function is declared public because it used by the 'loc_begin_page_header'
472   * event callback
473   *
474   * DO NOT USE IT DIRECTLY
475   *
476   */
477  static public function insertJSandCSSFiles()
478  {
479    global $template;
480
481
482    $baseName=basename(dirname(dirname(__FILE__))).'/css/';
483    $template->append('head_elements', '<link href="plugins/'.$baseName.'rbuilder.css" type="text/css" rel="stylesheet"/>');
484
485    $baseName=basename(dirname(dirname(__FILE__))).'/js/';
486    GPCCore::addHeaderJS('jquery', 'themes/default/js/jquery.packed.js');
487    GPCCore::addHeaderJS('gpc.interface', 'plugins/'.$baseName.'external/interface/interface.js');
488    GPCCore::addHeaderJS('gpc.inestedsortable', 'plugins/'.$baseName.'external/inestedsortable.pack.js');
489    GPCCore::addHeaderJS('gpc.criteriaBuilder', 'plugins/'.$baseName.'criteriaBuilder.js');
490
491    $template->append('head_elements',
492"<script type=\"text/javascript\">
493  requestBuilderOptions = {
494      textAND:'".l10n('gpc_rb_textAND')."',
495      textOR:'".l10n('gpc_rb_textOR')."',
496      imgEditUrl:'',
497      imgDeleteUrl:'',
498      ajaxUrl:'plugins/GrumPluginClasses/gpc_ajax.php',
499  }
500</script>");
501  }
502
503
504  /**
505   * execute request from the ajax call
506   *
507   * @return String : a ready to use HTML code
508   */
509  static public function executeRequest($ajaxfct)
510  {
511    switch($ajaxfct)
512    {
513      case 'admin.rbuilder.searchExecute':
514        $result=self::doCache();
515        break;
516      case 'admin.rbuilder.searchGetPage':
517        $result=self::getPage($_REQUEST['requestNumber'], $_REQUEST['page'], $_REQUEST['numPerPage']);
518        break;
519    }
520    return($result);
521  }
522
523
524  /**
525   * clear the cache table
526   *
527   * @param Boolean $clearAll : if set to true, clear all records without
528   *                            checking timestamp
529   */
530  static public function clearCache($clearAll=false)
531  {
532    if($clearAll)
533    {
534      $sql="DELETE FROM ".self::$tables['result_cache'];
535    }
536    else
537    {
538      $sql="DELETE pgrc FROM ".self::$tables['result_cache']." pgrc
539              LEFT JOIN ".self::$tables['request']." pgr
540                ON pgrc.id = pgr.id
541              WHERE pgr.date < '".date('Y-m-d H:i:s', strtotime("-2 hour"))."'";
542    }
543    pwg_query($sql);
544  }
545
546  /**
547   * prepare the temporary table used for multirecord requests
548   *
549   * @param Integer $requestNumber : id of request
550   * @return String : name of the request key temporary table
551   */
552  static private function prepareTempTable($requestNumber)
553  {
554    //$tableName=call_user_func(Array('RBCallBack'.$plugin, 'getFrom'));
555    //$imageIdName=call_user_func(Array('RBCallBack'.$plugin, 'getImageId'));
556
557    $tempWHERE=array();
558    foreach($_REQUEST['extraData'] as $key => $extraData)
559    {
560      $tmpWHERE[$key]=array(
561        'plugin' => $extraData['owner'],
562        'where' => call_user_func(Array('RBCallBack'.$extraData['owner'], 'getWhere'), $extraData['param'])
563      );
564    }
565
566    $sql="INSERT INTO ".self::$tables['temp']." ".self::buildGroupRequest($_REQUEST[$_REQUEST['requestName']], $tmpWHERE, $_REQUEST['operator'], ' AND ', $requestNumber);
567
568    $result=pwg_query($sql);
569
570    return($requestNumber);
571  }
572
573  /**
574   * clear the temporary table used for multirecord requests
575   *
576   * @param Array $requestNumber : the requestNumber to delete
577   */
578  static private function clearTempTable($requestNumber)
579  {
580    $sql="DELETE FROM ".self::$tables['temp']." WHERE requestId = '$requestNumber';";
581    pwg_query($sql);
582  }
583
584
585  /**
586   * execute a query, and place result in cache
587   *
588   *
589   * @return String : queryNumber;numberOfItems
590   */
591  static private function doCache()
592  {
593    global $user;
594
595    self::clearCache();
596
597    $registeredPlugin=self::getRegistered();
598    $requestNumber=self::getNewRequest($user['id']);
599
600    $build=Array(
601      'SELECT' => 'pit.id',
602      'FROM' => '',
603      'WHERE' => 'pit.level <= '.$user['level'],
604      'GROUPBY' => '',
605      'FILTER' => '',
606    );
607    $tmpBuild=Array(
608      'FROM' => Array(
609        '('.IMAGES_TABLE.' pit LEFT JOIN '.IMAGE_CATEGORY_TABLE.' pic ON pit.id = pic.image_id)' /*JOIN IMAGES & IMAGE_CATEGORY tables*/
610       .'   JOIN '.USER_CACHE_CATEGORIES_TABLE.' pucc ON pucc.cat_id=pic.category_id',  /* IMAGE_CATEGORY & USER_CACHE_CATEGORIES_TABLE tables*/
611
612      ),
613      'WHERE' => Array(),
614      'JOIN' => Array(999=>'pucc.user_id='.$user['id']),
615      'GROUPBY' => Array(
616        'pit.id'
617      ),
618      'FILTER' => Array(),
619    );
620
621    /* build data request for plugins
622     *
623     * Array('Plugin1' =>
624     *          Array(
625     *            criteriaNumber1 => pluginParam1,
626     *            criteriaNumber2 => pluginParam2,
627     *            criteriaNumberN => pluginParamN
628     *          ),
629     *       'Plugin2' =>
630     *          Array(
631     *            criteriaNumber1 => pluginParam1,
632     *            criteriaNumber2 => pluginParam2,
633     *            criteriaNumberN => pluginParamN
634     *          )
635     * )
636     *
637     */
638    $pluginNeeded=Array();
639    $pluginList=Array();
640    $tempName=Array();
641    foreach($_REQUEST['extraData'] as $key => $val)
642    {
643      $pluginNeeded[$val['owner']][$key]=$_REQUEST['extraData'][$key]['param'];
644      $pluginList[$val['owner']]=$val['owner'];
645    }
646
647    /* for each plugin, include the rb callback class file */
648    foreach($pluginList as $val)
649    {
650      if(file_exists($registeredPlugin[$val]['fileName']))
651      {
652        include_once($registeredPlugin[$val]['fileName']);
653      }
654    }
655
656    /* prepare the temp table for the request */
657    self::prepareTempTable($requestNumber);
658    $tmpBuild['FROM'][]=self::$tables['temp'];
659    $tmpBuild['JOIN'][]=self::$tables['temp'].".requestId = '".$requestNumber."'
660                        AND ".self::$tables['temp'].".imageId = pit.id";
661
662    /* for each needed plugin, prepare the filter */
663    foreach($pluginNeeded as $key => $val)
664    {
665      foreach($val as $itemNumber => $param)
666      {
667        $tmpBuild['FILTER'][$key][]='('.call_user_func(Array('RBCallBack'.$key, 'getFilter'), $param).')';
668      }
669    }
670
671
672    /* build FROM
673     *
674     */
675    $build['FROM']=implode(',', $tmpBuild['FROM']);
676    unset($tmpBuild['FROM']);
677
678    /* build WHERE
679     */
680    self::cleanArray($tmpBuild['WHERE']);
681    if(count($tmpBuild['WHERE'])>0)
682    {
683      $build['WHERE']=' ('.self::buildGroup($_REQUEST[$_REQUEST['requestName']], $tmpBuild['WHERE'], $_REQUEST['operator'], ' AND ').') ';
684    }
685    unset($tmpBuild['WHERE']);
686
687
688    /* build FILTER
689     */
690    self::cleanArray($tmpBuild['FILTER']);
691    if(count($tmpBuild['FILTER'])>0)
692    {
693      $tmp=array();
694      foreach($tmpBuild['FILTER'] as $key=>$val)
695      {
696        $tmp[$key]='('.implode(' OR ', $val).')';
697      }
698      $build['FILTER']=' ('.implode(' AND ', $tmp).') ';
699      // array_flip twice => simply remove identical values...
700      //$build['FILTER']=' ('.implode(' AND ', array_flip(array_flip($tmpBuild['FILTER']))).') ';
701    }
702    unset($tmpBuild['FILTER']);
703
704
705    /* for each plugin, adds jointure with the IMAGE table
706     */
707    self::cleanArray($tmpBuild['JOIN']);
708    if(count($tmpBuild['JOIN'])>0)
709    {
710      if($build['WHERE']!='') $build['WHERE'].=' AND ';
711      $build['WHERE'].=' ('.implode(' AND ', $tmpBuild['JOIN']).') ';
712    }
713    unset($tmpBuild['JOIN']);
714
715    self::cleanArray($tmpBuild['GROUPBY']);
716    if(count($tmpBuild['GROUPBY'])>0)
717    {
718      $build['GROUPBY'].=' '.implode(', ', $tmpBuild['GROUPBY']).' ';
719    }
720    unset($tmpBuild['GROUPBY']);
721
722
723
724    $sql=' FROM '.$build['FROM'];
725    if($build['WHERE']!='')
726    {
727      $sql.=' WHERE '.$build['WHERE'];
728    }
729    if($build['GROUPBY']!='')
730    {
731      $sql.=' GROUP BY '.$build['GROUPBY'];
732    }
733
734    $sql.=" ORDER BY pit.id ";
735
736    $sql="INSERT INTO ".self::$tables['result_cache']." (SELECT DISTINCT $requestNumber, ".$build['SELECT']." $sql)";
737
738
739    $returned="0;0";
740
741    $result=pwg_query($sql);
742    if($result)
743    {
744      $numberItems=pwg_db_changes($result);
745      self::updateRequest($requestNumber, $numberItems, 0, implode(',', $pluginList), $build['FILTER'], $_REQUEST['extraData']);
746
747      $returned="$requestNumber;".$numberItems;
748    }
749
750    self::clearTempTable($requestNumber);
751
752    return($returned);
753  }
754
755  /**
756   * return a page content. use the cache table to find request result
757   *
758   * @param Integer $requestNumber : the request number (from cache table)
759   * @param Integer $pageNumber : the page to be returned
760   * @param Integer $numPerPage : the number of items returned on a page
761   * @return String : formatted HTML code
762   */
763  static private function getPage($requestNumber, $pageNumber, $numPerPage)
764  {
765    global $conf, $user;
766    $request=self::getRequest($requestNumber);
767
768    if($request===false)
769    {
770      return("KO");
771    }
772
773    $limitFrom=$numPerPage*($pageNumber-1);
774
775    $pluginNeeded=explode(',', $request['connected_plugin']);
776    $registeredPlugin=self::getRegistered();
777
778    $build=Array(
779      'SELECT' => '',
780      'FROM' => '',
781      'WHERE' => '',
782      'GROUPBY' => '',
783    );
784    $tmpBuild=Array(
785      'SELECT' => Array(
786        'RB_PIT' => "pit.id AS imageId, pit.name AS imageName, pit.path AS imagePath", // from the piwigo's image table
787        'RB_PIC' => "GROUP_CONCAT(DISTINCT pic.category_id SEPARATOR ',') AS imageCategoriesId",     // from the piwigo's image_category table
788        'RB_PCT' => "GROUP_CONCAT(DISTINCT CASE WHEN pct.name IS NULL THEN '' ELSE pct.name END SEPARATOR '#sep#') AS imageCategoriesNames,
789                     GROUP_CONCAT(DISTINCT CASE WHEN pct.permalink IS NULL THEN '' ELSE pct.permalink END SEPARATOR '#sep#') AS imageCategoriesPLink,
790                     GROUP_CONCAT(DISTINCT CASE WHEN pct.dir IS NULL THEN 'V' ELSE 'P' END) AS imageCategoriesDir",   //from the piwigo's categories table
791      ),
792      'FROM' => Array(
793        // join rb result_cache table with piwigo's images table, joined with the piwigo's image_category table, joined with the categories table
794        'RB' => "(((".self::$tables['result_cache']." pgrc
795                  RIGHT JOIN ".IMAGES_TABLE." pit
796                  ON pgrc.image_id = pit.id)
797                    RIGHT JOIN ".IMAGE_CATEGORY_TABLE." pic
798                    ON pit.id = pic.image_id)
799                       RIGHT JOIN ".CATEGORIES_TABLE." pct
800                       ON pct.id = pic.category_id)
801                          RIGHT JOIN ".USER_CACHE_CATEGORIES_TABLE." pucc
802                          ON pucc.cat_id = pic.category_id",
803      ),
804      'WHERE' => Array(
805        'RB' => "pgrc.id=".$requestNumber." AND pucc.user_id=".$user['id'],
806        ),
807      'JOIN' => Array(),
808      'GROUPBY' => Array(
809        'RB' => "pit.id"
810      )
811    );
812
813
814    $extraData=array();
815    foreach($request['parameters'] as $data)
816    {
817      $extraData[$data['owner']]=$data['param'];
818    }
819
820    /* for each needed plugin :
821     *  - include the file
822     *  - call the static public function getFrom, getJoin, getSelect
823     */
824    foreach($pluginNeeded as $key => $val)
825    {
826      if(array_key_exists($val, $registeredPlugin))
827      {
828        if(file_exists($registeredPlugin[$val]['fileName']))
829        {
830          include_once($registeredPlugin[$val]['fileName']);
831
832          $tmp=explode(',', call_user_func(Array('RBCallBack'.$val, 'getSelect'), $extraData[$val]));
833          foreach($tmp as $key2=>$val2)
834          {
835            $tmp[$key2]=self::groupConcatAlias($val2, '#sep#');
836          }
837          $tmpBuild['SELECT'][$val]=implode(',', $tmp);
838          $tmpBuild['FROM'][$val]=call_user_func(Array('RBCallBack'.$val, 'getFrom'), $extraData[$val]);
839          $tmpBuild['JOIN'][$val]=call_user_func(Array('RBCallBack'.$val, 'getJoin'), $extraData[$val]);
840        }
841      }
842    }
843
844    /* build SELECT
845     *
846     */
847    $build['SELECT']=implode(',', $tmpBuild['SELECT']);
848
849    /* build FROM
850     *
851     */
852    $build['FROM']=implode(',', $tmpBuild['FROM']);
853    unset($tmpBuild['FROM']);
854
855
856    /* build WHERE
857     */
858    if($request['filter']!='') $tmpBuild['WHERE'][]=$request['filter'];
859    $build['WHERE']=implode(' AND ', $tmpBuild['WHERE']);
860    unset($tmpBuild['WHERE']);
861
862
863    /* for each plugin, adds jointure with the IMAGE table
864     */
865    self::cleanArray($tmpBuild['JOIN']);
866    if(count($tmpBuild['JOIN'])>0)
867    {
868      $build['WHERE'].=' AND ('.implode(' AND ', $tmpBuild['JOIN']).') ';
869    }
870    unset($tmpBuild['JOIN']);
871
872    self::cleanArray($tmpBuild['GROUPBY']);
873    if(count($tmpBuild['GROUPBY'])>0)
874    {
875      $build['GROUPBY'].=' '.implode(', ', $tmpBuild['GROUPBY']).' ';
876    }
877    unset($tmpBuild['GROUPBY']);
878
879
880    $imagesList=Array();
881
882    $sql='SELECT '.$build['SELECT']
883        .' FROM '.$build['FROM']
884        .' WHERE '.$build['WHERE']
885        .' GROUP BY '.$build['GROUPBY']
886        .' ORDER BY pit.id '
887        .' LIMIT '.$limitFrom.', '.$numPerPage;
888//echo $sql;
889    $result=pwg_query($sql);
890    if($result)
891    {
892      while($row=pwg_db_fetch_assoc($result))
893      {
894        // affect standard datas
895        $datas['imageThumbnail']=dirname($row['imagePath'])."/".$conf['dir_thumbnail']."/".$conf['prefix_thumbnail'].basename($row['imagePath']);
896        $datas['imageId']=$row['imageId'];
897        $datas['imagePath']=$row['imagePath'];
898        $datas['imageName']=$row['imageName'];
899
900        $datas['imageCategoriesId']=explode(',', $row['imageCategoriesId']);
901        $datas['imageCategoriesNames']=explode('#sep#', $row['imageCategoriesNames']);
902        $datas['imageCategoriesPLink']=explode('#sep#', $row['imageCategoriesPLink']);
903        $datas['imageCategoriesDir']=explode(',', $row['imageCategoriesDir']);
904
905
906        $datas['imageCategories']=Array();
907        for($i=0;$i<count($datas['imageCategoriesId']);$i++)
908        {
909          $datas['imageCategories'][]=array(
910            'id' => $datas['imageCategoriesId'][$i],
911            'name' => $datas['imageCategoriesNames'][$i],
912            'dirType' => $datas['imageCategoriesDir'][$i],
913            'pLinks' => $datas['imageCategoriesPLink'][$i],
914            'link'=> make_index_url(
915                              array(
916                                'category' => array(
917                                  'id' => $datas['imageCategoriesId'][$i],
918                                  'name' => $datas['imageCategoriesNames'][$i],
919                                  'permalink' => $datas['imageCategoriesPLink'][$i])
920                              )
921                            )
922          );
923        }
924
925        /* affect datas for each plugin
926         *
927         * each plugin have to format the data in an HTML code
928         *
929         * so, for each plugin :
930         *  - look the attributes given in the SELECT clause
931         *  - for each attributes, associate the returned value of the record
932         *  - affect in datas an index equals to the plugin pluginName, with returned HTML code ; HTML code is get from a formatData function
933         *
934         * Example :
935         *  plugin ColorStart provide 2 attributes 'csColors' and 'csColorsPct'
936         *
937         *  we affect to the $attributes var :
938         *  $attributes['csColors'] = $row['csColors'];
939         *  $attributes['csColorsPct'] = $row['csColorsPct'];
940         *
941         *  call the ColorStat RB callback formatData with the $attributes => the function return a HTML code ready to use in the template
942         *
943         *  affect $datas['ColorStat'] = $the_returned_html_code;
944         *
945         *
946         */
947        foreach($tmpBuild['SELECT'] as $key => $val)
948        {
949          if($key!='RB_PIT' && $key!='RB_PIC' && $key!='RB_PCT')
950          {
951            $tmp=explode(',', $val);
952
953            $attributes=Array();
954
955            foreach($tmp as $key2 => $val2)
956            {
957              $name=self::getAttribute($val2);
958              $attributes[$name]=$row[$name];
959            }
960
961            $datas['plugin'][$key]=call_user_func(Array('RBCallBack'.$key, 'formatData'), $attributes);
962
963            unset($tmp);
964            unset($attributes);
965          }
966        }
967        $imagesList[]=$datas;
968        unset($datas);
969      }
970    }
971
972    return(self::toHtml($imagesList));
973    //return("get page : $requestNumber, $pageNumber, $numPerPage<br>$debug<br>$sql");
974  }
975
976  /**
977   * remove all empty value from an array
978   * @param Array a$array : the array to clean
979   */
980  static private function cleanArray(&$array)
981  {
982    foreach($array as $key => $val)
983    {
984      if(is_array($val))
985      {
986        self::cleanArray($val);
987        if(count($val)==0) unset($array[$key]);
988      }
989      elseif(trim($val)=='') unset($array[$key]);
990    }
991  }
992
993  /**
994   * returns the alias for an attribute
995   *
996   *  item1                          => returns item1
997   *  table1.item1                   => returns item1
998   *  table1.item1 AS alias1         => returns alias1
999   *  item1 AS alias1                => returns alias1
1000   *  GROUP_CONCAT( .... ) AS alias1 => returns alias1
1001   *
1002   * @param String $var : value to examine
1003   * @return String : the attribute name
1004   */
1005  static private function getAttribute($val)
1006  {
1007    preg_match('/(?:GROUP_CONCAT\(.*\)|(?:[A-Z0-9_]*)\.)?([A-Z0-9_]*)(?:\s+AS\s+([A-Z0-9_]*))?/i', trim($val), $result);
1008    if(array_key_exists(2, $result))
1009    {
1010      return($result[2]);
1011    }
1012    elseif(array_key_exists(1, $result))
1013    {
1014      return($result[1]);
1015    }
1016    else
1017    {
1018      return($val);
1019    }
1020  }
1021
1022
1023  /**
1024   * returns a a sql statement GROUP_CONCAT for an alias
1025   *
1026   *  item1                  => returns GROUP_CONCAT(item1 SEPARATOR $sep) AS item1
1027   *  table1.item1           => returns GROUP_CONCAT(table1.item1 SEPARATOR $sep) AS item1
1028   *  table1.item1 AS alias1 => returns GROUP_CONCAT(table1.item1 SEPARATOR $sep) AS alias1
1029   *  item1 AS alias1        => returns GROUP_CONCAT(item1 SEPARATOR $sep) AS alias1
1030   *
1031   * @param String $val : value to examine
1032   * @param String $sep : the separator
1033   * @return String : the attribute name
1034   */
1035  static private function groupConcatAlias($val, $sep=',')
1036  {
1037    /*
1038     * table1.item1 AS alias1
1039     *
1040     * $result[3] = alias1
1041     * $result[2] = item1
1042     * $result[1] = table1.item1
1043     */
1044    preg_match('/((?:(?:[A-Z0-9_]*)\.)?([A-Z0-9_]*))(?:\s+AS\s+([A-Z0-9_]*))?/i', trim($val), $result);
1045    if(array_key_exists(3, $result))
1046    {
1047      return("GROUP_CONCAT(".$result[1]." SEPARATOR '$sep') AS ".$result[3]);
1048    }
1049    elseif(array_key_exists(2, $result))
1050    {
1051      return("GROUP_CONCAT(".$result[1]." SEPARATOR '$sep') AS ".$result[2]);
1052    }
1053    else
1054    {
1055      return("GROUP_CONCAT($val SEPARATOR '$sep') AS ".$val);
1056    }
1057  }
1058
1059
1060  /**
1061   * get a new request number and create it in the request table
1062   *
1063   * @param Integer $userId : id of the user
1064   * @return Integer : the new request number, -1 if something wrong appened
1065   */
1066  static private function getNewRequest($userId)
1067  {
1068    $sql="INSERT INTO ".self::$tables['request']." VALUES('', '$userId', '".date('Y-m-d H:i:s')."', 0, 0, '', '', '')";
1069    $result=pwg_query($sql);
1070    if($result)
1071    {
1072      return(pwg_db_insert_id());
1073    }
1074    return(-1);
1075  }
1076
1077  /**
1078   * update request properties
1079   *
1080   * @param Integer $request_id : the id of request to update
1081   * @param Integer $numItems : number of items found in the request
1082   * @param Float $executionTime : time in second to execute the request
1083   * @param String $pluginList : list of used plugins
1084   * @param String $parameters : parameters given for the request
1085   * @return Boolean : true if request was updated, otherwise false
1086   */
1087  static private function updateRequest($requestId, $numItems, $executionTime, $pluginList, $additionalFilter, $parameters)
1088  {
1089    $sql="UPDATE ".self::$tables['request']."
1090            SET num_items = $numItems,
1091                execution_time = $executionTime,
1092                connected_plugin = '$pluginList',
1093                filter = '".mysql_escape_string($additionalFilter)."',
1094                parameters = '".serialize($parameters)."'
1095            WHERE id = $requestId";
1096    $result=pwg_query($sql);
1097    if($result)
1098    {
1099      return(true);
1100    }
1101    return(false);
1102  }
1103
1104  /**
1105   * returns request properties
1106   *
1107   * @param Integer $request_id : the id of request to update
1108   * @return Array : properties for request, false if request doesn't exist
1109   */
1110  static private function getRequest($requestId)
1111  {
1112    $returned=false;
1113    $sql="SELECT user_id, date, num_items, execution_time, connected_plugin, filter, parameters
1114          FROM ".self::$tables['request']."
1115          WHERE id = $requestId";
1116    $result=pwg_query($sql);
1117    if($result)
1118    {
1119      while($row=pwg_db_fetch_assoc($result))
1120      {
1121        if($row['parameters']!='') $row['parameters']=unserialize($row['parameters']);
1122        $returned=$row;
1123      }
1124    }
1125    return($returned);
1126  }
1127
1128
1129  /**
1130   * internal function used by the executeRequest function for single record
1131   * requests
1132   *
1133   * this function is called recursively
1134   *
1135   * @param Array $groupContent :
1136   * @param Array $items :
1137   * @return String : a where clause
1138   */
1139  static private function buildGroup($groupContent, $items, $groups, $operator)
1140  {
1141    $returned=Array();
1142    foreach($groupContent as $key => $val)
1143    {
1144      if(strpos($val['id'], 'iCbGroup')!==false)
1145      {
1146        preg_match('/[0-9]*$/i', $val['id'], $groupNumber);
1147        $returned[]=self::buildGroup($val['children'], $items, $groups, $groups[$groupNumber[0]]);
1148      }
1149      else
1150      {
1151        preg_match('/[0-9]*$/i', $val['id'], $itemNumber);
1152        $returned[]=" (".$items[$itemNumber[0]].") ";
1153      }
1154    }
1155    return('('.implode($operator, $returned).')');
1156  }
1157
1158
1159  /**
1160   * internal function used by the executeRequest function for multi records
1161   * requests
1162   *
1163   * this function is called recursively
1164   *
1165   * @param Array $groupContent :
1166   * @param Array $items :
1167   * @return String : a SQL request
1168   */
1169  static private function buildGroupRequest($groupContent, $whereItems, $groups, $operator, $requestNumber)
1170  {
1171    $returnedS='';
1172    $returned=Array();
1173    foreach($groupContent as $key => $val)
1174    {
1175      if(strpos($val['id'], 'iCbGroup')!==false)
1176      {
1177        preg_match('/[0-9]*$/i', $val['id'], $groupNumber);
1178
1179        $groupValue=self::buildGroupRequest($val['children'], $whereItems, $groups, $groups[$groupNumber[0]], $requestNumber);
1180
1181        if($groupValue!='')
1182          $returned[]=array(
1183            'mode'  => 'group',
1184            'value' => $groupValue
1185          );
1186      }
1187      else
1188      {
1189        preg_match('/[0-9]*$/i', $val['id'], $itemNumber);
1190
1191        $returned[]=array(
1192          'mode'  => 'item',
1193          'plugin' => $whereItems[$itemNumber[0]]['plugin'],
1194          'value' => " (".$whereItems[$itemNumber[0]]['where'].") "
1195        );
1196      }
1197    }
1198
1199    if(count($returned)>0)
1200    {
1201      if(strtolower(trim($operator))=='and')
1202      {
1203        $tId=0;
1204        foreach($returned as $key=>$val)
1205        {
1206          if($tId>0) $returnedS.=" JOIN ";
1207
1208          if($val['mode']=='item')
1209          {
1210            $returnedS.="(SELECT DISTINCT ".call_user_func(Array('RBCallBack'.$val['plugin'], 'getImageId'))." AS imageId
1211                          FROM ".call_user_func(Array('RBCallBack'.$val['plugin'], 'getFrom'))."
1212                          WHERE ".$val['value'].") t".self::$tGlobalId." ";
1213          }
1214          else
1215          {
1216            $returnedS.="(".$val['value'].") t".self::$tGlobalId." ";
1217          }
1218
1219          if($tId>0) $returnedS.=" ON t".(self::$tGlobalId-1).".imageId = t".self::$tGlobalId.".imageId ";
1220          $tId++;
1221          self::$tGlobalId++;
1222        }
1223        $returnedS="SELECT '$requestNumber', t".(self::$tGlobalId-$tId).".imageId FROM ".$returnedS;
1224      }
1225      else
1226      {
1227        foreach($returned as $key=>$val)
1228        {
1229          if($returnedS!='') $returnedS.=" UNION DISTINCT ";
1230
1231          if($val['mode']=='item')
1232          {
1233            $returnedS.="SELECT DISTINCT '$requestNumber', t".self::$tGlobalId.".imageId
1234                          FROM (SELECT ".call_user_func(Array('RBCallBack'.$val['plugin'], 'getImageId'))." AS imageId
1235                                FROM ".call_user_func(Array('RBCallBack'.$val['plugin'], 'getFrom'))."
1236                                WHERE ".$val['value'].") t".self::$tGlobalId." ";
1237          }
1238          else
1239          {
1240            $returnedS.="SELECT '$requestNumber', t".self::$tGlobalId.".imageId FROM (".$val['value'].") t".self::$tGlobalId;
1241          }
1242
1243          self::$tGlobalId++;
1244        }
1245      }
1246    }
1247
1248    return($returnedS);
1249  }
1250
1251
1252  /**
1253   * convert a list of images to HTML
1254   *
1255   * @param Array $imagesList : list of images id & associated datas
1256   * @return String : list formatted into HTML code
1257   */
1258  static protected function toHtml($imagesList)
1259  {
1260    global $template;
1261
1262    $template->set_filename('result_items',
1263                dirname(dirname(__FILE__)).'/templates/GPCRequestBuilder_result.tpl');
1264
1265
1266
1267    $template->assign('datas', $imagesList);
1268
1269    return($template->parse('result_items', true));
1270  }
1271
1272
1273  /**
1274   * returns allowed (or not allowed) categories for a user
1275   *
1276   * used the USER_CACHE_TABLE if possible
1277   *
1278   * @param Integer $userId : a valid user Id
1279   * @return String : IN(...), NOT IN(...) or nothing if there is no restriction
1280   *                  for the user
1281   */
1282  public function getUserCategories($userId)
1283  {
1284/*
1285    $returned='';
1286    if($user['forbidden_categories']!='')
1287    {
1288      $returned=Array(
1289        'JOIN' => 'AND ('.IMAGE_CATEGORY.'.category_id NOT IN ('.$user['forbidden_categories'].') ) ',
1290        'FROM' => IMAGE_CATEGORY
1291      );
1292
1293
1294    }
1295    *
1296    *
1297    */
1298  }
1299
1300
1301  /**
1302   * display search page
1303   *
1304   * @param Array $filter : an array of string ; each item is the name of a
1305   *                        registered plugin
1306   *                        if no parameters are given, no filter is applied
1307   *                        otherwise only plugin wich name is given are
1308   *                        accessible
1309   */
1310  static public function displaySearchPage($filter=array())
1311  {
1312    global $template, $lang;
1313
1314    load_language('rbuilder.lang', GPC_PATH);
1315
1316    if(is_string($filter)) $filter=array($filter);
1317    $filter=array_flip($filter);
1318
1319    $template->set_filename('gpc_search_page',
1320                dirname(dirname(__FILE__)).'/templates/GPCRequestBuilder_search.tpl');
1321
1322    $registeredPlugin=self::getRegistered();
1323    $dialogBox=Array();
1324    foreach($registeredPlugin as $key=>$val)
1325    {
1326      if(array_key_exists($key, $registeredPlugin) and
1327         (count($filter)==0 or array_key_exists($key, $filter)))
1328      {
1329        if(file_exists($registeredPlugin[$key]['fileName']))
1330        {
1331          include_once($registeredPlugin[$key]['fileName']);
1332
1333          $dialogBox[]=Array(
1334            'handle' => $val['name'].'DB',
1335            'dialogBoxClass' => call_user_func(Array('RBCallBack'.$key, 'getInterfaceDBClass')),
1336            'label' => call_user_func(Array('RBCallBack'.$key, 'getInterfaceLabel')),
1337            'content' => call_user_func(Array('RBCallBack'.$key, 'getInterfaceContent')),
1338          );
1339        }
1340      }
1341    }
1342
1343    $datas=Array(
1344      'dialogBox' => $dialogBox,
1345      'themeName' => $template->get_themeconf('name'),
1346    );
1347
1348    $template->assign('datas', $datas);
1349
1350    return($template->parse('gpc_search_page', true));
1351  } //displaySearchPage
1352
1353}
1354
1355
1356?>
Note: See TracBrowser for help on using the repository browser.