source: trunk/admin/include/themes.class.php @ 13082

Last change on this file since 13082 was 12922, checked in by mistic100, 12 years ago

update Piwigo headers to 2012, last change before the expected (or not) apocalypse

File size: 20.2 KB
Line 
1<?php
2// +-----------------------------------------------------------------------+
3// | Piwigo - a PHP based photo gallery                                    |
4// +-----------------------------------------------------------------------+
5// | Copyright(C) 2008-2012 Piwigo Team                  http://piwigo.org |
6// | Copyright(C) 2003-2008 PhpWebGallery Team    http://phpwebgallery.net |
7// | Copyright(C) 2002-2003 Pierrick LE GALL   http://le-gall.net/pierrick |
8// +-----------------------------------------------------------------------+
9// | This program is free software; you can redistribute it and/or modify  |
10// | it under the terms of the GNU General Public License as published by  |
11// | the Free Software Foundation                                          |
12// |                                                                       |
13// | This program is distributed in the hope that it will be useful, but   |
14// | WITHOUT ANY WARRANTY; without even the implied warranty of            |
15// | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      |
16// | General Public License for more details.                              |
17// |                                                                       |
18// | You should have received a copy of the GNU General Public License     |
19// | along with this program; if not, write to the Free Software           |
20// | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
21// | USA.                                                                  |
22// +-----------------------------------------------------------------------+
23
24class themes
25{
26  var $fs_themes = array();
27  var $db_themes_by_id = array();
28  var $server_themes = array();
29
30  /**
31   * Initialize $fs_themes and $db_themes_by_id
32  */
33  function themes()
34  {
35    $this->get_fs_themes();
36
37    foreach ($this->get_db_themes() as $db_theme)
38    {
39      $this->db_themes_by_id[$db_theme['id']] = $db_theme;
40    }
41  }
42
43  /**
44   * Perform requested actions
45   * @param string - action
46   * @param string - theme id
47   * @param array - errors
48   */
49  function perform_action($action, $theme_id)
50  {
51    if (isset($this->db_themes_by_id[$theme_id]))
52    {
53      $crt_db_theme = $this->db_themes_by_id[$theme_id];
54    }
55
56    $file_to_include = PHPWG_THEMES_PATH.'/'.$theme_id.'/admin/maintain.inc.php';
57
58    $errors = array();
59
60    switch ($action)
61    {
62      case 'activate':
63        if (isset($crt_db_theme))
64        {
65          // the theme is already active
66          break;
67        }
68
69        if ('default' == $theme_id)
70        {
71          // you can't activate the "default" theme
72          break;
73        }
74
75        $missing_parent = $this->missing_parent_theme($theme_id);
76        if (isset($missing_parent))
77        {
78          array_push(
79            $errors, 
80            sprintf(
81              l10n('Impossible to activate this theme, the parent theme is missing: %s'),
82              $missing_parent
83              )
84            );
85         
86          break;
87        }
88
89        if (file_exists($file_to_include))
90        {
91          include($file_to_include);
92          if (function_exists('theme_activate'))
93          {
94            theme_activate($theme_id, $this->fs_themes[$theme_id]['version'], $errors);
95          }
96        }
97
98        if (empty($errors))
99        {
100          $query = '
101INSERT INTO '.THEMES_TABLE.'
102  (id, version, name)
103  VALUES(\''.$theme_id.'\',
104         \''.$this->fs_themes[$theme_id]['version'].'\',
105         \''.$this->fs_themes[$theme_id]['name'].'\')
106;';
107          pwg_query($query);
108        }
109        break;
110
111      case 'deactivate':
112        if (!isset($crt_db_theme))
113        {
114          // the theme is already inactive
115          break;
116        }
117
118        // you can't deactivate the last theme
119        if (count($this->db_themes_by_id) <= 1)
120        {
121          array_push(
122            $errors,
123            l10n('Impossible to deactivate this theme, you need at least one theme.')
124            );
125          break;
126        }
127
128        if ($theme_id == get_default_theme())
129        {
130          // find a random theme to replace
131          $new_theme = null;
132
133          $query = '
134SELECT
135    id
136  FROM '.THEMES_TABLE.'
137  WHERE id != \''.$theme_id.'\'
138;';
139          $result = pwg_query($query);
140          if (pwg_db_num_rows($result) == 0)
141          {
142            $new_theme = 'default';
143          }
144          else
145          {
146            list($new_theme) = pwg_db_fetch_row($result);
147          }
148
149          $this->set_default_theme($new_theme);
150        }
151
152        if (file_exists($file_to_include))
153        {
154          include($file_to_include);
155          if (function_exists('theme_deactivate'))
156          {
157            theme_deactivate($theme_id);
158          }
159        }
160
161        $query = '
162DELETE
163  FROM '.THEMES_TABLE.'
164  WHERE id= \''.$theme_id.'\'
165;';
166        pwg_query($query);
167        break;
168
169      case 'delete':
170        if (!empty($crt_db_theme))
171        {
172          array_push($errors, 'CANNOT DELETE - THEME IS INSTALLED');
173          break;
174        }
175        if (!isset($this->fs_themes[$theme_id]))
176        {
177          // nothing to do here
178          break;
179        }
180
181        $children = $this->get_children_themes($theme_id);
182        if (count($children) > 0)
183        {
184          array_push(
185            $errors,
186            sprintf(
187              l10n('Impossible to delete this theme. Other themes depends on it: %s'),
188              implode(', ', $children)
189              )
190            );
191          break;
192        }
193       
194        if (!$this->deltree(PHPWG_THEMES_PATH.$theme_id))
195        {
196          $this->send_to_trash(PHPWG_THEMES_PATH.$theme_id);
197        }
198        break;
199
200      case 'set_default':
201        // first we need to know which users are using the current default theme
202        $this->set_default_theme($theme_id);       
203        break;
204    }
205    return $errors;
206  }
207
208  function missing_parent_theme($theme_id)
209  {
210    if (!isset($this->fs_themes[$theme_id]['parent']))
211    {
212      return null;
213    }
214   
215    $parent = $this->fs_themes[$theme_id]['parent'];
216     
217    if ('default' == $parent)
218    {
219      return null;
220    }
221     
222    if (!isset($this->fs_themes[$parent]))
223    {
224      return $parent;
225    }
226
227    return $this->missing_parent_theme($parent);
228  }
229
230  function get_children_themes($theme_id)
231  {
232    $children = array();
233   
234    foreach ($this->fs_themes as $test_child)
235    {
236      if (isset($test_child['parent']) and $test_child['parent'] == $theme_id)
237      {
238        array_push($children, $test_child['name']);
239      }
240    }
241
242    return $children;
243  } 
244
245  function set_default_theme($theme_id)
246  {
247    global $conf;
248   
249    // first we need to know which users are using the current default theme
250    $default_theme = get_default_theme();
251   
252    $query = '
253SELECT
254    user_id
255  FROM '.USER_INFOS_TABLE.'
256  WHERE theme = \''.$default_theme.'\'
257;';
258    $user_ids = array_unique(
259      array_merge(
260        array_from_query($query, 'user_id'),
261        array($conf['guest_id'], $conf['default_user_id'])
262        )
263      );
264
265    // $user_ids can't be empty, at least the default user has the default
266    // theme
267
268    $query = '
269UPDATE '.USER_INFOS_TABLE.'
270  SET theme = \''.$theme_id.'\'
271  WHERE user_id IN ('.implode(',', $user_ids).')
272;';
273    pwg_query($query);
274  }
275
276  function get_db_themes($id='')
277  {
278    $query = '
279SELECT
280    *
281  FROM '.THEMES_TABLE;
282   
283    $clauses = array();
284    if (!empty($id))
285    {
286      $clauses[] = 'id = \''.$id.'\'';
287    }
288    if (count($clauses) > 0)
289    {
290      $query .= '
291  WHERE '. implode(' AND ', $clauses);
292    }
293
294    $result = pwg_query($query);
295    $themes = array();
296    while ($row = pwg_db_fetch_assoc($result))
297    {
298      array_push($themes, $row);
299    }
300    return $themes;
301  }
302
303 
304  /**
305  *  Get themes defined in the theme directory
306  */ 
307  function get_fs_themes()
308  {
309    $dir = opendir(PHPWG_THEMES_PATH);
310   
311    while ($file = readdir($dir))
312    {
313      if ($file!='.' and $file!='..')
314      {
315        $path = PHPWG_THEMES_PATH.$file;
316        if (is_dir($path)
317            and preg_match('/^[a-zA-Z0-9-_]+$/', $file)
318            and file_exists($path.'/themeconf.inc.php')
319            )
320        {
321          $theme = array(
322            'id' => $file,
323            'name' => $file,
324            'version' => '0',
325            'uri' => '',
326            'description' => '',
327            'author' => '',
328            );
329          $theme_data = implode( '', file($path.'/themeconf.inc.php') );
330
331          if ( preg_match("|Theme Name: (.*)|", $theme_data, $val) )
332          {
333            $theme['name'] = trim( $val[1] );
334          }
335          if (preg_match("|Version: (.*)|", $theme_data, $val))
336          {
337            $theme['version'] = trim($val[1]);
338          }
339          if ( preg_match("|Theme URI: (.*)|", $theme_data, $val) )
340          {
341            $theme['uri'] = trim($val[1]);
342          }
343          if ($desc = load_language('description.txt', $path.'/', array('return' => true)))
344          {
345            $theme['description'] = trim($desc);
346          }
347          elseif ( preg_match("|Description: (.*)|", $theme_data, $val) )
348          {
349            $theme['description'] = trim($val[1]);
350          }
351          if ( preg_match("|Author: (.*)|", $theme_data, $val) )
352          {
353            $theme['author'] = trim($val[1]);
354          }
355          if ( preg_match("|Author URI: (.*)|", $theme_data, $val) )
356          {
357            $theme['author uri'] = trim($val[1]);
358          }
359          if (!empty($theme['uri']) and strpos($theme['uri'] , 'extension_view.php?eid='))
360          {
361            list( , $extension) = explode('extension_view.php?eid=', $theme['uri']);
362            if (is_numeric($extension)) $theme['extension'] = $extension;
363          }
364          if (preg_match('/["\']parent["\'][^"\']+["\']([^"\']+)["\']/', $theme_data, $val))
365          {
366            $theme['parent'] = $val[1];
367          }
368          if (preg_match('/["\']activable["\'].*?(true|false)/', $theme_data, $val))
369          {
370            $theme['activable'] = get_boolean($val[1]);
371          }
372
373          // screenshot
374          $screenshot_path = $path.'/screenshot.png';
375          if (file_exists($screenshot_path))
376          {
377            $theme['screenshot'] = $screenshot_path;
378          }
379          else
380          {
381            global $conf;
382            $theme['screenshot'] =
383              PHPWG_ROOT_PATH.'admin/themes/'
384              .$conf['admin_theme']
385              .'/images/missing_screenshot.png'
386              ;
387          }
388
389          $admin_file = $path.'/admin/admin.inc.php';
390          if (file_exists($admin_file))
391          {
392            $theme['admin_uri'] = get_root_url().'admin.php?page=theme&theme='.$file;
393          }
394
395          // IMPORTANT SECURITY !
396          $theme = array_map('htmlspecialchars', $theme);
397          $this->fs_themes[$file] = $theme;
398        }
399      }
400    }
401    closedir($dir);
402  }
403
404  /**
405   * Sort fs_themes
406   */
407  function sort_fs_themes($order='name')
408  {
409    switch ($order)
410    {
411      case 'name':
412        uasort($this->fs_themes, 'name_compare');
413        break;
414      case 'status':
415        $this->sort_themes_by_state();
416        break;
417      case 'author':
418        uasort($this->fs_themes, array($this, 'theme_author_compare'));
419        break;
420      case 'id':
421        uksort($this->fs_themes, 'strcasecmp');
422        break;
423    }
424  }
425
426  /**
427   * Retrieve PEM server datas to $server_themes
428   */
429  function get_server_themes($new=false)
430  {
431    global $user;
432
433    $get_data = array(
434      'category_id' => 10,
435      'format' => 'php',
436    );
437
438    // Retrieve PEM versions
439    $version = PHPWG_VERSION;
440    $versions_to_check = array();
441    $url = PEM_URL . '/api/get_version_list.php';
442    if (fetchRemote($url, $result, $get_data) and $pem_versions = @unserialize($result))
443    {
444      if (!preg_match('/^\d+\.\d+\.\d+$/', $version))
445      {
446        $version = $pem_versions[0]['name'];
447      }
448      $branch = substr($version, 0, strrpos($version, '.'));
449      foreach ($pem_versions as $pem_version)
450      {
451        if (strpos($pem_version['name'], $branch) === 0)
452        {
453          $versions_to_check[] = $pem_version['id'];
454        }
455      }
456    }
457    if (empty($versions_to_check))
458    {
459      return false;
460    }
461
462    // Themes to check
463    $themes_to_check = array();
464    foreach($this->fs_themes as $fs_theme)
465    {
466      if (isset($fs_theme['extension']))
467      {
468        $themes_to_check[] = $fs_theme['extension'];
469      }
470    }
471
472    // Retrieve PEM themes infos
473    $url = PEM_URL . '/api/get_revision_list.php';
474    $get_data = array_merge($get_data, array(
475      'last_revision_only' => 'true',
476      'version' => implode(',', $versions_to_check),
477      'lang' => substr($user['language'], 0, 2),
478      'get_nb_downloads' => 'true',
479      )
480    );
481
482    if (!empty($themes_to_check))
483    {
484      if ($new)
485      {
486        $get_data['extension_exclude'] = implode(',', $themes_to_check);
487      }
488      else
489      {
490        $get_data['extension_include'] = implode(',', $themes_to_check);
491      }
492    }
493    if (fetchRemote($url, $result, $get_data))
494    {
495      $pem_themes = @unserialize($result);
496      if (!is_array($pem_themes))
497      {
498        return false;
499      }
500      foreach ($pem_themes as $theme)
501      {
502        $this->server_themes[$theme['extension_id']] = $theme;
503      }
504      return true;
505    }
506    return false;
507  }
508 
509  /**
510   * Sort $server_themes
511   */
512  function sort_server_themes($order='date')
513  {
514    switch ($order)
515    {
516      case 'date':
517        krsort($this->server_themes);
518        break;
519      case 'revision':
520        usort($this->server_themes, array($this, 'extension_revision_compare'));
521        break;
522      case 'name':
523        uasort($this->server_themes, array($this, 'extension_name_compare'));
524        break;
525      case 'author':
526        uasort($this->server_themes, array($this, 'extension_author_compare'));
527        break;
528      case 'downloads':
529        usort($this->server_themes, array($this, 'extension_downloads_compare'));
530        break;
531    }
532  }
533
534  /**
535   * Extract theme files from archive
536   *
537   * @param string - install or upgrade
538   * @param string - remote revision identifier (numeric)
539   * @param string - theme id or extension id
540   */
541  function extract_theme_files($action, $revision, $dest)
542  {
543    if ($archive = tempnam( PHPWG_THEMES_PATH, 'zip'))
544    {
545      $url = PEM_URL . '/download.php';
546      $get_data = array(
547        'rid' => $revision,
548        'origin' => 'piwigo_'.$action,
549      );
550
551      if ($handle = @fopen($archive, 'wb') and fetchRemote($url, $handle, $get_data))
552      {
553        fclose($handle);
554        include(PHPWG_ROOT_PATH.'admin/include/pclzip.lib.php');
555        $zip = new PclZip($archive);
556        if ($list = $zip->listContent())
557        {
558          foreach ($list as $file)
559          {
560            // we search main.inc.php in archive
561            if (basename($file['filename']) == 'themeconf.inc.php'
562              and (!isset($main_filepath)
563              or strlen($file['filename']) < strlen($main_filepath)))
564            {
565              $main_filepath = $file['filename'];
566            }
567          }
568          if (isset($main_filepath))
569          {
570            $root = dirname($main_filepath); // main.inc.php path in archive
571            if ($action == 'upgrade')
572            {
573              $extract_path = PHPWG_THEMES_PATH . $dest;
574            }
575            else
576            {
577              $extract_path = PHPWG_THEMES_PATH . ($root == '.' ? 'extension_' . $dest : basename($root));
578            }
579            if (
580              $result = $zip->extract(
581                PCLZIP_OPT_PATH, $extract_path,
582                PCLZIP_OPT_REMOVE_PATH, $root,
583                PCLZIP_OPT_REPLACE_NEWER
584                )
585              )
586            {
587              foreach ($result as $file)
588              {
589                if ($file['stored_filename'] == $main_filepath)
590                {
591                  $status = $file['status'];
592                  break;
593                }
594              }
595              if (file_exists($extract_path.'/obsolete.list')
596                and $old_files = file($extract_path.'/obsolete.list', FILE_IGNORE_NEW_LINES)
597                and !empty($old_files))
598              {
599                array_push($old_files, 'obsolete.list');
600                foreach($old_files as $old_file)
601                {
602                  $path = $extract_path.'/'.$old_file;
603                  if (is_file($path))
604                  {
605                    @unlink($path);
606                  }
607                  elseif (is_dir($path))
608                  {
609                    if (!$this->deltree($path))
610                    {
611                      $this->send_to_trash($path);
612                    }
613                  }
614                }
615              }
616            }
617            else $status = 'extract_error';
618          }
619          else $status = 'archive_error';
620        }
621        else $status = 'archive_error';
622      }
623      else $status = 'dl_archive_error';
624    }
625    else $status = 'temp_path_error';
626
627    @unlink($archive);
628    return $status;
629  }
630 
631  /**
632   * delete $path directory
633   * @param string - path
634   */
635  function deltree($path)
636  {
637    if (is_dir($path))
638    {
639      $fh = opendir($path);
640      while ($file = readdir($fh))
641      {
642        if ($file != '.' and $file != '..')
643        {
644          $pathfile = $path . '/' . $file;
645          if (is_dir($pathfile))
646          {
647            $this->deltree($pathfile);
648          }
649          else
650          {
651            @unlink($pathfile);
652          }
653        }
654      }
655      closedir($fh);
656      return @rmdir($path);
657    }
658  }
659
660  /**
661   * send $path to trash directory
662   * @param string - path
663   */
664  function send_to_trash($path)
665  {
666    $trash_path = PHPWG_THEMES_PATH . 'trash';
667    if (!is_dir($trash_path))
668    {
669      @mkdir($trash_path);
670      $file = @fopen($trash_path . '/.htaccess', 'w');
671      @fwrite($file, 'deny from all');
672      @fclose($file);
673    }
674    while ($r = $trash_path . '/' . md5(uniqid(rand(), true)))
675    {
676      if (!is_dir($r))
677      {
678        @rename($path, $r);
679        break;
680      }
681    }
682  }
683
684  /**
685   * Sort functions
686   */
687  function theme_version_compare($a, $b)
688  {
689    $pattern = array('/([a-z])/ei', '/\.+/', '/\.\Z|\A\./');
690    $replacement = array( "'.'.intval('\\1', 36).'.'", '.', '');
691
692    $array = preg_replace($pattern, $replacement, array($a, $b));
693
694    return version_compare($array[0], $array[1], '>=');
695  }
696
697  function extension_revision_compare($a, $b)
698  {
699    if ($a['revision_date'] < $b['revision_date']) return 1;
700    else return -1;
701  }
702
703  function extension_name_compare($a, $b)
704  {
705    return strcmp(strtolower($a['extension_name']), strtolower($b['extension_name']));
706  }
707
708  function extension_author_compare($a, $b)
709  {
710    $r = strcasecmp($a['author_name'], $b['author_name']);
711    if ($r == 0) return $this->extension_name_compare($a, $b);
712    else return $r;
713  }
714
715  function theme_author_compare($a, $b)
716  {
717    $r = strcasecmp($a['author'], $b['author']);
718    if ($r == 0) return name_compare($a, $b);
719    else return $r;
720  }
721
722  function extension_downloads_compare($a, $b)
723  {
724    if ($a['extension_nb_downloads'] < $b['extension_nb_downloads']) return 1;
725    else return -1;
726  }
727
728  function sort_themes_by_state()
729  {
730    uasort($this->fs_themes, 'name_compare');
731
732    $active_themes = array();
733    $inactive_themes = array();
734    $not_installed = array();
735
736    foreach($this->fs_themes as $theme_id => $theme)
737    {
738      if (isset($this->db_themes_by_id[$theme_id]))
739      {
740        $this->db_themes_by_id[$theme_id]['state'] == 'active' ?
741          $active_themes[$theme_id] = $theme : $inactive_themes[$theme_id] = $theme;
742      }
743      else
744      {
745        $not_installed[$theme_id] = $theme;
746      }
747    }
748    $this->fs_themes = $active_themes + $inactive_themes + $not_installed;
749  }
750
751  // themes specific methods
752  function get_fs_themes_with_ini()
753  {
754    $themes_dir = PHPWG_ROOT_PATH.'themes';
755
756    $fs_themes = array();
757
758    foreach (get_dirs($themes_dir) as $theme)
759    {
760      $conf_file = $themes_dir.'/'.$theme.'/themeconf.inc.php';
761      if (file_exists($conf_file))
762      {
763        $theme_data = array(
764          'name' => $theme,
765          );
766       
767        $ini_file = $themes_dir.'/'.$theme.'/theme.ini';
768        if (file_exists($ini_file))
769        {
770          $theme_ini = parse_ini_file($ini_file);
771          if (isset($theme_ini['extension_id']))
772          {
773            $theme_data['extension_id'] = $theme_ini['extension_id'];
774          }
775        }
776
777        array_push($fs_themes, $theme_data);
778      }
779    }
780
781    echo '<pre>'; print_r($fs_themes); echo '</pre>';
782  }
783
784 
785}
786?>
Note: See TracBrowser for help on using the repository browser.