diff -u -ru typo3_src-4.0/t3lib/stddb/tables.sql typo3_src-4.0.em/t3lib/stddb/tables.sql --- typo3_src-4.0/t3lib/stddb/tables.sql 2006-04-07 02:18:29.000000000 +0200 +++ typo3_src-4.0.em/t3lib/stddb/tables.sql 2006-08-15 13:57:08.452249000 +0200 @@ -96,6 +96,33 @@ ); # +# Table structure for table 'cache_extensions' +# +CREATE TABLE cache_extensions ( + extkey varchar(60) NOT NULL default '', + version varchar(10) NOT NULL default '', + alldownloadcounter int(11) unsigned NOT NULL default '0', + downloadcounter int(11) unsigned NOT NULL default '0', + title varchar(150) NOT NULL default '', + description mediumtext NOT NULL, + state int(4) NOT NULL default '0', + reviewstate int(4) unsigned NOT NULL default '0', + category int(4) NOT NULL default '0', + lastuploaddate int(11) unsigned NOT NULL default '0', + dependencies mediumtext NOT NULL, + authorname varchar(100) NOT NULL default '', + authoremail varchar(100) NOT NULL default '', + ownerusername varchar(50) NOT NULL default '', + t3xfilemd5 varchar(35) NOT NULL default '', + uploadcomment mediumtext NOT NULL, + authorcompany varchar(100) NOT NULL default '', + intversion int(11) NOT NULL default '0', + lastversion int(3) NOT NULL default '0', + lastreviewedversion int(3) NOT NULL default '0', + PRIMARY KEY (extkey,version) +); + +# # Table structure for table 'cache_hash' # CREATE TABLE cache_hash ( diff -u -ru typo3_src-4.0/typo3/mod/tools/em/class.em_index.php typo3_src-4.0.em/typo3/mod/tools/em/class.em_index.php --- typo3_src-4.0/typo3/mod/tools/em/class.em_index.php 2006-04-07 02:18:29.000000000 +0200 +++ typo3_src-4.0.em/typo3/mod/tools/em/class.em_index.php 2006-08-14 22:29:32.873768750 +0200 @@ -201,6 +201,8 @@ var $maxUploadSize = 31457280; // Max size in bytes of extension upload to repository var $kbMax = 500; // Max size in kilobytes for files to be edited. var $doPrintContent = true; // If set (default), the function printContent() will echo the content which was collected in $this->content. You can set this to FALSE in order to echo content from elsewhere, fx. when using outbut buffering + var $listingLimit = 500; // List that many extension maximally at one time (fixing memory problems) + var $listingLimitAuthor = 250; // List that many extension maximally at one time (fixing memory problems) /** * Internal variable loaded with extension categories (for display/listing). Should reflect $categories above @@ -761,18 +763,25 @@ $this->detailCols[1]+=6; // see if we have an extensionlist at all - $this->xmlhandler->loadExtensionsXML(); - if (!count($this->xmlhandler->extensionsXML)) { + $this->extensionCount = $this->xmlhandler->countExtensions(); + if (!$this->extensionCount) { $content .= $this->fetchMetaData('extensions'); } + if($this->MOD_SETTINGS['listOrder']=='author_company') { + $this->listingLimit = $this->listingLimitAuthor; + } + + $this->pointer = intval(t3lib_div::_GP('pointer')); + $offset = $this->listingLimit*$this->pointer; + if($this->MOD_SETTINGS['display_own'] && strlen($this->fe_user['username'])) { - $this->xmlhandler->searchExtensionsXML($this->listRemote_search, $this->fe_user['username']); + $this->xmlhandler->searchExtensionsXML($this->listRemote_search, $this->fe_user['username'], $this->MOD_SETTINGS['listOrder']); } else { - $this->xmlhandler->searchExtensionsXML($this->listRemote_search); + $this->xmlhandler->searchExtensionsXML($this->listRemote_search, '', $this->MOD_SETTINGS['listOrder'], false, false, $offset, $this->listingLimit); } if (count($this->xmlhandler->extensionsXML)) { - list($list,$cat) = $this->prepareImportExtList(); + list($list,$cat) = $this->prepareImportExtList(true); // Available extensions if (is_array($cat[$this->MOD_SETTINGS['listOrder']])) { @@ -815,9 +824,11 @@ } $lines[]=$this->extensionListRow($extKey,$ext,array(''.$loadUnloadLink.''),$theRowClass,$inst_list,1,'index.php?CMD[importExtInfo]='.rawurlencode($extKey)); + unset($list[$extKey]); } } } + unset($list); // CSH: $content.= t3lib_BEfunc::cshItem('_MOD_tools_em', 'import_ter', $GLOBALS['BACK_PATH'],'|
'); @@ -825,10 +836,13 @@ $content.= '
List or look up extensions


'; + $content .= $this->browseLinks(); + $content.= ' '.implode(chr(10),$lines).'
'; + $content .= '
'.$this->browseLinks(); $content.= '

PRIVACY NOTICE:
'.$this->privacyNotice; $this->content.=$this->doc->section('Extensions in TYPO3 Extension Repository (online) - Grouped by: '.$this->MOD_MENU['listOrder'][$this->MOD_SETTINGS['listOrder']],$content,0,1); @@ -911,6 +925,29 @@ $this->content.=$this->doc->spacer(20); $this->content.=$this->doc->section('Upload extension file directly (.t3x):',$content,0,1); } + + /** + * Generates a link to the next page of extensions + * + * @return void + */ + function browseLinks() { + $content = ''; + if ($this->pointer) { + $content .= ' Prev page'; + } + if ($content) $content .= '   '; + if (intval($this->extensionCount/$this->listingLimit)>$this->pointer) { + $content .= ' Next page'; + } + $upper = (($this->pointer+1)*$this->listingLimit); + if ($upper>$this->extensionCount) { + $upper = $this->extensionCount; + } + if ($content) $content .= '

Showing extensions '.($this->pointer*$this->listingLimit+1).' to '.$upper.''; + if ($content) $content .= '

'; + return $content; + } /** * Allows changing of settings @@ -1328,9 +1365,8 @@ $content = ''; // Fetch remote data: - $this->xmlhandler->loadExtensionsXML(); - $this->xmlhandler->extensionsXML = array($extKey => $this->xmlhandler->extensionsXML[$extKey]); - list($fetchData,) = $this->prepareImportExtList(); + $this->xmlhandler->searchExtensionsXML($extKey, '', '', true, true); + list($fetchData,) = $this->prepareImportExtList(true); $versions = array_keys($fetchData[$extKey]['versions']); $version = ($version == '') ? end($versions) : $version; @@ -1421,8 +1457,7 @@ $content .= '

Error: The extension list could not be fetched from '.$extfile.'. Possible reasons: network problems, allow_url_fopen is off, curl is not enabled in Install tool.

'; } else { t3lib_div::writeFile(PATH_site.'typo3temp/extensions.xml.gz', $extXML); - $content .= $this->xmlhandler->parseExtensionsXML(implode(gzfile(PATH_site.'typo3temp/extensions.xml.gz'))); - $this->xmlhandler->saveExtensionsXML(); + $content .= $this->xmlhandler->parseExtensionsXML(PATH_site.'typo3temp/extensions.xml.gz'); } } break; @@ -1537,10 +1572,10 @@ // at this point we know we need to import (a matching version of) the extension from TER2 // see if we have an extensionlist at all - $this->xmlhandler->loadExtensionsXML(); - if (!count($this->xmlhandler->extensionsXML)) { + if (!$this->xmlhandler->countExtensions()) { $this->fetchMetaData('extensions'); } + $this->xmlhandler->searchExtensionsXML($extKey, '', '', true); // check if extension can be fetched if(isset($this->xmlhandler->extensionsXML[$extKey])) { @@ -1651,7 +1686,7 @@ } else return 'Wrong file format. No data recognized, '.$fetchData; } else return 'No file uploaded! Probably the file was too large for PHPs internal limit for uploadable files.'; } else { - $this->xmlhandler->loadExtensionsXML(); + $this->xmlhandler->searchExtensionsXML($extKey, '', '', true); // Fetch extension from TER: if(!strlen($version)) { @@ -3026,7 +3061,7 @@ * * @return array List array and category index as key 0 / 1 in an array. */ - function prepareImportExtList() { + function prepareImportExtList($unsetProc = false) { $list = array(); $cat = $this->defaultCategories; $filepath = $this->getMirrorURL(); @@ -3057,8 +3092,10 @@ ); } $this->setCat($cat, $list[$extKey]['versions'][$version], $extKey); + if ($unsetProc) { + unset($this->xmlhandler->extensionsXML[$extKey]); + } } - return array($list,$cat); } @@ -4879,7 +4916,7 @@ $res = array(); $res['version'] = $parts[0].'.'.$parts[1].'.'.$parts[2]; - $res['version_int'] = intval(str_pad($parts[0],3,'0',STR_PAD_LEFT).str_pad($parts[1],3,'0',STR_PAD_LEFT).str_pad($parts[2],3,'0',STR_PAD_LEFT)); + $res['version_int'] = intval($parts[0]*1000000+$parts[1]*1000+$parts[2]); $res['version_main'] = $parts[0]; $res['version_sub'] = $parts[1]; $res['version_dev'] = $parts[2]; diff -u -ru typo3_src-4.0/typo3/mod/tools/em/class.em_xmlhandler.php typo3_src-4.0.em/typo3/mod/tools/em/class.em_xmlhandler.php --- typo3_src-4.0/typo3/mod/tools/em/class.em_xmlhandler.php 2006-04-07 02:18:29.000000000 +0200 +++ typo3_src-4.0.em/typo3/mod/tools/em/class.em_xmlhandler.php 2006-08-14 22:32:54.919853500 +0200 @@ -56,50 +56,78 @@ * @param boolean $latest If true, only the latest version is kept in the list * @return [type] ... */ - function searchExtensionsXML($search, $owner='') { - if(!count($this->extensionsXML)) $this->loadExtensionsXML(); - - reset($this->extensionsXML); - while (list($extkey, $data) = each($this->extensionsXML)) { - - // Unset extension key in installed keys array (for tracking) - if(isset($this->emObj->inst_keys[$extkey])) unset($this->emObj->inst_keys[$extkey]); + function searchExtensionsXML($search, $owner='', $order = '', $allExt = false, $allVer = false, $offset = 0, $limit = 500) { + $where = '1=1'; + if ($search) { + $where .= ' AND extkey LIKE \'%'.$GLOBALS['TYPO3_DB']->quoteStr($search, 'cache_extensions').'%\''; + } + if ($owner) { + $where .= ' AND ownerusername='.$GLOBALS['TYPO3_DB']->fullQuoteStr($owner, 'cache_extensions'); + } + if(!(strlen($owner) || $this->useUnsupported || $allExt)) { + $where .= ' AND reviewstate>0'; + } + if(!($this->useObsolete || $allExt)) { + $where .= ' AND state!=5'; // 5 == obsolete + } + switch ($order) { + case 'author_company': + $forder = 'authorname, authorcompany'; + break; + case 'state': + $forder = 'state'; + break; + case 'cat': + default: + $forder = 'category'; + break; + } + $order = $forder.', title'; - if(strlen($search) && !stristr($extkey,$search)) { - unset($this->extensionsXML[$extkey]); - continue; + if (!$allVer) { + if ($this->useUnsupported) { + $where .= ' AND lastversion>0'; + } else { + $where .= ' AND lastreviewedversion>0'; } + } + $this->catArr = array(); + $idx = 0; + foreach ($this->emObj->defaultCategories['cat'] as $catKey => $tmp) { + $this->catArr[$idx] = $catKey; + $idx++; + } + $this->stateArr = array(); + $idx = 0; + foreach ($this->emObj->states as $state => $tmp) { + $this->stateArr[$idx] = $state; + $idx++; + } - if(strlen($owner) && !$this->checkOwner($extkey, $owner)) { - unset($this->extensionsXML[$extkey]); - continue; - } + $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('*', 'cache_extensions', $where, $groupby, $order, $offset.','.$limit); - if(!strlen($owner)) { - $this->checkReviewState($this->extensionsXML[$extkey]['versions']); // if showing only own extensions, never hide unreviewed + $this->extensionsXML = array(); + while ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { + $row['category'] = $this->catArr[$row['category']]; + $row['state'] = $this->stateArr[$row['state']]; + + if (!is_array($this->extensionsXML[$row['extkey']])) { + $this->extensionsXML[$row['extkey']] = array(); + $this->extensionsXML[$row['extkey']]['downloadcounter'] = $row['alldownloadcounter']; } - $this->removeObsolete($this->extensionsXML[$extkey]['versions']); - - uksort($data['versions'], array($this->emObj, 'versionDifference')); // needed? or will the extensions always be sorted in the XML anyway? Robert? - - if(!count($this->extensionsXML[$extkey]['versions'])) { - unset($this->extensionsXML[$extkey]); + if (!is_array($this->extensionsXML[$row['extkey']]['versions'])) { + $this->extensionsXML[$row['extkey']]['versions'] = array(); } + $row['dependencies'] = unserialize($row['dependencies']); + $this->extensionsXML[$row['extkey']]['versions'][$row['version']] = $row; } } - - /** - * Checks whether at least one of the extension versions is owned by the given username - * - * @param string $extkey - * @param string $owner - * @return boolean - */ - function checkOwner($extkey, $owner) { - foreach($this->extensionsXML[$extkey]['versions'] as $ext) { - if($ext['ownerusername'] == $owner) return true; - } - return false; + + function countExtensions() { + $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('extkey', 'cache_extensions', '1=1', 'extkey'); + $cnt = $GLOBALS['TYPO3_DB']->sql_num_rows($res); + $GLOBALS['TYPO3_DB']->sql_free_result($res); + return $cnt; } /** @@ -108,38 +136,7 @@ * @return boolean true on success, false on error */ function loadExtensionsXML() { - if(is_file(PATH_site.'typo3temp/extensions.bin')) { - $this->extensionsXML = unserialize(gzuncompress(t3lib_div::getURL(PATH_site.'typo3temp/extensions.bin'))); - return true; - } else { - $this->extensionsXML = array(); - return false; - } - } - - /** - * Loads the pre-parsed extension list - * - * @return boolean true on success, false on error - */ - function loadReviewStates() { - if(is_file(PATH_site.'typo3temp/reviewstates.bin')) { - $this->reviewStates = unserialize(gzuncompress(t3lib_div::getURL(PATH_site.'typo3temp/reviewstates.bin'))); - return true; - } else { - $this->reviewStates = array(); - return false; - } - } - - /** - * Enter description here... - * - * @return [type] ... - */ - function saveExtensionsXML() { - t3lib_div::writeFile(PATH_site.'typo3temp/extensions.bin',gzcompress(serialize($this->extXMLResult))); - t3lib_div::writeFile(PATH_site.'typo3temp/reviewstates.bin',gzcompress(serialize($this->reviewStates))); + $this->searchExtensionsXML('', '', true); } /** @@ -169,20 +166,20 @@ } /** - * Enter description here... + * Returns the reviewstate of a specific extension-key/version * * @param unknown_type $extKey * @param [type] $version: ... * @return [type] ... */ function getReviewState($extKey, $version) { - if(!is_array($this->reviewStates)) $this->loadReviewStates(); - - if(isset($this->reviewStates[$extKey])) { - return (int)$this->reviewStates[$extKey][$version]; - } else { - return 0; + $where = 'extkey='.$GLOBALS['TYPO3_DB']->fullQuoteStr($extkey, 'cache_extensions').' AND version='.$GLOBALS['TYPO3_DB']->fullQuoteStr($version, 'cache_extensions'); + $res = $GLOBALS['TYPO3_DB']->exec_SELECTquery('reviewstate', 'cache_extensions', $where); + if ($row = $GLOBALS['TYPO3_DB']->sql_fetch_assoc($res)) { + return $row['reviewstate']; } + $GLOBALS['TYPO3_DB']->sql_free_result($res); + return 0; } /** @@ -195,10 +192,10 @@ if($this->useUnsupported) return; reset($extensions); - while (list($version, $data) = each($extensions)) { - if($data['reviewstate']<1) - unset($extensions[$version]); - } + while (list($version, $data) = each($extensions)) { + if($data['reviewstate']<1) + unset($extensions[$version]); + } } /** @@ -301,7 +298,7 @@ * @param string XML data file to parse * @return string HTLML output informing about result */ - function parseExtensionsXML($string) { + function parseExtensionsXML($filename) { global $TYPO3_CONF_VARS; $parser = xml_parser_create(); @@ -310,6 +307,13 @@ xml_set_element_handler($parser, array(&$this,'startElement'), array(&$this,'endElement')); xml_set_character_data_handler($parser, array(&$this,'characterData')); + $fd = gzopen($filename, 'rb'); + if (!$fd) { + $content.= 'Error opening XML extension file "'.$filename.'"'; + return $content; + } + $string = gzread($fd, 0xffff); // Read 64KB + if ((double)phpversion()>=5) { $preg_result = array(); preg_match('/^[[:space:]]*<\?xml[^>]*encoding[[:space:]]*=[[:space:]]*"([^"]*)"/',substr($string,0,200),$preg_result); @@ -317,20 +321,132 @@ xml_parser_set_option($parser, XML_OPTION_TARGET_ENCODING, $theCharset); // us-ascii / utf-8 / iso-8859-1 } - // Parse content: - if (!xml_parse($parser, $string)) { - $content.= 'Error in XML parser while decoding extensions XML file. Line '.xml_get_current_line_number($parser).': '.xml_error_string(xml_get_error_code($parser)); - $error = true; - } + $this->revCatArr = array(); + $idx = 0; + foreach ($this->emObj->defaultCategories['cat'] as $catKey => $tmp) { + $this->revCatArr[$catKey] = $idx; + $idx++; + } + $this->revStateArr = array(); + $idx = 0; + foreach ($this->emObj->states as $state => $tmp) { + $this->revStateArr[$state] = $idx; + $idx++; + } + + $GLOBALS['TYPO3_DB']->exec_DELETEquery('cache_extensions', '1=1'); + + $extcount = 0; + do { + if (preg_match('/.*(.*<\/extension>)/sU', $string, $match)>0) { + // Parse content: + if (!xml_parse($parser, $match[0], 0)) { + $content.= 'Error in XML parser while decoding extensions XML file. Line '.xml_get_current_line_number($parser).': '.xml_error_string(xml_get_error_code($parser)); + $error = true; + break; + } + $this->storeXMLResult(); + $this->extXMLResult = array(); + $extcount++; + $string = substr($string, strlen($match[0])); + } else { + $len = strlen($string); + $string .= gzread($fd, 0xffff); // Read 64KB + if (strlen($string)==$len) break; // Nothing more can get read + } + } while (true); + xml_parser_free($parser); + gzclose($fd); if(!$error) { - $content.= '

The extensions list has been updated and now contains '.count($this->extXMLResult).' extension entries.

'; + $content.= '

The extensions list has been updated and now contains '.$extcount.' extension entries.

'; } return $content; } + function storeXMLResult() { + foreach ($this->extXMLResult as $extkey => $extArr) { + $max = -1; + $maxrev = -1; + $last = ''; + $lastrev = ''; + $usecat = ''; + $usetitle = ''; + $useauthor = ''; + $verArr = array(); + foreach ($extArr['versions'] as $version => $vArr) { + $iv = $this->emObj->makeVersion($version, 'int'); + if ($vArr['title']&&!$usetitle) { + $usetitle = $vArr['title']; + } + if ($vArr['state']&&!$usestate) { + $usestate = $vArr['state']; + } + if ($vArr['authorcompany']&&!$useauthorcompany) { + $useauthorcompany = $vArr['authorcompany']; + } + if ($vArr['authorname']&&!$useauthorname) { + $useauthorname = $vArr['authorname']; + } + $verArr[$version] = $iv; + if ($iv>$max) { + $max = $iv; + $last = $version; + if ($vArr['title']) { + $usetitle = $vArr['title']; + } + if ($vArr['state']) { + $usestate = $vArr['state']; + } + if ($vArr['authorcompany']) { + $useauthorcompany = $vArr['authorcompany']; + } + if ($vArr['authorname']) { + $useauthorname = $vArr['authorname']; + } + $usecat = $vArr['category']; + } + if ($vArr['reviewstate'] && ($iv>$maxrev)) { + $maxrev = $iv; + $lastrev = $version; + } + } + if (!strlen($usecat)) { + $usecat = 4; // Extensions without a category end up in "misc" + } else { + if (isset($this->revCatArr[$usecat])) { + $usecat = $this->revCatArr[$usecat]; + } else { + $usecat = 4; // Extensions without a category end up in "misc" + } + } + if (isset($this->revStateArr[$usestate])) { + $usestate = $this->revCatArr[$usestate]; + } else { + $usestate = 999; // Extensions without a category end up in "misc" + } + foreach ($extArr['versions'] as $version => $vArr) { + $vArr['version'] = $version; + $vArr['intversion'] = $verArr[$version]; + $vArr['extkey'] = $extkey; + $vArr['alldownloadcounter'] = $extArr['downloadcounter']; + $vArr['dependencies'] = serialize($vArr['dependencies']); + $vArr['category'] = $usecat; + $vArr['title'] = $usetitle; + if ($version==$last) { + $vArr['lastversion'] = 1; + } + if ($version==$lastrev) { + $vArr['lastreviewedversion'] = 1; + } + $vArr['state'] = isset($this->revStateArr[$vArr['state']])?$this->revStateArr[$vArr['state']]:$usestate; // 999 = not set category + $GLOBALS['TYPO3_DB']->exec_INSERTquery('cache_extensions', $vArr); + } + } + } + /** * Parses content of mirrors.xml into a suitable array *