source: extensions/GrumPluginClasses/classes/GPCCore.class.inc.php @ 9066

Last change on this file since 9066 was 9066, checked in by grum, 10 years ago

release 3.4.4
fix bug:2170, bug:2178, bug:2179

File size: 22.4 KB
Line 
1<?php
2
3/* -----------------------------------------------------------------------------
4  class name     : GPCCore
5  class version  : 1.3.3
6  plugin version : 3.4.4
7  date           : 2011-02-02
8  ------------------------------------------------------------------------------
9  author: grum at piwigo.org
10  << May the Little SpaceFrog be with you >>
11  ------------------------------------------------------------------------------
12
13  :: HISTORY
14
15| release | date       |
16| 1.0.0   | 2010/03/30 | * Update class & function names
17|         |            |
18| 1.1.0   | 2010/03/30 | * add the BBtoHTML function
19|         |            |
20| 1.2.0   | 2010/07/28 | * add the loadConfigFromFile function
21|         |            |
22| 1.3.0   | 2010/10/13 | * add the addHeaderCSS, addHeaderJS functions
23|         |            |
24| 1.3.1   | 2010/10/20 | * applyHeaderItems functions implemented with an
25|         |            |   higher priority on the 'loc_begin_page_header' event
26|         |            |
27|         |            | * implement the getUserLanguageDesc() function, using
28|         |            |   extended description function if present
29|         |            |
30|         |            | * implement the getPiwigoSystemPath() function
31|         |            |
32|         |            | * implement the rmDir() function
33|         |            |
34| 1.3.2   | 2011/01/28 | * implement the addUI() function
35|         |            |
36|         |            | * implement getMinified() & setMinifiedState() functions
37|         |            |
38| 1.3.3   | 2011/02/01 | * fix bug on loadConfig() function
39|         |            |
40|         |            | * update deleteConfig() function (allow to be used to
41|         |            |   delete the GPCCore config)
42|         |            |
43|         |            | * mantis bug:2167
44|         |            |
45| 1.3.4   | 2011/02/02 | * mantis bug:2170
46|         |            |   . File path for RBuilder registered plugins is corrupted
47|         |            |
48|         |            | * mantis bug:2178
49|         |            |   . RBuilder register function don't work
50|         |            |
51|         |            | * mantis bug:2179
52|         |            |   . JS file loaded in wrong order made incompatibility
53|         |            |     with Lightbox, GMaps & ASE plugins (and probably other)
54|         |            |
55|         |            |
56|         |            |
57|         |            |
58
59  ------------------------------------------------------------------------------
60    no constructor, only static function are provided
61    - static function loadConfig
62    - static function loadConfigFromFile
63    - static function saveConfig
64    - static function deleteConfig
65    - static function getRegistered
66    - static function getModulesInfos
67    - static function register
68    - static function unregister
69    - static function BBtoHTML
70    - static function addHeaderCSS
71    - static function addHeaderJS
72    - static function addUI
73    - static function getMinified
74    - static function setMinifiedState
75    - static function getUserLanguageDesc
76    - static function getPiwigoSystemPath
77    - static function formatOctet
78    - static function rmDir
79   ---------------------------------------------------------------------- */
80
81
82
83class GPCCore
84{
85  static private $piwigoSystemPath;
86  static private $minified='.min';
87
88  static public $pluginName = "GPCCore";
89  static protected $headerItems = array(
90    'css' => array(),
91    'js'  => array()
92  );
93
94  static public function init()
95  {
96    self::$piwigoSystemPath=dirname(dirname(dirname(dirname(__FILE__))));
97  }
98
99  /* ---------------------------------------------------------------------------
100   * grum plugin classes informations functions
101   * -------------------------------------------------------------------------*/
102  static public function getModulesInfos()
103  {
104    return(
105      Array(
106        Array('name' => "CommonPlugin", 'version' => "2.2.0"),
107        Array('name' => "GPCAjax", 'version' => "3.0.0"),
108        Array('name' => "GPCCategorySelector", 'version' => "1.0.1"),
109        Array('name' => "GPCCore", 'version' => "1.3.4"),
110        Array('name' => "GPCCss", 'version' => "3.0.0"),
111        Array('name' => "GPCPagesNavigation", 'version' => "2.0.0"),
112        Array('name' => "GPCPublicIntegration", 'version' => "2.0.0"),
113        Array('name' => "GPCRequestBuilder", 'version' => "1.1.4"),
114        Array('name' => "GPCTables", 'version' => "1.5.0"),
115        Array('name' => "GPCTabSheet", 'version' => "1.1.1"),
116        Array('name' => "GPCTranslate", 'version' => "2.1.1"),
117        Array('name' => "GPCUsersGroups", 'version' => "2.1.0")
118      )
119    );
120  }
121
122
123  /* ---------------------------------------------------------------------------
124   * register oriented functions
125   * -------------------------------------------------------------------------*/
126
127  /**
128   * register a plugin using GPC
129   *
130   * @param String $pluginName : the plugin name
131   * @param String $release : the plugin version like "2.0.0"
132   * @param String $GPCNeed : the minimal version of GPC needed by the plugin to
133   *                          work
134   * @return Boolean : true if registering is Ok, otherwise false
135   */
136  static public function register($plugin, $release, $GPCneeded)
137  {
138    $config=Array();
139    if(!self::loadConfig(self::$pluginName, $config))
140    {
141      $config['registered']=array();
142    }
143
144    $config['registered'][$plugin]=Array(
145      'name' => $plugin,
146      'release' => $release,
147      'needed' => $GPCneeded,
148      'date' => date("Y-m-d"),
149    );
150    return(self::saveConfig(self::$pluginName, $config));
151  }
152
153  /**
154   * unregister a plugin using GPC
155   *
156   * assume that if the plugin was not registerd before, unregistering returns
157   * a true value
158   *
159   * @param String $pluginName : the plugin name
160   * @return Boolean : true if registering is Ok, otherwise false
161   */
162  static public function unregister($plugin)
163  {
164    $config=Array();
165    if(self::loadConfig(self::$pluginName, $config))
166    {
167      if(array_key_exists('registered', $config))
168      {
169        if(array_key_exists($plugin, $config['registered']))
170        {
171          unset($config['registered'][$plugin]);
172          return(self::saveConfig(self::$pluginName, $config));
173        }
174      }
175    }
176    // assume if the plugin was not registered before, unregistering it is OK
177    return(true);
178  }
179
180  /**
181   * @return Array : list of registered plugins
182   */
183  static public function getRegistered()
184  {
185    $config=Array();
186    if(self::loadConfig(self::$pluginName, $config))
187    {
188      if(array_key_exists('registered', $config))
189      {
190        return($config['registered']);
191      }
192    }
193    return(Array());
194  }
195
196
197
198  /* ---------------------------------------------------------------------------
199   * config oriented functions
200   * -------------------------------------------------------------------------*/
201
202  /**
203   *  load config from CONFIG_TABLE into an array
204   *
205   * @param String $pluginName : the plugin name, must contain only alphanumerical
206   *                             character
207   * @param Array $config : array, initialized or not with default values ; the
208   *                        config values are loaded in this value
209   * @return Boolean : true if config is loaded, otherwise false
210   */
211  static public function loadConfig($pluginName, &$config=Array())
212  {
213    global $conf;
214
215    if(!isset($conf[$pluginName.'_config']))
216    {
217      return(false);
218    }
219
220    $configValues = unserialize($conf[$pluginName.'_config']);
221    reset($configValues);
222    while (list($key, $val) = each($configValues))
223    {
224      if(is_array($val))
225      {
226        foreach($val as $key2 => $val2)
227        {
228          $config[$key][$key2]=$val2;
229        }
230      }
231      else
232      {
233        $config[$key] =$val;
234      }
235    }
236
237    $conf[$pluginName.'_config']=serialize($config);
238
239    return(true);
240  }
241
242  /**
243   *  load config from a file into an array
244   *
245   *  note : the config file is a PHP file one var $conf used as an array,
246   *  like the piwigo $conf var
247   *
248   * @param String $fileName : the file name
249   * @param Array $config : array, initialized or not with default values ; the
250   *                        config values are loaded in this value
251   * @return Boolean : true if config is loaded, otherwise false
252   */
253  static public function loadConfigFromFile($fileName, &$config=Array())
254  {
255    $conf=array();
256
257    if(!is_array($config) or !file_exists($fileName))
258    {
259      return(false);
260    }
261
262    include_once($fileName);
263
264    foreach($conf as $key=>$val)
265    {
266      $config[$key]=$val;
267    }
268    return(true);
269  }
270
271
272  /**
273   * save var $my_config into CONFIG_TABLE
274   *
275   * @param String $pluginName : the plugin name, must contain only alphanumerical
276   *                             character
277   * @param Array $config : array of configuration values
278   * @return Boolean : true if config is saved, otherwise false
279   */
280  static public function saveConfig($pluginName, $config)
281  {
282    global $conf;
283
284    $sql="REPLACE INTO ".CONFIG_TABLE."
285           VALUES('".$pluginName."_config', '"
286           .pwg_db_real_escape_string(serialize($config))."', '')";
287    $result=pwg_query($sql);
288    if($result)
289    {
290      $conf[$pluginName.'_config']=serialize($config);
291      return true;
292    }
293    else
294    {
295      return false;
296    }
297  }
298
299  /**
300   * delete config from CONFIG_TABLE
301   *
302   * @param String $pluginName : the plugin name, must contain only alphanumerical
303   *                             character ; if empty, assume GPCCore config
304   * @return Boolean : true if config is deleted, otherwise false
305   */
306  static public function deleteConfig($pluginName='')
307  {
308    if($pluginName=='') $pluginName=self::$pluginName;
309    $sql="DELETE FROM ".CONFIG_TABLE."
310          WHERE param='".$pluginName."_config'";
311    $result=pwg_query($sql);
312    if($result)
313    { return true; }
314    else
315    { return false; }
316  }
317
318
319  /**
320   * convert (light) BB tag to HTML tag
321   *
322   * all BB codes are not recognized, only :
323   *  - [ul] [/ul]
324   *  - [li] [/li]
325   *  - [b] [/b]
326   *  - [i] [/i]
327   *  - [url] [/url]
328   *  - carriage return is replaced by a <br>
329   *
330   * @param String $text : text to convert
331   * @return String : BB to HTML text
332   */
333  static public function BBtoHTML($text)
334  {
335    $patterns = Array(
336      '/\[li\](.*?)\[\/li\]\n*/im',
337      '/\[b\](.*?)\[\/b\]/ism',
338      '/\[i\](.*?)\[\/i\]/ism',
339      '/\[p\](.*?)\[\/p\]/ism',
340      '/\[url\]([\w]+?:\/\/[^ \"\n\r\t<]*?)\[\/url\]/ism',
341      '/\[url=([\w]+?:\/\/[^ \"\n\r\t<]*?)\](.*?)\[\/url\]/ism',
342      '/\n{0,1}\[ul\]\n{0,1}/im',
343      '/\n{0,1}\[\/ul\]\n{0,1}/im',
344      '/\n{0,1}\[ol\]\n{0,1}/im',
345      '/\n{0,1}\[\/ol\]\n{0,1}/im',
346      '/\n/im',
347    );
348    $replacements = Array(
349      '<li>\1</li>',
350      '<b>\1</b>',
351      '<i>\1</i>',
352      '<p>\1</p>',
353      '<a href="\1">\1</a>',
354      '<a href="\1">\2</a>',
355      '<ul>',
356      '</ul>',
357      '<ol>',
358      '</ol>',
359      '<br>',
360    );
361
362    return(preg_replace($patterns, $replacements, $text));
363  }
364
365  /**
366   * used to add a css file in the header
367   *
368   * @param String $id : a unique id for the file
369   * @param String $file : the css file
370   */
371  static public function addHeaderCSS($id, $file)
372  {
373    if(!array_key_exists($file, self::$headerItems['css']))
374    {
375      self::$headerItems['css'][$id]=$file;
376    }
377  }
378  static public function addHeaderJS($id, $file)
379  {
380    global $template;
381
382    if(!isset($template->known_scripts)) $template->known_scripts=array();
383
384    if(!array_key_exists($id,  $template->known_scripts) and !array_key_exists($file, self::$headerItems['js']))
385    {
386      $dummy=null;
387
388      $template->func_known_script(
389        array('id'=>$id,
390              'src'=>$file),
391        $dummy);
392     //$template->known_scripts[$id]=$file;
393     self::$headerItems['js'][$id]=$file;
394    }
395  }
396
397  /**
398   * declared as public to be accessible by the event manager, but this funcion
399   * is not aimed to be used directly
400   */
401  static public function applyHeaderItems()
402  {
403    global $template;
404    $dummy1=null;
405    $dummy2=null;
406
407    foreach(self::$headerItems['css'] as $file)
408    {
409      $template->append('head_elements', '<link rel="stylesheet" type="text/css" href="'.$file.'"/>');
410    }
411/*
412    foreach(self::$headerItems['js'] as $file)
413    {
414      //$template->append('head_elements', '<script type="text/javascript" src="'.$file.'"></script>');
415      $template->block_html_head(null, '<script type="text/javascript" src="'.$file.'"></script>', $dummy1, $dummy2);
416    }
417*/
418  }
419
420  /**
421   * add a ui component ; css & js dependencies are managed
422   *
423   * @param Array $list : possibles values are
424   *                        - inputCheckbox
425   *                        - inputColorPicker
426   *                        - inputColorsFB
427   *                        - inputConsole
428   *                        - inputDotArea
429   *                        - inputList
430   *                        - inputNum
431   *                        - inputPosition
432   *                        - inputRadio
433   *                        - inputStatusBar
434   *                        - inputText
435   *                        - categorySelector
436   */
437  static public function addUI($list)
438  {
439    global $template;
440
441    if(is_string($list)) $list=explode(',', $list);
442    if(!is_array($list)) return(false);
443
444    if(defined('IN_ADMIN'))
445    {
446      $themeFile=GPC_PATH.'css/%s_'.$template->get_themeconf('name').'.css';
447    }
448    else
449    {
450      $themeFile='themes/'.$template->get_themeconf('name').'/css/GPC%s.css';
451    }
452
453    foreach($list as $ui)
454    {
455      switch($ui)
456      {
457        case 'googleTranslate':
458          self::addHeaderJS('google.jsapi', 'http://www.google.com/jsapi');
459          self::addHeaderJS('gpc.googleTranslate', 'plugins/GrumPluginClasses/js/google_translate'.self::$minified.'.js');
460        case 'categorySelector':
461          self::addHeaderCSS('gpc.categorySelector', GPC_PATH.'css/categorySelector.css');
462          self::addHeaderCSS('gpc.categorySelectorT', sprintf($themeFile, 'categorySelector'));
463          self::addHeaderJS('gpc.categorySelector', GPC_PATH.'js/ui.categorySelector'.self::$minified.'.js');
464          break;
465        case 'inputCheckbox':
466          self::addHeaderCSS('gpc.inputCheckbox', GPC_PATH.'css/inputCheckbox.css');
467          self::addHeaderJS('gpc.inputCheckbox', GPC_PATH.'js/ui.inputCheckbox'.self::$minified.'.js');
468          break;
469        case 'inputColorPicker':
470          self::addHeaderCSS('gpc.inputText', GPC_PATH.'css/inputText.css');
471          self::addHeaderCSS('gpc.inputNum', GPC_PATH.'css/inputNum.css');
472          self::addHeaderCSS('gpc.inputColorsFB', GPC_PATH.'css/inputColorsFB.css');
473          self::addHeaderCSS('gpc.inputDotArea', GPC_PATH.'css/inputDotArea.css');
474          self::addHeaderCSS('gpc.inputColorPicker', GPC_PATH.'css/inputColorPicker.css');
475          self::addHeaderCSS('gpc.inputTextT', sprintf($themeFile, 'inputText'));
476          self::addHeaderCSS('gpc.inputNumT', sprintf($themeFile, 'inputNum'));
477          self::addHeaderCSS('gpc.inputColorsFBT', sprintf($themeFile, 'inputColorsFB'));
478          self::addHeaderCSS('gpc.inputDotAreaT', sprintf($themeFile, 'inputDotArea'));
479          self::addHeaderCSS('gpc.inputColorPickerT', sprintf($themeFile, 'inputColorPicker'));
480          self::addHeaderJS('jquery.ui', 'themes/default/js/ui/packed/ui.core.packed.js');
481          self::addHeaderJS('jquery.ui.slider', 'themes/default/js/ui/packed/ui.slider.packed.js');
482          self::addHeaderJS('jquery.ui.draggable', 'themes/default/js/ui/packed/ui.draggable.packed.js');
483          self::addHeaderJS('jquery.ui.dialog', 'themes/default/js/ui/packed/ui.dialog.packed.js');
484          self::addHeaderJS('gpc.inputText', GPC_PATH.'js/ui.inputText'.self::$minified.'.js');
485          self::addHeaderJS('gpc.inputNum', GPC_PATH.'js/ui.inputNum'.self::$minified.'.js');
486          self::addHeaderJS('gpc.inputColorsFB', GPC_PATH.'js/ui.inputColorsFB'.self::$minified.'.js');
487          self::addHeaderJS('gpc.inputDotArea', GPC_PATH.'js/ui.inputDotArea'.self::$minified.'.js');
488          self::addHeaderJS('gpc.inputColorPicker', GPC_PATH.'js/ui.inputColorPicker'.self::$minified.'.js');
489          break;
490        case 'inputColorsFB':
491          self::addHeaderCSS('gpc.inputColorsFB', GPC_PATH.'css/inputColorsFB.css');
492          self::addHeaderCSS('gpc.inputColorsFBT', sprintf($themeFile, 'inputColorsFB'));
493          self::addHeaderJS('gpc.inputColorsFB', GPC_PATH.'js/ui.inputColorsFB'.self::$minified.'.js');
494          break;
495        case 'inputConsole':
496          self::addHeaderCSS('gpc.inputConsole', GPC_PATH.'css/inputConsole.css');
497          self::addHeaderCSS('gpc.inputConsoleT', sprintf($themeFile, 'inputConsole'));
498          self::addHeaderJS('gpc.inputConsole', GPC_PATH.'js/ui.inputConsole'.self::$minified.'.js');
499          break;
500        case 'inputDotArea':
501          self::addHeaderCSS('gpc.inputDotArea', GPC_PATH.'css/inputDotArea.css');
502          self::addHeaderCSS('gpc.inputDotAreaT', sprintf($themeFile, 'inputDotArea'));
503          self::addHeaderJS('gpc.inputDotArea', GPC_PATH.'js/ui.inputDotArea'.self::$minified.'.js');
504          break;
505        case 'inputList':
506          self::addHeaderCSS('gpc.inputList', GPC_PATH.'css/inputList.css');
507          self::addHeaderCSS('gpc.inputListT', sprintf($themeFile, 'inputList'));
508          self::addHeaderJS('gpc.inputList', GPC_PATH.'js/ui.inputList'.self::$minified.'.js');
509          break;
510        case 'inputNum':
511          self::addHeaderCSS('gpc.inputNum', GPC_PATH.'css/inputNum.css');
512          self::addHeaderCSS('gpc.inputNumT', sprintf($themeFile, 'inputNum'));
513          self::addHeaderJS('jquery.ui', 'themes/default/js/ui/packed/ui.core.packed.js');
514          self::addHeaderJS('jquery.ui.slider', 'themes/default/js/ui/packed/ui.slider.packed.js');
515          self::addHeaderJS('gpc.inputNum', GPC_PATH.'js/ui.inputNum'.self::$minified.'.js');
516          break;
517        case 'inputPosition':
518          self::addHeaderCSS('gpc.inputPosition', GPC_PATH.'css/inputPosition.css');
519          self::addHeaderCSS('gpc.inputPositionT', sprintf($themeFile, 'inputPosition'));
520          self::addHeaderJS('gpc.inputPosition', GPC_PATH.'js/ui.inputPosition'.self::$minified.'.js');
521          break;
522        case 'inputRadio':
523          self::addHeaderJS('gpc.inputRadio', GPC_PATH.'js/ui.inputRadio'.self::$minified.'.js');
524          break;
525        case 'inputStatusBar':
526          self::addHeaderCSS('gpc.inputStatusBar', GPC_PATH.'css/inputStatusBar.css');
527          self::addHeaderCSS('gpc.inputStatusBarT', sprintf($themeFile, 'inputStatusBar'));
528          self::addHeaderJS('gpc.inputStatusBar', GPC_PATH.'js/ui.inputStatusBar'.self::$minified.'.js');
529          break;
530        case 'inputText':
531          self::addHeaderCSS('gpc.inputText', GPC_PATH.'css/inputText.css');
532          self::addHeaderCSS('gpc.inputTextT', sprintf($themeFile, 'inputText'));
533          self::addHeaderJS('gpc.inputText', GPC_PATH.'js/ui.inputText'.self::$minified.'.js');
534          break;
535      }
536    }
537  }
538
539  /**
540   * return the minified value
541   *
542   * @return String
543   */
544  static public function getMinified()
545  {
546    return(self::$minified);
547  }
548
549  /**
550   * set the minified state
551   *
552   * @param Bool $state
553   * @return Bool
554   */
555  static public function setMinifiedState($state)
556  {
557    if($state)
558    {
559      self::$minified='.min';
560    }
561    else
562    {
563      self::$minified='';
564    }
565    return(self::$minified!='');
566  }
567
568
569  /**
570   * use the extended description get_user_language_desc() function if exist
571   * otherwise returns the value
572   *
573   * @param String $value : value to translate
574   * @return String : translated value
575   */
576  static public function getUserLanguageDesc($value)
577  {
578    if(function_exists('get_user_language_desc'))
579    {
580      return(get_user_language_desc($value));
581    }
582    else
583    {
584      return($value);
585    }
586  }
587
588
589  /**
590   * remove a path recursively
591   *
592   * @param String $directory : directory to remove
593   * @param Bool $removePath : if set to true, remove the path himself, if set
594   *                           to false, remove only file & sub-directories
595   * @return Bool : true if directory was succesfully removed, otherwise false
596   */
597  static public function rmDir($directory, $removePath=true)
598  {
599    $directory=rtrim($directory, '\/').'/';
600    $returned=true;
601    if(file_exists($directory) and is_dir($directory) and $directory!='./' and $directory!='../')
602    {
603      $dhandle=scandir($directory);
604      foreach($dhandle as $file)
605      {
606        if($file!='.' and $file!='..' )
607        {
608          if(is_dir($directory.$file))
609          {
610            $returned=self::rmDir($directory.$file, true) & $returned;
611          }
612          else
613          {
614            $returned=unlink($directory.$file) & $returned;
615          }
616        }
617      }
618      if($returned and $removePath) $returned=rmdir($directory);
619    }
620    return($returned);
621  }
622
623
624  /**
625   * returns the piwigo system path
626   * @return String
627   */
628  static public function getPiwigoSystemPath()
629  {
630    return(self::$piwigoSystemPath);
631  }
632
633
634 /**
635  * formats a file size into a human readable size
636  *
637  * @param String $format : "A"  : auto
638  *                         "Ai" : auto (io)
639  *                         "O"  : o
640  *                         "K"  : Ko
641  *                         "M"  : Mo
642  *                         "G"  : Go
643  *                         "Ki" : Kio
644  *                         "Mi" : Mio
645  *                         "Gi" : Gio
646  * @param String $thsep : thousand separator
647  * @param Integer $prec : number of decimals
648  * @param Bool $visible : display or not the unit
649  * @return String : a formatted file size
650  */
651 static public function formatOctet($octets, $format="Ai", $thsep="", $prec=2, $visible=true)
652 {
653  if($format=="Ai")
654  {
655   if($octets<1024)
656   { $format="O"; }
657   elseif($octets<1024000)
658   { $format="Ki"; }
659   elseif($octets<1024000000)
660   { $format="Mi"; }
661   else
662   { $format="Gi"; }
663  }
664  elseif($format=="A")
665  {
666   if($octets<1000)
667   { $format="O"; }
668   elseif($octets<1000000)
669   { $format="Ki"; }
670   elseif($octets<1000000000)
671   { $format="Mi"; }
672   else
673   { $format="Gi"; }
674  }
675
676  switch($format)
677  {
678   case "O":
679    $unit="o"; $div=1;
680    break;
681   case "K":
682    $unit="Ko"; $div=1000;
683    break;
684   case "M":
685    $unit="Mo"; $div=1000000;
686    break;
687   case "G":
688    $unit="Go"; $div=1000000000;
689    break;
690   case "Ki":
691    $unit="Kio"; $div=1024;
692    break;
693   case "Mi":
694    $unit="Mio"; $div=1024000;
695    break;
696   case "Gi":
697    $unit="Gio"; $div=1024000000;
698    break;
699  }
700
701  $returned=number_format($octets/$div, $prec, '.', $thsep);
702  if($visible) $returned.=' '.$unit;
703  return($returned);
704 } //function formatOctet
705
706
707} //class
708
709add_event_handler('loc_begin_page_header', array('GPCCore', 'applyHeaderItems'), 10);
710
711GPCCore::init();
712
713?>
Note: See TracBrowser for help on using the repository browser.