Commit 24ceff8b authored by speedprogs_de's avatar speedprogs_de
Browse files

#45962 :: Added upload form for new extensions

git-svn-id: https://svn.typo3.org/TYPO3v4/Extensions/terfe/branches/ter_fe2@74309 735d13b6-9817-0410-8766-e36946ffe9aa
parent 920fd8ac
......@@ -111,6 +111,40 @@
}
/**
* Send flash message and redirect to given action
*
* @param string $message Identifier of the message to send
* @param string $action Name of the action
* @param string $controller Unqualified object name of the controller
* @param string $extension Name of the extension containing the controller
* @param array $arguments Arguments to pass to the target action
* @return void
*/
protected function redirectWithError($message, $action, $controller = NULL, $extension = NULL, array $arguments = NULL) {
$this->flashMessageContainer->add($message, '', t3lib_FlashMessage::ERROR);
$this->clearPageCache($GLOBALS['TSFE']->id);
$this->redirect($action, $controller, $extension, $arguments);
}
/**
* Send flash message and forward to given action
*
* @param string $message Identifier of the message to send
* @param string $action Name of the action
* @param string $controller Unqualified object name of the controller
* @param string $extension Name of the extension containing the controller
* @param array $arguments Arguments to pass to the target action
* @return void
*/
protected function forwardWithError($message, $action, $controller = NULL, $extension = NULL, array $arguments = NULL) {
$this->flashMessageContainer->add($message, '', t3lib_FlashMessage::ERROR);
$this->clearPageCache($GLOBALS['TSFE']->id);
$this->forward($action, $controller, $extension, $arguments);
}
/**
* Clear cache of given pages
*
......
......@@ -73,6 +73,11 @@
*/
protected $ownerRepository;
/**
* @var array
*/
protected $frontendUser;
/**
* Initializes the controller
*
......@@ -92,6 +97,8 @@
// Show insecure extensions only for reviewers
$this->extensionRepository->setShowInsecure($this->securityRole->isReviewer());
$this->versionRepository->setShowInsecure($this->securityRole->isReviewer());
$this->frontendUser = (!empty($GLOBALS['TSFE']->fe_user->user) ? $GLOBALS['TSFE']->fe_user->user : array());
}
/**
......@@ -364,6 +371,85 @@
}
/**
* Show upload form for a new extension version
*
* @param Tx_TerFe2_Domain_Model_Extension $extension The extension object
* @param array $form Form information for the new version
* @return void
* @dontvalidate $extension
* @dontvalidate $form
*/
public function uploadVersionAction(Tx_TerFe2_Domain_Model_Extension $extension, array $form = array()) {
if (!t3lib_extMgm::isLoaded('ter')) {
$this->flashMessageContainer->add($this->translate('msq.createVersionTerNotLoaded'));
}
$this->view->assign('extension', $extension);
$this->view->assign('form', $form);
}
/**
* Create new version of an extension
*
* @param Tx_TerFe2_Domain_Model_Extension $extension The extension object
* @param array $form Form information for the new version
* @return void
* @dontvalidate $extension
* @dontvalidate $form
*/
public function createVersionAction(Tx_TerFe2_Domain_Model_Extension $extension, array $form) {
if (!t3lib_extMgm::isLoaded('ter')) {
$this->forwardWithError($this->translate('msg.createVersionTerNotLoaded'), 'uploadVersion');
}
if (empty($this->frontendUser['username'])) {
$this->forwardWithError($this->translate('msg.createVersionNotLoggedIn'), 'uploadVersion');
}
if (empty($form['comment'])) {
$this->forwardWithError($this->translate('msg.createVersionCommentEmpty'), 'uploadVersion');
}
$fileInfo = Tx_TerFe2_Utility_File::getFileInfo('tx_terfe2_pi1.form.file');
if (empty($fileInfo) || empty($fileInfo['tmp_name']) || $fileInfo['error'] != UPLOAD_ERR_OK) {
$this->forwardWithError($this->translate('msg.createVersionFileEmpty'), 'uploadVersion');
}
if (empty($fileInfo['name']) || substr($fileInfo['name'], -3) !== 'zip') {
$this->forwardWithError($this->translate('msg.createVersionFileNoZip'), 'uploadVersion');
}
$files = array();
try {
$extensionInfo = Tx_TerFe2_Utility_Archive::getExtensionDetailsFromZipArchive($fileInfo['tmp_name'], $files);
} catch (Exception $exception) {
$this->forwardWithError($exception->getMessage(), 'uploadVersion');
}
unlink($fileInfo['tmp_name']);
if (empty($extensionInfo->version)) {
$this->forwardWithError($this->translate('msg.createVersionVersionEmpty'), 'uploadVersion');
}
$extensionKey = preg_replace('/_(\d+)(\.|\-)(\d+)(\.|\-)(\d+)/i', '', strtolower($fileInfo['name']));
$extensionKey = substr($extensionKey, 0, strrpos($extensionKey, '.'));
if ($extensionKey !== $extension->getExtKey()) {
$this->forwardWithError($this->translate('msg.createVersionFilenameNotValid'), 'uploadVersion');
}
if (!$this->userIsAllowedToUploadExtension($extensionKey)) {
$this->forwardWithError($this->translate('msg.createVersionUploadNotAllowed'), 'uploadVersion');
}
if (!$this->versionIsPossibleForExtension($extensionKey, $extensionInfo->version)) {
$this->forwardWithError($this->translate('msg.createVersionVersionExists'), 'uploadVersion');
}
$extensionInfo->extensionKey = $extensionKey;
$filesData = (object) array('fileData' => $files);
try {
$result = tx_ter_api::uploadExtensionWithoutSoap($this->frontendUser['username'], $extensionInfo, $filesData);
if ($result) {
$this->redirect('index', 'Registerkey', NULL, array('uploaded' => TRUE), $this->settings['pages']['manageKeysPID']);
}
} catch(Exception $exception) {
$this->forwardWithError($exception->getMessage(), 'uploadVersion');
}
$this->forwardWithError($this->translate('msg.createVersionUploadFailed'), 'uploadVersion');
}
/**
* Returns all / filtered extensions
*
......@@ -449,5 +535,51 @@
$number = $this->extensionRepository->findAll()->count();
return (int) $number;
}
/**
* Check if current frontend user can upload given extension
*
* There is no better (and faster) way to do this at the momement.
*
* @param string $extensionKey The extension key
* @return boolean TRUE if upload is allowed
*/
protected function userIsAllowedToUploadExtension($extensionKey) {
if (empty($this->frontendUser['username'])) {
return FALSE;
}
$isAllowedToUploadKey = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
'uid',
'tx_ter_extensionkeys',
'ownerusername LIKE "' . $GLOBALS['TYPO3_DB']->quoteStr($this->frontendUser['username'], 'foo') . '"
AND extensionkey LIKE "' . $GLOBALS['TYPO3_DB']->quoteStr($extensionKey, 'foo') . '"'
);
return !empty($isAllowedToUploadKey);
}
/**
* Check if an version does not exist for extension
*
* There is no better (and faster) way to do this at the momement.
*
* @param string $extensionKey The extension key
* @param string $versionString The extension version
* @return boolean TRUE if version already exists
*/
protected function versionIsPossibleForExtension($extensionKey, $versionString) {
if (empty($extensionKey) || empty($versionString)) {
return FALSE;
}
$versionExistsForExtension = $GLOBALS['TYPO3_DB']->exec_SELECTcountRows(
'uid',
'tx_ter_extensions',
'extensionkey = "' . $GLOBALS['TYPO3_DB']->quoteStr($extensionKey, 'foo') . '"
AND version LIKE "' . $GLOBALS['TYPO3_DB']->quoteStr($versionString, 'foo') . '"'
);
return empty($versionExistsForExtension);
}
}
?>
\ No newline at end of file
......@@ -65,9 +65,11 @@ class Tx_TerFe2_Controller_RegisterkeyController extends Tx_TerFe2_Controller_Ab
/**
* Initialize all actions
*
* @param boolean $uploaded TRUE if an extension version was successfully uploaded
* @return void
* @dontvalidate $uploaded
*/
public function indexAction() {
public function indexAction($uploaded = FALSE) {
// get categories for register key
// $categories = $this->categoryRepository->findAll();
// $this->view->assign('categories', $categories);
......@@ -75,6 +77,7 @@ class Tx_TerFe2_Controller_RegisterkeyController extends Tx_TerFe2_Controller_Ab
if (!empty($this->frontendUser)) {
$extensions = $this->extensionRepository->findByFrontendUser($this->frontendUser['username']);
$this->view->assign('extensions', $extensions);
$this->view->assign('uploaded', $uploaded);
}
}
......
......@@ -39,12 +39,12 @@
protected $authenticationHeader;
protected
$wsdlUrl,
$username,
$password,
$returnExceptions
;
protected
$wsdlUrl,
$username,
$password,
$returnExceptions
;
/**
* Setup connection
......@@ -57,58 +57,57 @@
*/
public function connect($wsdlUrl, $username = '', $password = '', $returnExceptions = FALSE) {
if (empty($wsdlUrl)) {
throw new Exception('No valid wsdl URL given');
}
if (!class_exists('SoapClient')) {
throw new Exception('PHP SOAP extension not available');
}
$this->wsdlUrl = $wsdlUrl;
$this->username = $username;
$this->password = $password;
$this->returnExceptions = $returnExceptions;
return $this->resetConnection();
if (empty($wsdlUrl)) {
throw new Exception('No valid wsdl URL given');
}
if (!class_exists('SoapClient')) {
throw new Exception('PHP SOAP extension not available');
}
$this->wsdlUrl = $wsdlUrl;
$this->username = $username;
$this->password = $password;
$this->returnExceptions = $returnExceptions;
return $this->resetConnection();
}
/**
* creates a new connection
*
* A new SoapClient has to be used to prevent unexpected behavior.
* The bug occured when registering an extension key. The response to the second request to the server (createExtensionKey)
* just returned pure jibberish and caused an exception.
* This seems to be a bug in PHP (@see https://bugs.php.net/bug.php?id=42191) and the easiest way to fix it, is
* to reset the connection.
* Maybe this bug was fixed in a newer version of PHP (don't know), but it is a real issue on my local PHP
* installation (5.2.10). So this *might* be reverted later on.
*
* @author Christian Zenker <christian.zenker@599media.de>
*/
public function resetConnection() {
// Create connection
$this->soapConnection = new SoapClient($this->wsdlUrl, array(
'trace' => 1,
'exceptions' => (int) $this->returnExceptions,
));
// Get authentication header
if (!empty($this->username) && !empty($this->password)) {
$headerData = array('username' => $this->username, 'password' => $this->password);
$this->authenticationHeader = new SoapHeader('', 'HeaderLogin', (object) $headerData, TRUE);
}
}
/**
* creates a new connection
*
* A new SoapClient has to be used to prevent unexpected behavior.
* The bug occured when registering an extension key. The response to the second request to the server (createExtensionKey)
* just returned pure jibberish and caused an exception.
* This seems to be a bug in PHP (@see https://bugs.php.net/bug.php?id=42191) and the easiest way to fix it, is
* to reset the connection.
* Maybe this bug was fixed in a newer version of PHP (don't know), but it is a real issue on my local PHP
* installation (5.2.10). So this *might* be reverted later on.
*
* @author Christian Zenker <christian.zenker@599media.de>
*/
public function resetConnection() {
// Create connection
$this->soapConnection = new SoapClient($this->wsdlUrl, array(
'trace' => 1,
'exceptions' => (bool) $this->returnExceptions,
'encoding' => 'UTF-8',
'soap_version' => SOAP_1_2,
));
// Get authentication header
if (!empty($this->username) && !empty($this->password)) {
$headerData = array('username' => $this->username, 'password' => $this->password);
$this->authenticationHeader = new SoapHeader('', 'HeaderLogin', (object) $headerData, TRUE);
}
}
/**
* Set connection object
*
* @param SoapClient $soapConnection SOAP connection object
* @deprecated Christian Zenker: Seems not to be used anywhere on typo3.org and the method is useless if the connection is reset for each call
* @deprecated Christian Zenker: Seems not to be used anywhere on typo3.org and the method is useless if the connection is reset for each call
* @return void
*/
public function setConnection(SoapClient $soapConnection) {
......@@ -157,7 +156,7 @@
*/
public function __call($methodName, array $params = array()) {
$this->resetConnection();
$this->resetConnection();
// // Check for existing connection
// if (empty($this->soapConnection)) {
......
......@@ -73,7 +73,7 @@
/**
* Returns the content of a zip archive
* Writes a zip archive to filesystem
*
* @param string $filename File name
* @param string $path Path to extract into
......@@ -116,6 +116,55 @@
}
/**
* Returns the content of a zip archive
*
* @param string $filename File name
* @return array File informations
*/
public static function getZipArchiveContent($filename) {
if (!class_exists('ZipArchive')) {
throw new Exception('Please make sure that php zip extension is installed');
}
// Check if file exists
if (!Tx_TerFe2_Utility_File::fileExists($filename)) {
throw new Exception('File "' . $filename . '" not found to extract');
}
// Load zip archive
$zipArchive = new ZipArchive();
if (empty($zipArchive) || !$zipArchive->open($filename)) {
throw new Exception('Could not open zip file to read');
}
// Get all files
$files = array();
for($i = 0; $i < $zipArchive->numFiles; $i++){
$fileInfo = $zipArchive->statIndex($i);
$filePointer = $zipArchive->getStream($fileInfo['name']);
if (!$filePointer) {
continue;
}
$content = '';
while (!feof($filePointer)) {
$content .= fread($filePointer, 1024);
}
fclose($filePointer);
$files[$fileInfo['name']] = (object) array(
'name' => $fileInfo['name'],
'size' => $fileInfo['size'],
'modificationTime' => $fileInfo['mtime'],
'isExecutable' => (substr($fileInfo['name'], -3) === 'php'),
'content' => base64_encode($content),
'contentMD5' => md5($content),
);
}
return $files;
}
/**
* Creates a zip file from given extension T3X file
*
......@@ -211,5 +260,97 @@
return unserialize($files);
}
/**
* Load extension information from zip file
*
* @param string $filename Path to zip file
* @param array $files Reference to files
* @return stdObj Extension information
* @see tx_em_Extensions_Details::uploadToTER
*/
public static function getExtensionDetailsFromZipArchive($filename, array &$files = array()) {
$files = self::getZipArchiveContent($filename);
if (empty($files) || empty($files['ext_emconf.php'])) {
return NULL;
}
$extEmconf = str_replace(array('<?php', '<?', '?>'), '', base64_decode($files['ext_emconf.php']->content));
eval($extEmconf);
if (empty($EM_CONF) || !is_array($EM_CONF)) {
return NULL;
}
$extEmconf = reset($EM_CONF);
// Dependencies / conflicts
$dependencies = array();
$extKeysArr = $extEmconf['dependencies'];
if (is_array($extKeysArr)) {
foreach ($extKeysArr as $extKey => $version) {
if (strlen($extKey)) {
$dependenciesArr[] = array(
'kind' => 'depends',
'extensionKey' => $extKey,
'versionRange' => $version,
);
}
}
}
$extKeysArr = $extEmconf['conflicts'];
if (is_array($extKeysArr)) {
foreach ($extKeysArr as $extKey => $version) {
if (strlen($extKey)) {
$dependenciesArr[] = array(
'kind' => 'conflicts',
'extensionKey' => $extKey,
'versionRange' => $version,
);
}
}
}
if (count($dependenciesArr) == 1) {
$dependenciesArr[] = array(
'kind' => 'depends',
'extensionKey' => '',
'versionRange' => '',
);
}
$clearCacheOnLoad = (isset($extEmconf['clearCacheOnLoad']) ? $extEmconf['clearCacheOnLoad'] : $extEmconf['clearcacheonload']);
// Build extension information
return (object) array(
'extensionKey' => '',
'version' => $extEmconf['version'],
'metaData' => (object) array(
'title' => $extEmconf['title'],
'description' => $extEmconf['description'],
'category' => $extEmconf['category'],
'state' => $extEmconf['state'],
'authorName' => $extEmconf['author'],
'authorEmail' => $extEmconf['author_email'],
'authorCompany' => $extEmconf['author_company'],
),
'technicalData' => (object) array(
'dependencies' => (object) $dependenciesArr,
'loadOrder' => $extEmconf['loadOrder'],
'uploadFolder' => (bool) $extEmconf['uploadfolder'],
'createDirs' => $extEmconf['createDirs'],
'shy' => (bool) $extEmconf['shy'],
'modules' => $extEmconf['module'],
'modifyTables' => $extEmconf['modify_tables'],
'priority' => $extEmconf['priority'],
'clearCacheOnLoad' => (bool) $clearCacheOnLoad,
'lockType' => $extEmconf['lockType'],
'doNotLoadInFEe' => $extEmconf['doNotLoadInFE'],
'docPath' => $extEmconf['docPath'],
),
'infoData' => (object) array(
'codeLines' => 0,
'codeBytes' => 0,
'codingGuidelinesCompliance' => $extEmconf['CGLcompliance'],
'codingGuidelinesComplianceNotes' => $extEmconf['CGLcompliance_note'],
'uploadComment' => '',
'techInfo' => '',
),
);
}
}
?>
\ No newline at end of file
......@@ -380,6 +380,25 @@
}
/**
* Move uploaded file to given directory
*
* @param string $tempname Temporary file name
* @param string $filename Original file name
* @param string $directory Directory path
* @return New file name
*/
static public function moveUploadedFile($tempname, $filename, $directory = 'uploads/') {
$basicFileFunctions = t3lib_div::makeInstance('t3lib_basicFileFunctions');
$newFilename = $basicFileFunctions->getUniqueName($filename, self::getAbsoluteDirectory($directory));
if (t3lib_div::upload_copy_move($tempname, $newFilename)) {
return basename($newFilename);
}
return '';
}
/**
* Remove a file
*
......@@ -490,5 +509,47 @@ $EM_CONF[$_EXTKEY] = ' . tx_em_Tools::arrayToCode($emConfArray, 0) . ';
return str_replace(CR, '', $content);
}
/**
* Returns information about uploaded file
*
* @param string $field Path to field (e.g. tx_myext_pi1.myObject.myAttribute)
* @return array File information
*/
public static function getFileInfo($field) {
$arrayKeys = t3lib_div::trimExplode('.', $field, TRUE);
// No information found
if (empty($_FILES[$arrayKeys[0]]['tmp_name'])) {
return array();
}
// Single file structure
if (is_string($_FILES[$arrayKeys[0]]['tmp_name'])) {
return $_FILES[$arrayKeys[0]];
}
// Multi file structure
if (is_array($_FILES[$arrayKeys[0]]['tmp_name'])) {
$fileInfo = array();
$fileArray = $_FILES[$arrayKeys[0]];
array_shift($arrayKeys);
foreach ($fileArray as $key => $values) {
$info = $values;
foreach ($arrayKeys as $arrayKey) {
if (isset($info[$arrayKey])) {
$info = $info[$arrayKey];
}
}
$fileInfo[$key] = (!is_array($info) ? $info : NULL);
}
return $fileInfo;
}
return array();
}
}
?>
\ No newline at end of file
......@@ -20,11 +20,11 @@
<items type="array">
<numIndex index="0" type="array">
<numIndex index="0">LLL:EXT:ter_fe2/Resources/Private/Language/locallang_db.xml:tt_content.flexform_pi1.s_def.switchableControllerActions.0</numIndex>
<numIndex index="1">Extension->index;Extension->search;Extension->list;Extension->listLatest;Extension->show;Extension->new;Extension->create;Extension->edit;Extension->update;Extension->delete;Extension->download;Category->list;Category->new;Category->create;Category->edit;Category->update;Category->delete;Category->show;Tag->list;Tag->new;Tag->create;Tag->edit;Tag->update;Tag->delete;Tag->show;Author->list;Author->edit;Author->update;Author->show;Media->list;Media->new;Media->create;Media->edit;Media->update;Media->delete;Media->show;Review->update;</numIndex>
<numIndex index="1">Extension->index;Extension->search;Extension->list;Extension->listLatest;Extension->show;Extension->new;Extension->create;Extension->edit;Extension->update;Extension->delete;Extension->download;Category->list;Category->new;Category->create;Category->edit;Category->update;Category->delete;Category->show;Tag->list;Tag->new;Tag->create;Tag->edit;Tag->update;Tag->delete;Tag->show;Author->list;Author->edit;Author->update;Author->show;Media->list;Media->new;Media->create;Media->edit;Media->update;Media->delete;Media->show;Review->update;Extension->uploadVersion;Extension->createVersion</numIndex>
</numIndex>
<numIndex index="1" type="array">
<numIndex index="0">LLL:EXT:ter_fe2/Resources/Private/Language/locallang_db.xml:tt_content.flexform_pi1.s_def.switchableControllerActions.1</numIndex>
<numIndex index="1">Registerkey->index;Registerkey->create;Registerkey->edit;Registerkey->manage;Registerkey->update;Registerkey->delete;Registerkey->transfer;Registerkey->salvage</numIndex>
<numIndex index="1">Registerkey->index;Registerkey->create;Registerkey->edit;Registerkey->manage;Registerkey->update;Registerkey->delete;Registerkey->transfer;Registerkey->salvage;Extension->uploadVersion;Extension->createVersion</numIndex>
</numIndex>
<numIndex index="2" type="array">
<numIndex index="0">LLL:EXT:ter_fe2/Resources/Private/Language/locallang_db.xml:tt_content.flexform_pi1.s_def.switchableControllerActions.2</numIndex>
......@@ -32,7 +32,7 @@
</numIndex>
<numIndex index="3" type="array">
<numIndex index="0">LLL:EXT:ter_fe2/Resources/Private/Language/locallang_db.xml:tt_content.flexform_pi1.s_def.switchableControllerActions.3</numIndex>
<numIndex index="1">Registerkey->index;Registerkey->admin;Registerkey->deleteExtensionVersion;Registerkey->create;Registerkey->edit;Registerkey->manage;Registerkey->update;Registerkey->delete;Registerkey->transfer;Registerkey->salvage</numIndex>
<numIndex index="1">Registerkey->index;Registerkey->admin;Registerkey->deleteExtensionVersion;Registerkey->create;Registerkey->edit;Registerkey->manage;Registerkey->update;Registerkey->delete;Registerkey->transfer;Registerkey->salvage;Extension->uploadVersion;Extension->createVersion</numIndex>
</numIndex>
</items>