[TASK] Integrate Flow Package Subpackage 66/24666/4
authorThomas Maroschik <tmaroschik@dfau.de>
Sat, 12 Oct 2013 23:29:32 +0000 (01:29 +0200)
committerThomas Maroschik <tmaroschik@dfau.de>
Sun, 13 Oct 2013 00:37:51 +0000 (02:37 +0200)
In order to enable the Package Management API, the used Flow classes
have been placed in the core. The last synced state and the made
changes to the source are noted in a README file.

Resolves: #52737
Related: #47018
Releases: 6.2
Change-Id: Ic8fda623d9ff81f52c3c104a0d5ae7c290bf7953
Reviewed-on: https://review.typo3.org/24666
Reviewed-by: Alexander Opitz
Tested-by: Alexander Opitz
Reviewed-by: Sascha Egerer
Tested-by: Sascha Egerer
Reviewed-by: Thomas Maroschik
Tested-by: Thomas Maroschik
30 files changed:
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Exception.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Documentation.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Documentation/Format.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/CorruptPackageException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/DuplicatePackageException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageKeyException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageManifestException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackagePathException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageStateException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/MissingPackageManifestException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/PackageKeyAlreadyExistsException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/PackageRepositoryException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/ProtectedPackageKeyException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/UnknownPackageException.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/AbstractConstraint.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/AbstractParty.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/Company.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/PackageConstraint.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/Person.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/SystemConstraint.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaDataInterface.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Package.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageFactory.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageInterface.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageManager.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageManagerInterface.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Utility/Files.php [new file with mode: 0644]
typo3/sysext/core/Resources/PHP/TYPO3.Flow/README [new file with mode: 0644]

diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Exception.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Exception.php
new file mode 100644 (file)
index 0000000..b37ce58
--- /dev/null
@@ -0,0 +1,56 @@
+<?php
+namespace TYPO3\Flow;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * A generic Flow Exception
+ *
+ * @api
+ */
+class Exception extends \TYPO3\CMS\Core\Exception {
+
+       /**
+        * @var string
+        */
+       protected $referenceCode;
+
+       /**
+        * @var integer
+        */
+       protected $statusCode = 500;
+
+       /**
+        * Returns a code which can be communicated publicly so that whoever experiences the exception can refer
+        * to it and a developer can find more information about it in the system log.
+        *
+        * @return string
+        * @api
+        */
+       public function getReferenceCode() {
+               if (!isset($this->referenceCode)) {
+                       $this->referenceCode = date('YmdHis', $_SERVER['REQUEST_TIME']) . substr(md5(rand()), 0, 6);
+               }
+               return $this->referenceCode;
+       }
+
+       /**
+        * Returns the HTTP status code this exception corresponds to (defaults to 500).
+        *
+        * @return integer
+        * @api
+        */
+       public function getStatusCode() {
+               return $this->statusCode;
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Documentation.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Documentation.php
new file mode 100644 (file)
index 0000000..5971b90
--- /dev/null
@@ -0,0 +1,104 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Documentation for a package
+ *
+ * @api
+ */
+class Documentation {
+
+       /**
+        * Reference to the package of this documentation
+        * @var \TYPO3\Flow\Package\PackageInterface
+        */
+       protected $package;
+
+       /**
+        * @var string
+        */
+       protected $documentationName;
+
+       /**
+        * Absolute path to the documentation
+        * @var string
+        */
+       protected $documentationPath;
+
+       /**
+        * Constructor
+        *
+        * @param \TYPO3\Flow\Package\PackageInterface $package Reference to the package of this documentation
+        * @param string $documentationName Name of the documentation
+        * @param string $documentationPath Absolute path to the documentation directory
+        */
+       public function __construct($package, $documentationName, $documentationPath) {
+               $this->package = $package;
+               $this->documentationName = $documentationName;
+               $this->documentationPath = $documentationPath;
+       }
+
+       /**
+        * Get the package of this documentation
+        *
+        * @return \TYPO3\Flow\Package\PackageInterface The package of this documentation
+        * @api
+        */
+       public function getPackage() {
+               return $this->package;
+       }
+
+       /**
+        * Get the name of this documentation
+        *
+        * @return string The name of this documentation
+        * @api
+        */
+       public function getDocumentationName() {
+               return $this->documentationName;
+       }
+
+       /**
+        * Get the full path to the directory of this documentation
+        *
+        * @return string Path to the directory of this documentation
+        * @api
+        */
+       public function getDocumentationPath() {
+               return $this->documentationPath;
+       }
+
+       /**
+        * Returns the available documentation formats for this documentation
+        *
+        * @return array Array of \TYPO3\Flow\Package\DocumentationFormat
+        * @api
+        */
+       public function getDocumentationFormats() {
+               $documentationFormats = array();
+
+               $documentationFormatsDirectoryIterator = new \DirectoryIterator($this->documentationPath);
+               $documentationFormatsDirectoryIterator->rewind();
+               while ($documentationFormatsDirectoryIterator->valid()) {
+                       $filename = $documentationFormatsDirectoryIterator->getFilename();
+                       if ($filename[0] != '.' && $documentationFormatsDirectoryIterator->isDir()) {
+                               $documentationFormat = new \TYPO3\Flow\Package\Documentation\Format($filename, $this->documentationPath . $filename . '/');
+                               $documentationFormats[$filename] = $documentationFormat;
+                       }
+                       $documentationFormatsDirectoryIterator->next();
+               }
+
+               return $documentationFormats;
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Documentation/Format.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Documentation/Format.php
new file mode 100644 (file)
index 0000000..f57fd71
--- /dev/null
@@ -0,0 +1,89 @@
+<?php
+namespace TYPO3\Flow\Package\Documentation;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\Flow\Annotations as Flow;
+
+/**
+ * Documentation format of a documentation
+ *
+ * @Flow\Scope("singleton")
+ * @api
+ */
+class Format {
+
+       /**
+        * @var string
+        */
+       protected $formatName;
+
+       /**
+        * Absolute path to the documentation format
+        * @var string
+        */
+       protected $formatPath;
+
+       /**
+        * Constructor
+        *
+        * @param string $formatName Name of the documentation format
+        * @param string $formatPath Absolute path to the documentation format
+        */
+       public function __construct($formatName, $formatPath) {
+               $this->formatName = $formatName;
+               $this->formatPath = $formatPath;
+       }
+
+       /**
+        * Get the name of this documentation format
+        *
+        * @return string The name of this documentation format
+        * @api
+        */
+       public function getFormatName() {
+               return $this->formatName;
+       }
+
+       /**
+        * Get the full path to the directory of this documentation format
+        *
+        * @return string Path to the directory of this documentation format
+        * @api
+        */
+       public function getFormatPath() {
+               return $this->formatPath;
+       }
+
+       /**
+        * Returns the available languages for this documentation format
+        *
+        * @return array Array of string language codes
+        * @api
+        */
+       public function getAvailableLanguages() {
+               $languages = array();
+
+               $languagesDirectoryIterator = new \DirectoryIterator($this->formatPath);
+               $languagesDirectoryIterator->rewind();
+               while ($languagesDirectoryIterator->valid()) {
+                       $filename = $languagesDirectoryIterator->getFilename();
+                       if ($filename[0] != '.' && $languagesDirectoryIterator->isDir()) {
+                               $language = $filename;
+                               $languages[] = $language;
+                       }
+                       $languagesDirectoryIterator->next();
+               }
+
+               return $languages;
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception.php
new file mode 100644 (file)
index 0000000..59d88cf
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * A generic Package Exception
+ *
+ * @api
+ */
+class Exception extends \TYPO3\CMS\Core\Exception {
+
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/CorruptPackageException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/CorruptPackageException.php
new file mode 100644 (file)
index 0000000..7301a48
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * "Corrupt Package" Exception
+ *
+ */
+class CorruptPackageException extends \TYPO3\Flow\Package\Exception {
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/DuplicatePackageException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/DuplicatePackageException.php
new file mode 100644 (file)
index 0000000..f5b45a5
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * "Duplicate Package" Exception
+ *
+ * @api
+ */
+class DuplicatePackageException extends \TYPO3\Flow\Package\Exception {
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageKeyException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageKeyException.php
new file mode 100644 (file)
index 0000000..0924879
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * An "Invalid Package Key" exception
+ *
+ * @api
+ */
+class InvalidPackageKeyException extends \TYPO3\Flow\Package\Exception {
+
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageManifestException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageManifestException.php
new file mode 100644 (file)
index 0000000..128bab9
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * An "Invalid Package Key" exception
+ *
+ * @api
+ */
+class InvalidPackageManifestException extends \TYPO3\Flow\Package\Exception {
+
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackagePathException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackagePathException.php
new file mode 100644 (file)
index 0000000..685d220
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * "Invalid Package Path" Exception
+ *
+ * @api
+ */
+class InvalidPackagePathException extends \TYPO3\Flow\Package\Exception {
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageStateException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/InvalidPackageStateException.php
new file mode 100644 (file)
index 0000000..fb1862c
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * An "Invalid Package State" exception
+ *
+ * @api
+ */
+class InvalidPackageStateException extends \TYPO3\Flow\Package\Exception {
+
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/MissingPackageManifestException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/MissingPackageManifestException.php
new file mode 100644 (file)
index 0000000..4e4106e
--- /dev/null
@@ -0,0 +1,23 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * An "Invalid Package Key" exception
+ *
+ * @api
+ */
+class MissingPackageManifestException extends \TYPO3\Flow\Package\Exception {
+
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/PackageKeyAlreadyExistsException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/PackageKeyAlreadyExistsException.php
new file mode 100644 (file)
index 0000000..9e125db
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * A "Package Key Already Exists" exception
+ *
+ * @api
+ */
+class PackageKeyAlreadyExistsException extends \TYPO3\Flow\Package\Exception {
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/PackageRepositoryException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/PackageRepositoryException.php
new file mode 100644 (file)
index 0000000..fabf9a4
--- /dev/null
@@ -0,0 +1,21 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * A "Package Repository" exception
+ *
+ */
+class PackageRepositoryException extends \TYPO3\Flow\Package\Exception {
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/ProtectedPackageKeyException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/ProtectedPackageKeyException.php
new file mode 100644 (file)
index 0000000..fa9674b
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * A "Protected Package Key" exception
+ *
+ * @api
+ */
+class ProtectedPackageKeyException extends \TYPO3\Flow\Package\Exception {
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/UnknownPackageException.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Exception/UnknownPackageException.php
new file mode 100644 (file)
index 0000000..68da4a2
--- /dev/null
@@ -0,0 +1,22 @@
+<?php
+namespace TYPO3\Flow\Package\Exception;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * "Unknown Package" Exception
+ *
+ * @api
+ */
+class UnknownPackageException extends \TYPO3\Flow\Package\Exception {
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData.php
new file mode 100644 (file)
index 0000000..f850cec
--- /dev/null
@@ -0,0 +1,212 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * The default TYPO3 Package MetaData implementation
+ *
+ */
+class MetaData implements \TYPO3\Flow\Package\MetaDataInterface {
+
+       /**
+        * @var array
+        */
+       protected static $CONSTRAINT_TYPES = array(self::CONSTRAINT_TYPE_DEPENDS, self::CONSTRAINT_TYPE_CONFLICTS, self::CONSTRAINT_TYPE_SUGGESTS);
+
+       /**
+        * @var string
+        */
+       protected $packageKey;
+
+       /**
+        * Package type
+        *
+        * @var string
+        */
+       protected $packageType;
+
+       /**
+        * The version number
+        * @var string
+        */
+       protected $version;
+
+       /**
+        * Package title
+        * @var string
+        */
+       protected $title;
+
+       /**
+        * Package description
+        * @var string
+        */
+       protected $description;
+
+       /**
+        * Package categories as string
+        * @var array
+        */
+       protected $categories = array();
+
+       /**
+        * Package parties (person, company)
+        * @var array
+        */
+       protected $parties = array();
+
+       /**
+        * constraints by constraint type (depends, conflicts, suggests)
+        * @var array
+        */
+       protected $constraints = array();
+
+       /**
+        * Get all available constraint types
+        *
+        * @return array All constraint types
+        */
+       public function getConstraintTypes() {
+               return self::$CONSTRAINT_TYPES;
+       }
+
+       /**
+        * Package metadata constructor
+        *
+        * @param string $packageKey The package key
+        */
+       public function __construct($packageKey) {
+               $this->packageKey = $packageKey;
+       }
+
+       /**
+        * @return string The package key
+        */
+       public function getPackageKey() {
+               return $this->packageKey;
+       }
+
+       /**
+        * Get package type
+        *
+        * @return string
+        */
+       public function getPackageType() {
+               return $this->packageType;
+       }
+
+       /**
+        * Set package type
+        *
+        * @param string $packageType
+        */
+       public function setPackageType($packageType) {
+               $this->packageType = $packageType;
+       }
+
+       /**
+        * @return string The package version
+        */
+       public function getVersion() {
+               return $this->version;
+       }
+
+       /**
+        * @param string $version The package version to set
+        * @return void
+        */
+       public function setVersion($version) {
+               $this->version = $version;
+       }
+
+       /**
+        * @return string The package description
+        */
+       public function getDescription() {
+               return $this->description;
+       }
+
+       /**
+        * @param string $description The package description to set
+        * @return void
+        */
+       public function setDescription($description) {
+               $this->description = $description;
+       }
+
+       /**
+        * @return Array of string The package categories
+        */
+       public function getCategories() {
+               return $this->categories;
+       }
+
+       /**
+        * Adds a package category
+        *
+        * @param string $category
+        * @return void
+        */
+       public function addCategory($category) {
+               $this->categories[] = $category;
+       }
+
+       /**
+        * @return Array of TYPO3\Flow\Package\MetaData\AbstractParty The package parties
+        */
+       public function getParties() {
+               return $this->parties;
+       }
+
+       /**
+        * Add a party
+        *
+        * @param \TYPO3\Flow\Package\MetaData\AbstractParty $party
+        * @return void
+        */
+       public function addParty(\TYPO3\Flow\Package\MetaData\AbstractParty $party) {
+               $this->parties[] = $party;
+       }
+
+       /**
+        * Get all constraints
+        *
+        * @return array Package constraints
+        */
+       public function getConstraints() {
+               return $this->constraints;
+       }
+
+       /**
+        * Get the constraints by type
+        *
+        * @param string $constraintType Type of the constraints to get: CONSTRAINT_TYPE_*
+        * @return array Package constraints
+        */
+       public function getConstraintsByType($constraintType) {
+               if (!isset($this->constraints[$constraintType])) {
+                       return array();
+               }
+               return $this->constraints[$constraintType];
+       }
+
+       /**
+        * Add a constraint
+        *
+        * @param \TYPO3\Flow\Package\MetaData\AbstractConstraint $constraint The constraint to add
+        * @return void
+        */
+       public function addConstraint(\TYPO3\Flow\Package\MetaData\AbstractConstraint $constraint) {
+               $this->constraints[$constraint->getConstraintType()][] = $constraint;
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/AbstractConstraint.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/AbstractConstraint.php
new file mode 100644 (file)
index 0000000..68ce3c4
--- /dev/null
@@ -0,0 +1,66 @@
+<?php
+namespace TYPO3\Flow\Package\MetaData;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Constraint meta data model
+ *
+ */
+abstract class AbstractConstraint {
+
+       /**
+        * One of depends, conflicts or suggests
+        * @var string
+        */
+       protected $constraintType;
+
+       /**
+        * The constraint name or value
+        * @var string
+        */
+       protected $value;
+
+       /**
+        * Meta data constraint constructor
+        *
+        * @param string $constraintType
+        * @param string $value
+        * @param string $minVersion
+        * @param string $maxVersion
+        */
+       public function __construct($constraintType, $value, $minVersion = NULL, $maxVersion = NULL) {
+               $this->constraintType = $constraintType;
+               $this->value = $value;
+               $this->minVersion = $minVersion;
+               $this->maxVersion = $maxVersion;
+       }
+
+       /**
+        * @return string The constraint name or value
+        */
+       public function getValue() {
+               return $this->value;
+       }
+
+       /**
+        * @return string The constraint type (depends, conflicts, suggests)
+        */
+       public function getConstraintType() {
+               return $this->constraintType;
+       }
+
+       /**
+        * @return string The constraint scope (package, system)
+        */
+       abstract public function getConstraintScope();
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/AbstractParty.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/AbstractParty.php
new file mode 100644 (file)
index 0000000..3aff466
--- /dev/null
@@ -0,0 +1,98 @@
+<?php
+namespace TYPO3\Flow\Package\MetaData;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Party meta model for persons and companies
+ *
+ */
+abstract class AbstractParty {
+
+       /**
+        * The party role
+        *
+        * @var string
+        */
+       protected $role;
+
+       /**
+        * Name of the party
+        *
+        * @var string
+        */
+       protected $name;
+
+       /**
+        * Email of the party
+        *
+        * @var string
+        */
+       protected $email;
+
+       /**
+        * Website of the party
+        *
+        * @var string
+        */
+       protected $website;
+
+       /**
+        * Meta data party model constructor
+        *
+        * @param string $role
+        * @param string $name
+        * @param string $email
+        * @param string $website
+        */
+       public function __construct($role, $name, $email = NULL, $website = NULL) {
+               $this->role = $role;
+               $this->name = $name;
+               $this->email = $email;
+               $this->website = $website;
+       }
+
+       /**
+        * @return string The role of the party
+        */
+       public function getRole() {
+               return $this->role;
+       }
+
+       /**
+        * @return string The name of the party
+        */
+       public function getName() {
+               return $this->name;
+       }
+
+       /**
+        * @return string The email of the party
+        */
+       public function getEmail() {
+               return $this->email;
+       }
+
+       /**
+        * @return string The website of the party
+        */
+       public function getWebsite() {
+               return $this->website;
+       }
+
+       /**
+        * Get the party type (MetaData\PARTY_TYPE_PERSON, MetaData\PARTY_TYPE_COMPANY)
+        *
+        * @return string The type of the party (person, company)
+        */
+       abstract public function getPartyType();
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/Company.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/Company.php
new file mode 100644 (file)
index 0000000..85b7433
--- /dev/null
@@ -0,0 +1,30 @@
+<?php
+namespace TYPO3\Flow\Package\MetaData;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+
+/**
+ * Package company party meta model
+ *
+ */
+class Company extends \TYPO3\Flow\Package\MetaData\AbstractParty {
+
+       /**
+        * Get the party type
+        *
+        * @return string Party type "company"
+        */
+       public function getPartyType() {
+               return \TYPO3\Flow\Package\MetaDataInterface::PARTY_TYPE_COMPANY;
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/PackageConstraint.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/PackageConstraint.php
new file mode 100644 (file)
index 0000000..7dbc4d7
--- /dev/null
@@ -0,0 +1,29 @@
+<?php
+namespace TYPO3\Flow\Package\MetaData;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+
+/**
+ * Package constraint meta model
+ *
+ */
+class PackageConstraint extends \TYPO3\Flow\Package\MetaData\AbstractConstraint {
+
+       /**
+        * @return string The constraint scope
+        * @see \TYPO3\Flow\Package\MetaData\Constraint::getConstraintScope()
+        */
+       public function getConstraintScope() {
+               return \TYPO3\Flow\Package\MetaDataInterface::CONSTRAINT_SCOPE_PACKAGE;
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/Person.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/Person.php
new file mode 100644 (file)
index 0000000..6603b92
--- /dev/null
@@ -0,0 +1,73 @@
+<?php
+namespace TYPO3\Flow\Package\MetaData;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+
+/**
+ * Package person party meta model
+ *
+ */
+class Person extends \TYPO3\Flow\Package\MetaData\AbstractParty {
+
+       /**
+        * Company of the person
+        *
+        * @var string
+        */
+       protected $company;
+
+       /**
+        * Repository user name of the person
+        *
+        * @var string
+        */
+       protected $repositoryUserName;
+
+       /**
+        * Meta data person model constructor
+        *
+        * @param string $role
+        * @param string $name
+        * @param string $email
+        * @param string $website
+        * @param string $company
+        * @param string $repositoryUserName
+        */
+       public function __construct($role, $name, $email = NULL, $website = NULL, $company = NULL, $repositoryUserName = NULL) {
+               parent::__construct($role, $name, $email, $website);
+
+               $this->company = $company;
+               $this->repositoryUserName = $repositoryUserName;
+       }
+
+       /**
+        * @return string The company of the person
+        */
+       public function getCompany() {
+               return $this->company;
+       }
+
+       /**
+        * @return string The repository username
+        */
+       public function getRepositoryUserName() {
+               return $this->repositoryUserName;
+       }
+
+       /**
+        * @return string Party type "person"
+        */
+       public function getPartyType() {
+               return \TYPO3\Flow\Package\MetaDataInterface::PARTY_TYPE_PERSON;
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/SystemConstraint.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaData/SystemConstraint.php
new file mode 100644 (file)
index 0000000..1235ace
--- /dev/null
@@ -0,0 +1,60 @@
+<?php
+namespace TYPO3\Flow\Package\MetaData;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+
+/**
+ * System constraint meta model
+ *
+ */
+class SystemConstraint extends \TYPO3\Flow\Package\MetaData\AbstractConstraint {
+
+       /**
+        * The type for a system scope constraint (e.g. "Memory")
+        *
+        * @var string
+        */
+       protected $type;
+
+       /**
+        * Meta data system constraint constructor
+        *
+        * @param string $constraintType
+        * @param string $type
+        * @param string $value
+        * @param string $minVersion
+        * @param string $maxVersion
+        */
+       public function __construct($constraintType, $type, $value = NULL, $minVersion = NULL, $maxVersion = NULL) {
+               if (!strlen($value)) {
+                       $value = NULL;
+               }
+               parent::__construct($constraintType, $value, $minVersion, $maxVersion);
+               $this->type = $type;
+       }
+
+       /**
+        * @return string The system constraint type
+        */
+       public function getType() {
+               return $this->type;
+       }
+
+       /**
+        * @return string The constraint scope
+        * @see \TYPO3\Flow\Package\MetaData\Constraint\getConstraintScope()
+        */
+       public function getConstraintScope() {
+               return \TYPO3\Flow\Package\MetaDataInterface::CONSTRAINT_SCOPE_SYSTEM;
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaDataInterface.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/MetaDataInterface.php
new file mode 100644 (file)
index 0000000..c8f2513
--- /dev/null
@@ -0,0 +1,68 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Interface for TYPO3 Package MetaData information
+ *
+ */
+interface MetaDataInterface {
+
+       const CONSTRAINT_TYPE_DEPENDS = 'depends';
+       const CONSTRAINT_TYPE_CONFLICTS = 'conflicts';
+       const CONSTRAINT_TYPE_SUGGESTS = 'suggests';
+
+       const PARTY_TYPE_PERSON = 'person';
+       const PARTY_TYPE_COMPANY = 'company';
+
+       const CONSTRAINT_SCOPE_PACKAGE = 'package';
+       const CONSTRAINT_SCOPE_SYSTEM = 'system';
+
+       /**
+        * @return string The package key
+        */
+       public function getPackageKey();
+
+       /**
+        * @return string The package version
+        */
+       public function getVersion();
+
+       /**
+        * @return string The package description
+        */
+       public function getDescription();
+
+       /**
+        * @return Array of string The package categories
+        */
+       public function getCategories();
+
+       /**
+        * @return Array of TYPO3\Flow\Package\MetaData\Party The package parties
+        */
+       public function getParties();
+
+       /**
+        * @param string $constraintType Type of the constraints to get: CONSTRAINT_TYPE_*
+        * @return Array of TYPO3\Flow\Package\MetaData\Constraint Package constraints
+        */
+       public function getConstraintsByType($constraintType);
+
+       /**
+        * Get all constraints
+        *
+        * @return array An array of array of \TYPO3\Flow\Package\MetaData\Constraint Package constraints
+        */
+       public function getConstraints();
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Package.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/Package.php
new file mode 100644 (file)
index 0000000..1cd8946
--- /dev/null
@@ -0,0 +1,452 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\Flow\Utility\Files;
+
+/**
+ * A Package
+ *
+ * @api
+ */
+class Package implements PackageInterface {
+
+       /**
+        * Unique key of this package. Example for the Flow package: "TYPO3.Flow"
+        * @var string
+        */
+       protected $packageKey;
+
+       /**
+        * @var string
+        */
+       protected $manifestPath;
+
+       /**
+        * Full path to this package's main directory
+        * @var string
+        */
+       protected $packagePath;
+
+       /**
+        * Full path to this package's PSR-0 class loader entry point
+        * @var string
+        */
+       protected $classesPath;
+
+       /**
+        * If this package is protected and therefore cannot be deactivated or deleted
+        * @var boolean
+        * @api
+        */
+       protected $protected = FALSE;
+
+       /**
+        * @var \stdClass
+        */
+       protected $composerManifest;
+
+       /**
+        * Meta information about this package
+        * @var \TYPO3\Flow\Package\MetaData
+        */
+       protected $packageMetaData;
+
+       /**
+        * Names and relative paths (to this package directory) of files containing classes
+        * @var array
+        */
+       protected $classFiles;
+
+       /**
+        * The namespace of the classes contained in this package
+        * @var string
+        */
+       protected $namespace;
+
+       /**
+        * If enabled, the files in the Classes directory are registered and Reflection, Dependency Injection, AOP etc. are supported.
+        * Disable this flag if you don't need object management for your package and want to save some memory.
+        * @var boolean
+        * @api
+        */
+       protected $objectManagementEnabled = TRUE;
+
+       /**
+        * @var \TYPO3\Flow\Package\PackageManager
+        */
+       protected $packageManager;
+
+       /**
+        * Constructor
+        *
+        * @param \TYPO3\Flow\Package\PackageManager $packageManager the package manager which knows this package
+        * @param string $packageKey Key of this package
+        * @param string $packagePath Absolute path to the location of the package's composer manifest
+        * @param string $classesPath Path the classes of the package are in, relative to $packagePath. Optional, read from Composer manifest if not set.
+        * @param string $manifestPath Path the composer manifest of the package, relative to $packagePath. Optional, defaults to ''.
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackageKeyException if an invalid package key was passed
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackagePathException if an invalid package path was passed
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackageManifestException if no composer manifest file could be found
+        */
+       public function __construct(\TYPO3\Flow\Package\PackageManager $packageManager, $packageKey, $packagePath, $classesPath = NULL, $manifestPath = '') {
+               if (preg_match(self::PATTERN_MATCH_PACKAGEKEY, $packageKey) !== 1) {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackageKeyException('"' . $packageKey . '" is not a valid package key.', 1217959510);
+               }
+               if (!(is_dir($packagePath) || (Files::is_link($packagePath) && is_dir(Files::getNormalizedPath($packagePath))))) {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('Tried to instantiate a package object for package "%s" with a non-existing package path "%s". Either the package does not exist anymore, or the code creating this object contains an error.', $packageKey, $packagePath), 1166631889);
+               }
+               if (substr($packagePath, -1, 1) !== '/') {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('The package path "%s" provided for package "%s" has no trailing forward slash.', $packagePath, $packageKey), 1166633720);
+               }
+               if (substr($classesPath, 1, 1) === '/') {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackagePathException(sprintf('The package classes path provided for package "%s" has a leading forward slash.', $packageKey), 1334841320);
+               }
+               if (!file_exists($packagePath . $manifestPath . 'composer.json')) {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackageManifestException(sprintf('No composer manifest file found for package "%s". Please create one at "%scomposer.json".', $packageKey, $manifestPath), 1349776393);
+               }
+
+               $this->packageManager = $packageManager;
+               $this->manifestPath = $manifestPath;
+               $this->packageKey = $packageKey;
+               $this->packagePath = Files::getNormalizedPath($packagePath);
+               if (isset($this->getComposerManifest()->autoload->{'psr-0'})) {
+                       $this->classesPath = Files::getNormalizedPath($this->packagePath . $this->getComposerManifest()->autoload->{'psr-0'}->{$this->getNamespace()});
+               } else {
+                       $this->classesPath = Files::getNormalizedPath($this->packagePath . $classesPath);
+               }
+       }
+
+       /**
+        * Invokes custom PHP code directly after the package manager has been initialized.
+        *
+        * @param \TYPO3\Flow\Core\Bootstrap $bootstrap The current bootstrap
+        * @return void
+        */
+       public function boot(\TYPO3\Flow\Core\Bootstrap $bootstrap) {
+       }
+
+       /**
+        * Returns the package meta data object of this package.
+        *
+        * @return \TYPO3\Flow\Package\MetaData
+        */
+       public function getPackageMetaData() {
+               if ($this->packageMetaData === NULL) {
+                       $this->packageMetaData = new MetaData($this->getPackageKey());
+                       $this->packageMetaData->setDescription($this->getComposerManifest('description'));
+                       $this->packageMetaData->setVersion($this->getComposerManifest('version'));
+                       $requirements = $this->getComposerManifest('require');
+                       if ($requirements !== NULL) {
+                               foreach ($requirements as $requirement => $version) {
+                                       if ($this->packageRequirementIsComposerPackage($requirement) === FALSE) {
+                                                       // Skip non-package requirements
+                                               continue;
+                                       }
+                                       $packageKey = $this->packageManager->getPackageKeyFromComposerName($requirement);
+                                       $constraint = new MetaData\PackageConstraint(MetaDataInterface::CONSTRAINT_TYPE_DEPENDS, $packageKey);
+                                       $this->packageMetaData->addConstraint($constraint);
+                               }
+                       }
+
+               }
+               return $this->packageMetaData;
+       }
+
+       /**
+        * Check whether the given package requirement (like "typo3/flow" or "php") is a composer package or not
+        *
+        * @param string $requirement the composer requirement string
+        * @return boolean TRUE if $requirement is a composer package (contains a slash), FALSE otherwise
+        */
+       protected function packageRequirementIsComposerPackage($requirement) {
+               return (strpos($requirement, '/') !== FALSE);
+       }
+
+       /**
+        * Returns the array of filenames of the class files
+        *
+        * @return array An array of class names (key) and their filename, including the relative path to the package's directory
+        */
+       public function getClassFiles() {
+               if (!is_array($this->classFiles)) {
+                       $this->classFiles = $this->buildArrayOfClassFiles($this->classesPath);
+               }
+               return $this->classFiles;
+       }
+
+       /**
+        * Returns the array of filenames of class files provided by functional tests contained in this package
+        *
+        * @return array An array of class names (key) and their filename, including the relative path to the package's directory
+        */
+       public function getFunctionalTestsClassFiles() {
+               return $this->buildArrayOfClassFiles($this->packagePath . self::DIRECTORY_TESTS_FUNCTIONAL, $this->getNamespace() . '\\Tests\\Functional\\');
+       }
+
+       /**
+        * Returns the package key of this package.
+        *
+        * @return string
+        * @api
+        */
+       public function getPackageKey() {
+               return $this->packageKey;
+       }
+
+       /**
+        * Returns the PHP namespace of classes in this package.
+        *
+        * @return string
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackageStateException
+        * @api
+        */
+       public function getNamespace() {
+               if (!$this->namespace) {
+                       $manifest = $this->getComposerManifest();
+                       if (isset($manifest->autoload->{'psr-0'})) {
+                               $namespaces = $manifest->autoload->{'psr-0'};
+                               if (count($namespaces) === 1) {
+                                       $namespace = key($namespaces);
+                               } else {
+                                       throw new \TYPO3\Flow\Package\Exception\InvalidPackageStateException(sprintf('The Composer manifest of package "%s" contains multiple namespace definitions in its autoload section but Flow does only support one namespace per package.', $this->packageKey), 1348053245);
+                               }
+                       } else {
+                               $namespace = str_replace('.', '\\', $this->getPackageKey());
+                       }
+                       $this->namespace = $namespace;
+               }
+               return $this->namespace;
+       }
+
+       /**
+        * Tells if this package is protected and therefore cannot be deactivated or deleted
+        *
+        * @return boolean
+        * @api
+        */
+       public function isProtected() {
+               return $this->protected;
+       }
+
+       /**
+        * Tells if files in the Classes directory should be registered and object management enabled for this package.
+        *
+        * @return boolean
+        */
+       public function isObjectManagementEnabled() {
+               return $this->objectManagementEnabled;
+       }
+
+       /**
+        * Sets the protection flag of the package
+        *
+        * @param boolean $protected TRUE if the package should be protected, otherwise FALSE
+        * @return void
+        * @api
+        */
+       public function setProtected($protected) {
+               $this->protected = (boolean)$protected;
+       }
+
+       /**
+        * Returns the full path to this package's main directory
+        *
+        * @return string Path to this package's main directory
+        * @api
+        */
+       public function getPackagePath() {
+               return $this->packagePath;
+       }
+
+       /**
+        * Returns the full path to the packages Composer manifest
+        *
+        * @return string
+        */
+       public function getManifestPath() {
+               return $this->packagePath . $this->manifestPath;
+       }
+
+       /**
+        * Returns the full path to this package's Classes directory
+        *
+        * @return string Path to this package's Classes directory
+        * @api
+        */
+       public function getClassesPath() {
+               return $this->classesPath;
+       }
+
+       /**
+        * Returns the full path to the package's classes namespace entry path,
+        * e.g. "My.Package/ClassesPath/My/Package/"
+        *
+        * @return string Path to this package's Classes directory
+        * @api
+        */
+       public function getClassesNamespaceEntryPath() {
+               $pathifiedNamespace = str_replace('\\', '/', $this->getNamespace());
+               return Files::getNormalizedPath($this->classesPath . trim($pathifiedNamespace, '/'));
+       }
+
+       /**
+        * Returns the full path to this package's functional tests directory
+        *
+        * @return string Path to this package's functional tests directory
+        * @api
+        */
+       public function getFunctionalTestsPath() {
+               return $this->packagePath . self::DIRECTORY_TESTS_FUNCTIONAL;
+       }
+
+       /**
+        * Returns the full path to this package's Resources directory
+        *
+        * @return string Path to this package's Resources directory
+        * @api
+        */
+       public function getResourcesPath() {
+               return $this->packagePath . self::DIRECTORY_RESOURCES;
+       }
+
+       /**
+        * Returns the full path to this package's Configuration directory
+        *
+        * @return string Path to this package's Configuration directory
+        * @api
+        */
+       public function getConfigurationPath() {
+               return $this->packagePath . self::DIRECTORY_CONFIGURATION;
+       }
+
+       /**
+        * Returns the full path to the package's meta data directory
+        *
+        * @return string Full path to the package's meta data directory
+        * @api
+        */
+       public function getMetaPath() {
+               return $this->packagePath . self::DIRECTORY_METADATA;
+       }
+
+       /**
+        * Returns the full path to the package's documentation directory
+        *
+        * @return string Full path to the package's documentation directory
+        * @api
+        */
+       public function getDocumentationPath() {
+               return $this->packagePath . self::DIRECTORY_DOCUMENTATION;
+       }
+
+       /**
+        * Returns the available documentations for this package
+        *
+        * @return array Array of \TYPO3\Flow\Package\Documentation
+        * @api
+        */
+       public function getPackageDocumentations() {
+               $documentations = array();
+               $documentationPath = $this->getDocumentationPath();
+               if (is_dir($documentationPath)) {
+                       $documentationsDirectoryIterator = new \DirectoryIterator($documentationPath);
+                       $documentationsDirectoryIterator->rewind();
+                       while ($documentationsDirectoryIterator->valid()) {
+                               $filename = $documentationsDirectoryIterator->getFilename();
+                               if ($filename[0] != '.' && $documentationsDirectoryIterator->isDir()) {
+                                       $filename = $documentationsDirectoryIterator->getFilename();
+                                       $documentation = new \TYPO3\Flow\Package\Documentation($this, $filename, $documentationPath . $filename . '/');
+                                       $documentations[$filename] = $documentation;
+                               }
+                               $documentationsDirectoryIterator->next();
+                       }
+               }
+               return $documentations;
+       }
+
+       /**
+        * Returns contents of Composer manifest - or part there of.
+        *
+        * @param string $key Optional. Only return the part of the manifest indexed by 'key'
+        * @return mixed|NULL
+        * @see json_decode for return values
+        */
+       public function getComposerManifest($key = NULL) {
+               if (!isset($this->composerManifest)) {
+                       $this->composerManifest = PackageManager::getComposerManifest($this->getManifestPath());
+               }
+
+               return PackageManager::getComposerManifest($this->getManifestPath(), $key, $this->composerManifest);
+       }
+
+       /**
+        * Builds and returns an array of class names => file names of all
+        * *.php files in the package's Classes directory and its sub-
+        * directories.
+        *
+        * @param string $classesPath Base path acting as the parent directory for potential class files
+        * @param string $extraNamespaceSegment A PHP class namespace segment which should be inserted like so: \TYPO3\PackageKey\{namespacePrefix\}PathSegment\PathSegment\Filename
+        * @param string $subDirectory Used internally
+        * @param integer $recursionLevel Used internally
+        * @return array
+        * @throws \TYPO3\Flow\Package\Exception if recursion into directories was too deep or another error occurred
+        */
+       protected function buildArrayOfClassFiles($classesPath, $extraNamespaceSegment = '', $subDirectory = '', $recursionLevel = 0) {
+               $classFiles = array();
+               $currentPath = $classesPath . $subDirectory;
+               $currentRelativePath = substr($currentPath, strlen($this->packagePath));
+
+               if (!is_dir($currentPath)) {
+                       return array();
+               }
+               if ($recursionLevel > 100) {
+                       throw new \TYPO3\Flow\Package\Exception('Recursion too deep.', 1166635495);
+               }
+
+               try {
+                       $classesDirectoryIterator = new \DirectoryIterator($currentPath);
+                       while ($classesDirectoryIterator->valid()) {
+                               $filename = $classesDirectoryIterator->getFilename();
+                               if ($filename[0] != '.') {
+                                       if (is_dir($currentPath . $filename)) {
+                                               $classFiles = array_merge($classFiles, $this->buildArrayOfClassFiles($classesPath, $extraNamespaceSegment, $subDirectory . $filename . '/', ($recursionLevel + 1)));
+                                       } else {
+                                               if (substr($filename, -4, 4) === '.php') {
+                                                       $className = (str_replace('/', '\\', ($extraNamespaceSegment . substr($currentPath, strlen($classesPath)) . substr($filename, 0, -4))));
+                                                       $classFiles[$className] = $currentRelativePath . $filename;
+                                               }
+                                       }
+                               }
+                               $classesDirectoryIterator->next();
+                       }
+
+               } catch (\Exception $exception) {
+                       throw new \TYPO3\Flow\Package\Exception($exception->getMessage(), 1166633720);
+               }
+               return $classFiles;
+       }
+
+       public function __sleep() {
+               $properties = get_class_vars(__CLASS__);
+               unset($properties['packageManager']);
+               return array_keys($properties);
+       }
+
+       public function __wakeup() {
+               if (isset($GLOBALS['TYPO3_currentPackageManager'])) {
+                       $this->packageManager = $GLOBALS['TYPO3_currentPackageManager'];
+               }
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageFactory.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageFactory.php
new file mode 100644 (file)
index 0000000..ae799be
--- /dev/null
@@ -0,0 +1,109 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\Flow\Utility\Files;
+
+/**
+ * Class for building Packages
+ */
+class PackageFactory {
+
+       /**
+        * @var PackageManagerInterface
+        */
+       protected $packageManager;
+
+       /**
+        * Constructor
+        *
+        * @param \TYPO3\Flow\Package\PackageManagerInterface $packageManager
+        */
+       public function __construct(PackageManagerInterface $packageManager) {
+               $this->packageManager = $packageManager;
+       }
+
+       /**
+        * Returns a package instance.
+        *
+        * @param string $packagesBasePath the base install path of packages,
+        * @param string $packagePath path to package, relative to base path
+        * @param string $packageKey key / name of the package
+        * @param string $classesPath path to the classes directory, relative to the package path
+        * @param string $manifestPath path to the package's Composer manifest, relative to package path, defaults to same path
+        * @return \TYPO3\Flow\Package\PackageInterface
+        * @throws Exception\CorruptPackageException
+        */
+       public function create($packagesBasePath, $packagePath, $packageKey, $classesPath, $manifestPath = '') {
+               $packageClassPathAndFilename = Files::concatenatePaths(array($packagesBasePath, $packagePath, 'Classes/' . str_replace('.', '/', $packageKey) . '/Package.php'));
+               if (file_exists($packageClassPathAndFilename)) {
+                       require_once($packageClassPathAndFilename);
+                       /**
+                        * @todo there should be a general method for getting Namespace from $packageKey
+                        * @todo it should be tested if the package class implements the interface
+                        */
+                       $packageClassName = str_replace('.', '\\', $packageKey) . '\Package';
+                       if (!class_exists($packageClassName)) {
+                               throw new \TYPO3\Flow\Package\Exception\CorruptPackageException(sprintf('The package "%s" does not contain a valid package class. Check if the file "%s" really contains a class called "%s".', $packageKey, $packageClassPathAndFilename, $packageClassName), 1327587091);
+                       }
+               } else {
+                       $packageClassName = 'TYPO3\Flow\Package\Package';
+               }
+               $packagePath = Files::concatenatePaths(array($packagesBasePath, $packagePath)) . '/';
+
+               $package = new $packageClassName($this->packageManager, $packageKey, $packagePath, $classesPath, $manifestPath);
+
+               return $package;
+       }
+
+       /**
+        * Resolves package key from Composer manifest
+        *
+        * If it is a Flow package the name of the containing directory will be used.
+        *
+        * Else if the composer name of the package matches the first part of the lowercased namespace of the package, the mixed
+        * case version of the composer name / namespace will be used, with backslashes replaced by dots.
+        *
+        * Else the composer name will be used with the slash replaced by a dot
+        *
+        * @param object $manifest
+        * @param string $packagesBasePath
+        * @return string
+        */
+       public static function getPackageKeyFromManifest($manifest, $packagePath, $packagesBasePath) {
+               if (!is_object($manifest)) {
+                       throw new  \TYPO3\Flow\Package\Exception\InvalidPackageManifestException('Invalid composer manifest.', 1348146450);
+               }
+               if (isset($manifest->type) && substr($manifest->type, 0, 11) === 'typo3-flow-') {
+                       $relativePackagePath = substr($packagePath, strlen($packagesBasePath));
+                       $packageKey = substr($relativePackagePath, strpos($relativePackagePath, '/') + 1, -1);
+                       /**
+                        * @todo check that manifest name and directory follows convention
+                        */
+               } else {
+                       $packageKey = str_replace('/', '.', $manifest->name);
+                       if (isset($manifest->autoload) && isset($manifest->autoload->{"psr-0"})) {
+                               $namespaces = array_keys(get_object_vars($manifest->autoload->{"psr-0"}));
+                               foreach ($namespaces as $namespace) {
+                                       $namespaceLead = substr($namespace, 0, strlen($manifest->name));
+                                       $dottedNamespaceLead = str_replace('\\', '.', $namespaceLead);
+                                       if (strtolower($dottedNamespaceLead) === $packageKey) {
+                                               $packageKey = $dottedNamespaceLead;
+                                       }
+                               }
+                       }
+               }
+               $packageKey = preg_replace('/[^A-Za-z0-9.]/', '', $packageKey);
+               return $packageKey;
+       }
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageInterface.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageInterface.php
new file mode 100644 (file)
index 0000000..ad41b07
--- /dev/null
@@ -0,0 +1,160 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Interface for a Flow Package class
+ *
+ * @api
+ */
+interface PackageInterface {
+
+       const PATTERN_MATCH_PACKAGEKEY = '/^[a-z0-9]+\.(?:[a-z0-9][\.a-z0-9]*)+$/i';
+
+       const DIRECTORY_CLASSES = 'Classes/';
+       const DIRECTORY_CONFIGURATION = 'Configuration/';
+       const DIRECTORY_DOCUMENTATION = 'Documentation/';
+       const DIRECTORY_METADATA = 'Meta/';
+       const DIRECTORY_TESTS_FUNCTIONAL = 'Tests/Functional/';
+       const DIRECTORY_TESTS_UNIT = 'Tests/Unit/';
+       const DIRECTORY_RESOURCES = 'Resources/';
+
+       /**
+        * Invokes custom PHP code directly after the package manager has been initialized.
+        *
+        * @param \TYPO3\Flow\Core\Bootstrap $bootstrap The current bootstrap
+        * @return void
+        */
+       public function boot(\TYPO3\Flow\Core\Bootstrap $bootstrap);
+
+       /**
+        * Returns the package meta object of this package.
+        *
+        * @return \TYPO3\Flow\Package\MetaData
+        */
+       public function getPackageMetaData();
+
+       /**
+        * Returns the array of filenames of the class files
+        *
+        * @return array An array of class names (key) and their filename, including the relative path to the package's directory
+        * @api
+        */
+       public function getClassFiles();
+
+       /**
+        * Returns the package key of this package.
+        *
+        * @return string
+        * @api
+        */
+       public function getPackageKey();
+
+       /**
+        * Returns the PHP namespace of classes in this package.
+        *
+        * @return string
+        * @api
+        */
+       public function getNamespace();
+
+       /**
+        * Tells if this package is protected and therefore cannot be deactivated or deleted
+        *
+        * @return boolean
+        * @api
+        */
+       public function isProtected();
+
+       /**
+        * Tells if files in the Classes directory should be registered and object management enabled for this package.
+        *
+        * @return boolean
+        */
+       public function isObjectManagementEnabled();
+
+       /**
+        * Sets the protection flag of the package
+        *
+        * @param boolean $protected TRUE if the package should be protected, otherwise FALSE
+        * @return void
+        * @api
+        */
+       public function setProtected($protected);
+
+       /**
+        * Returns the full path to this package's main directory
+        *
+        * @return string Path to this package's main directory
+        * @api
+        */
+       public function getPackagePath();
+
+       /**
+        * Returns the full path to this package's Classes directory
+        *
+        * @return string Path to this package's Classes directory
+        * @api
+        */
+       public function getClassesPath();
+
+       /**
+        * Returns the full path to the package's classes namespace entry path,
+        * e.g. "My.Package/ClassesPath/My/Package/"
+        *
+        * @return string Path to this package's Classes directory
+        * @api
+        */
+       public function getClassesNamespaceEntryPath();
+
+       /**
+        * Returns the full path to this package's Resources directory
+        *
+        * @return string Path to this package's Resources directory
+        * @api
+        */
+       public function getResourcesPath();
+
+       /**
+        * Returns the full path to this package's Configuration directory
+        *
+        * @return string Path to this package's Configuration directory
+        * @api
+        */
+       public function getConfigurationPath();
+
+       /**
+        * Returns the full path to this package's Package.xml file
+        *
+        * @return string Path to this package's Package.xml file
+        * @api
+        */
+       public function getMetaPath();
+
+       /**
+        * Returns the full path to the package's documentation directory
+        *
+        * @return string Full path to the package's documentation directory
+        * @api
+        */
+       public function getDocumentationPath();
+
+       /**
+        * Returns the available documentations for this package
+        *
+        * @return array Array of \TYPO3\Flow\Package\Documentation
+        * @api
+        */
+       public function getPackageDocumentations();
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageManager.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageManager.php
new file mode 100644 (file)
index 0000000..631122f
--- /dev/null
@@ -0,0 +1,1020 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+use TYPO3\Flow\Package\Package;
+use TYPO3\Flow\Package\PackageFactory;
+use TYPO3\Flow\Package\PackageInterface;
+use TYPO3\Flow\Utility\Files;
+use TYPO3\Flow\Annotations as Flow;
+
+/**
+ * The default TYPO3 Package Manager
+ *
+ * @api
+ * @Flow\Scope("singleton")
+ */
+class PackageManager implements \TYPO3\Flow\Package\PackageManagerInterface {
+
+       /**
+        * @var \TYPO3\Flow\Core\ClassLoader
+        */
+       protected $classLoader;
+
+       /**
+        * @var \TYPO3\Flow\Core\Bootstrap
+        */
+       protected $bootstrap;
+
+       /**
+        * @var PackageFactory
+        */
+       protected $packageFactory;
+
+       /**
+        * Array of available packages, indexed by package key
+        * @var array
+        */
+       protected $packages = array();
+
+       /**
+        * A translation table between lower cased and upper camel cased package keys
+        * @var array
+        */
+       protected $packageKeys = array();
+
+       /**
+        * A map between ComposerName and PackageKey, only available when scanAvailablePackages is run
+        * @var array
+        */
+       protected $composerNameToPackageKeyMap = array();
+
+       /**
+        * List of active packages as package key => package object
+        * @var array
+        */
+       protected $activePackages = array();
+
+       /**
+        * Absolute path leading to the various package directories
+        * @var string
+        */
+       protected $packagesBasePath;
+
+       /**
+        * @var string
+        */
+       protected $packageStatesPathAndFilename;
+
+       /**
+        * Package states configuration as stored in the PackageStates.php file
+        * @var array
+        */
+       protected $packageStatesConfiguration = array();
+
+       /**
+        * @var array
+        */
+       protected $settings;
+
+       /**
+        * @var \TYPO3\Flow\Log\SystemLoggerInterface
+        */
+       protected $systemLogger;
+
+       /**
+        * @param \TYPO3\Flow\Core\ClassLoader $classLoader
+        * @return void
+        */
+       public function injectClassLoader(\TYPO3\Flow\Core\ClassLoader $classLoader) {
+               $this->classLoader = $classLoader;
+       }
+
+       /**
+        * @param array $settings
+        * @return void
+        */
+       public function injectSettings(array $settings) {
+               $this->settings = $settings;
+       }
+
+       /**
+        * @param \TYPO3\Flow\Log\SystemLoggerInterface $systemLogger
+        * @return void
+        */
+       public function injectSystemLogger(\TYPO3\Flow\Log\SystemLoggerInterface $systemLogger) {
+               if ($this->systemLogger instanceof \TYPO3\Flow\Log\EarlyLogger) {
+                       $this->systemLogger->replayLogsOn($systemLogger);
+                       unset($this->systemLogger);
+               }
+               $this->systemLogger = $systemLogger;
+       }
+
+       /**
+        * Initializes the package manager
+        *
+        * @param \TYPO3\Flow\Core\Bootstrap $bootstrap The current bootstrap
+        * @param string $packagesBasePath Absolute path of the Packages directory
+        * @param string $packageStatesPathAndFilename
+        * @return void
+        */
+       public function initialize(\TYPO3\Flow\Core\Bootstrap $bootstrap, $packagesBasePath = FLOW_PATH_PACKAGES, $packageStatesPathAndFilename = '') {
+               $this->systemLogger = new \TYPO3\Flow\Log\EarlyLogger();
+
+               $this->bootstrap = $bootstrap;
+               $this->packagesBasePath = $packagesBasePath;
+               $this->packageStatesPathAndFilename = ($packageStatesPathAndFilename === '') ? FLOW_PATH_CONFIGURATION . 'PackageStates.php' : $packageStatesPathAndFilename;
+               $this->packageFactory = new PackageFactory($this);
+
+               $this->loadPackageStates();
+
+               foreach ($this->packages as $packageKey => $package) {
+                       if ($package->isProtected() || (isset($this->packageStatesConfiguration['packages'][$packageKey]['state']) && $this->packageStatesConfiguration['packages'][$packageKey]['state'] === 'active')) {
+                               $this->activePackages[$packageKey] = $package;
+                       }
+               }
+
+               $this->classLoader->setPackages($this->activePackages);
+
+               foreach ($this->activePackages as $package) {
+                       $package->boot($bootstrap);
+               }
+
+       }
+
+       /**
+        * Returns TRUE if a package is available (the package's files exist in the packages directory)
+        * or FALSE if it's not. If a package is available it doesn't mean necessarily that it's active!
+        *
+        * @param string $packageKey The key of the package to check
+        * @return boolean TRUE if the package is available, otherwise FALSE
+        * @api
+        */
+       public function isPackageAvailable($packageKey) {
+               return (isset($this->packages[$packageKey]));
+       }
+
+       /**
+        * Returns TRUE if a package is activated or FALSE if it's not.
+        *
+        * @param string $packageKey The key of the package to check
+        * @return boolean TRUE if package is active, otherwise FALSE
+        * @api
+        */
+       public function isPackageActive($packageKey) {
+               return (isset($this->activePackages[$packageKey]));
+       }
+
+       /**
+        * Returns the base path for packages
+        *
+        * @return string
+        */
+       public function getPackagesBasePath() {
+               return $this->packagesBasePath;
+       }
+
+       /**
+        * Returns a PackageInterface object for the specified package.
+        * A package is available, if the package directory contains valid MetaData information.
+        *
+        * @param string $packageKey
+        * @return \TYPO3\Flow\Package\PackageInterface The requested package object
+        * @throws \TYPO3\Flow\Package\Exception\UnknownPackageException if the specified package is not known
+        * @api
+        */
+       public function getPackage($packageKey) {
+               if (!$this->isPackageAvailable($packageKey)) {
+                       throw new \TYPO3\Flow\Package\Exception\UnknownPackageException('Package "' . $packageKey . '" is not available. Please check if the package exists and that the package key is correct (package keys are case sensitive).', 1166546734);
+               }
+               return $this->packages[$packageKey];
+       }
+
+       /**
+        * Finds a package by a given object of that package; if no such package
+        * could be found, NULL is returned. This basically works with comparing the package class' location
+        * against the given class' location. In order to not being satisfied with a shorter package's root path,
+        * the packages to check are sorted by the length of their root path descending.
+        *
+        * Please note that the class itself must be existing anyways, else PHP's generic "class not found"
+        * exception will be thrown.
+        *
+        * @param object $object The object to find the possessing package of
+        * @return \TYPO3\Flow\Package\PackageInterface The package the given object belongs to or NULL if it could not be found
+        */
+       public function getPackageOfObject($object) {
+               $sortedAvailablePackages = $this->getAvailablePackages();
+               usort($sortedAvailablePackages, function (PackageInterface $packageOne, PackageInterface $packageTwo) {
+                       return strlen($packageTwo->getPackagePath()) - strlen($packageOne->getPackagePath());
+               });
+
+               $className = $this->bootstrap->getObjectManager()->get('TYPO3\Flow\Reflection\ReflectionService')->getClassNameByObject($object);
+               $reflectedClass = new \ReflectionClass($className);
+               $fileName = Files::getUnixStylePath($reflectedClass->getFileName());
+
+               foreach ($sortedAvailablePackages as $package) {
+                       $packagePath = Files::getUnixStylePath($package->getPackagePath());
+                       if (strpos($fileName, $packagePath) === 0) {
+                               return $package;
+                       }
+               }
+               return NULL;
+       }
+
+       /**
+        * Returns an array of \TYPO3\Flow\Package objects of all available packages.
+        * A package is available, if the package directory contains valid meta information.
+        *
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        * @api
+        */
+       public function getAvailablePackages() {
+               return $this->packages;
+       }
+
+       /**
+        * Returns an array of \TYPO3\Flow\Package objects of all active packages.
+        * A package is active, if it is available and has been activated in the package
+        * manager settings.
+        *
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        * @api
+        */
+       public function getActivePackages() {
+               return $this->activePackages;
+       }
+
+       /**
+        * Returns an array of \TYPO3\Flow\Package objects of all frozen packages.
+        * A frozen package is not considered by file monitoring and provides some
+        * precompiled reflection data in order to improve performance.
+        *
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        */
+       public function getFrozenPackages() {
+               $frozenPackages = array();
+               if ($this->bootstrap->getContext()->isDevelopment()) {
+                       foreach ($this->packages as $packageKey => $package) {
+                               if (isset($this->packageStatesConfiguration['packages'][$packageKey]['frozen']) &&
+                                               $this->packageStatesConfiguration['packages'][$packageKey]['frozen'] === TRUE) {
+                                       $frozenPackages[$packageKey] = $package;
+                               }
+                       }
+               }
+               return $frozenPackages;
+       }
+
+       /**
+        * Returns an array of \TYPO3\Flow\PackageInterface objects of all packages that match
+        * the given package state, path, and type filters. All three filters must match, if given.
+        *
+        * @param string $packageState defaults to available
+        * @param string $packagePath
+        * @param string $packageType
+        *
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        * @throws Exception\InvalidPackageStateException
+        * @api
+        */
+       public function getFilteredPackages($packageState = 'available', $packagePath = NULL, $packageType = NULL) {
+               $packages = array();
+               switch (strtolower($packageState)) {
+                       case 'available':
+                               $packages = $this->getAvailablePackages();
+                       break;
+                       case 'active':
+                               $packages = $this->getActivePackages();
+                       break;
+                       case 'frozen':
+                               $packages = $this->getFrozenPackages();
+                       break;
+                       default:
+                               throw new \TYPO3\Flow\Package\Exception\InvalidPackageStateException('The package state "' . $packageState . '" is invalid', 1372458274);
+               }
+
+               if($packagePath !== NULL) {
+                       $packages = $this->filterPackagesByPath($packages, $packagePath);
+               }
+               if($packageType !== NULL) {
+                       $packages = $this->filterPackagesByType($packages, $packageType);
+               }
+
+               return $packages;
+       }
+
+       /**
+        * Returns an array of \TYPO3\Flow\Package objects in the given array of packages
+        * that are in the specified Package Path
+        *
+        * @param array $packages Array of \TYPO3\Flow\Package\PackageInterface to be filtered
+        * @param string $filterPath Filter out anything that's not in this path
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        */
+       protected function filterPackagesByPath(&$packages, $filterPath) {
+               $filteredPackages = array();
+               /** @var $package Package */
+               foreach ($packages as $package) {
+                       $packagePath = substr($package->getPackagePath(), strlen(FLOW_PATH_PACKAGES));
+                       $packageGroup = substr($packagePath, 0, strpos($packagePath, '/'));
+                       if ($packageGroup === $filterPath) {
+                               $filteredPackages[$package->getPackageKey()] = $package;
+                       }
+               }
+               return $filteredPackages;
+       }
+
+       /**
+        * Returns an array of \TYPO3\Flow\Package objects in the given array of packages
+        * that are of the specified package type.
+        *
+        * @param array $packages Array of \TYPO3\Flow\Package\PackageInterface to be filtered
+        * @param string $packageType Filter out anything that's not of this packageType
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        */
+       protected function filterPackagesByType(&$packages, $packageType) {
+               $filteredPackages = array();
+               /** @var $package Package */
+               foreach ($packages as $package) {
+                       if ($package->getComposerManifest('type') === $packageType) {
+                               $filteredPackages[$package->getPackageKey()] = $package;
+                       }
+               }
+               return $filteredPackages;
+       }
+
+       /**
+        * Returns the upper camel cased version of the given package key or FALSE
+        * if no such package is available.
+        *
+        * @param string $unknownCasedPackageKey The package key to convert
+        * @return mixed The upper camel cased package key or FALSE if no such package exists
+        * @api
+        */
+       public function getCaseSensitivePackageKey($unknownCasedPackageKey) {
+               $lowerCasedPackageKey = strtolower($unknownCasedPackageKey);
+               return (isset($this->packageKeys[$lowerCasedPackageKey])) ? $this->packageKeys[$lowerCasedPackageKey] : FALSE;
+       }
+
+       /**
+        * Resolves a Flow package key from a composer package name.
+        *
+        * @param string $composerName
+        * @return string
+        * @throws Exception\InvalidPackageStateException
+        */
+       public function getPackageKeyFromComposerName($composerName) {
+               if (count($this->composerNameToPackageKeyMap) === 0) {
+                       foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $packageStateConfiguration) {
+                               $this->composerNameToPackageKeyMap[strtolower($packageStateConfiguration['composerName'])] = $packageKey;
+                       }
+               }
+               $lowercasedComposerName = strtolower($composerName);
+               if (!isset($this->composerNameToPackageKeyMap[$lowercasedComposerName])) {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackageStateException('Could not find package with composer name "' . $composerName . '" in PackageStates configuration.', 1352320649);
+               }
+               return $this->composerNameToPackageKeyMap[$lowercasedComposerName];
+       }
+
+       /**
+        * Check the conformance of the given package key
+        *
+        * @param string $packageKey The package key to validate
+        * @return boolean If the package key is valid, returns TRUE otherwise FALSE
+        * @api
+        */
+       public function isPackageKeyValid($packageKey) {
+               return preg_match(PackageInterface::PATTERN_MATCH_PACKAGEKEY, $packageKey) === 1;
+       }
+
+       /**
+        * Create a package, given the package key
+        *
+        * @param string $packageKey The package key of the new package
+        * @param \TYPO3\Flow\Package\MetaData $packageMetaData If specified, this package meta object is used for writing the Package.xml file, otherwise a rudimentary Package.xml file is created
+        * @param string $packagesPath If specified, the package will be created in this path, otherwise the default "Application" directory is used
+        * @param string $packageType If specified, the package type will be set, otherwise it will default to "typo3-flow-package"
+        * @return \TYPO3\Flow\Package\PackageInterface The newly created package
+        * @throws \TYPO3\Flow\Package\Exception
+        * @throws \TYPO3\Flow\Package\Exception\PackageKeyAlreadyExistsException
+        * @throws \TYPO3\Flow\Package\Exception\InvalidPackageKeyException
+        * @api
+        */
+       public function createPackage($packageKey, \TYPO3\Flow\Package\MetaData $packageMetaData = NULL, $packagesPath = NULL, $packageType = 'typo3-flow-package') {
+               if (!$this->isPackageKeyValid($packageKey)) {
+                       throw new \TYPO3\Flow\Package\Exception\InvalidPackageKeyException('The package key "' . $packageKey . '" is invalid', 1220722210);
+               }
+               if ($this->isPackageAvailable($packageKey)) {
+                       throw new \TYPO3\Flow\Package\Exception\PackageKeyAlreadyExistsException('The package key "' . $packageKey . '" already exists', 1220722873);
+               }
+
+               if ($packagesPath === NULL) {
+                       if (is_array($this->settings['package']['packagesPathByType']) && isset($this->settings['package']['packagesPathByType'][$packageType])) {
+                               $packagesPath = $this->settings['package']['packagesPathByType'][$packageType];
+                       } else {
+                               $packagesPath = 'Application';
+                       }
+                       $packagesPath = Files::getUnixStylePath(Files::concatenatePaths(array($this->packagesBasePath, $packagesPath)));
+               }
+
+               if ($packageMetaData === NULL) {
+                       $packageMetaData = new MetaData($packageKey);
+               }
+               if ($packageMetaData->getPackageType() === NULL) {
+                       $packageMetaData->setPackageType($packageType);
+               }
+
+               $packagePath = Files::concatenatePaths(array($packagesPath, $packageKey)) . '/';
+               Files::createDirectoryRecursively($packagePath);
+
+               foreach (
+                       array(
+                               PackageInterface::DIRECTORY_METADATA,
+                               PackageInterface::DIRECTORY_CLASSES,
+                               PackageInterface::DIRECTORY_CONFIGURATION,
+                               PackageInterface::DIRECTORY_DOCUMENTATION,
+                               PackageInterface::DIRECTORY_RESOURCES,
+                               PackageInterface::DIRECTORY_TESTS_UNIT,
+                               PackageInterface::DIRECTORY_TESTS_FUNCTIONAL,
+                       ) as $path) {
+                       Files::createDirectoryRecursively(Files::concatenatePaths(array($packagePath, $path)));
+               }
+
+               $this->writeComposerManifest($packagePath, $packageKey, $packageMetaData);
+
+               $packagePath = str_replace($this->packagesBasePath, '', $packagePath);
+               $package = $this->packageFactory->create($this->packagesBasePath, $packagePath, $packageKey, PackageInterface::DIRECTORY_CLASSES);
+
+               $this->packages[$packageKey] = $package;
+               foreach (array_keys($this->packages) as $upperCamelCasedPackageKey) {
+                       $this->packageKeys[strtolower($upperCamelCasedPackageKey)] = $upperCamelCasedPackageKey;
+               }
+
+               $this->activatePackage($packageKey);
+
+               return $package;
+       }
+
+       /**
+        * Write a composer manifest for the package.
+        *
+        * @param string $manifestPath
+        * @param string $packageKey
+        * @param MetaData $packageMetaData
+        * @return void
+        */
+       protected function writeComposerManifest($manifestPath, $packageKey, \TYPO3\Flow\Package\MetaData $packageMetaData = NULL) {
+               $manifest = array();
+
+               $nameParts = explode('.', $packageKey);
+               $vendor = array_shift($nameParts);
+               $manifest['name'] = strtolower($vendor . '/' . implode('-', $nameParts));
+               if ($packageMetaData !== NULL) {
+                       $manifest['type'] = $packageMetaData->getPackageType();
+                       $manifest['description'] = $packageMetaData->getDescription();
+                       $manifest['version'] = $packageMetaData->getVersion();
+               } else {
+                       $manifest['type'] = 'typo3-flow-package';
+                       $manifest['description'] = '';
+               }
+               $manifest['require'] = array('typo3/flow' => '*');
+               $manifest['autoload'] = array('psr-0' => array(str_replace('.', '\\', $packageKey) => 'Classes'));
+
+               if (defined('JSON_PRETTY_PRINT')) {
+                       file_put_contents(Files::concatenatePaths(array($manifestPath, 'composer.json')), json_encode($manifest, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
+               } else {
+                       file_put_contents(Files::concatenatePaths(array($manifestPath, 'composer.json')), json_encode($manifest));
+               }
+       }
+
+       /**
+        * Deactivates a package
+        *
+        * @param string $packageKey The package to deactivate
+        * @return void
+        * @throws \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException if a package is protected and cannot be deactivated
+        * @api
+        */
+       public function deactivatePackage($packageKey) {
+               if (!$this->isPackageActive($packageKey)) {
+                       return;
+               }
+
+               $package = $this->getPackage($packageKey);
+               if ($package->isProtected()) {
+                       throw new \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException('The package "' . $packageKey . '" is protected and cannot be deactivated.', 1308662891);
+               }
+
+               unset($this->activePackages[$packageKey]);
+               $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'inactive';
+               $this->sortAndSavePackageStates();
+       }
+
+       /**
+        * Activates a package
+        *
+        * @param string $packageKey The package to activate
+        * @return void
+        * @api
+        */
+       public function activatePackage($packageKey) {
+               if ($this->isPackageActive($packageKey)) {
+                       return;
+               }
+
+               $package = $this->getPackage($packageKey);
+               $this->activePackages[$packageKey] = $package;
+               $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'active';
+               if (!isset($this->packageStatesConfiguration['packages'][$packageKey]['packagePath'])) {
+                       $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $package->getPackagePath());
+               }
+               if (!isset($this->packageStatesConfiguration['packages'][$packageKey]['classesPath'])) {
+                       $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = Package::DIRECTORY_CLASSES;
+               }
+               $this->sortAndSavePackageStates();
+       }
+
+       /**
+        * Freezes a package
+        *
+        * @param string $packageKey The package to freeze
+        * @return void
+        * @throws \LogicException
+        * @throws \TYPO3\Flow\Package\Exception\UnknownPackageException
+        */
+       public function freezePackage($packageKey) {
+               if (!$this->bootstrap->getContext()->isDevelopment()) {
+                       throw new \LogicException('Package freezing is only supported in Development context.', 1338810870);
+               }
+
+               if (!$this->isPackageActive($packageKey)) {
+                       throw new \TYPO3\Flow\Package\Exception\UnknownPackageException('Package "' . $packageKey . '" is not available or active.', 1331715956);
+               }
+               if ($this->isPackageFrozen($packageKey)) {
+                       return;
+               }
+
+               $this->bootstrap->getObjectManager()->get('TYPO3\Flow\Reflection\ReflectionService')->freezePackageReflection($packageKey);
+
+               $this->packageStatesConfiguration['packages'][$packageKey]['frozen'] = TRUE;
+               $this->sortAndSavePackageStates();
+       }
+
+       /**
+        * Tells if a package is frozen
+        *
+        * @param string $packageKey The package to check
+        * @return boolean
+        */
+       public function isPackageFrozen($packageKey) {
+               return (
+                       $this->bootstrap->getContext()->isDevelopment()
+                       && isset($this->packageStatesConfiguration['packages'][$packageKey]['frozen'])
+                       && $this->packageStatesConfiguration['packages'][$packageKey]['frozen'] === TRUE
+               );
+       }
+
+       /**
+        * Unfreezes a package
+        *
+        * @param string $packageKey The package to unfreeze
+        * @return void
+        */
+       public function unfreezePackage($packageKey) {
+               if (!$this->isPackageFrozen($packageKey)) {
+                       return;
+               }
+
+               $this->bootstrap->getObjectManager()->get('TYPO3\Flow\Reflection\ReflectionService')->unfreezePackageReflection($packageKey);
+
+               unset($this->packageStatesConfiguration['packages'][$packageKey]['frozen']);
+               $this->sortAndSavePackageStates();
+       }
+
+       /**
+        * Refreezes a package
+        *
+        * @param string $packageKey The package to refreeze
+        * @return void
+        */
+       public function refreezePackage($packageKey) {
+               if (!$this->isPackageFrozen($packageKey)) {
+                       return;
+               }
+
+               $this->bootstrap->getObjectManager()->get('TYPO3\Flow\Reflection\ReflectionService')->unfreezePackageReflection($packageKey);
+       }
+
+       /**
+        * Register a native Flow package
+        *
+        * @param string $packageKey The Package to be registered
+        * @param boolean $sortAndSave allows for not saving packagestates when used in loops etc.
+        * @return PackageInterface
+        * @throws Exception\CorruptPackageException
+        */
+       public function registerPackage(PackageInterface $package, $sortAndSave = TRUE) {
+               $packageKey = $package->getPackageKey();
+               if ($this->isPackageAvailable($packageKey)) {
+                       throw new Exception\InvalidPackageStateException('Package "' . $packageKey . '" is already registered.', 1338996122);
+               }
+
+               $this->packages[$packageKey] = $package;
+               $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $package->getPackagePath());
+               $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = str_replace($package->getPackagePath(), '', $package->getClassesPath());
+
+               if ($sortAndSave === TRUE) {
+                       $this->sortAndSavePackageStates();
+               }
+
+               return $package;
+       }
+
+       /**
+        * Unregisters a package from the list of available packages
+        *
+        * @param PackageInterface $package The package to be unregistered
+        * @return void
+        * @throws Exception\InvalidPackageStateException
+        */
+       public function unregisterPackage(PackageInterface $package) {
+               $packageKey = $package->getPackageKey();
+               if (!$this->isPackageAvailable($packageKey)) {
+                       throw new Exception\InvalidPackageStateException('Package "' . $packageKey . '" is not registered.', 1338996142);
+               }
+               $this->unregisterPackageByPackageKey($packageKey);
+       }
+
+       /**
+        * Unregisters a package from the list of available packages
+        *
+        * @param string $packageKey Package Key of the package to be unregistered
+        * @return void
+        */
+       protected function unregisterPackageByPackageKey($packageKey) {
+               unset($this->packages[$packageKey]);
+               unset($this->packageKeys[strtolower($packageKey)]);
+               unset($this->packageStatesConfiguration['packages'][$packageKey]);
+               $this->sortAndSavePackageStates();
+       }
+
+       /**
+        * Removes a package from registry and deletes it from filesystem
+        *
+        * @param string $packageKey package to remove
+        * @return void
+        * @throws \TYPO3\Flow\Package\Exception\UnknownPackageException if the specified package is not known
+        * @throws \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException if a package is protected and cannot be deleted
+        * @throws \TYPO3\Flow\Package\Exception
+        * @api
+        */
+       public function deletePackage($packageKey) {
+               if (!$this->isPackageAvailable($packageKey)) {
+                       throw new \TYPO3\Flow\Package\Exception\UnknownPackageException('Package "' . $packageKey . '" is not available and cannot be removed.', 1166543253);
+               }
+
+               $package = $this->getPackage($packageKey);
+               if ($package->isProtected()) {
+                       throw new \TYPO3\Flow\Package\Exception\ProtectedPackageKeyException('The package "' . $packageKey . '" is protected and cannot be removed.', 1220722120);
+               }
+
+               if ($this->isPackageActive($packageKey)) {
+                       $this->deactivatePackage($packageKey);
+               }
+
+               $packagePath = $package->getPackagePath();
+               try {
+                       Files::removeDirectoryRecursively($packagePath);
+               } catch (\TYPO3\Flow\Utility\Exception $exception) {
+                       throw new \TYPO3\Flow\Package\Exception('Please check file permissions. The directory "' . $packagePath . '" for package "' . $packageKey . '" could not be removed.', 1301491089, $exception);
+               }
+
+               $this->unregisterPackage($package);
+       }
+
+       /**
+        * Loads the states of available packages from the PackageStates.php file.
+        * The result is stored in $this->packageStatesConfiguration.
+        *
+        * @return void
+        */
+       protected function loadPackageStates() {
+               $this->packageStatesConfiguration = file_exists($this->packageStatesPathAndFilename) ? include($this->packageStatesPathAndFilename) : array();
+               if (!isset($this->packageStatesConfiguration['version']) || $this->packageStatesConfiguration['version'] < 4) {
+                       $this->packageStatesConfiguration = array();
+               }
+               if ($this->packageStatesConfiguration === array() || !$this->bootstrap->getContext()->isProduction()) {
+                       $this->scanAvailablePackages();
+               } else {
+                       $this->registerPackagesFromConfiguration();
+               }
+       }
+
+       /**
+        * Scans all directories in the packages directories for available packages.
+        * For each package a Package object is created and stored in $this->packages.
+        *
+        * @return void
+        * @throws \TYPO3\Flow\Package\Exception\DuplicatePackageException
+        */
+       protected function scanAvailablePackages() {
+               $previousPackageStatesConfiguration = $this->packageStatesConfiguration;
+
+               if (isset($this->packageStatesConfiguration['packages'])) {
+                       foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $configuration) {
+                               if (!file_exists($this->packagesBasePath . $configuration['packagePath'])) {
+                                       unset($this->packageStatesConfiguration['packages'][$packageKey]);
+                               }
+                       }
+               } else {
+                       $this->packageStatesConfiguration['packages'] = array();
+               }
+
+               $packagePaths = array();
+               foreach (new \DirectoryIterator($this->packagesBasePath) as $parentFileInfo) {
+                       $parentFilename = $parentFileInfo->getFilename();
+                       if ($parentFilename[0] !== '.' && $parentFileInfo->isDir()) {
+                               $packagePaths = array_merge($packagePaths, $this->scanPackagesInPath($parentFileInfo->getPathName()));
+                       }
+               }
+
+               /**
+                * @todo similar functionality in registerPackage - should be refactored
+                */
+               foreach ($packagePaths as $packagePath => $composerManifestPath) {
+                       try {
+                               $composerManifest = self::getComposerManifest($composerManifestPath);
+                               $packageKey = PackageFactory::getPackageKeyFromManifest($composerManifest, $packagePath, $this->packagesBasePath);
+                               $this->composerNameToPackageKeyMap[strtolower($composerManifest->name)] = $packageKey;
+                               $this->packageStatesConfiguration['packages'][$packageKey]['manifestPath'] = substr($composerManifestPath, strlen($packagePath)) ?: '';
+                               $this->packageStatesConfiguration['packages'][$packageKey]['composerName'] = $composerManifest->name;
+                       } catch (\TYPO3\Flow\Package\Exception\MissingPackageManifestException $exception) {
+                               $relativePackagePath = substr($packagePath, strlen($this->packagesBasePath));
+                               $packageKey = substr($relativePackagePath, strpos($relativePackagePath, '/') + 1, -1);
+                       }
+                       if (!isset($this->packageStatesConfiguration['packages'][$packageKey]['state'])) {
+                               /**
+                                * @todo doesn't work, settings not available at this time
+                                */
+                               if (is_array($this->settings['package']['inactiveByDefault']) && in_array($packageKey, $this->settings['package']['inactiveByDefault'], TRUE)) {
+                                       $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'inactive';
+                               } else {
+                                       $this->packageStatesConfiguration['packages'][$packageKey]['state'] = 'active';
+                               }
+                       }
+
+                       $this->packageStatesConfiguration['packages'][$packageKey]['packagePath'] = str_replace($this->packagesBasePath, '', $packagePath);
+
+                               // Change this to read the target from Composer or any other source
+                       $this->packageStatesConfiguration['packages'][$packageKey]['classesPath'] = Package::DIRECTORY_CLASSES;
+               }
+
+               $this->registerPackagesFromConfiguration();
+               if ($this->packageStatesConfiguration != $previousPackageStatesConfiguration) {
+                       $this->sortAndsavePackageStates();
+               }
+       }
+
+       /**
+        * Looks for composer.json in the given path and returns a path or NULL.
+        *
+        * @param string $packagePath
+        * @return array
+        */
+       protected function findComposerManifestPaths($packagePath) {
+               $manifestPaths = array();
+               if (file_exists($packagePath . '/composer.json')) {
+                       $manifestPaths[] = $packagePath . '/';
+               } else {
+                       $jsonPathsAndFilenames = Files::readDirectoryRecursively($packagePath, '.json');
+                       asort($jsonPathsAndFilenames);
+                       while (list($unusedKey, $jsonPathAndFilename) = each($jsonPathsAndFilenames)) {
+                               if (basename($jsonPathAndFilename) === 'composer.json') {
+                                       $manifestPath = dirname($jsonPathAndFilename) . '/';
+                                       $manifestPaths[] = $manifestPath;
+                                       $isNotSubPathOfManifestPath = function ($otherPath) use ($manifestPath) {
+                                               return strpos($otherPath, $manifestPath) !== 0;
+                                       };
+                                       $jsonPathsAndFilenames = array_filter($jsonPathsAndFilenames, $isNotSubPathOfManifestPath);
+                               }
+                       }
+               }
+
+               return $manifestPaths;
+       }
+
+       /**
+        * Scans all sub directories of the specified directory and collects the package keys of packages it finds.
+        *
+        * The return of the array is to make this method usable in array_merge.
+        *
+        * @param string $startPath
+        * @param array $collectedPackagePaths
+        * @return array
+        */
+       protected function scanPackagesInPath($startPath, array &$collectedPackagePaths = array()) {
+               foreach (new \DirectoryIterator($startPath) as $fileInfo) {
+                       if (!$fileInfo->isDir()) {
+                               continue;
+                       }
+                       $filename = $fileInfo->getFilename();
+                       if ($filename[0] !== '.') {
+                               $currentPath = Files::getUnixStylePath($fileInfo->getPathName());
+                               $composerManifestPaths = $this->findComposerManifestPaths($currentPath);
+                               foreach ($composerManifestPaths as $composerManifestPath) {
+                                       $targetDirectory = rtrim(self::getComposerManifest($composerManifestPath, 'target-dir'), '/');
+                                       $packagePath = $targetDirectory ? substr(rtrim($composerManifestPath, '/'), 0, -strlen((string)$targetDirectory)) : $composerManifestPath;
+                                       $collectedPackagePaths[$packagePath] = $composerManifestPath;
+                               }
+                       }
+               }
+               return $collectedPackagePaths;
+       }
+
+       /**
+        * Returns contents of Composer manifest - or part there of.
+        *
+        * @param string $manifestPath
+        * @param string $key Optional. Only return the part of the manifest indexed by 'key'
+        * @param object $composerManifest Optional. Manifest to use instead of reading it from file
+        * @return mixed
+        * @throws \TYPO3\Flow\Package\Exception\MissingPackageManifestException
+        * @see json_decode for return values
+        */
+       static public function getComposerManifest($manifestPath, $key = NULL, $composerManifest = NULL) {
+               if ($composerManifest === NULL) {
+                       if (!file_exists($manifestPath . 'composer.json')) {
+                               throw new \TYPO3\Flow\Package\Exception\MissingPackageManifestException('No composer manifest file found at "' . $manifestPath . '/composer.json".', 1349868540);
+                       }
+                       $json = file_get_contents($manifestPath . 'composer.json');
+                       $composerManifest = json_decode($json);
+               }
+
+               if ($key !== NULL) {
+                       if (isset($composerManifest->{$key})) {
+                               $value = $composerManifest->{$key};
+                       } else {
+                               $value = NULL;
+                       }
+               } else {
+                       $value = $composerManifest;
+               }
+               return $value;
+       }
+
+       /**
+        * Requires and registers all packages which were defined in packageStatesConfiguration
+        *
+        * @return void
+        * @throws \TYPO3\Flow\Package\Exception\CorruptPackageException
+        */
+       protected function registerPackagesFromConfiguration() {
+               foreach ($this->packageStatesConfiguration['packages'] as $packageKey => $stateConfiguration) {
+
+                       $packagePath = isset($stateConfiguration['packagePath']) ? $stateConfiguration['packagePath'] : NULL;
+                       $classesPath = isset($stateConfiguration['classesPath']) ? $stateConfiguration['classesPath'] : NULL;
+                       $manifestPath = isset($stateConfiguration['manifestPath']) ? $stateConfiguration['manifestPath'] : NULL;
+
+                       try {
+                               $package = $this->packageFactory->create($this->packagesBasePath, $packagePath, $packageKey, $classesPath, $manifestPath);
+                       } catch (\TYPO3\Flow\Package\Exception\InvalidPackagePathException $exception) {
+                               $this->unregisterPackageByPackageKey($packageKey);
+                               $this->systemLogger->log('Package ' . $packageKey . ' could not be loaded, it has been unregistered. Error description: "' . $exception->getMessage() . '" (' . $exception->getCode() . ')', LOG_WARNING);
+                               continue;
+                       }
+
+                       $this->registerPackage($package, FALSE);
+
+                       if (!$this->packages[$packageKey] instanceof PackageInterface) {
+                               throw new \TYPO3\Flow\Package\Exception\CorruptPackageException(sprintf('The package class in package "%s" does not implement PackageInterface.', $packageKey), 1300782487);
+                       }
+
+                       $this->packageKeys[strtolower($packageKey)] = $packageKey;
+                       if ($stateConfiguration['state'] === 'active') {
+                               $this->activePackages[$packageKey] = $this->packages[$packageKey];
+                       }
+               }
+       }
+
+       /**
+        * Saves the current content of $this->packageStatesConfiguration to the
+        * PackageStates.php file.
+        *
+        * @return void
+        */
+       protected function sortAndSavePackageStates() {
+               $this->sortAvailablePackagesByDependencies();
+
+               $this->packageStatesConfiguration['version'] = 4;
+
+               $fileDescription = "# PackageStates.php\n\n";
+               $fileDescription .= "# This file is maintained by Flow's package management. Although you can edit it\n";
+               $fileDescription .= "# manually, you should rather use the command line commands for maintaining packages.\n";
+               $fileDescription .= "# You'll find detailed information about the typo3.flow:package:* commands in their\n";
+               $fileDescription .= "# respective help screens.\n\n";
+               $fileDescription .= "# This file will be regenerated automatically if it doesn't exist. Deleting this file\n";
+               $fileDescription .= "# should, however, never become necessary if you use the package commands.\n";
+
+                       // we do not need the dependencies on disk...
+               foreach ($this->packageStatesConfiguration['packages'] as &$packageConfiguration) {
+                       if (isset($packageConfiguration['dependencies'])) {
+                               unset($packageConfiguration['dependencies']);
+                       }
+               }
+               $packageStatesCode = "<?php\n$fileDescription\nreturn " . var_export($this->packageStatesConfiguration, TRUE) . "\n ?>";
+               @file_put_contents($this->packageStatesPathAndFilename, $packageStatesCode);
+       }
+
+       /**
+        * Resolves the dependent packages from the meta data of all packages recursively. The
+        * resolved direct or indirect dependencies of each package will put into the package
+        * states configuration array.
+        *
+        * @return void
+        */
+       protected function resolvePackageDependencies() {
+               foreach ($this->packages as $packageKey => $package) {
+                       $this->packageStatesConfiguration['packages'][$packageKey]['dependencies'] = $this->getDependencyArrayForPackage($packageKey);
+               }
+       }
+
+       /**
+        * Returns an array of dependent package keys for the given package. It will
+        * do this recursively, so dependencies of dependant packages will also be
+        * in the result.
+        *
+        * @param string $packageKey The package key to fetch the dependencies for
+        * @param array $dependentPackageKeys
+        * @param array $trace An array of already visited package keys, to detect circular dependencies
+        * @return array|NULL An array of direct or indirect dependant packages
+        * @throws \TYPO3\Flow\Mvc\Exception\InvalidPackageKeyException
+        */
+       protected function getDependencyArrayForPackage($packageKey, array &$dependentPackageKeys = array(), array $trace = array()) {
+               if (!isset($this->packages[$packageKey])) {
+                       return NULL;
+               }
+               if (in_array($packageKey, $trace) !== FALSE) {
+                       return $dependentPackageKeys;
+               }
+               $trace[] = $packageKey;
+               $dependentPackageConstraints = $this->packages[$packageKey]->getPackageMetaData()->getConstraintsByType(MetaDataInterface::CONSTRAINT_TYPE_DEPENDS);
+               foreach ($dependentPackageConstraints as $constraint) {
+                       if ($constraint instanceof \TYPO3\Flow\Package\MetaData\PackageConstraint) {
+                               $dependentPackageKey = $constraint->getValue();
+                               if (in_array($dependentPackageKey, $dependentPackageKeys) === FALSE && in_array($dependentPackageKey, $trace) === FALSE) {
+                                       $dependentPackageKeys[] = $dependentPackageKey;
+                               }
+                               $this->getDependencyArrayForPackage($dependentPackageKey, $dependentPackageKeys, $trace);
+                       }
+               }
+               return array_reverse($dependentPackageKeys);
+       }
+
+       /**
+        * Orders all packages by comparing their dependencies. By this, the packages
+        * and package configurations arrays holds all packages in the correct
+        * initialization order.
+        *
+        * @return void
+        */
+       protected function sortAvailablePackagesByDependencies() {
+               $this->resolvePackageDependencies();
+
+               $packageStatesConfiguration = $this->packageStatesConfiguration['packages'];
+
+               $comparator = function ($firstPackageKey, $secondPackageKey) use ($packageStatesConfiguration) {
+                       if (isset($packageStatesConfiguration[$firstPackageKey]['dependencies'])
+                                       && (in_array($secondPackageKey, $packageStatesConfiguration[$firstPackageKey]['dependencies'])
+                                               && !in_array($firstPackageKey, $packageStatesConfiguration[$secondPackageKey]['dependencies']))) {
+                               return 1;
+                       } elseif (isset($packageStatesConfiguration[$secondPackageKey]['dependencies'])
+                               && (in_array($firstPackageKey, $packageStatesConfiguration[$secondPackageKey]['dependencies'])
+                                       && !in_array($secondPackageKey, $packageStatesConfiguration[$firstPackageKey]['dependencies']))) {
+                               return -1;
+                       }
+                       return strcmp($firstPackageKey, $secondPackageKey);
+               };
+
+               uasort($this->packages,
+                       function (\TYPO3\Flow\Package\PackageInterface $firstPackage, \TYPO3\Flow\Package\PackageInterface $secondPackage) use ($comparator) {
+                               return $comparator($firstPackage->getPackageKey(), $secondPackage->getPackageKey());
+                       }
+               );
+
+               uksort($this->packageStatesConfiguration['packages'],
+                       function ($firstPackageKey, $secondPackageKey) use ($comparator) {
+                               return $comparator($firstPackageKey, $secondPackageKey);
+                       }
+               );
+       }
+}
+
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageManagerInterface.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Package/PackageManagerInterface.php
new file mode 100644 (file)
index 0000000..106dedd
--- /dev/null
@@ -0,0 +1,207 @@
+<?php
+namespace TYPO3\Flow\Package;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * Interface for the TYPO3 Package Manager
+ *
+ * @api
+ */
+interface PackageManagerInterface {
+
+       /**
+        * Initializes the package manager.
+        *
+        * @param \TYPO3\Flow\Core\Bootstrap $bootstrap
+        * @return void
+        */
+       public function initialize(\TYPO3\Flow\Core\Bootstrap $bootstrap);
+
+       /**
+        * Returns TRUE if a package is available (the package's files exist in the packages directory)
+        * or FALSE if it's not. If a package is available it doesn't mean necessarily that it's active!
+        *
+        * @param string $packageKey The key of the package to check
+        * @return boolean TRUE if the package is available, otherwise FALSE
+        * @api
+        */
+       public function isPackageAvailable($packageKey);
+
+       /**
+        * Returns TRUE if a package is activated or FALSE if it's not.
+        *
+        * @param string $packageKey The key of the package to check
+        * @return boolean TRUE if package is active, otherwise FALSE
+        * @api
+        */
+       public function isPackageActive($packageKey);
+
+       /**
+        * Returns a \TYPO3\Flow\Package\PackageInterface object for the specified package.
+        * A package is available, if the package directory contains valid meta information.
+        *
+        * @param string $packageKey
+        * @return \TYPO3\Flow\Package\PackageInterface
+        * @api
+        */
+       public function getPackage($packageKey);
+
+       /**
+        * Finds a package by a given object of that package; if no such package
+        * could be found, NULL is returned.
+        *
+        * @param object $object The object to find the possessing package of
+        * @return \TYPO3\Flow\Package\PackageInterface The package the given object belongs to or NULL if it could not be found
+        */
+       public function getPackageOfObject($object);
+
+       /**
+        * Returns an array of \TYPO3\Flow\Package\PackageInterface objects of all available packages.
+        * A package is available, if the package directory contains valid meta information.
+        *
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        * @api
+        */
+       public function getAvailablePackages();
+
+       /**
+        * Returns an array of \TYPO3\Flow\PackageInterface objects of all active packages.
+        * A package is active, if it is available and has been activated in the package
+        * manager settings.
+        *
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        * @api
+        */
+       public function getActivePackages();
+
+       /**
+        * Returns an array of \TYPO3\Flow\PackageInterface objects of all packages that match
+        * the given package state, path, and type filters. All three filters must match, if given.
+        *
+        * @param string $packageState defaults to available
+        * @param string $packagePath
+        * @param string $packageType
+        *
+        * @return array Array of \TYPO3\Flow\Package\PackageInterface
+        * @api
+        */
+       public function getFilteredPackages($packageState = 'available', $packagePath = NULL, $packageType = NULL);
+
+       /**
+        * Returns the upper camel cased version of the given package key or FALSE
+        * if no such package is available.
+        *
+        * @param string $unknownCasedPackageKey The package key to convert
+        * @return mixed The upper camel cased package key or FALSE if no such package exists
+        * @api
+        */
+       public function getCaseSensitivePackageKey($unknownCasedPackageKey);
+
+       /**
+        * Check the conformance of the given package key
+        *
+        * @param string $packageKey The package key to validate
+        * @api
+        */
+       public function isPackageKeyValid($packageKey);
+
+       /**
+        * Create a new package, given the package key
+        *
+        * @param string $packageKey The package key to use for the new package
+        * @param \TYPO3\Flow\Package\MetaData $packageMetaData Package metadata
+        * @param string $packagesPath If specified, the package will be created in this path
+        * @param string $packageType If specified, the package type will be set
+        * @return \TYPO3\Flow\Package\Package The newly created package
+        * @api
+        */
+       public function createPackage($packageKey, \TYPO3\Flow\Package\MetaData $packageMetaData = NULL, $packagesPath = NULL, $packageType = NULL);
+
+       /**
+        * Deactivates a package if it is in the list of active packages
+        *
+        * @param string $packageKey The package to deactivate
+        * @return void
+        * @api
+        */
+       public function deactivatePackage($packageKey);
+
+       /**
+        * Activates a package
+        *
+        * @param string $packageKey The package to activate
+        * @return void
+        * @api
+        */
+       public function activatePackage($packageKey);
+
+       /**
+        * Freezes a package
+        *
+        * @param string $packageKey The package to freeze
+        * @return void
+        */
+       public function freezePackage($packageKey);
+
+       /**
+        * Tells if a package is frozen
+        *
+        * @param string $packageKey The package to check
+        * @return boolean
+        */
+       public function isPackageFrozen($packageKey);
+
+       /**
+        * Unfreezes a package
+        *
+        * @param string $packageKey The package to unfreeze
+        * @return void
+        */
+       public function unfreezePackage($packageKey);
+
+       /**
+        * Refreezes a package
+        *
+        * @param string $packageKey The package to refreeze
+        * @return void
+        */
+       public function refreezePackage($packageKey);
+
+       /**
+        * Register a native Flow package
+        *
+        * @param string $packageKey The Package to be registered
+        * @param boolean $sortAndSave allows for not saving packagestates when used in loops etc.
+        * @return PackageInterface
+        * @throws Exception\CorruptPackageException
+        */
+       public function registerPackage(PackageInterface $package, $sortAndSave = TRUE);
+
+       /**
+        * Unregisters a package from the list of available packages
+        *
+        * @param PackageInterface $package The package to be unregistered
+        * @throws Exception\InvalidPackageStateException
+        */
+       public function unregisterPackage(PackageInterface $package);
+
+       /**
+        * Removes a package from registry and deletes it from filesystem
+        *
+        * @param string $packageKey package to delete
+        * @return void
+        * @api
+        */
+       public function deletePackage($packageKey);
+
+}
+?>
\ No newline at end of file
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Utility/Files.php b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/Classes/TYPO3/Flow/Utility/Files.php
new file mode 100644 (file)
index 0000000..914d724
--- /dev/null
@@ -0,0 +1,311 @@
+<?php
+namespace TYPO3\Flow\Utility;
+
+/*                                                                        *
+ * This script belongs to the TYPO3 Flow framework.                       *
+ *                                                                        *
+ * It is free software; you can redistribute it and/or modify it under    *
+ * the terms of the GNU Lesser General Public License, either version 3   *
+ * of the License, or (at your option) any later version.                 *
+ *                                                                        *
+ * The TYPO3 project - inspiring people to share!                         *
+ *                                                                        */
+
+/**
+ * File and directory functions
+ *
+ */
+class Files {
+
+       /**
+        * Replacing backslashes and double slashes to slashes.
+        * It's needed to compare paths (especially on windows).
+        *
+        * @param string $path Path which should transformed to the Unix Style.
+        * @return string
+        */
+       static public function getUnixStylePath($path) {
+               if (strpos($path, ':') === FALSE) {
+                       return str_replace('//', '/', strtr($path, '\\', '/'));
+               } else {
+                       return preg_replace('/^([a-z]{2,}):\//', '$1://', str_replace('//', '/', strtr($path, '\\', '/')));
+               }
+       }
+
+       /**
+        * Makes sure path has a trailing slash
+        *
+        * @param string $path
+        * @return string
+        */
+       static public function getNormalizedPath($path) {
+               return rtrim($path, '/') .'/';
+       }
+
+       /**
+        * Properly glues together filepaths / filenames by replacing
+        * backslashes and double slashes of the specified paths.
+        * Note: trailing slashes will be removed, leading slashes won't.
+        * Usage: concatenatePaths(array('dir1/dir2', 'dir3', 'file'))
+        *
+        * @param array $paths the file paths to be combined. Last array element may include the filename.
+        * @return string concatenated path without trailing slash.
+        * @see getUnixStylePath()
+        */
+       static public function concatenatePaths(array $paths) {
+               return rtrim(self::getUnixStylePath(implode('/', $paths)), '/');
+       }
+
+       /**
+        * Returns all filenames from the specified directory. Filters hidden files and
+        * directories.
+        *
+        * @param string $path Path to the directory which shall be read
+        * @param string $suffix If specified, only filenames with this extension are returned (eg. ".php" or "foo.bar")
+        * @param boolean $returnRealPath If turned on, all paths are resolved by calling realpath()
+        * @param boolean $returnDotFiles If turned on, also files beginning with a dot will be returned
+        * @param array $filenames Internally used for the recursion - don't specify!
+        * @return array Filenames including full path
+        * @throws Exception
+        */
+       static public function readDirectoryRecursively($path, $suffix = NULL, $returnRealPath = FALSE, $returnDotFiles = FALSE, &$filenames = array()) {
+               if (!is_dir($path)) throw new \TYPO3\Flow\Utility\Exception('"' . $path . '" is no directory.', 1207253462);
+
+               $directoryIterator = new \DirectoryIterator($path);
+               $suffixLength = strlen($suffix);
+
+               foreach ($directoryIterator as $fileInfo) {
+                       $filename = $fileInfo->getFilename();
+                       if ($filename === '.' || $filename === '..' || ($returnDotFiles === FALSE && $filename[0] === '.')) {
+                               continue;
+                       }
+                       if ($fileInfo->isFile() && ($suffix === NULL || substr($filename, -$suffixLength) === $suffix)) {
+                               $filenames[] = self::getUnixStylePath(($returnRealPath === TRUE ? realpath($fileInfo->getPathname()) : $fileInfo->getPathname()));
+                       }
+                       if ($fileInfo->isDir()) {
+                               self::readDirectoryRecursively($fileInfo->getPathname(), $suffix, $returnRealPath, $returnDotFiles, $filenames);
+                       }
+               }
+               return $filenames;
+       }
+
+       /**
+        * Deletes all files, directories and subdirectories from the specified
+        * directory. The passed directory itself won't be deleted though.
+        *
+        * @param string $path Path to the directory which shall be emptied.
+        * @return void
+        * @throws Exception
+        * @see removeDirectoryRecursively()
+        */
+       static public function emptyDirectoryRecursively($path) {
+               if (!is_dir($path)) {
+                       throw new \TYPO3\Flow\Utility\Exception('"' . $path . '" is no directory.', 1169047616);
+               }
+
+               if (self::is_link($path)) {
+                       if (self::unlink($path) !== TRUE) {
+                               throw new \TYPO3\Flow\Utility\Exception('Could not unlink symbolic link "' . $path . '".', 1323697654);
+                       }
+               } else {
+                       $directoryIterator = new \RecursiveDirectoryIterator($path);
+                       foreach ($directoryIterator as $fileInfo) {
+                               if (!$fileInfo->isDir()) {
+                                       if (self::unlink($fileInfo->getPathname()) !== TRUE) {
+                                               throw new \TYPO3\Flow\Utility\Exception('Could not unlink file "' . $fileInfo->getPathname() . '".', 1169047619);
+                                       }
+                               } elseif (!$directoryIterator->isDot()) {
+                                       self::removeDirectoryRecursively($fileInfo->getPathname());
+                               }
+                       }
+               }
+       }
+
+       /**
+        * Deletes all files, directories and subdirectories from the specified
+        * directory. Contrary to emptyDirectoryRecursively() this function will
+        * also finally remove the emptied directory.
+        *
+        * @param  string $path Path to the directory which shall be removed completely.
+        * @return void
+        * @throws Exception
+        * @see emptyDirectoryRecursively()
+        */
+       static public function removeDirectoryRecursively($path) {
+               if (self::is_link($path)) {
+                       if (self::unlink($path) !== TRUE) {
+                               throw new \TYPO3\Flow\Utility\Exception('Could not unlink symbolic link "' . $path . '".', 1316000297);
+                       }
+               } else {
+                       self::emptyDirectoryRecursively($path);
+                       try {
+                               if (rmdir($path) !== TRUE) {
+                                       throw new \TYPO3\Flow\Utility\Exception('Could not remove directory "' . $path . '".', 1316000298);
+                               }
+                       } catch (\Exception $exception) {
+                               throw new \TYPO3\Flow\Utility\Exception('Could not remove directory "' . $path . '".', 1323961907);
+                       }
+               }
+       }
+
+       /**
+        * Creates a directory specified by $path. If the parent directories
+        * don't exist yet, they will be created as well.
+        *
+        * @param string $path Path to the directory which shall be created
+        * @return void
+        * @throws Exception
+        * @todo Make mode configurable / make umask configurable
+        */
+       static public function createDirectoryRecursively($path) {
+               if (substr($path, -2) === '/.') {
+                       $path = substr($path, 0, -1);
+               }
+               if (is_file($path)) {
+                       throw new \TYPO3\Flow\Utility\Exception('Could not create directory "' . $path . '", because a file with that name exists!', 1349340620);
+               }
+               if (!is_dir($path) && strlen($path) > 0) {
+                       $oldMask = umask(000);
+                       mkdir($path, 0777, TRUE);
+                       umask($oldMask);
+                       if (!is_dir($path)) {
+                               throw new \TYPO3\Flow\Utility\Exception('Could not create directory "' . $path . '"!', 1170251400);
+                       }
+               }
+       }
+
+       /**
+        * Copies the contents of the source directory to the target directory.
+        * $targetDirectory will be created if it does not exist.
+        *
+        * If $keepExistingFiles is TRUE, this will keep files already present
+        * in the target location. It defaults to FALSE.
+        *
+        * If $copyDotFiles is TRUE, this will copy files whose name begin with
+        * a dot. It defaults to FALSE.
+        *
+        * @param string $sourceDirectory
+        * @param string $targetDirectory
+        * @param boolean $keepExistingFiles
+        * @param boolean $copyDotFiles
+        * @return void
+        * @throws Exception
+        */
+       static public function copyDirectoryRecursively($sourceDirectory, $targetDirectory, $keepExistingFiles = FALSE, $copyDotFiles = FALSE) {
+               if (!is_dir($sourceDirectory)) {
+                       throw new \TYPO3\Flow\Utility\Exception('"' . $sourceDirectory . '" is no directory.', 1235428779);
+               }
+
+               self::createDirectoryRecursively($targetDirectory);
+               if (!is_dir($targetDirectory)) {
+                       throw new \TYPO3\Flow\Utility\Exception('"' . $targetDirectory . '" is no directory.', 1235428780);
+               }
+
+               $sourceFilenames = self::readDirectoryRecursively($sourceDirectory, NULL, FALSE, $copyDotFiles);
+               foreach ($sourceFilenames as $filename) {
+                       $relativeFilename = str_replace($sourceDirectory, '', $filename);
+                       self::createDirectoryRecursively($targetDirectory . dirname($relativeFilename));
+                       $targetPathAndFilename = self::concatenatePaths(array($targetDirectory, $relativeFilename));
+                       if ($keepExistingFiles === FALSE || !file_exists($targetPathAndFilename)) {
+                               copy($filename, $targetPathAndFilename);
+                       }
+               }
+       }
+
+       /**
+        * An enhanced version of file_get_contents which intercepts the warning
+        * issued by the original function if a file could not be loaded.
+        *
+        * @param string $pathAndFilename Path and name of the file to load
+        * @param integer $flags (optional) ORed flags using PHP's FILE_* constants (see manual of file_get_contents).
+        * @param resource $context (optional) A context resource created by stream_context_create()
+        * @param integer $offset (optional) Offset where reading of the file starts.
+        * @param integer $maximumLength (optional) Maximum length to read. Default is -1 (no limit)
+        * @return mixed The file content as a string or FALSE if the file could not be opened.
+        */
+       static public function getFileContents($pathAndFilename, $flags = 0, $context = NULL, $offset = -1, $maximumLength = -1) {
+               if ($flags === TRUE) $flags = FILE_USE_INCLUDE_PATH;
+               try {
+                       if ($maximumLength > -1) {
+                               $content = file_get_contents($pathAndFilename, $flags, $context, $offset, $maximumLength);
+                       } else {
+                               $content = file_get_contents($pathAndFilename, $flags, $context, $offset);
+                       }
+               } catch (\TYPO3\Flow\Error\Exception $ignoredException) {
+                       $content = FALSE;
+               }
+               return $content;
+       }
+
+       /**
+        * Returns a human-readable message for the given PHP file upload error
+        * constant.
+        *
+        * @param integer $errorCode One of the UPLOAD_ERR_ constants
+        * @return string
+        */
+       static public function getUploadErrorMessage($errorCode) {
+               switch ($errorCode) {
+                       case \UPLOAD_ERR_INI_SIZE:
+                               return 'The uploaded file exceeds the upload_max_filesize directive in php.ini';
+                       case \UPLOAD_ERR_FORM_SIZE:
+                               return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
+                       case \UPLOAD_ERR_PARTIAL:
+                               return 'The uploaded file was only partially uploaded';
+                       case \UPLOAD_ERR_NO_FILE:
+                               return 'No file was uploaded';
+                       case \UPLOAD_ERR_NO_TMP_DIR:
+                               return 'Missing a temporary folder';
+                       case \UPLOAD_ERR_CANT_WRITE:
+                               return 'Failed to write file to disk';
+                       case \UPLOAD_ERR_EXTENSION:
+                               return 'File upload stopped by extension';
+                       default:
+                               return 'Unknown upload error';
+               }
+       }
+
+       /**
+        * A version of is_link() that works on Windows too
+        * @see http://www.php.net/is_link
+        *
+        * If http://bugs.php.net/bug.php?id=51766 gets fixed we can drop this.
+        *
+        * @param string $pathAndFilename Path and name of the file or directory
+        * @return boolean TRUE if the path exists and is a symbolic link, FALSE otherwise
+        */
+       static public function is_link($pathAndFilename) {
+                       // if not on Windows, call PHPs own is_link() function
+               if (DIRECTORY_SEPARATOR === '/') {
+                       return \is_link($pathAndFilename);
+               }
+               if (!file_exists($pathAndFilename)) {
+                       return FALSE;
+               }
+               $normalizedPathAndFilename = strtolower(rtrim(self::getUnixStylePath($pathAndFilename), '/'));
+               $normalizedTargetPathAndFilename = strtolower(self::getUnixStylePath(realpath($pathAndFilename)));
+               if ($normalizedTargetPathAndFilename === '') {
+                       return FALSE;
+               }
+               return $normalizedPathAndFilename !== $normalizedTargetPathAndFilename;
+       }
+
+       /**
+        * A version of unlink() that works on Windows regardless on the symlink type (file/directory)
+        *
+        * @param string $pathAndFilename Path and name of the file or directory
+        * @return boolean TRUE if file/directory was removed successfully
+        */
+       static public function unlink($pathAndFilename) {
+               try {
+                               // if not on Windows, call PHPs own unlink() function
+                       if (DIRECTORY_SEPARATOR === '/' || is_file($pathAndFilename)) {
+                               return @\unlink($pathAndFilename);
+                       }
+                       return rmdir($pathAndFilename);
+               } catch (\Exception $exception) {
+                       return FALSE;
+               }
+       }
+}
+?>
diff --git a/typo3/sysext/core/Resources/PHP/TYPO3.Flow/README b/typo3/sysext/core/Resources/PHP/TYPO3.Flow/README
new file mode 100644 (file)
index 0000000..ec93f6d
--- /dev/null
@@ -0,0 +1,10 @@
+This folder contains the parts of TYPO3 Flow for the intermediary
+package management to work.
+
+The following has been changed:
+* TYPO3\Flow\Exception now extends TYPO3\CMS\Core\Exception
+* TYPO3\Flow\Package\Package has magic sleep and wakeup methods
+
+It has been imported from this commmit hash:
+
+327db44575d90f5f7309ec9febaa444fc2ac8a4e
\ No newline at end of file