From f82430dd3b92a2e37a58108fa81cd0bc6255fb00 Mon Sep 17 00:00:00 2001 From: plegall Date: Wed, 17 Mar 2010 00:48:38 +0000 Subject: feature 1514: new screen to manage installed themes; activate, deactivate, delete, set as default. plugins.class.php was merged back to a state it doesn't manage themes at all. themes.class.php was created instead, from a duplication of plugins.class.php and strongly modified then. feature 1507: the display of available themes is now much more "graphic". git-svn-id: http://piwigo.org/svn/trunk@5153 68402e56-0260-453c-a942-63ccdbb3a9ee --- admin.php | 2 +- admin/include/plugins.class.php | 37 +- admin/include/themes.class.php | 662 +++++++++++++++++++++ admin/themes/clear/images/missing_screenshot.png | Bin 0 -> 1684 bytes admin/themes/default/default-layout.css | 11 +- admin/themes/default/template/footer.tpl | 2 +- admin/themes/default/template/themes_installed.tpl | 42 ++ admin/themes/default/template/themes_new.tpl | 10 +- admin/themes/roma/images/missing_screenshot.png | Bin 0 -> 3316 bytes admin/themes/roma/theme.css | 2 + admin/themes_installed.php | 112 ++++ admin/themes_new.php | 18 +- include/config_default.inc.php | 3 + include/constants.php | 6 +- include/functions.inc.php | 19 +- install/db/85-database.php | 46 ++ install/piwigo_structure-mysql.sql | 12 + profile.php | 11 +- themes/Sylvia/screenshot.png | Bin 0 -> 53583 bytes themes/clear/screenshot.png | Bin 0 -> 63755 bytes themes/dark/screenshot.png | Bin 0 -> 62117 bytes 21 files changed, 929 insertions(+), 66 deletions(-) create mode 100644 admin/include/themes.class.php create mode 100644 admin/themes/clear/images/missing_screenshot.png create mode 100644 admin/themes/default/template/themes_installed.tpl create mode 100644 admin/themes/roma/images/missing_screenshot.png create mode 100644 admin/themes_installed.php create mode 100644 install/db/85-database.php create mode 100644 themes/Sylvia/screenshot.png create mode 100644 themes/clear/screenshot.png create mode 100644 themes/dark/screenshot.png diff --git a/admin.php b/admin.php index 739f0df5f..a0d6daf39 100644 --- a/admin.php +++ b/admin.php @@ -123,7 +123,7 @@ $template->assign( 'U_CONFIG_DISPLAY'=> $conf_link.'default', 'U_CONFIG_EXTENTS'=> $link_start.'extend_for_templates', 'U_CONFIG_MENUBAR'=> $link_start.'menubar', - 'U_CONFIG_THEMES'=> $link_start.'themes_new', + 'U_CONFIG_THEMES'=> $link_start.'themes_installed', 'U_CATEGORIES'=> $link_start.'cat_list', 'U_MOVE'=> $link_start.'cat_move', 'U_CAT_OPTIONS'=> $link_start.'cat_options', diff --git a/admin/include/plugins.class.php b/admin/include/plugins.class.php index a7ea5cc7f..fb9db0c88 100644 --- a/admin/include/plugins.class.php +++ b/admin/include/plugins.class.php @@ -268,20 +268,14 @@ DELETE FROM ' . PLUGINS_TABLE . ' WHERE id=\'' . $plugin_id . '\''; /** * Retrieve PEM server datas to $server_plugins */ - function get_server_plugins($new=false, $ext_type='plugin') + function get_server_plugins($new=false) { global $user; - $pem_category_id = 12; - if ('theme' == $ext_type) - { - $pem_category_id = 10; - } - // Retrieve PEM versions $version = PHPWG_VERSION; $versions_to_check = array(); - $url = PEM_URL . '/api/get_version_list.php?category_id='.$pem_category_id.'&format=php'; + $url = PEM_URL . '/api/get_version_list.php?category_id=12&format=php'; if (fetchRemote($url, $result) and $pem_versions = @unserialize($result)) { if (!preg_match('/^\d+\.\d+\.\d+/', $version)) @@ -313,7 +307,7 @@ DELETE FROM ' . PLUGINS_TABLE . ' WHERE id=\'' . $plugin_id . '\''; } // Retrieve PEM plugins infos - $url = PEM_URL . '/api/get_revision_list.php?category_id='.$pem_category_id.'&format=php&last_revision_only=true'; + $url = PEM_URL . '/api/get_revision_list.php?category_id=12&format=php&last_revision_only=true'; $url .= '&version=' . implode(',', $versions_to_check); $url .= '&lang=' . substr($user['language'], 0, 2); $url .= '&get_nb_downloads=true'; @@ -370,24 +364,9 @@ DELETE FROM ' . PLUGINS_TABLE . ' WHERE id=\'' . $plugin_id . '\''; * @param string - archive URL * @param string - plugin id or extension id */ - function extract_plugin_files($action, $revision, $dest, $ext_type = 'plugin') + function extract_plugin_files($action, $revision, $dest) { - if ('plugin' == $ext_type) - { - $install_basedir = PHPWG_PLUGINS_PATH; - $main_filename = 'main.inc.php'; - } - elseif ('theme' == $ext_type) - { - $install_basedir = PHPWG_ROOT_PATH.'themes/'; - $main_filename = 'themeconf.inc.php'; - } - else - { - fatal_error('unknown extension type "'.$ext_type.'"'); - } - - if ($archive = tempnam( $install_basedir, 'zip')) + if ($archive = tempnam( PHPWG_PLUGINS_PATH, 'zip')) { $url = PEM_URL . '/download.php?rid=' . $revision; $url .= '&origin=piwigo_' . $action; @@ -402,7 +381,7 @@ DELETE FROM ' . PLUGINS_TABLE . ' WHERE id=\'' . $plugin_id . '\''; foreach ($list as $file) { // we search main.inc.php in archive - if (basename($file['filename']) == $main_filename + if (basename($file['filename']) == 'main.inc.php' and (!isset($main_filepath) or strlen($file['filename']) < strlen($main_filepath))) { @@ -414,11 +393,11 @@ DELETE FROM ' . PLUGINS_TABLE . ' WHERE id=\'' . $plugin_id . '\''; $root = dirname($main_filepath); // main.inc.php path in archive if ($action == 'upgrade') { - $extract_path = $install_basedir . $dest; + $extract_path = PHPWG_PLUGINS_PATH . $dest; } else { - $extract_path = $install_basedir + $extract_path = PHPWG_PLUGINS_PATH . ($root == '.' ? 'extension_' . $dest : basename($root)); } if($result = $zip->extract(PCLZIP_OPT_PATH, $extract_path, diff --git a/admin/include/themes.class.php b/admin/include/themes.class.php new file mode 100644 index 000000000..873e99b3a --- /dev/null +++ b/admin/include/themes.class.php @@ -0,0 +1,662 @@ +get_fs_themes(); + + foreach ($this->get_db_themes() as $db_theme) + { + $this->db_themes_by_id[$db_theme['id']] = $db_theme; + } + } + + /** + * Set tabsheet for themes pages. + * @param string selected page. + */ + function set_tabsheet($selected) + { + include_once(PHPWG_ROOT_PATH.'admin/include/tabsheet.class.php'); + + $link = get_root_url().'admin.php?page='; + + $tabsheet = new tabsheet(); + $tabsheet->add('themes_installed', l10n('Installed Themes'), $link.'themes_installed'); + $tabsheet->add('themes_new', l10n('Add New Theme'), $link.'themes_new'); + $tabsheet->select($selected); + $tabsheet->assign(); + } + + /** + * Perform requested actions + * @param string - action + * @param string - theme id + * @param array - errors + */ + function perform_action($action, $theme_id) + { + if (isset($this->db_themes_by_id[$theme_id])) + { + $crt_db_theme = $this->db_themes_by_id[$theme_id]; + } + + $errors = array(); + + switch ($action) + { + case 'activate': + if (isset($crt_db_theme)) + { + // the theme is already active + break; + } + + $query = " +INSERT INTO ".THEMES_TABLE." + SET id = '".$theme_id."' + , version = '".$this->fs_themes[$theme_id]['version']."' + , name = '".$this->fs_themes[$theme_id]['name']."' +;"; + pwg_query($query); + break; + + case 'deactivate': + if (!isset($crt_db_theme)) + { + // the theme is already inactive + break; + } + + if ($theme_id == get_default_theme()) + { + // find a random theme to replace + $new_theme = null; + + $query = ' +SELECT + id + FROM '.THEMES_TABLE.' + WHERE id != "'.$theme_id.'" +;'; + $result = pwg_query($query); + if (pwg_db_num_rows($result) == 0) + { + $new_theme = 'default'; + } + else + { + list($new_theme) = pwg_db_fetch_row($result); + } + + $this->set_default_theme($new_theme); + } + + $query = " +DELETE + FROM ".THEMES_TABLE." + WHERE id= '".$theme_id."' +;"; + pwg_query($query); + break; + + case 'delete': + if (!empty($crt_db_theme)) + { + array_push($errors, 'CANNOT DELETE - THEME IS INSTALLED'); + break; + } + if (!isset($this->fs_themes[$theme_id])) + { + // nothing to do here + break; + } + + if (!$this->deltree(PHPWG_THEMES_PATH.$theme_id)) + { + $this->send_to_trash(PHPWG_THEMES_PATH.$theme_id); + } + break; + + case 'set_default': + // first we need to know which users are using the current default theme + $this->set_default_theme($theme_id); + break; + } + return $errors; + } + + function set_default_theme($theme_id) + { + // first we need to know which users are using the current default theme + $default_theme = get_default_theme(); + + $query = ' +SELECT + user_id + FROM '.USER_INFOS_TABLE.' + WHERE theme = "'.$default_theme.'" +;'; + $user_ids = array_from_query($query, 'user_id'); + + // $user_ids can't be empty, at least the default user has the default + // theme + + $query = ' +UPDATE '.USER_INFOS_TABLE.' + SET theme = "'.$theme_id.'" + WHERE user_id IN ('.implode(',', $user_ids).') +;'; + pwg_query($query); + } + + function get_db_themes($id='') + { + $query = ' +SELECT + * + FROM '.THEMES_TABLE; + + $clauses = array(); + if (!empty($id)) + { + $clauses[] = "id = '".$id."'"; + } + if (count($clauses) > 0) + { + $query .= ' + WHERE '. implode(' AND ', $clauses); + } + + $result = pwg_query($query); + $themes = array(); + while ($row = pwg_db_fetch_assoc($result)) + { + array_push($themes, $row); + } + return $themes; + } + + + /** + * Get themes defined in the theme directory + */ + function get_fs_themes() + { + $dir = opendir(PHPWG_THEMES_PATH); + + while ($file = readdir($dir)) + { + if ($file!='.' and $file!='..') + { + $path = PHPWG_THEMES_PATH.$file; + if (is_dir($path) + and preg_match('/^[a-zA-Z0-9-_]+$/', $file) + and file_exists($path.'/themeconf.inc.php') + ) + { + $theme = array( + 'id' => $file, + 'name' => $file, + 'version' => '0', + 'uri' => '', + 'description' => '', + 'author' => '', + ); + $theme_data = implode( '', file($path.'/themeconf.inc.php') ); + + if ( preg_match("|Theme Name: (.*)|", $theme_data, $val) ) + { + $theme['name'] = trim( $val[1] ); + } + if (preg_match("|Version: (.*)|", $theme_data, $val)) + { + $theme['version'] = trim($val[1]); + } + if ( preg_match("|Theme URI: (.*)|", $theme_data, $val) ) + { + $theme['uri'] = trim($val[1]); + } + if ($desc = load_language('description.txt', $path.'/', array('return' => true))) + { + $theme['description'] = trim($desc); + } + elseif ( preg_match("|Description: (.*)|", $theme_data, $val) ) + { + $theme['description'] = trim($val[1]); + } + if ( preg_match("|Author: (.*)|", $theme_data, $val) ) + { + $theme['author'] = trim($val[1]); + } + if ( preg_match("|Author URI: (.*)|", $theme_data, $val) ) + { + $theme['author uri'] = trim($val[1]); + } + if (!empty($theme['uri']) and strpos($theme['uri'] , 'extension_view.php?eid=')) + { + list( , $extension) = explode('extension_view.php?eid=', $theme['uri']); + if (is_numeric($extension)) $theme['extension'] = $extension; + } + + // screenshot + $screenshot_path = $path.'/screenshot.png'; + if (file_exists($screenshot_path)) + { + $theme['screenshot'] = $screenshot_path; + } + else + { + global $conf; + $theme['screenshot'] = + PHPWG_ROOT_PATH.'admin/themes/' + .$conf['admin_theme'] + .'/images/missing_screenshot.png' + ; + } + + // IMPORTANT SECURITY ! + $theme = array_map('htmlspecialchars', $theme); + $this->fs_themes[$file] = $theme; + } + } + } + closedir($dir); + } + + /** + * Sort fs_themes + */ + function sort_fs_themes($order='name') + { + switch ($order) + { + case 'name': + uasort($this->fs_themes, 'name_compare'); + break; + case 'status': + $this->sort_themes_by_state(); + break; + case 'author': + uasort($this->fs_themes, array($this, 'theme_author_compare')); + break; + case 'id': + uksort($this->fs_themes, 'strcasecmp'); + break; + } + } + + /** + * Retrieve PEM server datas to $server_themes + */ + function get_server_themes($new=false) + { + global $user; + + $pem_category_id = 10; + + // Retrieve PEM versions + $version = PHPWG_VERSION; + $versions_to_check = array(); + $url = PEM_URL . '/api/get_version_list.php?category_id='.$pem_category_id.'&format=php'; + if (fetchRemote($url, $result) and $pem_versions = @unserialize($result)) + { + if (!preg_match('/^\d+\.\d+\.\d+/', $version)) + { + $version = $pem_versions[0]['name']; + } + $branch = substr($version, 0, strrpos($version, '.')); + foreach ($pem_versions as $pem_version) + { + if (strpos($pem_version['name'], $branch) === 0) + { + $versions_to_check[] = $pem_version['id']; + } + } + } + if (empty($versions_to_check)) + { + return false; + } + + // Themes to check + $themes_to_check = array(); + foreach($this->fs_themes as $fs_theme) + { + if (isset($fs_theme['extension'])) + { + $themes_to_check[] = $fs_theme['extension']; + } + } + + // Retrieve PEM themes infos + $url = PEM_URL . '/api/get_revision_list.php?category_id='.$pem_category_id.'&format=php&last_revision_only=true'; + $url .= '&version=' . implode(',', $versions_to_check); + $url .= '&lang=' . substr($user['language'], 0, 2); + $url .= '&get_nb_downloads=true'; + + if (!empty($themes_to_check)) + { + $url .= $new ? '&extension_exclude=' : '&extension_include='; + $url .= implode(',', $themes_to_check); + } + if (fetchRemote($url, $result)) + { + $pem_themes = @unserialize($result); + if (!is_array($pem_themes)) + { + return false; + } + foreach ($pem_themes as $theme) + { + $this->server_themes[$theme['extension_id']] = $theme; + } + return true; + } + return false; + } + + /** + * Sort $server_themes + */ + function sort_server_themes($order='date') + { + switch ($order) + { + case 'date': + krsort($this->server_themes); + break; + case 'revision': + usort($this->server_themes, array($this, 'extension_revision_compare')); + break; + case 'name': + uasort($this->server_themes, array($this, 'extension_name_compare')); + break; + case 'author': + uasort($this->server_themes, array($this, 'extension_author_compare')); + break; + case 'downloads': + usort($this->server_themes, array($this, 'extension_downloads_compare')); + break; + } + } + + /** + * Extract theme files from archive + * + * @param string - install or upgrade + * @param string - remote revision identifier (numeric) + * @param string - theme id or extension id + */ + function extract_theme_files($action, $revision, $dest) + { + if ($archive = tempnam( PHPWG_THEMES_PATH, 'zip')) + { + $url = PEM_URL . '/download.php?rid=' . $revision; + $url .= '&origin=piwigo_' . $action; + + if ($handle = @fopen($archive, 'wb') and fetchRemote($url, $handle)) + { + fclose($handle); + include(PHPWG_ROOT_PATH.'admin/include/pclzip.lib.php'); + $zip = new PclZip($archive); + if ($list = $zip->listContent()) + { + foreach ($list as $file) + { + // we search main.inc.php in archive + if (basename($file['filename']) == 'themeconf.inc.php' + and (!isset($main_filepath) + or strlen($file['filename']) < strlen($main_filepath))) + { + $main_filepath = $file['filename']; + } + } + if (isset($main_filepath)) + { + $root = dirname($main_filepath); // main.inc.php path in archive + if ($action == 'upgrade') + { + $extract_path = PHPWG_THEMES_PATH . $dest; + } + else + { + $extract_path = PHPWG_THEMES_PATH . ($root == '.' ? 'extension_' . $dest : basename($root)); + } + if ( + $result = $zip->extract( + PCLZIP_OPT_PATH, $extract_path, + PCLZIP_OPT_REMOVE_PATH, $root, + PCLZIP_OPT_REPLACE_NEWER + ) + ) + { + foreach ($result as $file) + { + if ($file['stored_filename'] == $main_filepath) + { + $status = $file['status']; + break; + } + } + if (file_exists($extract_path.'/obsolete.list') + and $old_files = file($extract_path.'/obsolete.list', FILE_IGNORE_NEW_LINES) + and !empty($old_files)) + { + array_push($old_files, 'obsolete.list'); + foreach($old_files as $old_file) + { + $path = $extract_path.'/'.$old_file; + if (is_file($path)) + { + @unlink($path); + } + elseif (is_dir($path)) + { + if (!$this->deltree($path)) + { + $this->send_to_trash($path); + } + } + } + } + } + else $status = 'extract_error'; + } + else $status = 'archive_error'; + } + else $status = 'archive_error'; + } + else $status = 'dl_archive_error'; + } + else $status = 'temp_path_error'; + + @unlink($archive); + return $status; + } + + /** + * delete $path directory + * @param string - path + */ + function deltree($path) + { + if (is_dir($path)) + { + $fh = opendir($path); + while ($file = readdir($fh)) + { + if ($file != '.' and $file != '..') + { + $pathfile = $path . '/' . $file; + if (is_dir($pathfile)) + { + $this->deltree($pathfile); + } + else + { + @unlink($pathfile); + } + } + } + closedir($fh); + return @rmdir($path); + } + } + + /** + * send $path to trash directory + * @param string - path + */ + function send_to_trash($path) + { + $trash_path = PHPWG_THEMES_PATH . 'trash'; + if (!is_dir($trash_path)) + { + @mkdir($trash_path); + $file = @fopen($trash_path . '/.htaccess', 'w'); + @fwrite($file, 'deny from all'); + @fclose($file); + } + while ($r = $trash_path . '/' . md5(uniqid(rand(), true))) + { + if (!is_dir($r)) + { + @rename($path, $r); + break; + } + } + } + + /** + * Sort functions + */ + function theme_version_compare($a, $b) + { + $pattern = array('/([a-z])/ei', '/\.+/', '/\.\Z|\A\./'); + $replacement = array( "'.'.intval('\\1', 36).'.'", '.', ''); + + $array = preg_replace($pattern, $replacement, array($a, $b)); + + return version_compare($array[0], $array[1], '>='); + } + + function extension_revision_compare($a, $b) + { + if ($a['revision_date'] < $b['revision_date']) return 1; + else return -1; + } + + function extension_name_compare($a, $b) + { + return strcmp(strtolower($a['extension_name']), strtolower($b['extension_name'])); + } + + function extension_author_compare($a, $b) + { + $r = strcasecmp($a['author_name'], $b['author_name']); + if ($r == 0) return $this->extension_name_compare($a, $b); + else return $r; + } + + function theme_author_compare($a, $b) + { + $r = strcasecmp($a['author'], $b['author']); + if ($r == 0) return name_compare($a, $b); + else return $r; + } + + function extension_downloads_compare($a, $b) + { + if ($a['extension_nb_downloads'] < $b['extension_nb_downloads']) return 1; + else return -1; + } + + function sort_themes_by_state() + { + uasort($this->fs_themes, 'name_compare'); + + $active_themes = array(); + $inactive_themes = array(); + $not_installed = array(); + + foreach($this->fs_themes as $theme_id => $theme) + { + if (isset($this->db_themes_by_id[$theme_id])) + { + $this->db_themes_by_id[$theme_id]['state'] == 'active' ? + $active_themes[$theme_id] = $theme : $inactive_themes[$theme_id] = $theme; + } + else + { + $not_installed[$theme_id] = $theme; + } + } + $this->fs_themes = $active_themes + $inactive_themes + $not_installed; + } + + // themes specific methods + function get_fs_themes_with_ini() + { + $themes_dir = PHPWG_ROOT_PATH.'themes'; + + $fs_themes = array(); + + foreach (get_dirs($themes_dir) as $theme) + { + $conf_file = $themes_dir.'/'.$theme.'/themeconf.inc.php'; + if (file_exists($conf_file)) + { + $theme_data = array( + 'name' => $theme, + ); + + $ini_file = $themes_dir.'/'.$theme.'/theme.ini'; + if (file_exists($ini_file)) + { + $theme_ini = parse_ini_file($ini_file); + if (isset($theme_ini['extension_id'])) + { + $theme_data['extension_id'] = $theme_ini['extension_id']; + } + } + + array_push($fs_themes, $theme_data); + } + } + + echo '
'; print_r($fs_themes); echo '
'; + } + + +} +?> \ No newline at end of file diff --git a/admin/themes/clear/images/missing_screenshot.png b/admin/themes/clear/images/missing_screenshot.png new file mode 100644 index 000000000..4c67f618d Binary files /dev/null and b/admin/themes/clear/images/missing_screenshot.png differ diff --git a/admin/themes/default/default-layout.css b/admin/themes/default/default-layout.css index 6b7827fad..4aa719cda 100644 --- a/admin/themes/default/default-layout.css +++ b/admin/themes/default/default-layout.css @@ -737,8 +737,13 @@ BODY#thePopuphelpPage { height: 4em; /* legend height (don't set auto to be Gecko friendly)*/ } -.themeBox {float:left; text-align:center; height:170px; background-color:#eee; margin:5px; -moz-border-radius:5px;} +.themeBox {float:left; text-align:center; height:180px; background-color:#eee; margin:5px; -moz-border-radius:5px;} .themeBox IMG {border:1px solid white; margin:0 15px;} .themeName {font-size:1.1em; margin:5px 0;} -.themeActions {margin:5px 0;} -.themeActions A {display:block;} +.themeActions {margin:5px 0; font-size:12px;} +.themeActions A {} + +#themesContent .themeBox IMG {width:150px; height:120px;} +#themesContent H3 {font-size:16px; text-align:left; border-bottom:1px solid #444; letter-spacing:1px; margin:5px;} + +.themeBoxes {min-height:300px;} \ No newline at end of file diff --git a/admin/themes/default/template/footer.tpl b/admin/themes/default/template/footer.tpl index e8709b59f..f7c83474e 100644 --- a/admin/themes/default/template/footer.tpl +++ b/admin/themes/default/template/footer.tpl @@ -45,7 +45,7 @@ {literal}