source: extensions/ColorStat/cstat_aip.class.inc.php @ 12446

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

Release 1.0.3
Fix bug bug:2074 (optimized process)

  • Property svn:executable set to *
File size: 21.9 KB
Line 
1<?php
2/* -----------------------------------------------------------------------------
3  Plugin     : ColorStat
4  Author     : Grum
5    email    : grum@piwigo.org
6    website  : http://photos.grum.fr
7
8    << May the Little SpaceFrog be with you ! >>
9  ------------------------------------------------------------------------------
10  See main.inc.php for release information
11
12  CStat_AIP : classe to manage plugin admin pages
13
14  --------------------------------------------------------------------------- */
15
16if(!defined('PHPWG_ROOT_PATH')) die('Hacking attempt!');
17
18include_once('cstat_root.class.inc.php');
19include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/GPCTables.class.inc.php');
20include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/GPCTabSheet.class.inc.php');
21include_once(PHPWG_PLUGINS_PATH.'GrumPluginClasses/classes/GPCRequestBuilder.class.inc.php');
22include_once(PHPWG_ROOT_PATH.'admin/include/tabsheet.class.php');
23
24class CStat_AIP extends CStat_root
25{
26  protected $tabsheet;
27
28  public function __construct($prefixeTable, $filelocation)
29  {
30    parent::__construct($prefixeTable, $filelocation);
31    $this->checkRequest();
32
33    $this->loadConfig();
34    $this->initEvents();
35
36    $this->tabsheet = new tabsheet();
37    $this->tabsheet->add('database',
38                          l10n('cstat_database'),
39                          $this->getAdminLink()."&amp;f_tabsheet=database");
40    $this->tabsheet->add('stat',
41                          l10n('cstat_stat'),
42                          $this->getAdminLink()."&amp;f_tabsheet=stat");
43    $this->tabsheet->add('search',
44                          l10n('cstat_search'),
45                          $this->getAdminLink()."&amp;f_tabsheet=search");
46    $this->tabsheet->add('config',
47                          l10n('cstat_config'),
48                          $this->getAdminLink()."&amp;f_tabsheet=config");
49  }
50
51  public function __destruct()
52  {
53    unset($this->tabsheet);
54    parent::__destruct();
55  }
56
57  /*
58    initialize events call for the plugin
59  */
60  public function initEvents()
61  {
62    parent::initEvents();
63    if($_REQUEST['f_tabsheet']=='search')
64    {
65      // load request builder JS only on the search page
66      GPCRequestBuilder::loadJSandCSS();
67    }
68    add_event_handler('loc_end_page_header', array(&$this->css, 'applyCSS'));
69    GPCCss::applyGpcCss();
70  }
71
72  /*
73    display administration page
74  */
75  public function manage()
76  {
77    global $template;
78
79    $this->returnAjaxContent();
80
81    $template->set_filename('plugin_admin_content', dirname(__FILE__)."/admin/cstat_admin.tpl");
82
83
84    switch($_REQUEST['f_tabsheet'])
85    {
86      case 'database':
87        $this->displayDatabasePage();
88        break;
89      case 'stat':
90        $this->displayStatPage();
91        break;
92      case 'search':
93        $this->displaySearchPage();
94        break;
95      case 'config':
96        $this->displayConfigPage();
97        break;
98    }
99
100    $this->tabsheet->select($_REQUEST['f_tabsheet']);
101    $this->tabsheet->assign();
102    $selected_tab=$this->tabsheet->get_selected();
103    $template->assign($this->tabsheet->get_titlename(), "[".$selected_tab['caption']."]");
104
105    $template_plugin["CSTAT_VERSION"] = "<i>".$this->getPluginName()."</i> ".l10n('cstat_release').CSTAT_VERSION;
106
107    $template->assign('plugin', $template_plugin);
108    $template->assign_var_from_handle('ADMIN_CONTENT', 'plugin_admin_content');
109  }
110
111
112  /*
113    return ajax content
114  */
115  protected function returnAjaxContent()
116  {
117    global $ajax, $template;
118
119    if(isset($_REQUEST['ajaxfct']))
120    {
121      //$this->debug("AJAXFCT:".$_REQUEST['ajaxfct']);
122      $result="<p class='errors'>An error has occured</p>";
123      switch($_REQUEST['ajaxfct'])
124      {
125        case 'updateDatabaseGetStatus':
126          $result=$this->ajax_cstat_updateDatabaseGetStatus();
127          break;
128        case 'updateDatabaseGetList':
129          $result=$this->ajax_cstat_updateDatabaseGetList($_REQUEST['selectMode'], $_REQUEST['numOfItems']);
130          break;
131        case 'updateDatabaseDoAnalyze':
132          $result=$this->ajax_cstat_updateDatabaseDoAnalyze($_REQUEST['imagesList']);
133          break;
134        case 'updateDatabaseConsolidation':
135          $result=$this->ajax_cstat_updateDatabaseConsolidation();
136          break;
137        case 'showStatsGetListColors':
138          $result=$this->ajax_cstat_showStatsGetListColors($_REQUEST['orderType']);
139          break;
140        case 'doPpsBench':
141          $result=$this->ajax_cstat_ppsBench($_REQUEST['quality'], true);
142          break;
143      }
144      GPCAjax::returnResult($result);
145    }
146  }
147
148  /**
149   * check the $_REQUEST values and set default values
150   *
151   */
152  protected function checkRequest()
153  {
154    if(!isset($_REQUEST['f_tabsheet'])) $_REQUEST['f_tabsheet']='stat';
155
156
157    if(!($_REQUEST['f_tabsheet']=='database' or
158         $_REQUEST['f_tabsheet']=='stat' or
159         $_REQUEST['f_tabsheet']=='search' or
160         $_REQUEST['f_tabsheet']=='config')) $_REQUEST['f_tabsheet']='stat';
161
162
163    if(isset($_REQUEST['ajaxfct']))
164    {
165      if($_REQUEST['ajaxfct']=='updateDatabaseGetList')
166      {
167        if(!isset($_REQUEST['selectMode'])) $_REQUEST['selectMode']='caddieAdd';
168        if(!isset($_REQUEST['numOfItems'])) $_REQUEST['numOfItems']=$this->config['analyze_itemPerRequest'];
169
170        if(!($_REQUEST['selectMode']=='notAnalyzed' or
171             $_REQUEST['selectMode']=='all' or
172             $_REQUEST['selectMode']=='caddieAdd' or
173             $_REQUEST['selectMode']=='caddieReplace')) $_REQUEST['selectMode']='caddieAdd';
174
175        if($_REQUEST['numOfItems'] <=0 or $_REQUEST['numOfItems']>100) $_REQUEST['numOfItems']=10;
176      }
177
178
179      if($_REQUEST['ajaxfct']=='updateDatabaseDoAnalyze')
180      {
181        if(!isset($_REQUEST['imagesList'])) $_REQUEST['imagesList']='';
182      }
183
184      if($_REQUEST['ajaxfct']=='showStatsGetListColors')
185      {
186        if(!isset($_REQUEST['orderType'])) $_REQUEST['orderType']='img';
187
188        if(!($_REQUEST['orderType']=='color' or
189             $_REQUEST['orderType']=='img' or
190             $_REQUEST['orderType']=='pixels')) $_REQUEST['orderType']=='img';
191      }
192
193
194      if($_REQUEST['ajaxfct']=='doPpsBench')
195      {
196        if(!isset($_REQUEST['quality'])) $_REQUEST['quality']=8;
197        if($_REQUEST['quality']>50 or $_REQUEST['quality']<1) $_REQUEST['quality']=8;
198      }
199    }
200
201  }
202
203  /**
204   * display the database page
205   */
206  protected function displayDatabasePage()
207  {
208    global $template;
209
210    $template->set_filename('body_page',
211                dirname($this->getFileLocation()).'/admin/cstat_database.tpl');
212
213    pwg_query("INSERT INTO ".$this->tables['images']."
214                SELECT id, 'n', 0, 0, 0, 0, 0, 0, '', ''
215                  FROM ".IMAGES_TABLE."
216                  WHERE id NOT IN (SELECT image_id FROM ".$this->tables['images'].")");
217
218    $datas=Array(
219      'urlRequest' => $this->getAdminLink(),
220      'numberOfItemsPerRequest' => $this->config['analyze_itemPerRequest']
221    );
222    $template->assign('datas', $datas);
223    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
224  } //displayDatabasePage
225
226  /**
227   * display the stat page
228   */
229  protected function displayStatPage()
230  {
231    global $template;
232
233    $template->set_filename('body_page',
234                dirname($this->getFileLocation()).'/admin/cstat_stat.tpl');
235
236    $colorTable=CStat_functions::getColorTableWithStat();
237
238
239    $datas=Array(
240      //'themeconf' => Array('name' => $template->get_themeconf('name')),
241      'colorTable' => CStat_functions::htmlColorTable(
242                        $colorTable,
243                        ($this->config['analyze_colorTable']=='small')?19:10,
244                        "",
245                        "color0px",
246                        "<br>"
247                      ),
248      'urlRequest' => $this->getAdminLink(),
249      'config_GetListColors_OrderType' => $this->config['display_stat_orderType'],
250    );
251    $template->assign('datas', $datas);
252    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
253  } //displayStatPage
254
255
256  /**
257   * display search page
258   */
259  protected function displaySearchPage()
260  {
261    global $template, $lang;
262
263    $template->set_filename('body_page',
264                dirname($this->getFileLocation()).'/admin/cstat_search.tpl');
265
266    $template->assign('cstat_search_page', GPCRequestBuilder::displaySearchPage($this->getPluginName()));
267
268    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
269  } //displaySearchPage
270
271
272  /**
273   * manage display of config page & save config
274   */
275  protected function displayConfigPage()
276  {
277    $tmpPct=$this->config['stat_minPct'];
278
279    if(!$this->adviser_abort())
280    {
281      if(isset($_POST['submit_save_config']))
282      {
283        foreach($this->config as $key => $val)
284        {
285          if(isset($_REQUEST['f_'.$key]))
286          {
287            $this->config[$key] = $_REQUEST['f_'.$key];
288          }
289        }
290        $this->displayResult(l10n('cstat_save_config'), $this->saveConfig());
291      }
292    }
293
294    if($tmpPct!=$this->config['stat_minPct'])
295    {
296      $this->updateDatabaseConsolidation();
297    }
298
299    $this->displayConfig();
300  }
301
302  /**
303   * display config page
304   */
305  protected function displayConfig()
306  {
307    global $template, $lang;
308
309    $configTabs=new GPCTabSheet('configTabsheet', $this->tabsheet->get_titlename(), 'tabsheet2 gcBorder', 'itab2');
310    $configTabs->add('database',
311                      l10n('cstat_database'),
312                      '', true, "displayConfig('database');");
313    $configTabs->add('statsearch',
314                      l10n('cstat_stat_and_search'),
315                      '', false, "displayConfig('statsearch');");
316    $configTabs->add('display',
317                      l10n('cstat_gallery_integration'),
318                      '', false, "displayConfig('display');");
319    $configTabs->assign();
320
321    $template->set_filename('body_page',
322                dirname($this->getFileLocation()).'/admin/cstat_config.tpl');
323
324    $nbPictures=0;
325    $sql="SELECT COUNT(image_id) FROM ".$this->tables['images'];
326    $result=pwg_query($sql);
327    if($result)
328    {
329      while($row=pwg_db_fetch_row($result))
330      {
331        $nbPictures=$row[0];
332      }
333    }
334
335
336    $datas=Array(
337      'nbPictures' => $nbPictures,
338      'pps' => $this->config['analyze_pps'],
339      'quality' => $this->config['analyze_ppsQuality'],
340      'qualityHighest' => 14000,
341      'qualityHigh' => 7500,
342      'qualityNormal' => 3500,
343      'qualityLow' => 1200,
344      'qualityLowest' => 600,
345      'urlRequest' => $this->getAdminLink(),
346      'minPct' => $this->config['stat_minPct'],
347      'showColors' => $this->config['display_gallery_showColors'],
348      'colorSize' => $this->config['display_gallery_colorSize'],
349    );
350
351    $template->assign('datas', $datas);
352
353    $template->assign_var_from_handle('CSTAT_BODY_PAGE', 'body_page');
354  } //displayConfig
355
356
357  /**
358   * manage adviser profile
359   * return true if user is adviser
360   */
361  protected function adviser_abort()
362  {
363    if(is_adviser())
364    {
365      $this->displayResult(l10n("cstat_adviser_not_allowed"), false);
366      return(true);
367    }
368    return(false);
369  }
370
371
372  /* ---------------------------------------------------------------------------
373    function to manage database manipulation
374  --------------------------------------------------------------------------- */
375  protected function analyzeImageFile($fileName, $imageId, $colorTable)
376  {
377    // set the picture to the 'try to analyze' statut
378    $sql="UPDATE ".$this->tables['images']." SET analyzed='t'
379          WHERE image_id=".$imageId.";";
380    pwg_query($sql);
381
382    $colors=ColorStat::getFileColors(
383      $fileName,
384      $colorTable,
385      Array(
386        'mode' => 'numAnalyzed',
387        'numColors' => 24,
388        'numAnalyzed' => $this->config['analyze_ppsQuality'],
389      )
390    );
391
392    if($colors!==false and
393       ColorStat::$fileColorsStat['colors']>0 and
394       ColorStat::$fileColorsStat['analyzed']>0)
395    {
396      $sql="UPDATE ".$this->tables['images']."
397              SET analyzed='y',
398                  num_colors='".ColorStat::$fileColorsStat['colors']."',
399                  num_pixels='".ColorStat::$fileColorsStat['pixels']."',
400                  analyzed_pixels='".ColorStat::$fileColorsStat['analyzed']."',
401                  pps='".ColorStat::$fileColorsStat['pps']."',
402                  time='".ColorStat::$fileColorsStat['time']."',
403                  quality='".ColorStat::$fileColorsStat['quality']."'
404            WHERE image_id=".$imageId.";";
405      pwg_query($sql);
406      $sql="";
407      foreach($colors as $key=>$val)
408      {
409        /*
410         * $key => RGB color #RRGGBB
411         * $val => Array (
412         *            'hsv' => Array ('H', 'S', 'V')
413         *            'num' => integer
414         *            'pct' => float
415         *          )
416         */
417
418        $sql.=(($sql=="")?"":", ")."
419              ('".$imageId."', '".$key."', ".$val['pct'].", ".$val['num'].")";
420      }
421      $sql="REPLACE INTO ".$this->tables['images_colors']."
422                VALUES ".$sql;
423      pwg_query($sql);
424
425      return($imageId.'='.ColorStat::$fileColorsStat['colors'].';');
426    }
427    else
428    {
429      return($imageId.'=KO;');
430    }
431  }
432
433
434  /**
435   * make consolidation for the color_table :
436   *  - count number of images using a color
437   *  - count number of pixels of a color
438   */
439  protected function updateDatabaseConsolidation()
440  {
441    $sql="UPDATE ".$this->tables['color_table']." cct
442          SET cct.num_images=0,
443              cct.num_pixels=0;";
444    pwg_query($sql);
445
446    $sql="UPDATE ".$this->tables['color_table']." cct,
447                 (SELECT color_id,
448                         count(image_id) AS num_images,
449                         sum(num_pixels) AS num_pixels
450                  FROM ".$this->tables['images_colors']."
451                  WHERE pct >= ".$this->config['stat_minPct']."
452                  GROUP BY color_id) cic
453          SET cct.num_images=cic.num_images,
454              cct.num_pixels=cic.num_pixels
455          WHERE cct.color_id=cic.color_id;";
456    pwg_query($sql);
457
458    $sql="UPDATE ".$this->tables['images']." pci
459          SET pci.colors = '',
460              pci.colors_pct = ''";
461    pwg_query($sql);
462
463    $sql="UPDATE ".$this->tables['images']." pci,
464          (SELECT image_id,
465                  GROUP_CONCAT(color_id ORDER BY pct DESC SEPARATOR ',') AS colors,
466                  GROUP_CONCAT(pct ORDER BY pct DESC SEPARATOR ',') AS colors_pct
467           FROM ".$this->tables['images_colors']."
468           WHERE pct >= ".$this->config['stat_minPct']."
469           GROUP BY image_id) pcic
470          SET pci.colors = pcic.colors,
471              pci.colors_pct = pcic.colors_pct
472          WHERE pci.image_id = pcic.image_id;";
473    pwg_query($sql);
474  }
475
476  /* ---------------------------------------------------------------------------
477    ajax functions
478  --------------------------------------------------------------------------- */
479
480  /**
481   * returns a list of formated string, separated with a semi-colon :
482   *  - number of current analyzed pictures
483   *  - number of pictures not analyzed + number of picture in error
484   *
485   * @return String
486   */
487  private function ajax_cstat_updateDatabaseGetStatus()
488  {
489    $numOfPictures=0;
490    $numOfPicturesNotAnalyzed=0;
491    $numOfPicturesInError=0;
492
493    $sql="SELECT COUNT(image_id), analyzed FROM ".$this->tables['images']."
494            GROUP BY analyzed;";
495    $result=pwg_query($sql);
496    if($result)
497    {
498      while($row=pwg_db_fetch_row($result))
499      {
500        switch($row[1])
501        {
502          case 'n': //no
503            $numOfPicturesNotAnalyzed=$row[0];
504            break;
505          case 'y': //yes
506            $numOfPictures=$row[0];
507            break;
508          case 't': //tried to be analyzed, but not finished
509            $numOfPicturesInError=$row[0];
510            break;
511        }
512      }
513    }
514
515    return(sprintf(l10n("cstat_numberOfAnalyzedPictures"), $numOfPictures).";".
516           sprintf(l10n("cstat_numberOfNotAnalyzedPictures"), $numOfPicturesNotAnalyzed).";".
517           sprintf(l10n("cstat_numberOfPicturesInError"), $numOfPicturesInError));
518  }
519
520
521  /**
522   * return a list of picture Id
523   *
524   * picture id are separated with a space " "
525   * picture id are grouped in blocks of 'NumberOfItemsPerRequest' items and
526   * are separated with a semi-colon ";"
527   *
528   * client side just have to split blocks, and transmit it to the server
529   *
530   * There is four mode to determine the pictures being analyzed :
531   *  - "all"           : analyze all the images (add & replace)
532   *  - "notAnalyzed"   : analyze only the images not yet analyzed (add)
533   *  - "caddieAdd"     : analyze all the images of the caddie (add)
534   *  - "caddieReplace" : analyze all the images of the caddie (add & replace)
535   *
536   * @param String $mode
537   * @param Integer $nbOfItems : number of items per request
538   * @return String : list of image id to be analyzed, separated with a space
539   *                      "23 78 4523 5670"
540   */
541  private function ajax_cstat_updateDatabaseGetList($mode, $nbOfItems)
542  {
543    global $user;
544
545    $returned="";
546
547    $sql="SELECT cit.image_id FROM ".$this->tables['images']." cit";
548    if($mode=="notAnalyzed")
549    {
550      $sql.=" WHERE cit.analyzed='n'";
551    }
552    elseif($mode=="caddieAdd" or $mode=="caddieReplace")
553    {
554      $sql.=" LEFT JOIN ".CADDIE_TABLE." ct ON cit.image_id = ct.element_id
555            WHERE ct.user_id = ".$user['id']." ";
556      if($mode=="caddieAdd") $sql.=" AND cit.analyzed='n'";
557    }
558
559    if($mode=="all" or $mode=="caddieReplace")
560    {
561      pwg_query("UPDATE ".$this->tables['images']."
562                  SET analyzed='n',
563                      num_colors=0,
564                      num_pixels=0,
565                      analyzed_pixels=0,
566                      pps=0,
567                      time=0,
568                      quality=0
569                  WHERE analyzed<>'t'");
570      pwg_query("DELETE FROM ".$this->tables['images_colors']);
571      pwg_query("UPDATE ".$this->tables['color_table']."
572                 SET num_pixels=0, num_images=0;");
573    }
574
575    $result=pwg_query($sql);
576    if($result)
577    {
578      $i=0;
579      while($row=pwg_db_fetch_row($result))
580      {
581        $returned.=$row[0];
582        $i++;
583        if($i>=$nbOfItems)
584        {
585          $returned.=";";
586          $i=0;
587        }
588        else
589        {
590          $returned.=" ";
591        }
592      }
593    }
594    return(trim($returned).";");
595  }
596
597
598
599  /**
600   * extract metadata from images
601   *
602   * @param String $imageList : list of image id to be analyzed, separated with
603   *                            a space
604   *                                "23 78 4523 5670"
605   * @return String : list of the analyzed pictures, with number of colors found
606   *                  for each picture
607   *                    "23=0;78=66;4523=33;5670=91;"
608   */
609  private function ajax_cstat_updateDatabaseDoAnalyze($imagesList)
610  {
611    $list=explode(" ", trim($imagesList));
612
613    $returned="";
614
615    if(count($list)>0 and trim($imagesList)!='')
616    {
617      // $path = path of piwigo's on the server filesystem
618      $path=dirname(dirname(dirname(__FILE__)));
619
620      $sql="SELECT id, path FROM ".IMAGES_TABLE." WHERE id IN (".implode(", ", $list).")";
621      $result=pwg_query($sql);
622      if($result)
623      {
624        $colorTable=ColorStat::getColorTable(
625          CStat_root::$colorTableSize[$this->config['analyze_colorTable']][0],
626          CStat_root::$colorTableSize[$this->config['analyze_colorTable']][1]
627        );
628
629        while($row=pwg_db_fetch_assoc($result))
630        {
631          /*
632           * in some case (in a combination of some pictures), when there is too
633           * much pictures to analyze in the same request, a fatal error occurs
634           * with the message : "Allowed memory size of XXXXX bytes exhausted"
635           *
636           *
637           * tracking memory leak is not easy... :-(
638           *
639           */
640          //echo "analyzing:".$row['id']."\n";
641          //$mem1=memory_get_usage();
642          //echo "memory before analyze:".$mem1."\n";
643          $returned.=$this->analyzeImageFile($path."/".$row['path'], $row['id'], $colorTable);
644          //echo $returned."\n";
645          //$mem2=memory_get_usage();
646          //echo "memory after analyze:".$mem2." (".($mem2-$mem1).")\n";
647        }
648      }
649    }
650    return($returned);
651  }
652
653
654  /**
655   * make consolidation for the color_table :
656   *  - count number of images using a color
657   *  - count number of pixels of a color
658   */
659  private function ajax_cstat_updateDatabaseConsolidation()
660  {
661    $this->updateDatabaseConsolidation();
662    return("ok");
663  }
664
665
666
667  /**
668   * return a formatted <table> (using the template "cstat_stat_show_iListColors")
669   * of used tag with, for each tag, the number and the percentage of pictures
670   * where the tag was found
671   *
672   * @param String $orderType : order for the list (by color 'color' or by number
673   *                            of pictures 'img' or by number of pixels 'pixels')
674   * @return String
675   */
676  private function ajax_cstat_showStatsGetListColors($orderType)
677  {
678    global $template;
679
680    $this->config['display_stat_orderType'] = $orderType;
681    $this->saveConfig();
682
683    $local_tpl = new Template(CSTAT_PATH."admin/", "");
684    $local_tpl->set_filename('body_page',
685                  dirname($this->getFileLocation()).'/admin/cstat_stat_show_iListColors.tpl');
686
687    $generalStats=CStat_functions::getGeneralStats();
688
689    $sql="SELECT color_id, num_images, num_pixels
690          FROM ".$this->tables['color_table']."
691          WHERE num_images > 0 ";
692    if($orderType=='color')
693    {
694      $sql.=" ORDER BY hue ASC, saturation ASC, value DESC";
695    }
696    elseif($orderType=='img')
697    {
698      $sql.=" ORDER BY num_images DESC, num_pixels DESC ";
699    }
700    elseif($orderType=='pixels')
701    {
702      $sql.=" ORDER BY num_pixels DESC, num_images DESC ";
703    }
704
705    $datas=Array();
706    $result=pwg_query($sql);
707    if($result)
708    {
709      while($row=pwg_db_fetch_assoc($result))
710      {
711        $row['pct_images']=sprintf('%.2f', round(100*$row['num_images']/$generalStats['nbImages'],2));
712        $row['pct_pixels']=sprintf('%.2f', round(100*$row['num_pixels']/$generalStats['pixelsAnalyzedSum'],2));
713        $datas[]=$row;
714      }
715    }
716
717    $local_tpl->assign('themeconf', Array('name' => $template->get_themeconf('name')));
718    $local_tpl->assign('datas', $datas);
719
720    return($local_tpl->parse('body_page', true));
721  }
722
723} //class
724
725
726?>
Note: See TracBrowser for help on using the repository browser.