| +----------------------------------------------------------------------+ $Id$ */ require_once "PEAR.php"; require_once "PEAR/Remote.php"; #require_once "HTML/Template/IT.php"; require_once "Net/UserAgent/Detect.php"; /** * PEAR_Frontend_Web is a HTML based Webfrontend for the PEAR Installer * * The Webfrontend provides basic functionality of the Installer, such as * a package list grouped by categories, a search mask, the possibility * to install/upgrade/uninstall packages and some minor things. * PEAR_Frontend_Web makes use of the PEAR::HTML_IT Template engine which * provides the possibillity to skin the Installer. * * @author Christian Dickmann * @package PEAR_Frontend_Web * @access private */ class PEAR_Frontend_WebSGL extends PEAR { // {{{ properties /** * What type of user interface this frontend is for. * @var string * @access public */ var $type = 'WebSGL'; /** * Container, where values can be saved temporary * @var array * @access private */ var $_data = array(); // }}} var $config; var $_no_delete_pkgs = array( 'PEAR', 'PEAR_Frontend_Web', 'Archive_Tar', 'Console_Getopt', 'XML_RPC', 'Net_UserAgent_Detect', 'Pager'); // {{{ constructor function PEAR_Frontend_WebSGL() { parent::PEAR(); $GLOBALS['_PEAR_Frontend_Web_log'] = ''; $this->config = $GLOBALS['_PEAR_Frontend_Web_config']; } // }}} // {{{ displayLine() /* XXX some methods from CLI following. should be deleted in the near future */ function displayLine($text) { trigger_error("Frontend::display deprecated", E_USER_ERROR); } // }}} // {{{ display() function display($text) { trigger_error("Frontend::display deprecated", E_USER_ERROR); } // }}} // {{{ userConfirm() function userConfirm($prompt, $default = 'yes') { trigger_error("Frontend::display deprecated", E_USER_ERROR); return false; } // }}} // {{{ displayStart(prompt, [default]) function displayStart() { $tpl = $this->_initTemplate("start.tpl.html", 'PEAR Installer'); $tpl->setVariable('Version', '0.5'); $tpl->show(); exit; } // }}} // {{{ _initTemplate() /** * Initialize a TemplateObject, add a title, and icon and add JS and CSS for DHTML * * @param string $file filename of the template file * @param string $title (optional) title of the page * @param string $icon (optional) iconhandle for this page * @param boolean $useDHTML (optional) add JS and CSS for DHTML-features * * @access private * * @return object Object of HTML/IT - Template - Class */ function _initTemplate($file, $title = '', $icon = '', $useDHTML = true) { return false; $tpl = new HTML_Template_IT(dirname(__FILE__)."/WebSGL"); $tpl->loadTemplateFile($file); $tpl->setVariable("InstallerURL", $_SERVER["PHP_SELF"]); $tpl->setVariable("ImgPEAR", $_SERVER["PHP_SELF"].'?img=pear'); if ($title) { $tpl->setVariable("Title", $title); } // if ($icon) { // $tpl->setCurrentBlock("TitleBlock"); // $tpl->setVariable("_InstallerURL", $_SERVER["PHP_SELF"]); // $tpl->setVariable("_Title", $title); // $tpl->setVariable("_Icon", $icon); // $tpl->parseCurrentBlock(); // } $tpl->setCurrentBlock(); if ($useDHTML && Net_UserAgent_Detect::getBrowser('ie5up') == 'ie5up') { $dhtml = true; } else { $dhtml = false; } if ($dhtml) { $tpl->setVariable("JS", 'dhtml'); $css = ''; $tpl->setVariable("DHTMLcss", $css); } else { $tpl->setVariable("JS", 'nodhtml'); } if (!isset($_SESSION['_PEAR_Frontend_Web_js']) || $_SESSION['_PEAR_Frontend_Web_js'] == false) { $tpl->setCurrentBlock('JSEnable'); $tpl->setVariable('RedirectURL', $_SERVER['REQUEST_URI']. (!empty($_GET) ? '&' : '?') .'enableJS=1'); $tpl->parseCurrentBlock(); $tpl->setCurrentBlock(); } return $tpl; } // }}} // {{{ displayError() /** * Display an error page * * @param mixed $eobj PEAR_Error object or string containing the error message * @param string $title (optional) title of the page * @param string $img (optional) iconhandle for this page * @param boolean $popup (optional) popuperror or normal? * * @access public * * @return null does not return anything, but exit the script */ function displayError($eobj, $title = 'Error', $img = 'error', $popup = false) { return false; $msg = ''; if (isset($GLOBALS['_PEAR_Frontend_Web_log']) && trim($GLOBALS['_PEAR_Frontend_Web_log'])) { $msg = trim($GLOBALS['_PEAR_Frontend_Web_log'])."\n\n"; } if (PEAR::isError($eobj)) { $msg .= trim($eobj->getMessage()); } else { $msg .= trim($eobj); } $msg = nl2br($msg."\n"); $tplfile = ($popup ? "error.popup.tpl.html" : "error.tpl.html"); $tpl = $this->_initTemplate($tplfile, $title, $img); $tpl->setVariable("Error", $msg); $command_map = array( "install" => "list-all", "uninstall" => "list-all", "upgrade" => "list-all", ); if (isset($_GET['command'])) { if (isset($command_map[$_GET['command']])) { $_GET['command'] = $command_map[$_GET['command']]; } $tpl->setVariable("param", '?command='.$_GET['command']); } $tpl->show(); exit; } // }}} // {{{ displayFatalError() /** * Alias for PEAR_Frontend_Web::displayError() * * @see PEAR_Frontend_Web::displayError() */ function displayFatalError($eobj, $title = 'Error', $img = 'error') { $this->displayError($eobj, $title, $img); } function displayErrorImg($eobj) { $msg = ''; if (isset($GLOBALS['_PEAR_Frontend_Web_log']) && trim($GLOBALS['_PEAR_Frontend_Web_log'])) $msg = trim($GLOBALS['_PEAR_Frontend_Web_log'])."\n\n"; $_SESSION['_PEAR_Frontend_Web_LastError'] = $eobj; $_SESSION['_PEAR_Frontend_Web_LastError_log'] = $msg; echo ''; printf('', $_SERVER['PHP_SELF']); exit; } // }}} // {{{ _outputListAll() /** * Output a list of packages, grouped by categories. Uses Paging * * @param array $data array containing all data to display the list * @param string $title (optional) title of the page * @param string $img (optional) iconhandle for this page * @param boolean $useDHTML (optional) add JS and CSS for DHTML-features * @param boolean $paging (optional) use Paging or not * * @access private * * @return boolean true (yep. i am an optimist) */ function _outputListAll($data, $title = 'Install / Upgrade / Remove PEAR Packages', $img = 'pkglist', $useDHTML = false, $paging = true) { $tpl = $this->_initTemplate("package.list.tpl.html", $title, $img, $useDHTML); if (!isset($data['data'])) { $data['data'] = array(); } $pageId = isset($_GET['from'])?$_GET['from']:0; $paging_data = $this->__getData($pageId, 5, count($data['data']), false); $data['data'] = array_slice($data['data'], $pageId, 5); $links = array(); $from = $paging_data['from']; $to = $paging_data['next']; // Generate Linkinformation to redirect to _this_ page after performing an action $link_str = '%s'; $command = isset($_GET['command']) ? $_GET['command']:'list-all'; $mode = isset($_GET['mode'])?$_GET['mode']:''; if ($paging_data['from']>1) { $links['back'] = sprintf($link_str, $command, $paging_data['prev'], $mode, '<<'); } else { $links['back'] = ''; } if ( $paging_data['next']) { $links['next'] = sprintf($link_str, $command, $paging_data['next'], $mode, '>>'); } else { $links['next'] = ''; } $links['current'] = '&from=' . $paging_data['from'] . '&mode=' . $mode; if (isset($_GET['command']) && $_GET['command'] == 'search') { $links['current'] .= '&redirect=search&0='.$_REQUEST[0].'&1='.$_REQUEST[1]; } $modes = array( 'installed' => 'list installed packages', '' => 'list all packages', 'notinstalled' => 'list not installed packages', 'upgrades' => 'list avail. upgrades', ); $i = 1; foreach($modes as $mode => $text) { $tpl->setVariable('mode' . $i , !empty($mode) ? '&mode='.$mode : ''); $tpl->setVariable('mode' . $i.'text', $text); $i++; } $tpl->setVariable('Prev', $links['back']); $tpl->setVariable('Next', $links['next']); $tpl->setVariable('PagerFrom', $from); $tpl->setVariable('PagerTo', $to); $tpl->setVariable('PagerCount', $paging_data['numrows']); foreach($data['data'] as $category => $packages) { foreach($packages as $row) { list($pkgName, $pkgVersionLatest, $pkgVersionInstalled, $pkgSummary) = $row; $tpl->setCurrentBlock("Row"); $tpl->setVariable("ImgPackage", $_SERVER["PHP_SELF"].'?img=package'); $images = array( 'install' => 'install', 'uninstall' => 'uninstall', 'upgrade' => 'upgrade', 'info' => 'info', 'infoExt' => 'extended info', ); $urls = array( 'install' => sprintf('%s?command=install&pkg=%s%s', $_SERVER["PHP_SELF"], $pkgName, $links['current']), 'uninstall' => sprintf('%s?command=uninstall&pkg=%s%s', $_SERVER["PHP_SELF"], $pkgName, $links['current']), 'upgrade' => sprintf('%s?command=upgrade&pkg=%s%s', $_SERVER["PHP_SELF"], $pkgName, $links['current']), 'info' => sprintf('%s?command=remote-info&pkg=%s', $_SERVER["PHP_SELF"], $pkgName), 'infoExt' => sprintf('%s?package=%s', 'http://pear.php.net/package-info.php', $row[0]), ); $compare = version_compare($pkgVersionLatest, $pkgVersionInstalled); $id = 'id="'.$pkgName.'_href"'; if (!$pkgVersionInstalled || $pkgVersionInstalled == "- no -") { $inst = sprintf('%s', $urls['install'], $pkgName, $id, $images['install']); $del = ''; } else if ($compare == 1) { $inst = sprintf('%s', $urls['upgrade'], $pkgName, $id, $images['upgrade']); $del = sprintf('%s', $urls['uninstall'], $pkgName, $id, $images['uninstall']); } else { $del = sprintf('%s', $urls['uninstall'], $pkgName, $id, $images['uninstall']); $inst = ''; } $info = sprintf('%s', $urls['info'], $images['info']); $infoExt = sprintf('%s', $urls['infoExt'], $images['infoExt']); if (in_array($pkgName, $this->_no_delete_pkgs)) { $del = ''; } $tpl->setVariable("Latest", $pkgVersionLatest); $tpl->setVariable("Installed", $pkgVersionInstalled); $tpl->setVariable("Install", $inst); $tpl->setVariable("Delete", $del); $tpl->setVariable("Info", $info); $tpl->setVariable("InfoExt", $infoExt); $tpl->setVariable("Package", $pkgName); $tpl->setVariable("Summary", nl2br($pkgSummary)); $tpl->parseCurrentBlock(); } $tpl->setCurrentBlock("Category"); $tpl->setVariable("categoryName", $category); $tpl->setVariable("ImgCategory", $_SERVER["PHP_SELF"].'?img=category'); $tpl->parseCurrentBlock(); } $tpl->show(); return true; } function _getPackageDeps($deps) { if (count($deps) == 0) { return "No dependencies registered.\n"; } else { $lastversion = ''; $rel_trans = array( 'lt' => 'older than %s', 'le' => 'version %s or older', 'eq' => 'version %s', 'ne' => 'any version but %s', 'gt' => 'newer than %s', 'ge' => '%s or newer', ); $dep_type_desc = array( 'pkg' => 'PEAR Package', 'ext' => 'PHP Extension', 'php' => 'PHP Version', 'prog' => 'Program', 'ldlib' => 'Development Library', 'rtlib' => 'Runtime Library', 'os' => 'Operating System', 'websrv' => 'Web Server', 'sapi' => 'SAPI Backend', ); $result = "
\n"; foreach($deps as $row) { // Print link if it's a PEAR package if ($row['type'] == 'pkg') { $row['name'] = sprintf('%s', $_SERVER['PHP_SELF'], $row['name'], $row['name']); } if (isset($rel_trans[$row['relation']])) { $rel = sprintf($rel_trans[$row['relation']], $row['version']); $result .= sprintf("%s: %s %s", $dep_type_desc[$row['type']], $row['name'], $rel); } else { $result .= sprintf("%s: %s", $dep_type_desc[$row['type']], $row['name']); } $lastversion = $row['version']; $result .= '
'; } if ($lastversion) { } $result .= "
\n"; } return $result; } /** * Output details of one package * * @param array $data array containing all information about the package * * @access private * * @return boolean true (yep. i am an optimist) */ function _outputPackageInfo($data) { $tpl = $this->_initTemplate("package.info.tpl.html", 'Package Management :: '.$data['name'], 'pkglist'); $tpl->setVariable("Latest", $data['stable']); $tpl->setVariable("Installed", $data['installed']); $tpl->setVariable("Package", $data['name']); $tpl->setVariable("License", $data['license']); $tpl->setVariable("Category", $data['category']); $tpl->setVariable("Summary", nl2br($data['summary'])); $tpl->setVariable("Description", nl2br($data['description'])); $deps = $data['releases'][$data['stable']]['deps']; $tpl->setVariable("Dependencies", $this->_getPackageDeps($deps)); $compare = version_compare($data['stable'], $data['installed']); $images = array( 'install' => 'install', 'uninstall' => 'uninstall', 'upgrade' => 'upgrade', ); $opt_img = array(); $opt_text = array(); if (!$data['installed'] || $data['installed'] == "- no -") { $opt_img[] = sprintf( '%s', $_SERVER["PHP_SELF"], $data['name'], $images['install']); $opt_text[] = sprintf( 'Install package', $_SERVER["PHP_SELF"], $data['name']); } else if ($compare == 1) { $opt_img[] = sprintf( '%s
', $_SERVER["PHP_SELF"], $data['name'], $images['upgrade']); $opt_text[] = sprintf( 'Upgrade package', $_SERVER["PHP_SELF"], $data['name']); if (!in_array($data['name'], $this->_no_delete_pkgs)) { $opt_img[] = sprintf( '%s', $_SERVER["PHP_SELF"], $data['name'], 'onClick="return confirm(\'Do you really want to uninstall \\\''.$data['name'].'\\\'?\')"', $images['uninstall']); $opt_text[] = sprintf( 'Uninstall package', $_SERVER["PHP_SELF"], $data['name'], 'onClick="return confirm(\'Do you really want to uninstall \\\''.$data['name'].'\\\'?\')"'); } } else { if (!in_array($data['name'], $this->_no_delete_pkgs)) { $opt_img[] = sprintf( '%s', $_SERVER["PHP_SELF"], $data['name'], 'onClick="return confirm(\'Do you really want to uninstall \\\''.$data['name'].'\\\'?\')"', $images['uninstall']); $opt_text[] = sprintf( 'Uninstall package', $_SERVER["PHP_SELF"], $data['name'], 'onClick="return confirm(\'Do you really want to uninstall \\\''.$data['name'].'\\\'?\')"'); } } if (isset($opt_img[0])) { $tpl->setVariable("Opt_Img_1", $opt_img[0]); $tpl->setVariable("Opt_Text_1", $opt_text[0]); } if (isset($opt_img[1])) { $tpl->setVariable("Opt_Img_2", $opt_img[1]); $tpl->setVariable("Opt_Text_2", $opt_text[1]); } $tpl->show(); return true; } /** * Output all kinds of data depending on the command which called this method * * @param mixed $data datastructure containing the information to display * @param string $command (optional) command from which this method was called * * @access public * * @return mixed highly depends on the command */ function outputData($data, $command = '_default') { switch ($command) { case 'config-show': $prompt = array(); $default = array(); foreach($data['data'] as $group) { foreach($group as $row) { $prompt[$row[1]] = $row[0]; $default[$row[1]] = $row[2]; } } $title = 'Configuration :: '.$GLOBALS['pear_user_config']; $GLOBALS['_PEAR_Frontend_Web_Config'] = $this->userDialog($command, $prompt, array(), $default, $title, 'config'); return true; case 'list-all': #return $this->_outputListAll($data); return $data; case 'search': return $this->_outputListAll($data, 'Package Search :: Result', 'pkgsearch', false, false); case 'remote-info': return $this->_outputPackageInfo($data); case 'install': case 'upgrade': case 'uninstall': return true; case 'login': if ($_SERVER["REQUEST_METHOD"] != "POST") $this->_data[$command] = $data; return true; case 'logout': $this->displayError($data, 'Logout', 'logout'); break; case 'package': echo $data; break; default: echo $data; } return true; } /** * Display a formular and return the given input (yes. needs to requests) * * @param string $command command from which this method was called * @param array $prompts associative array. keys are the inputfieldnames * and values are the description * @param array $types (optional) array of inputfieldtypes (text, password, * etc.) keys have to be the same like in $prompts * @param array $defaults (optional) array of defaultvalues. again keys have * to be the same like in $prompts * @param string $title (optional) title of the page * @param string $icon (optional) iconhandle for this page * * @access public * * @return array input sended by the user */ function userDialog($command, $prompts, $types = array(), $defaults = array(), $title = '', $icon = '') { // If this is an POST Request, we can return the userinput if (isset($_GET["command"]) && $_GET["command"]==$command && $_SERVER["REQUEST_METHOD"] == "POST") { $result = array(); foreach($prompts as $key => $prompt) { $result[$key] = $_POST[$key]; } return $result; } // If this is an Answer GET Request , we can return the userinput if (isset($_GET["command"]) && $_GET["command"]==$command && isset($_GET["userDialogResult"]) && $_GET["userDialogResult"]=='get') { $result = array(); foreach($prompts as $key => $prompt) { $result[$key] = $_GET[$key]; } return $result; } // Assign title and icon to some commands if ($command == 'login') { $title = 'Login'; $icon = 'login'; } $tpl = $this->_initTemplate("userDialog.tpl.html", $title, $icon); $tpl->setVariable("Command", $command); if (isset($this->_data[$command])) { $tpl->setVariable("Headline", nl2br($this->_data[$command])); } if (is_array($prompts)) { $maxlen = 0; foreach($prompts as $key => $prompt) { if (strlen($prompt) > $maxlen) { $maxlen = strlen($prompt); } } foreach($prompts as $key => $prompt) { $tpl->setCurrentBlock("InputField"); $type = (isset($types[$key]) ? $types[$key] : 'text'); $default = (isset($defaults[$key]) ? $defaults[$key] : ''); $tpl->setVariable("prompt", $prompt); $tpl->setVariable("name", $key); $tpl->setVariable("default", $default); $tpl->setVariable("type", $type); if ($maxlen > 25) { $tpl->setVariable("width", 'width="275"'); } $tpl->parseCurrentBlock(); } } $tpl->show(); exit; } /** * Write message to log * * @param string $text message which has to written to log * * @access public * * @return boolean true */ function log($text) { $GLOBALS['_PEAR_Frontend_Web_log'] .= $text."\n"; return true; } /** * Sends the required file along with Headers and exits the script * * @param string $handle handle of the requested file * @param string $group group of the requested file * * @access public * * @return null nothing, because script exits */ function outputFrontendFile($handle, $group) { $handles = array( "css" => array( "style" => "style.css", "dhtml" => "dhtml.css", ), "js" => array( "dhtml" => "dhtml.js", "nodhtml" => "nodhtml.js", ), "image" => array( "logout" => array( "type" => "gif", "file" => "logout.gif", ), "login" => array( "type" => "gif", "file" => "login.gif", ), "config" => array( "type" => "gif", "file" => "config.gif", ), "pkglist" => array( "type" => "png", "file" => "pkglist.png", ), "pkgsearch" => array( "type" => "png", "file" => "pkgsearch.png", ), "package" => array( "type" => "jpeg", "file" => "package.jpg", ), "category" => array( "type" => "jpeg", "file" => "category.jpg", ), "install" => array( "type" => "gif", "file" => "install.gif", ), "install_wait" => array( "type" => "gif", "file" => "install_wait.gif", ), "install_ok" => array( "type" => "gif", "file" => "install_ok.gif", ), "install_fail" => array( "type" => "gif", "file" => "install_fail.gif", ), "uninstall" => array( "type" => "gif", "file" => "trash.gif", ), "info" => array( "type" => "gif", "file" => "info.gif", ), "infoplus" => array( "type" => "gif", "file" => "infoplus.gif", ), "pear" => array( "type" => "gif", "file" => "pearsmall.gif", ), "error" => array( "type" => "gif", "file" => "error.gif", ), "manual" => array( "type" => "gif", "file" => "manual.gif", ), "download" => array( "type" => "gif", "file" => "download.gif", ), ), ); $file = $handles[$group][$handle]; switch ($group) { case 'css': header("Content-Type: text/css"); readfile(dirname(__FILE__).'/Web/'.$file); exit; case 'image': $filename = dirname(__FILE__).'/Web/'.$file['file']; header("Content-Type: image/".$file['type']); header("Expires: ".gmdate("D, d M Y H:i:s \G\M\T", time() + 60*60*24*100)); header("Last-Modified: ".gmdate("D, d M Y H:i:s \G\M\T", filemtime($filename))); header("Cache-Control: public"); header("Pragma: "); readfile($filename); exit; case 'js': header("Content-Type: text/javascript"); readfile(dirname(__FILE__).'/Web/'.$file); exit; } } /* * From DB::Pager. Removing Pager dependency. * @private */ function __getData($from, $limit, $numrows, $maxpages = false) { if (empty($numrows) || ($numrows < 0)) { return null; } $from = (empty($from)) ? 0 : $from; if ($limit <= 0) { return false; } // Total number of pages $pages = ceil($numrows/$limit); $data['numpages'] = $pages; // first & last page $data['firstpage'] = 1; $data['lastpage'] = $pages; // Build pages array $data['pages'] = array(); for ($i=1; $i <= $pages; $i++) { $offset = $limit * ($i-1); $data['pages'][$i] = $offset; // $from must point to one page if ($from == $offset) { // The current page we are $data['current'] = $i; } } if (!isset($data['current'])) { return PEAR::raiseError (null, 'wrong "from" param', null, null, null, 'DB_Error', true); } // Limit number of pages (Goole algorithm) if ($maxpages) { $radio = floor($maxpages/2); $minpage = $data['current'] - $radio; if ($minpage < 1) { $minpage = 1; } $maxpage = $data['current'] + $radio - 1; if ($maxpage > $data['numpages']) { $maxpage = $data['numpages']; } foreach (range($minpage, $maxpage) as $page) { $tmp[$page] = $data['pages'][$page]; } $data['pages'] = $tmp; $data['maxpages'] = $maxpages; } else { $data['maxpages'] = null; } // Prev link $prev = $from - $limit; $data['prev'] = ($prev >= 0) ? $prev : null; // Next link $next = $from + $limit; $data['next'] = ($next < $numrows) ? $next : null; // Results remaining in next page & Last row to fetch if ($data['current'] == $pages) { $data['remain'] = 0; $data['to'] = $numrows; } else { if ($data['current'] == ($pages - 1)) { $data['remain'] = $numrows - ($limit*($pages-1)); } else { $data['remain'] = $limit; } $data['to'] = $data['current'] * $limit; } $data['numrows'] = $numrows; $data['from'] = $from + 1; $data['limit'] = $limit; return $data; } // }}} } ?>