Index: typo3/sysext/em/classes/connection/class.tx_em_connection_extdirectsoap.php
===================================================================
--- typo3/sysext/em/classes/connection/class.tx_em_connection_extdirectsoap.php	(revision )
+++ typo3/sysext/em/classes/connection/class.tx_em_connection_extdirectsoap.php	(revision )
@@ -0,0 +1,425 @@
+<?php
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <steffen@typo3.org>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *  A copy is found in the textfile GPL.txt and important notices to the license
+ *  from the author is found in LICENSE.txt distributed with these scripts.
+ *
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+class tx_em_Connection_ExtDirectSoap {
+	/**
+	 * @var tx_em_Repository
+	 */
+	var $repository;
+
+	/**
+	 * @var tx_em_Connection_Soap
+	 */
+	var $soap = NULL;
+
+
+	/**
+	 * Keeps instance of settings class.
+	 *
+	 * @var tx_em_Settings
+	 */
+	static protected $objSettings;
+
+	/**
+	 * @var array
+	 */
+	protected $settings;
+
+	/**
+	 * @var array
+	 */
+	protected $accountData = NULL;
+
+	/**
+	 * Constructor
+	 *
+	 * @return void
+	 */
+	public function __construct() {
+		$this->settings = $this->getSettingsObject()->getSettings();
+		/** @var $repository tx_em_Repository */
+		$this->repository = t3lib_div::makeInstance('tx_em_Repository', $this->settings['selectedRepository']);
+
+		if ($this->settings['fe_u'] && $this->settings['fe_p']) {
+			$this->setAccountData($this->settings['fe_u'], $this->settings['fe_p']);
+		}
+
+
+	}
+
+	/**
+	 * Login test with user credentials
+	 *
+	 * @return array
+	 */
+	public function testUserLogin() {
+		if (is_array($this->accountData)) {
+			$login = false; #$this->soap->login($this->accountData['accountData']['username'], $this->accountData['accountData']['password']);
+			if ($login) {
+				$data = array(
+					'success' => TRUE,
+					'id' => $login
+				);
+			} else {
+				$data = array(
+					'success' => FALSE,
+					'error' => 'login failed'
+				);
+			}
+		} else {
+			$data = array(
+				'success' => FALSE,
+				'error' => 'No user login data'
+			);
+		}
+
+		return $data;
+	}
+
+	/**
+	 * Show Info of extension record
+	 *
+	 * @param  array $record
+	 * @return string
+	 */
+	public function  showRemoteExtInfo($record) {
+		return t3lib_div::view_array(array($record, $this->settings));
+	}
+
+	/**
+	 * Checks validity of extension key
+	 *
+	 * @formHandler
+	 * @param  array $parameter
+	 * @return array
+	 */
+	public function checkExtensionkey($parameter) {
+	    $params = array(
+		 	'extensionKey' => $parameter['extkey']
+		);
+		$result = $this->soapCall('checkExtensionKey', $params);
+		$message = $this->getSoapResultMessageFromCode($result['resultCode']);
+		//debug(array($result,$parameter), $message);
+		if ($result['resultCode'] == 10501) {
+			$return =  array(
+				'success' => TRUE,
+				'message' => $message,
+				'valid' => TRUE,
+				'raw' => $result
+			);
+		} else {
+			$return =  array(
+				'success' => FALSE,
+				'message' => $message,
+				'raw' => $result
+			);
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Register extension key
+	 *
+	 * @formHandler
+	 * @param  array $parameter
+	 * @return array
+	 */
+	public function registerExtensionkey($parameter) {
+	    $params = array(
+		 	'registerExtensionKeyData' => array(
+				 'extensionKey' => $parameter['extkey'],
+				 'title' => $parameter['title'],
+				 'description' => $parameter['description']
+			)
+		);
+		$result = $this->soapCall('registerExtensionKey', $params);
+		$message = $this->getSoapResultMessageFromCode($result['resultCode']);
+		//debug(array($result,$parameter), $message);
+		if ($result['resultCode'] == 10503) {
+			$return =  array(
+				'success' => TRUE,
+				'message' => $message,
+				'valid' => TRUE,
+				'raw' => $result
+			);
+		} else {
+			$return =  array(
+				'success' => FALSE,
+				'message' => $message,
+				'raw' => $result
+			);
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Get own extensions
+	 *
+	 * @return array
+	 */
+	public function getExtensions() {
+		$params = array(
+			'extensionKeyFilterOptions' => array(
+				'username' => $this->settings['fe_u']
+			)
+		);
+		$result = @$this->soapCall('getExtensionKeys', $params);
+		$data = $this->addUploads($result['extensionKeyData']);
+
+		if ($result['simpleResult']['resultCode'] == 10000 && $data !== NULL) {
+			$return =  array(
+				'success' => TRUE,
+				'total' => count($result['extensionKeyData']),
+				'data' => $data,
+				'raw' => $result
+			);
+		} else {
+			$return =  array(
+				'success' => FALSE,
+				'raw' => $result
+			);
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Delete extension key
+	 *
+	 * @param  string $key
+	 * @return void
+	 */
+	public function deleteExtensionKey($key) {
+		$params = array(
+		 	'extensionKey' => $key
+		);
+		$result = $this->soapCall('deleteExtensionKey', $params);
+		$message = $this->getSoapResultMessageFromCode($result['resultCode']);
+
+		if ($result['resultCode'] == 10000) {
+			$return =  array(
+				'success' => TRUE,
+				'message' => $this->getSoapResultMessageFromCode(10505), // TER API doesn't send correct result code
+				'key' => $key
+			);
+		} else {
+			$return =  array(
+				'success' => FALSE,
+				'message' => $message,
+				'key' => $key
+			);
+		}
+
+		return $return;
+	}
+
+	/**
+	 * Transfer extension key to other user
+	 *
+	 * @param  $key
+	 * @param  $user
+	 * @return void
+	 */
+	public function transferExtensionKey($key, $user) {
+		$params = array(
+		 	'modifyExtensionKeyData' => array(
+				 'extensionKey' => $key,
+				 'ownerUsername' => $user
+			)
+		);
+		$result = $this->soapCall('modifyExtensionKey', $params);
+		$message = $this->getSoapResultMessageFromCode($result['resultCode']);
+
+		if ($result['resultCode'] == 10000) {
+			$return =  array(
+				'success' => TRUE,
+				'message' => $message,
+				'key' => $key,
+				'user' => $user
+			);
+		} else {
+			$return =  array(
+				'success' => FALSE,
+				'message' => $message,
+				'key' => $key,
+				'user' => $user
+			);
+		}
+
+		return $return;
+	}
+
+
+	/*
+	 * protected class functions
+	 */
+
+	/**
+	 * Sets the account data
+	 *
+	 * @param  string  $user
+	 * @param  string  $password
+	 * @return void
+	 */
+	protected function setAccountData($user, $password) {
+		$this->accountData = array(
+			'accountData' => array(
+				'username' => $user,
+				'password' => $password
+			)
+		);
+		$this->initSoap();
+	}
+
+	/**
+	 * Init soap
+	 *
+	 * @return void
+	 */
+	protected function initSoap() {
+		if ($this->repository->getWsdlUrl()) {
+			/** @var $soap tx_em_Connection_Soap */
+			$this->soap = t3lib_div::makeInstance('tx_em_Connection_Soap');
+			$this->soap->init(
+				array(
+					'wsdl' => $this->repository->getWsdlUrl(),
+					//'authentication' => 'headers',
+					'soapoptions' =>
+					array(
+						'trace' => 1,
+						'exceptions' => 1
+					)
+				),
+				$this->settings['fe_u'],
+				$this->settings['fe_p']
+			);
+		}
+	}
+	/**
+	 * @param  $data
+	 * @return bool|null|string|tx_em_Settings|unknown
+	 */
+	protected function addUploads($data) {
+		if (count((array) $data) === 0) {
+			return NULL;
+		}
+
+		foreach ($data as $key => $extkey) {
+			$row = $GLOBALS['TYPO3_DB']->exec_SELECTgetSingleRow(
+				'extkey, count(version) as uploads',
+				'cache_extensions',
+				'extkey="' . $extkey['extensionkey'] . '" AND repository=1',
+				'extkey'
+			);
+			$data[$key]['uploads'] = intval($row['uploads']);
+			$data[$key]['hasUploads'] = (intval($row['uploads']) > 0);
+		}
+
+		return $data;
+	}
+	/**
+	 * Get settings object
+	 *
+	 * @return tx_em_Settings
+	 */
+	protected function getSettingsObject() {
+		if (!is_object(self::$objSettings) && !(self::$objSettings instanceof tx_em_Settings)) {
+			self::$objSettings = t3lib_div::makeInstance('tx_em_Settings');
+		}
+		return self::$objSettings;
+	}
+
+	/**
+	 * Executes a soap call
+	 *
+	 * @param  $name
+	 * @param  $params
+	 * @return string $response
+	 */
+	protected function soapCall($name, $params) {
+		try {
+			$response = $this->soap->call(
+				$name,
+				array_merge($this->accountData, $params),
+				$this->accountData['accountData']['username'],
+				$this->accountData['accountData']['password']
+			);
+			return $response;
+		} catch (SoapFault $error) {
+			return array(
+				'success' => FALSE,
+				'error' => $error->faultstring
+			);
+		}
+
+
+	}
+
+	/**
+	 * Translates SOAP return codes to messages
+	 *
+	 * @param  int $code
+	 * @return string
+	 */
+	protected function getSoapResultMessageFromCode($code) {
+		switch ($code) {
+			case 10000:
+				return 'OK';
+			break;
+			case 102:
+				return 'User does not exists.';
+			break;
+			case 10500:
+				return 'Extensionkey already exists.';
+			break;
+			case 10501:
+				return 'Extensionkey does not exists.';
+			break;
+			case 10502:
+				return 'Extensionkey is not valid.';
+			break;
+			case 10503:
+				return 'Extensionkey was successful registered.';
+			break;
+			case 10504:
+				return 'Extension was successful upladed.';
+			break;
+			case 10505:
+				return 'Extension was successful deleted.';
+			break;
+			default:
+				return 'Unknow error occured.';
+
+		}
+	}
+}
+
+
+
+?>
\ No newline at end of file
Index: typo3/sysext/em/res/js/em_files.js
===================================================================
--- typo3/sysext/em/res/js/em_files.js	(revision )
+++ typo3/sysext/em/res/js/em_files.js	(revision )
@@ -0,0 +1,215 @@
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+
+Ext.ns('TYPO3.EM');
+
+TYPO3.EM.ExtFilelist = Ext.extend(Ext.Panel, {
+	//border: false,
+	height: 400,
+
+	recordData: null,
+	isWindow: false,
+
+	initComponent:function() {
+
+		var editArea = new Ext.form.TextArea({
+			itemId: 'editarea',
+			ctCls: 'editareapanel',
+			enableKeyEvents: true,
+			listeners: {
+				change: function() {
+					//Ext.getComponent('editarea-save').enable();
+				},
+				keypress: function(textfield, event) {
+					event.stopPropagation();
+				},
+				specialkey: function(textfield, event) {
+					event.stopPropagation();
+				},
+				scope: this
+			}
+
+		});
+
+
+		var fileTree = new Ext.tree.TreePanel ({
+			//directFn: TYPO3.EM.ExtDirect.getExtFileTree,
+			itemId: 'extfiletree',
+			autoScroll: true,
+			containerScroll: true,
+			margins: '0 0 0 0',
+			cmargins: '0 0 0 0',
+			//useArrows: true,
+
+			root: {
+				text: 'Extension Files',
+				itemId: 'fileroot',
+				expanded: true
+			},
+			loader: {
+				directFn: TYPO3.EM.ExtDirect.getExtFileTree,
+				baseParams: {
+					extkey: this.recordData.extkey,
+					typeShort: this.recordData.typeShort,
+					baseNode: this.recordData.nodePath
+				},
+				paramsAsHash: true
+			},
+			listeners: {
+				click: function(node) {  console.log(node.attributes.fileType);
+					var p = this.getComponent('editarea');
+					if (node.attributes.fileType === 'text') {
+						this.layout.center.panel.reloadButton.show().disable();
+						TYPO3.EM.ExtDirect.readExtFile(node.attributes.id , function(response) {
+							// load in textarea
+							editArea.setValue(response);
+							this.layout.center.panel.reloadButton.enable();
+							this.layout.center.panel.fileLabel.setText('File: ' + node.attributes.text);
+							this.layout.center.panel.saveButton.disable();
+						}, this);
+					}
+					if (node.attributes.fileType === 'image') {
+						var w = new Ext.Window({
+							width: 200,
+							height: 200,
+							title: node.attributes.text,
+							layout: 'fit',
+							items: [{
+								xtype: 'image',
+								src: TYPO3.settings.EM.siteUrl + node.attributes.id,
+								autoSize: true,
+								resizable: false,
+								renderTo: document.body
+							}]
+						}).show();
+					}
+				},
+				scope: this
+			}
+
+		});
+
+		Ext.apply(this, {
+
+			layout: 'border',
+			items: [{
+				region: 'west',
+				layout: 'fit',
+				split: true,
+				width: 260,
+				collapsible: true,
+				collapseMode: 'mini',
+				cls: 'filetree-panel',
+				hideCollapseTool: true,
+				items: [fileTree],
+				tbar: [{
+					iconCls: 'x-tbar-loading',
+					handler: function() {
+						fileTree.getRootNode().reload();
+					},
+					scope: this
+				}, '-', {
+					iconCls: 'x-btn-upload',
+					tooltip: 'upload'
+				}, {
+					iconCls: 'x-btn-download',
+					tooltip: 'download'
+				}]
+			}, {
+
+				 region: 'center',
+
+				 layout: 'fit',
+				 margins: '0 0 0 0',
+				 cmargins: '0 0 0 0',
+				 border: false,
+				 items: [editArea],
+				 tbar: [{
+				 	iconCls: 'x-btn-filebrowser',
+					tooltip: 'open in new window',
+					ref: '../openWindowButton',
+					scope: this,
+					hidden: this.isWindow,
+					handler: function() {
+
+						var newEditor = new Ext.Window({
+							title: this.recordData.title + ' (' + this.recordData.extkey + ')',
+							width: 600,
+							height: 400,
+							layout: 'fit',
+							maximizable: true,
+							collapsible: true,
+							items: [{
+								xtype: 'extfilelist',
+								minHeight: 400,
+								recordData: this.recordData,
+								isWindow: true
+							}]
+						}).show();
+					}
+				 }, {
+					 xtype: 'tbseparator',
+					 hidden: this.isWindow
+				 }, {
+					iconCls: 'x-tbar-loading',
+					tooltip: 'reload the file',
+					ref: '../reloadButton',
+					scope: this,
+					hidden: true,
+					handler: function() {
+						this.layout.center.panel.reloadButton.disable();
+						TYPO3.EM.ExtDirect.readExtFile(fileTree.getSelectionModel().getSelectedNode().attributes.id , function(response) {
+							editArea.setValue(response);
+							this.layout.center.panel.reloadButton.enable();
+						}, this);
+					}
+				}, {
+					iconCls: 'x-btn-save',
+					tooltip: 'save',
+					ref: '../saveButton',
+					disabled: true,
+					scope: this,
+					handler: function() {
+						this.layout.center.panel.reloadButton.disable();
+						TYPO3.EM.ExtDirect.saveExtFile(
+							Ext.getComponent('extfiletree').getSelectionModel().getSelectedNode().attributes.id,
+							Ext.getComponent('editarea').getValue(),
+							function(response) {
+								this.layout.center.panel.saveButton.disable();
+								this.layout.center.panel.reloadButton.enable();
+						}, this);
+					}
+				}, '->', {
+					xtype: 'tbtext',
+					ref: '../fileLabel',
+					itemId: 'editarea-filename',
+					text: 'click on a file to load in editor ...'
+				}]
+			}]
+		});
+
+		TYPO3.EM.ExtFilelist.superclass.initComponent.apply(this, arguments);
+
+	},
+
+	fileClick: function(response) {
+		Ext.getComponent('editarea').setValue(response);
+	},
+
+	onRender: function() {
+		TYPO3.EM.ExtFilelist.superclass.onRender.apply(this, arguments);
+	}
+
+
+
+});
+
+// register xtype
+Ext.reg('extfilelist', TYPO3.EM.ExtFilelist);
Index: typo3/sysext/em/classes/extensions/class.tx_em_extensions_details.php
===================================================================
--- typo3/sysext/em/classes/extensions/class.tx_em_extensions_details.php	(revision 9674)
+++ typo3/sysext/em/classes/extensions/class.tx_em_extensions_details.php	(revision )
@@ -573,7 +573,7 @@
 
 			$headerCol = $GLOBALS['LANG']->getLL('extInfoArray_inst_type');
 			$headerCol = t3lib_BEfunc::wrapInHelp($this->parentObject->descrTable, 'emconf_type', $headerCol);
-			$dataCol = $this->api->typeLabels[$extInfo['type']] . ' - <em>' . $this->parentObject->typeDescr[$extInfo['type']] . '</em>';
+			$dataCol = $this->api->typeLabels[$extInfo['type']] . ' - <em>' . $this->api->typeDescr[$extInfo['type']] . '</em>';
 			$lines[] = array($headerCol, $dataCol);
 
 
@@ -665,7 +665,7 @@
 				if (!t3lib_div::inList($this->parentObject->nameSpaceExceptions, $extKey)) {
 					$dataCol = t3lib_utility_Debug::viewarray($techInfo['NSerrors']);
 				} else {
-					$dataCol = $GLOBALS['TBE_TEMPLATE']->dfw($GLOBALS['LANG']->getLL('extInfoArray_exception'));
+					$dataCol = tx_em_Tools::dfw($GLOBALS['LANG']->getLL('extInfoArray_exception'));
 				}
 
 			}
Index: typo3/sysext/em/ext_emconf.php
===================================================================
--- typo3/sysext/em/ext_emconf.php	(revision 9674)
+++ typo3/sysext/em/ext_emconf.php	(revision )
@@ -3,7 +3,7 @@
 ########################################################################
 # Extension Manager/Repository config file for ext "em".
 #
-# Auto generated 13-11-2010 01:00
+# Auto generated 01-12-2010 23:35
 #
 # Manual updates:
 # Only the data in the array - everything else is removed by next
@@ -33,8 +33,8 @@
 	'author_company' => '',
 	'CGLcompliance' => '',
 	'CGLcompliance_note' => '',
-	'version' => '2.0.0',
-	'_md5_values_when_last_written' => 'a:40:{s:16:"ext_autoload.php";s:4:"e526";s:12:"ext_icon.gif";s:4:"3a30";s:17:"ext_localconf.php";s:4:"fd30";s:14:"ext_tables.php";s:4:"c5d9";s:14:"ext_tables.sql";s:4:"4997";s:25:"ext_tables_static+adt.sql";s:4:"3c1b";s:16:"classes/conf.php";s:4:"a8a0";s:17:"classes/index.php";s:4:"808a";s:61:"classes/connection/class.tx_em_connection_extdirectserver.php";s:4:"50cf";s:50:"classes/connection/class.tx_em_connection_soap.php";s:4:"1623";s:49:"classes/connection/class.tx_em_connection_ter.php";s:4:"e818";s:41:"classes/database/class.tx_em_database.php";s:4:"67e9";s:54:"classes/exception/class.tx_em_connection_exception.php";s:4:"8b14";s:59:"classes/exception/class.tx_em_extensionimport_exception.php";s:4:"62e2";s:56:"classes/exception/class.tx_em_extensionxml_exception.php";s:4:"fcfe";s:53:"classes/exception/class.tx_em_mirrorxml_exception.php";s:4:"ad05";s:47:"classes/exception/class.tx_em_xml_exception.php";s:4:"3679";s:53:"classes/extensions/class.tx_em_extensions_details.php";s:4:"5b9b";s:50:"classes/extensions/class.tx_em_extensions_list.php";s:4:"2b68";s:59:"classes/import/class.tx_em_import_extensionlistimporter.php";s:4:"dd13";s:56:"classes/import/class.tx_em_import_mirrorlistimporter.php";s:4:"c512";s:39:"classes/install/class.tx_em_install.php";s:4:"c861";s:64:"classes/parser/class.tx_em_parser_extensionxmlabstractparser.php";s:4:"d268";s:60:"classes/parser/class.tx_em_parser_extensionxmlpullparser.php";s:4:"c604";s:60:"classes/parser/class.tx_em_parser_extensionxmlpushparser.php";s:4:"3bcc";s:61:"classes/parser/class.tx_em_parser_mirrorxmlabstractparser.php";s:4:"802b";s:57:"classes/parser/class.tx_em_parser_mirrorxmlpullparser.php";s:4:"1054";s:55:"classes/parser/class.tx_em_parser_xmlabstractparser.php";s:4:"822b";s:54:"classes/parser/class.tx_em_parser_xmlparserfactory.php";s:4:"ae4d";s:45:"classes/repository/class.tx_em_repository.php";s:4:"58c6";s:53:"classes/repository/class.tx_em_repository_mirrors.php";s:4:"dc40";s:53:"classes/repository/class.tx_em_repository_utility.php";s:4:"ac3a";s:41:"classes/settings/class.tx_em_settings.php";s:4:"aaf1";s:55:"classes/tasks/class.tx_em_tasks_updateextensionlist.php";s:4:"f022";s:35:"classes/tools/class.tx_em_tools.php";s:4:"fd03";s:41:"classes/tools/class.tx_em_tools_unzip.php";s:4:"ffe1";s:46:"classes/tools/class.tx_em_tools_xmlhandler.php";s:4:"48bb";s:49:"classes/translations/class.tx_em_translations.php";s:4:"7a8b";s:61:"interfaces/interface.tx_em_index_checkdatabaseupdateshook.php";s:4:"7178";s:22:"language/locallang.xml";s:4:"5d2d";}',
+	'version' => '2.0.2beta2',
+	'_md5_values_when_last_written' => 'a:91:{s:16:"ext_autoload.php";s:4:"55c9";s:12:"ext_icon.gif";s:4:"2cc2";s:17:"ext_localconf.php";s:4:"fd30";s:14:"ext_tables.php";s:4:"4ef6";s:14:"ext_tables.sql";s:4:"5a42";s:25:"ext_tables_static+adt.sql";s:4:"3c1b";s:27:"classes/class.tx_em_api.php";s:4:"7b62";s:31:"classes/class.tx_em_develop.php";s:4:"98bd";s:40:"classes/class.tx_em_extensionmanager.php";s:4:"7ccc";s:16:"classes/conf.php";s:4:"d842";s:17:"classes/index.php";s:4:"7117";s:61:"classes/connection/class.tx_em_connection_extdirectserver.php";s:4:"ac98";s:59:"classes/connection/class.tx_em_connection_extdirectsoap.php";s:4:"daf8";s:50:"classes/connection/class.tx_em_connection_soap.php";s:4:"d1f9";s:49:"classes/connection/class.tx_em_connection_ter.php";s:4:"3d38";s:41:"classes/database/class.tx_em_database.php";s:4:"e6c0";s:54:"classes/exception/class.tx_em_connection_exception.php";s:4:"8b14";s:59:"classes/exception/class.tx_em_extensionimport_exception.php";s:4:"62e2";s:56:"classes/exception/class.tx_em_extensionxml_exception.php";s:4:"fcfe";s:53:"classes/exception/class.tx_em_mirrorxml_exception.php";s:4:"ad05";s:47:"classes/exception/class.tx_em_xml_exception.php";s:4:"3679";s:53:"classes/extensions/class.tx_em_extensions_details.php";s:4:"ae75";s:50:"classes/extensions/class.tx_em_extensions_list.php";s:4:"f74a";s:59:"classes/import/class.tx_em_import_extensionlistimporter.php";s:4:"62c8";s:56:"classes/import/class.tx_em_import_mirrorlistimporter.php";s:4:"3ea7";s:39:"classes/install/class.tx_em_install.php";s:4:"0c5c";s:64:"classes/parser/class.tx_em_parser_extensionxmlabstractparser.php";s:4:"d268";s:60:"classes/parser/class.tx_em_parser_extensionxmlpullparser.php";s:4:"c604";s:60:"classes/parser/class.tx_em_parser_extensionxmlpushparser.php";s:4:"3bcc";s:61:"classes/parser/class.tx_em_parser_mirrorxmlabstractparser.php";s:4:"802b";s:57:"classes/parser/class.tx_em_parser_mirrorxmlpullparser.php";s:4:"1054";s:57:"classes/parser/class.tx_em_parser_mirrorxmlpushparser.php";s:4:"b5f5";s:55:"classes/parser/class.tx_em_parser_xmlabstractparser.php";s:4:"911a";s:54:"classes/parser/class.tx_em_parser_xmlparserfactory.php";s:4:"f56a";s:45:"classes/repository/class.tx_em_repository.php";s:4:"5dbd";s:53:"classes/repository/class.tx_em_repository_mirrors.php";s:4:"dc40";s:53:"classes/repository/class.tx_em_repository_utility.php";s:4:"886d";s:41:"classes/settings/class.tx_em_settings.php";s:4:"68fc";s:55:"classes/tasks/class.tx_em_tasks_updateextensionlist.php";s:4:"7e39";s:35:"classes/tools/class.tx_em_tools.php";s:4:"050f";s:41:"classes/tools/class.tx_em_tools_unzip.php";s:4:"ffe1";s:46:"classes/tools/class.tx_em_tools_xmlhandler.php";s:4:"48bb";s:49:"classes/translations/class.tx_em_translations.php";s:4:"7a8b";s:61:"interfaces/interface.tx_em_index_checkdatabaseupdateshook.php";s:4:"7178";s:22:"language/locallang.xml";s:4:"2732";s:17:"res/css/t3_em.css";s:4:"274a";s:20:"res/icons/cancel.png";s:4:"757a";s:22:"res/icons/download.png";s:4:"c5b2";s:19:"res/icons/drive.png";s:4:"9520";s:25:"res/icons/filebrowser.png";s:4:"25b9";s:18:"res/icons/flag.png";s:4:"8798";s:19:"res/icons/image.png";s:4:"82ab";s:21:"res/icons/install.gif";s:4:"8d57";s:19:"res/icons/oodoc.gif";s:4:"744b";s:20:"res/icons/server.png";s:4:"92ce";s:22:"res/icons/settings.png";s:4:"30a1";s:19:"res/icons/tools.png";s:4:"16d9";s:23:"res/icons/uninstall.gif";s:4:"a77f";s:16:"res/js/em_app.js";s:4:"38e3";s:23:"res/js/em_components.js";s:4:"6517";s:18:"res/js/em_files.js";s:4:"efa9";s:22:"res/js/em_languages.js";s:4:"b9b0";s:20:"res/js/em_layouts.js";s:4:"0c4f";s:22:"res/js/em_locallist.js";s:4:"e321";s:27:"res/js/em_repositorylist.js";s:4:"7096";s:21:"res/js/em_settings.js";s:4:"d657";s:16:"res/js/em_ter.js";s:4:"a237";s:18:"res/js/em_tools.js";s:4:"8b1b";s:22:"res/js/em_usertools.js";s:4:"37ce";s:33:"res/js/overrides/ext_overrides.js";s:4:"3bc1";s:24:"res/js/ux/GridFilters.js";s:4:"b42c";s:27:"res/js/ux/custom_plugins.js";s:4:"a27f";s:28:"res/js/ux/fileuploadfield.js";s:4:"06a5";s:29:"res/js/ux/rowpanelexpander.js";s:4:"fc07";s:24:"res/js/ux/searchfield.js";s:4:"41a1";s:29:"res/js/ux/css/GridFilters.css";s:4:"78fa";s:27:"res/js/ux/css/RangeMenu.css";s:4:"c5f6";s:33:"res/js/ux/filter/BooleanFilter.js";s:4:"d67f";s:30:"res/js/ux/filter/DateFilter.js";s:4:"1d6d";s:26:"res/js/ux/filter/Filter.js";s:4:"5e35";s:30:"res/js/ux/filter/ListFilter.js";s:4:"a9ab";s:33:"res/js/ux/filter/NumericFilter.js";s:4:"abb4";s:32:"res/js/ux/filter/StringFilter.js";s:4:"0923";s:27:"res/js/ux/images/equals.png";s:4:"87b7";s:25:"res/js/ux/images/find.png";s:4:"9f1c";s:33:"res/js/ux/images/greater_than.png";s:4:"746c";s:30:"res/js/ux/images/less_than.png";s:4:"2fb7";s:38:"res/js/ux/images/sort_filtered_asc.gif";s:4:"9e7a";s:39:"res/js/ux/images/sort_filtered_desc.gif";s:4:"6d59";s:26:"res/js/ux/menu/ListMenu.js";s:4:"d6c1";s:27:"res/js/ux/menu/RangeMenu.js";s:4:"cc46";};',
 	'constraints' => array(
 		'depends' => array(
 			'cms' => '',
Index: typo3/sysext/em/res/js/em_settings.js
===================================================================
--- typo3/sysext/em/res/js/em_settings.js	(revision )
+++ typo3/sysext/em/res/js/em_settings.js	(revision )
@@ -0,0 +1,484 @@
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+
+Ext.ns('TYPO3.EM');
+
+TYPO3.EM.Settings = Ext.extend(Ext.FormPanel, {
+	border: false,
+	labelWidth: 240,
+	bodyStyle: 'padding:5px 5px 0',
+
+
+	initComponent: function() {
+
+		this.repositoryStore = new Ext.data.DirectStore({
+			storeId: 'repositoriessettings',
+			directFn: TYPO3.EM.ExtDirect.getRepositories,
+			idProperty: 'uid',
+			root: 'data',
+			totalProperty: 'length',
+			fields : ['title', 'uid', 'updated', 'count', 'selected', 'description', 'wsdl_url', 'mirror_url'],
+			paramsAsHash: true,
+			listeners: {
+				load: function(store) {
+					if (this.isLoaded) {
+						record = store.getById(this.repSettingsCombo.getValue()).data;
+						this.repositoryInfo(record);
+					}
+				},
+				scope: this
+			}
+		});
+
+		this.repSettingsCombo = new Ext.form.ComboBox({
+			id: 'repSettingsCombo',
+			mode: 'local',
+			width: 300,
+			triggerAction: 'all',
+			forceSelection: true,
+			editable: false,
+			name: 'selectedRepository',
+			hiddenName: 'selectedRepository',
+			displayField: 'title',
+			valueField: 'uid',
+			store: this.repositoryStore,
+			fieldLabel: 'Select Repository',
+			listeners: {
+				scope: this,
+				select: function(comboBox, newValue, oldValue) {
+					this.repositoryInfo(newValue.data);
+					TYPO3.EM.ExtDirect.saveSetting('selectedRepository', newValue.data.uid);
+				}
+			}
+		});
+
+		var mirrorData = TYPO3.settings.EM.extMirrors;
+
+		this.mirrorStore = new Ext.data.DirectStore({
+			storeId: 'em-mirror-store',
+			directFn: TYPO3.EM.ExtDirect.getMirrors,
+			idProperty: 'host',
+			root: 'data',
+			totalProperty: 'length',
+			fields: [
+				{name : 'title'},
+				{name : 'country'},
+				{name : 'host'},
+				{name : 'path'},
+				{name : 'sponsor'},
+				{name : 'link'},
+				{name : 'logo'}
+			]
+		});
+
+
+
+		var mirrorSm  = new Ext.grid.CheckboxSelectionModel({
+			singleSelect: true,
+			header: '',
+			listeners: {
+				'selectionchange': function(selectionModel) {
+					var selectedMirror = '';
+					if (selectionModel.getSelected()) {
+						var sel = selectionModel.getSelected();
+						selectedMirror = sel.data.host;
+						this.getForm().setValues({selectedMirror: selectedMirror});
+					} else {
+						this.getForm().setValues({selectedMirror: ''});
+					}
+					TYPO3.EM.ExtDirect.saveSetting('selectedMirror', selectedMirror);
+				},
+				scope: this
+			}
+		});
+
+		var mirrorCm = new Ext.grid.ColumnModel([
+			mirrorSm,
+			{
+				id: 'mirror-title',
+				header: TYPO3.lang.mirror,
+				width: 200,
+				sortable: false,
+				menuDisabled: true,
+				fixed: true,
+				dataIndex: 'title',
+				hidable: false
+			},{
+				id: 'mirror-country',
+				header: TYPO3.lang.mirror_country,
+				width: 80,
+				sortable: false,
+				menuDisabled: true,
+				fixed: true,
+				dataIndex: 'country',
+				hidable: false
+			},{
+				id: 'mirror-host',
+				header: TYPO3.lang.mirror_url,
+				width: 180,
+				sortable: false,
+				menuDisabled: true,
+				fixed: true,
+				dataIndex: 'host',
+				hidable: false
+			},{
+				id: 'mirror-sponsor',
+				header: TYPO3.lang.mirror_sponsored_by,
+				width: 180,
+				sortable: false,
+				menuDisabled: true,
+				fixed: true,
+				dataIndex: 'sponsor',
+				hidable: false
+
+			},{
+				id: 'mirror-logo',
+				header: TYPO3.lang.mirror_logo_link,
+				width: 180,
+				sortable: false,
+				menuDisabled: true,
+				fixed: true,
+				dataIndex: 'logo',
+				hidable: false,
+				renderer: function(value, metaData, record, rowIndex, colIndex, store) {
+					if (value == '') {
+						return ''
+					} else {
+						return '<a href="' + record.data.link + '" title="' + record.data.sponsor + '" target="_blank"><img src="' + record.data.logo + '" alt="' + record.data.sponsor + '" title="' + record.data.sponsor + '" /></a>';
+					}
+				}
+			}
+		]);
+
+		Ext.apply(this, {
+			isLoaded: false,
+			items: [{
+				layout: 'hbox',
+				align: 'stretchmax',
+				border: false,
+				items: [{
+					width: 450,
+					border: false,
+					labelWidth: 100,
+					items: [{
+							xtype:'fieldset',
+							title: TYPO3.lang.user_settings,
+							collapsible: true,
+							defaults: {},
+							defaultType: 'textfield',
+							items :[
+							{
+								fieldLabel: TYPO3.lang.enter_repository_username,
+								name: 'fe_u'
+							}, {
+								fieldLabel: TYPO3.lang.enter_repository_password,
+								inputType: 'password',
+								name: 'fe_p'
+							},
+								new Ext.Container({
+									html: '<b>' + TYPO3.lang.notice + '</b> ' + TYPO3.lang.repository_password_info,
+									xtype: 'displayfield',
+									labelWidth: 1
+								})
+							],
+							buttons: [
+								{
+									text: TYPO3.lang.cmd_save,
+									iconCls: 'x-btn-save',
+									handler: function() {
+										this.saveFormHandler();
+									},
+									scope: this
+								}
+							]
+						}, {
+							xtype:'fieldset',
+							title: TYPO3.lang.repositories,
+							collapsible: true,
+							defaultType: 'textfield',
+							items :[
+								this.repSettingsCombo,
+							{
+								title: TYPO3.lang.repository_details,
+								xtype: 'panel',
+								id: 'repDescriptionDisplay',
+								record: null,
+								labelWidth: 0,
+								width: 420,
+								html: '',
+								bodyStyle: 'padding: 10px;',
+								buttons: [{
+									text: TYPO3.lang.cmd_edit,
+									iconCls: 'x-btn-edit',
+									ref: '../editRep',
+									handler: function() {
+										var record = this.repositoryStore.getById(this.repSettingsCombo.getValue());
+										var win = new TYPO3.EM.EditRepository({
+											title: 'Edit Repository "' + record.data.title + '"'
+										});
+										win.getComponent('repForm').getForm().setValues({
+											'title': record.data.title,
+											'description': record.data.description,
+											'wsdl_url': record.data.wsdl_url,
+											'mirror_url': record.data.mirror_url,
+											'rep':  record.data.uid
+										});
+										win.show();
+									},
+									scope: this
+								}, ' ', {
+									text: TYPO3.lang.cmd_create,
+									iconCls: 'x-btn-new',
+									ref: '../newRep',
+									handler: function() {
+										var win = new TYPO3.EM.EditRepository({
+											isCreate: true,
+											title: 'Create new Repository'
+										}).show();
+									},
+									scope: this
+								}, ' ', {
+									text: TYPO3.lang.cmd_delete,
+									iconCls: 'x-btn-delete',
+									ref: '../deleteRep'
+								}]
+							}]
+						}]
+					}, {
+						flex: 1,
+						border: false,
+						items: [{
+							xtype:'fieldset',
+							title: TYPO3.lang.mirror_selection,
+							collapsible: true,
+							defaults: {},
+							items :[{
+								xtype: 'grid',
+								id: 'em-mirrorgrid',
+								stripeRows: true,
+								store: this.mirrorStore,
+								cm: mirrorCm,
+								sm: mirrorSm,
+								viewConfig: {
+									forceFit: true
+								},
+								height: 450
+							},{
+								xtype: 'hidden',
+								name: 'selectedMirror'
+							}]
+						}]
+					}]
+				}]
+		});
+
+
+		// call parent
+		TYPO3.EM.Settings.superclass.initComponent.apply(this, arguments);
+
+	} ,
+
+	saveFormHandler: function() {
+		this.getForm().submit({
+			waitMsg : 'Saving Settings...',
+			success: function(form, action) {
+				TYPO3.Flashmessage.display(TYPO3.Severity.information,'Settings', 'Settings were saved ', 5);
+				TYPO3.settings.EM.hasCredentials = (action.result.data.fe_u !== '' && action.result.data.fe_p !== '')
+			},
+			failure: function(form, action) {
+				if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
+					TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Error',
+							'Status:'+action.response.status+': '+
+							action.response.statusText, 5);
+				}
+				if (action.failureType === Ext.form.Action.SERVER_INVALID){
+					// server responded with success = false
+					TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Invalid', action.result.errormsg, 5);
+				}
+			}
+
+		});
+	},
+
+	repositoryInfo: function(record) {
+		var panel = Ext.getCmp('repDescriptionDisplay');
+		panel.update([
+			'<h1 class="h1Panel">',record.title, '</h1>',
+			'<p class="panelDescription">', record.description, '</p>',
+			'<p><b>', 'Mirror URL: ', '</b>', record.mirror_url, '<br />',
+			'<b>', 'WSDL URL: ', '</b>', record.wsdl_url, '</p>'
+		].join(''));
+		if (record.uid == 1) {
+			panel.editRep.disable();
+			panel.deleteRep.disable();
+		} else {
+			panel.editRep.enable();
+			panel.deleteRep.enable();
+		}
+		this.mirrorStore.load({
+			params: {
+				repository: this.repSettingsCombo.getValue()
+			},
+			callback: function() {
+				var mirror = this.getForm().getValues().selectedMirror;
+				if (mirror) {
+					var record = this.mirrorStore.getAt(this.mirrorStore.find('host', mirror));
+					Ext.getCmp('em-mirrorgrid').getSelectionModel().selectRecords([record]);
+				} else {
+					Ext.getCmp('em-mirrorgrid').getSelectionModel().selectFirstRow();
+				}
+			},
+			scope: this
+		});
+
+	},
+
+
+	onRender:function() {
+
+		// call parent
+		TYPO3.EM.Settings.superclass.onRender.apply(this, arguments);
+
+		Ext.apply(this.getForm(),{
+			api: {
+				load: TYPO3.EM.ExtDirect.settingsFormLoad,
+				submit: TYPO3.EM.ExtDirect.settingsFormSubmit
+			},
+			paramsAsHash: false
+		});
+
+		this.repositoryStore.load();
+		this.getForm().load({
+			success: function(form, response) {
+				record = this.repositoryStore.getById(this.repSettingsCombo.getValue()).data;
+				this.repositoryInfo(record);
+				this.isLoaded = true;
+			},
+			scope: this
+		});
+
+	}
+
+
+
+
+});
+
+// register xtype
+Ext.reg('extsettings', TYPO3.EM.Settings);
+
+// window with repository edit/create form
+TYPO3.EM.EditRepository = Ext.extend(Ext.Window, {
+	isCreate: false,
+	width: 500,
+	height: 260,
+	layout: 'fit',
+	frame: true,
+	resizable: false,
+	modal: true,
+	caller: null,
+	initComponent : function() {
+		var form = new Ext.form.FormPanel({
+			//baseCls: 'x-plain',
+			border: false,
+			labelWidth: 80,
+			itemId: 'repForm',
+			bodyStyle:'padding:5px 5px 0',
+			width: 350,
+			defaults: {width: 380},
+			defaultType: 'textfield',
+			api: {
+				submit: TYPO3.EM.ExtDirect.repositoryEditFormSubmit
+			},
+			paramsAsHash: false,
+			items: [{
+				itemId: 'title',
+				fieldLabel: 'Title',
+				name: 'title'
+			}, {
+				itemId: 'description',
+				fieldLabel: 'Description',
+				xtype: 'textarea',
+				name: 'description',
+				height: 100
+			}, {
+				itemId: 'mirror_url',
+				fieldLabel: 'Mirror URL',
+				name: 'mirror_url'
+			}, {
+				itemId: 'wsdl_url',
+				fieldLabel: 'WSDL URL',
+				name: 'wsdl_url'
+			}, {
+				xtype: 'hidden',
+				name: 'create',
+				value: this.isCreate ? 1 : 0
+			}, {
+				xtype: 'hidden',
+				name: 'rep',
+				value: 0
+			}]
+		});
+
+		Ext.apply(this, {
+			items: form,
+			buttons : [{
+				text: 'Create',
+				iconCls: 'x-btn-save',
+				handler: function() {
+					this.repositoryUpdate(form, 1);
+				},
+				hidden: !this.isCreate,
+				scope: this
+			}, {
+				text: 'Update',
+				iconCls: 'x-btn-save',
+				handler: function() {
+				  this.repositoryUpdate(form, 0);
+				},
+				hidden: this.isCreate,
+				scope: this
+			}, {
+				text: 'Cancel',
+				iconCls: 'x-btn-cancel',
+				handler: function() {
+					this.close();
+				},
+				scope: this
+			}]
+		});
+		TYPO3.EM.EditRepository.superclass.initComponent.call(this);
+	},
+
+	repositoryUpdate: function(form, type) {
+
+		form.getForm().submit({
+			waitMsg : type === 0 ? 'Saving Repository...' : 'Creating Repository ...',
+			success: function(form, action) {
+				TYPO3.Flashmessage.display(TYPO3.Severity.information,'Repository', 'Repository was ' + (type == 0 ? 'saved.' : 'created.'), 5);
+				Ext.StoreMgr.get('repositoriessettings').load();
+				this.close();
+			},
+			failure: function(form, action) {
+				if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
+					TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Error',
+							'Status:'+action.response.status+': '+
+							action.response.statusText, 5);
+				}
+				if (action.failureType === Ext.form.Action.SERVER_INVALID){
+					// server responded with success = false
+					TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Invalid', action.result.errormsg, 15);
+				}
+			},
+			scope: this
+		});
+	}
+
+});
Index: typo3/sysext/em/res/js/ux/menu/RangeMenu.js
===================================================================
--- typo3/sysext/em/res/js/ux/menu/RangeMenu.js	(revision )
+++ typo3/sysext/em/res/js/ux/menu/RangeMenu.js	(revision )
@@ -0,0 +1,128 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+Ext.ns('Ext.ux.menu');
+
+/** 
+ * @class Ext.ux.menu.RangeMenu
+ * @extends Ext.menu.Menu
+ * Custom implementation of Ext.menu.Menu that has preconfigured
+ * items for gt, lt, eq.
+ * <p><b><u>Example Usage:</u></b></p>
+ * <pre><code>    
+
+ * </code></pre> 
+ */
+Ext.ux.menu.RangeMenu = Ext.extend(Ext.menu.Menu, {
+
+    constructor : function (config) {
+
+        Ext.ux.menu.RangeMenu.superclass.constructor.call(this, config);
+
+        this.addEvents(
+            /**
+             * @event update
+             * Fires when a filter configuration has changed
+             * @param {Ext.ux.grid.filter.Filter} this The filter object.
+             */
+            'update'
+        );
+      
+        this.updateTask = new Ext.util.DelayedTask(this.fireUpdate, this);
+    
+        var i, len, item, cfg, Cls;
+
+        for (i = 0, len = this.menuItems.length; i < len; i++) {
+            item = this.menuItems[i];
+            if (item !== '-') {
+                // defaults
+                cfg = {
+                    itemId: 'range-' + item,
+                    enableKeyEvents: true,
+                    iconCls: this.iconCls[item] || 'no-icon',
+                    listeners: {
+                        scope: this,
+                        keyup: this.onInputKeyUp
+                    }
+                };
+                Ext.apply(
+                    cfg,
+                    // custom configs
+                    Ext.applyIf(this.fields[item] || {}, this.fieldCfg[item]),
+                    // configurable defaults
+                    this.menuItemCfgs
+                );
+                Cls = cfg.fieldCls || this.fieldCls;
+                item = this.fields[item] = new Cls(cfg);
+            }
+            this.add(item);
+        }
+    },
+
+    /**
+     * @private
+     * called by this.updateTask
+     */
+    fireUpdate : function () {
+        this.fireEvent('update', this);
+    },
+    
+    /**
+     * Get and return the value of the filter.
+     * @return {String} The value of this filter
+     */
+    getValue : function () {
+        var result = {}, key, field;
+        for (key in this.fields) {
+            field = this.fields[key];
+            if (field.isValid() && String(field.getValue()).length > 0) {
+                result[key] = field.getValue();
+            }
+        }
+        return result;
+    },
+  
+    /**
+     * Set the value of this menu and fires the 'update' event.
+     * @param {Object} data The data to assign to this menu
+     */	
+    setValue : function (data) {
+        var key;
+        for (key in this.fields) {
+            this.fields[key].setValue(data[key] !== undefined ? data[key] : '');
+        }
+        this.fireEvent('update', this);
+    },
+
+    /**  
+     * @private
+     * Handler method called when there is a keyup event on an input
+     * item of this menu.
+     */
+    onInputKeyUp : function (field, e) {
+        var k = e.getKey();
+        if (k == e.RETURN && field.isValid()) {
+            e.stopEvent();
+            this.hide(true);
+            return;
+        }
+        
+        if (field == this.fields.eq) {
+            if (this.fields.gt) {
+                this.fields.gt.setValue(null);
+            }
+            if (this.fields.lt) {
+                this.fields.lt.setValue(null);
+            }
+        }
+        else {
+            this.fields.eq.setValue(null);
+        }
+        
+        // restart the timer
+        this.updateTask.delay(this.updateBuffer);
+    }
+});
Index: typo3/sysext/em/res/js/ux/css/RangeMenu.css
===================================================================
--- typo3/sysext/em/res/js/ux/css/RangeMenu.css	(revision )
+++ typo3/sysext/em/res/js/ux/css/RangeMenu.css	(revision )
@@ -0,0 +1,20 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+/**
+ * RangeMenu Styles
+ **/
+.ux-rangemenu-gt {
+	background-image: url(../images/greater_than.png) !important;
+}
+
+.ux-rangemenu-lt {
+	background-image: url(../images/less_than.png) !important;
+}
+
+.ux-rangemenu-eq {
+	background-image: url(../images/equals.png) !important;
+}
Index: typo3/sysext/em/res/js/ux/filter/ListFilter.js
===================================================================
--- typo3/sysext/em/res/js/ux/filter/ListFilter.js	(revision )
+++ typo3/sysext/em/res/js/ux/filter/ListFilter.js	(revision )
@@ -0,0 +1,176 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+/** 
+ * @class Ext.ux.grid.filter.ListFilter
+ * @extends Ext.ux.grid.filter.Filter
+ * <p>List filters are able to be preloaded/backed by an Ext.data.Store to load
+ * their options the first time they are shown. ListFilter utilizes the
+ * {@link Ext.ux.menu.ListMenu} component.</p>
+ * <p>Although not shown here, this class accepts all configuration options
+ * for {@link Ext.ux.menu.ListMenu}.</p>
+ * 
+ * <p><b><u>Example Usage:</u></b></p>
+ * <pre><code>    
+var filters = new Ext.ux.grid.GridFilters({
+    ...
+    filters: [{
+        type: 'list',
+        dataIndex: 'size',
+        phpMode: true,
+        // options will be used as data to implicitly creates an ArrayStore
+        options: ['extra small', 'small', 'medium', 'large', 'extra large']
+    }]
+});
+ * </code></pre>
+ * 
+ */
+Ext.ux.grid.filter.ListFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
+
+    /**
+     * @cfg {Array} options
+     * <p><code>data</code> to be used to implicitly create a data store
+     * to back this list when the data source is <b>local</b>. If the
+     * data for the list is remote, use the <code>{@link #store}</code>
+     * config instead.</p>
+     * <br><p>Each item within the provided array may be in one of the
+     * following formats:</p>
+     * <div class="mdetail-params"><ul>
+     * <li><b>Array</b> :
+     * <pre><code>
+options: [
+    [11, 'extra small'], 
+    [18, 'small'],
+    [22, 'medium'],
+    [35, 'large'],
+    [44, 'extra large']
+]
+     * </code></pre>
+     * </li>
+     * <li><b>Object</b> :
+     * <pre><code>
+labelField: 'name', // override default of 'text'
+options: [
+    {id: 11, name:'extra small'}, 
+    {id: 18, name:'small'}, 
+    {id: 22, name:'medium'}, 
+    {id: 35, name:'large'}, 
+    {id: 44, name:'extra large'} 
+]
+     * </code></pre>
+     * </li>
+     * <li><b>String</b> :
+     * <pre><code>
+     * options: ['extra small', 'small', 'medium', 'large', 'extra large']
+     * </code></pre>
+     * </li>
+     */
+    /**
+     * @cfg {Boolean} phpMode
+     * <p>Adjust the format of this filter. Defaults to false.</p>
+     * <br><p>When GridFilters <code>@cfg encode = false</code> (default):</p>
+     * <pre><code>
+// phpMode == false (default):
+filter[0][data][type] list
+filter[0][data][value] value1
+filter[0][data][value] value2
+filter[0][field] prod 
+
+// phpMode == true:
+filter[0][data][type] list
+filter[0][data][value] value1, value2
+filter[0][field] prod 
+     * </code></pre>
+     * When GridFilters <code>@cfg encode = true</code>:
+     * <pre><code>
+// phpMode == false (default):
+filter : [{"type":"list","value":["small","medium"],"field":"size"}]
+
+// phpMode == true:
+filter : [{"type":"list","value":"small,medium","field":"size"}]
+     * </code></pre>
+     */
+    phpMode : false,
+    /**
+     * @cfg {Ext.data.Store} store
+     * The {@link Ext.data.Store} this list should use as its data source
+     * when the data source is <b>remote</b>. If the data for the list
+     * is local, use the <code>{@link #options}</code> config instead.
+     */
+
+    /**  
+     * @private
+     * Template method that is to initialize the filter and install required menu items.
+     * @param {Object} config
+     */
+    init : function (config) {
+        this.dt = new Ext.util.DelayedTask(this.fireUpdate, this);
+
+        // if a menu already existed, do clean up first
+        if (this.menu){
+            this.menu.destroy();
+        }
+        this.menu = new Ext.ux.menu.ListMenu(config);
+        this.menu.on('checkchange', this.onCheckChange, this);
+    },
+    
+    /**
+     * @private
+     * Template method that is to get and return the value of the filter.
+     * @return {String} The value of this filter
+     */
+    getValue : function () {
+        return this.menu.getSelected();
+    },
+    /**
+     * @private
+     * Template method that is to set the value of the filter.
+     * @param {Object} value The value to set the filter
+     */	
+    setValue : function (value) {
+        this.menu.setSelected(value);
+        this.fireEvent('update', this);
+    },
+
+    /**
+     * @private
+     * Template method that is to return <tt>true</tt> if the filter
+     * has enough configuration information to be activated.
+     * @return {Boolean}
+     */
+    isActivatable : function () {
+        return this.getValue().length > 0;
+    },
+    
+    /**
+     * @private
+     * Template method that is to get and return serialized filter data for
+     * transmission to the server.
+     * @return {Object/Array} An object or collection of objects containing
+     * key value pairs representing the current configuration of the filter.
+     */
+    getSerialArgs : function () {
+        var args = {type: 'list', value: this.phpMode ? this.getValue().join(',') : this.getValue()};
+        return args;
+    },
+
+    /** @private */
+    onCheckChange : function(){
+        this.dt.delay(this.updateBuffer);
+    },
+    
+    
+    /**
+     * Template method that is to validate the provided Ext.data.Record
+     * against the filters configuration.
+     * @param {Ext.data.Record} record The record to validate
+     * @return {Boolean} true if the record is valid within the bounds
+     * of the filter, false otherwise.
+     */
+    validateRecord : function (record) {
+        return this.getValue().indexOf(record.get(this.dataIndex)) > -1;
+    }
+});
\ No newline at end of file
Index: typo3/sysext/em/ext_autoload.php
===================================================================
--- typo3/sysext/em/ext_autoload.php	(revision 9674)
+++ typo3/sysext/em/ext_autoload.php	(revision )
@@ -51,8 +51,8 @@
 	'tx_em_translations' => $emClassesPath . 'translations/class.tx_em_translations.php',
 
 
-	//'tx_em_develop' => $emClassesPath . 'class.tx_em_develop.php',
-	//'tx_em_extensionmanager' => $emClassesPath . 'class.tx_em_extensionmanager.php',
+	'tx_em_develop' => $emClassesPath . 'class.tx_em_develop.php',
+	'tx_em_extensionmanager' => $emClassesPath . 'class.tx_em_extensionmanager.php',
 
 );
 ?>
Index: typo3/sysext/em/classes/tools/class.tx_em_tools.php
===================================================================
--- typo3/sysext/em/classes/tools/class.tx_em_tools.php	(revision 9674)
+++ typo3/sysext/em/classes/tools/class.tx_em_tools.php	(revision )
@@ -71,6 +71,31 @@
 	);
 
 	/**
+	 * Colors for states
+	 *
+	 * @var array
+	 */
+	protected static $stateColors = array(
+		'alpha' => '#d12438',
+		'beta' => '#97b17e',
+		'stable' => '#3bb65c',
+		'experimental' => '#007eba',
+		'test' => '#979797',
+		'obsolete' => '#000000',
+		'excludeFromUpdates' => '#cf7307'
+	);
+
+	/**
+	 * Gets the stateColor array
+	 *
+	 * @static
+	 * @return array
+	 */
+	public static function getStateColors() {
+		return self::$stateColors;
+	}
+
+	/**
 	 * Unzips a zip file in the given path.
 	 *
 	 * Uses unzip binary if available, otherwise a pure PHP unzip is used.
Index: typo3/sysext/em/res/js/ux/filter/NumericFilter.js
===================================================================
--- typo3/sysext/em/res/js/ux/filter/NumericFilter.js	(revision )
+++ typo3/sysext/em/res/js/ux/filter/NumericFilter.js	(revision )
@@ -0,0 +1,202 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+/** 
+ * @class Ext.ux.grid.filter.NumericFilter
+ * @extends Ext.ux.grid.filter.Filter
+ * Filters using an Ext.ux.menu.RangeMenu.
+ * <p><b><u>Example Usage:</u></b></p>
+ * <pre><code>    
+var filters = new Ext.ux.grid.GridFilters({
+    ...
+    filters: [{
+        type: 'numeric',
+        dataIndex: 'price'
+    }]
+});
+ * </code></pre> 
+ */
+Ext.ux.grid.filter.NumericFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
+
+    /**
+     * @cfg {Object} fieldCls
+     * The Class to use to construct each field item within this menu
+     * Defaults to:<pre>
+     * fieldCls : Ext.form.NumberField
+     * </pre>
+     */
+    fieldCls : Ext.form.NumberField,
+    /**
+     * @cfg {Object} fieldCfg
+     * The default configuration options for any field item unless superseded
+     * by the <code>{@link #fields}</code> configuration.
+     * Defaults to:<pre>
+     * fieldCfg : {}
+     * </pre>
+     * Example usage:
+     * <pre><code>
+fieldCfg : {
+    width: 150,
+},
+     * </code></pre>
+     */
+    /**
+     * @cfg {Object} fields
+     * The field items may be configured individually
+     * Defaults to <tt>undefined</tt>.
+     * Example usage:
+     * <pre><code>
+fields : {
+    gt: { // override fieldCfg options
+        width: 200,
+        fieldCls: Ext.ux.form.CustomNumberField // to override default {@link #fieldCls}
+    }
+},
+     * </code></pre>
+     */
+    /**
+     * @cfg {Object} iconCls
+     * The iconCls to be applied to each comparator field item.
+     * Defaults to:<pre>
+iconCls : {
+    gt : 'ux-rangemenu-gt',
+    lt : 'ux-rangemenu-lt',
+    eq : 'ux-rangemenu-eq'
+}
+     * </pre>
+     */
+    iconCls : {
+        gt : 'ux-rangemenu-gt',
+        lt : 'ux-rangemenu-lt',
+        eq : 'ux-rangemenu-eq'
+    },
+
+    /**
+     * @cfg {Object} menuItemCfgs
+     * Default configuration options for each menu item
+     * Defaults to:<pre>
+menuItemCfgs : {
+    emptyText: 'Enter Filter Text...',
+    selectOnFocus: true,
+    width: 125
+}
+     * </pre>
+     */
+    menuItemCfgs : {
+        emptyText: 'Enter Filter Text...',
+        selectOnFocus: true,
+        width: 125
+    },
+
+    /**
+     * @cfg {Array} menuItems
+     * The items to be shown in this menu.  Items are added to the menu
+     * according to their position within this array. Defaults to:<pre>
+     * menuItems : ['lt','gt','-','eq']
+     * </pre>
+     */
+    menuItems : ['lt', 'gt', '-', 'eq'],
+
+    /**  
+     * @private
+     * Template method that is to initialize the filter and install required menu items.
+     */
+    init : function (config) {
+        // if a menu already existed, do clean up first
+        if (this.menu){
+            this.menu.destroy();
+        }        
+        this.menu = new Ext.ux.menu.RangeMenu(Ext.apply(config, {
+            // pass along filter configs to the menu
+            fieldCfg : this.fieldCfg || {},
+            fieldCls : this.fieldCls,
+            fields : this.fields || {},
+            iconCls: this.iconCls,
+            menuItemCfgs: this.menuItemCfgs,
+            menuItems: this.menuItems,
+            updateBuffer: this.updateBuffer
+        }));
+        // relay the event fired by the menu
+        this.menu.on('update', this.fireUpdate, this);
+    },
+    
+    /**
+     * @private
+     * Template method that is to get and return the value of the filter.
+     * @return {String} The value of this filter
+     */
+    getValue : function () {
+        return this.menu.getValue();
+    },
+
+    /**
+     * @private
+     * Template method that is to set the value of the filter.
+     * @param {Object} value The value to set the filter
+     */	
+    setValue : function (value) {
+        this.menu.setValue(value);
+    },
+
+    /**
+     * @private
+     * Template method that is to return <tt>true</tt> if the filter
+     * has enough configuration information to be activated.
+     * @return {Boolean}
+     */
+    isActivatable : function () {
+        var values = this.getValue();
+        for (key in values) {
+            if (values[key] !== undefined) {
+                return true;
+            }
+        }
+        return false;
+    },
+    
+    /**
+     * @private
+     * Template method that is to get and return serialized filter data for
+     * transmission to the server.
+     * @return {Object/Array} An object or collection of objects containing
+     * key value pairs representing the current configuration of the filter.
+     */
+    getSerialArgs : function () {
+        var key,
+            args = [],
+            values = this.menu.getValue();
+        for (key in values) {
+            args.push({
+                type: 'numeric',
+                comparison: key,
+                value: values[key]
+            });
+        }
+        return args;
+    },
+
+    /**
+     * Template method that is to validate the provided Ext.data.Record
+     * against the filters configuration.
+     * @param {Ext.data.Record} record The record to validate
+     * @return {Boolean} true if the record is valid within the bounds
+     * of the filter, false otherwise.
+     */
+    validateRecord : function (record) {
+        var val = record.get(this.dataIndex),
+            values = this.getValue();
+        if (values.eq !== undefined && val != values.eq) {
+            return false;
+        }
+        if (values.lt !== undefined && val >= values.lt) {
+            return false;
+        }
+        if (values.gt !== undefined && val <= values.gt) {
+            return false;
+        }
+        return true;
+    }
+});
\ No newline at end of file
Index: typo3/sysext/em/res/js/overrides/ext_overrides.js
===================================================================
--- typo3/sysext/em/res/js/overrides/ext_overrides.js	(revision )
+++ typo3/sysext/em/res/js/overrides/ext_overrides.js	(revision )
@@ -0,0 +1,206 @@
+/**
+ * ExtJS for the extension manager.
+ * 
+ * 
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+ 
+/***********************************/
+/*	Ext.data.Store                 */
+/***********************************/
+ Ext.override(Ext.data.Store, {
+    add : function(records){
+        records = [].concat(records);
+        if(records.length < 1){
+            return;
+        }
+        for(var i = 0, len = records.length; i < len; i++){
+            records[i].join(this);
+        }
+        var index = this.data.length;
+        this.data.addAll(records);
+        if(this.snapshot){
+            this.snapshot.addAll(records);
+        }
+        this.totalLength += records.length;
+        this.fireEvent('add', this, records, index);
+    },
+    remove : function(record){
+        if(Ext.isArray(record)){
+            Ext.each(record, function(r){
+                this.remove(r);
+            }, this);
+        }
+        var index = this.data.indexOf(record);
+        if(index > -1){
+            record.join(null);
+            this.data.removeAt(index);
+            if(this.pruneModifiedRecords){
+                this.modified.remove(record);
+            }
+            if(this.snapshot){
+                this.snapshot.remove(record);
+            }
+            this.totalLength--;
+            this.fireEvent('remove', this, record, index);
+        }
+    },
+    removeAll : function(){
+        var items = [];
+        this.each(function(rec){
+            items.push(rec);
+        });
+        this.clearData();
+        if(this.snapshot){
+            this.snapshot.clear();
+        }
+        if(this.pruneModifiedRecords){
+            this.modified = [];
+        }
+        this.totalLength = 0;
+        this.fireEvent('clear', this, items);
+    },
+    addField: function(field){
+		field = new Ext.data.Field(field);
+		this.recordType.prototype.fields.replace(field);
+		if(typeof field.defaultValue != 'undefined'){
+			this.each(function(r){
+				if(typeof r.data[field.name] == 'undefined'){
+					r.data[field.name] = field.defaultValue;
+				}
+			});
+		}
+	},
+	removeField: function(name){
+		this.recordType.prototype.fields.removeKey(name);
+		this.each(function(r){
+			delete r.data[name];
+			if(r.modified){
+				delete r.modified[name];
+			}
+		});
+	}
+});
+/***********************************/
+/*	Ext.PagingToolbar              */
+/***********************************/
+ 
+Ext.override(Ext.PagingToolbar, {
+    bindStore : function(store, initial){
+        var doLoad;
+        if(!initial && this.store){
+            if(store !== this.store && this.store.autoDestroy){
+                this.store.destroy();
+            }else{
+                this.store.un('beforeload', this.beforeLoad, this);
+                this.store.un('load', this.onLoad, this);
+                this.store.un('exception', this.onLoadError, this);
+                this.store.un('datachanged', this.onChange, this);
+                this.store.un('add', this.onChange, this);
+                this.store.un('remove', this.onChange, this);
+                this.store.un('clear', this.onClear, this);
+            }
+            if(!store){
+                this.store = null;
+            }
+        }
+        if(store){
+            store = Ext.StoreMgr.lookup(store);
+            store.on({
+                scope: this,
+                beforeload: this.beforeLoad,
+                load: this.onLoad,
+                exception: this.onLoadError,
+                datachanged: this.onChange,
+                add: this.onChange,
+                remove: this.onChange,
+                clear: this.onClear
+            });
+            doLoad = true;
+        }
+        this.store = store;
+        if(doLoad){
+            this.onLoad(store, null, {});
+        }
+    },
+    onLoad : function(store, r, o){
+        if(!this.rendered){
+            this.dsLoaded = [store, r, o];
+            return;
+        }
+        var p = this.getParams();
+        this.cursor = (o.params && o.params[p.start]) ? o.params[p.start] : 0;
+        this.onChange();
+    },
+    onChange : function(){
+        if(this.rendered){
+            var d = this.getPageData(), ap = d.activePage, ps = d.pages;
+            this.afterTextItem.setText(String.format(this.afterPageText, d.pages));
+            this.inputItem.setValue(ap);
+            this.first.setDisabled(ap == 1);
+            this.prev.setDisabled(ap == 1);
+            this.next.setDisabled(ap == ps);
+            this.last.setDisabled(ap == ps);
+            this.refresh.enable();
+            this.updateInfo();
+        }
+        this.fireEvent('change', this, d);
+    },
+    onClear : function(){
+        this.cursor = 0;
+        this.onChange();
+    }
+});
+
+/***********************************/
+/*	Ext.grid.ColumnModel           */
+/***********************************/
+ 
+Ext.override(Ext.grid.ColumnModel,{
+	addColumn: function(column, colIndex){
+		if(typeof column == 'string'){
+			column = {header: column, dataIndex: column};
+		}
+		var config = this.config;
+		this.config = [];
+		if(typeof colIndex == 'number'){
+			config.splice(colIndex, 0, column);
+		}else{
+			colIndex = config.push(column);
+		}
+		this.setConfig(config);
+		return colIndex;
+	},
+	removeColumn: function(colIndex){
+		var config = this.config;
+		this.config = [config[colIndex]];
+		config.splice(colIndex, 1);
+		this.setConfig(config);
+	}
+});
+Ext.override(Ext.grid.GridPanel,{
+	addColumn: function(field, column, colIndex){
+		if(!column){
+			if(field.dataIndex){
+				column = field;
+				field = field.dataIndex;
+			} else{
+				column = field.name || field;
+			}
+		}
+		this.store.addField(field);
+		return this.colModel.addColumn(column, colIndex);
+	},
+	removeColumn: function(name, colIndex){
+		this.store.removeField(name);
+		if(typeof colIndex != 'number'){
+			colIndex = this.colModel.findColumnIndex(name);
+		}
+		if(colIndex >= 0){
+			this.colModel.removeColumn(colIndex);
+		}
+	}
+});
\ No newline at end of file
Index: typo3/sysext/em/res/js/ux/css/GridFilters.css
===================================================================
--- typo3/sysext/em/res/js/ux/css/GridFilters.css	(revision )
+++ typo3/sysext/em/res/js/ux/css/GridFilters.css	(revision )
@@ -0,0 +1,54 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+/**
+ * GridFilters Styles
+ **/
+/*
+.x-grid3-hd-row .ux-filtered-column {
+	border-left:  1px solid #C7E3B4;
+	border-right: 1px solid #C7E3B4;
+}
+
+.x-grid3-hd-row .ux-filtered-column .x-grid3-hd-inner {
+	background-image: url(../images/header_bg.gif);
+}
+
+.ux-filtered-column .x-grid3-hd-btn {
+	background-image: url(../images/hd-btn.gif);
+}
+*/
+.x-grid3-hd-row td.ux-filtered-column {   
+    font-style: italic;  
+    font-weight: bold;
+}		
+
+.ux-filtered-column.sort-asc .x-grid3-sort-icon {
+	background-image: url(../images/sort_filtered_asc.gif) !important;
+}
+
+.ux-filtered-column.sort-desc .x-grid3-sort-icon {
+	background-image: url(../images/sort_filtered_desc.gif) !important;
+}
+
+.ux-gridfilter-text-icon {
+	background-image: url(../images/find.png) !important;
+}
+
+/* Temporary Patch for Bug ??? */
+.x-menu-list-item-indent .x-menu-item-icon {
+	position: relative;
+    top: 3px;
+    left: 3px;
+	margin-right: 10px;
+}
+li.x-menu-list-item-indent {
+	padding-left:0px;
+}
+li.x-menu-list-item div {
+	display: inline;
+}
+
Index: typo3/sysext/em/ext_tables.php
===================================================================
--- typo3/sysext/em/ext_tables.php	(revision 9674)
+++ typo3/sysext/em/ext_tables.php	(revision )
@@ -7,6 +7,7 @@
 	t3lib_extMgm::addModule('tools', 'em', 'after:layout', t3lib_extMgm::extPath($_EXTKEY) . 'classes/');
 		// register Ext.Direct
 	$TYPO3_CONF_VARS['SC_OPTIONS']['ExtDirect']['TYPO3.EM.ExtDirect'] = t3lib_extMgm::extPath($_EXTKEY) . 'classes/connection/class.tx_em_connection_extdirectserver.php:tx_em_Connection_ExtDirectServer';
+	$TYPO3_CONF_VARS['SC_OPTIONS']['ExtDirect']['TYPO3.EMSOAP.ExtDirect'] = t3lib_extMgm::extPath($_EXTKEY) . 'classes/connection/class.tx_em_connection_extdirectsoap.php:tx_em_Connection_ExtDirectSoap';
 
 }
 ?>
\ No newline at end of file
Index: typo3/sysext/em/res/js/em_repositorylist.js
===================================================================
--- typo3/sysext/em/res/js/em_repositorylist.js	(revision )
+++ typo3/sysext/em/res/js/em_repositorylist.js	(revision )
@@ -0,0 +1,248 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <info@sk-typo3.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+Ext.ns('TYPO3.EM', 'TYPO3.EM.GridColumns', 'TYPO3.EM.ExtDirect');
+
+TYPO3.EM.RepositoryList = Ext.extend(Ext.grid.GridPanel, {
+	border:false,
+	stripeRows: true,
+
+	expander: new Ext.ux.grid.RowPanelExpander({
+		createExpandingRowPanelItems: function(record, rowIndex){
+			var panelItems = [
+				new Ext.TabPanel({
+					plain: true,
+					activeTab: 0,
+					defaults: {
+						autoHeight: true
+					},
+					record: record,
+					items:[
+						{
+							title: TYPO3.lang.details_info,
+							listeners: {
+								activate: function(panel) {
+									panel.update(TYPO3.EM.Layouts.remoteExtensionInfo().applyTemplate(panel.ownerCt.record.data));
+								}
+							}
+						}
+					]
+				})
+			];
+			return panelItems;
+		}
+	}),
+
+	initComponent:function() {
+		this.repositoryListStore = new Ext.data.DirectStore({
+			storeId: 'repositoryliststore',
+			directFn: TYPO3.EM.ExtDirect.getRemoteExtensionList,
+			//paramsAsHash: false,
+			idProperty: 'extkey',
+			root: 'data',
+			totalProperty: 'length',
+			fields:[
+				{name:'install'},
+				{name:'title'},
+				{name:'extkey'},
+				{name:'category'},
+				{name:'version'},
+				{name:'alldownloadcounter', type: 'int'},
+				{name:'state'},
+				{name:'icon'},
+				{name:'description'},
+				{name:'lastuploaddate'},
+				{name:'authorname'},
+				{name:'authoremail'},
+				{name:'versions', type: 'int'}
+			],
+			paramNames: {
+				start : 'start',
+				limit : 'limit',
+				sort : 'sort',
+				dir : 'dir',
+				query: 'query'
+			},
+			baseParams: {
+				query: '*',
+				repository: 1,
+				start: 0,
+				limit: 50
+
+			},
+			remoteSort: true,
+			sortInfo:{
+				field:'title',
+				direction:"ASC"
+			},
+			listeners: {
+				beforeload: function(store, records){
+					store.setBaseParam('rep', Ext.getCmp('repCombo').getValue());
+				},
+				load: function(store, records){
+					this.filterBy(this.storeFilter);
+				},
+				datachanged: function(store){
+					//Ext.getCmp('rdisplayExtensionLabel').setText('Extensions found: ' + store.data.length);
+				}
+			},
+			storeFilter: function(record,id){
+
+				return true;
+			}
+		});
+
+		this.repositoryStore = new Ext.data.DirectStore({
+			storeId: 'repositories',
+			idProperty: 'uid',
+			directFn: TYPO3.EM.ExtDirect.getRepositories,
+			root: 'data',
+			totalProperty: 'length',
+			fields : ['title', 'uid', 'updated', 'count', 'selected'],
+			paramsAsHash: true
+		});
+
+		var searchField = new Ext.ux.form.SearchField({
+			id: 'rsearchField',
+			store: this.repositoryListStore,
+			width: 200
+		});
+
+		Ext.apply(this, {
+			loadMask: {msg: 'Loading Repository Extensionlist ...'},
+			store: this.repositoryListStore,
+			columns: [
+				//sm,
+				this.expander,
+				TYPO3.EM.GridColumns.ImportExtension,
+				TYPO3.EM.GridColumns.ExtensionTitle,
+				TYPO3.EM.GridColumns.ExtensionKey,
+				TYPO3.EM.GridColumns.ExtensionCategoryRemote,
+				TYPO3.EM.GridColumns.ExtensionDownloads,
+				TYPO3.EM.GridColumns.ExtensionAuthor,
+				TYPO3.EM.GridColumns.ExtensionType,
+				TYPO3.EM.GridColumns.ExtensionState
+			],
+			plugins: [this.expander],
+			viewConfig: {
+				forceFit: true,
+				enableRowBody: true,
+				showPreview: true,
+				getRowClass: this.applyRowClass,
+				iconCls: 'icon-grid'
+			},
+			sm: new Ext.grid.CellSelectionModel({
+				select: Ext.emptyFn
+			}),
+			tbar: [
+				{
+					xtype: 'tbtext',
+					text: 'Filter:',
+					qTip: 'Enter something for search and press Enter. To list all, use "*"'
+				},
+				searchField,
+				' ',
+				{
+					xtype: 'tbtext',
+					text: 'Repository:'
+				},
+				TYPO3.EM.RepositoryCombo,
+				{
+					xtype: 'container',
+					id: 'repInfo',
+					html: ''
+				},
+				' ',
+				{
+					xtype: 'button',
+					text: 'Retrieve / Update',
+					scope: this,
+					handler: this.repositoryUpdate
+				}
+
+			],
+			bbar:[
+				'->', {
+					id: 'rresultPaging',
+					xtype: 'paging',
+					store: this.repositoryListStore,
+					pageSize: 50,
+					displayInfo: true,
+					emptyMsg: 'start searching ...'
+				}
+			]
+		});
+
+		TYPO3.EM.RepositoryList.superclass.initComponent.apply(this, arguments);
+	},
+
+	onRender:function() {
+		TYPO3.EM.RepositoryCombo.store = this.repositoryStore;
+		TYPO3.EM.RepositoryCombo.on('select', function(comboBox, newValue, oldValue) {
+            Ext.getCmp('repInfo').update(TYPO3.EM.Layouts.repositoryInfo().applyTemplate(newValue.data));
+			this.repositoryListStore.reload({ params: {repository: newValue.data.uid} });
+		}, this);
+		this.repositoryStore.load({
+			callback: function() {
+				if (this.getCount() == 0) {
+					TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_error, TYPO3.lang.repository_notfound, 15);
+				} else {
+					var rec = this.getById(TYPO3.settings.EM.selectedRepository);
+					TYPO3.EM.RepositoryCombo.setValue(TYPO3.settings.EM.selectedRepository);
+					Ext.getCmp('repInfo').update(TYPO3.EM.Layouts.repositoryInfo().applyTemplate(rec.data));
+				}
+			}
+
+		});
+		TYPO3.EM.RepositoryList.superclass.onRender.apply(this, arguments);
+	},
+
+	repositoryUpdate: function() {
+		var m = Ext.MessageBox.wait(TYPO3.lang.msg_longwait, TYPO3.lang.repository_update);
+		TYPO3.EM.ExtDirect.repositoryUpdate(1, function(response) {
+			if (!response.success) {
+				if (response.rep == 0) {
+					TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_error, response.errormsg, 15);
+				} else {
+					TYPO3.Flashmessage.display(TYPO3.Severity.notice, TYPO3.lang.repository_update_not_needed, response.errormsg, 5);
+				}
+			} else {
+				TYPO3.Flashmessage.display(TYPO3.Severity.information, TYPO3.lang.repository_updated, String.format(TYPO3.lang.repository_extensions_count, response.data.count), 10);
+				this.repositoryListStore.load();
+			}
+			m.hide();
+		}, this);
+
+	}
+});
+
+Ext.reg('remoteextlist', TYPO3.EM.RepositoryList);
\ No newline at end of file
Index: typo3/sysext/em/classes/extensions/class.tx_em_extensions_list.php
===================================================================
--- typo3/sysext/em/classes/extensions/class.tx_em_extensions_list.php	(revision 9674)
+++ typo3/sysext/em/classes/extensions/class.tx_em_extensions_list.php	(revision )
@@ -36,13 +36,6 @@
 
 	protected $parentObject;
 
-	/**
-	 * Instance of EM API
-	 *
-	 * @var tx_em_API
-	 */
-	protected $api;
-
 	protected $categories;
 	protected $types;
 
@@ -54,7 +47,6 @@
 	 */
 	public function __construct($parentObject = NULL) {
 		$this->parentObject = $parentObject;
-		$this->api = t3lib_div::makeInstance('tx_em_API');
 		$this->install = t3lib_div::makeInstance('tx_em_Install', $this);
 
 		$this->categories = array(
@@ -86,7 +78,7 @@
 		$list = array();
 
 		if (!$new) {
-			$cat = $this->parentObject->defaultCategories;
+			$cat = tx_em_Tools::getDefaultCategory();
 
 			$path = PATH_typo3 . 'sysext/';
 			$this->getInstExtList($path, $list, $cat, 'S');
@@ -184,7 +176,8 @@
 	 */
 	public function singleExtInfo($extKey, $path, &$list, $type = '') {
 		if (@is_file($path . $extKey . '/ext_emconf.php')) {
-			$relPath = '../../../../' . substr($path, strlen(PATH_site));
+			$relPath = '../' . substr($path, strlen(PATH_site));
+			$directLink = 'mod.php?M=tools_em';
 			$emConf = tx_em_Tools::includeEMCONF($path . $extKey . '/ext_emconf.php', $extKey);
 			$manual = $path . $extKey . '/doc/manual.sxw';
 			if ($type === '') {
@@ -193,6 +186,8 @@
 			if (is_array($emConf)) {
 				$key = count($list);
 				$loaded = t3lib_extMgm::isLoaded($extKey);
+
+
 				if (is_array($list[$key])) {
 					$list[$key] = array('doubleInstall' => $list[$key]['doubleInstall']);
 				}
@@ -210,37 +205,22 @@
 				$list[$key]['title'] = htmlspecialchars($list[$key]['title']);
 				$list[$key]['description'] = htmlspecialchars($list[$key]['description']);
 				$list[$key]['files'] = t3lib_div::getFilesInDir($path . $extKey, '', 0, '', $this->excludeForPackaging);
-				$list[$key]['install'] = $loaded ? '<a href="' . htmlspecialchars(t3lib_div::linkThisScript(array(
-					'CMD[showExt]' => $extKey,
-					'CMD[remove]' => 1,
-					'CMD[clrCmd]' => 1,
-					'SET[singleDetails]' => 'info'
-				))) . '">' . tx_em_Tools::removeButton() . '</a>' :
-						'<a href="' . htmlspecialchars(t3lib_div::linkThisScript(array(
-							'CMD[showExt]' => $extKey,
-							'CMD[load]' => 1,
-							'CMD[clrCmd]' => 1,
-							'SET[singleDetails]' => 'info'
-						))) . '">' . tx_em_Tools::installButton() . '</a>';
+				//$list[$key]['install'] = $loaded ? tx_em_Tools::removeButton() : tx_em_Tools::installButton();
 
-				$list[$key]['install'] = $loaded ? tx_em_Tools::removeButton() : tx_em_Tools::installButton();
+				$list[$key]['download'] = '<a href="' . htmlspecialchars(
+					$directLink .'&CMD[doBackup]=1&SET[singleDetails]=backup&CMD[showExt]=' . $extKey
+				) . '" title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:download') . '">' .
+					t3lib_iconWorks::getSpriteIcon('actions-system-extension-download') . '</a>';
 
-				$list[$key]['download'] = '<a href="' . htmlspecialchars(t3lib_div::linkThisScript(array(
-					'CMD[doBackup]' => 1,
-					'SET[singleDetails]' => 'backup',
-					'CMD[showExt]' => $extKey
-				))) . '">' .
-						t3lib_iconWorks::getSpriteIcon('actions-system-extension-download') . ' title="' . $GLOBALS['LANG']->sL('LLL:EXT:lang/locallang_common.xml:download') . '" alt=""></a>';
-
 				$list[$key]['doc'] = '';
 				if (@is_file($manual)) {
-					$list[$key]['doc'] = '<a href="' . htmlspecialchars($relPath . $extKey . '/doc/manual.sxw') . '" target="_blank">
-					<img src="res/icons/oodoc.gif" width="13" height="16" title="' . $GLOBALS['LANG']->getLL('listRow_local_manual') . '" alt="" /></a>';
+					$list[$key]['doc'] = '<a href="' . htmlspecialchars($relPath . $extKey . '/doc/manual.sxw') . '">'
+						. t3lib_iconWorks::getSpriteIcon('actions-system-extension-documentation') . '</a>';
 				}
 				$list[$key]['icon'] = @is_file($path . $extKey . '/ext_icon.gif') ? '<img src="' . $relPath . $extKey . '/ext_icon.gif" alt="" width="16" height="16" />' : '<img src="clear.gif" alt="" width="16" height="16" />';
 
 				$list[$key]['categoryShort'] = $list[$key]['category'];
-				$list[$key]['category'] = $this->categories[$list[$key]['category']];
+				$list[$key]['category'] = isset($this->categories[$list[$key]['category']]) ? $this->categories[$list[$key]['category']] : $list[$key]['category'];
 
 				unset($list[$key]['_md5_values_when_last_written']);
 			}
@@ -466,6 +446,8 @@
 	 * @return	string		HTML <tr> content
 	 */
 	function extensionListRow($extKey, $extInfo, $cells, $bgColorClass = '', $inst_list = array(), $import = 0, $altLinkUrl = '') {
+		$stateColors = tx_em_Tools::getStateColors();
+
 		// Icon:
 		$imgInfo = @getImageSize(tx_em_Tools::getExtPath($extKey, $extInfo['type']) . '/ext_icon.gif');
 		if (is_array($imgInfo)) {
@@ -552,7 +534,7 @@
 						'</a></td>';
 
 				// Manual download
-				$fileP = PATH_site . tx_em_Tools::typePath($extInfo['type']) . $extKey . '/doc/manual.sxw';
+				$fileP = tx_em_Tools::typePath($extInfo['type']) . $extKey . '/doc/manual.sxw';
 				$cells[] = '<td nowrap="nowrap">' .
 						(tx_em_Tools::typePath($extInfo['type']) && @is_file($fileP) ?
 								'<a href="' . htmlspecialchars(t3lib_div::resolveBackPath($this->parentObject->doc->backPath . '../' .
@@ -603,7 +585,7 @@
 				$cells[] = '<td nowrap="nowrap">' . $this->api->typeLabels[$inst_list[$extKey]['type']] . (strlen($inst_list[$extKey]['doubleInstall']) > 1 ? '<strong> ' . tx_em_Tools::rfw($inst_list[$extKey]['doubleInstall']) . '</strong>' : '') . '</td>';
 				$cells[] = '<td nowrap="nowrap">' . ($extInfo['downloadcounter_all'] ? $extInfo['downloadcounter_all'] : '&nbsp;&nbsp;') . '/' . ($extInfo['downloadcounter'] ? $extInfo['downloadcounter'] : '&nbsp;') . '</td>';
 			}
-			$cells[] = '<td nowrap="nowrap" class="extstate" style="background-color:' . $this->parentObject->stateColors[$extInfo['EM_CONF']['state']] . ';">' . $this->parentObject->states[$extInfo['EM_CONF']['state']] . '</td>';
+			$cells[] = '<td nowrap="nowrap" class="extstate" style="background-color:' . $stateColors[$extInfo['EM_CONF']['state']] . ';">' . $this->parentObject->states[$extInfo['EM_CONF']['state']] . '</td>';
 		}
 
 		// show a different background through a different class for insecure (-1) extensions,
@@ -695,7 +677,7 @@
 						}
 					}
 				}
-				//TODO: $extInfo is unknown in this context
+
 				$content .= '<tr class="bgColor4"><td valign="top">' . $icon . '</td>' .
 						'<td valign="top">' . ($data['EM_CONF']['state'] == 'excludeFromUpdates'
 							? '<span style="color:#cf7307">' . $data['EM_CONF']['title'] . ' ' . $LANG->sL('LLL:EXT:lang/locallang_mod_tools_em.xml:write_protected') . '</span>'
@@ -705,7 +687,7 @@
 						'<td valign="top">' . $name . '</td>' .
 						'<td valign="top" align="right">' . $data[EM_CONF][version] . '</td>' .
 						'<td valign="top" align="right">' . $lastversion . '</td>' .
-						'<td valign="top" nowrap="nowrap">' . $this->api->typeLabels[$data['type']] . (strlen($data['doubleInstall']) > 1 ? '<strong> ' . tx_em_Tools::rfw($extInfo['doubleInstall']) . '</strong>' : '') . '</td>' .
+						'<td valign="top" nowrap="nowrap">' . $this->api->typeLabels[$data['type']] . (strlen($data['doubleInstall']) > 1 ? '<strong> ' . tx_em_Tools::rfw($data['doubleInstall']) . '</strong>' : '') . '</td>' .
 						'<td valign="top">' . $comment . '</td></tr>' . LF .
 						$warn .
 						'<tr class="bgColor4"><td colspan="7"><hr style="margin:0px" /></td></tr>' . LF;
@@ -723,7 +705,7 @@
 	 */
 	function prepareImportExtList($unsetProc = false) {
 		$list = array();
-		$cat = $this->parentObject->defaultCategories;
+		$cat = tx_em_Tools::getDefaultCategory();
 		$filepath = $this->parentObject->getMirrorURL();
 
 		foreach ($this->parentObject->xmlhandler->extensionsXML as $extKey => $data) {
@@ -857,4 +839,4 @@
 
 }
 
-?>
\ No newline at end of file
+?>
Index: typo3/sysext/em/classes/class.tx_em_develop.php
===================================================================
--- typo3/sysext/em/classes/class.tx_em_develop.php	(revision )
+++ typo3/sysext/em/classes/class.tx_em_develop.php	(revision )
@@ -0,0 +1,357 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2010 Steffen Kamper (info@sk-typo3.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Module: Extension manager, developer module
+ *
+ * $Id: class.em_develop.php 2082 2010-03-21 17:19:42Z steffenk $
+ *
+ * @author	Steffen Kamper <info@sk-typo3.de>
+ */
+
+
+class tx_em_Develop {
+
+	/**
+	 * Parent module object
+	 *
+	 * @var SC_mod_tools_em_index
+	 */
+	protected $parentObject;
+
+	/**
+	 * Develop commands
+	 *
+	 * @var string
+	 */
+	protected $command;
+	/**
+	 * Develop sub commands
+	 *
+	 * @var string
+	 */
+	protected $sub;
+	/**
+	 * Extension key
+	 *
+	 * @var string
+	 */
+	protected $extKey;
+
+	/**
+	 * Internal Timer
+	 *
+	 * @var float
+	 */
+	private $timer;
+
+	/**
+	 * Constructor
+	 *
+	 * @param SC_mod_tools_em_index $parentObject
+	 */
+	public function __construct(SC_mod_tools_em_index $parentObject) {
+		$this->parentObject = $parentObject;
+
+		$this->command = t3lib_div::_GP('devCmd');
+		$this->sub = t3lib_div::_GP('sub');
+		$this->extKey = t3lib_div::_GP('extkey');
+	}
+
+	/**
+	 * Menu
+	 *
+	 * @return string $menu
+	 */
+	protected function devMenu() {
+		$menu = '<input type="button" value="parse extension XML file" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'parseextensions'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="parse mirror XML file" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'parsemirrors'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="repository utility" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'repoutility'))) . '\';" />';
+		$menu .= '<hr /><input type="button" value="get Extlist" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'extList'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="Single Ext" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'single','sub'=>'conf'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="Single Ext Update" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'single','sub'=>'update'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="Single Ext Config" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'single','sub'=>'config'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="Single Ext Files" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'single','sub'=>'files'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="get Settings" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'settings'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="get Labels" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'labels'))) . '\';" />';
+		$menu .= '<hr />';
+		$menu .= '<input type="button" value="get remote extlist" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'remoteext'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="get installed extkeys" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'instextkeys'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="search for \'temp\'" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'searchremote'))) . '\';" />';
+		$menu .= '&nbsp;<input type="button" value="temp. test" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'temp'))) . '\';" />';
+		$menu .= '<hr />';
+		$menu .= '<input type="button" value="install extension" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'installext'))) . '\';" />';
+		$menu .= '<input type="button" value="enable extension" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'enableext'))) . '\';" />';
+		$menu .= '<input type="button" value="disable extension" onclick="document.location.href=\'' . htmlspecialchars(t3lib_div::linkThisScript(array('devCmd'=>'disableext'))) . '\';" />';
+		$menu .= '<hr />';
+		return $menu;
+	}
+	/**
+	 * Render module content
+	 *
+	 * @return string $content
+	 */
+	public function renderModule() {
+
+			// Override content output - we now do that ourselves:
+			//prepare docheader
+		$docHeaderButtons = $this->parentObject->getButtons();
+		$markers = array(
+			'CSH' => $docHeaderButtons['csh'],
+			'FUNC_MENU' => $this->parentObject->getFuncMenu(),
+		);
+
+		$content = '';
+		$this->parentObject->content .= $this->parentObject->doc->section('Welcome Dev\'s', $content, 0, 1);
+
+			// Setting up the buttons and markers for docheader
+		$content = $this->parentObject->doc->startPage('Extension Manager');
+		$content .= $this->parentObject->doc->moduleBody($this->parentObject->pageinfo, $docHeaderButtons, $markers);
+		$contentParts = explode('###CONTENT###', $content);
+
+		echo $contentParts[0] . $this->parentObject->content;
+		echo $this->devMenu();
+
+		#
+
+
+		switch ($this->command) {
+			case 'parseextensions':
+				$this->timerStart();
+
+				echo("<h1>Extensions</h1><pre>");
+				$objRepository = t3lib_div::makeInstance('tx_em_Repository');
+				$objRepositoryUtility = t3lib_div::makeInstance('tx_em_Repository_Utility', $objRepository);
+				$mirrors = $objRepositoryUtility->updateExtList();
+
+				echo("</pre><p>Will start import database only if required!</p>\r\n");
+
+				$time = $this->timerStop();
+				echo '<hr />Processing time: ' . $time . ' sec<br>';
+					// backend memory overhead usually 4 MB in comparison to eID invocation
+				if(function_exists('memory_get_peak_usage')) {
+					$memSize = memory_get_peak_usage();
+					echo 'Maximum memory usage for this PHP process: ' . t3lib_div::formatSize($memSize) . ' (' . $memSize . ')';
+				}
+			break;
+			case 'parsemirrors':
+				$this->timerStart();
+
+				$objRepository = t3lib_div::makeInstance('tx_em_Repository');
+				$objRepositoryUtility = t3lib_div::makeInstance('tx_em_Repository_Utility', $objRepository);
+				$mirrors = $objRepositoryUtility->getMirrors(TRUE)->getMirror();
+				echo("<h1>Mirrors</h1>");
+				echo("<p>Mirror file fetched from repository</p>\r\n");
+				echo("<h2>Currently selected mirror (non persisted)</h2>\r\n");
+				echo("<p>");
+				echo("<b>Title:</b> " . $mirrors['title'] . "<br>\r\n");
+				echo("<b>Host:</b> " . $mirrors['host'] . "<br>\r\n");
+				echo("</p>");
+
+				$time = $this->timerStop();
+				echo '<hr />Processing time: ' . $time . ' sec<br>';
+					// backend memory overhead usually 4 MB in comparison to eID invocation
+				if(function_exists('memory_get_peak_usage')) {
+					$memSize = memory_get_peak_usage();
+					echo 'Maximum memory usage for this PHP process: ' . t3lib_div::formatSize($memSize) . ' (' . $memSize . ')';
+				}
+				break;
+			case 'repoutility':
+				$objRepository = t3lib_div::makeInstance('tx_em_Repository');
+				$objRepositoryUtility = t3lib_div::makeInstance('tx_em_Repository_Utility', $objRepository);
+
+				echo("<h1>Files</h1>");
+				echo("<h2>Extension list</h2>");
+				echo("<p>");
+				echo("<b>Lokale Datei (Extension list):</b> " . $objRepositoryUtility->getLocalExtListFile() . "<br>\r\n");
+				echo("<b>Enfernte Datei (Extension list):</b> " . $objRepositoryUtility->getRemoteExtListFile() . "<br>\r\n");
+				echo("<b>Entfernte Hash-Datei:</b> " . $objRepositoryUtility->getRemoteExtHashFile() . "<br>\r\n");
+				echo("</p>");
+
+				echo("<h2>Mirrors</h2>");
+				echo("<p>");
+				echo("<b>Lokale Datei (Mirrors):</b> " . $objRepositoryUtility->getLocalMirrorListFile() . "<br>\r\n");
+				echo("<b>Enfernte Datei (Mirrors):</b> " . $objRepositoryUtility->getRemoteMirrorListFile() . "<br>\r\n");
+				echo("</p>");
+				break;
+			case 'extList':
+				$extList = $this->parentObject->extensionList->getInstalledExtensions(TRUE);
+				echo t3lib_utility_Debug::viewArray($extList);
+				break;
+			case 'settings':
+				/* @var $settings em_settings */
+				$settings = t3lib_div::makeInstance('tx_em_Settings');
+				$s = $settings->getSettings();
+				echo '<h2>Settings</h2>';
+				echo t3lib_utility_Debug::viewArray($s);
+				echo '<h2>Repositories</h2>';
+
+				$selected = $settings->getSelectedRepository();
+				echo print_r($selected, TRUE);
+
+				$repos = $settings->getRegisteredRepositories();
+				echo t3lib_utility_Debug::viewArray($repos);
+				$repo = $settings->getSelectedRepository();
+				print_r($repo);
+				echo '<h2>Mirrors</h2>';
+				echo t3lib_utility_Debug::viewArray(unserialize($s['extMirrors']));
+				echo '<h2>Selected Languages</h2>';
+				echo t3lib_utility_Debug::viewArray(unserialize($s['selectedLanguages']));
+				break;
+			case 'single':
+				echo $this->singleInfo();
+				break;
+			case 'labels':
+				$labels = tx_em_Tools::getArrayFromLocallang(t3lib_extMgm::extPath('em', 'language/locallang.xml'));
+				echo t3lib_utility_Debug::viewArray($labels);
+				break;
+			default:
+			case 'remoteext':
+				$this->timerStart();
+				$list = tx_em_Database::getExtensionListFromRepository(0);
+				echo '<h1>Read Extensionlist from cache_extensions, Repository=1</h1>';
+				echo count($list) . ' Extensions read in ' . $this->timerStop() . ' sec.';
+				echo '<pre>' . print_r($list, TRUE) . '</pre>';
+				break;
+			case 'instextkeys':
+				$list = t3lib_div::makeInstance('tx_em_Connection_ExtDirectServer');
+				$keys = $list->getInstalledExtkeys();
+				echo t3lib_utility_Debug::viewArray(tx_em_Database::getExtensionListFromRepository(1));
+				echo t3lib_utility_Debug::viewArray($keys);
+				break;
+			case 'searchremote':
+				$quotedSearch = $GLOBALS['TYPO3_DB']->escapeStrForLike(
+					$GLOBALS['TYPO3_DB']->quoteStr('temp', 'cache_extensions'),
+					'cache_extensions'
+				);
+				$where = ' AND (extkey LIKE \'%' . $quotedSearch . '%\' OR title LIKE \'%' . $quotedSearch . '%\')';
+				$orderBy = '';
+				$orderDir = '';
+				$limit = '';
+				$list = tx_em_Database::getExtensionListFromRepository(
+					0,
+					$where,
+					$orderBy,
+					$orderDir,
+					$limit
+				);
+				echo t3lib_utility_Debug::viewArray($list);
+
+				break;
+			case 'temp':
+				$list = t3lib_div::makeInstance('tx_em_Connection_ExtDirectServer');
+				$tmp = $list->fetchTranslations('cms', 0, array('de','fr','ru'));
+				echo t3lib_utility_Debug::viewArray($tmp);
+				break;
+			case 'installext':
+
+
+				$em = t3lib_div::makeinstance('tx_em_Connection_ExtDirectServer');
+				$param['loc'] = 'L'; //local
+			    $param['extfile'] = PATH_site . 'fileadmin/test.t3x';
+				$param['uploadOverwrite'] = TRUE;
+				echo t3lib_utility_Debug::viewArray($em->uploadExtension($param));
+				break;
+			case 'enableext':
+				$em = t3lib_div::makeinstance('tx_em_Connection_ExtDirectServer');
+				$em->enableExtension('sktertest');
+				echo 'sktertest was enabled';
+			default:
+				echo 'select a command';
+		}
+
+
+		return;
+	}
+
+
+	/**
+	 *
+	 */
+	protected function singleInfo() {
+		$content = '<form action="' . $this->script . '" method="post">
+		Extkey: <input type="text" name="extkey" value="' . htmlspecialchars($this->extKey) . '" />
+		<input type="submit" value="get" /></form><br />';
+
+		if ($this->extKey && t3lib_extMgm::extPath($this->extKey)) {
+			$info = array();
+			$extpath = t3lib_extMgm::extPath($this->extKey);
+			$path = substr($extpath, 0, -1 * (strlen($this->extKey) + 1));
+			$extlist = t3lib_div::makeInstance('tx_em_Extensions_List');
+			$extlist->singleExtInfo($this->extKey, $path, $info);
+			$info = $info[0];
+			$content .= 'Path: ' . $extpath . ' (' . $path . ')<br />';
+			$content .= t3lib_utility_Debug::viewArray($info);
+
+			$files = $info['files'];
+			$fileArray = array();
+			foreach ($files as $file) {
+				$fileExt = strtolower(substr($file, strrpos($file, '.') + 1));
+				$fileArray[] = array(
+					'id' => $file,
+					'text' => htmlspecialchars($file),
+					'leaf' => true
+				);
+			}
+			$content .= t3lib_utility_Debug::viewArray($fileArray);
+
+			switch ($this->sub) {
+				case 'conf':
+				case 'update':
+
+					$install = t3lib_div::makeInstance('tx_em_Install');
+					$content .= $install->checkDBupdates($this->extKey, $info);
+					break;
+				case 'config':
+					break;
+				case 'files':
+					$path = $path . $this->extKey . '/';
+					$files = t3lib_div::getAllFilesAndFoldersInPath(array(), $path, '', 1, 20, '.svn');
+					$content = '<h2>Files from ' . $this->extKey . ' (' . $path . ')</h2>' . t3lib_utility_Debug::viewArray($files);
+			}
+		}
+
+		return $content;
+	}
+
+	/**
+	 * Start internal timer, used for time measure
+	 */
+	protected function timerStart() {
+		$this->timer = microtime(true);
+	}
+
+	/**
+	 * Stop internal timer and return difference, used for time measure
+	 *
+	 * @return float timer difference
+	 */
+	protected function timerStop() {
+		return microtime(true) - $this->timer;
+	}
+}
+//NO XCLASS
+?>
Index: typo3/sysext/em/classes/database/class.tx_em_database.php
===================================================================
--- typo3/sysext/em/classes/database/class.tx_em_database.php	(revision 9674)
+++ typo3/sysext/em/classes/database/class.tx_em_database.php	(revision )
@@ -100,13 +100,14 @@
 		);
 		$ret['count'] = count($temp);
 		$ret['results'] = $GLOBALS['TYPO3_DB']->exec_SELECTgetRows(
-			'*, count(*) versions, max(intversion) maxintversion',
+			'*, count(*) AS versions, max(intversion) AS maxintversion',
 			'cache_extensions',
 			'repository=' . intval($repository) . $andWhere,
 			'extkey',
 			$order,
 			$limit
 		);
+
 		return $ret;
 	}
 
@@ -131,17 +132,19 @@
 	 * @return  void
 	 */
 	public function updateRepository(tx_em_Repository $repository) {
-		$GLOBALS['TYPO3_DB']->exec_UPDATEquery(
-			self::TABLE_REPOSITORY,
-			'uid=' . $repository->getId(),
-			array(
+		$repositoryData = array(
-				'title' => $repository->getTitle(),
-				'description' => $repository->getDescription(),
-				'wsdl_url' => $repository->getWsdlUrl(),
-				'mirror_url' => $repository->getMirrorListUrl(),
-				'lastUpdated' => $repository->getLastUpdate(),
-				'extCount' => $repository->getExtensionCount(),
+			'title' => $repository->getTitle(),
+			'description' => $repository->getDescription(),
+			'wsdl_url' => $repository->getWsdlUrl(),
+			'mirror_url' => $repository->getMirrorListUrl(),
+			'lastUpdated' => $repository->getLastUpdate(),
+			'extCount' => $repository->getExtensionCount(),
-			));
+		);
+		$GLOBALS['TYPO3_DB']->exec_UPDATEquery(
+			self::TABLE_REPOSITORY,
+			'uid=' . $repository->getId(),
+			$repositoryData
+		);
 
 	}
 
@@ -154,15 +157,18 @@
 	 * @return  integer  UID of the newly inserted repository object
 	 */
 	public function insertRepository(tx_em_Repository $repository) {
-		$GLOBALS['TYPO3_DB']->exec_INSERTquery(self::TABLE_REPOSITORY,
-			array(
-				'title' => $repository->getId(),
+		$repositoryData = array(
+			'title' => $repository->getTitle(),
-				'description' => $repository->getDescription(),
-				'wsdl_url' => $repository->getWsdlUrl(),
-				'mirror_url' => $repository->getMirrorListUrl(),
-				'lastUpdated' => $repository->getLastUpdate(),
-				'extCount' => $repository->getExtensionCount(),
+			'description' => $repository->getDescription(),
+			'wsdl_url' => $repository->getWsdlUrl(),
+			'mirror_url' => $repository->getMirrorListUrl(),
+			'lastUpdated' => $repository->getLastUpdate(),
+			'extCount' => $repository->getExtensionCount(),
-			));
+		);
+		$GLOBALS['TYPO3_DB']->exec_INSERTquery(
+			self::TABLE_REPOSITORY,
+			$repositoryData
+		);
 		return $GLOBALS['TYPO3_DB']->sql_insert_id();
 	}
 
Index: typo3/sysext/em/classes/install/class.tx_em_install.php
===================================================================
--- typo3/sysext/em/classes/install/class.tx_em_install.php	(revision 9674)
+++ typo3/sysext/em/classes/install/class.tx_em_install.php	(revision )
@@ -412,7 +412,7 @@
 				}
 			}
 		}
-		if ($depError || $depIgnore) {
+		if (($depError || $depIgnore) && $this->parentObject instanceof SC_mod_tools_em_index) {
 			$content .= $this->parentObject->doc->section(
 				$GLOBALS['LANG']->getLL('removeExtFromList_dependency_error'),
 				implode('<br />', $msg), 0, 1, 2
Index: typo3/sysext/em/classes/connection/class.tx_em_connection_soap.php
===================================================================
--- typo3/sysext/em/classes/connection/class.tx_em_connection_soap.php	(revision 9674)
+++ typo3/sysext/em/classes/connection/class.tx_em_connection_soap.php	(revision )
@@ -193,6 +193,11 @@
 				$this->password = false;
 			}
 		}
+		 /*return array(
+						'username' => $this->username,
+						'password' => $this->password,
+			 		'func' => $func
+					); */
 
 		$result = $this->client->__soapCall($func, $param, NULL, $header);
 
Index: typo3/sysext/em/res/js/em_usertools.js
===================================================================
--- typo3/sysext/em/res/js/em_usertools.js	(revision )
+++ typo3/sysext/em/res/js/em_usertools.js	(revision )
@@ -0,0 +1,511 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <info@sk-typo3.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+Ext.ns('TYPO3.EM', 'TYPO3.EMSOAP.ExtDirect');
+
+TYPO3.EM.UserTools = Ext.extend(Ext.Panel, {
+	border:false,
+
+	initComponent:function() {
+		var extkeysSm = new Ext.grid.CheckboxSelectionModel({
+			singleSelect: true,
+			header: '',
+			listeners: {
+				selectionchange: function(selectionModel) {
+					var sel, record, transferkeybutton = Ext.getCmp('transferkeybutton'),
+							deletekeybutton = Ext.getCmp('deletekeybutton');
+
+					if (selectionModel.getCount() == 0) {
+						transferkeybutton.disable();
+						deletekeybutton.disable();
+					} else {
+						transferkeybutton.enable();
+						record = selectionModel.getSelected();
+						if (record.data.hasUploads === true) {
+							deletekeybutton.disable();
+						} else {
+							deletekeybutton.enable();
+						}
+						}
+
+				}
+			}
+		});
+
+		var extkeysCm = new Ext.grid.ColumnModel([
+			extkeysSm,
+			{
+				id: 'extensionkey',
+				header: TYPO3.lang.tab_mod_key,
+				width: .2,
+				sortable: true,
+				menuDisabled: true,
+				dataIndex: 'extensionkey',
+				hidable: false
+			},
+			{
+				id: 'extensiontitle',
+				header: TYPO3.lang.extInfoArray_title,
+				width: .2,
+				sortable: true,
+				menuDisabled: true,
+				dataIndex: 'title',
+				hidable: false
+			},
+			{
+				id: 'extensiondescription',
+				header: TYPO3.lang.extInfoArray_description,
+				width: .5,
+				sortable: true,
+				menuDisabled: true,
+				dataIndex: 'description',
+				hidable: false
+			},
+			{
+				id: 'extensionuploads',
+				header: TYPO3.lang.extInfoArray_uploads,
+				width: .1,
+				sortable: true,
+				menuDisabled: true,
+				dataIndex: 'uploads',
+				hidable: false
+			},
+			{
+				id: 'extensionhasuploads',
+				hidden: true,
+				dataIndex: 'hasUploads'
+			}
+		]);
+
+		var userExtStore = new Ext.data.DirectStore({
+			storeId	 : 'em-userext',
+			autoLoad	: false,
+			directFn	: TYPO3.EMSOAP.ExtDirect.getExtensions,
+			paramsAsHash: false,
+			root		: 'data',
+			idProperty  : 'extensionkey',
+			fields : [
+				{name : 'extensionkey', type : 'string'},
+				{name : 'title', type : 'string'},
+				{name : 'description', type : 'string'},
+				{name : 'uploads', type : 'int'},
+				{name : 'hasUploads', type : 'bool'}
+			],
+			sortInfo:{
+				field: 'extensionkey',
+				direction: 'ASC'
+			},
+			storeFilter: function(record, id) {
+				var filtertext = Ext.getCmp('myExtSearchField').getRawValue();
+				if (filtertext) {
+					//filter by search string
+					var re = new RegExp(Ext.escapeRe(filtertext));
+					var isMatched = record.data.extensionkey.match(re) || record.data.title.match(re) || record.data.description.match(re);
+					if (!isMatched) {
+						return false;
+					}
+				}
+				return true;
+			},
+listeners: {
+	load: function(store, records) {
+		Ext.getCmp('extvalidformbutton').enable();
+	},
+	exception: function(proxy, response, read, request, ExtDirectParams) {
+		var error;
+
+		if (!ExtDirectParams.result.raw) {
+			error = 'Unexpected result from SOAP';
+		} else {
+			error = ExtDirectParams.result.raw.error;
+		}
+		TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_invalid, error, 15);
+		Ext.getCmp('extvalidformbutton').disable();
+	},
+	scope: this
+}
+		});
+
+		var searchField = new Ext.ux.form.FilterField({
+			store: userExtStore,
+			id: 'myExtSearchField',
+			width: 200
+		});
+
+		Ext.apply(this, {
+			itemId: 'UserTools',
+			layout: 'hbox',
+			align: 'stretchmax',
+			border: false,
+			items: [
+				{
+					width: 300,
+					border: false,
+					items: [
+						{
+							layout: 'form',
+							xtype: 'form',
+							labelWidth: 100,
+							defaults: {margins: '10 0 0 0'},
+							ref: '../validityCheckForm',
+							items: [
+								{
+									xtype:'fieldset',
+									title: TYPO3.lang.registerkeys_check_validity_extkey,
+									defaults: {},
+									defaultType: 'textfield',
+									items :[
+										{
+											fieldLabel: TYPO3.lang.tab_mod_key,
+											name: 'extkey',
+											width: 170,
+											allowBlank: false,
+											validator: function(value) {
+												if (value.length < 3) {
+													return false;
+												}
+												return true;
+											}
+										},
+										new Ext.Container({
+											html: TYPO3.EM.Layouts.getExtensionRules(),
+											xtype: 'displayfield',
+											labelWidth: 1
+										})
+									],
+									buttons: [
+										{
+											id: 'extvalidformbutton',
+											text: TYPO3.lang.registerkeys_check_validity,
+											handler: function() {
+												this.validityCheckForm.getForm().submit({
+													waitMsg : TYPO3.lang.registerkeys_check_validity,
+													success: function(form, action) {
+														TYPO3.Flashmessage.display(TYPO3.Severity.information, TYPO3.lang.registerkeys_check_validity_extkey_isvalid, '', 5);
+
+														this.registerForm.getForm().setValues({
+															extkey: form.getValues().extkey,
+															extkeydisplay: '<b>' + form.getValues().extkey + '</b>'
+														});
+														form.reset();
+														this.validityCheckForm.hide()
+														this.registerForm.show();
+													},
+
+													failure: function(form, action) {
+														if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
+															TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_error,
+																	'Status:' + action.response.status + ': ' +
+																			action.response.statusText, 15);
+														}
+														if (action.failureType === Ext.form.Action.SERVER_INVALID) {
+															// server responded with success = false
+															TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_invalid, action.result.message, 5);
+														}
+													},
+													scope: this
+												});
+											},
+											scope: this
+										}
+									]
+								}
+							]
+						},
+						{
+							layout: 'form',
+							xtype: 'form',
+							hidden: true,
+							labelWidth: 100,
+							ref: '../registerForm',
+							items: [
+								{
+									xtype:'fieldset',
+									title: TYPO3.lang.registerkeys_registerkey,
+									defaults: {},
+									defaultType: 'textfield',
+									items :[
+										{
+											xtype: 'hidden',
+											name: 'extkey',
+											value: ''
+										},
+										{
+											html: '',
+											xtype: 'displayfield',
+											name: 'extkeydisplay',
+											fieldLabel: TYPO3.lang.tab_mod_key
+										},
+										{
+											fieldLabel: TYPO3.lang.extInfoArray_title,
+											name: 'title',
+											width: 170,
+											allowBlank: false
+										},
+										{
+											fieldLabel: TYPO3.lang.extInfoArray_description,
+											xtype: 'textarea',
+											name: 'description',
+											width: 170,
+											height: 80
+										}
+									],
+									buttons: [
+										{
+											id: 'extregisterformbutton',
+											text: TYPO3.lang.cmd_register,
+											handler: function() {
+												this.registerForm.getForm().submit({
+													waitMsg : TYPO3.lang.registerkeys_register_extkey,
+													success: function(form, action) {
+														var msg = String.format(registerkeys_register_extkey_success, this.registerForm.getForm().getValues().extkey);
+														TYPO3.Flashmessage.display(TYPO3.Severity.information, msg, '', 5);
+														form.reset();
+														this.registerForm.hide();
+														this.validityCheckForm.show();
+														Ext.getCmp('em-userextgrid').store.load();
+													},
+													failure: function(form, action) {
+														if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
+															TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_error,
+																TYPO3.lang.list_order_state + ':' + action.response.status + ': ' +
+																action.response.statusText, 15);
+														}
+														if (action.failureType === Ext.form.Action.SERVER_INVALID) {
+															// server responded with success = false
+															TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_invalid, action.result.message, 5);
+														}
+													},
+													scope: this
+												});
+											},
+											scope: this
+
+										},
+										{
+											text: TYPO3.lang.registerkeys_cancel_register,
+											handler: function() {
+												this.registerForm.hide();
+												this.validityCheckForm.show()
+											},
+											scope: this
+										}
+									]
+								}
+							]
+						}
+					]
+				},
+				{
+					flex: 1,
+					border: false,
+					//layout: 'fit',
+					items: [
+						{
+							xtype: 'fieldset',
+							title: TYPO3.lang.myExtensions,
+							items : [
+								{
+									xtype: 'grid',
+									id: 'em-userextgrid',
+									stripeRows: true,
+									store: userExtStore,
+									loadMask: {msg: TYPO3.lang.action_loading_extlist},
+									cm: extkeysCm,
+									sm: extkeysSm,
+									viewConfig: {
+										forceFit: true,
+										autofill: true
+									},
+									//anchor: '100% 100%',
+									tbar: [
+										{
+											xtype: 'tbtext',
+											text: TYPO3.lang.cmd_filter + ':'
+										},
+										searchField,
+										'->',
+										{
+											xtype: 'tbtext',
+											text: TYPO3.lang.cmd_action + ':'
+										},
+										' ',
+										{
+											xtype: 'tbbutton',
+											disabled: true,
+											id: 'transferkeybutton',
+											text: TYPO3.lang.cmd_transferkey,
+											handler: this.transferkey,
+											scope: this
+										},
+										' ',
+										{
+											xtype: 'tbbutton',
+											disabled: true,
+											id: 'deletekeybutton',
+											text: TYPO3.lang.cmd_deletekey,
+											handler: this.deletekey,
+											scope: this
+										}
+									],
+									height: 450
+								}
+							]
+						}
+					]
+				}
+			]
+		});
+
+		TYPO3.EM.UserTools.superclass.initComponent.apply(this, arguments);
+	},
+
+	onRender: function() {
+
+		TYPO3.EM.UserTools.superclass.onRender.apply(this, arguments);
+
+		Ext.apply(this.validityCheckForm.getForm(), {
+			api: {
+				submit: TYPO3.EMSOAP.ExtDirect.checkExtensionkey
+			},
+			paramsAsHash: false
+
+		});
+		Ext.apply(this.registerForm.getForm(), {
+			api: {
+				submit: TYPO3.EMSOAP.ExtDirect.registerExtensionkey
+			},
+			paramsAsHash: false
+
+		});
+
+	},
+
+	transferkey: function() {
+		var grid = Ext.getCmp('em-userextgrid');
+		var extkey = grid.getSelectionModel().getSelected().data.extensionkey;
+		this.transferWindow = new Ext.Window({
+			width: 300,
+			height: 170,
+			modal: true,
+			title: TYPO3.lang.cmd_transferkey + ' "' + extkey + '"',
+			layout: 'form',
+			items: [
+				{
+					xtype: 'form',
+					labelWidth: 120,
+					items: [
+						new Ext.Container({
+							xtype: 'displayfield',
+							html: TYPO3.lang.transferkeys_info,
+							labelWidth: 1,
+							style: 'margin: 10px 5px;'
+						}),
+						{
+							xtype: 'textfield',
+							fieldLabel: TYPO3.lang.cmd_transferkey_to_user,
+							ref: '../transfertouser',
+							allowBlank: false
+						}
+					]
+				}
+			],
+			buttons: [
+				{
+					text: TYPO3.lang.cmd_transferkey_do,
+					handler: function(button) {
+						var toUser = this.transferWindow.transfertouser.getRawValue();
+						button.disable();
+						this.transferWindow.body.mask(TYPO3.lang.cmd_transferkey);
+						TYPO3.EMSOAP.ExtDirect.transferExtensionKey(extkey, toUser, function(response) {
+							if (response.success) {
+								TYPO3.Flashmessage.display(TYPO3.Severity.ok, String.format(TYPO3.lang.transferkeys_success, response.key, response.user), '', 5);
+								Ext.getCmp('em-userextgrid').store.load();
+							} else {
+								TYPO3.Flashmessage.display(TYPO3.Severity.error, String.format(TYPO3.lang.transferkeys_fail, response.key, response.user),response.message, 15);
+							}
+							this.transferWindow.close();
+						}, this)
+					},
+					scope: this
+				}
+			]
+		}).show();
+	},
+
+	deletekey: function() {
+		var grid = Ext.getCmp('em-userextgrid');
+		var extkey = grid.getSelectionModel().getSelected().data.extensionkey;
+
+		this.deleteWindow = new Ext.Window({
+			width: 300,
+			height: 130,
+			modal: true,
+			title: TYPO3.lang.cmd_deletekey + ' "' + extkey + '"',
+			items: [
+				{
+					items: [
+						new Ext.Container({
+							xtype: 'displayfield',
+							html: TYPO3.lang.deletekey_info,
+							labelWidth: 1,
+							style: 'margin: 10px 5px;'
+						})
+					]
+				}
+			],
+			buttons: [
+				{
+					text: 'delete',
+					handler: function(button) {
+						button.disable();
+						this.deleteWindow.body.mask(TYPO3.lang.cmd_deletekey);
+						TYPO3.EMSOAP.ExtDirect.deleteExtensionKey(extkey, function(response) {
+							if (response.success) {
+								TYPO3.Flashmessage.display(TYPO3.Severity.ok, String.format(TYPO3.lang.deletekey_success, response.key), '', 5);
+								Ext.getCmp('em-userextgrid').store.load();
+							} else {
+								TYPO3.Flashmessage.display(TYPO3.Severity.error, String.format(TYPO3.lang.deletekey_fail, response.key), response.message, 15);
+							}
+							this.deleteWindow.close();
+						}, this)
+					},
+					scope: this
+				}
+			]
+		}).show();
+	}
+
+
+});
+
+Ext.reg('TYPO3.EM.UserTools', TYPO3.EM.UserTools);
Index: typo3/sysext/em/classes/index.php
===================================================================
--- typo3/sysext/em/classes/index.php	(revision 9674)
+++ typo3/sysext/em/classes/index.php	(revision )
@@ -76,37 +76,6 @@
 
 	var $states = array(); // Extension States; see init()
 
-	/**
-	 * Internal variable loaded with extension categories (for display/listing). Should reflect $categories above
-	 * Dynamic var.
-	 */
-	var $defaultCategories = array(
-		'cat' => array(
-		'be' => array(),
-		'module' => array(),
-		'fe' => array(),
-		'plugin' => array(),
-		'misc' => array(),
-		'services' => array(),
-		'templates' => array(),
-		'example' => array(),
-		'doc' => array()
-		)
-	);
-
-	/**
-	 * Colors for extension states
-	 */
-	var $stateColors = array(
-		'alpha' => '#d12438',
-		'beta' => '#97b17e',
-		'stable' => '#3bb65c',
-		'experimental' => '#007eba',
-		'test' => '#979797',
-		'obsolete' => '#000000',
-		'excludeFromUpdates' => '#cf7307'
-	);
-
 	var $detailCols = array(
 		0 => 2,
 		1 => 5,
@@ -150,7 +119,7 @@
 	protected $api;
 
 	/**
-	 * instance of TER connection handler
+	 * Instance of TER connection handler
 	 *
 	 * @var tx_em_Connection_Ter
 	 */
@@ -207,7 +176,14 @@
 	 */
 	public $install;
 
+	/**
+	 * Settings object
+	 *
+	 * @var tx_em_Settings
+	 */
+	public $settings;
 
+
 	var $JScode; // JavaScript code to be forwared to $this->doc->JScode
 
 	// GPvars:
@@ -284,6 +260,7 @@
 		$this->lookUpStr = trim(t3lib_div::_GP('lookUp'));
 		$this->listRemote = t3lib_div::_GP('ter_connect');
 		$this->listRemote_search = trim(t3lib_div::_GP('ter_search'));
+		$this->noDocHeader = intval(t3lib_div::_GP('nodoc') > 0);
 
 		$this->settings = t3lib_div::makeInstance('tx_em_Settings');
 		$this->install = t3lib_div::makeInstance('tx_em_Install', $this);
@@ -400,13 +377,9 @@
 			// MENU-ITEMS:
 		$this->MOD_MENU = $this->settings->MOD_MENU;
 
-			// temporary unset new modules
-		unset ($this->MOD_MENU['function']['extensionmanager'], $this->MOD_MENU['function']['develop']);
-		/*
 		if (!intval($GLOBALS['TYPO3_CONF_VARS']['BE']['debug'])) {
 			unset ($this->MOD_MENU['function']['develop']);
 		}
-        */
 
 		$this->MOD_MENU['singleDetails'] = $this->mergeExternalItems($this->MCONF['name'], 'singleDetails', $this->MOD_MENU['singleDetails']);
 
@@ -484,7 +457,14 @@
 
 			$this->content .= $menu ? $this->doc->section('', '<form action="' . $this->script . '" method="post" name="pageform"><span class="nobr">' . $menu . '</span></form>') : '';
 
-			switch ($this->MOD_SETTINGS['function']) {
+
+			$view = $this->MOD_SETTINGS['function'];
+			if (t3lib_div::_GP('view')) {
+					// temporary overwrite the view with GP var. Used from ExtJS without changing the submodule
+				$view = t3lib_div::_GP('view');
+			}
+
+			switch ($view) {
 				case 'loaded_list':
 					// Lists loaded (installed) extensions
 					$headline = $GLOBALS['LANG']->getLL('loaded_exts');
@@ -540,21 +520,24 @@
 			$this->content .= '</form>';
 		}
 
+		if (!$this->noDocHeader) {
-		// Setting up the buttons and markers for docheader
-		$docHeaderButtons = $this->getButtons();
-		$markers = array(
-			'CSH' => $docHeaderButtons['csh'],
-			'FUNC_MENU' => $this->getFuncMenu(),
-			'CONTENT' => $this->content
-		);
+			// Setting up the buttons and markers for docheader
+			$docHeaderButtons = $this->getButtons();
+			$markers = array(
+				'CSH' => $docHeaderButtons['csh'],
+				'FUNC_MENU' => $this->getFuncMenu(),
+				'CONTENT' => $this->content
+			);
 
-		// Build the <body> for the module
-		$this->content = $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
+			// Build the <body> for the module
+			$this->content = $this->doc->moduleBody($this->pageinfo, $docHeaderButtons, $markers);
+		}
 			// Renders the module page
 		$this->content = $this->doc->render(
 			'Extension Manager',
 			$this->content
-		);
+	    );
+
 	}
 
 	/**
@@ -2167,7 +2150,7 @@
 			$content .= '<a class="t3-link" href="#" onclick="' . htmlspecialchars($onClick) .
 					' return false;"><strong>' . $updateEMConf . '</strong> ' .
 					sprintf($GLOBALS['LANG']->getLL('extDelete_from_location'),
-						$this->api->typeLabels[$extInfo['type']],
+						$this->typeLabels[$extInfo['type']],
 						substr($absPath, strlen(PATH_site))
 					) . '</a>';
 			$content .= '<br /><br />' . $GLOBALS['LANG']->getLL('extUpdateEMCONF_info_changes') . '<br />
Index: typo3/sysext/em/res/js/em_layouts.js
===================================================================
--- typo3/sysext/em/res/js/em_layouts.js	(revision )
+++ typo3/sysext/em/res/js/em_layouts.js	(revision )
@@ -0,0 +1,111 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <info@sk-typo3.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+Ext.ns('TYPO3.EM');
+
+TYPO3.EM.Layouts = {
+
+	getInfoTemplate: function() {
+		// later get template via Ajax
+		return new Ext.XTemplate(
+			'<div class="em-extlist-extinfo">',
+				'<h2>{icon} {title}</h2>',
+				'<p class="desc">{description}</p>',
+				'<h3>' + TYPO3.lang.show_links + '</h3>',
+				'<p><label>' + TYPO3.lang.cmd_downloadext + ':</label> {download} ',
+				'<tpl if="this.doc"><label>' + TYPO3.lang.cmd_readdoc + ':</label> {doc}</tpl></p>',
+				'<h3>' + TYPO3.lang.show_details + '</h3>',
+				'<p><label>' + TYPO3.lang.extInfoArray_author + ':</label> {author}<br />',
+				'<label>' + TYPO3.lang.extInfoArray_version + ':</label> {version}<br />',
+				'<label>' + TYPO3.lang.extInfoArray_category + ':</label> {category}<br />',
+				'<label>' + TYPO3.lang.extInfoArray_state + ':</label> {state}<br />',
+				'<label>' + TYPO3.lang.extInfoArray_shy + ':</label> {shyword}<br />',
+				'<label>' + TYPO3.lang.extInfoArray_internal + ':</label> {internal}<br />',
+				'<label>' + TYPO3.lang.extInfoArray_depends_on + ':</label> {depends}<br />',
+				'<label>' + TYPO3.lang.extInfoArray_conflicts_with + ':</label> {conflicts}<br />',
+			'</div>'
+		);
+	},
+
+	remoteExtensionInfo: function() {
+		return new Ext.XTemplate(
+			'<div class="em-info">',
+				'<p><label>' + TYPO3.lang.extInfoArray_title + ':</label>{title}</p>',
+				'<p><label>' + TYPO3.lang.listRowHeader_ext_key + '</label>{extkey}</p>',
+				'<p><label>' + TYPO3.lang.extInfoArray_category + ':</label>{category}</p>',
+				'<p><label>' + TYPO3.lang.extInfoArray_version + ':</label>{version}</p>',
+				'<p><label>' + TYPO3.lang.extInfoArray_downloads + ':</label>{alldownloadcounter}</p>',
+				'<p><label>' + TYPO3.lang.extInfoArray_state + ':</label>{state}</p>',
+				'<p><label>' + TYPO3.lang.extInfoArray_author + ':</label>{authorname}</p>',
+				'<p><label>' + TYPO3.lang.extInfoArray_versions + ':</label>{versions}</p>',
+				'<p><label>' + TYPO3.lang.extInfoArray_description + ':</label>{description}</p>',
+			'</div>',
+		{
+			getCls: function(value) {
+				return '';
+			}
+		});
+	},
+
+	getExtensionRules: function() {
+		return [
+			'<div class="extvalidinfo">',
+				'<b>' + TYPO3.lang.registerkeys_rules_heading + '</b>',
+				'<ul>',
+					'<li>' + TYPO3.lang.registerkeys_rules_allowedcharacters + '</li>',
+					'<li>' + TYPO3.lang.registerkeys_rules_prefixes + '</li>',
+					'<li>' + TYPO3.lang.registerkeys_rules_startandend + '</li>',
+					'<li>' + TYPO3.lang.registerkeys_rules_length + '</li>',
+				'</ul>',
+			'</div>'
+		].join('');
+	},
+	repositoryInfo: function() {
+		return new Ext.XTemplate(
+			'<span class="typo3-message message-notice" style="padding-right: 50px;">',
+			'last update: {updated}&nbsp;&nbsp;',
+			'Extensions in repository: {count}</span>'
+		);
+	},
+
+	showExtInfo: function (panel, data) {
+		panel.update('');
+		var t = this.getInfoTemplate().compile();
+		data.shyword = data.shy ? 'Yes' : 'No';
+		t.append(panel.body, data);
+	},
+
+	showExtUpdate: function(panel, data) {
+
+	}
+};
+
Index: typo3/sysext/em/res/js/ux/rowpanelexpander.js
===================================================================
--- typo3/sysext/em/res/js/ux/rowpanelexpander.js	(revision )
+++ typo3/sysext/em/res/js/ux/rowpanelexpander.js	(revision )
@@ -0,0 +1,280 @@
+Ext.ns('Ext.ux.grid');
+
+/**
+ * @class Ext.ux.grid.RowPanelExpander
+ * @extends Ext.util.Observable
+ * Plugin (ptype = 'rowexpander') that adds the ability to have a Column in a grid which enables
+ * a second row body which expands/contracts.  The expand/contract behavior is configurable to react
+ * on clicking of the column, double click of the row, and/or hitting enter while a row is selected.
+ *
+ * @ptype rowexpander
+ */
+Ext.ux.grid.RowPanelExpander = Ext.extend(
+Ext.util.Observable, {
+	/**
+	 * @cfg {Boolean} expandOnEnter
+	 * <tt>true</tt> to toggle selected row(s) between expanded/collapsed when the enter
+	 * key is pressed (defaults to <tt>true</tt>).
+	 */
+	expandOnEnter : true,
+	/**
+	 * @cfg {Boolean} expandOnDblClick
+	 * <tt>true</tt> to toggle a row between expanded/collapsed when double clicked
+	 * (defaults to <tt>true</tt>).
+	 */
+	expandOnDblClick : true,
+
+	header : '',
+	width : 20,
+	sortable : false,
+	fixed : true,
+	menuDisabled : true,
+	dataIndex : '',
+	id : 'expander',
+	lazyRender : true,
+	enableCaching : true,
+
+	constructor : function(config) {
+		Ext.apply(this, config);
+
+		this.addEvents( {
+			/**
+			 * @event beforeexpand
+			 * Fires before the row expands. Have the listener return false to prevent the row from expanding.
+			 * @param {Object} this RowExpander object.
+			 * @param {Object} Ext.data.Record Record for the selected row.
+			 * @param {Object} body body element for the secondary row.
+			 * @param {Number} rowIndex The current row index.
+			 */
+			beforeexpand : true,
+			/**
+			 * @event expand
+			 * Fires after the row expands.
+			 * @param {Object} this RowExpander object.
+			 * @param {Object} Ext.data.Record Record for the selected row.
+			 * @param {Object} body body element for the secondary row.
+			 * @param {Number} rowIndex The current row index.
+			 */
+			expand : true,
+			/**
+			 * @event beforecollapse
+			 * Fires before the row collapses. Have the listener return false to prevent the row from collapsing.
+			 * @param {Object} this RowExpander object.
+			 * @param {Object} Ext.data.Record Record for the selected row.
+			 * @param {Object} body body element for the secondary row.
+			 * @param {Number} rowIndex The current row index.
+			 */
+			beforecollapse : true,
+			/**
+			 * @event collapse
+			 * Fires after the row collapses.
+			 * @param {Object} this RowExpander object.
+			 * @param {Object} Ext.data.Record Record for the selected row.
+			 * @param {Object} body body element for the secondary row.
+			 * @param {Number} rowIndex The current row index.
+			 */
+			collapse : true
+		});
+
+		Ext.ux.grid.RowPanelExpander.superclass.constructor
+				.call(this);
+
+		if (this.tpl) {
+			if (typeof this.tpl == 'string') {
+				this.tpl = new Ext.Template(this.tpl);
+			}
+			this.tpl.compile();
+		}
+
+		this.state = {};
+		this.bodyContent = {};
+	},
+
+	getRowClass : function(record, rowIndex, p, ds) {
+		p.cols = p.cols - 1;
+		var content = this.bodyContent[record.id];
+		if (!content && !this.lazyRender) {
+			content = this.getBodyContent(record, rowIndex);
+		}
+		if (content) {
+			p.body = content;
+		}
+		return this.state[record.id] ? 'x-grid3-row-expanded'
+				: 'x-grid3-row-collapsed';
+	},
+
+	init : function(grid) {
+		this.grid = grid;
+
+		var view = grid.getView();
+		view.getRowClass = this.getRowClass
+				.createDelegate(this);
+
+		view.enableRowBody = true;
+
+		grid.on('render', this.onRender, this);
+		grid.on('destroy', this.onDestroy, this);
+	},
+
+	// @private
+	onRender : function() {
+		var grid = this.grid;
+		var mainBody = grid.getView().mainBody;
+		mainBody.on('mousedown', this.onMouseDown, this, {
+			delegate : '.x-grid3-row-expander'
+		});
+		if (this.expandOnEnter) {
+			this.keyNav = new Ext.KeyNav(this.grid.getGridEl(),
+					{
+						'enter' : this.onEnter,
+						scope : this
+					});
+		}
+		if (this.expandOnDblClick) {
+			grid.on('rowdblclick', this.onRowDblClick, this);
+		}
+	},
+
+	// @private    
+	onDestroy : function() {
+		if (this.keyNav) {
+			this.keyNav.disable();
+			delete this.keyNav;
+		}
+		/*
+		 * A majority of the time, the plugin will be destroyed along with the grid,
+		 * which means the mainBody won't be available. On the off chance that the plugin
+		 * isn't destroyed with the grid, take care of removing the listener.
+		 */
+		var mainBody = this.grid.getView().mainBody;
+		if (mainBody) {
+			mainBody.un('mousedown', this.onMouseDown, this);
+		}
+	},
+	// @private
+	onRowDblClick : function(grid, rowIdx, e) {
+		this.toggleRow(rowIdx);
+	},
+
+	/*onEnter : function(e) {
+		var g = this.grid;
+		var sm = g.getSelectionModel();
+		var sels = sm.getSelections();
+		for ( var i = 0, len = sels.length; i < len; i++) {
+			var rowIdx = g.getStore().indexOf(sels[i]);
+			this.toggleRow(rowIdx);
+		}
+	},*/
+
+	getBodyContent : function(record, index) {
+		// extend here
+		if (!this.enableCaching) {
+			return this.tpl.apply(record.data);
+		}
+		var content = this.bodyContent[record.id];
+		if (!content) {
+			content = this.tpl.apply(record.data);
+			this.bodyContent[record.id] = content;
+		}
+		return content;
+	},
+
+	onMouseDown : function(e, t) {
+		e.stopEvent();
+		var row = e.getTarget('.x-grid3-row');
+		this.toggleRow(row);
+	},
+
+	renderer : function(v, p, record) {
+		p.cellAttr = 'rowspan="2"';
+		return '<div class="x-grid3-row-expander">&#160;</div>';
+	},
+
+	beforeExpand : function(record, body, rowIndex) {
+		if (this.fireEvent('beforeexpand', this, record, body,
+				rowIndex) !== false) {
+			if (this.tpl && this.lazyRender) {
+				body.innerHTML = this.getBodyContent(record,
+						rowIndex);
+			}
+			if (body.innerHTML == '' || !this.enableCaching) {
+				this.createExpandingRowPanel(record, body,
+						rowIndex);
+			}
+			return true;
+		} else {
+			return false;
+		}
+	},
+
+	toggleRow : function(row) {
+		if (typeof row == 'number') {
+			row = this.grid.view.getRow(row);
+		}
+		this[Ext.fly(row).hasClass('x-grid3-row-collapsed') ? 'expandRow' : 'collapseRow'](row);
+	},
+
+	expandRow : function(row) {
+		if (typeof row == 'number') {
+			row = this.grid.view.getRow(row);
+		}
+		var record = this.grid.store.getAt(row.rowIndex);
+		var body = Ext.DomQuery.selectNode('tr:nth(2) div.x-grid3-row-body', row);
+		if (this.beforeExpand(record, body, row.rowIndex)) {
+			this.state[record.id] = true;
+			Ext.fly(row).replaceClass('x-grid3-row-collapsed', 'x-grid3-row-expanded');
+			this.fireEvent('expand', this, record, body, row.rowIndex);
+		}
+	},
+
+	collapseRow : function(row) {
+		if (typeof row == 'number') {
+			row = this.grid.view.getRow(row);
+		}
+		var record = this.grid.store.getAt(row.rowIndex);
+		var body = Ext.fly(row).child(
+				'tr:nth(1) div.x-grid3-row-body', true);
+		if (this.fireEvent('beforecollapse', this, record, 	body, row.rowIndex) !== false) {
+			this.state[record.id] = false;
+			Ext.fly(row).replaceClass('x-grid3-row-expanded', 'x-grid3-row-collapsed');
+			this.fireEvent('collapse', this, record, body, row.rowIndex);
+		}
+	},
+
+	createExpandingRowPanel : function(record, rowBody, rowIndex) {
+
+		// record.id is more stable than rowIndex for panel item's key; rows can be deleted.
+		var panelItemIndex = record.id;
+		// var panelItemIndex = rowIndex;
+
+		// init array of expanding row panels if not already inited
+		if (!this.expandingRowPanel) {
+			this.expandingRowPanel = [];
+		}
+
+		// Destroy the existing panel if present
+		if (this.expandingRowPanel[panelItemIndex]) {
+			this.expandingRowPanel[panelItemIndex].destroy();
+		}
+		this.expandingRowPanel[panelItemIndex] = new Ext.Panel({
+			border : false,
+			bodyBorder : false,
+			layout : 'form',
+			renderTo : rowBody,
+			items : this.createExpandingRowPanelItems(record, rowIndex)
+		});
+
+	},
+
+	/**
+	 * Override this method to put Ext form items into the expanding row panel.
+	 * @return Array of panel items.
+	 */
+	createExpandingRowPanelItems : function(record, rowIndex) {
+		var panelItems = [];
+
+		return panelItems;
+	}
+});
+
+Ext.preg('rowexpander', Ext.ux.grid.RowPanelExpander);
\ No newline at end of file
Index: typo3/sysext/em/res/js/em_languages.js
===================================================================
--- typo3/sysext/em/res/js/em_languages.js	(revision )
+++ typo3/sysext/em/res/js/em_languages.js	(revision )
@@ -0,0 +1,374 @@
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+
+Ext.ns('TYPO3.EM');
+
+TYPO3.EM.LanguagesSelectionModel  = new Ext.grid.CheckboxSelectionModel({
+	singleSelect: false,
+	header: '',
+	dataIndex: 'selected'
+});
+
+TYPO3.EM.LanguagesColumnModel = new Ext.grid.ColumnModel([
+	TYPO3.EM.LanguagesSelectionModel, {
+		id: 'lang-label',
+		header: TYPO3.lang.lang_language,
+		sortable: true,
+		menuDisabled: true,
+		fixed: true,
+		dataIndex: 'label',
+		width: 150,
+		hidable: false,
+		renderer: function(value, metaData, record, rowIndex, colIndex, store) {
+			return '<span class="' + record.data.cls + '">&nbsp</span>' + value;
+		}
+	},{
+		id: 'lang-key',
+		header: TYPO3.lang.lang_short,
+		menuDisabled: true,
+		fixed: true,
+		sortable: true,
+		dataIndex: 'lang',
+		hidable: false
+	}
+]);
+
+TYPO3.EM.LanguagesProgressBar = new Ext.ProgressBar ({
+	id:  'langpb',
+	cls: 'left-align',
+	autoWidth: true,
+	style: 'margin: 10px',
+	animate: true,
+	height: 20,
+	hidden: true
+});
+
+
+
+TYPO3.EM.Languages = Ext.extend(Ext.FormPanel, {
+	border:false,
+	layout: 'form',
+	id: 'em-labguage-modul',
+	extCount: 0,
+	fetchType: 0,
+	extkeyArray : [],
+	selectedLanguages: [],
+	cb: null,
+
+
+	initComponent: function() {
+		var langExtStore = new Ext.data.DirectStore({
+			storeId     : 'em-languageext-store',
+			autoLoad	: false,
+			directFn	: TYPO3.EM.ExtDirect.getInstalledExtkeys,
+			root		: 'data',
+			idProperty  : 'extkey',
+			//fields : [{name : 'extkey'},{name : 'icon'},{name : 'lang'},{name: 'Danish'}, {name: 'German'}, {name: 'Norwegian'}, {name: 'Italian'}, {name: 'French'}, {name: 'Spanish'}, {name: 'Dutch'}, {name: 'Czech'}, {name: 'Polish'}, {name: 'Slovenian'}, {name: 'Finnish'}, {name: 'Turkish'}, {name: 'Swedish'}, {name: 'Portuguese'}, {name: 'Russian'}, {name: 'Romanian'}, {name: 'Chinese (Simpl)'}, {name: 'Slovak'}, {name: 'Lithuanian'}, {name: 'Icelandic'}, {name: 'Croatian'}, {name: 'Hungarian'}, {name: 'Greenlandic'}, {name: 'Thai'}, {name: 'Greek'}, {name: 'Chinese (Trad)'}, {name: 'Basque'}, {name: 'Bulgarian'}, {name: 'Brazilian Portuguese'}, {name: 'Estonian'}, {name: 'Arabic'}, {name: 'Hebrew'}, {name: 'Ukrainian'}, {name: 'Latvian'}, {name: 'Japanese'}, {name: 'Vietnamese'}, {name: 'Catalan'}, {name: 'Bosnian'}, {name: 'Korean'}, {name: 'Esperanto'}, {name: 'Bahasa Malaysia'}, {name: 'Hindi'}, {name: 'Faroese'}, {name: 'Persian'}, {name: 'Serbian'}, {name: 'Albanian'}, {name: 'Georgian'}, {name: 'Galician'}],
+			fields : [{name : 'extkey'},{name : 'icon'},{name: 'stype'}],
+			listeners : {
+				'load': function(store, records, options){
+					if(records.length) {
+						Ext.getCmp('lang-checkbutton').enable();
+						Ext.getCmp('lang-updatebutton').enable();
+						this.restoreExtLanguageGrid();
+					}
+					this.languageLoaded = true;
+				},
+				scope : this
+			}
+
+		});
+
+		var langStore = new Ext.data.DirectStore({
+			storeId     : 'em-language-store',
+			autoLoad	: false,
+			directFn	: TYPO3.EM.ExtDirect.getLanguages,
+			paramsAsHash: false,
+			root		: 'data',
+			idProperty  : 'lang',
+			fields : [
+				{name : 'lang', type : 'string'},
+				{name : 'label', type : 'string'},
+				{name : 'cls', type : 'string'},
+				{name : 'selected', type: 'bool'}
+			],
+			listeners : {
+				'load': function(store, records){
+					// get selected languages and update selection and extGrid
+					TYPO3.settings.LangLoaded = false;
+					var a = [];
+					for (var i=0; i<records.length; i++) {
+						if(records[i].data.selected) {
+							a.push(records[i]);
+						}
+					}
+					TYPO3.EM.LanguagesSelectionModel.selectRecords(a);
+					langExtStore.load();
+				},
+				scope: this
+			}
+		});
+
+
+		Ext.apply(this, {
+			languagesLoaded: false,
+			layout:'hbox',
+			layoutConfig: {
+				align: 'stretch'
+			},
+			defaults: {
+				border: false
+			},
+			items: [{
+				width: 350,
+				layout: 'fit',
+				items: [{
+					xtype:'fieldset',
+					title: TYPO3.lang.translation_settings,
+					collapsible: false,
+					labelWidth: 1,
+					labelPad: 0,
+					defaults: {
+						border: false
+					},
+					items: [{
+						xtype: 'grid',
+						id: 'em-languagegrid',
+						stripeRows: true,
+						store: langStore,
+						cm: TYPO3.EM.LanguagesColumnModel,
+						sm: TYPO3.EM.LanguagesSelectionModel,
+						enableColumnMove: false,
+						anchor: '100% 100%'
+					}]
+				}]
+			}, {
+				flex: 1,
+				layout: 'fit',
+				items: [{
+					xtype:'fieldset',
+					title: TYPO3.lang.translation_status,
+					collapsible: false,
+					items: [
+						TYPO3.EM.LanguagesActionPanel,
+						TYPO3.EM.LanguagesProgressBar, {
+					    	xtype: 'grid',
+							id: 'em-extlanguagegrid',
+							stripeRows: true,
+							store: langExtStore,
+							loadMask: {msg: TYPO3.lang.translation_refresh_languages},
+							enableColumnMove: false,
+							enableHdMenu : false,
+							autoheight: true,
+							cm: new Ext.grid.DynamicColumnModelForLanguages(langExtStore),
+							margins: '0 10 0 0',
+							anchor: '100% -40'
+						}]
+					}]
+				}]
+		});
+
+		// call parent
+		TYPO3.EM.Languages.superclass.initComponent.apply(this, arguments);
+		this.langGrid = Ext.getCmp('em-languagegrid');
+		this.langGrid.getSelectionModel().on('selectionchange', function(){
+			this.saveSelection();
+		}, this);
+		Ext.getCmp('lang-checkbutton').handler = this.langActionHandler.createDelegate(this);
+		Ext.getCmp('lang-updatebutton').handler = this.langActionHandler.createDelegate(this);
+
+	} ,
+
+	langActionHandler: function(button, event) {
+		var bp = Ext.getCmp('LanguagesActionPanel');
+		var pp = Ext.getCmp('langpb');
+		bp.hide();
+		pp.show();
+
+		if (button.id === 'lang-checkbutton') {
+			// check languages
+			this.startFetchLanguages(0, Ext.StoreMgr.get('em-languageext-store'), function(){
+				TYPO3.EM.LanguagesProgressBar.updateText(TYPO3.lang.msg_finished);
+				(function() {
+					pp.hide();
+					bp.show();
+				}).defer(5000, this);
+				TYPO3.Flashmessage.display(TYPO3.Severity.information, TYPO3.lang.translation_checking_extension, TYPO3.lang.translation_check_done,3);
+			});
+		} else {
+			// update languages
+			this.startFetchLanguages(1, Ext.StoreMgr.get('em-languageext-store'), function(){
+				TYPO3.EM.LanguagesProgressBar.updateText(TYPO3.lang.msg_finished);
+				TYPO3.Flashmessage.display(TYPO3.Severity.information, TYPO3.langtranslation_update_extension, TYPO3.langtranslation_update_done, 3);
+				pp.hide();
+				bp.show();
+			});
+		}
+	},
+
+	getSelectedLanguages: function() {
+		var selLanguages = this.langGrid.getSelectionModel().getSelections();
+		this.selectedLanguages = [];
+		if (selLanguages.length > 0 ) {
+			for (var i=0; i<selLanguages.length; i++) {
+				this.selectedLanguages.push(selLanguages[i].data.lang);
+			}
+		}
+	},
+
+	saveSelection: function() {
+		if (this.languageLoaded === true) {
+			this.getSelectedLanguages();
+			if (this.selectedLanguages.length > 0 ) {
+				(function() {
+					this.restoreExtLanguageGrid();
+				}).defer(100, this);
+			}
+
+			TYPO3.EM.ExtDirect.saveLanguageSelection(this.selectedLanguages, function(response) {
+				TYPO3.Flashmessage.display(TYPO3.Severity.information, TYPO3.lang.translation_selection_saved, response, 3);
+			});
+		}
+	},
+
+	startFetchLanguages: function(type, store, callback) {
+		this.fetchType = type;
+		this.extCount = store.data.items.length;
+		this.cb = callback;
+
+
+		// fill arrays
+		for(var i = 0; i < this.extCount; i++) {
+			this.extkeyArray.push(store.data.items[i].data.extkey);
+		}
+		if (!this.selectedLanguages.length) {
+			this.getSelectedLanguages();
+		}
+		// start process
+		this.fetchLanguage();
+	},
+
+	fetchLanguage: function(res) {
+		var grid = Ext.getCmp('em-extlanguagegrid');
+		var row = this.extCount - this.extkeyArray.length;
+		var record = grid.store.getAt(row);
+		var i;
+
+		// res is response from request
+		// array selectedLanguage key => grid html
+
+		if (res) {
+			// update fetched record
+			var fetchedRecord = grid.store.getAt(row-1);
+			var key = '';
+			for (i = 0; i < this.selectedLanguages.length; i++) {
+				key = this.selectedLanguages[i];
+				fetchedRecord.set(key, res[0][key]);
+    		}
+    		fetchedRecord.commit();
+		}
+
+		if(this.extkeyArray.length > 0) {
+			var ext = this.extkeyArray.shift();
+
+
+			//update Grid
+			grid.getView().focusRow(row);
+			grid.getSelectionModel().selectRow(row);
+			for (i = 0; i < this.selectedLanguages.length; i++) {
+				record.set(this.selectedLanguages[i], TYPO3.lang.translation_checking);
+    		}
+    		record.commit();
+
+			// update Progressbar
+			Ext.getCmp('langpb').updateProgress(
+				(row+1)/this.extCount,
+				(this.fetchType === 0 ?
+						TYPO3.lang.msg_checking + ': ' :
+						TYPO3.lang.msg_updating + ': ') +
+					String.format(TYPO3.lang.translation_fetch_extension, ext, (row+1), this.extCount));
+
+			// fetch language request
+			TYPO3.EM.ExtDirect.fetchTranslations(ext, this.fetchType, this.selectedLanguages, function(response) {
+				this.fetchLanguage(response);
+			}, this);
+		} else {
+			// finished
+			Ext.getCmp('lang-checkbutton').enable();
+			Ext.getCmp('lang-updatebutton').enable();
+			// call callback
+			this.cb();
+		}
+	},
+
+
+	restoreExtLanguageGrid: function() {
+
+		var extLangGrid = Ext.getCmp('em-extlanguagegrid');
+		var i;
+
+		var selLanguages = Ext.getCmp('em-languagegrid').getSelectionModel().getSelections();
+		var columns = extLangGrid.getColumnModel();
+		var count = columns.getColumnCount();
+		if (count > 1) {
+			for(i=1; i<count; i++) {
+				columns.removeColumn(1);
+			}
+		}
+		if (selLanguages.length > 0 ) {
+			for (i=0; i < selLanguages.length; i++) {
+				this.addRemoveExtLanguageGridColumn(selLanguages[i].data);
+			}
+		}
+	},
+
+	addRemoveExtLanguageGridColumn: function(record) {
+		var extLangGrid = Ext.getCmp('em-extlanguagegrid');
+		var columns = extLangGrid.getColumnModel();
+		var index = columns.getIndexById(record.lang);
+
+		if (index === -1) {
+			extLangGrid.addColumn({
+				name: record.lang,
+				defaultValue: TYPO3.lang.translation_status_notchecked
+			}, {
+				header: '<span class="' + record.cls + '">&nbsp</span>' + record.label,
+				dataIndex: record.lang,
+				id: record.lang
+			});
+		} else {
+			columns.removeColumn(index);
+		}
+	},
+
+	langColumnRenderer: function(value) {
+		if (value === 'update') {
+			return '<div style="background:#ff0;">' + TYPO3.lang.translation_status_update + '</div>';
+		} else if(value === 'N/A') {
+			return '<div style="background:red;">' + TYPO3.lang.translation_n_a + '</div>';
+		} else if(value === 'ok') {
+			return '<div style="background:#69a550;">' + TYPO3.lang.translation_status_ok + '</div>';
+		} else if(value === 'new') {
+			return '<div style="background:#ff0;">' + TYPO3.lang.translation_status_new + '</div>';
+		} else {
+			return '<i>' + value + '</i>';
+		}
+	},
+
+	onRender:function() {
+
+
+
+		// call parent
+		TYPO3.EM.Languages.superclass.onRender.apply(this, arguments);
+
+	}
+});
+Ext.reg('extlanguages', TYPO3.EM.Languages);
Index: typo3/sysext/em/res/js/ux/filter/Filter.js
===================================================================
--- typo3/sysext/em/res/js/ux/filter/Filter.js	(revision )
+++ typo3/sysext/em/res/js/ux/filter/Filter.js	(revision )
@@ -0,0 +1,185 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+Ext.namespace('Ext.ux.grid.filter');
+
+/** 
+ * @class Ext.ux.grid.filter.Filter
+ * @extends Ext.util.Observable
+ * Abstract base class for filter implementations.
+ */
+Ext.ux.grid.filter.Filter = Ext.extend(Ext.util.Observable, {
+    /**
+     * @cfg {Boolean} active
+     * Indicates the initial status of the filter (defaults to false).
+     */
+    active : false,
+    /**
+     * True if this filter is active.  Use setActive() to alter after configuration.
+     * @type Boolean
+     * @property active
+     */
+    /**
+     * @cfg {String} dataIndex 
+     * The {@link Ext.data.Store} dataIndex of the field this filter represents.
+     * The dataIndex does not actually have to exist in the store.
+     */
+    dataIndex : null,
+    /**
+     * The filter configuration menu that will be installed into the filter submenu of a column menu.
+     * @type Ext.menu.Menu
+     * @property
+     */
+    menu : null,
+    /**
+     * @cfg {Number} updateBuffer
+     * Number of milliseconds to wait after user interaction to fire an update. Only supported 
+     * by filters: 'list', 'numeric', and 'string'. Defaults to 500.
+     */
+    updateBuffer : 500,
+
+    constructor : function (config) {
+        Ext.apply(this, config);
+            
+        this.addEvents(
+            /**
+             * @event activate
+             * Fires when an inactive filter becomes active
+             * @param {Ext.ux.grid.filter.Filter} this
+             */
+            'activate',
+            /**
+             * @event deactivate
+             * Fires when an active filter becomes inactive
+             * @param {Ext.ux.grid.filter.Filter} this
+             */
+            'deactivate',
+            /**
+             * @event serialize
+             * Fires after the serialization process. Use this to attach additional parameters to serialization
+             * data before it is encoded and sent to the server.
+             * @param {Array/Object} data A map or collection of maps representing the current filter configuration.
+             * @param {Ext.ux.grid.filter.Filter} filter The filter being serialized.
+             */
+            'serialize',
+            /**
+             * @event update
+             * Fires when a filter configuration has changed
+             * @param {Ext.ux.grid.filter.Filter} this The filter object.
+             */
+            'update'
+        );
+        Ext.ux.grid.filter.Filter.superclass.constructor.call(this);
+
+        this.menu = new Ext.menu.Menu();
+        this.init(config);
+        if(config && config.value){
+            this.setValue(config.value);
+            this.setActive(config.active !== false, true);
+            delete config.value;
+        }
+    },
+
+    /**
+     * Destroys this filter by purging any event listeners, and removing any menus.
+     */
+    destroy : function(){
+        if (this.menu){
+            this.menu.destroy();
+        }
+        this.purgeListeners();
+    },
+
+    /**
+     * Template method to be implemented by all subclasses that is to
+     * initialize the filter and install required menu items.
+     * Defaults to Ext.emptyFn.
+     */
+    init : Ext.emptyFn,
+    
+    /**
+     * Template method to be implemented by all subclasses that is to
+     * get and return the value of the filter.
+     * Defaults to Ext.emptyFn.
+     * @return {Object} The 'serialized' form of this filter
+     * @methodOf Ext.ux.grid.filter.Filter
+     */
+    getValue : Ext.emptyFn,
+    
+    /**
+     * Template method to be implemented by all subclasses that is to
+     * set the value of the filter and fire the 'update' event.
+     * Defaults to Ext.emptyFn.
+     * @param {Object} data The value to set the filter
+     * @methodOf Ext.ux.grid.filter.Filter
+     */	
+    setValue : Ext.emptyFn,
+    
+    /**
+     * Template method to be implemented by all subclasses that is to
+     * return <tt>true</tt> if the filter has enough configuration information to be activated.
+     * Defaults to <tt>return true</tt>.
+     * @return {Boolean}
+     */
+    isActivatable : function(){
+        return true;
+    },
+    
+    /**
+     * Template method to be implemented by all subclasses that is to
+     * get and return serialized filter data for transmission to the server.
+     * Defaults to Ext.emptyFn.
+     */
+    getSerialArgs : Ext.emptyFn,
+
+    /**
+     * Template method to be implemented by all subclasses that is to
+     * validates the provided Ext.data.Record against the filters configuration.
+     * Defaults to <tt>return true</tt>.
+     * @param {Ext.data.Record} record The record to validate
+     * @return {Boolean} true if the record is valid within the bounds
+     * of the filter, false otherwise.
+     */
+    validateRecord : function(){
+        return true;
+    },
+
+    /**
+     * Returns the serialized filter data for transmission to the server
+     * and fires the 'serialize' event.
+     * @return {Object/Array} An object or collection of objects containing
+     * key value pairs representing the current configuration of the filter.
+     * @methodOf Ext.ux.grid.filter.Filter
+     */
+    serialize : function(){
+        var args = this.getSerialArgs();
+        this.fireEvent('serialize', args, this);
+        return args;
+    },
+
+    /** @private */
+    fireUpdate : function(){
+        if (this.active) {
+            this.fireEvent('update', this);
+        }
+        this.setActive(this.isActivatable());
+    },
+    
+    /**
+     * Sets the status of the filter and fires the appropriate events.
+     * @param {Boolean} active        The new filter state.
+     * @param {Boolean} suppressEvent True to prevent events from being fired.
+     * @methodOf Ext.ux.grid.filter.Filter
+     */
+    setActive : function(active, suppressEvent){
+        if(this.active != active){
+            this.active = active;
+            if (suppressEvent !== true) {
+                this.fireEvent(active ? 'activate' : 'deactivate', this);
+            }
+        }
+    }    
+});
\ No newline at end of file
Index: typo3/sysext/em/classes/connection/class.tx_em_connection_ter.php
===================================================================
--- typo3/sysext/em/classes/connection/class.tx_em_connection_ter.php	(revision 9674)
+++ typo3/sysext/em/classes/connection/class.tx_em_connection_ter.php	(revision )
@@ -345,10 +345,17 @@
 
 		$soap = t3lib_div::makeInstance('tx_em_Connection_Soap');
 		$soap->init(array('wsdl' => $this->wsdlURL, 'soapoptions' => array('trace' => 1, 'exceptions' => 0)));
-		$response = $soap->call('uploadExtension', array('accountData' => $accountData, 'extensionData' => $extensionData, 'filesData' => $filesData));
+		$response = $soap->call(
+			'uploadExtension',
+			array(
+				'accountData' => $accountData,
+				'extensionData' => $extensionData,
+				'filesData' => $filesData
+			)
+		);
 
-		if ($response===false) {
-			switch (true) {
+		if ($response === FALSE) {
+			switch (TRUE) {
 				case is_string($soap->error):
 					return $soap->error;
 					break;
Index: typo3/sysext/em/res/js/ux/searchfield.js
===================================================================
--- typo3/sysext/em/res/js/ux/searchfield.js	(revision )
+++ typo3/sysext/em/res/js/ux/searchfield.js	(revision )
@@ -0,0 +1,94 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+Ext.ns('Ext.ux.form');
+	
+Ext.ux.form.SearchField = Ext.extend(Ext.form.TwinTriggerField, {
+	initComponent : function(){
+	
+		Ext.ux.form.SearchField.superclass.initComponent.call(this);
+		this.on('specialkey', function(f, e){
+			if(e.getKey() == e.ENTER){
+				this.onTrigger2Click();
+			}
+		}, this);
+	},
+	
+	validationEvent:false,
+	validateOnBlur:false,
+	trigger1Class:'x-form-clear-trigger',
+	trigger2Class:'x-form-search-trigger',
+	hideTrigger1:true,
+	width:180,
+	hasSearch : false,
+	paramName : 'query',
+
+	onTrigger1Click : function(){
+		if(this.hasSearch){
+			this.el.dom.value = '';
+			var o = {start: 0};
+			this.store.baseParams = this.store.baseParams || {};
+			this.store.baseParams[this.paramName] = '';
+			this.store.reload({params:o});
+			this.triggers[0].hide();
+			this.hasSearch = false;
+		}
+	},
+
+	onTrigger2Click : function(){
+		var v = this.getRawValue();
+		if(v.length < 1){
+			this.onTrigger1Click();
+			return;
+		}
+		var o = {start: 0};
+		this.store.baseParams = this.store.baseParams || {};
+		this.store.baseParams[this.paramName] = v;
+		this.store.reload({params:o});
+		this.hasSearch = true;
+		this.triggers[0].show();
+	}
+});
+
+Ext.ux.form.FilterField = Ext.extend(Ext.form.TwinTriggerField, {
+	initComponent : function(){
+		Ext.ux.form.SearchField.superclass.initComponent.call(this);
+		this.on('specialkey', function(f, e){
+			if(e.getKey() == e.ENTER){
+				this.onTrigger2Click();
+			}
+		}, this);
+	},
+	
+	validationEvent:false,
+	validateOnBlur:false,
+	trigger1Class:'x-form-clear-trigger',
+	trigger2Class:'x-form-search-trigger',
+	hideTrigger1:true,
+	width:180,
+	hasSearch : false,
+	paramName : 'query',
+
+	onTrigger1Click : function(){
+		if(this.hasSearch){
+			this.el.dom.value = '';
+			this.triggers[0].hide();
+			this.hasSearch = false;
+			this.store.filterBy(this.store.storeFilter);
+		}
+	},
+
+	onTrigger2Click : function(){
+		var v = this.getRawValue();
+		if(v.length < 1){
+			this.onTrigger1Click();
+			return;
+		}
+		this.store.filterBy(this.store.storeFilter);
+		this.hasSearch = true;
+		this.triggers[0].show();
+	}
+});
\ No newline at end of file
Index: typo3/sysext/em/res/js/ux/filter/DateFilter.js
===================================================================
--- typo3/sysext/em/res/js/ux/filter/DateFilter.js	(revision )
+++ typo3/sysext/em/res/js/ux/filter/DateFilter.js	(revision )
@@ -0,0 +1,313 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+/** 
+ * @class Ext.ux.grid.filter.DateFilter
+ * @extends Ext.ux.grid.filter.Filter
+ * Filter by a configurable Ext.menu.DateMenu
+ * <p><b><u>Example Usage:</u></b></p>
+ * <pre><code>    
+var filters = new Ext.ux.grid.GridFilters({
+    ...
+    filters: [{
+        // required configs
+        type: 'date',
+        dataIndex: 'dateAdded',
+        
+        // optional configs
+        dateFormat: 'm/d/Y',  // default
+        beforeText: 'Before', // default
+        afterText: 'After',   // default
+        onText: 'On',         // default
+        pickerOpts: {
+            // any DateMenu configs
+        },
+
+        active: true // default is false
+    }]
+});
+ * </code></pre>
+ */
+Ext.ux.grid.filter.DateFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
+    /**
+     * @cfg {String} afterText
+     * Defaults to 'After'.
+     */
+    afterText : 'After',
+    /**
+     * @cfg {String} beforeText
+     * Defaults to 'Before'.
+     */
+    beforeText : 'Before',
+    /**
+     * @cfg {Object} compareMap
+     * Map for assigning the comparison values used in serialization.
+     */
+    compareMap : {
+        before: 'lt',
+        after:  'gt',
+        on:     'eq'
+    },
+    /**
+     * @cfg {String} dateFormat
+     * The date format to return when using getValue.
+     * Defaults to 'm/d/Y'.
+     */
+    dateFormat : 'm/d/Y',
+
+    /**
+     * @cfg {Date} maxDate
+     * Allowable date as passed to the Ext.DatePicker
+     * Defaults to undefined.
+     */
+    /**
+     * @cfg {Date} minDate
+     * Allowable date as passed to the Ext.DatePicker
+     * Defaults to undefined.
+     */
+    /**
+     * @cfg {Array} menuItems
+     * The items to be shown in this menu
+     * Defaults to:<pre>
+     * menuItems : ['before', 'after', '-', 'on'],
+     * </pre>
+     */
+    menuItems : ['before', 'after', '-', 'on'],
+
+    /**
+     * @cfg {Object} menuItemCfgs
+     * Default configuration options for each menu item
+     */
+    menuItemCfgs : {
+        selectOnFocus: true,
+        width: 125
+    },
+
+    /**
+     * @cfg {String} onText
+     * Defaults to 'On'.
+     */
+    onText : 'On',
+    
+    /**
+     * @cfg {Object} pickerOpts
+     * Configuration options for the date picker associated with each field.
+     */
+    pickerOpts : {},
+
+    /**  
+     * @private
+     * Template method that is to initialize the filter and install required menu items.
+     */
+    init : function (config) {
+        var menuCfg, i, len, item, cfg, Cls;
+
+        menuCfg = Ext.apply(this.pickerOpts, {
+            minDate: this.minDate, 
+            maxDate: this.maxDate, 
+            format:  this.dateFormat,
+            listeners: {
+                scope: this,
+                select: this.onMenuSelect
+            }
+        });
+
+        this.fields = {};
+        for (i = 0, len = this.menuItems.length; i < len; i++) {
+            item = this.menuItems[i];
+            if (item !== '-') {
+                cfg = {
+                    itemId: 'range-' + item,
+                    text: this[item + 'Text'],
+                    menu: new Ext.menu.DateMenu(
+                        Ext.apply(menuCfg, {
+                            itemId: item
+                        })
+                    ),
+                    listeners: {
+                        scope: this,
+                        checkchange: this.onCheckChange
+                    }
+                };
+                Cls = Ext.menu.CheckItem;
+                item = this.fields[item] = new Cls(cfg);
+            }
+            //this.add(item);
+            this.menu.add(item);
+        }
+    },
+
+    onCheckChange : function () {
+        this.setActive(this.isActivatable());
+        this.fireEvent('update', this);
+    },
+
+    /**  
+     * @private
+     * Handler method called when there is a keyup event on an input
+     * item of this menu.
+     */
+    onInputKeyUp : function (field, e) {
+        var k = e.getKey();
+        if (k == e.RETURN && field.isValid()) {
+            e.stopEvent();
+            this.menu.hide(true);
+            return;
+        }
+    },
+
+    /**
+     * Handler for when the menu for a field fires the 'select' event
+     * @param {Object} date
+     * @param {Object} menuItem
+     * @param {Object} value
+     * @param {Object} picker
+     */
+    onMenuSelect : function (menuItem, value, picker) {
+        var fields = this.fields,
+            field = this.fields[menuItem.itemId];
+        
+        field.setChecked(true);
+        
+        if (field == fields.on) {
+            fields.before.setChecked(false, true);
+            fields.after.setChecked(false, true);
+        } else {
+            fields.on.setChecked(false, true);
+            if (field == fields.after && fields.before.menu.picker.value < value) {
+                fields.before.setChecked(false, true);
+            } else if (field == fields.before && fields.after.menu.picker.value > value) {
+                fields.after.setChecked(false, true);
+            }
+        }
+        this.fireEvent('update', this);
+    },
+
+    /**
+     * @private
+     * Template method that is to get and return the value of the filter.
+     * @return {String} The value of this filter
+     */
+    getValue : function () {
+        var key, result = {};
+        for (key in this.fields) {
+            if (this.fields[key].checked) {
+                result[key] = this.fields[key].menu.picker.getValue();
+            }
+        }
+        return result;
+    },
+
+    /**
+     * @private
+     * Template method that is to set the value of the filter.
+     * @param {Object} value The value to set the filter
+     * @param {Boolean} preserve true to preserve the checked status
+     * of the other fields.  Defaults to false, unchecking the
+     * other fields
+     */	
+    setValue : function (value, preserve) {
+        var key;
+        for (key in this.fields) {
+            if(value[key]){
+                this.fields[key].menu.picker.setValue(value[key]);
+                this.fields[key].setChecked(true);
+            } else if (!preserve) {
+                this.fields[key].setChecked(false);
+            }
+        }
+        this.fireEvent('update', this);
+    },
+
+    /**
+     * @private
+     * Template method that is to return <tt>true</tt> if the filter
+     * has enough configuration information to be activated.
+     * @return {Boolean}
+     */
+    isActivatable : function () {
+        var key;
+        for (key in this.fields) {
+            if (this.fields[key].checked) {
+                return true;
+            }
+        }
+        return false;
+    },
+
+    /**
+     * @private
+     * Template method that is to get and return serialized filter data for
+     * transmission to the server.
+     * @return {Object/Array} An object or collection of objects containing
+     * key value pairs representing the current configuration of the filter.
+     */
+    getSerialArgs : function () {
+        var args = [];
+        for (var key in this.fields) {
+            if(this.fields[key].checked){
+                args.push({
+                    type: 'date',
+                    comparison: this.compareMap[key],
+                    value: this.getFieldValue(key).format(this.dateFormat)
+                });
+            }
+        }
+        return args;
+    },
+
+    /**
+     * Get and return the date menu picker value
+     * @param {String} item The field identifier ('before', 'after', 'on')
+     * @return {Date} Gets the current selected value of the date field
+     */
+    getFieldValue : function(item){
+        return this.fields[item].menu.picker.getValue();
+    },
+    
+    /**
+     * Gets the menu picker associated with the passed field
+     * @param {String} item The field identifier ('before', 'after', 'on')
+     * @return {Object} The menu picker
+     */
+    getPicker : function(item){
+        return this.fields[item].menu.picker;
+    },
+
+    /**
+     * Template method that is to validate the provided Ext.data.Record
+     * against the filters configuration.
+     * @param {Ext.data.Record} record The record to validate
+     * @return {Boolean} true if the record is valid within the bounds
+     * of the filter, false otherwise.
+     */
+    validateRecord : function (record) {
+        var key,
+            pickerValue,
+            val = record.get(this.dataIndex);
+            
+        if(!Ext.isDate(val)){
+            return false;
+        }
+        val = val.clearTime(true).getTime();
+        
+        for (key in this.fields) {
+            if (this.fields[key].checked) {
+                pickerValue = this.getFieldValue(key).clearTime(true).getTime();
+                if (key == 'before' && pickerValue <= val) {
+                    return false;
+                }
+                if (key == 'after' && pickerValue >= val) {
+                    return false;
+                }
+                if (key == 'on' && pickerValue != val) {
+                    return false;
+                }
+            }
+        }
+        return true;
+    }
+});
\ No newline at end of file
Index: typo3/sysext/em/res/js/ux/menu/ListMenu.js
===================================================================
--- typo3/sysext/em/res/js/ux/menu/ListMenu.js	(revision )
+++ typo3/sysext/em/res/js/ux/menu/ListMenu.js	(revision )
@@ -0,0 +1,177 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+Ext.namespace('Ext.ux.menu');
+
+/** 
+ * @class Ext.ux.menu.ListMenu
+ * @extends Ext.menu.Menu
+ * This is a supporting class for {@link Ext.ux.grid.filter.ListFilter}.
+ * Although not listed as configuration options for this class, this class
+ * also accepts all configuration options from {@link Ext.ux.grid.filter.ListFilter}.
+ */
+Ext.ux.menu.ListMenu = Ext.extend(Ext.menu.Menu, {
+    /**
+     * @cfg {String} labelField
+     * Defaults to 'text'.
+     */
+    labelField :  'text',
+    /**
+     * @cfg {String} paramPrefix
+     * Defaults to 'Loading...'.
+     */
+    loadingText : 'Loading...',
+    /**
+     * @cfg {Boolean} loadOnShow
+     * Defaults to true.
+     */
+    loadOnShow : true,
+    /**
+     * @cfg {Boolean} single
+     * Specify true to group all items in this list into a single-select
+     * radio button group. Defaults to false.
+     */
+    single : false,
+
+    constructor : function (cfg) {
+        this.selected = [];
+        this.addEvents(
+            /**
+             * @event checkchange
+             * Fires when there is a change in checked items from this list
+             * @param {Object} item Ext.menu.CheckItem
+             * @param {Object} checked The checked value that was set
+             */
+            'checkchange'
+        );
+      
+        Ext.ux.menu.ListMenu.superclass.constructor.call(this, cfg = cfg || {});
+    
+        if(!cfg.store && cfg.options){
+            var options = [];
+            for(var i=0, len=cfg.options.length; i<len; i++){
+                var value = cfg.options[i];
+                switch(Ext.type(value)){
+                    case 'array':  options.push(value); break;
+                    case 'object': options.push([value.id, value[this.labelField]]); break;
+                    case 'string': options.push([value, value]); break;
+                }
+            }
+            
+            this.store = new Ext.data.Store({
+                reader: new Ext.data.ArrayReader({id: 0}, ['id', this.labelField]),
+                data:   options,
+                listeners: {
+                    'load': this.onLoad,
+                    scope:  this
+                }
+            });
+            this.loaded = true;
+        } else {
+            this.add({text: this.loadingText, iconCls: 'loading-indicator'});
+            this.store.on('load', this.onLoad, this);
+        }
+    },
+
+    destroy : function () {
+        if (this.store) {
+            this.store.destroy();    
+        }
+        Ext.ux.menu.ListMenu.superclass.destroy.call(this);
+    },
+
+    /**
+     * Lists will initially show a 'loading' item while the data is retrieved from the store.
+     * In some cases the loaded data will result in a list that goes off the screen to the
+     * right (as placement calculations were done with the loading item). This adapter will
+     * allow show to be called with no arguments to show with the previous arguments and
+     * thus recalculate the width and potentially hang the menu from the left.
+     */
+    show : function () {
+        var lastArgs = null;
+        return function(){
+            if(arguments.length === 0){
+                Ext.ux.menu.ListMenu.superclass.show.apply(this, lastArgs);
+            } else {
+                lastArgs = arguments;
+                if (this.loadOnShow && !this.loaded) {
+                    this.store.load();
+                }
+                Ext.ux.menu.ListMenu.superclass.show.apply(this, arguments);
+            }
+        };
+    }(),
+    
+    /** @private */
+    onLoad : function (store, records) {
+        var visible = this.isVisible();
+        this.hide(false);
+        
+        this.removeAll(true);
+        
+        var gid = this.single ? Ext.id() : null;
+        for(var i=0, len=records.length; i<len; i++){
+            var item = new Ext.menu.CheckItem({
+                text:    records[i].get(this.labelField), 
+                group:   gid,
+                checked: this.selected.indexOf(records[i].id) > -1,
+                hideOnClick: false});
+            
+            item.itemId = records[i].id;
+            item.on('checkchange', this.checkChange, this);
+                        
+            this.add(item);
+        }
+        
+        this.loaded = true;
+        
+        if (visible) {
+            this.show();
+        }	
+        this.fireEvent('load', this, records);
+    },
+
+    /**
+     * Get the selected items.
+     * @return {Array} selected
+     */
+    getSelected : function () {
+        return this.selected;
+    },
+    
+    /** @private */
+    setSelected : function (value) {
+        value = this.selected = [].concat(value);
+
+        if (this.loaded) {
+            this.items.each(function(item){
+                item.setChecked(false, true);
+                for (var i = 0, len = value.length; i < len; i++) {
+                    if (item.itemId == value[i]) {
+                        item.setChecked(true, true);
+                    }
+                }
+            }, this);
+        }
+    },
+    
+    /**
+     * Handler for the 'checkchange' event from an check item in this menu
+     * @param {Object} item Ext.menu.CheckItem
+     * @param {Object} checked The checked value that was set
+     */
+    checkChange : function (item, checked) {
+        var value = [];
+        this.items.each(function(item){
+            if (item.checked) {
+                value.push(item.itemId);
+            }
+        },this);
+        this.selected = value;
+        
+        this.fireEvent('checkchange', item, checked);
+    }    
+});
\ No newline at end of file
Index: typo3/sysext/em/res/js/ux/custom_plugins.js
===================================================================
--- typo3/sysext/em/res/js/ux/custom_plugins.js	(revision )
+++ typo3/sysext/em/res/js/ux/custom_plugins.js	(revision )
@@ -0,0 +1,92 @@
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+
+Ext.ns('Ext.ux');
+
+Ext.ux.Image = Ext.extend(Ext.BoxComponent, {
+	initComponent: function() {
+		Ext.ux.Image.superclass.initComponent.apply(this, arguments);
+		this.addEvents('load');
+	},
+
+	onRender: function() {
+		this.autoEl = {
+			cn: {tag: 'img', src: this.src}
+		}
+		Ext.ux.Image.superclass.onRender.apply(this, arguments);
+		this.positionEl = this.el;
+		this.el = this.resizeEl = Ext.get(this.positionEl.dom.firstChild);
+		this.el.on('load', this.onLoad, this);
+	},
+
+	onResize: function() {
+		this.positionEl.setSize(this.getSize());
+	},
+
+	onLoad: function() {
+		if (this.autoSize) {
+			this.syncSize();
+		}
+		if (this.resizable && !this.resizer) {
+			this.resizer = new Ext.Resizable(this.positionEl, {
+				preserveRatio: true,
+				handles: 'all',
+				draggable: true,
+				dynamic: true,
+				resizeChild: true
+			});
+		}
+		this.fireEvent('load', this);
+	}
+});
+Ext.reg('image', Ext.ux.Image);
+
+Ext.grid.DynamicColumnModelForLanguages = function(store){
+	var cols = [];
+	var recordType = store.recordType;
+	var fields = recordType.prototype.fields;
+
+	for (var i = 0; i < fields.keys.length; i++) {
+		var fieldName = fields.keys[i];
+		var field = recordType.getField(fieldName);
+
+		if (i === 0) {
+			cols[i] = {
+				header: 'Extension',
+				dataIndex: field.name,
+				width:180,
+				fixed: true,
+				sortable: false,
+				hidable: false,
+				menuDisabled: true,
+				renderer:function(value, metaData, record, rowIndex, colIndex, store){
+					return record.data.icon + ' ' + value;
+				}
+			};
+		} else if (i === 1 || i === 2 || i === 3) {
+			//bypass
+		} else {
+			cols[i-3] = {
+				header: field.name,
+				dataIndex: field.name,
+				width:100,
+				hidden: true,
+				fixed: true,
+				sortable: false,
+				hidable: false,
+				menuDisabled: true
+			};
+
+		}
+	}
+	Ext.grid.DynamicColumnModelForLanguages.superclass.constructor.call(this, cols);
+};
+Ext.extend(Ext.grid.DynamicColumnModelForLanguages, Ext.grid.ColumnModel, {});
+
Index: typo3/sysext/em/res/js/ux/filter/BooleanFilter.js
===================================================================
--- typo3/sysext/em/res/js/ux/filter/BooleanFilter.js	(revision )
+++ typo3/sysext/em/res/js/ux/filter/BooleanFilter.js	(revision )
@@ -0,0 +1,103 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+/** 
+ * @class Ext.ux.grid.filter.BooleanFilter
+ * @extends Ext.ux.grid.filter.Filter
+ * Boolean filters use unique radio group IDs (so you can have more than one!)
+ * <p><b><u>Example Usage:</u></b></p>
+ * <pre><code>    
+var filters = new Ext.ux.grid.GridFilters({
+    ...
+    filters: [{
+        // required configs
+        type: 'boolean',
+        dataIndex: 'visible'
+
+        // optional configs
+        defaultValue: null, // leave unselected (false selected by default)
+        yesText: 'Yes',     // default
+        noText: 'No'        // default
+    }]
+});
+ * </code></pre>
+ */
+Ext.ux.grid.filter.BooleanFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
+	/**
+	 * @cfg {Boolean} defaultValue
+	 * Set this to null if you do not want either option to be checked by default. Defaults to false.
+	 */
+	defaultValue : false,
+	/**
+	 * @cfg {String} yesText
+	 * Defaults to 'Yes'.
+	 */
+	yesText : 'Yes',
+	/**
+	 * @cfg {String} noText
+	 * Defaults to 'No'.
+	 */
+	noText : 'No',
+
+    /**  
+     * @private
+     * Template method that is to initialize the filter and install required menu items.
+     */
+    init : function (config) {
+        var gId = Ext.id();
+		this.options = [
+			new Ext.menu.CheckItem({text: this.yesText, group: gId, checked: this.defaultValue === true}),
+			new Ext.menu.CheckItem({text: this.noText, group: gId, checked: this.defaultValue === false})];
+		
+		this.menu.add(this.options[0], this.options[1]);
+		
+		for(var i=0; i<this.options.length; i++){
+			this.options[i].on('click', this.fireUpdate, this);
+			this.options[i].on('checkchange', this.fireUpdate, this);
+		}
+	},
+	
+    /**
+     * @private
+     * Template method that is to get and return the value of the filter.
+     * @return {String} The value of this filter
+     */
+    getValue : function () {
+		return this.options[0].checked;
+	},
+
+    /**
+     * @private
+     * Template method that is to set the value of the filter.
+     * @param {Object} value The value to set the filter
+     */	
+	setValue : function (value) {
+		this.options[value ? 0 : 1].setChecked(true);
+	},
+
+    /**
+     * @private
+     * Template method that is to get and return serialized filter data for
+     * transmission to the server.
+     * @return {Object/Array} An object or collection of objects containing
+     * key value pairs representing the current configuration of the filter.
+     */
+    getSerialArgs : function () {
+		var args = {type: 'boolean', value: this.getValue()};
+		return args;
+	},
+	
+    /**
+     * Template method that is to validate the provided Ext.data.Record
+     * against the filters configuration.
+     * @param {Ext.data.Record} record The record to validate
+     * @return {Boolean} true if the record is valid within the bounds
+     * of the filter, false otherwise.
+     */
+    validateRecord : function (record) {
+		return record.get(this.dataIndex) == this.getValue();
+	}
+});
\ No newline at end of file
Index: typo3/sysext/em/res/js/ux/GridFilters.js
===================================================================
--- typo3/sysext/em/res/js/ux/GridFilters.js	(revision )
+++ typo3/sysext/em/res/js/ux/GridFilters.js	(revision )
@@ -0,0 +1,354 @@
+/**
+ * Ext.ux.grid.GridFilters v0.2.7
+ **/
+
+Ext.namespace("Ext.ux.grid");
+Ext.ux.grid.GridFilters = function(config){		
+	this.filters = new Ext.util.MixedCollection();
+	this.filters.getKey = function(o){return o ? o.dataIndex : null};
+	
+	for(var i=0, len=config.filters.length; i<len; i++)
+		this.addFilter(config.filters[i]);
+	
+	this.deferredUpdate = new Ext.util.DelayedTask(this.reload, this);
+	
+	delete config.filters;
+	Ext.apply(this, config);
+};
+Ext.extend(Ext.ux.grid.GridFilters, Ext.util.Observable, {
+	/**
+	 * @cfg {Integer} updateBuffer
+	 * Number of milisecond to defer store updates since the last filter change.
+	 */
+	updateBuffer: 500,
+	/**
+	 * @cfg {String} paramPrefix
+	 * The url parameter prefix for the filters.
+	 */
+	paramPrefix: 'filter',
+	/**
+	 * @cfg {String} fitlerCls
+	 * The css class to be applied to column headers that active filters. Defaults to 'ux-filterd-column'
+	 */
+	filterCls: 'ux-filtered-column',
+	/**
+	 * @cfg {Boolean} local
+	 * True to use Ext.data.Store filter functions instead of server side filtering.
+	 */
+	local: false,
+	/**
+	 * @cfg {Boolean} autoReload
+	 * True to automagicly reload the datasource when a filter change happens.
+	 */
+	autoReload: true,
+	/**
+	 * @cfg {String} stateId
+	 * Name of the Ext.data.Store value to be used to store state information.
+	 */
+	stateId: undefined,
+	/**
+	 * @cfg {Boolean} showMenu
+	 * True to show the filter menus
+	 */
+	showMenu: true,
+
+	menuFilterText: 'Filters',
+
+	init: function(grid){
+		if(grid instanceof Ext.grid.GridPanel){
+			this.grid  = grid;
+		  
+			this.store = this.grid.getStore();
+			if(this.local){
+				this.store.on('load', function(store){
+					store.filterBy(this.getRecordFilter());
+				}, this);
+			} else {
+			  this.store.on('beforeload', this.onBeforeLoad, this);
+			}
+			  
+			this.grid.filters = this;
+			 
+			this.grid.addEvents({"filterupdate": true});
+			  
+			grid.on("render", this.onRender, this);
+			  
+			grid.on("beforestaterestore", this.applyState, this);
+			grid.on("beforestatesave", this.saveState, this);
+					  
+		} else if(grid instanceof Ext.PagingToolbar){
+		  this.toolbar = grid;
+		}
+	},
+		
+	/** private **/
+	applyState: function(grid, state){
+		this.suspendStateStore = true;
+		this.clearFilters();
+		if(state.filters)
+			for(var key in state.filters){
+				var filter = this.filters.get(key);
+				if(filter){
+					filter.setValue(state.filters[key]);
+					filter.setActive(true);
+				}
+			}
+			
+		this.deferredUpdate.cancel();
+		if(this.local)
+			this.reload();
+			
+		this.suspendStateStore = false;
+	},
+	
+	/** private **/
+	saveState: function(grid, state){
+		var filters = {};
+		this.filters.each(function(filter){
+			if(filter.active)
+				filters[filter.dataIndex] = filter.getValue();
+		});
+		return state.filters = filters;
+	},
+	
+	/** private **/
+	onRender: function(){
+		var hmenu;
+		
+		if(this.showMenu){
+			hmenu = this.grid.getView().hmenu;
+			
+			this.sep  = hmenu.addSeparator();
+			this.menu = hmenu.add(new Ext.menu.CheckItem({
+					text: this.menuFilterText,
+					menu: new Ext.menu.Menu()
+				}));
+			this.menu.on('checkchange', this.onCheckChange, this);
+			this.menu.on('beforecheckchange', this.onBeforeCheck, this);
+				
+			hmenu.on('beforeshow', this.onMenu, this);
+		}
+		
+		this.grid.getView().on("refresh", this.onRefresh, this);
+		this.updateColumnHeadings(this.grid.getView());
+	},
+	
+	/** private **/
+	onMenu: function(filterMenu){
+		var filter = this.getMenuFilter();
+		if(filter){
+			this.menu.menu = filter.menu;
+			this.menu.setChecked(filter.active, false);
+		}
+		
+		this.menu.setVisible(filter !== undefined);
+		this.sep.setVisible(filter !== undefined);
+	},
+	
+	/** private **/
+	onCheckChange: function(item, value){
+		this.getMenuFilter().setActive(value);
+	},
+	
+	/** private **/
+	onBeforeCheck: function(check, value){
+		return !value || this.getMenuFilter().isActivatable();
+	},
+	
+	/** private **/
+	onStateChange: function(event, filter){
+    if(event == "serialize") return;
+    
+		if(filter == this.getMenuFilter())
+			this.menu.setChecked(filter.active, false);
+			
+		if(this.autoReload || this.local)
+			this.deferredUpdate.delay(this.updateBuffer);
+		
+		var view = this.grid.getView();
+		this.updateColumnHeadings(view);
+			
+		this.grid.saveState();
+			
+		this.grid.fireEvent('filterupdate', this, filter);
+	},
+	
+	/** private **/
+	onBeforeLoad: function(store, options){
+    options.params = options.params || {};
+		this.cleanParams(options.params);		
+		var params = this.buildQuery(this.getFilterData());
+		Ext.apply(options.params, params);
+	},
+	
+	/** private **/
+	onRefresh: function(view){
+		this.updateColumnHeadings(view);
+	},
+	
+	/** private **/
+	getMenuFilter: function(){
+		var view = this.grid.getView();
+		if(!view || view.hdCtxIndex === undefined)
+			return null;
+		
+		return this.filters.get(
+			view.cm.config[view.hdCtxIndex].dataIndex);
+	},
+	
+	/** private **/
+	updateColumnHeadings: function(view){
+		if(!view || !view.mainHd) return;
+		
+		var hds = view.mainHd.select('td').removeClass(this.filterCls);
+		for(var i=0, len=view.cm.config.length; i<len; i++){
+			var filter = this.getFilter(view.cm.config[i].dataIndex);
+			if(filter && filter.active)
+				hds.item(i).addClass(this.filterCls);
+		}
+	},
+	
+	/** private **/
+	reload: function(){
+		if(this.local){
+			this.grid.store.clearFilter(true);
+			this.grid.store.filterBy(this.getRecordFilter());
+		} else {
+			this.deferredUpdate.cancel();
+			var store = this.grid.store;
+			if(this.toolbar){
+				var start = this.toolbar.paramNames.start;
+				if(store.lastOptions && store.lastOptions.params && store.lastOptions.params[start])
+					store.lastOptions.params[start] = 0;
+			}
+			store.reload();
+		}
+	},
+	
+	/**
+	 * Method factory that generates a record validator for the filters active at the time
+	 * of invokation.
+	 * 
+	 * @private
+	 */
+	getRecordFilter: function(){
+		var f = [];
+		this.filters.each(function(filter){
+			if(filter.active) f.push(filter);
+		});
+		
+		var len = f.length;
+		return function(record){
+			for(var i=0; i<len; i++)
+				if(!f[i].validateRecord(record))
+					return false;
+				
+			return true;
+		};
+	},
+	
+	/**
+	 * Adds a filter to the collection.
+	 * 
+	 * @param {Object/Ext.ux.grid.filter.Filter} config A filter configuration or a filter object.
+	 * 
+	 * @return {Ext.ux.grid.filter.Filter} The existing or newly created filter object.
+	 */
+	addFilter: function(config){
+		var filter = config.menu ? config : 
+				new (this.getFilterClass(config.type))(config);
+		this.filters.add(filter);
+		
+		Ext.util.Observable.capture(filter, this.onStateChange, this);
+		return filter;
+	},
+	
+	/**
+	 * Returns a filter for the given dataIndex, if on exists.
+	 * 
+	 * @param {String} dataIndex The dataIndex of the desired filter object.
+	 * 
+	 * @return {Ext.ux.grid.filter.Filter}
+	 */
+	getFilter: function(dataIndex){
+		return this.filters.get(dataIndex);
+	},
+
+	/**
+	 * Turns all filters off. This does not clear the configuration information.
+	 */
+	clearFilters: function(){
+		this.filters.each(function(filter){
+			filter.setActive(false);
+		});
+	},
+
+	/** private **/
+	getFilterData: function(){
+		var filters = [],
+			fields  = this.grid.getStore().fields;
+		
+		this.filters.each(function(f){
+			if(f.active){
+				var d = [].concat(f.serialize());
+				for(var i=0, len=d.length; i<len; i++)
+					filters.push({
+						field: f.dataIndex,
+						data: d[i]
+					});
+			}
+		});
+		
+		return filters;
+	},
+	
+	/**
+	 * Function to take structured filter data and 'flatten' it into query parameteres. The default function
+	 * will produce a query string of the form:
+	 * 		filters[0][field]=dataIndex&filters[0][data][param1]=param&filters[0][data][param2]=param...
+	 * 
+	 * @param {Array} filters A collection of objects representing active filters and their configuration.
+	 * 	  Each element will take the form of {field: dataIndex, data: filterConf}. dataIndex is not assured
+	 *    to be unique as any one filter may be a composite of more basic filters for the same dataIndex.
+	 * 
+	 * @return {Object} Query keys and values
+	 */
+	buildQuery: function(filters){
+		var p = {};
+		for(var i=0, len=filters.length; i<len; i++){
+			var f    = filters[i];
+			var root = [this.paramPrefix, '[', i, ']'].join('');
+			p[root + '[field]'] = f.field;
+			
+			var dataPrefix = root + '[data]';
+			for(var key in f.data)
+				p[[dataPrefix, '[', key, ']'].join('')] = f.data[key];
+		}
+		
+		return p;
+	},
+	
+	/**
+	 * Removes filter related query parameters from the provided object.
+	 * 
+	 * @param {Object} p Query parameters that may contain filter related fields.
+	 */
+	cleanParams: function(p){
+		var regex = new RegExp("^" + this.paramPrefix + "\[[0-9]+\]");
+		for(var key in p)
+			if(regex.test(key))
+				delete p[key];
+	},
+	
+	/**
+	 * Function for locating filter classes, overwrite this with your favorite
+	 * loader to provide dynamic filter loading.
+	 * 
+	 * @param {String} type The type of filter to load.
+	 * 
+	 * @return {Class}
+	 */
+	getFilterClass: function(type){
+		return Ext.ux.grid.filter[type.substr(0, 1).toUpperCase() + type.substr(1) + 'Filter'];
+	}
+});
\ No newline at end of file
Index: typo3/sysext/em/res/js/em_components.js
===================================================================
--- typo3/sysext/em/res/js/em_components.js	(revision )
+++ typo3/sysext/em/res/js/em_components.js	(revision )
@@ -0,0 +1,527 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <info@sk-typo3.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+Ext.ns('TYPO3.EM', 'TYPO3.EM.GridColumns', 'TYPO3.EM.ExtDirect', 'TYPO3.EMSOAP.ExtDirect');
+
+///////////////////////////////////////////////////////
+// Grid
+///////////////////////////////////////////////////////
+TYPO3.EM.Filters = new Ext.ux.grid.GridFilters({
+	encode: true,
+	local: true,
+	filters: [{
+		type: 'string',
+		dataIndex: 'author'
+		}, {
+		type: 'string',
+		dataIndex: 'category'
+		}, {
+		type: 'list',
+		dataIndex: 'state',
+		options: ['alpha', 'beta', 'stable', 'experimental', 'test', 'obsolete', 'excludeFromUpdates'],
+		phpMode: true
+		}, {
+		type: 'boolean',
+		dataIndex: 'installed'
+	}]
+});
+
+TYPO3.EM.GridColumns.InstallExtension = {
+	header: '',
+	width: 30,
+	sortable: false,
+	fixed: true,
+	groupable: false,
+	hideable: false,
+	menuDisabled: true,
+	xtype: 'actioncolumn',
+	items: [
+		{
+			getClass: function(value, meta, record) {
+				if (record.get('installed') == 0) {
+					this.items[0].tooltip = TYPO3.lang.menu_install_extensions;
+					return 't3-icon t3-icon-actions t3-icon-actions-system t3-icon-system-extension-install';
+				} else {
+					this.items[0].tooltip = TYPO3.lang.ext_details_remove_ext;
+					return 't3-icon t3-icon-actions t3-icon-actions-system t3-icon-system-extension-uninstall';
+				}
+			},
+			handler: function(grid, rowIndex, colIndex) {
+				var record = grid.store.getAt(rowIndex).data;
+				var action = record.installed ? TYPO3.lang.ext_details_remove_ext : TYPO3.lang.menu_install_extensions;
+				var link = TYPO3.settings.EM.scriptLink
+						+ '&nodoc=1&view=installed_list&CMD[showExt]=' + record.extkey
+						+ '&CMD[' + (record.installed ? 'remove' : 'load') + ']=1&CMD[clrCmd]=1&SET[singleDetails]=info';
+
+				var w = new TYPO3.EM.InstallWindow({
+				 	title: action + ': ' + record.title + ' (' + record.extkey + ') version ' + record.version
+				}).show(true, function(){
+					Ext.getCmp('emInstallIframeWindow').setUrl(link);
+				});
+			}
+		}
+	]
+}
+
+TYPO3.EM.GridColumns.ImportExtension = {
+	header: '',
+	width: 30,
+	sortable: false,
+	fixed: true,
+	groupable: false,
+	hideable: false,
+	menuDisabled: true,
+	xtype: 'actioncolumn',
+	items: [
+		{
+			getClass: function(value, meta, record) {
+				if (record.get('installed') == 0) {
+					this.items[0].tooltip = TYPO3.lang.menu_install_extensions;
+					return 't3-icon t3-icon-actions t3-icon-actions-system t3-icon-system-extension-install';
+				} else {
+					this.items[0].tooltip = TYPO3.lang.ext_details_remove_ext;
+					return 't3-icon t3-icon-actions t3-icon-actions-system t3-icon-system-extension-import';
+				}
+			},
+			handler: function(grid, rowIndex, colIndex) {
+				var record = grid.store.getAt(rowIndex).data;
+				var action = record.installed ? TYPO3.lang.ext_details_remove_ext : TYPO3.lang.menu_install_extensions;
+				//http://intro.local.com/typo3/mod.php?M=tools_em&ter_connect=1&ter_search=templavoila&CMD[importExt]=ics_templavoila_migration_tool&CMD[extVersion]=1.0.3&CMD[loc]=L
+				var link = TYPO3.settings.EM.scriptLink
+						+ '&nodoc=1&view=installed_list&ter_connect=1&CMD[importExt]=' + record.extkey  + '&CMD[extVersion]=' + record.version + '&CMD[loc]=L'
+
+
+				var w = new TYPO3.EM.InstallWindow({
+				 	title: action + ': ' + record.title + ' (' + record.extkey + ') version ' + record.version
+				}).show(true, function(){
+					Ext.getCmp('emInstallIframeWindow').setUrl(link);
+				});
+			}
+		}
+	]
+}
+
+TYPO3.EM.GridColumns.ExtensionTitle = {
+	header: TYPO3.lang.tab_mod_name,
+	width: 150,
+	sortable: true,
+	dataIndex: 'title',
+	renderer:function(value, metaData, record, rowIndex, colIndex, store) {
+		if (value == '') {
+			value = '[no title]';
+		}
+		if (record.data.description) {
+			metaData.attr = 'ext:qtip="' + record.data.description + '"';
+		}
+		return record.data.icon + ' ' + value + ' (v' + record.data.version + ')';
+	}
+}
+
+TYPO3.EM.GridColumns.ExtensionKey = {
+	header: TYPO3.lang.tab_mod_key,
+	width: 80,
+	sortable: true,
+	dataIndex: 'extkey'
+}
+
+TYPO3.EM.GridColumns.ExtensionCategory = {
+	header: TYPO3.lang.list_order_category,
+	width: 70,
+	sortable: true,
+	dataIndex: 'category',
+	filterable: true,
+	hidden: true
+}
+
+TYPO3.EM.GridColumns.ExtensionCategoryRemote = {
+	header: TYPO3.lang.list_order_category,
+	width: 70,
+	sortable: true,
+	dataIndex: 'category',
+	renderer: function(value, metaData, record, rowIndex, colIndex, store) {
+		var cats = ['be','module','fe','plugin','misc','services','templates','example','doc'];
+		return cats[value];
+	}
+}
+
+TYPO3.EM.GridColumns.ExtensionAuthor = {
+	header: TYPO3.lang.list_order_author,
+	width: 120,
+	sortable: true,
+	hidden: true,
+	dataIndex:'author',
+	renderer: function(value, metaData, record, rowIndex, colIndex, store) {
+		metaData.attr='ext:qtip="'+value+'"';
+		var t = Ext.util.Format.ellipsis(value, 20);
+		if (record.data.author_email) {
+			return '<a href="mailto:' + record.data.author_email + '">' + t + '</a>';
+		} else {
+			return t;
+		}
+	}
+}
+
+TYPO3.EM.GridColumns.ExtensionType = {
+	header: TYPO3.lang.list_order_type,
+	width:50,
+	sortable:true,
+	dataIndex:'type',
+	hidden: true
+}
+
+TYPO3.EM.GridColumns.ExtensionState = {
+	header: TYPO3.lang.list_order_state,
+	align: 'center',
+	width: 100,
+	sortable: true,
+	resizable: false,
+	fixed: true,
+	dataIndex:'state',
+	renderer: function(value, metaData, record, rowIndex, colIndex, store){
+		metaData.css += 'state-' + value + ' ';
+		return value;
+	},
+	filterable: true
+}
+
+TYPO3.EM.GridColumns.ExtensionDownloads = {
+	header: TYPO3.lang.list_order_downloads,
+	width: 40,
+	sortable: true,
+	dataIndex:'alldownloadcounter'
+}
+
+TYPO3.EM.GridColumns.ExtensionVersion = {
+	header: TYPO3.lang.extInfoArray_version,
+	width:40,
+	sortable: true,
+	dataIndex:'version'
+}
+
+
+///////////////////////////////////////////////////////
+// Stores
+///////////////////////////////////////////////////////
+
+TYPO3.EM.LocationStore = new Ext.data.JsonStore({
+	fields : ['name', 'value'],
+	data   : [
+		{name : 'Local (../typo3conf/ext/)',   value: 'L'}
+	]
+});
+
+
+///////////////////////////////////////////////////////
+// Panels + Tabs
+///////////////////////////////////////////////////////
+
+TYPO3.EM.LocalListTab = {
+	title : TYPO3.lang.localExtensions,
+	xtype: 'TYPO3.EM.LocalList',
+	//iconCls: 'x-btn-drive',
+	id: 'em-local-extensions',
+	listeners: {
+		activate: function(panel) {
+			TYPO3.EM.ExtDirect.saveSetting('mainTab', 0);
+			var store = Ext.StoreMgr.get('localstore');
+			if (!store.getCount()) {
+				store.load();
+			}
+
+		},
+		scope: this
+	}
+};
+TYPO3.EM.RepositoryListTab = {
+	title : TYPO3.lang.remoteRepository,
+	//iconCls: 'x-btn-server',
+	layout: 'fit',
+	items: [/*{
+		xtype: 'repository',
+		region: 'north'
+	},*/{
+		xtype: 'remoteextlist',
+		flex: 1,
+		region: 'center',
+		margins: '0 0 0 0'
+	}],
+	id: 'em-remote-extensions',
+	listeners: {
+		activate: function(panel) {
+			TYPO3.EM.ExtDirect.saveSetting('mainTab', 1);
+			var store = Ext.StoreMgr.get('repositoryliststore');
+			if (!store.getCount()) {
+				store.load();
+			}
+		},
+		scope: this
+	}
+};
+TYPO3.EM.LanguageTab = {
+	title: TYPO3.lang.menu_translation_handling,
+	xtype: 'extlanguages',
+	id: 'em-translations',
+	//iconCls: 'x-btn-flag',
+	listeners: {
+		activate: function(panel) {
+			TYPO3.EM.ExtDirect.saveSetting('mainTab', 2);
+			var store = Ext.StoreMgr.get('em-language-store');
+			if (!store.getCount()) {
+				store.load();
+			}
+		},
+		afterrender: function(panel) {
+
+		},
+		scope: this
+	}
+};
+TYPO3.EM.SettingsTab = {
+	title: TYPO3.lang.menu_settings,
+	xtype: 'extsettings',
+	//iconCls: 'x-btn-settings',
+	listeners: {
+		activate: function(panel) {
+			TYPO3.EM.ExtDirect.saveSetting('mainTab', 3);
+		},
+		scope: this
+	}
+};
+
+TYPO3.EM.UserTab = {
+	title: TYPO3.lang.myExtensions,
+	xtype: 'TYPO3.EM.UserTools',
+	//disabled: !TYPO3.settings.EM.hasCredentials,
+	//iconCls: 'x-btn-tools',
+	listeners: {
+		activate: function(panel) {
+			var store = Ext.StoreMgr.get('em-userext');
+			if (!TYPO3.settings.EM.hasCredentials) {
+				TYPO3.Flashmessage.display(TYPO3.Severity.error,'Settings', 'No user+password specified. Please enter your credentials in "Settings" tab! ', 15);
+				store.removeAll();
+				Ext.getCmp('extvalidformbutton').disable();
+			} else {
+				TYPO3.EM.ExtDirect.saveSetting('mainTab', 4);
+				store.load();
+			}
+		},
+		scope: this
+	}
+};
+
+
+TYPO3.EM.UploadLocationCombo = new Ext.form.ComboBox({
+	mode: 'local',
+	value: 'L',
+	triggerAction: 'all',
+	forceSelection: true,
+	editable: false,
+	name: 'loc',
+	hiddenName:     'loc',
+	displayField:   'name',
+	valueField:     'value',
+	store: TYPO3.EM.LocationStore,
+	width: 250,
+	fieldLabel: '...to location'
+});
+
+TYPO3.EM.RepositoryCombo = new Ext.form.ComboBox({
+	id: 'repCombo',
+	mode: 'local',
+	triggerAction: 'all',
+	forceSelection: true,
+	editable: false,
+	name: 'selectedRepository',
+	hiddenName: 'selectedRepository',
+	displayField: 'title',
+	valueField: 'uid',
+	store: null,
+	width: 250
+});
+
+TYPO3.EM.LanguagesActionPanel = {
+	xtype: 'container',
+	layout: 'hbox',
+	height: 40,
+	id: 'LanguagesActionPanel',
+	layoutConfig: {
+		align: 'stretch'
+	},
+	defaults: {
+		border:false,
+		flex: 1,
+		margins: '10 10 10 10'
+	},
+	items: [{
+		xtype: 'button',
+		text: TYPO3.lang.translation_check_status_button,
+		id: 'lang-checkbutton',
+		disabled: true
+	}, {
+		xtype: 'button',
+		text: TYPO3.lang.translation_update_button,
+		id: 'lang-updatebutton',
+		disabled: true
+	}]
+};
+
+
+TYPO3.EM.InstallWindow = Ext.extend(Ext.Window, {
+	width: 800,
+	height: 600,
+	closable: true,
+	resizable: true,
+	plain: true,
+	border: false,
+	modal: true,
+	draggable: true,
+	layout: 'anchor',
+	constructor: function(config) {
+		config = config || {};
+		Ext.apply(this, config, {
+			items: [{
+				xtype: 'iframePanel',
+				anchor: '100% 100%',
+				border: false,
+				id: 'emInstallIframeWindow'
+			}]
+		});
+		TYPO3.EM.InstallWindow.superclass.constructor.call(this, config);
+	}
+});
+TYPO3.EM.TerUpload = Ext.extend(Ext.form.FormPanel, {
+	border:false,
+	recordData: null,
+
+	initComponent:function() {
+
+
+
+		Ext.apply(this, {
+			itemId: 'extUploadForm',
+			height: 340,
+			defaultType: 'textfield',
+
+			defaults: {width: 350},
+			items: [{
+				fieldLabel: TYPO3.lang.repositoryUploadForm_username,
+				name: 'fe_u'
+			}, {
+				fieldLabel: TYPO3.lang.repositoryUploadForm_password,
+				inputType: 'password',
+				name: 'fe_p'
+			}, {
+				fieldLabel: TYPO3.lang.repositoryUploadForm_changelog,
+				xtype: 'textarea',
+				height: 150,
+				name: 'uploadcomment'
+			}, {
+				xtype: 'radiogroup',
+				fieldLabel: TYPO3.lang.repositoryUploadForm_new_version,
+				itemCls: 'x-check-group-alt',
+				columns: 1,
+				items: [
+					{
+						boxLabel: TYPO3.lang.repositoryUploadForm_new_bugfix.replace('%s', 'x.x.<strong><span class="typo3-red">x+1</span></strong>'),
+						name: 'newversion',
+						inputValue: 'new_dev',
+						checked: true
+					},
+					{
+						boxLabel: TYPO3.lang.repositoryUploadForm_new_sub_version.replace('%s', 'x.<strong><span class="typo3-red">x+1</span></strong>.0'),
+						name: 'newversion',
+						inputValue: 'new_sub'
+					},
+					{
+						boxLabel: TYPO3.lang.repositoryUploadForm_new_main_version.replace('%s', '<strong><span class="typo3-red">x+1</span></strong>.0.0'),
+						name: 'newversion',
+						inputValue: 'new_main'
+					}
+				]
+			}, {
+				xtype: 'button',
+				text: TYPO3.lang.repositoryUploadForm_upload,
+				scope: this,
+				handler: function() {
+					this.form.submit({
+						waitMsg : 'Sending data...',
+						success: function(form, action) {
+							TYPO3.Flashmessage.display(TYPO3.Severity.information, TYPO3.lang.cmd_terupload, TYPO3.lang.msg_terupload, 5);
+							form.reset();
+						},
+						failure: function(form, action) {
+							if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
+								TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_error,
+										TYPO3.lang.list_order_state + ':' + action.response.status + ': ' +
+										action.response.statusText, 15);
+							}
+							if (action.failureType === Ext.form.Action.SERVER_INVALID){
+								// server responded with success = false
+								TYPO3.Flashmessage.display(TYPO3.Severity.error, TYPO3.lang.msg_invalid, action.result.errormsg, 5);
+							}
+						}
+					});
+				}
+			}],
+			listeners: {
+
+				activate: function(panel) {
+
+
+				}
+			},
+			scope: this
+		});
+
+		TYPO3.EM.TerUpload.superclass.initComponent.apply(this, arguments);
+	},
+
+	onRender: function() {
+
+
+		TYPO3.EM.TerUpload.superclass.onRender.apply(this, arguments);
+
+		Ext.apply(this.getForm(),{
+			api: {
+				load: TYPO3.EM.ExtDirect.loadUploadExtToTer,
+				submit: TYPO3.EM.ExtDirect.uploadExtToTer
+			},
+			paramsAsHash: false
+
+		});
+		this.form.load();
+	}
+
+
+});
+
+Ext.reg('terupload', TYPO3.EM.TerUpload);
Index: typo3/sysext/em/classes/connection/class.tx_em_connection_extdirectserver.php
===================================================================
--- typo3/sysext/em/classes/connection/class.tx_em_connection_extdirectserver.php	(revision 9674)
+++ typo3/sysext/em/classes/connection/class.tx_em_connection_extdirectserver.php	(revision )
@@ -102,6 +102,7 @@
 	 * @return string $content
 	 */
 	public function getExtensionList() {
+		/** @var $list tx_em_Extensions_List */
 		$list = t3lib_div::makeInstance('tx_em_Extensions_List');
 		$extList = $list->getInstalledExtensions(TRUE);
 
@@ -155,6 +156,7 @@
 	 * @return string $content
 	 */
 	public function getExtensionDetails() {
+		/** @var $list tx_em_Extensions_List */
 		$list = t3lib_div::makeInstance('tx_em_Extensions_List');
 		$extList = $list->getInstalledExtensions(TRUE);
 
@@ -177,7 +179,9 @@
 			$path = t3lib_extMgm::extPath($extKey);
 			$ext = array();
 
+			/** @var $install tx_em_Install */
 			$install = t3lib_div::makeInstance('tx_em_Install');
+			/** @var $extension tx_em_Extensions_List */
 			$extension = t3lib_div::makeInstance('tx_em_Extensions_List');
 			$extension->singleExtInfo($extKey, $path, $ext);
 			$ext = $ext[0];
@@ -260,21 +264,24 @@
 
 		foreach ($files as $key => $file) {
 			$fileExt = strtolower(substr($file, strrpos($file, '.') + 1));
-			if (in_array($fileExt, $imageTypes)) {
-				$cls = 'tree-image';
-			} elseif (in_array($fileExt, $editTypes)) {
-				$cls = 'tree-edit';
+			if (in_array($fileExt, $imageTypes) || in_array($fileExt, $editTypes)) {
+				$cls = t3lib_iconWorks::mapFileExtensionToSpriteIconClass($fileExt);
+				$type = in_array($fileExt, $imageTypes) ? 'image' : 'text';
 			} else {
-				$cls = 'tree-unknown';
+				$cls = t3lib_iconWorks::mapFileExtensionToSpriteIconClass('');
+
 			}
+
 			$fileArray[] = array(
 				'id' => $node . '/' . $file,
 				'text' => htmlspecialchars($file),
 				'leaf' => true,
 				'qtip' => $fileExt . ' - file',
-				'iconCls' => $cls
+				'iconCls' => $cls,
+				'fileType' => $type
 			);
 		}
+
 		return $fileArray;
 	}
 
@@ -370,7 +377,8 @@
 	 * @return string $content
 	 */
 	public function getRemoteExtensionList($parameters) {
-		$repositoryId = $this->getSettingsObject()->getSelectedRepository();
+		$repositoryId = $parameters->repository;
+		$mirrorUrl = $this->getMirrorUrl($repositoryId);
 
 		$search = $parameters->query;
 		$limit = $parameters->start . ', ' . $parameters->limit;
@@ -395,7 +403,17 @@
 			$limit
 		);
 
+			//TODO: compare with local extensions to decide for import/upload/no action
 
+			// transform array
+		foreach ($list['results'] as $key => $value) {
+			$list['results'][$key]['dependencies'] = unserialize($value['dependencies']);
+			$extPath = t3lib_div::strtolower($value['extkey']);
+			$list['results'][$key]['icon'] = '<img alt="" src="' . $mirrorUrl . $extPath{0} . '/' . $extPath{1} . '/' . $extPath . '_' . $value['version'] . '.gif" />';
+			$list['results'][$key]['state'] = tx_em_Tools::getDefaultState(intval($value['state']));
+
+		}
+
 		return array(
 			'length' => $list['count'],
 			'data' => $list['results']
@@ -409,13 +427,13 @@
 	 *
 	 * @return array
 	 */
-	public function repositoryUpdateLoad() {
+	public function getRepositories() {
 		$settings = $this->getSettings();
 		$repositories = tx_em_Database::getRepositories();
 		foreach ($repositories as $uid => $repository) {
 			$data[] = array(
 				'title' => $repository['title'],
-				'value' => $repository['uid'],
+				'uid' => $repository['uid'],
 				'description' => $repository['description'],
 				'wsdl_url' => $repository['wsdl_url'],
 				'mirror_url' => $repository['mirror_url'],
@@ -432,6 +450,55 @@
 	}
 
 	/**
+	 * Get Mirrors for selected repository
+	 *
+	 * @param  object $parameter
+	 * @return array
+	 */
+	public function getMirrors($parameter) {
+		$data = array();
+		/** @var $objRepository tx_em_Repository */
+		$objRepository = t3lib_div::makeInstance('tx_em_Repository', $parameter->repository);
+		//debug(array($objRepository->getMirrorListUrl(),$objRepository->getId(), $settings['selectedRepository']));
+		if ($objRepository->getMirrorListUrl()) {
+			$objRepositoryUtility = t3lib_div::makeInstance('tx_em_Repository_Utility', $objRepository);
+			$mirrors = $objRepositoryUtility->getMirrors(TRUE)->getMirrors();
+
+
+			if (count($mirrors)) {
+				$data = array(
+					array(
+						'title' => 'Random (recommended)',
+						'country' => '',
+						'host' => '',
+						'path' => '',
+						'sponsor' => '',
+						'link' => '',
+						'logo' => '',
+					)
+				);
+				foreach ($mirrors as $mirror) {
+					$data[] = array(
+						'title' => $mirror['title'],
+						'country' => $mirror['country'],
+						'host' => $mirror['host'],
+						'path' => $mirror['path'],
+						'sponsor' => $mirror['sponsorname'],
+						'link' => $mirror['sponsorlink'],
+						'logo' => $mirror['sponsorlogo'],
+					);
+				}
+			}
+		}
+
+		return array(
+			'length' => count($data),
+			'data' => $data,
+		);
+
+	}
+
+	/**
 	 * Edit / Create repository
 	 *
 	 * @formHandler
@@ -439,32 +506,62 @@
 	 * @return array
 	 */
 	public function repositoryEditFormSubmit($parameter) {
+		//debug($parameter);
+		$error = FALSE;
+		/** @var $repository tx_em_Repository */
+		$repository = t3lib_div::makeInstance('tx_em_Repository', $parameter['rep']);
+		$repository->setTitle($parameter['title']);
+		$repository->setDescription($parameter['description']);
+		$repository->setWsdlUrl($parameter['wsdl_url']);
+		$repository->setMirrorListUrl($parameter['mirror_url']);
+		$repositoryData = array(
+			'title' => $repository->getTitle(),
+			'description' => $repository->getDescription(),
+			'wsdl_url' => $repository->getWsdlUrl(),
+			'mirror_url' => $repository->getMirrorListUrl(),
+			'lastUpdated' => $repository->getLastUpdate(),
+			'extCount' => $repository->getExtensionCount(),
+		);
+		//debug($repositoryData);
+
+		if ($parameter['rep'] == 0) {
+			// create a new repository
+			$id = tx_em_Database::insertRepository($repository);
-		return array(
-			'success' => TRUE,
+			return array(
+				'success' => TRUE,
-			'data' => array(
-				'params' => $parameter
-			),
+				'newId' => $id,
+				'params' => $repositoryData
-		);
+			);
+
+		} else {
+			tx_em_Database::updateRepository($repository);
+			return array(
+				'success' => TRUE,
+				'params' => $repositoryData
+			);
-	}
+		}
 
+
+
+	}
+
 	/**
 	 * Update repository
 	 *
-	 * @formHandler
-	 *
 	 * @param array $parameter
 	 * @return array
 	 */
-	public function repositoryUpdate($parameter) {
-		//debug($parameter);
-		if (!intval($parameter['rep'])) {
+	public function repositoryUpdate($repositoryId) {
+
+		if (!intval($repositoryId)) {
 			return array(
 				'success' => FALSE,
-				'errors' => 'no repository choosen'
+				'errors' => 'no repository choosen',
+				'rep' => 0
 			);
 		}
 
-		$objRepository = t3lib_div::makeInstance('tx_em_Repository', intval($parameter['rep']));
+		$objRepository = t3lib_div::makeInstance('tx_em_Repository', intval($repositoryId));
 		$objRepositoryUtility = t3lib_div::makeInstance('tx_em_Repository_Utility', $objRepository);
 		$count = $objRepositoryUtility->updateExtList();
 
@@ -477,12 +574,14 @@
 				'success' => TRUE,
 				'data' => array(
 					'count' => $count
-				)
+				),
+				'rep' =>  intval($repositoryId)
 			);
 		} else {
 			return array(
 				'success' => FALSE,
-				'errormsg' => 'Your repository is up to date.'
+				'errormsg' => 'Your repository is up to date.',
+				'rep' =>  intval($repositoryId)
 			);
 		}
 	}
@@ -508,11 +607,11 @@
 		$lang = array();
 		foreach ($theLanguages as $language) {
 			$label = htmlspecialchars($GLOBALS['LANG']->sL('LLL:EXT:setup/mod/locallang.xml:lang_' . $language));
-			$flag = @is_file(PATH_typo3 . 'gfx/flags/' . $language . '.gif') ? 'gfx/flags/' . $language . '.gif' : 'gfx/flags/unknown.gif';
+			$cls =  t3lib_iconWorks::getSpriteIconClasses('flags-' . $language);
 			$lang[] = array(
 				'label' => $label,
 				'lang' => $language,
-				'flag' => $flag,
+				'cls' => $cls,
 				'selected' => in_array($language, $selected) ? 1 : 0
 			);
 		}
@@ -714,6 +813,37 @@
 		tx_em_Tools::refreshGlobalExtList();
 		$install->forceDBupdates($extensionKey, $newExtensionList[$extensionKey]);
 	}
+
+	/**
+	 * Gets the mirror url from selected mirror
+	 *
+	 * @param  $repositoryId
+	 * @return string
+	 */
+	protected function getMirrorUrl($repositoryId) {
+		$settings = $this->getSettings();
+		/** @var $objRepository  tx_em_Repository */
+		$objRepository = t3lib_div::makeInstance('tx_em_Repository', $repositoryId);
+		/** @var $objRepositoryUtility  tx_em_Repository_Utility */
+		$objRepositoryUtility = t3lib_div::makeInstance('tx_em_Repository_Utility', $objRepository);
+		$mirrors = $objRepositoryUtility->getMirrors(TRUE)->getMirrors();
+
+
+		if ($settings['selectedMirror'] == '') {
+			$randomMirror = array_rand($mirrors);
+			$mirrorUrl = $mirrors[$randomMirror]['host'] . $mirrors[$randomMirror]['path'];
+		} else {
+			foreach($mirrors as $mirror) {
+				if ($mirror['host'] == $settings['selectedMirror']) {
+					$mirrorUrl = $mirror['host'] . $mirror['path'];
+					break;
-}
+				}
+			}
+		}
 
+		return 'http://' . $mirrorUrl;
+	}
+
+}
+
-?>
\ No newline at end of file
+?>
Index: typo3/sysext/em/res/js/ux/filter/StringFilter.js
===================================================================
--- typo3/sysext/em/res/js/ux/filter/StringFilter.js	(revision )
+++ typo3/sysext/em/res/js/ux/filter/StringFilter.js	(revision )
@@ -0,0 +1,132 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+/** 
+ * @class Ext.ux.grid.filter.StringFilter
+ * @extends Ext.ux.grid.filter.Filter
+ * Filter by a configurable Ext.form.TextField
+ * <p><b><u>Example Usage:</u></b></p>
+ * <pre><code>    
+var filters = new Ext.ux.grid.GridFilters({
+    ...
+    filters: [{
+        // required configs
+        type: 'string',
+        dataIndex: 'name',
+        
+        // optional configs
+        value: 'foo',
+        active: true, // default is false
+        iconCls: 'ux-gridfilter-text-icon' // default
+        // any Ext.form.TextField configs accepted
+    }]
+});
+ * </code></pre>
+ */
+Ext.ux.grid.filter.StringFilter = Ext.extend(Ext.ux.grid.filter.Filter, {
+
+    /**
+     * @cfg {String} iconCls
+     * The iconCls to be applied to the menu item.
+     * Defaults to <tt>'ux-gridfilter-text-icon'</tt>.
+     */
+    iconCls : 'ux-gridfilter-text-icon',
+
+    emptyText: 'Enter Filter Text...',
+    selectOnFocus: true,
+    width: 125,
+    
+    /**  
+     * @private
+     * Template method that is to initialize the filter and install required menu items.
+     */
+    init : function (config) {
+        Ext.applyIf(config, {
+            enableKeyEvents: true,
+            iconCls: this.iconCls,
+            listeners: {
+                scope: this,
+                keyup: this.onInputKeyUp
+            }
+        });
+
+        this.inputItem = new Ext.form.TextField(config); 
+        this.menu.add(this.inputItem);
+        this.updateTask = new Ext.util.DelayedTask(this.fireUpdate, this);
+    },
+    
+    /**
+     * @private
+     * Template method that is to get and return the value of the filter.
+     * @return {String} The value of this filter
+     */
+    getValue : function () {
+        return this.inputItem.getValue();
+    },
+    
+    /**
+     * @private
+     * Template method that is to set the value of the filter.
+     * @param {Object} value The value to set the filter
+     */	
+    setValue : function (value) {
+        this.inputItem.setValue(value);
+        this.fireEvent('update', this);
+    },
+
+    /**
+     * @private
+     * Template method that is to return <tt>true</tt> if the filter
+     * has enough configuration information to be activated.
+     * @return {Boolean}
+     */
+    isActivatable : function () {
+        return this.inputItem.getValue().length > 0;
+    },
+
+    /**
+     * @private
+     * Template method that is to get and return serialized filter data for
+     * transmission to the server.
+     * @return {Object/Array} An object or collection of objects containing
+     * key value pairs representing the current configuration of the filter.
+     */
+    getSerialArgs : function () {
+        return {type: 'string', value: this.getValue()};
+    },
+
+    /**
+     * Template method that is to validate the provided Ext.data.Record
+     * against the filters configuration.
+     * @param {Ext.data.Record} record The record to validate
+     * @return {Boolean} true if the record is valid within the bounds
+     * of the filter, false otherwise.
+     */
+    validateRecord : function (record) {
+        var val = record.get(this.dataIndex);
+
+        if(typeof val != 'string') {
+            return (this.getValue().length === 0);
+        }
+
+        return val.toLowerCase().indexOf(this.getValue().toLowerCase()) > -1;
+    },
+    
+    /**  
+     * @private
+     * Handler method called when there is a keyup event on this.inputItem
+     */
+    onInputKeyUp : function (field, e) {
+        var k = e.getKey();
+        if (k == e.RETURN && field.isValid()) {
+            e.stopEvent();
+            this.menu.hide(true);
+            return;
+        }
+        // restart the timer
+        this.updateTask.delay(this.updateBuffer);
+    }
+});
Index: typo3/sysext/em/res/css/t3_em.css
===================================================================
--- typo3/sysext/em/res/css/t3_em.css	(revision )
+++ typo3/sysext/em/res/css/t3_em.css	(revision )
@@ -0,0 +1,120 @@
+/* Global overwrite */
+strong {
+	font-weight: bold;
+}
+/* Extension list */
+.x-grid3-col-col-title {
+	font-weight: bold;
+	padding: 5px;
+}
+.x-grid3-col-col-title img {
+	vertical-align: middle;
+}
+
+/* extension states */
+.state-alpha div {background-color:#d12438;color:#fff !important;font-weight:bold !important;}
+.state-beta div {background-color:#97b17e;color:#fff !important;font-weight:bold !important;}
+.state-stable div {background-color:#3bb65c;color:#fff !important;font-weight:bold !important;}
+.state-experimental div {background-color:#007eba;color:#fff !important;font-weight:bold !important;}
+.state-test div {background-color:#979797;color:#fff !important;font-weight:bold !important;}
+.state-obsolete div {background-color:#000000;color:#fff !important;font-weight:bold !important;}
+.state-excludeFromUpdates div {background-color:#cf7307;color:#fff !important;font-weight:bold !important;}
+
+img.installExtension, img.removeExtension {
+	cursor: pointer;
+}
+
+/* Repository Update Form */
+.em-repupdate {
+	margin: 5px 0 10px 0;
+	padding: 5px;
+}
+
+/* Translation Handling */
+.saveMessage {
+	font-weight: bold;
+	padding: 10px 5px 0 5px;
+}
+.x-progress-text {text-align: left;}
+
+/* File Editor */
+.editareapanel {
+	padding: 0 !important;
+	-moz-user-select: text;
+	-khtml-user-select: text;
+	-webkit-user-select: text;
+}
+#editarea-context_help::selection, #editarea-context_help::-moz-selection {
+	background: #ffb7b7 !important;
+}
+
+
+.x-btn-filebrowser { background-image:url(../icons/filebrowser.png) !important; }
+.x-tree-node-leaf img.tree-image { background-image:url(../icons/image.png); }
+.x-btn-cancel { background-image:url(../icons/cancel.png) !important;}
+.x-btn-drive { background-image:url(../icons/drive.png) !important;}
+.x-btn-server { background-image:url(../icons/server.png) !important;}
+.x-btn-flag { background-image:url(../icons/flag.png) !important;}
+.x-btn-tools { background-image:url(../icons/tools.png) !important;}
+.x-btn-settings { background-image:url(../icons/settings.png) !important;}
+
+
+/*
+ * FileUploadField component styles
+ */
+.x-form-file-wrap {
+    position: relative;
+    height: 22px;
+}
+.x-form-file-wrap .x-form-file {
+	position: absolute;
+	right: 0;
+	-moz-opacity: 0;
+	filter:alpha(opacity: 0);
+	opacity: 0;
+	z-index: 2;
+    height: 22px;
+}
+.x-form-file-wrap .x-form-file-btn {
+	position: absolute;
+	right: 0;
+	z-index: 1;
+}
+.x-form-file-wrap .x-form-file-text {
+    position: absolute;
+    left: 0;
+    z-index: 3;
+    color: #777;
+}
+
+/*
+ * Settings
+ */
+ .h1Panel {
+ 	margin: 0 0 5px 0 !important;
+ }
+ .panelDescription {
+ 	margin: 15px 0;
+ }
+
+.em-info {
+	margin: 10px;
+}
+
+
+.extvalidinfo ul {
+	list-style: disc;
+	margin: 5px 20px;
+}
+
+.em-extlist-extinfo {
+	line-height: 14px;
+}
+
+.em-extlist-extinfo label,
+.em-info label {
+	vertical-align: none;
+	display: inline-block;
+	width: 100px;
+	font-weight: bold;
+}
\ No newline at end of file
Index: typo3/sysext/em/classes/repository/class.tx_em_repository.php
===================================================================
--- typo3/sysext/em/classes/repository/class.tx_em_repository.php	(revision 9674)
+++ typo3/sysext/em/classes/repository/class.tx_em_repository.php	(revision )
@@ -119,16 +119,10 @@
 	 * @return  void
 	 */
 	function __construct($uid = 0) {
-		if ($uid === 0) {
-			// default repository
-			$this->setTitle('TYPO3.org Main Repository');
-			$this->setId('1');
-			$this->setPriority(1);
-			$this->setDescription('Main repository on typo3.org. For extension download there are mirrors available.');
-			$this->setMirrorListUrl('http://repositories.typo3.org/mirrors.xml.gz');
-			$this->setWsdlUrl('http://typo3.org/wsdl/tx_ter_wsdl.php');
-		} else {
-			$row = tx_em_Database::getRepositoryByUID($uid);
+		$row = tx_em_Database::getRepositoryByUID($uid);
+		if (is_null($row)) {
+			$this->fixMainRepository();
+		} else {
 			$this->setTitle($row['title']);
 			$this->setId($row['uid']);
 			$this->setPriority(1);
@@ -137,7 +131,6 @@
 			$this->setLastUpdate($row['lastUpdated']);
 			$this->setExtensionCount($row['extCount']);
 		}
-
 	}
 
 	/**
@@ -390,6 +383,21 @@
 	public function removeMirrors() {
 		unset($this->mirrors);
 	}
+
+	/**
+	 * Insert main repository if not present
+	 *
+	 * @return void
+	 */
+	protected function fixMainRepository() {
+		$this->setTitle('TYPO3.org Main Repository');
+		$this->setId('1');
+		$this->setPriority(1);
+		$this->setDescription('Main repository on typo3.org. For extension download there are mirrors available.');
+		$this->setMirrorListUrl('http://repositories.typo3.org/mirrors.xml.gz');
+		$this->setWsdlUrl('http://typo3.org/wsdl/tx_ter_wsdl.php');
+		tx_em_Database::insertRepository($this);
-}
+	}
+}
 
 ?>
\ No newline at end of file
Index: typo3/sysext/em/res/js/ux/fileuploadfield.js
===================================================================
--- typo3/sysext/em/res/js/ux/fileuploadfield.js	(revision )
+++ typo3/sysext/em/res/js/ux/fileuploadfield.js	(revision )
@@ -0,0 +1,182 @@
+/*!
+ * Ext JS Library 3.1.1
+ * Copyright(c) 2006-2010 Ext JS, LLC
+ * licensing@extjs.com
+ * http://www.extjs.com/license
+ */
+Ext.ns('Ext.ux.form');
+
+/**
+ * @class Ext.ux.form.FileUploadField
+ * @extends Ext.form.TextField
+ * Creates a file upload field.
+ * @xtype fileuploadfield
+ */
+Ext.ux.form.FileUploadField = Ext.extend(Ext.form.TextField,  {
+    /**
+     * @cfg {String} buttonText The button text to display on the upload button (defaults to
+     * 'Browse...').  Note that if you supply a value for {@link #buttonCfg}, the buttonCfg.text
+     * value will be used instead if available.
+     */
+    buttonText: 'Browse...',
+    /**
+     * @cfg {Boolean} buttonOnly True to display the file upload field as a button with no visible
+     * text field (defaults to false).  If true, all inherited TextField members will still be available.
+     */
+    buttonOnly: false,
+    /**
+     * @cfg {Number} buttonOffset The number of pixels of space reserved between the button and the text field
+     * (defaults to 3).  Note that this only applies if {@link #buttonOnly} = false.
+     */
+    buttonOffset: 3,
+    /**
+     * @cfg {Object} buttonCfg A standard {@link Ext.Button} config object.
+     */
+
+    // private
+    readOnly: true,
+
+    /**
+     * @hide
+     * @method autoSize
+     */
+    autoSize: Ext.emptyFn,
+
+    // private
+    initComponent: function(){
+        Ext.ux.form.FileUploadField.superclass.initComponent.call(this);
+
+        this.addEvents(
+            /**
+             * @event fileselected
+             * Fires when the underlying file input field's value has changed from the user
+             * selecting a new file from the system file selection dialog.
+             * @param {Ext.ux.form.FileUploadField} this
+             * @param {String} value The file value returned by the underlying file input field
+             */
+            'fileselected'
+        );
+    },
+
+    // private
+    onRender : function(ct, position){
+        Ext.ux.form.FileUploadField.superclass.onRender.call(this, ct, position);
+
+        this.wrap = this.el.wrap({cls:'x-form-field-wrap x-form-file-wrap'});
+        this.el.addClass('x-form-file-text');
+        this.el.dom.removeAttribute('name');
+        this.createFileInput();
+
+        var btnCfg = Ext.applyIf(this.buttonCfg || {}, {
+            text: this.buttonText
+        });
+        this.button = new Ext.Button(Ext.apply(btnCfg, {
+            renderTo: this.wrap,
+            cls: 'x-form-file-btn' + (btnCfg.iconCls ? ' x-btn-icon' : '')
+        }));
+
+        if(this.buttonOnly){
+            this.el.hide();
+            this.wrap.setWidth(this.button.getEl().getWidth());
+        }
+
+        this.bindListeners();
+        this.resizeEl = this.positionEl = this.wrap;
+    },
+    
+    bindListeners: function(){
+        this.fileInput.on({
+            scope: this,
+            mouseenter: function() {
+                this.button.addClass(['x-btn-over','x-btn-focus'])
+            },
+            mouseleave: function(){
+                this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click'])
+            },
+            mousedown: function(){
+                this.button.addClass('x-btn-click')
+            },
+            mouseup: function(){
+                this.button.removeClass(['x-btn-over','x-btn-focus','x-btn-click'])
+            },
+            change: function(){
+                var v = this.fileInput.dom.value;
+                this.setValue(v);
+                this.fireEvent('fileselected', this, v);    
+            }
+        }); 
+    },
+    
+    createFileInput : function() {
+        this.fileInput = this.wrap.createChild({
+            id: this.getFileInputId(),
+            name: this.name||this.getId(),
+            cls: 'x-form-file',
+            tag: 'input',
+            type: 'file',
+            size: 1
+        });
+    },
+    
+    reset : function(){
+        this.fileInput.remove();
+        this.createFileInput();
+        this.bindListeners();
+        Ext.ux.form.FileUploadField.superclass.reset.call(this);
+    },
+
+    // private
+    getFileInputId: function(){
+        return this.id + '-file';
+    },
+
+    // private
+    onResize : function(w, h){
+        Ext.ux.form.FileUploadField.superclass.onResize.call(this, w, h);
+
+        this.wrap.setWidth(w);
+
+        if(!this.buttonOnly){
+            var w = this.wrap.getWidth() - this.button.getEl().getWidth() - this.buttonOffset;
+            this.el.setWidth(w);
+        }
+    },
+
+    // private
+    onDestroy: function(){
+        Ext.ux.form.FileUploadField.superclass.onDestroy.call(this);
+        Ext.destroy(this.fileInput, this.button, this.wrap);
+    },
+    
+    onDisable: function(){
+        Ext.ux.form.FileUploadField.superclass.onDisable.call(this);
+        this.doDisable(true);
+    },
+    
+    onEnable: function(){
+        Ext.ux.form.FileUploadField.superclass.onEnable.call(this);
+        this.doDisable(false);
+
+    },
+    
+    // private
+    doDisable: function(disabled){
+        this.fileInput.dom.disabled = disabled;
+        this.button.setDisabled(disabled);
+    },
+
+
+    // private
+    preFocus : Ext.emptyFn,
+
+    // private
+    alignErrorIcon : function(){
+        this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
+    }
+
+});
+
+Ext.reg('fileuploadfield', Ext.ux.form.FileUploadField);
+
+// backwards compat
+Ext.form.FileUploadField = Ext.ux.form.FileUploadField;
Index: typo3/sysext/em/language/locallang.xml
===================================================================
--- typo3/sysext/em/language/locallang.xml	(revision 9674)
+++ typo3/sysext/em/language/locallang.xml	(revision )
@@ -42,13 +42,17 @@
 			<label index="menu_import_extensions">Import extensions</label>
 			<label index="menu_translation_handling">Translation handling</label>
 			<label index="menu_settings">Settings</label>
+			<label index="lang_language">Language</label>
+			<label index="lang_short">Short</label>
 			<label index="menu_extension_updates">Check for extension updates</label>
 			<label index="menu_extension_develop">Developer Module</label>
 			<label index="list_order_category">Category</label>
 			<label index="list_order_author">Author</label>
 			<label index="list_order_state">State</label>
 			<label index="list_order_type">Type</label>
+			<label index="list_order_downloads">Downloads</label>
 			<label index="show_details">Details</label>
+			<label index="show_links">Links</label>
 			<label index="show_description">Description</label>
 			<label index="show_more_details">More details</label>
 			<label index="show_technical">Technical (takes time!)</label>
@@ -112,6 +116,11 @@
 			<label index="enter_repository_username">Enter repository username:</label>
 			<label index="enter_repository_password">Enter repository password:</label>
 			<label index="repository_password_info">This is &lt;em&gt;not&lt;/em&gt; your password to the TYPO3 backend! This user information is what is needed to log in at typo3.org with your account there!</label>
+			<label index="repository_notfound">No repositories found.</label>
+			<label index="repository_update">Update Repository</label>
+			<label index="repository_update_not_needed">No update needed!</label>
+			<label index="repository_updated">Repository was updated.</label>
+			<label index="repository_extensions_count">There are {0} extensions now in repository.</label>
 			<label index="mirror_selection">Mirror selection</label>
 			<label index="mirror_list_url">Enter mirror list URL:</label>
 			<label index="mirror_select">Select a mirror from below. This list is built from the online mirror list retrieved from the URL above.</label>
@@ -119,9 +128,11 @@
 			<label index="mirror_list_reload">Click here to reload the list.</label>
 			<label index="mirror_use">Use</label>
 			<label index="mirror_name">Name</label>
+			<label index="mirror">Mirror</label>
 			<label index="mirror_url">URL</label>
 			<label index="mirror_country">Country</label>
 			<label index="mirror_sponsored_by">Sponsored by</label>
+			<label index="mirror_logo_link">Logo / Link</label>
 			<label index="mirror_use_random">Random (recommended!)</label>
 			<label index="enter_repository_url">Enter repository URL:</label>
 			<label index="repository_url_hint">If you set a repository URL, this overrides the use of a mirror. Use this to select a specific (private) repository.</label>
@@ -131,19 +142,28 @@
 			<label index="translation_info">For the selected languages the EM tries to download and install translation files if available, whenever an extension is installed. (This replaces the &lt;code&gt;csh_*&lt;/code&gt; extensions that were used to install core translations before TYPO3 version 4!)</label>
 			<label index="translation_loaded_exts">To request an update or installation for already loaded extensions, see below.</label>
 			<label index="translation_save_selection">Save selection</label>
+			<label index="translation_selection_saved">Language selection was saved</label>
 			<label index="translation_check_status_button">Check status against repository</label>
 			<label index="translation_update_button">Update from repository</label>
 			<label index="translation_status">Translation status</label>
 			<label index="translation_check_status">Checking translation status, please wait...</label>
+			<label index="translation_checking">Checking ...</label>
+			<label index="translation_refresh_languages">Refresh languages ...</label>
 			<label index="translation_table_check">This table shows the status of the translations of the loaded extensions.</label>
 			<label index="translation_extension_key">Extension key</label>
 			<label index="translation_checking_extension">Checking translation status for extension \"%s\"...</label>
+			<label index="translation_fetch_extension">Fetch language for extension "{0}"   {1} of {2}</label>
+			<label index="translation_checking_extension">Check languages against repository</label>
+			<label index="translation_update_extension">Update languages from repository</label>
 			<label index="translation_no_translation">No translation available</label>
-			<label index="translation_n_a">N/A</label>
+			<label index="translation_n_a">n/a</label>
 			<label index="translation_not_installed">Not installed/Unknown</label>
 			<label index="translation_status_unknown">???</label>
+			<label index="translation_status_new">new</label>
+			<label index="translation_status_ok">ok</label>
 			<label index="translation_needs_update">Needs update</label>
-			<label index="translation_status_update">UPD</label>
+			<label index="translation_status_update">update</label>
+			<label index="translation_status_notchecked">not checked</label>
 			<label index="translation_is_ok">Is up to date</label>
 			<label index="translation_status_ok">OK</label>
 			<label index="translation_check_done">Check done.</label>
@@ -311,27 +331,30 @@
 			<label index="extBackup_download_all_data">Download all data from:</label>
 			<label index="extBackup_nothing_to_dump">Nothing to dump...</label>
 			<label index="extInfoArray_general_info">General information:</label>
-			<label index="extInfoArray_title">Title:</label>
-			<label index="extInfoArray_description">Description:</label>
-			<label index="extInfoArray_author">Author:</label>
-			<label index="extInfoArray_version">Version:</label>
-			<label index="extInfoArray_category">Category:</label>
-			<label index="extInfoArray_state">State:</label>
-			<label index="extInfoArray_shy">Shy:</label>
-			<label index="extInfoArray_internal">Internal:</label>
-			<label index="extInfoArray_depends_on">Depends on:</label>
-			<label index="extInfoArray_conflicts_with">Conflicts with:</label>
-			<label index="extInfoArray_suggests">Suggests:</label>
-			<label index="extInfoArray_priority">Priority:</label>
-			<label index="extInfoArray_clear_cache">Clear cache:</label>
-			<label index="extInfoArray_incl_modules">Includes modules:</label>
-			<label index="extInfoArray_lock_type">Lock installation Type:</label>
-			<label index="extInfoArray_load_in_frontend">Load in frontend:</label>
-			<label index="extInfoArray_modifies_tables">Modifies tables:</label>
-			<label index="extInfoArray_inst_status">Installation status:</label>
-			<label index="extInfoArray_inst_type">Type of installation:</label>
-			<label index="extInfoArray_inst_twice">Double installation:</label>
-			<label index="extInfoArray_root_files">Root files:</label>
+			<label index="extInfoArray_title">Title</label>
+			<label index="extInfoArray_description">Description</label>
+			<label index="extInfoArray_author">Author</label>
+			<label index="extInfoArray_version">Version</label>
+			<label index="extInfoArray_versions">Versions</label>
+			<label index="extInfoArray_downloads">Downloads</label>
+			<label index="extInfoArray_category">Category</label>
+			<label index="extInfoArray_state">State</label>
+			<label index="extInfoArray_shy">Shy</label>
+			<label index="extInfoArray_internal">Internal</label>
+			<label index="extInfoArray_uploads">Uploads</label>
+			<label index="extInfoArray_depends_on">Depends on</label>
+			<label index="extInfoArray_conflicts_with">Conflicts with</label>
+			<label index="extInfoArray_suggests">Suggests</label>
+			<label index="extInfoArray_priority">Priority</label>
+			<label index="extInfoArray_clear_cache">Clear cache</label>
+			<label index="extInfoArray_incl_modules">Includes modules</label>
+			<label index="extInfoArray_lock_type">Lock installation Type</label>
+			<label index="extInfoArray_load_in_frontend">Load in frontend</label>
+			<label index="extInfoArray_modifies_tables">Modifies tables</label>
+			<label index="extInfoArray_inst_status">Installation status</label>
+			<label index="extInfoArray_inst_type">Type of installation</label>
+			<label index="extInfoArray_inst_twice">Double installation</label>
+			<label index="extInfoArray_root_files">Root files</label>
 			<label index="extInfoArray_table_error">Table error!</label>
 			<label index="extInfoArray_missing_fields">Probably one or more required fields/tables are missing in the database!</label>
 			<label index="extInfoArray_static_table_error">Static table error!</label>
@@ -357,10 +380,13 @@
 			<label index="extInfoArray_files_changed">Files changed:</label>
 			<label index="extInfoArray_additional_fields">Additional fields:</label>
 			<label index="extInfoArray_double_installation_infotext">A %s extension with this key is also available on the server, but cannot be loaded, because the "%s" version takes precedence.</label>
-			<label index="repositoryUploadForm_username">Repository Username:</label>
-			<label index="repositoryUploadForm_password">Repository Password:</label>
-			<label index="repositoryUploadForm_changelog">Changelog for upload:</label>
+			<label index="repositories">Repositories</label>
+			<label index="repository_details">Repository Details</label>
+			<label index="repositoryUploadForm_username">Repository Username</label>
+			<label index="repositoryUploadForm_password">Repository Password</label>
+			<label index="repositoryUploadForm_changelog">Changelog for upload</label>
 			<label index="repositoryUploadForm_command">Upload command:</label>
+			<label index="repositoryUploadForm_new_version">New version</label>
 			<label index="repositoryUploadForm_new_bugfix">New bugfix version (latest %s)</label>
 			<label index="repositoryUploadForm_new_sub_version">New sub version (latest %s)</label>
 			<label index="repositoryUploadForm_new_main_version">New main version (latest %s)</label>
@@ -491,7 +517,7 @@
 			<label index="header_upd_ext">There are new versions of the following extensions in the TER</label>
 			<label index="header_vers_ret">Version information</label>
 			<label index="tab_mod_name">Extension</label>
-			<label index="tab_mod_key">Ext-Key</label>
+			<label index="tab_mod_key">Extension Key</label>
 			<label index="tab_mod_loc_ver">Local</label>
 			<label index="tab_mod_rem_ver">Remote</label>
 			<label index="tab_mod_comment">Upload-Comment</label>
@@ -507,6 +533,54 @@
 			<label index="globalext">Globally installed extension</label>
 			<label index="localext">Locally installed extension</label>
 			<label index="sysext">System extension</label>
+			<label index="localExtensions">Local extensions</label>
+			<label index="remoteRepository">Remote repository</label>
+			<label index="myExtensions">My extensions</label>
+			<label index="emLoaded">Application is loaded</label>
+			<label index="cmd_edit">Edit</label>
+			<label index="cmd_create">Create</label>
+			<label index="cmd_delete">Delete</label>
+			<label index="cmd_save">Save</label>
+			<label index="cmd_register">Register</label>
+			<label index="cmd_filter">Filter</label>
+			<label index="cmd_action">Action</label>
+			<label index="cmd_update">Update</label>
+			<label index="cmd_install">Install</label>
+			<label index="cmd_transferkey">Transfer extension key</label>
+			<label index="cmd_transferkey_to_user">Transfer extension key to user</label>
+			<label index="cmd_transferkey_do">Do the transfer</label>
+			<label index="cmd_deletekey">Delete extension key</label>
+			<label index="cmd_terupload">TER upload</label>
+			<label index="cmd_readdoc">Read documentation</label>
+			<label index="cmd_downloadext">Download extension</label>
+			<label index="msg_terupload">Extension was uploaded to TER.</label>
+			<label index="msg_error">Error</label>
+			<label index="msg_invalid">Invalid</label>
+			<label index="msg_longwait">This may take some time, please wait...</label>
+			<label index="msg_finished">Finished!</label>
+			<label index="msg_checking">Checking</label>
+			<label index="msg_updating">Updating</label>
+			<label index="action_loading_extlist">Loading Extensionlist ...</label>
+			<label index="registerkeys_rules_heading">Extension key format rules</label>
+			<label index="registerkeys_rules_allowedcharacters">Allowed characters are: a-z (lowercase), 0-9 and '_' (underscore)</label>
+			<label index="registerkeys_rules_prefixes">The key must not being with one of the following prefixes: tx,u,user_,pages,tt_,sys_,ts_language_,csh_</label>
+			<label index="registerkeys_rules_startandend">Extension keys cannot start or end with 0-9 and '_' (underscore)</label>
+			<label index="registerkeys_rules_length">An extension key must have minimum 3, maximum 30 characters (not counting underscores)</label>
+			<label index="registerkeys_check_validity">Check Validity</label>
+			<label index="registerkeys_check_validity_extkey">Check validity of extension key</label>
+			<label index="registerkeys_check_validity_extkey_isvalid">Extension key is valid</label>
+			<label index="registerkeys_checking_validity">Checking validity ...</label>
+			<label index="registerkeys_register_extkey">Register extension key ...</label>
+			<label index="registerkeys_register_extkey_success">Extensionkey {0] was registered</label>
+			<label index="registerkeys_registerkey">Register extension key</label>
+			<label index="registerkeys_cancel_register">Cancel register</label>
+			<label index="transferkeys_info">Transfer an extension key to another user. There is no undo, be sure you have the right username of the user you want to transfer the extension key to.</label>
+			<label index="deletekey_info">Deletes an extension key. There is no undo, once a key is deleted, it is available again for registration.</label>
+			<label index="transferkeys_success">Transfer of extension key {0} to user {1} succeded.</label>
+			<label index="transferkeys_fail">Transfer of extension key {0} to user {1} failed.</label>
+			<label index="deletekey_success">Delete of extension key {0} succeded.</label>
+			<label index="deletekey_fail">Delete of extension key {0} failed.</label>
+
 		</languageKey>
 	</data>
 </T3locallang>
Index: typo3/sysext/em/res/js/em_app.js
===================================================================
--- typo3/sysext/em/res/js/em_app.js	(revision )
+++ typo3/sysext/em/res/js/em_app.js	(revision )
@@ -0,0 +1,71 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <info@sk-typo3.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+Ext.ns('TYPO3.EM', 'TYPO3.EM.ExtDirect');
+
+Ext.onReady(function() {
+		//save states in user cookie, TODO: use ucStateProvider instead
+	Ext.state.Manager.setProvider(new Ext.state.CookieProvider());
+	Ext.QuickTips.init();
+
+		// fire app
+	var EM = new TYPO3.EM.App.init();
+	TYPO3.Flashmessage.display(TYPO3.Severity.information, TYPO3.lang.header, TYPO3.lang.emLoaded, 2);
+
+
+});
+
+TYPO3.EM.App = {
+
+	init : function() {
+		if (!TYPO3.settings.hasCredentials &&  TYPO3.settings.EM.mainTab == '4') {
+			TYPO3.settings.EM.mainTab = 0;
+		}
+		var appPanel = new Ext.TabPanel( {
+			renderTo : 'em-app',
+			id: 'em-main',
+			activeTab : TYPO3.settings.EM.mainTab ? TYPO3.settings.EM.mainTab : 0,
+			layoutOnTabChange: true,
+			plain: true,
+			height: 450,
+			items : [
+				TYPO3.EM.LocalListTab,
+				TYPO3.EM.RepositoryListTab,
+				TYPO3.EM.LanguageTab,
+				TYPO3.EM.SettingsTab,
+				TYPO3.EM.UserTab
+			],
+			plugins: [new Ext.ux.plugins.FitToParent()]
+
+		});
+	}
+};
Index: typo3/sysext/em/classes/class.tx_em_extensionmanager.php
===================================================================
--- typo3/sysext/em/classes/class.tx_em_extensionmanager.php	(revision )
+++ typo3/sysext/em/classes/class.tx_em_extensionmanager.php	(revision )
@@ -0,0 +1,237 @@
+<?php
+/***************************************************************
+*  Copyright notice
+*
+*  (c) 2010 Steffen Kamper (info@sk-typo3.de)
+*  All rights reserved
+*
+*  This script is part of the TYPO3 project. The TYPO3 project is
+*  free software; you can redistribute it and/or modify
+*  it under the terms of the GNU General Public License as published by
+*  the Free Software Foundation; either version 2 of the License, or
+*  (at your option) any later version.
+*
+*  The GNU General Public License can be found at
+*  http://www.gnu.org/copyleft/gpl.html.
+*  A copy is found in the textfile GPL.txt and important notices to the license
+*  from the author is found in LICENSE.txt distributed with these scripts.
+*
+*
+*  This script is distributed in the hope that it will be useful,
+*  but WITHOUT ANY WARRANTY; without even the implied warranty of
+*  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+*  GNU General Public License for more details.
+*
+*  This copyright notice MUST APPEAR in all copies of the script!
+***************************************************************/
+/**
+ * Module: Extension manager, developer module
+ *
+ * $Id: class.em_extensionmanager.php 2106 2010-03-24 00:56:22Z steffenk $
+ *
+ * @author	Steffen Kamper <info@sk-typo3.de>
+ */
+
+
+class tx_em_ExtensionManager {
+
+	/**
+	 * Parent module object
+	 *
+	 * @var SC_mod_tools_em_index
+	 */
+	protected $parentObject;
+
+	/**
+	 * Page Renderer
+	 *
+	 * @var t3lib_PageRenderer
+	 */
+	protected $pageRenderer;
+
+	/**
+	 * Path of res for JS/CSS/resources
+	 *
+	 * @var string
+	 */
+	protected $resPath;
+
+	/**
+	 * Debug flag for develop debug=1 will use one uncompressed concatenated file, debug=2 will use single files
+	 *
+	 * @var int
+	 */
+	protected $debug;
+
+	/**
+	 * Gzip support - use it if server supports gzipped js files
+	 *
+	 * @var boolean
+	 */
+	protected $gzSupport = FALSE;
+
+
+
+	/**
+	 * Constructor
+	 *
+	 * @param SC_mod_tools_em_index $parentObject
+	 */
+	public function __construct(SC_mod_tools_em_index $parentObject) {
+		$this->parentObject = $parentObject;
+		$this->pageRenderer = $this->parentObject->doc->getPageRenderer();
+		$this->resPath = $this->parentObject->doc->backPath . t3lib_extMgm::extRelPath('em') . 'res/';
+
+		$userSettings = $this->parentObject->settings->getUserSettings();
+
+		$this->debug = isset($userSettings['debug']) ? intval($userSettings['debug']) : 0;
+		$this->gzSupport = isset($userSettings['jsGzCompressed']) ? TRUE : FALSE;
+
+		$this->checkRepository();
+	}
+
+	/**
+	 * Render module content
+	 *
+	 * @return string $content
+	 */
+	public function render() {
+
+		/* Add CSS */
+		if ($this->debug == 2 || 1) {
+			$this->pageRenderer->addCssFile($this->resPath . 'js/ux/css/GridFilters.css');
+			$this->pageRenderer->addCssFile($this->resPath . 'js/ux/css/RangeMenu.css');
+			$this->pageRenderer->addCssFile($this->resPath . 'css/t3_em.css');
+		} elseif($this->debug == 1) {
+
+		} else {
+
+		}
+
+			// TODO: use sprite iconCls
+		$iconsGfxPath = t3lib_extMgm::extRelPath('t3skin') . 'icons/gfx/';
+		$this->pageRenderer->addCssInlineBlock('em-t3skin-icons', '
+			.x-tree-node-leaf img.tree-edit { background-image:url(' . $iconsGfxPath . 'edit_file.gif);}
+			.x-btn-edit { background-image:url(' . $iconsGfxPath . 'edit2.gif) !important;}
+			.x-btn-new { background-image:url(' . $iconsGfxPath . 'new_el.gif) !important;}
+			.x-btn-delete { background-image:url(' . $iconsGfxPath . 'garbage.gif) !important;}
+			.x-tree-node-leaf img.tree-unknown { background-image:url(' . $iconsGfxPath . 'default.gif); }
+			.x-btn-save { background-image:url(' . $iconsGfxPath . 'savedok.gif) !important;}
+			.x-btn-upload { background-image:url(' . $iconsGfxPath . 'upload.gif) !important;}
+			.x-btn-download { background-image:url(' . $iconsGfxPath . 'down.gif) !important;}
+		');
+
+		/* load ExtJS */
+		$this->pageRenderer->loadExtJS();
+		$this->pageRenderer->enableExtJsDebug();
+
+			// Load  JavaScript:
+		$this->pageRenderer->addJsFile($this->parentObject->doc->backPath .
+			'ajax.php?ajaxID=ExtDirect::getAPI&namespace=TYPO3.EM',
+			NULL,
+			FALSE
+		);
+		$this->pageRenderer->addJsFile($this->parentObject->doc->backPath .
+			'ajax.php?ajaxID=ExtDirect::getAPI&namespace=TYPO3.EMSOAP',
+			NULL,
+			FALSE
+		);
+
+		$this->pageRenderer->addExtDirectCode();
+
+
+			// Localization
+		$labels = tx_em_Tools::getArrayFromLocallang(t3lib_extMgm::extPath('em', 'language/locallang.xml'));
+		$this->pageRenderer->addInlineLanguageLabelArray($labels);
+
+		/* TODO: new users have no settings */
+		$settings = $this->parentObject->MOD_SETTINGS;
+		$settings['siteUrl'] = t3lib_div::getIndpEnv('TYPO3_SITE_URL');
+		$settings['backPath'] = $this->parentObject->doc->backPath;
+		$settings['hasCredentials'] = ($settings['fe_u'] !== '' && $settings['fe_p'] !== '');
+		$settings['scriptLink'] = $this->parentObject->script;
+
+		// TODO add mirrors to sys_ter record and delete from settings
+		// restructure mirror data
+		$mirrors = unserialize($settings['extMirrors']);
+		$settings['extMirrors'] = array(array('Random (recommended)', '', '', '', '', '', ''));
+		if (is_array($mirrors)) {
+			foreach ($mirrors as $mirror) {
+				$settings['extMirrors'][] = array(
+					$mirror['title'], $mirror['country'], $mirror['host'], $mirror['path'],
+					$mirror['sponsor']['name'], $mirror['sponsor']['link'], $mirror['sponsor']['logo']
+				);
+			}
+		}
+		$settings['selectedLanguages'] = unserialize($settings['selectedLanguages']);
+		$settings['selectedLanguagesList'] = implode(',', (array) $settings['selectedLanguages']);
+
+		$this->pageRenderer->addInlineSettingArray('EM', $settings);
+
+
+		// Add JS
+		$this->pageRenderer->addJsFile($this->parentObject->doc->backPath . '../t3lib/js/extjs/ux/flashmessages.js');
+		$this->pageRenderer->addJsFile($this->parentObject->doc->backPath . 'js/extjs/iframepanel.js');
+
+		//Plugins
+		$this->pageRenderer->addJsFile($this->resPath . 'js/overrides/ext_overrides.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/custom_plugins.js');
+		$this->pageRenderer->addJsFile($this->parentObject->doc->backPath . '../t3lib/js/extjs/ux/Ext.ux.FitToParent.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/rowpanelexpander.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/searchfield.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/fileuploadfield.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/menu/RangeMenu.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/menu/ListMenu.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/GridFilters.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/filter/Filter.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/filter/BooleanFilter.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/filter/DateFilter.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/filter/ListFilter.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/filter/NumericFilter.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/ux/filter/StringFilter.js');
+
+		//Scripts
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_layouts.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_templates.js');
+
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_components.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_files.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_ter.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_tools.js');
+
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_locallist.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_repositorylist.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_usertools.js');
+
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_languages.js');
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_settings.js');
+		//Application
+		$this->pageRenderer->addJsFile($this->resPath . 'js/em_app.js');
+
+
+		//Update from repository - box
+		//$content = $this->parentObject->showRepositoryUpdateForm(0);
+
+		$content .= '<div id="em-message-area"></div><div id="em-app"></div>';
+		return $content;
+	}
+
+	/**
+	 * Check integrity of repository entry in sys_ter
+	 *
+	 * @return void
+	 */
+	protected function checkRepository() {
+		/** @var $repository tx_em_Repository  */
+		$repository = t3lib_div::makeInstance('tx_em_Repository');
+		if ($repository->getLastUpdate() == 0) {
+			$extCount = tx_em_Database::getExtensionCountFromRepository($repository);
+			if ($extCount > 0) {
+				$repository->setExtensionCount($extCount);
+				$repository->setLastUpdate(time());
+				tx_em_Database::updateRepository($repository);
+			}
+		}
+	}
+}
+?>
Index: typo3/sysext/em/classes/settings/class.tx_em_settings.php
===================================================================
--- typo3/sysext/em/classes/settings/class.tx_em_settings.php	(revision 9674)
+++ typo3/sysext/em/classes/settings/class.tx_em_settings.php	(revision )
@@ -105,7 +105,7 @@
 				'translations' => $GLOBALS['LANG']->getLL('menu_translation_handling'),
 				'settings' => $GLOBALS['LANG']->getLL('menu_settings'),
 				'updates' => $GLOBALS['LANG']->getLL('menu_extension_updates'),
-				'extensionmanager' => 'New Extension Manager',
+				'extensionmanager' => 'New Extension manager (beta2)', //$GLOBALS['LANG']->getLL('header'),
 				'develop' => $GLOBALS['LANG']->getLL('menu_extension_develop'),
 			),
 			'listOrder' => array(
Index: typo3/sysext/em/res/js/em_ter.js
===================================================================
--- typo3/sysext/em/res/js/em_ter.js	(revision )
+++ typo3/sysext/em/res/js/em_ter.js	(revision )
@@ -0,0 +1,128 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <info@sk-typo3.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+Ext.ns('TYPO3.EM');
+
+TYPO3.EM.TerUpload = Ext.extend(Ext.form.FormPanel, {
+	border:false,
+	recordData: null,
+
+	initComponent:function() {
+
+
+
+		Ext.apply(this, {
+			itemId: 'extUploadForm',
+			height: 340,
+			defaultType: 'textfield',
+
+			defaults: {width: 350},
+			items: [{
+				fieldLabel: 'Repository Username',
+				name: 'fe_u'
+			}, {
+				fieldLabel: 'Repository Username',
+				inputType: 'password',
+				name: 'fe_p'
+			}, {
+				fieldLabel: 'Changelog for upload',
+				xtype: 'textarea',
+				height: 150,
+				name: 'uploadcomment'
+			}, {
+				xtype: 'radiogroup',
+				fieldLabel: 'New Version',
+				itemCls: 'x-check-group-alt',
+				columns: 1,
+				items: [
+					{boxLabel: 'New bugfix version (latest x.x.<strong><span class="typo3-red">x+1</span></strong>)', name: 'newversion', inputValue: 'new_dev',checked: true},
+					{boxLabel: 'New sub version (latest x.<strong><span class="typo3-red">x+1</span></strong>.0)', name: 'newversion', inputValue: 'new_sub'},
+					{boxLabel: 'New main version (latest <strong><span class="typo3-red">x+1</span></strong>.0.0)', name: 'newversion', inputValue: 'new_main'}
+				]
+			}, {
+				xtype: 'button',
+				text: 'Upload extension',
+				scope: this,
+				handler: function() {
+					this.form.submit({
+						waitMsg : 'Sending data...',
+						success: function(form, action) {
+							TYPO3.Flashmessage.display(TYPO3.Severity.information,'TER Upload', 'Extension was uploaded to TER', 5);
+							form.reset();
+						},
+						failure: function(form, action) {
+							if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
+								TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Error',
+										'Status:'+action.response.status+': '+
+										action.response.statusText, 5);
+							}
+							if (action.failureType === Ext.form.Action.SERVER_INVALID){
+								// server responded with success = false
+								TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Invalid', action.result.errormsg, 5);
+							}
+						}
+					});
+				}
+			}],
+			listeners: {
+
+				activate: function(panel) {
+
+
+				}
+			},
+			scope: this
+		});
+
+		TYPO3.EM.TerUpload.superclass.initComponent.apply(this, arguments);
+	},
+
+	onRender: function() {
+
+
+		TYPO3.EM.TerUpload.superclass.onRender.apply(this, arguments);
+
+		Ext.apply(this.getForm(),{
+			api: {
+				load: TYPO3.EM.ExtDirect.loadUploadExtToTer,
+				submit: TYPO3.EM.ExtDirect.uploadExtToTer
+			},
+			paramsAsHash: false
+
+		});
+		this.form.load();
+	}
+
+
+});
+
+Ext.reg('terupload', TYPO3.EM.TerUpload);
\ No newline at end of file
Index: typo3/sysext/em/res/js/em_locallist.js
===================================================================
--- typo3/sysext/em/res/js/em_locallist.js	(revision )
+++ typo3/sysext/em/res/js/em_locallist.js	(revision )
@@ -0,0 +1,412 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <info@sk-typo3.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+Ext.ns('TYPO3.EM', 'TYPO3.EM.GridColumns');
+
+TYPO3.EM.LocalList = Ext.extend(Ext.grid.GridPanel, {
+	border:false,
+	stripeRows: true,
+
+	expander: new Ext.ux.grid.RowPanelExpander({
+		hideable: false,
+		createExpandingRowPanelItems: function(record, rowIndex){
+			var panelItems = [
+				new Ext.TabPanel({
+					plain: true,
+					activeTab: 0,
+					defaults: {
+						bodyStyle: 'background:#fff;padding:10px;',
+						boxMinHeight: 150
+					},
+					record: record,
+					items:[
+						{
+							title: 'Info',
+							autoHeight: true,
+							listeners: {
+								activate: function(panel) {
+									TYPO3.EM.Layouts.showExtInfo(panel, panel.ownerCt.record.data);
+								}
+							}
+						},
+						{
+							title:'Update',
+							html: '<div class="loading-indicator">Loading...</div>',
+							disabled: record.data.installed === 0,
+							listeners: {
+								activate: function(panel) {
+									TYPO3.EM.ExtDirect.getExtensionUpdate(record.data.extkey, function(response) {
+										panel.update(response);
+									});
+								}
+							}
+						},
+						{
+							title: 'Configuration',
+							xtype: 'form',
+							disabled: record.data.installed === 0,
+							autoHeight: true,
+							html: '<div class="loading-indicator">Loading...</div>',
+							listeners: {
+								activate: function(panel) {
+									TYPO3.EM.ExtDirect.getExtensionConfiguration(record.data.extkey, function(response) {
+										panel.update(response, true, this.readConfigForm.createDelegate(this));
+									}, this);
+								}
+							},
+							scope: this,
+							readConfigForm: function() {
+								var key = record.data.extkey;
+								var button = Ext.select('input[type="submit"]');
+								if (button) {
+									button.on('click', function() {
+										Ext.apply(this.form,{
+										api: {
+											submit: TYPO3.EM.ExtDirect.saveExtensionConfiguration
+										},
+										paramsAsHash: false
+
+									});
+										this.form.submit({
+											waitMsg : 'Saving Settings...',
+											success: function(form, action) {
+												TYPO3.Flashmessage.display(TYPO3.Severity.information, 'Configuration', 'Configuration was saved', 5);
+											},
+											failure: function(form, action) {
+												if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
+													TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Error',
+																			'Status:' + action.response.status + ': ' +
+																			action.response.statusText, 5);
+											}
+											if (action.failureType === Ext.form.Action.SERVER_INVALID) {
+													// server responded with success = false
+												TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Invalid', action.result.errormsg, 5);
+											}
+										 }
+									 });
+									}, this);
+								}
+							}
+						},
+						{
+							title: 'Files',
+							xtype: 'extfilelist',
+							recordData: record.data
+
+						},
+						{
+							xtype: 'terupload',
+							title:'Upload to TER',
+							recordData: record.data,
+							disabled: !TYPO3.settings.EM.hasCredentials
+						},
+						{
+							title:'Developer Information',
+							autoHeight: true,
+							html: '<div class="loading-indicator">Loading...</div>',
+							listeners: {
+								activate: function(panel) {
+									TYPO3.EM.ExtDirect.getExtensionDevelopInfo(record.data.extkey, function(response) {
+										panel.update(response);
+									});
+								}
+							}
+						},
+						{
+							title:'Backup/Delete',
+							disabled: true //record.data.installed === 0
+						}
+					]
+				})
+			];
+			return panelItems;
+		}
+	}),
+
+	initComponent:function() {
+		var localstore = new Ext.data.GroupingStore({
+			storeId: 'localstore',
+			proxy: new Ext.data.DirectProxy({
+				directFn: TYPO3.EM.ExtDirect.getExtensionList
+			}),
+
+			reader: new Ext.data.JsonReader({
+				idProperty: 'extkey',
+				root: 'data',
+				totalProperty: 'length',
+				fields:[
+					{name:'install'},
+					{name:'title'},
+					{name:'extkey'},
+					{name:'category'},
+					{name:'version'},
+					{name:'type'},
+					{name:'state'},
+					{name:'icon'},
+					{name:'description'},
+					{name:'shy'},
+					{name:'installed'},
+					{name:'author'},
+					{name:'author_email'},
+					{name:'author_company'},
+					{name:'download'},
+					{name:'doc'},
+					{name:'typeShort'},
+					{name:'nodePath'}
+				]
+			}),
+
+			sortInfo:{
+				field: 'title',
+				direction: 'ASC'
+			},
+			remoteSort: false,
+			groupField: 'category',
+			showAction: false,
+			listeners: {
+				beforeload: function() {
+					this.reloadButton.disable();
+				},
+				load: function(store, records) {
+					store.filterBy(store.storeFilter);
+					this.reloadButton.enable();
+					if (store.showAction) {
+						this.showExtension.defer(500, this);
+					}
+				},
+				datachanged: function(store){
+					Ext.getCmp('displayExtensionLabel').setText('Extensions: ' + store.data.length);
+					var hasFilters = false;
+					TYPO3.EM.Filters.filters.each(function (filter) {
+						if (filter.active) {
+							hasFilters = true;
+						}
+					});
+					if (hasFilters) {
+						this.doClearFilters.show();
+					} else {
+						this.doClearFilters.hide();
+					}
+				},
+				scope: this
+			},
+			storeFilter: function(record,id){
+				var shy = Ext.getCmp('shyFlag').getValue() ? 1 : 0;
+				var installed = Ext.getCmp('installedFlag').getValue() ? 1 : 0;
+				var obsolete = Ext.getCmp('obsoleteFlag').getValue() ? 1 : 0;
+				var filtertext = Ext.getCmp('localSearchField').getRawValue();
+				if (filtertext) {
+					//filter by search string
+					var re = new RegExp(Ext.escapeRe(filtertext));
+					var isMatched = record.data.extkey.match(re) || record.data.title.match(re);
+					if (!isMatched) {
+						return false;
+					}
+				}
+				if (obsolete && record.data.state === 'obsolete'){
+					return false;
+				}
+				var isShy = record.data.shy == 1 || '';
+				if (shy && isShy) {
+					return false;
+				}
+				if (installed && record.data.installed === 0) {
+					return false;
+				}
+				return true;
+			}
+		});
+
+		var searchField = new Ext.ux.form.FilterField({
+			store: localstore,
+			id: 'localSearchField',
+			width: 200
+		});
+
+		var cm = new Ext.grid.ColumnModel({
+			columns: [
+				this.expander,
+				TYPO3.EM.GridColumns.InstallExtension,
+				TYPO3.EM.GridColumns.ExtensionTitle,
+				TYPO3.EM.GridColumns.ExtensionKey,
+				TYPO3.EM.GridColumns.ExtensionCategory,
+				TYPO3.EM.GridColumns.ExtensionAuthor,
+				TYPO3.EM.GridColumns.ExtensionType,
+				TYPO3.EM.GridColumns.ExtensionState
+			],
+			defaultSortable: true
+
+		});
+
+
+
+		Ext.apply(this, {
+			itemId: 'em-localLocalExtensionlist',
+			title: 'Local Extension List',
+			loadMask: {msg: 'Loading Extensionlist ...'},
+			layout: 'fit',
+			store: localstore,
+			cm: cm,
+			plugins: [this.expander, TYPO3.EM.Filters],
+			view : new Ext.grid.GroupingView({
+				forceFit : true,
+				groupTextTpl : '{text} ({[values.rs.length]} {[values.rs.length > 1 ? "Items" : "Item"]})',
+				enableRowBody: true,
+				showPreview: true,
+				getRowClass: this.applyRowClass,
+				iconCls: 'icon-grid',
+				hideGroupedColumn: true
+			}),
+			sm: new Ext.grid.CellSelectionModel({
+				select: Ext.emptyFn
+			}),
+			tbar: [
+				{
+					xtype: 'tbtext',
+					text: 'Filter:'
+				},
+				searchField,
+				{
+					iconCls: 'x-tbar-loading',
+					ref: '../reloadButton',
+					handler: function() {
+						this.store.load();
+					},
+					scope: this
+				}, '-', {
+					text: 'Clear all Filters',
+					ref: '../doClearFilters',
+					handler: function() {
+						TYPO3.EM.Filters.clearFilters();
+					},
+					scope: this,
+					hidden: true
+				},
+				'->',
+				{
+					id: 'installedFlag',
+					xtype: 'checkbox',
+					checked: false,
+					boxLabel: 'show installed only' + '&nbsp;',
+					listeners: {
+						check: function(checkbox, checked) {
+							localstore.filterBy(localstore.storeFilter);
+						}
+					}
+				}, {
+					id: 'shyFlag',
+					xtype: 'checkbox',
+					checked: true,
+					boxLabel: 'show shy extensions' + '&nbsp;',
+					listeners: {
+						check: function(checkbox, checked) {
+							localstore.filterBy(localstore.storeFilter);
+						}
+					}
+				},{
+					id: 'obsoleteFlag',
+					xtype: 'checkbox',
+					checked: true,
+					boxLabel: 'show obsolete extensions' + '&nbsp;',
+					listeners: {
+						check: function(checkbox, checked) {
+							localstore.filterBy(localstore.storeFilter);
+						}
+					}
+				}
+			],
+			bbar:[
+				{
+					xtype: 'tbtext',
+					text: 'loading Extension list ...',
+					id: 'displayExtensionLabel',
+					style: {fontWeight: 'bold'}
+				},
+				'->',
+				{
+					text:'Upload Extension',
+					handler : function(){
+						TYPO3.EM.Tools.uploadExtension();
+					}
+				}, ' ', {
+					text:'Clear Grouping',
+					handler : function(){
+						localstore.clearGrouping();
+					}
+				}
+			]
+		});
+
+		TYPO3.EM.LocalList.superclass.initComponent.apply(this, arguments);
+
+		/* get install / uninstall clicks */
+		this.on('cellclick', function(grid, rowIndex, columnIndex, event) {
+			var record = grid.getStore().getAt(rowIndex);  // Get the Record
+
+			if (columnIndex === 1) { // column with install / remove images
+				if (event.getTarget('.installExtension', 1)) {
+					// install extension
+				}
+				if (event.getTarget('.removeExtension', 1)) {
+					// remove extension
+				}
+			}
+		}, this);
+
+	},
+
+
+	showExtension: function() {
+		var store = Ext.StoreMgr.lookup('localextensionstore');
+		var row = store.find('extkey', store.showAction);
+
+		if (row) {
+			this.expander.expandRow(row);
+			this.getSelectionModel().selectRow(row);
+			this.getView().focusRow(row);
+		}
+		store.showAction = false;
+	},
+
+	onRender: function() {
+		TYPO3.EM.LocalList.superclass.onRender.apply(this, arguments);
+	},
+
+	afterRender: function() {
+		TYPO3.EM.LocalList.superclass.afterRender.apply(this, arguments);
+	}
+
+
+
+});
+
+Ext.reg('TYPO3.EM.LocalList', TYPO3.EM.LocalList);
Index: typo3/sysext/em/res/js/em_tools.js
===================================================================
--- typo3/sysext/em/res/js/em_tools.js	(revision )
+++ typo3/sysext/em/res/js/em_tools.js	(revision )
@@ -0,0 +1,124 @@
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2010 Steffen Kamper <info@sk-typo3.de>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * ExtJS for the extension manager.
+ *
+ *
+ * @author Steffen Kamper <info@sk-typo3.de>
+ * @package TYPO3
+ * @subpackage extension manager
+ * @version $Id: $
+ */
+Ext.ns('TYPO3.EM');
+
+TYPO3.EM.Tools = function() {
+	return {
+		displayLocalExtension: function(extKey, reload) {
+			localStore = Ext.StoreMgr.get('localextensionstore');
+			// select local extension list
+			Ext.getCmp('em-main').setActiveTab(0);
+			if (reload === true) {
+				localStore.showAction = extKey;
+				localStore.load();
+			} else {
+				// find row and expand
+				/*var row = localStore.find('extkey', extKey);
+				 var grid = Ext.getCmp('em-local-extensions');
+
+				 grid.expander.expandRow(row);
+				 grid.getView().focusRow(row);
+				 grid.getSelectionModel().selectRow(row);*/
+			}
+		},
+
+		uploadExtension: function() {
+			w = new Ext.Window({
+				title: 'Upload extension file directly (.t3x)',
+				modal: true,
+				closable: true,
+				plain: true,
+				width: 400,
+				height: 160,
+				layout: 'form',
+				fileUpload: true,
+				items: [
+					{
+						xtype: 'fileuploadfield',
+						id: 'form-file',
+						emptyText: 'Select Extension (*.t3x)',
+						fieldLabel: 'Extension',
+						name: 'extupload-path',
+						buttonText: '...',
+						width: 250,
+						validator: function(value) {
+							if (value) {
+								return value.split('.').pop().toLowerCase() === 't3x';
+							}
+							return false;
+						}
+					},
+					TYPO3.EM.UploadLocationCombo,
+					{
+						xtype: 'checkbox',
+						fieldLabel: 'Overwrite any existing extension!',
+						name: 'uploadOverwrite',
+						labelWidth: 250
+					},
+					{
+						xtype: 'button',
+						text: 'Upload extension from your computer',
+						id: 'uploadSubmitButton',
+						width: 420,
+						scope: this,
+						handler: function() {
+							if (this.form.isValid()) {
+								this.form.submit({
+									waitMsg : 'Sending data...',
+									success: function(form, action) {
+										form.reset();
+										TYPO3.Flashmessage.display(TYPO3.Severity.information, 'Extension Upload', 'Extension "' + action.result.extKey + '" was uploaded.', 5);
+										w.close();
+										TYPO3.EM.Tools.displayLocalExtension(action.result.extKey, true);
+									},
+									failure: function(form, action) {
+										if (action.failureType === Ext.form.Action.CONNECT_FAILURE) {
+											TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Error',
+													'Status:' + action.response.status + ': ' +
+															action.response.statusText, 15);
+										}
+										if (action.failureType === Ext.form.Action.SERVER_INVALID) {
+											// server responded with success = false
+											TYPO3.Flashmessage.display(TYPO3.Severity.error, 'Invalid', action.result.errormsg, 5);
+										}
+										w.close();
+									}
+								});
+							}
+						}
+					}
+				]
+			}).show();
+		}
+	};
+}();
\ No newline at end of file
