From f6bd3e0b22554ef3ee51155d7661abfaebb75a33 Mon Sep 17 00:00:00 2001 From: "o.pinke" Date: Wed, 19 Feb 2020 21:58:47 +0100 Subject: [PATCH] re add pluginmanager --- .gitignore | 5 +- .../Zip/Contenido_ArchiveExtractor.class.php | 212 +++ .../class.pim.plugin.archive.extractor.php | 143 ++ .../classes/Util/class.pim.sql.parser.php | 55 + .../pluginmanager/classes/class.pim.ajax.php | 28 + .../classes/class.pim.plugin.dummy.php | 34 + .../classes/class.pim.plugin.handler.php | 271 ++++ .../classes/class.pim.plugin.php | 34 + .../classes/class.pim.plugin.relation.php | 58 + .../pluginmanager/classes/class.pim.view.php | 83 + .../classes/class.pluginmanager.ajax.php | 104 ++ .../classes/exeptions/class.pim.exeption.php | 46 + .../plugin/class.plugin.handler.abstract.php | 67 + .../classes/plugin/interface.plugins.php | 19 + .../classes/setup/class.pim.setup.base.php | 357 ++++ .../setup/class.pim.setup.plugin.install.php | 427 +++++ .../class.pim.setup.plugin.uninstall.php | 38 + .../pluginmanager/css/images/error.png | Bin 0 -> 4340 bytes .../plugins/pluginmanager/css/images/info.png | Bin 0 -> 4403 bytes .../pluginmanager/css/images/success.png | Bin 0 -> 4447 bytes .../pluginmanager/css/images/warning.png | Bin 0 -> 4567 bytes .../pluginmanager/css/pluginmanager.css | 101 ++ ...Dokumentation Contenido Plugin Manager.pdf | Bin 0 -> 104821 bytes .../plugins/pluginmanager/docs/install.sql | 42 + .../pluginmanager/docs/linkchecker.zip | Bin 0 -> 21026 bytes .../plugins/pluginmanager/docs/localize.txt | 2 + .../pluginmanager/docs/pear/File/Archive.php | 1434 +++++++++++++++++ .../docs/pear/File/Archive/Predicate.php | 57 + .../docs/pear/File/Archive/Predicate/And.php | 87 + .../pear/File/Archive/Predicate/Current.php | 52 + .../pear/File/Archive/Predicate/Custom.php | 88 + .../pear/File/Archive/Predicate/Duplicate.php | 116 ++ .../docs/pear/File/Archive/Predicate/Ereg.php | 59 + .../pear/File/Archive/Predicate/Eregi.php | 61 + .../pear/File/Archive/Predicate/Extension.php | 71 + .../pear/File/Archive/Predicate/False.php | 47 + .../pear/File/Archive/Predicate/Index.php | 62 + .../docs/pear/File/Archive/Predicate/MIME.php | 75 + .../pear/File/Archive/Predicate/MaxDepth.php | 63 + .../pear/File/Archive/Predicate/MinSize.php | 59 + .../pear/File/Archive/Predicate/MinTime.php | 63 + .../docs/pear/File/Archive/Predicate/Not.php | 55 + .../docs/pear/File/Archive/Predicate/Or.php | 85 + .../docs/pear/File/Archive/Predicate/True.php | 45 + .../docs/pear/File/Archive/Reader.php | 427 +++++ .../docs/pear/File/Archive/Reader/Ar.php | 387 +++++ .../docs/pear/File/Archive/Reader/Archive.php | 98 ++ .../docs/pear/File/Archive/Reader/Bzip2.php | 254 +++ .../docs/pear/File/Archive/Reader/Cache.php | 262 +++ .../pear/File/Archive/Reader/ChangeName.php | 136 ++ .../Reader/ChangeName/AddDirectory.php | 89 + .../Archive/Reader/ChangeName/Callback.php | 52 + .../Archive/Reader/ChangeName/Directory.php | 100 ++ .../docs/pear/File/Archive/Reader/Concat.php | 195 +++ .../pear/File/Archive/Reader/Directory.php | 309 ++++ .../docs/pear/File/Archive/Reader/File.php | 296 ++++ .../docs/pear/File/Archive/Reader/Filter.php | 90 ++ .../docs/pear/File/Archive/Reader/Gzip.php | 276 ++++ .../docs/pear/File/Archive/Reader/Memory.php | 227 +++ .../pear/File/Archive/Reader/MimeList.php | 939 +++++++++++ .../docs/pear/File/Archive/Reader/Multi.php | 95 ++ .../docs/pear/File/Archive/Reader/Relay.php | 134 ++ .../docs/pear/File/Archive/Reader/Select.php | 63 + .../docs/pear/File/Archive/Reader/Tar.php | 412 +++++ .../pear/File/Archive/Reader/Uncompress.php | 317 ++++ .../docs/pear/File/Archive/Reader/Zip.php | 482 ++++++ .../docs/pear/File/Archive/Writer.php | 119 ++ .../pear/File/Archive/Writer/AddBaseName.php | 102 ++ .../docs/pear/File/Archive/Writer/Ar.php | 209 +++ .../docs/pear/File/Archive/Writer/Archive.php | 129 ++ .../docs/pear/File/Archive/Writer/Bzip2.php | 147 ++ .../docs/pear/File/Archive/Writer/Files.php | 259 +++ .../docs/pear/File/Archive/Writer/Gzip.php | 139 ++ .../docs/pear/File/Archive/Writer/Mail.php | 204 +++ .../docs/pear/File/Archive/Writer/Memory.php | 127 ++ .../File/Archive/Writer/MemoryArchive.php | 213 +++ .../docs/pear/File/Archive/Writer/Multi.php | 130 ++ .../docs/pear/File/Archive/Writer/Output.php | 93 ++ .../docs/pear/File/Archive/Writer/Tar.php | 244 +++ .../File/Archive/Writer/UniqueAppender.php | 143 ++ .../docs/pear/File/Archive/Writer/Zip.php | 260 +++ .../pluginmanager/docs/pear/File/Iterator.php | 197 +++ .../docs/pear/File/Iterator/Factory.php | 155 ++ .../pluginmanager/docs/pear/MIME/Type.php | 523 ++++++ .../docs/pear/MIME/Type/Extension.php | 298 ++++ .../docs/pear/MIME/Type/Parameter.php | 163 ++ .../includes/config.autoloader.php | 30 + .../pluginmanager/includes/config.plugin.php | 16 + .../includes/functions/simplexml_dump.php | 200 +++ .../includes/functions/simplexml_tree.php | 249 +++ .../includes/include.right_bottom.php | 221 +++ .../locale/de_DE/LC_MESSAGES/pluginmanager.mo | Bin 0 -> 4605 bytes .../locale/de_DE/LC_MESSAGES/pluginmanager.po | 236 +++ .../pluginmanager/locale/pluginmanager.pot | 215 +++ .../plugins/pluginmanager/locale/potfiles.txt | 26 + .../scripts/jquery.plainmodal.js | 176 ++ .../scripts/jquery.plainmodal.min.js | 1 + .../scripts/jquery.plainoverlay.js | 426 +++++ .../scripts/jquery.plainoverlay.min.js | 1 + .../pluginmanager/scripts/pluginmanager.js | 275 ++++ .../temp/conlite_sample_plugin.zip | Bin 0 -> 1784 bytes .../pluginmanager/temp/linkchecker.zip | Bin 0 -> 21026 bytes .../pi_manager_extracted_plugins.html | 53 + .../templates/pi_manager_install.html | 11 + .../pi_manager_installed_plugins.html | 94 ++ .../templates/pi_manager_overview.html | 31 + .../templates/pi_manager_plugins.html | 12 + .../plugins/pluginmanager/xml/lang_de_DE.xml | 10 + .../plugins/pluginmanager/xml/lang_en_EN.xml | 10 + .../plugins/pluginmanager/xml/lang_en_US.xml | 10 + .../pluginmanager/xml/plugin_default.xml | 53 + .../plugins/pluginmanager/xml/plugin_info.xml | 44 + .../plugins/pluginmanager/xml/plugin_info.xsd | 179 ++ data/cronlog/move_articles.php.job | 1 + data/cronlog/move_old_stats.php.job | 1 + data/cronlog/optimize_database.php.job | 1 + data/cronlog/send_reminder.php.job | 1 + data/cronlog/session_cleanup.php.job | 1 + data/cronlog/setfrontenduserstate.php.job | 1 + data/logs/.gitignore | 5 + 120 files changed, 16788 insertions(+), 1 deletion(-) create mode 100644 conlite/plugins/pluginmanager/classes/Util/Zip/Contenido_ArchiveExtractor.class.php create mode 100644 conlite/plugins/pluginmanager/classes/Util/Zip/class.pim.plugin.archive.extractor.php create mode 100644 conlite/plugins/pluginmanager/classes/Util/class.pim.sql.parser.php create mode 100644 conlite/plugins/pluginmanager/classes/class.pim.ajax.php create mode 100644 conlite/plugins/pluginmanager/classes/class.pim.plugin.dummy.php create mode 100644 conlite/plugins/pluginmanager/classes/class.pim.plugin.handler.php create mode 100644 conlite/plugins/pluginmanager/classes/class.pim.plugin.php create mode 100644 conlite/plugins/pluginmanager/classes/class.pim.plugin.relation.php create mode 100644 conlite/plugins/pluginmanager/classes/class.pim.view.php create mode 100644 conlite/plugins/pluginmanager/classes/class.pluginmanager.ajax.php create mode 100644 conlite/plugins/pluginmanager/classes/exeptions/class.pim.exeption.php create mode 100644 conlite/plugins/pluginmanager/classes/plugin/class.plugin.handler.abstract.php create mode 100644 conlite/plugins/pluginmanager/classes/plugin/interface.plugins.php create mode 100644 conlite/plugins/pluginmanager/classes/setup/class.pim.setup.base.php create mode 100644 conlite/plugins/pluginmanager/classes/setup/class.pim.setup.plugin.install.php create mode 100644 conlite/plugins/pluginmanager/classes/setup/class.pim.setup.plugin.uninstall.php create mode 100644 conlite/plugins/pluginmanager/css/images/error.png create mode 100644 conlite/plugins/pluginmanager/css/images/info.png create mode 100644 conlite/plugins/pluginmanager/css/images/success.png create mode 100644 conlite/plugins/pluginmanager/css/images/warning.png create mode 100644 conlite/plugins/pluginmanager/css/pluginmanager.css create mode 100644 conlite/plugins/pluginmanager/docs/Dokumentation Contenido Plugin Manager.pdf create mode 100644 conlite/plugins/pluginmanager/docs/install.sql create mode 100644 conlite/plugins/pluginmanager/docs/linkchecker.zip create mode 100644 conlite/plugins/pluginmanager/docs/localize.txt create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/And.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Current.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Custom.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Duplicate.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Ereg.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Eregi.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Extension.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/False.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Index.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MIME.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MaxDepth.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MinSize.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MinTime.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Not.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Or.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/True.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Ar.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Archive.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Bzip2.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Cache.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/AddDirectory.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/Callback.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/Directory.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Concat.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Directory.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/File.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Filter.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Gzip.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Memory.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/MimeList.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Multi.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Relay.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Select.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Tar.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Uncompress.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Zip.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/AddBaseName.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Ar.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Archive.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Bzip2.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Files.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Gzip.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Mail.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Memory.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/MemoryArchive.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Multi.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Output.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Tar.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/UniqueAppender.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Zip.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Iterator.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/File/Iterator/Factory.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/MIME/Type.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/MIME/Type/Extension.php create mode 100644 conlite/plugins/pluginmanager/docs/pear/MIME/Type/Parameter.php create mode 100644 conlite/plugins/pluginmanager/includes/config.autoloader.php create mode 100644 conlite/plugins/pluginmanager/includes/config.plugin.php create mode 100644 conlite/plugins/pluginmanager/includes/functions/simplexml_dump.php create mode 100644 conlite/plugins/pluginmanager/includes/functions/simplexml_tree.php create mode 100644 conlite/plugins/pluginmanager/includes/include.right_bottom.php create mode 100644 conlite/plugins/pluginmanager/locale/de_DE/LC_MESSAGES/pluginmanager.mo create mode 100644 conlite/plugins/pluginmanager/locale/de_DE/LC_MESSAGES/pluginmanager.po create mode 100644 conlite/plugins/pluginmanager/locale/pluginmanager.pot create mode 100644 conlite/plugins/pluginmanager/locale/potfiles.txt create mode 100644 conlite/plugins/pluginmanager/scripts/jquery.plainmodal.js create mode 100644 conlite/plugins/pluginmanager/scripts/jquery.plainmodal.min.js create mode 100644 conlite/plugins/pluginmanager/scripts/jquery.plainoverlay.js create mode 100644 conlite/plugins/pluginmanager/scripts/jquery.plainoverlay.min.js create mode 100644 conlite/plugins/pluginmanager/scripts/pluginmanager.js create mode 100644 conlite/plugins/pluginmanager/temp/conlite_sample_plugin.zip create mode 100644 conlite/plugins/pluginmanager/temp/linkchecker.zip create mode 100644 conlite/plugins/pluginmanager/templates/pi_manager_extracted_plugins.html create mode 100644 conlite/plugins/pluginmanager/templates/pi_manager_install.html create mode 100644 conlite/plugins/pluginmanager/templates/pi_manager_installed_plugins.html create mode 100644 conlite/plugins/pluginmanager/templates/pi_manager_overview.html create mode 100644 conlite/plugins/pluginmanager/templates/pi_manager_plugins.html create mode 100644 conlite/plugins/pluginmanager/xml/lang_de_DE.xml create mode 100644 conlite/plugins/pluginmanager/xml/lang_en_EN.xml create mode 100644 conlite/plugins/pluginmanager/xml/lang_en_US.xml create mode 100644 conlite/plugins/pluginmanager/xml/plugin_default.xml create mode 100644 conlite/plugins/pluginmanager/xml/plugin_info.xml create mode 100644 conlite/plugins/pluginmanager/xml/plugin_info.xsd create mode 100644 data/cronlog/move_articles.php.job create mode 100644 data/cronlog/move_old_stats.php.job create mode 100644 data/cronlog/optimize_database.php.job create mode 100644 data/cronlog/send_reminder.php.job create mode 100644 data/cronlog/session_cleanup.php.job create mode 100644 data/cronlog/setfrontenduserstate.php.job create mode 100644 data/logs/.gitignore diff --git a/.gitignore b/.gitignore index 14bc68c..2120487 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,4 @@ -/nbproject/private/ \ No newline at end of file +/nbproject/private/ +/data/config/production/config.php +/_dev/ +/conlite/plugins/pluginmanager/_src/ \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/classes/Util/Zip/Contenido_ArchiveExtractor.class.php b/conlite/plugins/pluginmanager/classes/Util/Zip/Contenido_ArchiveExtractor.class.php new file mode 100644 index 0000000..acbc065 --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/Util/Zip/Contenido_ArchiveExtractor.class.php @@ -0,0 +1,212 @@ + + * @license http://www.contenido.org/license/LIZENZ.txt + * @link http://www.4fb.de + * @link http://www.contenido.org + * @since file available since contenido release 4.8.7 + * + * {@internal + * created 2008-06-06 + * modified 2008-07-03, Frederic Schneider, add security fix + * + * $Id: Contenido_ArchiveExtractor.class.php 7 2015-06-23 11:01:26Z oldperl $: + * }} + * + */ + +if(!defined('CON_FRAMEWORK')) { + die('Illegal call'); +} + +plugin_include('pluginmanager', 'classes/Exceptions/Contenido_ArchiveExtractor_Exception.php'); +//cInclude('pear', 'PHP/Archive.php'); +cInclude('pear', 'File/Archive.php'); + +class Contenido_ArchiveExtractor { + + /** + * The archive file + * @var string + */ + protected $sSource = ""; + + /** + * The destination path + * @var string + */ + protected $sDestination = ""; + + /** + * List of all files in the archive + * @var array + */ + protected $aFileList = array(); + + /** + * The absolute path + * @var string + */ + protected $sAbsPath = ""; + + /** + * Constructor of ArchiveExtractor, load the file list + * @access public + * @param string $sSource + * @return void + */ + public function __construct($sSource) { + global $cfg; + + $this->sSource = (string) $sSource; + + if (file_exists($sSource)) { + + $sTrailingSlash = substr($this->sSource, -1); + + if( $sTrailingSlash != DIRECTORY_SEPARATOR) { + $this->sSource = $this->sSource . DIRECTORY_SEPARATOR; + } + + // generate absolute path to the plugin manager directory + $this->sAbsPath = $cfg['path']['contenido'] . $cfg['path']['plugins'] . "pluginmanager" . DIRECTORY_SEPARATOR; + + $this->getFileList(); + + } else { + throw new Contenido_ArchiveExtractor_Exception("Source file does not exists"); + } + + } + + public function setErrorHandling($mode, $options) { + PEAR::setErrorHandling($mode, $options); // use temporary the error handler of PEAR + } + + /** + * Sets the path where the extractor extracts the archive files + * @access public + * @param string $sDestination + * @return void + */ + public function setDestinationPath($sDestination) { + + if (!is_dir($sDestination)) { + $bMakeDirectory = mkdir($sDestination, 0777); + + if ($bMakeDirectory != true) { + throw new Contenido_ArchiveExtractor_Exception("Can not set destination path: directoy is not writable"); + } + + $this->sDestination = (string) $sDestination; + + } else { + throw new Contenido_ArchiveExtractor_Exception("Destination already exists"); + } + + } + + /** + * Extracts the whole archive + * @access public + * @return void + */ + public function extractArchive() { + + if ($this->sDestination != "") { + File_Archive::extract($this->sSource, $this->sDestination); + } else { + throw new Contenido_ArchiveExtractor_Exception("Extraction failed: no destination path setted"); + } + + } + + /** + * Loads the file list of the archive + * @access public + * @return void + */ + public function getFileList(){ + $objArchiveReader = File_Archive::read($this->sSource); + $this->aFileList = $objArchiveReader->getFileList(); + } + + /** + * Extract only one specific file from archive + * @access public + * @param string $sFilename + * @return void + */ + public function extractArchiveFile($sFilename) { + + $sFilename = (string) $sFilename; + $sExtractFile = $this->sSource . $sFilename; + + if ($this->sDestination != "") { + File_Archive::extract($sExtractFile, $this->sDestination); + } else { + throw new Contenido_ArchiveExtractor_Exception("Extraction failed: no destination path setted"); + } + + } + + /** + * Returns the archives file list + * @access public + * @return array + */ + public function getArchiveFileList() { + return $this->aFileList; + } + + /** + * Checks if a specific file exists in archive + * @access public + * @param string $sFilename + * @return boolean + */ + public function existsInArchive($sFilename) { + + $aFileList = $this->getArchiveFileList(); + + if (in_array($sFilename, $aFileList)) { + $bFileCheck = true; + } else { + $bFileCheck = false; + } + + return $bFileCheck; + + } + + /** + * Extracts a specific file from archive and return its content to use it in a variable + * @access public + * @param string $sFilename + * @return string + */ + public function extractArchiveFileToVariable($sFilename) { + + $sFilename = (string) $sFilename; + $sExtractFile = $this->sSource . $sFilename; + + File_Archive::extract($sExtractFile, File_Archive::toVariable($sReturn)); + return $sReturn; + + } + +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/classes/Util/Zip/class.pim.plugin.archive.extractor.php b/conlite/plugins/pluginmanager/classes/Util/Zip/class.pim.plugin.archive.extractor.php new file mode 100644 index 0000000..ad2c1a3 --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/Util/Zip/class.pim.plugin.archive.extractor.php @@ -0,0 +1,143 @@ +_extractor = new ZipArchive(); + + // path to temp directory + $this->tempDir = $source; + + // temp directory with zip archive + $this->_source = (string) $source . (string) $filename; + + if (file_exists($source)) { + // generate absolute path to the plugin manager directory + $this->_absPath = $cfg['path']['contenido'] . $cfg['path']['plugins'] . 'pluginmanager' . DIRECTORY_SEPARATOR; + + // open the zip archive + $this->_extractor->open($this->_source); + } else { + throw new pimException('Source file does not exists'); + } + } + + public function closeArchive() { + $this->_extractor->close(); + } + + /** + * + * @param string $destination + * @throws pimException + */ + public function setDestinationPath($destination) { + if (!is_dir($destination)) { + $makeDirectory = mkdir($destination, 0777); + if ($makeDirectory != true) { + throw new pimException('Can not set destination path: directoy is not writable'); + } + $this->_destination = (string) $destination; + } else { + throw new pimException('Destination already exists'); + } + } + + /** + * + * @throws pimException + */ + public function extractArchive() { + if ($this->_destination != '') { + $this->_extractor->extractTo($this->_destination); + } else { + throw new pimException('Extraction failed: no destination path setted'); + } + } + + /** + * + * @param string $filename + * @param boolean $content + * @return type + */ + public function extractArchiveFileToVariable($filename, $content = true) { + $filename = (string) $filename; + $this->_extractor->extractTo($this->tempDir, $filename); + + if ($content) { + return file_get_contents($this->tempDir . $filename); + } else { + return $this->tempDir . $filename; + } + } + + /** + * + */ + public function destroyTempFiles() { + + // remove plugin.xml if exists + if (file_exists($this->tempDir . 'cl_plugin.xml')) { + unlink($this->tempDir . 'cl_plugin.xml'); + } + + // remove plugin_install.sql if exists + if (file_exists($this->tempDir . 'plugin_install.sql')) { + unlink($this->tempDir . 'plugin_install.sql'); + } + + // remove temporary plugin dir if exists + if (file_exists($this->_source)) { + unlink($this->_source); + } + } +} diff --git a/conlite/plugins/pluginmanager/classes/Util/class.pim.sql.parser.php b/conlite/plugins/pluginmanager/classes/Util/class.pim.sql.parser.php new file mode 100644 index 0000000..8616bef --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/Util/class.pim.sql.parser.php @@ -0,0 +1,55 @@ + \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/classes/class.pim.plugin.dummy.php b/conlite/plugins/pluginmanager/classes/class.pim.plugin.dummy.php new file mode 100644 index 0000000..20e0c8b --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/class.pim.plugin.dummy.php @@ -0,0 +1,34 @@ + \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/classes/class.pim.plugin.handler.php b/conlite/plugins/pluginmanager/classes/class.pim.plugin.handler.php new file mode 100644 index 0000000..82e59e6 --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/class.pim.plugin.handler.php @@ -0,0 +1,271 @@ +_oDomDocument = new DOMDocument(); + $this->_oDomDocument->preserveWhiteSpace = FALSE; + } + + /** + * + * @param int $iPluginId + * @return boolean + */ + public function loadPluginFromDb($iPluginId) { + $this->_oPlugin = new pimPlugin($iPluginId); + if ($this->_oPlugin->isLoaded()) { + $this->_iPluginId = $this->_oPlugin->get('idplugin'); + $this->_sPluginPath = cRegistry::getBackendPath() + . cRegistry::getConfigValue('path', 'plugins') + . $this->_oPlugin->get('folder') + . "/"; + return TRUE; + } + $this->_oPlugin = NULL; + return FALSE; + } + + /** + * + * @param string $sPluginFolderName + * @return boolean + */ + public function installPlugin($sPluginFolderName) { + $iNewPluginId = 0; + if (empty($sPluginFolderName)) { + return FALSE; + } + $pluginPath = cRegistry::getBackendPath() + . cRegistry::getConfigValue('path', 'plugins') + . $sPluginFolderName + . "/"; + + if (is_null($this->getCfgXmlObject())) { + $sPiCfg = $pluginPath . 'cl_plugin.xml'; + if (is_dir($pluginPath) && file_exists($sPiCfg)) { + $this->loadXmlFile($sPiCfg); + } else { + return FALSE; + } + } + + $oPluginInstaller = new pimSetupPluginInstall(); + $oPluginInstaller->setXsdFile($this->_xsd); + $oPluginInstaller->setXmlObject($this->getCfgXmlObject()); + $oPluginInstaller->setPluginPath($pluginPath); + $this->_iPluginId = $oPluginInstaller->installPlugin(); + if ($this->_iPluginId > 0) { + return TRUE; + } + return FALSE; + } + + /** + * + * @param string $sHandleSql + * @return boolean + */ + public function uninstallPlugin($sHandleSql) { + $oPluginUninstall = new pimSetupPluginUninstall(); + $oPluginUninstall->setPluginPath($this->_sPluginPath); + return $oPluginUninstall->uninstallPlugin($this->_iPluginId, $sHandleSql); + } + + /** + * + * @return int + */ + public function getPluginId() { + return $this->_iPluginId; + } + + /** + * + * @param string $sFile + * @return boolean + */ + public function loadXmlFile($sFile) { + $this->_oDomDocument->load($sFile); + if ($this->_validateXml()) { + $this->_oPiXml = simplexml_load_string($this->_oDomDocument->C14N()); + } + return (is_a($this->_oPiXml, "SimpleXMLElement")) ? TRUE : FALSE; + } + + /** + * + * @return object|null + */ + public function getCfgXmlObject() { + if (is_object($this->_oPiXml)) { + return $this->_oPiXml; + } + return NULL; + } + + /** + * + * @return array + */ + public function getPiGeneralArray() { + $aGeneral = array(); + if (is_object($this->_oPiXml)) { + $aGeneral = $this->_xml2Array($this->_oPiXml->general); + if($aDependencies = $this->_getDepencyArray()) { + foreach ($aDependencies as $aDependency) { + + } + } + $aGeneral['dependencies'] = print_r($aDependencies, TRUE); + } + return $aGeneral; + } + + /** + * Returns generated list entry (li) for plugin or empty string + * + * @param int $iPluginId + * @return string + */ + public function getInfoInstalled($iPluginId) { + $oPlugin = new pimPlugin($iPluginId); + if ($oPlugin->isLoaded()) { + $oView = new pimView(); + $oView->setMultiVariables($oPlugin->toArray()); + $aLang = array( + 'LANG_FOLDERNAME' => i18n("Foldername", "pluginmanager"), + 'LANG_AUTHOR' => i18n("Author", "pluginmanager"), + 'LANG_CONTACT' => i18n("Contact", "pluginmanager"), + 'LANG_LICENSE' => i18n("License", "pluginmanager"), + 'LANG_INSTALLED' => i18n('Installed since', 'pluginmanager'), + 'LANG_DEPENDENCIES' => i18n("Dependencies", "pluginmanager"), + 'LANG_WRITEABLE' => i18n("Writable", "pluginmanager"), + 'LANG_INSTALL' => i18n("Install", "pluginmanager"), + 'LANG_REMOVE' => i18n("Remove", "pluginmanager"), + 'LANG_UPDATE' => i18n('Update', 'pluginmanager'), + 'LANG_UPDATE_CHOOSE' => i18n('Please choose your new file', 'pluginmanager'), + 'LANG_UPDATE_UPLOAD' => i18n('Update', 'pluginmanager'), + 'LANG_REMOVE_SQL' => i18n('Execute uninstall.sql', 'pluginmanager') + ); + $oView->setMultiVariables($aLang); + // nav sub placeholder, @Todo: fill with content + $oView->set('s', 'NAVSUB', ''); + // enable / disable functionality + $activeStatus = $oPlugin->get('active'); + $oButton = new cHTMLButton('toggle_active'); + $oButton->setID("but-toggle-plugin-" . $oPlugin->get("idplugin")); + $oButton->setClass("pimImgBut"); + $oButton->setMode('image'); + $oButtonLabel = new cHTMLLabel("placeholder", $oButton->getID()); + $oButtonLabel->setClass("pimButLabel"); + if ($activeStatus == 1) { + $oButton->setAlt("Click to toggle status"); + $oButton->setImageSource('images/online.gif'); + $oButtonLabel->setLabelText(i18n("Plugin is active", "pluginmanager")); + } else { + $oButton->setImageSource('images/offline.gif'); + $oButtonLabel->setLabelText(i18n("Plugin not active", "pluginmanager")); + } + $oView->set('s', 'BUT_ACTIVESTATUS', $oButton->render() . ' ' . $oButtonLabel->render()); + + // update button - not used right now + $oView->set('s', 'BUT_UPDATE_PLUGIN', ''); + + // uninstall + $oDelBut = new cHTMLButton('uninstall_plugin'); + $oDelBut->setImageSource('images/but_cancel.gif'); + $oDelBut->setID("but-uninstall-plugin-" . $oPlugin->get("idplugin")); + $oDelBut->setClass("pimImgBut"); + $oDelBut->setMode('image'); + $oDelSqlCheckbox = new cHTMLCheckbox("delete_sql", "TRUE"); + $oDelSqlCheckbox->setStyle("display: inline-block;"); + $sDelSqlTxt = " " . sprintf(i18n("(%s remove database tables)", "pluginmanager"), $oDelSqlCheckbox->toHtml(FALSE)); + $oDelButLabel = new cHTMLLabel("placeholder", $oDelBut->getID()); + $oDelButLabel->setClass("pimButLabel"); + $oDelButLabel->setLabelText(i18n("Uninstall Plugin", "pluginmanager") . $sDelSqlTxt); + $oView->set('s', 'BUT_UNINSTALL_PLUGIN', $oDelBut->render() . ' ' . $oDelButLabel->render()); + + $oView->setTemplate('pi_manager_installed_plugins.html'); + return $oView->getRendered(1); + } + return ''; + } + + protected function _getDepencyArray() { + $aDependencies = array(); + $aAttributes = array(); + $iCountDependencies = count($this->_oPiXml->dependencies); + if($iCountDependencies > 0) { + for ($i = 0; $i < $iCountDependencies; $i++) { + $sPluginName = cSecurity::escapeString($this->_oPiXml->dependencies[$i]->depend); + foreach ($this->_oPiXml->dependencies[$i]->depend->attributes() as $sKey => $sValue) { + $aAttributes[$sKey] = cSecurity::escapeString($sValue); + } + $aDependencies[$i]["name"] = $sPluginName; + $aDependencies[$i] = array_merge($aDependencies[$i],$aAttributes); + } + return $aDependencies; + } + + return FALSE; + } + + /** + * + * @return boolean + * @throws pimXmlStructureException + */ + private function _validateXml() { + if ($this->_oDomDocument->schemaValidate($this->_xsd)) { + $this->_bValid = true; + return true; + } else { + return FALSE; + } + } + + /** + * + * @param xml $xml + * @return array + */ + private function _xml2Array($xml) { + $string = json_encode($xml); + $array = json_decode($string, true); + return $array; + } + +} diff --git a/conlite/plugins/pluginmanager/classes/class.pim.plugin.php b/conlite/plugins/pluginmanager/classes/class.pim.plugin.php new file mode 100644 index 0000000..2ab1531 --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/class.pim.plugin.php @@ -0,0 +1,34 @@ + + */ +class pimPluginCollection extends ItemCollection { + + public function __construct() { + global $cfg; + parent::__construct($cfg['tab']['plugins'], 'idplugin'); + $this->_setItemClass("pimPlugin"); + } +} + +class pimPlugin extends Item { + + public function __construct($mId = false) { + global $cfg; + parent::__construct($cfg['tab']['plugins'], 'idplugin'); + + if ($mId !== false) { + $this->loadByPrimaryKey($mId); + } + } +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/classes/class.pim.plugin.relation.php b/conlite/plugins/pluginmanager/classes/class.pim.plugin.relation.php new file mode 100644 index 0000000..fe37081 --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/class.pim.plugin.relation.php @@ -0,0 +1,58 @@ + + */ +class pimPluginRelationCollection extends ItemCollection { + + const REL_AREA = 'area'; + const REL_ACTION = 'action'; + const REL_NAVS = 'navs'; + const REL_CTYPE = 'ctype'; + + public function __construct() { + global $cfg; + parent::__construct($cfg['tab']['plugins_rel'], 'idpluginrelation'); + $this->_setItemClass("pimPluginRelation"); + } + + public function create($idItem, $idPlugin, $type) { + // create a new entry + $item = parent::create(); + $item->set('iditem', $idItem); + $item->set('idplugin', $idPlugin); + $item->set('type', $type); + + $item->store(); + return $item; + } + + public function getRelations($idPlugin, $type=NULL) { + return; + } + + public function deleteRelations($idPlugin, $type=NULL) { + return; + } +} + +class pimPluginRelation extends Item { + + public function __construct($mId = false) { + global $cfg; + parent::__construct($cfg['tab']['plugins_rel'], 'idpluginrelation'); + + if ($mId !== false) { + $this->loadByPrimaryKey($mId); + } + } +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/classes/class.pim.view.php b/conlite/plugins/pluginmanager/classes/class.pim.view.php new file mode 100644 index 0000000..9c3183c --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/class.pim.view.php @@ -0,0 +1,83 @@ + + * @license http://www.contenido.org/license/LIZENZ.txt + * @link http://www.4fb.de + * @link http://www.contenido.org + * @since file available since contenido release 4.8.7 + * + * {@internal + * created 2008-03-17 + * modified 2008-07-04, Frederic Schneider, add security fix and tpl settings + * + * $Id: class.pim.view.php 11 2015-07-14 12:34:24Z oldperl $: + * }} + * + */ + +if(!defined('CON_FRAMEWORK')) { + die('Illegal call'); +} + +class pimView extends Template{ + + protected $sPathToTpl; + protected $bIsGenerated; + protected $_sTplPath; + + public function __construct($tags = false, $parser = false) { + $this->reset(); + $this->set('s', 'SESSID', cRegistry::getSessionId()); + $this->bIsGenerated = false; + $this->_sTplPath = dirname(dirname(__FILE__)).DIRECTORY_SEPARATOR."templates".DIRECTORY_SEPARATOR; + parent::__construct($tags, $parser); + } + + public function setTemplate($sTplName) { + $this->sPathToTpl = $this->_sTplPath.$sTplName; + } + + public function setMultiVariables($aVariables) { + if(is_array($aVariables)) { + foreach($aVariables as $sName=>$sContent) { + if(is_numeric($sName)) { + continue; + } + $this->setVariable($sContent, strtoupper($sName)); + } + } + } + + public function setVariable($sVariable, $sName = '') { + if(empty($sName)) { + $sName = strtoupper($$sVariable); + } + $this->set('s', $sName, $sVariable); + } + + public function getRendered($mode = '') { + $this->bIsGenerated = true; + return $this->generate($this->sPathToTpl, $mode); + } + + public function __destruct() { + if ($this->bIsGenerated === false) { + $this->generate($this->sPathToTpl, true, false); + } + } + +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/classes/class.pluginmanager.ajax.php b/conlite/plugins/pluginmanager/classes/class.pluginmanager.ajax.php new file mode 100644 index 0000000..99239b0 --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/class.pluginmanager.ajax.php @@ -0,0 +1,104 @@ +isLoaded()) { + $iCurrentStat = (int) $oPlugin->get('active'); + $iNewStat = ($iCurrentStat == 1)?0:1; + $oPlugin->set('active', $iNewStat); + if($oPlugin->store()) { + $sString = "Ok:".$iNewStat; + if($iNewStat) { + $sString .= ":".i18n("Plugin is active", "pluginmanager"); + } else { + $sString .= ":".i18n("Plugin not active", "pluginmanager"); + } + break; + } + } + $sString = "Error:no changes! (Err21)"; + break; + + // save sortorder of plugins + case 'pim_save_sort': + parse_str($_REQUEST['plugins'], $aPlugins); + //print_r($aPlugins['plugin']); + if(is_array($aPlugins['plugin']) && count($aPlugins['plugin']) > 0) { + foreach($aPlugins['plugin'] as $sortorder=>$pluginid) { + $oPlugin = new pimPlugin($pluginid); + $oPlugin->set('executionorder', $sortorder); + $oPlugin->store(); + } + } + $sString = "Ok:executionorder saved"; + break; + + // install plugin with existing source in plugin dir + case 'pim_install': + //sleep(3); + $iNewPluginId = 0; + $sPluginPath = cRegistry::getBackendPath() + .cRegistry::getConfigValue('path', 'plugins') + .Contenido_Security::escapeDB($_POST['plugin_folder']).DIRECTORY_SEPARATOR; + + if(is_dir($sPluginPath) && is_readable($sPluginPath."cl_plugin.xml")) { + $oPluginHandler = new pimPluginHandler(); + if($oPluginHandler->loadXmlFile($sPluginPath."cl_plugin.xml")) { + if($oPluginHandler->installPlugin(Contenido_Security::escapeDB($_POST['plugin_folder']))) { + $iNewPluginId = $oPluginHandler->getPluginId(); + if($iNewPluginId > 0) { + $sString = "Ok:".$iNewPluginId.":Plugin successfully installed!"; + } else { + $sString = "Error:".$iNewPluginId.":Plugin not installed! (Err10)"; + } + } else { + $sString = "Error:0:Plugin not installed! (Err12)"; + } + } else { + $sString = "Error:0:Plugin xml-file missing or not correct! (Err13)"; + } + break; + } + $sString = "Error:0:Plugin folder missing or no readable xml-file found! (Err14)"; + break; + + // return info about installed plugin + case 'pim_get_info_installed': + $oPluginHandler = new pimPluginHandler(); + $sString = $oPluginHandler->getInfoInstalled((int) $_POST['plugin_id']); + break; + + //if action is unknown generate error message + default: + $sString = "Unknown Ajax Action! (Err01)"; + break; + } + return $sString; + } + +} diff --git a/conlite/plugins/pluginmanager/classes/exeptions/class.pim.exeption.php b/conlite/plugins/pluginmanager/classes/exeptions/class.pim.exeption.php new file mode 100644 index 0000000..76397ad --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/exeptions/class.pim.exeption.php @@ -0,0 +1,46 @@ + cRegistry::getConfigValue('path', 'data') + . 'logs/exception.log' + )); + $this->_logger = new cLog($writer); + + // determine if exception should be logged + if (false === $this->_log_exception + && !is_null(cRegistry::getConfigValue('debug', 'log_exeptions'))) { + $this->_log_exception = cRegistry::getConfigValue('debug', 'log_exeptions'); + } + + // log the exception if it should be logged + if (true === $this->_log_exception) { + $this->log(); + } + } + + public function log() { + // construct the log message with all infos and write it via the logger + $logMessage = get_class($this) . ' thrown at line ' . $this->getLine() . ' of file ' . $this->getFile() . ".\r\n"; + $logMessage .= 'Exception message: ' . $this->getMessage() . "\r\n"; + $logMessage .= "Call stack:\r\n"; + $logMessage .= $this->getTraceAsString(); + $logMessage .= "\r\n"; + $this->_logger->log($logMessage); + } +} + +class pimXmlStructureException extends pimExeption { + +} \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/classes/plugin/class.plugin.handler.abstract.php b/conlite/plugins/pluginmanager/classes/plugin/class.plugin.handler.abstract.php new file mode 100644 index 0000000..28be1a8 --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/plugin/class.plugin.handler.abstract.php @@ -0,0 +1,67 @@ +getFileName())); + } + return self::$_piPath; + } +} diff --git a/conlite/plugins/pluginmanager/classes/plugin/interface.plugins.php b/conlite/plugins/pluginmanager/classes/plugin/interface.plugins.php new file mode 100644 index 0000000..1e8fb24 --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/plugin/interface.plugins.php @@ -0,0 +1,19 @@ + 'idaction', + 'area' => 'idarea', + 'files' => 'idfile', + 'framefiles' => 'idframefile', + 'nav_main' => 'idnavm', + 'nav_sub' => 'idnavs', + 'plugins' => 'idplugin' + ); + + /** + * holds db object + * @var DB_ConLite + */ + protected $_oDb; + protected $_aSql = array(); + protected $_iPiId = 0; + protected $_iCntQueries = 0; + protected $_iClient; + protected $_aRelations; + + /** + * + * @var pimPluginCollection + */ + protected $_PimPluginCollection; + + /** + * + * @var pimPluginRelationCollection + */ + protected $_PimPluginRelationCollection; + protected $_sPluginPath; + + public function __construct() { + $this->_oDb = new DB_ConLite(); + $this->_iClient = cRegistry::getClientId(); + $this->_xmlParseIntoStruct(); + + $this->_PimPluginCollection = new pimPluginCollection(); + $this->_PimPluginRelationCollection = new pimPluginRelationCollection(); + + //print_r($this->_getAttrForTag("area")); + } + + public function setPluginPath($sPath) { + $this->_sPluginPath = $sPath; + } + + public function getPluginPath() { + return $this->_sPluginPath; + } + + /** + * + * @return boolean + */ + public function doQueries() { + if (!is_array($this->_aSql) || count($this->_aSql) <= 0) { + return TRUE; + } + $iQueries = count($this->_aSql); + + if ($iQueries > 0 && is_a($this->_oDb, "DB_ConLite")) { + foreach ($this->_aSql as $sSql) { + try { + $this->_oDb->query($sSql); + } catch (Exception $exc) { + self::error($exc->getTraceAsString()); + } + $this->_iCntQueries++; + } + if ($iQueries == $this->_iCntQueries) { + return TRUE; + } + } + return FALSE; + } + + public function undoQueries() { + + } + + public function setXmlObject($oXml, $bSplit = TRUE) { + if (is_object($oXml)) { + $this->_oXml = & $oXml; + } + + if ($bSplit) { + $this->_setXml(); + } + } + + public function setXsdFile($sFile) { + $this->_sXsdPath = $sFile; + } + + /** + * Returns next id for given table + * + * @param string $sTable + * @return int the next usable table id + */ + protected function _getNextId($sTable) { + cInclude("includes", "functions.database.php"); + dbUpdateSequence(cRegistry::getConfigValue("tab", "sequence"), cRegistry::getConfigValue('tab', $sTable), cRegistry::getDb()); + + $iNextId = $this->_oDb->nextid(cRegistry::getConfigValue('tab', $sTable)); + // id must be over 10.000 + if ($iNextId < 10000) { + $iNextId = 10000; + } + + // added ten + $iNextId = $iNextId + 10; + + // how long is the number? + $iResultStrlen = strlen($iNextId); + + // removed the last number + $iNextId = substr($iNextId, 0, $iResultStrlen - 1); + + return Contenido_Security::toInteger($iNextId . 0); // last number is always a zero + } + + protected function _getAttrForTag($sTag) { + foreach ($this->_aXmlDefaultValues as $Key => $aValue) { + if ($aValue['tag'] === strtoupper($sTag) && $aValue['type'] === "complete") { + if (isset($aValue['attributes']) && is_array($aValue['attributes'])) { + return array_change_key_case($aValue['attributes']); + } + } + } + return FALSE; + } + + protected function _getPluginSql() { + $sSqlFile = $this->_sPluginPath . static::SQL_FILE; + if (file_exists($sSqlFile) && is_readable($sSqlFile)) { + $this->_aSql = pimSqlParser::parse(file_get_contents($sSqlFile)); + } else if (!is_array($this->_aSql)) { + $this->_aSql = array(); + } + } + + protected static function error($sMessage, $iPiId = NULL) { + if (!is_null($iPiId)) { + $oUnInstall = new pimSetupPluginUninstall(); + $oUnInstall->uninstallPlugin($iPiId, ''); + } + print "Error:0:" . $sMessage; + die(); + } + + protected function _getRelations() { + $aTmpArray = array(); + $this->_PimPluginRelationCollection->setWhere('idplugin', $this->_iPiId); + $this->_PimPluginRelationCollection->query(); + if ($this->_PimPluginRelationCollection->count() > 0) { + while ($oPluginRelation = $this->_PimPluginRelationCollection->next()) { + if (isset($aTmpArray[$oPluginRelation->get('type')]) && is_array($aTmpArray[$oPluginRelation->get('type')])) { + array_push($aTmpArray[$oPluginRelation->get('type')], $oPluginRelation->get('iditem')); + } else { + $aTmpArray[$oPluginRelation->get('type')] = array($oPluginRelation->get('iditem')); + } + } + $this->_aRelations = $aTmpArray; + unset($aTmpArray); + } + } + + protected function _deleteRelations() { + $iDeletetRelations = $this->_PimPluginRelationCollection->deleteByWhereClause("idplugin = " . $this->_iPiId); + } + + protected function _deleteRelationEntries() { + $oDb = new DB_ConLite(); + foreach ($this->_aRelations as $sType => $aIds) { + $sSQL = 'DELETE FROM ' . cRegistry::getConfigValue('tab', $sType) . ' WHERE ' . $this->_aTables[$sType] . ' IN (' . implode(',', $aIds) . ')'; + if ($oDb->query($sSQL) == FALSE) { + return FALSE; + } + } + unset($oDb); + return TRUE; + } + + protected function _updateSortOrder() { + if(!isset($_REQUEST['new_position'])) { + return 0; + } + + $oPluginColl = new pimPluginCollection(); + $oPluginColl->setWhere("executionorder", (int) $_REQUEST['new_position'], ">="); + $oPluginColl->query(); + if($oPluginColl->count() > 0) { + /* @var $oPlugin cApiPlugin */ + while ($oPlugin = $oPluginColl->next()) { + $iOrder = $oPlugin->get("executionorder"); + $oPlugin->set("executionorder", $iOrder++); + $oPlugin->store(); + } + } + return (int) $_REQUEST['new_position']; + } + + /** + * Set temporary xml content to static variables + * + * @param string $xml + */ + private function _setXml() { + //simplexml_tree($this->_oXml); + // General plugin informations + self::$XmlGeneral = $this->_oXml->general; + + // Plugin requirements + self::$XmlRequirements = $this->_oXml->requirements; + + // Plugin dependencies + self::$XmlDependencies = $this->_oXml->dependencies; + + // CONTENIDO areas: *_area + self::$XmlArea = $this->_oXml->conlite->areas; + + // CONTENIDO actions: *_actions + self::$XmlActions = $this->_oXml->conlite->actions; + + // CONTENIDO frames: *_frame_files and *_files + self::$XmlFrames = $this->_oXml->conlite->frames; + + // CONTENIDO main navigations: *_nav_main + self::$XmlNavMain = $this->_oXml->conlite->nav_main; + + // CONTENIDO sub navigations: *_nav_sub + self::$XmlNavSub = $this->_oXml->conlite->nav_sub; + + // CONTENIDO Content Types: *_type + self::$XmlContentType = $this->_oXml->content_types; + } + + private function _xmlParseIntoStruct() { + $sData = implode("", file($this->_xmlDefault)); + $oParser = xml_parser_create(); + xml_parse_into_struct($oParser, $sData, $this->_aXmlDefaultValues, $this->_aXmlDefaultIndex); + xml_parser_free($oParser); + } + + private function _updateSequence($table = false) { + global $db, $cfg; + if (!$table) { + $sql = "SHOW TABLES"; + $db->query($sql); + while ($db->next_record()) { + dbUpdateSequence($cfg['sql']['sqlprefix'] . "_sequence", $db->f(0)); + } + } else { + dbUpdateSequence($cfg['sql']['sqlprefix'] . "_sequence", $table); + } + } +} diff --git a/conlite/plugins/pluginmanager/classes/setup/class.pim.setup.plugin.install.php b/conlite/plugins/pluginmanager/classes/setup/class.pim.setup.plugin.install.php new file mode 100644 index 0000000..64db36b --- /dev/null +++ b/conlite/plugins/pluginmanager/classes/setup/class.pim.setup.plugin.install.php @@ -0,0 +1,427 @@ +_initInstalledAreasArray(); + } + + public function installPlugin() { + if (is_null($this->_oXml)) { + cWarning(__FILE__, __LINE__, "installPlugin: No plugin xml loaded!"); + return 0; + } + $this->_installCheckUuid(); + $this->_installCheckRequirements(); + + $oPiColl = new pimPluginCollection(); + $this->_oPlugin = $oPiColl->createNewItem(); + if ($this->_oPlugin->isLoaded()) { + $this->_iPiId = $this->_oPlugin->get('idplugin'); + $this->_insertDbEntries(); + $this->_getPluginSql(); + if ($this->doQueries()) { + $this->_oPlugin->set('idclient', $this->_iClient, FALSE); + $this->_oPlugin->set('name', Contenido_Security::escapeDB(self::$XmlGeneral->plugin_name)); + $this->_oPlugin->set('description', Contenido_Security::escapeDB(self::$XmlGeneral->description)); + $this->_oPlugin->set('author', Contenido_Security::escapeDB(self::$XmlGeneral->author)); + $this->_oPlugin->set('copyright', Contenido_Security::escapeDB(self::$XmlGeneral->copyright)); + $this->_oPlugin->set('mail', Contenido_Security::escapeDB(self::$XmlGeneral->mail)); + $this->_oPlugin->set('website', Contenido_Security::escapeDB(self::$XmlGeneral->website)); + $this->_oPlugin->set('version', Contenido_Security::escapeDB(self::$XmlGeneral->version)); + $this->_oPlugin->set('folder', Contenido_Security::escapeDB(self::$XmlGeneral->plugin_foldername)); + $this->_oPlugin->set('uuid', Contenido_Security::escapeDB(self::$XmlGeneral->uuid)); + $this->_oPlugin->set('executionorder', $this->_updateSortOrder(), FALSE); + $this->_oPlugin->set('installed', date('Y-m-d H:i:s'), FALSE); + $this->_oPlugin->set('active', (int) self::$XmlGeneral['active'], FALSE); + + if ($this->_oPlugin->store()) { + //echo "stored: ".$this->_iPiId; + return $this->_iPiId; + } + } else { + $this->_removeEmptyPlugin(); + } + } + // something went wrong, return 0 + return 0; + } + + protected function _insertDbEntries() { + $this->_addAreas(); + $this->_addActions(); + $this->_addFrames(); + $this->_addNavMain(); + $this->_addNavSub(); + } + + protected function _addRelation($iIdItem, $sType) { + $oPluginRelation = $this->_PimPluginRelationCollection->createNewItem(); + $oPluginRelation->set('iditem', $iIdItem, FALSE); + $oPluginRelation->set('idplugin', $this->_iPiId, FALSE); + $oPluginRelation->set('type', $sType); + $oPluginRelation->store(); + unset($oPluginRelation); + } + + private function _addAreas() { + $aAttributes = array(); + $aDefaultAttr = array( + 'menuless' => 0, + 'parent' => 0, + 'relevant' => 1 + ); + + $iCountAreas = count(self::$XmlArea->area); + if ($iCountAreas > 0) { + $oAreaColl = new cApiAreaCollection(); + for ($i = 0; $i < $iCountAreas; $i++) { + $sName = Contenido_Security::escapeDB(self::$XmlArea->area[$i], $this->oDb); + // build attributes + foreach (self::$XmlArea->area[$i]->attributes() as $sKey => $sValue) { + $aAttributes[$sKey] = (string) $sValue; + } + $aAttributes = array_merge($aDefaultAttr, $aAttributes); + /* @var $oArea cApiArea */ + $oArea = $oAreaColl->createNewItem($this->_getNextId("area")); + $oArea->set('parent_id', Contenido_Security::escapeDB($aAttributes['parent'], $this->oDb)); + $oArea->set('name', $sName); + $oArea->set('menuless', Contenido_Security::toInteger($aAttributes['menuless'])); + $oArea->set('relevant', 1, FALSE); + $oArea->set('online', 1, FALSE); + if ($oArea->store()) { + $iIdItem = $oArea->get($oArea->primaryKey); + $this->_aAreas[$sName] = $iIdItem; + $this->_aInstalledAreas[] = $sName; + $this->_addRelation($iIdItem, 'area'); + } + } + } + } + + private function _addActions() { + $aAttributes = array(); + $aDefaultAttr = array( + 'relevant' => 1 + ); + + $iCountActions = count(self::$XmlActions->action); + if ($iCountActions > 0) { + $oActionColl = new cApiActionCollection(); + for ($i = 0; $i < $iCountActions; $i++) { + $sName = Contenido_Security::escapeDB(self::$XmlActions->action[$i], $this->_oDb); + foreach (self::$XmlActions->action[$i]->attributes() as $sKey => $sValue) { + $aAttributes[$sKey] = cSecurity::escapeString($sValue); + } + $aAttributes = array_merge($aDefaultAttr, array_filter($aAttributes)); + if (!in_array($aAttributes['area'], $this->_aInstalledAreas)) { + parent::error(sprintf(i18n('Defined area %s are not found on your ConLite installation. Please contact your plugin author.', 'pluginmanager'), $aAttributes['area'])); + } + /* @var $oAction cApiAction */ + $oAction = $oActionColl->createNewItem($this->_getNextId("actions")); + if ($oAction->isLoaded()) { + $oAction->set("idarea", $this->_getIdForArea($aAttributes['area'])); + $oAction->set("name", $sName); + $oAction->set("code", ''); + $oAction->set("location", ''); + $oAction->set("relevant", (int) $aAttributes['relevant']); + if ($oAction->store()) { + $this->_addRelation($oAction->get('idaction'), 'actions'); + } + } + //$oAction = $oActionColl->create($aAttributes['area'], $sName, '', '', $aAttributes['relevant']); + //$this->_addRelation($oAction->get('idaction'), 'actions'); + } + unset($oActionColl); + unset($oAction); + } + } + + private function _addFrames() { + $aAttributes = array(); + $aDefaultAttr = array(); + + $iCountFrames = count(self::$XmlFrames->frame); + if ($iCountFrames > 0) { + $oFrameFileColl = new cApiFrameFileCollection(); + $oFileColl = new cApiFileCollection(); + + for ($i = 0; $i < $iCountFrames; $i++) { + // Build attributes with security checks + foreach (self::$XmlFrames->frame[$i]->attributes() as $sKey => $sValue) { + $aAttributes[$sKey] = cSecurity::escapeString($sValue); + } + $aAttributes = array_merge($aDefaultAttr, array_filter($aAttributes)); + + // Check for valid area + if (!array_key_exists($aAttributes['area'], $this->_aAreas) && !in_array($aAttributes['area'], $this->_aInstalledAreas)) { + parent::error(sprintf(i18n('Defined area %s are not found on your ConLite installation. Please contact your plugin author.', 'pluginmanager'), $aAttributes['area'])); + } + + /* @var $oFile cApiFile */ + $oFile = $oFileColl->createNewItem($this->_getNextId("files")); + if ($oFile->isLoaded()) { + $this->_addRelation($oFile->get('idfile'), 'files'); + + $oFile->set("idarea", $this->_getIdForArea($aAttributes['area'])); + $oFile->set("filename", $aAttributes['name']); + $oFile->set("filetype", cSecurity::escapeString($aAttributes['filetype'])); + + if ($oFile->store()) { + if (!empty($aAttributes['frameId'])) { + /* @var $oFrameFile cApiFrameFile */ + $oFrameFile = $oFrameFileColl->createNewItem($this->_getNextId("framefiles")); + if ($oFrameFile->isLoaded()) { + $this->_addRelation($oFrameFile->get('idframefile'), 'framefiles'); + + $oFrameFile->set("idarea", $this->_getIdForArea($aAttributes['area'])); + $oFrameFile->set("idframe", (int) $aAttributes['frameId'], FALSE); + $oFrameFile->set("idfile", (int) $oFile->get('idfile'), FALSE); + + $oFrameFile->store(); + } + } + } + } + } + unset($oFrameFileColl); + unset($oFileColl); + unset($oFile); + unset($oFrameFile); + } + } + + private function _addNavMain() { + $aAttributes = array(); + + $iCountNavMain = count(self::$XmlNavMain->nav); + if ($iCountNavMain > 0) { + $oNavMainColl = new cApiNavMainCollection(); + + for ($i = 0; $i < $iCountNavMain; $i++) { + $sLocation = cSecurity::escapeString(self::$XmlNavMain->nav[$i]); + if (empty($sLocation)) { + parent::error(i18n('There seem to be an empty main navigation entry in plugin.xml. Please contact your plugin author.', 'pluginmanager'), $this->_iPiId); + } + + // Build attributes with security checks + foreach (self::$XmlNavMain->nav[$i]->attributes() as $sKey => $sValue) { + $aAttributes[$sKey] = cSecurity::escapeString($sValue); + } + + /* @var $oNavMain cApiNavMain */ + $oNavMain = $oNavMainColl->createNewItem($this->_getNextId("nav_main")); + if($oNavMain->isLoaded()) { + $this->_addRelation($oNavMain->get('idnavm'), 'nav_main'); + + $oNavMain->set("location", $sLocation, FALSE); + $oNavMain->set("name", cSecurity::escapeString($aAttributes['name'])); + + $oNavMain->store(); + } + } + unset($oNavMainColl); + unset($oNavMain); + } + } + + private function _addNavSub() { + $aAttributes = array(); + $this->_initInstalledNavMainArray(); + $iCountNavSub = count(self::$XmlNavSub->nav); + + if ($iCountNavSub > 0) { + $oNavSubColl = new cApiNavSubCollection(); + + for ($i = 0; $i < $iCountNavSub; $i++) { + $sLocation = cSecurity::escapeString(self::$XmlNavSub->nav[$i]); + + if (empty($sLocation)) { + parent::error(i18n('There seem to be an empty sub navigation entry in plugin.xml. Please contact your plugin author.', 'pluginmanager'), $this->_iPiId); + } + + // Build attributes with security checks + foreach (self::$XmlNavSub->nav[$i]->attributes() as $sKey => $sValue) { + $aAttributes[$sKey] = cSecurity::escapeString($sValue); + } + /* @var $oNavSub cApiNavSub */ + $oNavSub = $oNavSubColl->createNewItem($this->_getNextId("nav_sub")); + if ($oNavSub->isLoaded()) { + $this->_addRelation($oNavSub->get('idnavs'), 'nav_sub'); + + $oNavSub->set("idnavm", $this->_getIdForNavMain($aAttributes['navm'])); + $oNavSub->set("idarea", $this->_getIdForArea($aAttributes['area'])); + $oNavSub->set("level", (int) $aAttributes['level']); + $oNavSub->set("location", $sLocation, FALSE); + $oNavSub->set("online", 1, FALSE); + + $oNavSub->store(); + } + } + unset($oNavSubColl); + unset($oNavSub); + } + } + + private function _removeEmptyPlugin() { + if (empty($this->_iPiId)) { + return FALSE; + } + $this->_getRelations(); + if (count($this->_aRelations) > 0) { + $this->_deleteRelationEntries(); + $this->_deleteRelations(); + } + $this->_PimPluginCollection->delete($this->_iPiId); + } + + /** + * Check uuId: You can install a plugin only for one time + */ + private function _installCheckUuid() { + $this->_PimPluginCollection->setWhere('uuid', self::$XmlGeneral->uuid); + $this->_PimPluginCollection->query(); + if ($this->_PimPluginCollection->count() > 0) { + parent::error(i18n('You can install this plugin only for one time.', 'pluginmanager')); + } + } + + private function _installCheckRequirements() { + + // Check min ConLite version + if (version_compare(CL_VERSION, self::$XmlRequirements->conlite->attributes()->minversion, '<')) { + parent::error(sprintf(i18n('You have to install ConLite %s or higher to install this plugin!', 'pluginmanager'), self::$XmlRequirements->conlite->attributes()->minversion)); + } + + // Check max ConLite version + if (self::$XmlRequirements->conlite->attributes()->maxversion) { + if (version_compare(CL_VERSION, self::$XmlRequirements->conlite->attributes()->maxversion, '>')) { + parent::error(sprintf(i18n('Your current ConLite version is to new - max ConLite version %s', 'pluginmanager'), self::$XmlRequirements->conlite->attributes()->maxversion)); + } + } + + // Check PHP version + if (version_compare(phpversion(), self::$XmlRequirements->attributes()->php, '<')) { + parent::error(sprintf(i18n('You have to install PHP %s or higher to install this plugin!', 'pluginmanager'), self::$XmlRequirements->attributes()->php)); + } + + /* @todo check and implement other requirement checks + // Check extensions + if (count(parent::$XmlRequirements->extension) != 0) { + + for ($i = 0; $i < count(parent::$XmlRequirements->extension); $i++) { + + if (!extension_loaded(parent::$XmlRequirements->extension[$i]->attributes()->name)) { + parent::error(sprintf(i18n('The plugin could not find the PHP extension %s. Because this is required by the plugin, it can not be installed.', 'pim'), parent::$XmlRequirements->extension[$i]->attributes()->name)); + } + } + } + + // Check classes + if (count(parent::$XmlRequirements->class) != 0) { + + for ($i = 0; $i < count(parent::$XmlRequirements->class); $i++) { + + if (!class_exists(parent::$XmlRequirements->class[$i]->attributes()->name)) { + parent::error(sprintf(i18n('The plugin could not find the class %s. Because this is required by the plugin, it can not be installed.', 'pim'), parent::$XmlRequirements->class[$i]->attributes()->name)); + } + } + } + + // Check functions + if (count(parent::$XmlRequirements->function) != 0) { + + for ($i = 0; $i < count(parent::$XmlRequirements->function); $i++) { + + if (!function_exists(parent::$XmlRequirements->function[$i]->attributes()->name)) { + parent::error(sprintf(i18n('The plugin could not find the function %s. Because this is required by the plugin, it can not be installed.', 'pim'), parent::$XmlRequirements->function[$i]->attributes()->name)); + } + } + } + * + */ + } + + private function _initInstalledAreasArray() { + $this->_aInstalledAreas = array(); + $oAreaColl = new cApiAreaCollection(); + $oAreaColl->select(); + //$oAreaColl->query(); + /* @var $oArea cApiArea */ + while ($oArea = $oAreaColl->next()) { + $this->_aInstalledAreas[] = $oArea->get('name'); + } + //print_r($this->_aInstalledAreas); + } + + private function _initInstalledNavMainArray() { + $this->_aInstalledNavMain = array(); + $oNavMainColl = new cApiNavMainCollection(); + $oNavMainColl->select(); + //$oNavMainColl->query(); + /* @var $oArea cApiArea */ + while ($oNavMain = $oNavMainColl->next()) { + $this->_aInstalledNavMain[$oNavMain->get('name')] = $oNavMain->get('idnavm'); + } + } + + private function _initInstalledNavSubArray() { + $this->_aInstalledNavSub = array(); + $oNavSubColl = new cApiNavSubCollection(); + $oNavMainColl->select(); + //$oNavSubColl->query(); + /* @var $oArea cApiArea */ + while ($oNavSub = $oNavSubColl->next()) { + $this->_aInstalledNavMain[$oNavSub->get('idnavsub')] = $oNavSub->get('name'); + } + } + + private function _getIdForArea($sArea) { + if (array_key_exists($sArea, $this->_aAreas)) { + return $this->_aAreas[$sArea]; + } + + if (in_array($sArea, $this->_aInstalledAreas)) { + $oArea = new cApiArea(); + $oArea->loadBy("name", $sArea); + if ($oArea->isLoaded()) { + return $oArea->get($oArea->primaryKey); + } + } + parent::error(sprintf(i18n('Defined area %s not found on your ConLite installation. Please contact your plugin author.', "pluginmanager"), $sArea), $this->_iPiId); + } + + private function _getIdForNavMain($sNavMain) { + if($sNavMain == "0") { + return $sNavMain; + } + if (array_key_exists($sNavMain, $this->_aInstalledNavMain)) { + return $this->_aInstalledNavMain[$sNavMain]; + } + parent::error(sprintf(i18n('Defined nav main %s not found on your ConLite installation. Please contact your plugin author.', "pluginmanager"), $sNavMain), $this->_iPiId); + } + + public function __destruct() { + //echo "
";
+        //print_r($this->_aAreas);
+    }
+
+}
diff --git a/conlite/plugins/pluginmanager/classes/setup/class.pim.setup.plugin.uninstall.php b/conlite/plugins/pluginmanager/classes/setup/class.pim.setup.plugin.uninstall.php
new file mode 100644
index 0000000..b52ea86
--- /dev/null
+++ b/conlite/plugins/pluginmanager/classes/setup/class.pim.setup.plugin.uninstall.php
@@ -0,0 +1,38 @@
+isLoaded()) { // plugin exists in db
+            $this->_iPiId = (int) $iIdPlugin;
+            $this->_getRelations();
+            if(is_array($this->_aRelations) && count($this->_aRelations) > 0) {
+                if($this->_deleteRelationEntries()) {
+                    $this->_deleteRelations();
+                } else {
+                    return FALSE;
+                }
+            }
+            
+            if($sDeleteSql == "delete") {
+                $this->_getPluginSql();
+            }
+            
+            if($this->doQueries()) {
+                return $this->_PimPluginCollection->delete($iIdPlugin);
+            }
+        }
+        return FALSE;
+    }
+}
\ No newline at end of file
diff --git a/conlite/plugins/pluginmanager/css/images/error.png b/conlite/plugins/pluginmanager/css/images/error.png
new file mode 100644
index 0000000000000000000000000000000000000000..296415e2f6c565b38c2bfa0edd53f0b11ffa5cf3
GIT binary patch
literal 4340
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
iOM29*SF`&`{~rK=WQCuj>qPGW0000;(@

literal 0
HcmV?d00001

diff --git a/conlite/plugins/pluginmanager/css/images/info.png b/conlite/plugins/pluginmanager/css/images/info.png
new file mode 100644
index 0000000000000000000000000000000000000000..83de6545870d0e0af384f39f59b6a05c7277a3b9
GIT binary patch
literal 4403
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

literal 0
HcmV?d00001

diff --git a/conlite/plugins/pluginmanager/css/images/success.png b/conlite/plugins/pluginmanager/css/images/success.png
new file mode 100644
index 0000000000000000000000000000000000000000..743ef89c3a48cb65d51f0efb7305be9f2beba401
GIT binary patch
literal 4447
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

literal 0
HcmV?d00001

diff --git a/conlite/plugins/pluginmanager/css/images/warning.png b/conlite/plugins/pluginmanager/css/images/warning.png
new file mode 100644
index 0000000000000000000000000000000000000000..1c6b8eb0fb39720842be86b0343adfdcbf929982
GIT binary patch
literal 4567
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
By~_Xq

literal 0
HcmV?d00001

diff --git a/conlite/plugins/pluginmanager/css/pluginmanager.css b/conlite/plugins/pluginmanager/css/pluginmanager.css
new file mode 100644
index 0000000..2239862
--- /dev/null
+++ b/conlite/plugins/pluginmanager/css/pluginmanager.css
@@ -0,0 +1,101 @@
+/**
+ *
+ * @package pluginmanager
+ * @version $Rev:$
+ * @author Ortwin Pinke
+ *
+ * $Id:$
+ */
+
+#addplugin {
+    border: 1px solid #B5B5B5;
+    border-top: 0px;
+    height: 40px;
+    padding: 8px;
+    margin-left: 10px;
+    width: 650px;
+    text-align: center;
+}
+
+.plugininfo {
+    width: 550px;
+    margin-top: 10px;
+}
+
+hr {
+    border-color: #B5B5B5;
+    line-leight: 1em;
+}
+
+ul#pimPluginsInstalled, ul#pimPluginsExtracted {
+    max-width: 550px;
+    list-style-type: none;
+    margin: 0;
+    padding: 5px;
+    margin-top: 10px;
+    background: #eee;
+}
+
+ul#pimPluginsInstalled li, ul#pimPluginsExtracted li {
+    margin: 0 5px 5px 5px;
+    padding: 5px;
+    font-size: 1.2em;
+}
+
+ul#pimPluginsInstalled li>div .pimHeadline, ul#pimPluginsExtracted li>div .pimHeadline {
+    cursor: move;
+}
+
+.ui-state-highlight {
+    height: 1.5em;
+    line-height: 1.2em;
+    background-color: #eee;
+}
+
+ul#pimPluginsInstalled li:nth-of-type(even) {
+    background-color: #dddddd;
+}
+
+.loadie {
+    position: absolute;
+    top: 0;
+    left: 0;
+    background-color: #000;
+    width: 0;
+    height: 4px;
+    -webkit-transition: width 0.5s ease-out;
+    box-shadow: 0px 1px 5px rgba(0,0,0,0.25);
+}
+
+#pimmsg {    
+    font-family:Arial, Helvetica, sans-serif; 
+    font-size:13px;
+    margin: 10px 0px;
+    padding:15px 10px 15px 50px;
+    background-repeat: no-repeat;
+    background-position: 10px center;
+    box-shadow: 10px 10px 10px grey;
+    border: 1px solid grey;
+}
+
+.info, .success, .warning, .error, .validation {}
+.info {
+    color: #00529B;
+    background-color: #BDE5F8;
+    background-image: url('images/info.png');
+}
+.success {
+    color: #4F8A10;
+    background-color: #DFF2BF;
+    background-image:url('images/success.png');
+}
+.warning {
+    color: #9F6000;
+    background-color: #FEEFB3;
+    background-image: url('images/warning.png');
+}
+.error {
+    color: #D8000C;
+    background-color: #FFBABA;
+    background-image: url('images/error.png');
+}
\ No newline at end of file
diff --git a/conlite/plugins/pluginmanager/docs/Dokumentation Contenido Plugin Manager.pdf b/conlite/plugins/pluginmanager/docs/Dokumentation Contenido Plugin Manager.pdf
new file mode 100644
index 0000000000000000000000000000000000000000..07a29aec38996a46944d709eafac6d6b66db0778
GIT binary patch
literal 104821
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

)#? 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 z ./locale/potfiles.txt +xgettext --from-code=utf-8 --keyword=i18n -f ./locale/potfiles.txt -o ./locale/pluginmanager.pot \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive.php new file mode 100644 index 0000000..091d8ad --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive.php @@ -0,0 +1,1434 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Archive.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +/** + * To have access to PEAR::isError and PEAR::raiseError + * We should probably use lazy include and remove this inclusion... + */ +require_once "PEAR.php"; + +function File_Archive_cleanCache($file, $group) +{ + $file = split('_', $file); + if (count($file) != 3) { + return false; //not a File_Archive file, keep it + } + + $name = $file[2]; + $name = urldecode($name); + + $group = $file[1]; + + //clean the cache only for files in File_Archive groups + return substr($group, 0, 11) == 'FileArchive' && + !file_exists($name); //and only if the related file no longer exists +} + +/** + * Factory to access the most common File_Archive features + * It uses lazy include, so you dont have to include the files from + * File/Archive/* directories + */ +class File_Archive +{ + function& _option($name) + { + static $container = array( + 'zipCompressionLevel' => 9, + 'gzCompressionLevel' => 9, + 'tmpDirectory' => '.', + 'cache' => null, + 'appendRemoveDuplicates' => false, + 'blockSize' => 65536, + 'cacheCondition' => false + ); + return $container[$name]; + } + /** + * Sets an option that will be used by default by all readers or writers + * Option names are case sensitive + * Currently, the following options are used: + * + * "cache" + * Instance of a Cache_Lite object used to cache some compressed + * data to speed up future compressions of files + * Default: null (no cache used) + * + * "zipCompressionLevel" + * Value between 0 and 9 specifying the default compression level used + * by Zip writers (0 no compression, 9 highest compression) + * Default: 9 + * + * "gzCompressionLevel" + * Value between 0 and 9 specifying the default compression level used + * by Gz writers (0 no compression, 9 highest compression) + * Default: 9 + * + * "tmpDirectory" + * Directory where the temporary files generated by File_Archive will + * be created + * Default: '.' + * + * "appendRemoveDuplicates" + * If set to true, the appender created will by default remove the + * file present in the archive when adding a new one. This will slow the + * appending of files to archives + * Default: false + * + * "blockSize" + * To transfer data from a reader to a writer, some chunks a read from the + * source and written to the writer. This parameter controls the size of the + * chunks + * Default: 64kB + * + * "cacheCondition" + * This parameter specifies when a cache should be used. When the cache is + * used, the data of the reader is saved in a temporary file for future access. + * The cached reader will be read only once, even if you read it several times. + * This can be usefull to read compressed files or downloaded files (from http or ftp) + * The possible values for this option are + * - false: never use cache + * - a regexp: A cache will be used if the specified URL matches the regexp + * preg_match is used + * Default: false + * Example: '/^(http|ftp):\/\//' will cache all files downloaded via http or ftp + * + */ + function setOption($name, $value) + { + $option =& File_Archive::_option($name); + $option = $value; + if ($name == 'cache' && $value !== null) { + //TODO: ask to Cache_Lite to allow that + $value->_fileNameProtection = false; + } + } + + /** + * Retrieve the value of an option + */ + function getOption($name) + { + return File_Archive::_option($name); + } + + /** + * Create a reader to read the URL $URL. + * If the URL is a directory, it will recursively read that directory. + * If $uncompressionLevel is not null, the archives (files with extension + * tar, zip, gz or tgz) will be considered as directories (up to a depth of + * $uncompressionLevel if $uncompressionLevel > 0). The reader will only + * read files with a directory depth of $directoryDepth. It reader will + * replace the given URL ($URL) with $symbolic in the public filenames + * The default symbolic name is the last filename in the URL (or '' for + * directories) + * + * Examples: + * Considere the following file system + *

+     * a.txt
+     * b.tar (archive that contains the following files)
+     *     c.txt
+     *     d.tgz (archive that contains the following files)
+     *         e.txt
+     *         dir1/
+     *             f.txt
+     * dir2/
+     *     g.txt
+     *     dir3/
+     *         h.tar (archive that contains the following files)
+     *             i.txt
+     * 
+ * + * read('.') will return a reader that gives access to following + * files (recursively read current dir): + *
+     * a.txt
+     * b.tar
+     * dir2/g.txt
+     * dir2/dir3/h.tar
+     * 
+ * + * read('.', 'myBaseDir') will return the following reader: + *
+     * myBaseDir/a.txt
+     * myBaseDir/b.tar
+     * myBaseDir/dir2/g.txt
+     * myBaseDir/dir2/dir3/h.tar
+     * 
+ * + * read('.', '', -1) will return the following reader (uncompress + * everything) + *
+     * a.txt
+     * b.tar/c.txt
+     * b.tar/d.tgz/e.txt
+     * b.tar/d.tgz/dir1/f.txt
+     * dir2/g.txt
+     * dir2/dir3/h.tar/i.txt
+     * 
+ * + * read('.', '', 1) will uncompress only one level (so d.tgz will + * not be uncompressed): + *
+     * a.txt
+     * b.tar/c.txt
+     * b.tar/d.tgz
+     * dir2/g.txt
+     * dir2/dir3/h.tar/i.txt
+     * 
+ * + * read('.', '', 0, 0) will not recurse into subdirectories + *
+     * a.txt
+     * b.tar
+     * 
+ * + * read('.', '', 0, 1) will recurse only one level in + * subdirectories + *
+     * a.txt
+     * b.tar
+     * dir2/g.txt
+     * 
+ * + * read('.', '', -1, 2) will uncompress everything and recurse in + * only 2 levels in subdirectories or archives + *
+     * a.txt
+     * b.tar/c.txt
+     * b.tar/d.tgz/e.txt
+     * dir2/g.txt
+     * 
+ * + * The recursion level is determined by the real path, not the symbolic one. + * So read('.', 'myBaseDir', -1, 2) will result to the same files: + *
+     * myBaseDir/a.txt
+     * myBaseDir/b.tar/c.txt
+     * myBaseDir/b.tar/d.tgz/e.txt (accepted because the real depth is 2)
+     * myBaseDir/dir2/g.txt
+     * 
+ * + * Use readSource to do the same thing, reading from a specified reader instead of + * reading from the system files + * + * To read a single file, you can do read('a.txt', 'public_name.txt') + * If no public name is provided, the default one is the name of the file + * read('dir2/g.txt') contains the single file named 'g.txt' + * read('b.tar/c.txt') contains the single file named 'c.txt' + * + * Note: This function uncompress files reading their extension + * The compressed files must have a tar, zip, gz or tgz extension + * Since it is impossible for some URLs to use is_dir or is_file, this + * function may not work with + * URLs containing folders which name ends with such an extension + */ + function readSource(&$source, $URL, $symbolic = null, + $uncompression = 0, $directoryDepth = -1) + { + return File_Archive::_readSource($source, $URL, $reachable, $baseDir, + $symbolic, $uncompression, $directoryDepth); + } + + /** + * This function performs exactly as readSource, but with two additional parameters + * ($reachable and $baseDir) that will be set so that $reachable."/".$baseDir == $URL + * and $reachable can be reached (in case of error) + * + * @access private + */ + function _readSource(&$toConvert, $URL, &$reachable, &$baseDir, $symbolic = null, + $uncompression = 0, $directoryDepth = -1) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + if (is_array($URL)) { + $converted = array(); + foreach($URL as $key => $foo) { + $converted[] =& File_Archive::_convertToReader($URL[$key]); + } + return File_Archive::readMulti($converted); + } + + //No need to uncompress more than $directoryDepth + //That's not perfect, and some archives will still be uncompressed just + //to be filtered out :( + if ($directoryDepth >= 0) { + $uncompressionLevel = min($uncompression, $directoryDepth); + } else { + $uncompressionLevel = $uncompression; + } + + require_once 'File/Archive/Reader.php'; + $std = File_Archive_Reader::getStandardURL($URL); + + //Modify the symbolic name if necessary + $slashPos = strrpos($std, '/'); + if ($symbolic === null) { + if ($slashPos === false) { + $realSymbolic = $std; + } else { + $realSymbolic = substr($std, $slashPos+1); + } + } else { + $realSymbolic = $symbolic; + } + if ($slashPos !== false) { + $baseFile = substr($std, 0, $slashPos+1); + $lastFile = substr($std, $slashPos+1); + } else { + $baseFile = ''; + $lastFile = $std; + } + + if (strpos($lastFile, '*')!==false || + strpos($lastFile, '?')!==false) { + //We have to build a regexp here + $regexp = str_replace( + array('\*', '\?'), + array('[^/]*', '[^/]'), + preg_quote($lastFile) + ); + $result = File_Archive::_readSource($source, $baseFile, + $reachable, $baseDir, null, 0, -1); + return File_Archive::filter( + File_Archive::predEreg('^'.$regexp.'$'), + $result + ); + } + + //If the URL can be interpreted as a directory, and we are reading from the file system + if ((empty($URL) || is_dir($URL)) && $source === null) { + require_once "File/Archive/Reader/Directory.php"; + + if ($uncompressionLevel != 0) { + require_once "File/Archive/Reader/Uncompress.php"; + $result = new File_Archive_Reader_Uncompress( + new File_Archive_Reader_Directory($std, '', $directoryDepth), + $uncompressionLevel + ); + } else { + $result = new File_Archive_Reader_Directory($std, '', $directoryDepth); + } + + if ($directoryDepth >= 0) { + require_once 'File/Archive/Reader/Filter.php'; + require_once 'File/Archive/Predicate/MaxDepth.php'; + + $tmp =& File_Archive::filter( + new File_Archive_Predicate_MaxDepth($directoryDepth), + $result + ); + unset($result); + $result =& $tmp; + } + if (!empty($realSymbolic)) { + if ($symbolic === null) { + $realSymbolic = ''; + } + require_once "File/Archive/Reader/ChangeName/AddDirectory.php"; + $tmp = new File_Archive_Reader_ChangeName_AddDirectory( + $realSymbolic, + $result + ); + unset($result); + $result =& $tmp; + } + + //If the URL can be interpreted as a file, and we are reading from the file system + } else if (is_file($URL) && substr($URL, -1)!='/' && $source === null) { + require_once "File/Archive/Reader/File.php"; + $result = new File_Archive_Reader_File($URL, $realSymbolic); + + //Else, we will have to build a complex reader + } else { + require_once "File/Archive/Reader/File.php"; + + $realPath = $std; + + // Try to find a file with a known extension in the path ( + // (to manage URLs like archive.tar/directory/file) + $pos = 0; + do { + if ($pos+1setBaseDir($std); + if (PEAR::isError($isDir)) { + return $isDir; + } + if ($isDir && $symbolic==null) { + //Default symbolic name for directories is empty + $realSymbolic = ''; + } + + if ($directoryDepth >= 0) { + //Limit the maximum depth if necessary + require_once "File/Archive/Reader/Filter.php"; + require_once "File/Archive/Predicate/MaxDepth.php"; + + $tmp = new File_Archive_Reader_Filter( + new File_Archive_Predicate( + $directoryDepth + + substr_count(substr($std, $pos+1), '/') + ), + $result + ); + unset($result); + $result =& $tmp; + } + + if ($std != $realSymbolic) { + require_once "File/Archive/Reader/ChangeName/Directory.php"; + + //Change the base name to the symbolic one if necessary + $tmp = new File_Archive_Reader_ChangeName_Directory( + $std, + $realSymbolic, + $result + ); + unset($result); + $result =& $tmp; + } + } + + $cacheCondition = File_Archive::getOption('cacheCondition'); + if ($cacheCondition !== false && + preg_match($cacheCondition, $URL)) { + $tmp =& File_Archive::cache($result); + unset($result); + $result =& $tmp; + } + + return $result; + } + function read($URL, $symbolic = null, + $uncompression = 0, $directoryDepth = -1) + { + $source = null; + return File_Archive::readSource($source, $URL, $symbolic, $uncompression, $directoryDepth); + } + + /** + * Create a file reader on an uploaded file. The reader will read + * $_FILES[$name]['tmp_name'] and will have $_FILES[$name]['name'] + * as a symbolic filename. + * + * A PEAR error is returned if one of the following happen + * - $_FILES[$name] is not set + * - $_FILES[$name]['error'] is not 0 + * - is_uploaded_file returns false + * + * @param string $name Index of the file in the $_FILES array + * @return File_Archive_Reader File reader on the uploaded file + */ + function readUploadedFile($name) + { + if (!isset($_FILES[$name])) { + return PEAR::raiseError("File $name has not been uploaded"); + } + switch ($_FILES[$name]['error']) { + case 0: + //No error + break; + case 1: + return PEAR::raiseError( + "The upload size limit didn't allow to upload file ". + $_FILES[$name]['name'] + ); + case 2: + return PEAR::raiseError( + "The form size limit didn't allow to upload file ". + $_FILES[$name]['name'] + ); + case 3: + return PEAR::raiseError( + "The file was not entirely uploaded" + ); + case 4: + return PEAR::raiseError( + "The uploaded file is empty" + ); + default: + return PEAR::raiseError( + "Unknown error ".$_FILES[$name]['error']." in file upload. ". + "Please, report a bug" + ); + } + if (!is_uploaded_file($_FILES[$name]['tmp_name'])) { + return PEAR::raiseError("The file is not an uploaded file"); + } + + require_once "File/Archive/Reader/File.php"; + return new File_Archive_Reader_File( + $_FILES[$name]['tmp_name'], + $_FILES[$name]['name'], + $_FILES[$name]['type'] + ); + } + + /** + * Adds a cache layer above the specified reader + * The data of the reader is saved in a temporary file for future access. + * The cached reader will be read only once, even if you read it several times. + * This can be usefull to read compressed files or downloaded files (from http or ftp) + * + * @param mixed $toConvert The reader to cache + * It can be a File_Archive_Reader or a string, which will be converted using the + * read function + */ + function cache(&$toConvert) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + + require_once 'File/Archive/Reader/Cache.php'; + return new File_Archive_Reader_Cache($source); + } + + /** + * Try to interpret the object as a reader + * Strings are converted to readers using File_Archive::read + * Arrays are converted to readers using File_Archive::readMulti + * + * @access private + */ + function &_convertToReader(&$source) + { + if (is_string($source)) { + $cacheCondition = File_Archive::getOption('cacheCondition'); + if ($cacheCondition !== false && + preg_match($cacheCondition, $source)) { + $obj = File_Archive::cache(File_Archive::read($source)); + return $obj; + } else { + $obj = File_Archive::read($source); + return $obj; + } + } else if (is_array($source)) { + return File_Archive::readMulti($source); + } else { + return $source; + } + } + + /** + * Try to interpret the object as a writer + * Strings are converted to writers using File_Archive::appender + * Arrays are converted to writers using a multi writer + * + * @access private + */ + function &_convertToWriter(&$dest) + { + if (is_string($dest)) { + $obj =& File_Archive::appender($dest); + return $obj; + } else if (is_array($dest)) { + require_once 'File/Archive/Writer/Multi.php'; + $writer = new File_Archive_Writer_Multi(); + foreach($dest as $key => $foo) { + $writer->addWriter($dest[$key]); + } + return $writer; + } else { + return $dest; + } + } + + /** + * Check if a file with a specific extension can be read as an archive + * with File_Archive::read* + * This function is case sensitive. + * + * @param string $extension the checked extension + * @return bool whether this file can be understood reading its extension + * Currently, supported extensions are tar, zip, jar, gz, tgz, + * tbz, bz2, bzip2, ar, deb + */ + function isKnownExtension($extension) + { + return $extension == 'tar' || + $extension == 'zip' || + $extension == 'jar' || + $extension == 'gz' || + $extension == 'tgz' || + $extension == 'tbz' || + $extension == 'bz2' || + $extension == 'bzip2' || + $extension == 'ar' || + $extension == 'deb' /* || + $extension == 'cab' || + $extension == 'rar' */; + } + + /** + * Create a reader that will read the single file source $source as + * a specific archive + * + * @param string $extension determines the kind of archive $source contains + * $extension is case sensitive + * @param File_Archive_Reader $source stores the archive + * @param bool $sourceOpened specifies if the archive is already opened + * if false, next will be called on source + * Closing the returned archive will close $source iif $sourceOpened + * is true + * @return A File_Archive_Reader that uncompresses the archive contained in + * $source interpreting it as a $extension archive + * If $extension is not handled return false + */ + function readArchive($extension, &$toConvert, $sourceOpened = false) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + + switch($extension) { + case 'tgz': + return File_Archive::readArchive('tar', + File_Archive::readArchive('gz', $source, $sourceOpened) + ); + case 'tbz': + return File_Archive::readArchive('tar', + File_Archive::readArchive('bz2', $source, $sourceOpened) + ); + case 'tar': + require_once 'File/Archive/Reader/Tar.php'; + return new File_Archive_Reader_Tar($source, $sourceOpened); + + case 'gz': + case 'gzip': + require_once 'File/Archive/Reader/Gzip.php'; + return new File_Archive_Reader_Gzip($source, $sourceOpened); + + case 'zip': + case 'jar': + require_once 'File/Archive/Reader/Zip.php'; + return new File_Archive_Reader_Zip($source, $sourceOpened); + + case 'bz2': + case 'bzip2': + require_once 'File/Archive/Reader/Bzip2.php'; + return new File_Archive_Reader_Bzip2($source, $sourceOpened); + + case 'deb': + case 'ar': + require_once 'File/Archive/Reader/Ar.php'; + return new File_Archive_Reader_Ar($source, $sourceOpened); + +/* case 'cab': + require_once 'File/Archive/Reader/Cab.php'; + return new File_Archive_Reader_Cab($source, $sourceOpened); + + + case 'rar': + require_once "File/Archive/Reader/Rar.php"; + return new File_Archive_Reader_Rar($source, $sourceOpened); */ + + default: + return false; + } + } + + /** + * Contains only one file with data read from a memory buffer + * + * @param string $memory content of the file + * @param string $filename public name of the file + * @param array $stat statistics of the file. Index 7 (size) will be + * overwritten to match the size of $memory + * @param string $mime mime type of the file. Default will determine the + * mime type thanks to the extension of $filename + * @see File_Archive_Reader_Memory + */ + function readMemory($memory, $filename, $stat=array(), $mime=null) + { + require_once "File/Archive/Reader/Memory.php"; + return new File_Archive_Reader_Memory($memory, $filename, $stat, $mime); + } + + /** + * Contains several other sources. Take care the sources don't have several + * files with the same filename. The sources are given as a parameter, or + * can be added thanks to the reader addSource method + * + * @param array $sources Array of strings or readers that will be added to + * the multi reader. If the parameter is a string, a reader will be + * built thanks to the read function + * @see File_Archive_Reader_Multi, File_Archive::read() + */ + function readMulti($sources = array()) + { + require_once "File/Archive/Reader/Multi.php"; + $result = new File_Archive_Reader_Multi(); + foreach ($sources as $index => $foo) { + $s =& File_Archive::_convertToReader($sources[$index]); + if (PEAR::isError($s)) { + return $s; + } else { + $result->addSource($s); + } + } + return $result; + } + /** + * Make the files of a source appear as one large file whose content is the + * concatenation of the content of all the files + * + * @param File_Archive_Reader $toConvert The source whose files must be + * concatened + * @param string $filename name of the only file of the created reader + * @param array $stat statistics of the file. Index 7 (size) will be + * overwritten to match the total size of the files + * @param string $mime mime type of the file. Default will determine the + * mime type thanks to the extension of $filename + * @see File_Archive_Reader_Concat + */ + function readConcat(&$toConvert, $filename, $stat=array(), $mime=null) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + + require_once "File/Archive/Reader/Concat.php"; + return new File_Archive_Reader_Concat($source, $filename, $stat, $mime); + } + + /** + * Changes the name of each file in a reader by applying a custom function + * The function must return false if the file is to be discarded, or the new + * name of the file else + * + * @param Callable $function Function called to modify the name of the file + * $function takes the name of the file as a parameter and returns the + * new name, or false if the file must be discarded + * @param File_Archive_Reader $toConvert The files of this source will be + * modified + * @return File_Archive_Reader a new reader that contains the same files + * as $toConvert but with a different name + */ + function changeName($function, &$toConvert) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + + require_once "File/Archive/Reader/ChangeName.php"; + return new File_Archive_Reader_RemoveDirectory($source); + } + + /** + * Removes from a source the files that do not follow a given predicat + * + * @param File_Archive_Predicate $predicate Only the files for which + * $predicate->isTrue() will be kept + * @param File_Archive_Reader $source Source that will be filtered + * @see File_Archive_Reader_Filter + */ + function filter($predicate, &$toConvert) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + + require_once "File/Archive/Reader/Filter.php"; + return new File_Archive_Reader_Filter($predicate, $source); + } + /** + * Predicate that always evaluate to true + * + * @see File_Archive_Predicate_True + */ + function predTrue() + { + require_once "File/Archive/Predicate/True.php"; + return new File_Archive_Predicate_True(); + } + /** + * Predicate that always evaluate to false + * + * @see File_Archive_Predicate_False + */ + function predFalse() + { + require_once "File/Archive/Predicate/False.php"; + return new File_Archive_Predicate_False(); + } + /** + * Predicate that evaluates to the logical AND of the parameters + * You can add other predicates thanks to the + * File_Archive_Predicate_And::addPredicate() function + * + * @param File_Archive_Predicate (any number of them) + * @see File_Archive_Predicate_And + */ + function predAnd() + { + require_once "File/Archive/Predicate/And.php"; + $pred = new File_Archive_Predicate_And(); + $args = func_get_args(); + foreach ($args as $p) { + $pred->addPredicate($p); + } + return $pred; + } + /** + * Predicate that evaluates to the logical OR of the parameters + * You can add other predicates thanks to the + * File_Archive_Predicate_Or::addPredicate() function + * + * @param File_Archive_Predicate (any number of them) + * @see File_Archive_Predicate_Or + */ + function predOr() + { + require_once "File/Archive/Predicate/Or.php"; + $pred = new File_Archive_Predicate_Or(); + $args = func_get_args(); + foreach ($args as $p) { + $pred->addPredicate($p); + } + return $pred; + } + /** + * Negate a predicate + * + * @param File_Archive_Predicate $pred Predicate to negate + * @see File_Archive_Predicate_Not + */ + function predNot($pred) + { + require_once "File/Archive/Predicate/Not.php"; + return new File_Archive_Predicate_Not($pred); + } + /** + * Evaluates to true iif the file is larger than a given size + * + * @param int $size the minimal size of the files (in Bytes) + * @see File_Archive_Predicate_MinSize + */ + function predMinSize($size) + { + require_once "File/Archive/Predicate/MinSize.php"; + return new File_Archive_Predicate_MinSize($size); + } + /** + * Evaluates to true iif the file has been modified after a given time + * + * @param int $time Unix timestamp of the minimal modification time of the + * files + * @see File_Archive_Predicate_MinTime + */ + function predMinTime($time) + { + require_once "File/Archive/Predicate/MinTime.php"; + return new File_Archive_Predicate_MinTime($time); + } + /** + * Evaluates to true iif the file has less that a given number of + * directories in its path + * + * @param int $depth Maximal number of directories in path of the files + * @see File_Archive_Predicate_MaxDepth + */ + function predMaxDepth($depth) + { + require_once "File/Archive/Predicate/MaxDepth.php"; + return new File_Archive_Predicate_MaxDepth($depth); + } + /** + * Evaluates to true iif the extension of the file is in a given list + * + * @param array or string $list List or comma separated string of possible + * extension of the files + * @see File_Archive_Predicate_Extension + */ + function predExtension($list) + { + require_once "File/Archive/Predicate/Extension.php"; + return new File_Archive_Predicate_Extension($list); + } + /** + * Evaluates to true iif the MIME type of the file is in a given list + * + * @param array or string $list List or comma separated string of possible + * MIME types of the files. You may enter wildcards like "image/*" to + * select all the MIME in class image + * @see File_Archive_Predicate_MIME, MIME_Type::isWildcard() + */ + function predMIME($list) + { + require_once "File/Archive/Predicate/MIME.php"; + return new File_Archive_Predicate_MIME($list); + } + /** + * Evaluates to true iif the name of the file follow a given regular + * expression + * + * @param string $ereg regular expression that the filename must follow + * @see File_Archive_Predicate_Ereg, ereg() + */ + function predEreg($ereg) + { + require_once "File/Archive/Predicate/Ereg.php"; + return new File_Archive_Predicate_Ereg($ereg); + } + /** + * Evaluates to true iif the name of the file follow a given regular + * expression (case insensitive version) + * + * @param string $ereg regular expression that the filename must follow + * @see File_Archive_Predicate_Eregi, eregi + */ + function predEregi($ereg) + { + require_once "File/Archive/Predicate/Eregi.php"; + return new File_Archive_Predicate_Eregi($ereg); + } + /** + * Evaluates to true only after a given number of evaluations + * This can be used to select files by index since the evaluation is done + * once per file + * + * @param array The indexes for which the returned predicate will return true + * are the keys of the array + * The predicate will return true if isset($indexes[$pos]) + */ + function predIndex($indexes) + { + require_once "File/Archive/Predicate/Index.php"; + return new File_Archive_Predicate_Index($indexes); + } + /** + * Custom predicate built by supplying a string expression + * + * Here are different ways to create a predicate that keeps only files + * with names shorter than 100 chars + * + * File_Archive::predCustom("return strlen($name)<100;") + * File_Archive::predCustom("strlen($name)<100;") + * File_Archive::predCustom("strlen($name)<100") + * File_Archive::predCustom("strlen($source->getFilename())<100") + * + * + * @param string $expression String containing an expression that evaluates + * to a boolean. If the expression doesn't contain a return + * statement, it will be added at the begining of the expression + * A ';' will be added at the end of the expression so that you don't + * have to write it. You may use the $name variable to refer to the + * current filename (with path...), $time for the modification time + * (unix timestamp), $size for the size of the file in bytes, $mime + * for the MIME type of the file + * @see File_Archive_Predicate_Custom + */ + function predCustom($expression) + { + require_once "File/Archive/Predicate/Custom.php"; + return new File_Archive_Predicate_Custom($expression); + } + + /** + * Send the files as a mail attachment + * + * @param Mail $mail Object used to send mail (see Mail::factory) + * @param array or String $to An array or a string with comma separated + * recipients + * @param array $headers The headers that will be passed to the Mail_mime + * object + * @param string $message Text body of the mail + * @see File_Archive_Writer_Mail + */ + function toMail($to, $headers, $message, $mail = null) + { + require_once "File/Archive/Writer/Mail.php"; + return new File_Archive_Writer_Mail($to, $headers, $message, $mail); + } + /** + * Write the files on the hard drive + * + * @param string $baseDir if specified, the files will be created in that + * directory. If they don't exist, the directories will automatically + * be created + * @see File_Archive_Writer_Files + */ + function toFiles($baseDir = "") + { + require_once "File/Archive/Writer/Files.php"; + return new File_Archive_Writer_Files($baseDir); + } + /** + * Send the content of the files to a memory buffer + * + * toMemory returns a writer where the data will be written. + * In this case, the data is accessible using the getData member + * + * toVariable returns a writer that will write into the given + * variable + * + * @param out $data if specified, the data will be written to this buffer + * Else, you can retrieve the buffer with the + * File_Archive_Writer_Memory::getData() function + * @see File_Archive_Writer_Memory + */ + function toMemory() + { + $v = ''; + return File_Archive::toVariable($v); + } + function toVariable(&$v) + { + require_once "File/Archive/Writer/Memory.php"; + return new File_Archive_Writer_Memory($v); + } + /** + * Duplicate the writing operation on two writers + * + * @param File_Archive_Writer $a, $b writers where data will be duplicated + * @see File_Archive_Writer_Multi + */ + function toMulti(&$aC, &$bC) + { + $a =& File_Archive::_convertToWriter($aC); + $b =& File_Archive::_convertToWriter($bC); + + if (PEAR::isError($a)) { + return $a; + } + if (PEAR::isError($b)) { + return $b; + } + + require_once "File/Archive/Writer/Multi.php"; + $writer = new File_Archive_Writer_Multi(); + $writer->addWriter($a); + $writer->addWriter($b); + return $writer; + } + /** + * Send the content of the files to the standard output (so to the client + * for a website) + * + * @param bool $sendHeaders If true some headers will be sent to force the + * download of the file. Default value is true + * @see File_Archive_Writer_Output + */ + function toOutput($sendHeaders = true) + { + require_once "File/Archive/Writer/Output.php"; + return new File_Archive_Writer_Output($sendHeaders); + } + /** + * Compress the data to a tar, gz, tar/gz or zip format + * + * @param string $filename name of the archive file + * @param File_Archive_Writer $innerWriter writer where the archive will be + * written + * @param string $type can be one of tgz, tbz, tar, zip, gz, gzip, bz2, + * bzip2 (default is the extension of $filename) or any composition + * of them (for example tar.gz or tar.bz2). The case of this + * parameter is not important. + * @param array $stat Statistics of the archive (see stat function) + * @param bool $autoClose If set to true, $innerWriter will be closed when + * the returned archive is close. Default value is true. + */ + function toArchive($filename, &$toConvert, $type = null, + $stat = array(), $autoClose = true) + { + $innerWriter =& File_Archive::_convertToWriter($toConvert); + if (PEAR::isError($innerWriter)) { + return $innerWriter; + } + $shortcuts = array("tgz" , "tbz" ); + $reals = array("tar.gz", "tar.bz2"); + + if ($type === null) { + $extensions = strtolower($filename); + } else { + $extensions = strtolower($type); + } + $extensions = explode('.', str_replace($shortcuts, $reals, $extensions)); + if ($innerWriter !== null) { + $writer =& $innerWriter; + } else { + $writer = File_Archive::toFiles(); + } + $nbCompressions = 0; + $currentFilename = $filename; + while (($extension = array_pop($extensions)) !== null) { + unset($next); + switch($extension) { + case "tar": + require_once "File/Archive/Writer/Tar.php"; + $next = new File_Archive_Writer_Tar( + $currentFilename, $writer, $stat, $autoClose + ); + unset($writer); $writer =& $next; + break; + case "zip": + require_once "File/Archive/Writer/Zip.php"; + $next = new File_Archive_Writer_Zip( + $currentFilename, $writer, $stat, $autoClose + ); + unset($writer); $writer =& $next; + break; + case "gz": + case "gzip": + require_once "File/Archive/Writer/Gzip.php"; + $next = new File_Archive_Writer_Gzip( + $currentFilename, $writer, $stat, $autoClose + ); + unset($writer); $writer =& $next; + break; + case "bz2": + case "bzip2": + require_once "File/Archive/Writer/Bzip2.php"; + $next = new File_Archive_Writer_Bzip2( + $currentFilename, $writer, $stat, $autoClose + ); + unset($writer); $writer =& $next; + break; + case "deb": + case "ar": + require_once "File/Archive/Writer/Ar.php"; + $next = new File_Archive_Writer_Ar( + $currentFilename, $writer, $stat, $autoClose + ); + unset($writer); $writer =& $next; + break; + default: + if ($type !== null || $nbCompressions == 0) { + return PEAR::raiseError("Archive $extension unknown"); + } + break; + } + $nbCompressions ++; + $autoClose = true; + $currentFilename = implode(".", $extensions); + } + return $writer; + } + + + /** + * File_Archive::extract($source, $dest) is equivalent to $source->extract($dest) + * If $source is a PEAR error, the error will be returned + * It is thus easier to use this function than $source->extract, since it reduces the number of + * error checking and doesn't force you to define a variable $source + * + * You may use strings as source and dest. In that case the source is automatically + * converted to a reader using File_Archive::read and the dest is converted to a + * writer using File_Archive::appender + * Since PHP doesn't allow to pass literal strings by ref, you will have to use temporary + * variables. + * File_Archive::extract($src = 'archive.zip/', $dest = 'dir') will extract the archive to 'dir' + * It is the same as + * File_Archive::extract( + * File_Archive::read('archive.zip/'), + * File_Archive::appender('dir') + * ); + * You may use any variable in the extract function ($from/$to, $a/$b...). + * + * @param File_Archive_Reader $source The source that will be read + * @param File_Archive_Writer $dest Where to copy $source files + * @param bool $autoClose if true (default), $dest will be closed after the extraction + * @param int $bufferSize Size of the buffer to use to move data from the reader to the buffer + * If $bufferSize <= 0 (default), the blockSize option is used + * You shouldn't need to change that + * @return null or a PEAR error if an error occured + */ + function extract(&$sourceToConvert, &$destToConvert, $autoClose = true, $bufferSize = 0) + { + $source =& File_Archive::_convertToReader($sourceToConvert); + if (PEAR::isError($source)) { + return $source; + } + $dest =& File_Archive::_convertToWriter($destToConvert); + return $source->extract($dest, $autoClose, $bufferSize); + } + + /** + * Create a writer that can be used to append files to an archive inside a source + * If the archive can't be found in the source, it will be created + * If source is set to null, File_Archive::toFiles will be assumed + * If type is set to null, the type of the archive will be determined looking at + * the extension in the URL + * stat is the array of stat (returned by stat() PHP function of Reader getStat()) + * to use if the archive must be created + * + * This function allows to create or append data to nested archives. Only one + * archive will be created and if your creation requires creating several nested + * archives, a PEAR error will be returned + * + * After this call, $source will be closed and should not be used until the + * returned writer is closed. + * + * @param File_Archive_Reader $source A reader where some files will be appended + * @param string $URL URL to reach the archive in the source. + * if $URL is null, a writer to append files to the $source reader will + * be returned + * @param bool $unique If true, the duplicate files will be deleted on close + * Default is false (and setting it to true may have some performance + * consequences) + * @param string $type Extension of the archive (or null to use the one in the URL) + * @param array $stat Used only if archive is created, array of stat as returned + * by PHP stat function or Reader getStat function: stats of the archive) + * Time (index 9) will be overwritten to current time + * @return File_Archive_Writer a writer that you can use to append files to the reader + */ + function appenderFromSource(&$toConvert, $URL = null, $unique = null, + $type = null, $stat = array()) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + if ($unique == null) { + $unique = File_Archive::getOption("appendRemoveDuplicates"); + } + + //Do not report the fact that the archive does not exist as an error + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + + if ($URL === null) { + $result =& $source; + } else { + if ($type === null) { + $result = File_Archive::_readSource($source, $URL.'/', $reachable, $baseDir); + } else { + $result = File_Archive::readArchive( + $type, + File_Archive::_readSource($source, $URL, $reachable, $baseDir) + ); + } + } + + PEAR::popErrorHandling(); + + if (!PEAR::isError($result)) { + if ($unique) { + require_once "File/Archive/Writer/UniqueAppender.php"; + return new File_Archive_Writer_UniqueAppender($result); + } else { + return $result->makeAppendWriter(); + } + } + + //The source can't be found and has to be created + $stat[9] = $stat['mtime'] = time(); + + if (empty($baseDir)) { + if ($source !== null) { + $writer =& $source->makeWriter(); + } else { + $writer =& File_Archive::toFiles(); + } + if (PEAR::isError($writer)) { + return $writer; + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $result = File_Archive::toArchive($reachable, $writer, $type); + PEAR::popErrorHandling(); + + if (PEAR::isError($result)) { + $result = File_Archive::toFiles($reachable); + } + } else { + $reachedSource = File_Archive::readSource($source, $reachable); + if (PEAR::isError($reachedSource)) { + return $reachedSource; + } + $writer = $reachedSource->makeWriter(); + if (PEAR::isError($writer)) { + return $writer; + } + + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $result = File_Archive::toArchive($baseDir, $writer, $type); + PEAR::popErrorHandling(); + + if (PEAR::isError($result)) { + require_once "File/Archive/Writer/AddBaseName.php"; + $result = new File_Archive_Writer_AddBaseName( + $baseDir, $writer); + if (PEAR::isError($result)) { + return $result; + } + } + } + return $result; + } + + /** + * Create a writer that allows appending new files to an existing archive + * This function actes as appendToSource with source being the system files + * $URL can't be null here + * + * @param File_Archive_Reader $source A reader where some files will be appended + * @return File_Archive_Writer a writer that you can use to append files to the reader + */ + function appender($URL, $unique = null, $type = null, $stat = array()) + { + $source = null; + return File_Archive::appenderFromSource($source, $URL, $unique, $type, $stat); + } + + /** + * Remove the files that follow a given predicate from the source + * If URL is null, the files will be removed from the source directly + * Else, URL must link to a source from which the files will be removed + * + * @param File_Archive_Predicate $pred The files that follow the predicate + * (for which $pred->isTrue($source) is true) will be erased + * @param File_Archive_Reader $source A reader that contains the files to remove + */ + function removeFromSource(&$pred, &$toConvert, $URL = null) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + if ($URL === null) { + $result = &$source; + } else { + if (substr($URL, -1) !== '/') { + $URL .= '/'; + } + $result = File_Archive::readSource($source, $URL); + } + + $writer = $result->makeWriterRemoveFiles($pred); + if (PEAR::isError($writer)) { + return $writer; + } + $writer->close(); + } + + /** + * Remove the files that follow a given predicate from the archive specified + * in $URL + * + * @param $URL URL of the archive where some files must be removed + */ + function remove($pred, $URL) + { + $source = null; + return File_Archive::removeFromSource($pred, $source, $URL); + } + + /** + * Remove duplicates from a source, keeping the most recent one (or the one that has highest pos in + * the archive if the files have same date or no date specified) + * + * @param File_Archive_Reader a reader that may contain duplicates + */ + function removeDuplicatesFromSource(&$toConvert, $URL = null) + { + $source =& File_Archive::_convertToReader($toConvert); + if (PEAR::isError($source)) { + return $source; + } + if ($URL !== null && substr($URL, -1) != '/') { + $URL .= '/'; + } + + if ($source === null) { + $source = File_Archive::read($URL); + } + + require_once "File/Archive/Predicate/Duplicate.php"; + $pred = new File_Archive_Predicate_Duplicate($source); + $source->close(); + return File_Archive::removeFromSource( + $pred, + $source, + null + ); + } + + /** + * Remove duplicates from the archive specified in the URL + */ + function removeDuplicates($URL) + { + $source = null; + return File_Archive::removeDuplicatesFromSource($source, $URL); + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate.php new file mode 100644 index 0000000..6f171b4 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate.php @@ -0,0 +1,57 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Predicate.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader.php"; + +/** + * A predicate is an object that can evaluate to true or false depending on the + * file currently read by a File_Archive_Reader + * + * @see File_Archive_Reader_Filter + */ +class File_Archive_Predicate +{ + /** + * Indicates whether the current file from the reader should be kept + * + * @param File_Archive_Reader $source Reader which will be filtered + * @return bool False if the current file must be filtered out + */ + function isTrue(&$source) + { + return PEAR::raiseError("Predicat abstract function call"); + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/And.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/And.php new file mode 100644 index 0000000..674d80a --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/And.php @@ -0,0 +1,87 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: And.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Evaluates to true iif all predicates given as constructor parameters evaluate + * to true + * + * Example: + * new File_Archive_Predicate_And($pred1, $pred2, $pred3) + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_And extends File_Archive_Predicate +{ + /** + * @var Array List of File_Archive_Predicate objects given as an argument + * @access private + */ + var $preds; + + /** + * Build the predicate using the optional File_Archive_Predicates given as + * arguments + * + * Example: + * new File_Archive_Predicate_And($pred1, $pred2, $pred3) + */ + function File_Archive_Predicate_And() + { + $this->preds = func_get_args(); + } + + /** + * Add a new predicate to the list + * + * @param File_Archive_Predicate The predicate to add + */ + function addPredicate($pred) + { + $this->preds[] = $pred; + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + foreach ($this->preds as $p) { + if (!$p->isTrue($source)) { + return false; + } + } + return true; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Current.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Current.php new file mode 100644 index 0000000..2276a74 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Current.php @@ -0,0 +1,52 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Current.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Evaluates to true only once, and then always to false + */ +class File_Archive_Predicate_Current extends File_Archive_Predicate +{ + var $value = true; + + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + $tmp = $this->value; + $this->value = false; + return $tmp; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Custom.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Custom.php new file mode 100644 index 0000000..1b43655 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Custom.php @@ -0,0 +1,88 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Custom.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Custom predicate built by supplying a string expression + * + * Example: + * new File_Archive_Predicate_Custom("return strlen($name)<100;") + * new File_Archive_Predicate_Custom("strlen($name)<100;") + * new File_Archive_Predicate_Custom("strlen($name)<100") + * new File_Archive_Predicate_Custom("strlen($source->getFilename())<100") + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_Custom extends File_Archive_Predicate +{ + var $expression; + var $useName; + var $useStat; + var $useMIME; + + /** + * @param string $expression PHP code that evaluates too a boolean + * It can use the $source variable. If return is ommited, it will be + * added to the begining of the expression. A ; will also be added at + * the end so that you don't need to write it + */ + function File_Archive_Predicate_Custom($expression) + { + $this->expression = $expression.";"; + if (strpos($this->expression, "return") === false) { + $this->expression = "return ".$this->expression; + } + $this->useName = (strpos($this->expression, '$name') !== false); + $this->useStat = (strpos($this->expression, '$stat') !== false); + $this->useMIME = (strpos($this->expression, '$mime') !== false); + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + if ($this->useName) { + $name = $source->getFilename(); + } + if ($this->useStat) { + $stat = $source->getStat(); + $size = $stat[7]; + $time = (isset($stat[9]) ? $stat[9] : null); + } + if ($this->useMIME) { + $mime = $source->getMIME(); + } + return (bool)eval($this->expression); + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Duplicate.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Duplicate.php new file mode 100644 index 0000000..16ad94e --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Duplicate.php @@ -0,0 +1,116 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Duplicate.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Evaluates to true if a for the files for which a newer version + * can be found in a specified archive + * Comparison is by default made on dates of the files, or position + * in the archive (if two files have the same date or the date of a + * file is not specified). + */ +class File_Archive_Predicate_Duplicate extends File_Archive_Predicate +{ + /** + * @var array Key is the filename, value is an array of date (index 0) and + * position in the archive (index) 1 of the newest entry with this filename + */ + var $newest = array(); + + /** + * @var int The current position of the file in the source + */ + var $pos = 0; + + /** + * @param File_Archive_Reader $source The source will be inspected to find + * the date of old files + * The predicate should then be used on the same source to remove the + * old duplicate files + */ + function File_Archive_Predicate_Duplicate(&$source) + { + //Ensure we are at the begining of the file + $source->close(); + $pos = 0; + while ($source->next()) { + $filename = $source->getFilename(); + $stat = $source->getStat(); + $value = isset($this->newest[$filename]) ? $this->newest[$filename] : null; + + if ($value === null || + $this->compare($stat[9], $value[0]) >= 0 + ) { + $this->newest[$filename] = array($stat[9], $pos); + } + $pos++; + } + } + + /** + * Compare the dates of two files. null is considered infinitely old + * + * @return int < 0 if $a can be considered older than $b + * = 0 if $a and $b can be considered same age + * > 0 if $a can be considered newer than $b + */ + function compare($a, $b) { + return ($a === null ? -1 : $a) - ($b === null ? -1 : $b); + } + + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + $filename = $source->getFilename(); + $stat = $source->getStat(); + $value = isset($this->newest[$filename]) ? $this->newest[$filename] : null; + if ($value === null) { + $delete = false; + } else { + $comp = $this->compare($stat[9], $value[0]); + + $delete = $comp < 0 || + ($comp == 0 && $this->pos != $value[1]); + + } + $this->pos++; + return $delete; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Ereg.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Ereg.php new file mode 100644 index 0000000..e2bbd9d --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Ereg.php @@ -0,0 +1,59 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Ereg.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Keep only the files which name follow a given regular expression + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter ereg + */ +class File_Archive_Predicate_Ereg extends File_Archive_Predicate +{ + var $ereg; + + /** + * @param string $ereg is the regular expression + */ + function File_Archive_Predicate_Ereg($ereg) + { + $this->ereg = $ereg; + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + return (bool)ereg($this->ereg, $source->getFilename()); + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Eregi.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Eregi.php new file mode 100644 index 0000000..540f1c4 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Eregi.php @@ -0,0 +1,61 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Eregi.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Keep only the files which name follow a given case insensitive regular + * expression + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter eregi + */ +class File_Archive_Predicate_Eregi extends File_Archive_Predicate +{ + var $ereg; + + /** + * @param string $ereg is the regular expression + */ + function File_Archive_Predicate_Eregi($ereg) + { + $this->ereg = $ereg; + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + return (bool)eregi($this->ereg, $source->getFilename()); + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Extension.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Extension.php new file mode 100644 index 0000000..419e643 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Extension.php @@ -0,0 +1,71 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Extension.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Keep only the files that have a specific extension + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_Extension extends File_Archive_Predicate +{ + var $extensions; + + /** + * @param $extensions array or comma separated string of allowed extensions + */ + function File_Archive_Predicate_Extension($extensions) + { + if (is_string($extensions)) { + $this->extensions = explode(",",$extensions); + } else { + $this->extensions = $extensions; + } + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + $filename = $source->getFilename(); + $pos = strrpos($filename, '.'); + $extension = ""; + if ($pos !== FALSE) { + $extension = strtolower(substr($filename, $pos+1)); + } + $result = in_array($extension, $this->extensions); + + return $result; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/False.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/False.php new file mode 100644 index 0000000..cfa20b2 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/False.php @@ -0,0 +1,47 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: False.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Always evaluate to false + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_False extends File_Archive_Predicate +{ + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) { return false; } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Index.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Index.php new file mode 100644 index 0000000..9fde26c --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Index.php @@ -0,0 +1,62 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Index.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Evaluates to true if the index is in a given array of indexes + * The array has the indexes in key (so you may want to call + * array_flip if your array has indexes as value) + */ +class File_Archive_Predicate_Index extends File_Archive_Predicate +{ + var $indexes; + var $pos = 0; + + /** + * @param $extensions array or comma separated string of allowed extensions + */ + function File_Archive_Predicate_Index($indexes) + { + $this->indexes = $indexes; + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + return isset($this->indexes[$this->pos++]); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MIME.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MIME.php new file mode 100644 index 0000000..54c3d61 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MIME.php @@ -0,0 +1,75 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: MIME.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; +require_once "MIME/Type.php"; + +/** + * Keep only the files that have a specific MIME type + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_MIME extends File_Archive_Predicate +{ + var $mimes; + + /** + * @param $extensions array or comma separated string of allowed extensions + */ + function File_Archive_Predicate_MIME($mimes) + { + if (is_string($mimes)) { + $this->mimes = explode(",",$mimes); + } else { + $this->mimes = $mimes; + } + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + $sourceMIME = $source->getMIME(); + foreach ($this->mimes as $mime) { + if (MIME_Type::isWildcard($mime)) { + $result = MIME_Type::wildcardMatch($mime, $sourceMIME); + } else { + $result = ($mime == $sourceMIME); + } + if ($result !== false) { + return $result; + } + } + return false; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MaxDepth.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MaxDepth.php new file mode 100644 index 0000000..5d78f40 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MaxDepth.php @@ -0,0 +1,63 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: MaxDepth.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Remove the URLs with a too high number of nested directories + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_MaxDepth extends File_Archive_Predicate +{ + var $maxDepth; + + /** + * @param int $maxDepth Maximal number of folders before the actual file in + * $source->getFilename(). + * '1/2/3/4/foo.txt' will be accepted with $maxDepth == 4 and + * rejected with $maxDepth == 5 + */ + function File_Archive_Predicate_MaxDepth($maxDepth) + { + $this->maxDepth = $maxDepth; + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + $url = parse_url($source->getFilename()); + return substr_count($url['path'], '/') <= $this->maxDepth ; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MinSize.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MinSize.php new file mode 100644 index 0000000..9825622 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MinSize.php @@ -0,0 +1,59 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: MinSize.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Keep only the files larger than a given size + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_MinSize extends File_Archive_Predicate +{ + var $minSize = 0; + + /** + * @param int $minSize minimal size of the file (in Bytes) + */ + function File_Archive_Predicate_MinSize($minSize) + { + $this->minSize = $minSize; + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + $stat = $source->getStat(); + return !isset($stat[7]) || $stat[7]>=$this->minSize; + } +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MinTime.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MinTime.php new file mode 100644 index 0000000..7770b59 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/MinTime.php @@ -0,0 +1,63 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: MinTime.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Keep only the files modified after a given date (or with unknown modification + * date) + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_MinTime extends File_Archive_Predicate +{ + var $minTime = 0; + + /** + * @param int $minTime Unix timestamp of the minimal modification date of + * the files + */ + function File_Archive_Predicate_MinTime($minTime) + { + $this->minTime = $minTime; + + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + $stat = $source->getStat(); + return !isset($stat[9]) || $stat[9]>=$this->minTime; + } +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Not.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Not.php new file mode 100644 index 0000000..b058fe8 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Not.php @@ -0,0 +1,55 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Not.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Evaluates to true iif the predicate given in parameter evaluates to false + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_Not extends File_Archive_Predicate +{ + var $pred; + function File_Archive_Predicate_Not($pred) + { + $this->pred = $pred; + } + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + return !$this->pred->isTrue($source); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Or.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Or.php new file mode 100644 index 0000000..db82e4a --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/Or.php @@ -0,0 +1,85 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Or.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Evaluates to true iif one at least of the predicates + * given as constructor parameters evaluate to true + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +class File_Archive_Predicate_Or extends File_Archive_Predicate +{ + /** + * @var Array List of File_Archive_Predicate objects given as an argument + * @access private + */ + var $preds; + + /** + * Build the predicate using the optional File_Archive_Predicates given as + * arguments + * + * Example: + * new File_Archive_Predicate_And($pred1, $pred2, $pred3) + */ + function File_Archive_Predicate_And() + { + $this->preds = func_get_args(); + } + + /** + * Add a new predicate to the list + * + * @param File_Archive_Predicate The predicate to add + */ + function addPredicate($pred) + { + $this->preds[] = $pred; + } + + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) + { + foreach ($this->preds as $p) { + if ($p->isTrue($source)) { + return true; + } + } + return false; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/True.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/True.php new file mode 100644 index 0000000..fdfa5d0 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Predicate/True.php @@ -0,0 +1,45 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: True.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Predicate.php"; + +/** + * Always evaluate to true + */ +class File_Archive_Predicate_True extends File_Archive_Predicate +{ + /** + * @see File_Archive_Predicate::isTrue() + */ + function isTrue(&$source) { return true; } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader.php new file mode 100644 index 0000000..7c5db43 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader.php @@ -0,0 +1,427 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Reader.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "PEAR.php"; + +/** + * Abstract base class for all the readers + * + * A reader is a compilation of serveral files that can be read + */ +class File_Archive_Reader +{ + /** + * Move to the next file or folder in the reader + * + * @return bool false iif no more files are available + */ + function next() + { + return false; + } + + /** + * Move to the next file whose name is in directory $filename + * or is exactly $filename + * + * @param string $filename Name of the file to find in the archive + * @param bool $close If true, close the reader and search from the first file + * @return bool whether the file was found in the archive or not + */ + function select($filename, $close = true) + { + $std = $this->getStandardURL($filename); + if (substr($std, -1)=='/') { + $std = substr($std, 0, -1); + } + + if ($close) { + $error = $this->close(); + if (PEAR::isError($error)) { + return $error; + } + } + while (($error = $this->next()) === true) { + $sourceName = $this->getFilename(); + if ( + empty($std) || + + //$std is a file + $std == $sourceName || + + //$std is a directory + (strncmp($std.'/', $sourceName, strlen($std)+1) == 0 && + strlen($sourceName) > strlen($std)+1) + ) { + return true; + } + } + return $error; + } + + /** + * Returns the standard path + * Changes \ to / + * Removes the .. and . from the URL + * @param string $path a valid URL that may contain . or .. and \ + * @static + */ + function getStandardURL($path) + { + if ($path == '.') { + return ''; + } + $std = str_replace("\\", "/", $path); + while ($std != ($std = preg_replace("/[^\/:?]+\/\.\.\//", "", $std))) ; + $std = str_replace("/./", "", $std); + if (strncmp($std, "./", 2) == 0) { + /** + * If return value is going to be / (root on POSIX) + */ + if (substr($std, 2) === '/') { + return $std; + } + return substr($std, 2); + } else { + return $std; + } + } + + /** + * Returns the name of the file currently read by the reader + * + * Warning: undefined behaviour if no call to next have been + * done or if last call to next has returned false + * + * @return string Name of the current file + */ + function getFilename() + { + return PEAR::raiseError("Reader abstract function call (getFilename)"); + } + + /** + * Returns the list of filenames from the current pos to the end of the source + * The source will be closed after having called this function + * This function goes through the whole archive (which may be slow). + * If you intend to work on the reader, doing it in one pass would be faster + * + * @return array filenames from the current pos to the end of the source + */ + function getFileList() + { + $result = array(); + while ( ($error = $this->next()) === true) { + $result[] = $this->getFilename(); + } + $this->close(); + if (PEAR::isError($error)) { + return $error; + } else { + return $result; + } + } + + /** + * Returns an array of statistics about the file + * (see the PHP stat function for more information) + * + * The returned array may be empty, even if readers should try + * their best to return as many data as possible + */ + function getStat() { return array(); } + + /** + * Returns the MIME associated with the current file + * The default function does that by looking at the extension of the file + */ + function getMime() + { + require_once "File/Archive/Reader/MimeList.php"; + return File_Archive_Reader_GetMime($this->getFilename()); + } + + /** + * If the current file of the archive is a physical file, + * + * @return the name of the physical file containing the data + * or null if no such file exists + * + * The data filename may not be the same as the filename. + */ + function getDataFilename() { return null; } + + /** + * Reads some data from the current file + * If the end of the file is reached, returns null + * If $length is not specified, reads up to the end of the file + * If $length is specified reads up to $length + */ + function getData($length = -1) + { + return PEAR::raiseError("Reader abstract function call (getData)"); + } + + /** + * Skip some data and returns how many bytes have been skipped + * This is strictly equivalent to + * return strlen(getData($length)) + * But could be far more efficient + */ + function skip($length = -1) + { + $data = $this->getData($length); + if (PEAR::isError($data)) { + return $data; + } else { + return strlen($data); + } + } + + /** + * Move the current position back of a given amount of bytes. + * Not all readers may implement this function (a PEAR error will + * be returned if the reader can't rewind) + * + * @param int $length number of bytes to seek before the current pos + * or -1 to move back to the begining of the current file + * @return the number of bytes really rewinded (which may be less than + * $length if the current pos is less than $length + */ + function rewind($length = -1) + { + return PEAR::raiseError('Rewind function is not implemented on this reader'); + } + + /** + * Returns the current offset in the current file + */ + function tell() + { + $offset = $this->rewind(); + $this->skip($offset); + return $offset; + } + + /** + * Put back the reader in the state it was before the first call + * to next() + */ + function close() + { + } + + /** + * Sends the current file to the Writer $writer + * The data will be sent by chunks of at most $bufferSize bytes + * If $bufferSize <= 0 (default), the blockSize option is used + */ + function sendData(&$writer, $bufferSize = 0) + { + if (PEAR::isError($writer)) { + return $writer; + } + if ($bufferSize <= 0) { + $bufferSize = File_Archive::getOption('blockSize'); + } + + $filename = $this->getDataFilename(); + if ($filename !== null) { + $error = $writer->writeFile($filename); + if (PEAR::isError($error)) { + return $error; + } + } else { + while (($data = $this->getData($bufferSize)) !== null) { + if (PEAR::isError($data)) { + return $data; + } + $error = $writer->writeData($data); + if (PEAR::isError($error)) { + return $error; + } + } + } + } + + /** + * Sends the whole reader to $writer and close the reader + * + * @param File_Archive_Writer $writer Where to write the files of the reader + * @param bool $autoClose If true, close $writer at the end of the function. + * Default value is true + * @param int $bufferSize Size of the chunks that will be sent to the writer + * If $bufferSize <= 0 (default value), the blockSize option is used + */ + function extract(&$writer, $autoClose = true, $bufferSize = 0) + { + if (PEAR::isError($writer)) { + $this->close(); + return $writer; + } + + while (($error = $this->next()) === true) { + if ($writer->newFileNeedsMIME()) { + $mime = $this->getMime(); + } else { + $mime = null; + } + + $error = $writer->newFile( + $this->getFilename(), + $this->getStat(), + $mime + ); + if (PEAR::isError($error)) { + break; + } + $error = $this->sendData($writer, $bufferSize); + if (PEAR::isError($error)) { + break; + } + } + $this->close(); + if ($autoClose) { + $writer->close(); + } + if (PEAR::isError($error)) { + return $error; + } + } + + /** + * Extract only one file (given by the URL) + * + * @param string $filename URL of the file to extract from this + * @param File_Archive_Writer $writer Where to write the file + * @param bool $autoClose If true, close $writer at the end of the function + * Default value is true + * @param int $bufferSize Size of the chunks that will be sent to the writer + * If $bufferSize <= 0 (default value), the blockSize option is used + */ + function extractFile($filename, &$writer, + $autoClose = true, $bufferSize = 0) + { + if (PEAR::isError($writer)) { + return $writer; + } + + if (($error = $this->select($filename)) === true) { + $result = $this->sendData($writer, $bufferSize); + if (!PEAR::isError($result)) { + $result = true; + } + } else if ($error === false) { + $result = PEAR::raiseError("File $filename not found"); + } else { + $result = $error; + } + if ($autoClose) { + $error = $writer->close(); + if (PEAR::isError($error)) { + return $error; + } + } + return $result; + } + + /** + * Return a writer that allows appending files to the archive + * After having called makeAppendWriter, $this is closed and should not be + * used until the returned writer is closed. + * + * @return a writer that will allow to append files to an existing archive + * @see makeWriter + */ + function makeAppendWriter() + { + require_once "File/Archive/Predicate/False.php"; + return $this->makeWriterRemoveFiles(new File_Archive_Predicate_False()); + } + + /** + * Return a writer that has the same properties as the one returned by + * makeAppendWriter, but after having removed all the files that follow a + * given predicate. + * After a call to makeWriterRemoveFiles, $this is closed and should not + * be used until the returned writer is closed + * + * @param File_Archive_Predicate $pred the predicate verified by removed files + * @return File_Archive_Writer that allows to append files to the archive + */ + function makeWriterRemoveFiles($pred) + { + return PEAR::raiseError("Reader abstract function call (makeWriterRemoveFiles)"); + } + + /** + * Returns a writer that removes the current file + * This is a syntaxic sugar for makeWriterRemoveFiles(new File_Archive_Predicate_Current()); + */ + function makeWriterRemove() + { + require_once "File/Archive/Predicate/Current.php"; + return $this->makeWriterRemoveFiles(new File_Archive_Predicate_Current()); + } + + /** + * Removes the current file from the reader + */ + function remove() + { + $writer = $this->makeWriterRemove(); + if (PEAR::isError($writer)) { + return $writer; + } + $writer->close(); + } + + /** + * Return a writer that has the same properties as the one returned by makeWriter, but after + * having removed a block of data from the current file. The writer will append data to the current file + * no data (other than the block) will be removed + * + * @param array Lengths of the blocks. The first one will be discarded, the second one kept, the third + * one discarded... If the sum of the blocks is less than the size of the file, the comportment is the + * same as if a last block was set in the array to reach the size of the file + * if $length is -1, the file is truncated from the specified pos + * It is possible to specify blocks of size 0 + * @param int $seek relative pos of the block + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + return PEAR::raiseError("Reader abstract function call (makeWriterRemoveBlocks)"); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Ar.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Ar.php new file mode 100644 index 0000000..60cb557 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Ar.php @@ -0,0 +1,387 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Archive.php"; + +/** + * Read an Ar archive + */ +class File_Archive_Reader_Ar extends File_Archive_Reader_Archive +{ + /** + * @var int The number of files to read to reach the end of the + * current ar file + * + * @access private + */ + var $_nbBytesLeft = 0; + + /** + * @var int The size of the header in number of bytes + * The header is not always 60 bytes since it sometimes + * contains a long filename + * @access private + */ + var $_header = 0; + + /** + * @var boolean Flag set if their is a 1 byte footer after the data + * of the current ar file + * + * @access private + */ + var $_footer = false; + + /** + * @var boolean Flag that has tell us if we have read the header of the + * current file + * @access private + */ + var $_alreadyRead = false; + + /** + * @var string Name of the file being read + * @access private + */ + var $_currentFilename = null; + + /** + * @var string Stat properties of the file being read + * It has: name, utime, uid, gid, mode, size and data + * @access private + */ + var $_currentStat = null; + + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() + { + return $this->_currentFilename; + } + + /** + * @see File_Archive_Reader::close() + */ + function close() + { + $this->_currentFilename = null; + $this->_currentStat = null; + $this->_nbBytesLeft = 0; + $this->_header = 0; + $this->_footer = false; + $this->_alreadyRead = false; + return parent::close(); + } + + /** + * @see File_Archive_Reader::getStat() + */ + function getStat() + { + return $this->_currentStat; + } + + /** + * @see File_Archive_Reader::next() + */ + function next() + { + $error = parent::next(); + if ($error !== true) { + return $error; + } + + $this->source->skip( + $this->_nbBytesLeft + ($this->_footer ? 1 : 0) + ); + + $filename = $this->source->getDataFilename(); + + if (!$this->_alreadyRead) { + $header = $this->source->getData(8); + if ($header != "!\n") { + return PEAR::raiseError("File {$filename} is not a valid Ar file format (starts with $header)"); + } + $this->_alreadyRead = true; + } + + + $name = $this->source->getData(16); + $mtime = $this->source->getData(12); + $uid = $this->source->getData(6); + $gid = $this->source->getData(6); + $mode = $this->source->getData(8); + $size = $this->source->getData(10); + $delim = $this->source->getData(2); + + if ($delim === null) { + return false; + } + + // All files inside should have more than 0 bytes of size + if ($size < 0) { + return PEAR::raiseError("Files must be at least one byte long"); + } + + $this->_footer = ($size % 2 == 1); + + // if the filename starts with a length, then just read the bytes of it + if (preg_match("/\#1\/(\d+)/", $name, $matches)) { + $this->_header = 60 + $matches[1]; + $name = $this->source->getData($matches[1]); + $size -= $matches[1]; + } else { + // strip trailing spaces in name, so we can distinguish spaces in a filename with padding + $this->_header = 60; + $name = preg_replace ("/\s+$/", "", $name); + } + + $this->_nbBytesLeft = $size; + if (empty($name) || empty($mtime) || empty($uid) || + empty($gid) || empty($mode) || empty($size)) { + return PEAR::raiseError("An ar field is empty"); + } + + $this->_currentFilename = $this->getStandardURL($name); + $this->_currentStat = array( + 2 => $mode, + 'mode' => $mode, + 4 => $uid, + 'uid' => $uid, + 5 => $gid, + 'gid' => $gid, + 7 => $size, + 'size' => $size, + 9 => $mtime, + 'mtime' => $mtime + ); + + return true; + } + + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($length == -1) { + $length = $this->_nbBytesLeft; + } else { + $length = min($length, $this->_nbBytesLeft); + } + if ($length == 0) { + return null; + } else { + $this->_nbBytesLeft -= $length; + $data = $this->source->getData($length); + if (PEAR::isError($data)) { + return $data; + } + if (strlen($data) != $length) { + return PEAR::raiseError('Unexpected end of Ar archive'); + } + return $data; + } + } + + /** + * @see File_Archive_Reader::skip + */ + function skip($length = -1) + { + if ($length == -1) { + $length = $this->_nbBytesLeft; + } else { + $length = min($length, $this->_nbBytesLeft); + } + if ($length == 0) { + return 0; + } else { + $this->_nbBytesLeft -= $length; + $skipped = $this->source->skip($length); + if (PEAR::isError($skipped)) { + return $skipped; + } + if ($skipped != $length) { + return PEAR::raiseError('Unexpected end of Ar archive'); + } + return $skipped; + } + } + + /** + * @see File_Archive_Reader::rewind + */ + function rewind($length = -1) + { + if ($length == -1) { + $length = $this->_currentStat[7] - $this->_nbBytesLeft; + } else { + $length = min($length, $this->_currentStat[7] - $this->_nbBytesLeft); + } + if ($length == 0) { + return 0; + } else { + $rewinded = $this->source->rewind($length); + if (!PEAR::isError($rewinded)) { + $this->_nbBytesLeft += $rewinded; + } + return $rewinded; + } + } + + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + return $this->_currentStat[7] - $this->_nbBytesLeft; + } + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + require_once "File/Archive/Writer/Ar.php"; + + $blocks = array(); + $seek = null; + $gap = 0; + if ($this->_currentFilename !== null && $pred->isTrue($this)) { + $seek = $this->_header + $this->_currentStat[7] + ($this->_footer ? 1 : 0); + $blocks[] = $seek; //Remove this file + } + + while (($error = $this->next()) === true) { + $size = $this->_header + $this->_currentStat[7] + ($this->_footer ? 1 : 0); + if ($pred->isTrue($this)) { + if ($seek === null) { + $seek = $size; + $blocks[] = $size; + } else if ($gap > 0) { + $blocks[] = $gap; //Don't remove the files between the gap + $blocks[] = $size; + $seek += $size; + } else { + $blocks[count($blocks)-1] += $size; //Also remove this file + $seek += $size; + } + $gap = 0; + } else { + if ($seek !== null) { + $seek += $size; + $gap += $size; + } + } + } + if ($seek === null) { + $seek = 0; + } else { + if ($gap == 0) { + array_pop($blocks); + } else { + $blocks[] = $gap; + } + } + + $writer = new File_Archive_Writer_Ar(null, + $this->source->makeWriterRemoveBlocks($blocks, -$seek) + ); + $this->close(); + return $writer; + } + + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + if ($this->_currentStat === null) { + return PEAR::raiseError('No file selected'); + } + + $blockPos = $this->_currentStat[7] - $this->_nbBytesLeft + $seek; + + $this->rewind(); + $keep = false; + + $data = $this->getData($blockPos); + foreach ($blocks as $length) { + if ($keep) { + $data .= $this->getData($length); + } else { + $this->skip($length); + } + $keep = !$keep; + } + if ($keep) { + $data .= $this->getData(); + } + + $filename = $this->_currentFilename; + $stat = $this->_currentStat; + + $writer = $this->makeWriterRemove(); + if (PEAR::isError($writer)) { + return $writer; + } + + unset($stat[7]); + $writer->newFile($filename, $stat); + $writer->writeData($data); + return $writer; + } + + /** + * @see File_Archive_Reader::makeAppendWriter + */ + function makeAppendWriter() + { + require_once "File/Archive/Writer/Ar.php"; + + while (($error = $this->next()) === true) { } + if (PEAR::isError($error)) { + $this->close(); + return $error; + } + + $innerWriter = $this->source->makeWriterRemoveBlocks(array()); + if (PEAR::isError($innerWriter)) { + return $innerWriter; + } + + unset($this->source); + $this->close(); + + return new File_Archive_Writer_Ar(null, $innerWriter); + } +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Archive.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Archive.php new file mode 100644 index 0000000..71050b6 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Archive.php @@ -0,0 +1,98 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Archive.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader.php"; + +/** + * Base class for all the archive readers (that read from a single file) + */ +class File_Archive_Reader_Archive extends File_Archive_Reader +{ + /** + * @var File_Archive_Reader Single file source that contains the archive + * to uncompress + * @access protected + */ + var $source = null; + + /** + * @var bool Indicate whether the $source is currently opened + * @access private + */ + var $sourceOpened = false; + + /** + * The source was let in this state at the end + * + * @var bool Indicate whether the $source was given opened + * @access private + */ + var $sourceInitiallyOpened; + +//ABSTRACT + /** + * @see File_Archive_Reader::next() + * + * Open the source if necessary + */ + function next() + { + if (!$this->sourceOpened && ($error = $this->source->next()) !== true) { + return $error; + } + + $this->sourceOpened = true; + return true; + } + +//PUBLIC + function File_Archive_Reader_Archive(&$source, $sourceOpened = false) + { + $this->source =& $source; + $this->sourceOpened = $this->sourceInitiallyOpened = $sourceOpened; + } + /** + * Close the source if it was given closed in the constructor + * + * @see File_Archive_Reader::close() + */ + function close() + { + if (!$this->sourceInitiallyOpened && $this->sourceOpened) { + $this->sourceOpened = false; + if ($this->source !== null) { + return $this->source->close(); + } + } + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Bzip2.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Bzip2.php new file mode 100644 index 0000000..80b0cfa --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Bzip2.php @@ -0,0 +1,254 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Bzip2.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Archive.php"; +require_once "File/Archive/Writer/Files.php"; + +/** + * Uncompress a file that was compressed in the Bzip2 format + */ +class File_Archive_Reader_Bzip2 extends File_Archive_Reader_Archive +{ + var $nbRead = 0; + var $bzfile = null; + var $tmpName = null; + var $filePos = 0; + + /** + * @see File_Archive_Reader::close() + */ + function close($innerClose = true) + { + if ($this->bzfile !== null) + bzclose($this->bzfile); + if ($this->tmpName !== null) + unlink($this->tmpName); + + $this->bzfile = null; + $this->tmpName = null; + $this->nbRead = 0; + $this->filePos = 0; + return parent::close($innerClose); + } + + /** + * @see File_Archive_Reader::next() + */ + function next() + { + if (!parent::next()) { + return false; + } + + $this->nbRead++; + if ($this->nbRead > 1) { + return false; + } + + $dataFilename = $this->source->getDataFilename(); + if ($dataFilename !== null) + { + $this->tmpName = null; + $this->bzfile = @bzopen($dataFilename, 'r'); + if ($this->bzfile === false) { + return PEAR::raiseError("bzopen failed to open $dataFilename"); + } + } else { + $this->tmpName = tempnam(File_Archive::getOption('tmpDirectory'), 'far'); + + //Generate the tmp data + $dest = new File_Archive_Writer_Files(); + $dest->newFile($this->tmpName); + $this->source->sendData($dest); + $dest->close(); + + $this->bzfile = bzopen($this->tmpName, 'r'); + } + + return true; + } + /** + * Return the name of the single file contained in the archive + * deduced from the name of the archive (the extension is removed) + * + * @see File_Archive_Reader::getFilename() + */ + function getFilename() + { + $name = $this->source->getFilename(); + $pos = strrpos($name, "."); + if ($pos === false || $pos === 0) { + return $name; + } else { + return substr($name, 0, $pos); + } + } + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($length == -1) { + $data = ''; + do { + $newData = bzread($this->bzfile); + $data .= $newData; + } while ($newData != ''); + $this->filePos += strlen($data); + } else if ($length == 0) { + return ''; + } else { + $data = ''; + + //The loop is here to correct what appears to be a bzread bug + while (strlen($data) < $length) { + $newData = bzread($this->bzfile, $length - strlen($data)); + if ($newData == '') { + break; + } + $data .= $newData; + } + $this->filePos += strlen($data); + } + + return $data == '' ? null : $data; + } + + /** + * @see File_Archive_Reader::rewind + */ + function rewind($length = -1) + { + $before = $this->filePos; + + bzclose($this->bzfile); + if ($this->tmpName === null) { + $this->bzfile = bzopen($this->source->getDataFilename(), 'r'); + } else { + $this->bzfile = bzopen($this->tmpName, 'r'); + } + $this->filePos = 0; + + if ($length != -1) { + $this->skip($before - $length); + } + return $before - $this->filePos; + } + + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + return $this->filePos; + } + + /** + * @see File_Archive_Reader::makeAppendWriter() + */ + function makeAppendWriter() + { + return PEAR::raiseError('Unable to append files to a bzip2 archive'); + } + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + return PEAR::raiseError('Unable to remove files from a bzip2 archive'); + } + + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + require_once "File/Archive/Writer/Bzip2.php"; + + if ($this->nbRead == 0) { + return PEAR::raiseError('No file selected'); + } + + //Uncompress data to a temporary file + $tmp = tmpfile(); + $expectedPos = $this->filePos + $seek; + + $this->rewind(); + + //Read the begining of the file + while ($this->filePos < $expectedPos && + ($data = $this->getData(min($expectedPos - $this->filePos, 8192))) !== null) { + fwrite($tmp, $data); + } + + $keep = false; + foreach ($blocks as $length) { + if ($keep) { + $expectedPos = $this->filePos + $length; + while ($this->filePos < $expectedPos && + ($data = $this->getData(min($expectedPos - $this->filePos, 8192))) !== null) { + fwrite($tmp, $data); + } + } else { + $this->skip($length); + } + $keep = !$keep; + } + if ($keep) { + //Read the end of the file + while(($data = $this->getData(8192)) !== null) { + fwrite($tmp, $data); + } + } + fseek($tmp, 0); + + //Create the writer + $this->source->rewind(); + $innerWriter = $this->source->makeWriterRemoveBlocks(array()); //Truncate the source + unset($this->source); + $writer = new File_Archive_Writer_Bzip2(null, $innerWriter); + + //And compress data from the temporary file + while (!feof($tmp)) { + $data = fread($tmp, 8192); + $writer->writeData($data); + } + fclose($tmp); + + //Do not close inner writer since makeWriter was called + $this->close(); + + return $writer; + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Cache.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Cache.php new file mode 100644 index 0000000..5d676a1 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Cache.php @@ -0,0 +1,262 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Cache.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader.php"; + +/** + * This reader caches the files of another reader + * It allow fast access to files. This is usefull if the access to the reader + * is slow (HTTP, FTP...), but will need more IO if the file is only extracted + */ +class File_Archive_Reader_Cache extends File_Archive_Reader +{ + var $tmpFile; + var $files = array(); + var $pos = 0; + var $fromSource = true; + var $endOfSource = false; + var $source; + + /** + * $source is the reader to filter + */ + function File_Archive_Reader_Cache(&$source) + { + $this->source =& $source; + $this->tmpFile = tmpfile(); + } + + function _writeEndOfFile() + { + $bufferSize = File_Archive::getOption('blockSize'); + while (($data = $this->source->getData($bufferSize))!=null) { + fwrite($this->tmpFile, $data); + } + } + /** + * @see File_Archive_Reader::next() + */ + function next() + { + //Write the end of the current file to the temp file + if ($this->fromSource && !empty($this->files)) { + $this->_writeEndOfFile(); + } + + if ($this->pos+1 < count($this->files) && !$this->fromSource) { + $this->pos++; + fseek($this->tmpFile, $this->files[$this->pos]['pos'], SEEK_SET); + return true; + } else { + $this->fromSource = true; + if ($this->endOfSource) { + return false; + } + + $ret = $this->source->next(); + if ($ret !== true) { + $this->endOfSource = true; + $this->source->close(); + return $ret; + } + + $this->endOfSource = false; + fseek($this->tmpFile, 0, SEEK_END); + $this->files[] = array( + 'name' => $this->source->getFilename(), + 'stat' => $this->source->getStat(), + 'mime' => $this->source->getMime(), + 'pos' => ftell($this->tmpFile) + ); + $this->pos = count($this->files)-1; + + return true; + } + } + + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() { return $this->files[$this->pos]['name']; } + /** + * @see File_Archive_Reader::getStat() + */ + function getStat() { return $this->files[$this->pos]['stat']; } + /** + * @see File_Archive_Reader::getMime() + */ + function getMime() { return $this->files[$this->pos]['mime']; } + /** + * @see File_Archive_Reader::getDataFilename() + */ + function getDataFilename() { return null; } + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($this->fromSource) { + $data = $this->source->getData($length); + if (PEAR::isError($data)) { + return $data; + } + + fwrite($this->tmpFile, $data); + return $data; + } else { + if ($length == 0) { + return ''; + } + + if ($length > 0 && $this->pos+1 < count($this->files)) { + $maxSize = $this->files[$this->pos+1]['pos'] - ftell($this->tmpFile); + if ($maxSize == 0) { + return null; + } + if ($length > $maxSize) { + $length = $maxSize; + } + return fread($this->tmpFile, $length); + } else { + $contents = ''; + $blockSize = File_Archive::getOption('blockSize'); + while (!feof($this->tmpFile)) { + $contents .= fread($this->tmpFile, $blockSize); + } + return $contents == '' ? null : $contents; + } + } + } + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) + { + if ($this->fromSource) { + return strlen($this->getData($length)); + } else { + if ($length >= 0 && $this->pos+1 < count($this->files)) { + $maxSize = $this->files[$this->pos+1]['pos'] - ftell($this->tmpFile); + if ($maxSize == 0) { + return null; + } + if ($length > $maxSize) { + $length = $maxSize; + } + fseek($this->tmpFile, $length, SEEK_CUR); + return $length; + } else { + $before = ftell($this->tmpFile); + fseek($this->tmpFile, 0, SEEK_SET); + $after = fteel($this->tmpFile); + return $after - $before; + } + } + } + /** + * @see File_Archive_Reader::rewind() + */ + function rewind($length = -1) + { + if ($this->fromSource) { + $this->_writeEndOfFile(); + $this->fromSource = false; + } + $before = ftell($this->tmpFile); + $pos = $this->files[$this->pos]['pos']; + fseek($this->tmpFile, $pos, SEEK_SET); + return $pos - $before; + } + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + return ftell($this->tmpFile) - $this->files[$this->pos]['pos']; + } + /** + * @see File_Archive_Reader::close() + */ + function close() + { + $this->fromSource = false; + $this->pos = 0; + fseek($this->tmpFile, 0, SEEK_SET); + } + function _closeAndReset() + { + $this->close(); + + fclose($this->tmpFile); + $this->tmpFile = tmpfile(); + $this->endOfSource = false; + $this->files = array(); + $this->source->close(); + } + /** + * @see File_Archive_Reader::makeAppendWriter() + */ + function makeAppendWriter() + { + $writer = $this->source->makeAppendWriter(); + if (!PEAR::isError($writer)) { + $this->_closeAndReset(); + } + + return $writer; + } + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + $writer = $this->source->makeWriterRemoveFiles($pred); + if (!PEAR::isError($writer)) { + $this->_closeAndReset(); + } + return $writer; + } + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + $writer = $this->source->makeWriterRemoveBlocks($blocks, $seek); + if (!PEAR::isError($writer)) { + $this->_closeAndReset(); + } + return $writer; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName.php new file mode 100644 index 0000000..985145e --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName.php @@ -0,0 +1,136 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: ChangeName.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Relay.php"; + +/** + * Base class for readers that need to modify the name of files + */ +class File_Archive_Reader_ChangeName extends File_Archive_Reader_Relay +{ + /** + * Modify the name + * + * @param string $name Name as in the inner reader + * @return string New name as shown by this reader or false is the + * file or directory has to be skipped + */ + function modifyName($name) + { + } + + /** + * Modify the name back to the inner reader naming style + * If implemented, unmodifyName(modifyName($name)) should be true + * + * unmodifyName can be left unimplemented, this may only impact the + * efficiency of the select function (a full look up will be done when + * something more efficient with an index for example could be used on + * the inner reader of the original name is known). + * + * unmodifyName may be provided some names that where not in the inner reader + * and that cannot possibly be the result of modifyName. In this case + * unmodifyName must return false. + * + * @param string $name Name as shown by this reader + * @return string Name as in the inner reader, or false if there is no + * input such that modifyName would return $name or a file in + * a directory called $name + */ + function unmodifyName($name) + { + return null; + } + + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() + { + return $this->getStandardURL($this->modifyName(parent::getFilename())); + } + /** + * @see File_Archive_Reader::getFileList() + */ + function getFileList() + { + $list = parent::getFileList(); + $result = array(); + foreach ($list as $name) { + $result[] = $this->modifyName($name); + } + return $result; + } + /** + * @see File_Archive_Reader::select() + */ + function select($filename, $close = true) + { + $name = $this->unmodifyName($filename); + if ($name === false) { + return false; + } else if($name === null) { + $std = $this->getStandardURL($filename); + if (substr($std, -1)=='/') { + $std = substr($std, 0, -1); + } + + if ($close) { + $error = $this->close(); + if (PEAR::isError($error)) { + return $error; + } + } + while (($error = $this->next()) === true) { + $sourceName = $this->getFilename(); + $sourceName = $this->modifyName($sourceName, $this->isDirectory()); + $sourceName = $this->getStandardURL($sourceName); + if ( + empty($std) || + + //$std is a file + $std == $sourceName || + + //$std is a directory + (strncmp($std.'/', $sourceName, strlen($std)+1) == 0 && + strlen($sourceName) > strlen($std)+1) + ) { + return true; + } + } + return $error; + } else { + return $this->source->select($name, $close); + } + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/AddDirectory.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/AddDirectory.php new file mode 100644 index 0000000..42541c7 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/AddDirectory.php @@ -0,0 +1,89 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: AddDirectory.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/ChangeName.php"; + +/** + * Add a directory to the public name of all the files of a reader + * + * Example: + * If archive.tar is a file archive containing files a.txt and foo/b.txt + * new File_Archive_Reader_ChangeName_AddDirectory('bar', + * new File_Archive_Reader_Tar( + * new File_Archive_Reader_File('archive.tar') + * ) + * ) is a reader containing files bar/a.txt and bar/foo/b.txt + */ +class File_Archive_Reader_ChangeName_AddDirectory extends File_Archive_Reader_ChangeName +{ + var $baseName; + function File_Archive_Reader_ChangeName_AddDirectory($baseName, &$source) + { + // see http://pear.php.net/bugs/bug.php?id=17046&edit=12&patch=AddDirectory.php.patch&revision=1264868437 +// parent::File_Archive_Reader_ChangeName($source); + parent::__construct($source); + $this->baseName = $this->getStandardURL($baseName); + } + + /** + * Modify the name by adding baseName to it + */ + function modifyName($name) + { + return $this->baseName. + (empty($this->baseName) || empty($name) ? '': '/'). + $name; + } + + /** + * Remove baseName from the name + * Return false if the name doesn't start with baseName + */ + function unmodifyName($name) + { + if (strncmp($name, $this->baseName.'/', strlen($this->baseName)+1) == 0) { + $res = substr($name, strlen($this->baseName)+1); + if ($res === false) { + return ''; + } else { + return $res; + } + } else if (empty($this->baseName)) { + return $name; + } else if ($name == $this->baseName) { + return ''; + } else { + return false; + } + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/Callback.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/Callback.php new file mode 100644 index 0000000..32616a7 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/Callback.php @@ -0,0 +1,52 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Callback.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/ChangeName.php"; + +/** + * Discard the directory structure in a reader + */ +class File_Archive_Reader_ChangeName_Callback extends File_Archive_Reader_ChangeName +{ + var $function; + function File_Archive_Reader_ChangeName_Callback($function, &$source) + { + parent::File_Archive_Reader_ChangeName($source); + $this->function = $function; + } + + function modifyName($name) + { + return call_user_func($function, $name); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/Directory.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/Directory.php new file mode 100644 index 0000000..70e9550 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/ChangeName/Directory.php @@ -0,0 +1,100 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Directory.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/ChangeName.php"; + +/** + * Change a directory name to another + * + * Example: + * If archive.tar is a file archive containing files a.txt and foo/b.txt + * new File_Archive_Reader_ChangeName_Directory('foo', 'bar' + * new File_Archive_Reader_Tar( + * new File_Archive_Reader_File('archive.tar') + * ) + * ) is a reader containing files a.txt and bar/b.txt + */ +class File_Archive_Reader_ChangeName_Directory extends File_Archive_Reader_ChangeName +{ + var $oldBaseName; + var $newBaseName; + + function File_Archive_Reader_ChangeName_Directory + ($oldBaseName, $newBaseName, &$source) + { +// parent::File_Archive_Reader_ChangeName($source); + parent::__construct($source); + $this->oldBaseName = $this->getStandardURL($oldBaseName); + if (substr($this->oldBaseName, -1) == '/') { + $this->oldBaseName = substr($this->oldBaseName, 0, -1); + } + + $this->newBaseName = $this->getStandardURL($newBaseName); + if (substr($this->newBaseName, -1) == '/') { + $this->newBaseName = substr($this->newBaseName, 0, -1); + } + } + + function modifyName($name) + { + if (empty($this->oldBaseName) || + !strncmp($name, $this->oldBaseName.'/', strlen($this->oldBaseName)+1) || + strcmp($name, $this->oldBaseName) == 0) { + return $this->newBaseName. + ( + empty($this->newBaseName) || + strlen($name)<=strlen($this->oldBaseName)+1 ? + '' : '/' + ). + substr($name, strlen($this->oldBaseName)+1); + } else { + return $name; + } + } + function unmodifyName($name) + { + if (empty($this->newBaseName) || + !strncmp($name, $this->newBaseName.'/', strlen($this->newBaseName)+1) || + strcmp($name, $this->newBaseName) == 0) { + return $this->oldBaseName. + ( + empty($this->oldBaseName) || + strlen($name)<=strlen($this->newBaseName)+1 ? + '' : '/' + ). + substr($name, strlen($this->newBaseName)+1); + } else { + return $name; + } + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Concat.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Concat.php new file mode 100644 index 0000000..2997126 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Concat.php @@ -0,0 +1,195 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Concat.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Relay.php"; + +/** + * This reader provides one single file that is the concatenation of the data of + * all the files of another reader + */ +class File_Archive_Reader_Concat extends File_Archive_Reader +{ + var $source; + var $filename; + var $stat; + var $mime; + var $opened = false; + var $filePos = 0; + + function File_Archive_Reader_Concat(&$source, $filename, + $stat=array(), $mime=null) + { + $this->source =& $source; + $this->filename = $filename; + $this->stat = $stat; + $this->mime = $mime; + + //Compute the total length + $this->stat[7] = 0; + while (($error = $source->next()) === true) { + $sourceStat = $source->getStat(); + if (isset($sourceStat[7])) { + $this->stat[7] += $sourceStat[7]; + } else { + unset($this->stat[7]); + break; + } + } + if (isset($this->stat[7])) { + $this->stat['size'] = $this->stat[7]; + } + if (PEAR::isError($error) || PEAR::isError($source->close())) { + die("Error in File_Archive_Reader_Concat constructor ". + '('.$error->getMessage().'), cannot continue'); + } + } + + /** + * @see File_Archive_Reader::next() + */ + function next() + { + if (!$this->opened) { + return $this->opened = $this->source->next(); + } else { + return false; + } + } + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() { return $this->filename; } + /** + * @see File_Archive_Reader::getStat() + */ + function getStat() { return $this->stat; } + /** + * @see File_Archive_Reader::getMime() + */ + function getMime() + { + return $this->mime==null ? parent::getMime() : $this->mime; + } + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($length == 0) { + return ''; + } + + $result = ''; + while ($length == -1 || strlen($result)<$length) { + $sourceData = $this->source->getData( + $length==-1 ? -1 : $length - strlen($result) + ); + if (PEAR::isError($sourceData)) { + return $sourceData; + } + + if ($sourceData === null) { + $error = $this->source->next(); + if (PEAR::isError($error)) { + return $error; + } + if (!$error) { + break; + } + } else { + $result .= $sourceData; + } + } + $this->filePos += strlen($result); + return $result == '' ? null : $result; + } + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) + { + $skipped = 0; + while ($skipped < $length) { + $sourceSkipped = $this->source->skip($length); + if (PEAR::isError($sourceSkipped)) { + return $skipped; + } + $skipped += $sourceSkipped; + $filePos += $sourceSkipped; + if ($sourceSkipped < $length) { + $error = $this->source->next(); + if (PEAR::isError($error)) { + return $error; + } + if (!$error) { + return $skipped; + } + } + } + return $skipped; + } + /** + * @see File_Archive_Reader::rewind() + */ + function rewind($length = -1) + { + //TODO: implement rewind + return parent::rewind($length); + } + + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + return $this->filePos; + } + + /** + * @see File_Archive_Reader::close() + */ + function close() + { + $this->opened = false; + $this->filePos = 0; + return $this->source->close(); + } + + /** + * @see File_Archive_Reader::makeWriter + */ + function makeWriter($fileModif = true, $seek = 0) + { + return $this->source->makeWriter($fileModif, $seek); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Directory.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Directory.php new file mode 100644 index 0000000..e59daed --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Directory.php @@ -0,0 +1,309 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Directory.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Relay.php"; +require_once "File/Archive/Reader/File.php"; + +/** + * Recursively reads a directory + */ +class File_Archive_Reader_Directory extends File_Archive_Reader_Relay +{ + /** + * @var String URL of the directory that must be read + * @access private + */ + var $directory; + /** + * @var Int The subdirectories will be read up to a depth of maxRecurs + * If maxRecurs == 0, the subdirectories will not be read + * If maxRecurs == -1, the depth is considered infinite + * @access private + */ + var $maxRecurs; + /** + * @var Object Handle returned by the openedDirectory function + * @access private + */ + var $directoryHandle = null; + + /** + * $directory is the path of the directory that must be read + * If $maxRecurs is specified, the subdirectories will be read up to a depth + * of $maxRecurs. In particular, if $maxRecurs == 0, the subdirectories + * won't be read. + */ + function File_Archive_Reader_Directory($directory, $symbolic='', + $maxRecurs=-1) + { + parent::File_Archive_Reader_Relay($tmp = null); + $this->directory = empty($directory) ? '.' : $directory; + $this->symbolic = $this->getStandardURL($symbolic); + $this->maxRecurs = $maxRecurs; + } + + /** + * @see File_Archive_Reader::close() + */ + function close() + { + $error = parent::close(); + + if ($this->directoryHandle !== null) { + closedir($this->directoryHandle); + $this->directoryHandle = null; + } + + return $error; + } + + /** + * @see File_Archive_Reader::next() + * + * The files are returned in the same order as readdir + */ + function next() + { + if ($this->directoryHandle === null) { + $this->directoryHandle = opendir($this->directory); + if (!is_resource($this->directoryHandle)) { + return PEAR::raiseError( + "Directory {$this->directory} not found" + ); + } + $this->source = null; + + if (!empty($this->symbolic)) + return true; + } + + while ($this->source === null || + ($error = $this->source->next()) !== true) { + + if ($this->source !== null) { + $this->source->close(); + } + + $file = readdir($this->directoryHandle); + if ($file == '.' || $file == '..') { + continue; + } + if ($file === false) { + return false; + } + + $current = $this->directory.'/'.$file; + if (is_dir($current)) { + if ($this->maxRecurs != 0) { + $this->source = new File_Archive_Reader_Directory( + $current, $file.'/', $this->maxRecurs-1 + ); + } + + } else { + $this->source = new File_Archive_Reader_File($current, $file); + } + } + + return $error; + } + + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() + { + if ($this->source === null) { + return $this->symbolic; + } else { + return $this->symbolic . parent::getFilename(); + } + } + /** + * @see File_Archive_Reader::getStat() + */ + function getStat() + { + if ($this->source === null) { + return stat($this->directory); + } else { + return parent::getStat(); + } + } + /** + * @see File_Archive_Reader::getMime() + */ + function getMime() + { + if ($this->source === null) { + return ''; + } else { + return parent::getMime(); + } + } + /** + * @see File_Archive_Reader::getDataFilename() + */ + function getDataFilename() + { + if ($this->source === null) { + return null; + } else { + return parent::getDataFilename(); + } + } + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($this->source === null) { + return null; + } else { + return parent::getData($length); + } + } + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) + { + if ($this->source === null) { + return 0; + } else { + return parent::skip($length); + } + } + /** + * @see File_Archive_Reader::rewind() + */ + function rewind($length = -1) + { + if ($this->source === null) { + return 0; + } else { + return parent::rewind($length); + } + } + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + if ($this->source === null) { + return 0; + } else { + return parent::tell(); + } + } + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + if ($source !== null && $pred->isTrue($this)) { + $toUnlink = $this->getDataFilename(); + } else { + $toUnlink = null; + } + + while ($this->next()) { + if ($toUnlink !== null && + !@unlink($toUnlink)) { + return PEAR::raiseError("Unable to unlink $toUnlink"); + } + $toUnlink = ($pred->isTrue($this) ? $this->getDataFilename() : null); + } + if ($toUnlink !== null && + !@unlink("Unable to unlink $toUnlink")) { + return PEAR::raiseError($pred); + } + + require_once "File/Archive/Writer/Files.php"; + + $writer = new File_Archive_Writer_Files($this->directory); + $this->close(); + return $writer; + } + + function &getLastSource() + { + if ($this->source === null || + is_a($this->source, 'File_Archive_Reader_File')) { + return $this->source; + } else { + return $this->source->getLastSource(); + } + } + + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + $lastSource = &$this->getLastSource(); + if ($lastSource === null) { + return PEAR::raiseError('No file selected'); + } + + require_once "File/Archive/Writer/Files.php"; + + $writer = $lastSource->makeWriterRemoveBlocks($blocks, $seek); + if (!PEAR::isError($writer)) { + $writer->basePath = $this->directory; + $this->close(); + } + + return $writer; + } + + /** + * @see File_Archive_Reader::makeAppendWriter + */ + function makeAppendWriter() + { + require_once "File/Archive/Writer/Files.php"; + + if ($this->source === null || + is_a($this->source, 'File_Archive_Reader_File') ) { + $writer = new File_Archive_Writer_Files($this->directory); + } else { + $writer = $this->source->makeAppendWriter($seek); + } + + $this->close(); + + return $writer; + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/File.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/File.php new file mode 100644 index 0000000..2b7694c --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/File.php @@ -0,0 +1,296 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: File.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader.php"; +require_once "MIME/Type.php"; + +/** + * Reader that represents a single file + */ +class File_Archive_Reader_File extends File_Archive_Reader +{ + /** + * @var object Handle to the file being read + * @access private + */ + var $handle = null; + /** + * @var string Name of the physical file being read + * @access private + */ + var $filename; + /** + * @var string Name of the file returned by the reader + * @access private + */ + var $symbolic; + /** + * @var array Stats of the file + * Will only be set after a call to $this->getStat() + * @access private + */ + var $stat = null; + /** + * @var string Mime type of the file + * Will only be set after a call to $this->getMime() + */ + var $mime = null; + /** + * @var boolean Has the file already been read + * @access private + */ + var $alreadyRead = false; + + /** + * $filename is the physical file to read + * $symbolic is the name declared by the reader + * If $symbolic is not specified, $filename is assumed + */ + function File_Archive_Reader_File($filename, $symbolic = null, $mime = null) + { + $this->filename = $filename; + $this->mime = $mime; + if ($symbolic === null) { + $this->symbolic = $this->getStandardURL($filename); + } else { + $this->symbolic = $this->getStandardURL($symbolic); + } + } + /** + * @see File_Archive_Reader::close() + * + * Close the file handle + */ + function close() + { + $this->alreadyRead = false; + if ($this->handle !== null) { + fclose($this->handle); + $this->handle = null; + } + } + /** + * @see File_Archive_Reader::next() + * + * The first time next is called, it will open the file handle and return + * true. Then it will return false + * Raise an error if the file does not exist + */ + function next() + { + if ($this->alreadyRead) { + return false; + } else { + $this->alreadyRead = true; + return true; + } + } + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() { return $this->symbolic; } + /** + * @see File_Archive_Reader::getDataFilename() + * + * Return the name of the file + */ + function getDataFilename() { return $this->filename; } + /** + * @see File_Archive_Reader::getStat() stat() + */ + function getStat() + { + if ($this->stat === null) { + $this->stat = @stat($this->filename); + + //If we can't use the stat function + if ($this->stat === false) { + $this->stat = array(); + } + } + return $this->stat; + } + + /** + * @see File_Archive_Reader::getMime + */ + function getMime() + { + if ($this->mime === null) { + PEAR::pushErrorHandling(PEAR_ERROR_RETURN); + $this->mime = MIME_Type::autoDetect($this->getDataFilename()); + PEAR::popErrorHandling(); + + if (PEAR::isError($this->mime)) { + $this->mime = parent::getMime(); + } + } + return $this->mime; + } + + /** + * Opens the file if it was not already opened + */ + function _ensureFileOpened() + { + if ($this->handle === null) { + $this->handle = @fopen($this->filename, "r"); + + if (!is_resource($this->handle)) { + $this->handle = null; + return PEAR::raiseError("Can't open {$this->filename} for reading"); + } + if ($this->handle === false) { + $this->handle = null; + return PEAR::raiseError("File {$this->filename} not found"); + } + } + } + + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + $error = $this->_ensureFileOpened(); + if (PEAR::isError($error)) { + return $error; + } + + if (feof($this->handle)) { + return null; + } + if ($length == -1) { + $contents = ''; + $blockSize = File_Archive::getOption('blockSize'); + while (!feof($this->handle)) { + $contents .= fread($this->handle, $blockSize); + } + return $contents; + } else { + if ($length == 0) { + return ""; + } else { + return fread($this->handle, $length); + } + } + } + + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) + { + $error = $this->_ensureFileOpened(); + if (PEAR::isError($error)) { + return $error; + } + + $before = ftell($this->handle); + if (($length == -1 && @fseek($this->handle, 0, SEEK_END) === -1) || + ($length >= 0 && @fseek($this->handle, $length, SEEK_CUR) === -1)) { + return parent::skip($length); + } else { + return ftell($this->handle) - $before; + } + } + + /** + * @see File_Archive_Reader::rewind + */ + function rewind($length = -1) + { + if ($this->handle === null) { + return 0; + } + + $before = ftell($this->handle); + if (($length == -1 && @fseek($this->handle, 0, SEEK_SET) === -1) || + ($length >= 0 && @fseek($this->handle, -$length, SEEK_CUR) === -1)) { + return parent::rewind($length); + } else { + return $before - ftell($this->handle); + } + } + + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + if ($this->handle === null) { + return 0; + } else { + return ftell($this->handle); + } + } + + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + return PEAR::raiseError( + 'File_Archive_Reader_File represents a single file, you cant remove it'); + } + + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + require_once "File/Archive/Writer/Files.php"; + + $writer = new File_Archive_Writer_Files(); + + $file = $this->getDataFilename(); + $pos = $this->tell(); + $this->close(); + + $writer->openFileRemoveBlock($file, $pos + $seek, $blocks); + + return $writer; + } + + /** + * @see File_Archive_Reader::makeAppendWriter + */ + function makeAppendWriter() + { + return PEAR::raiseError( + 'File_Archive_Reader_File represents a single file.'. + ' makeAppendWriter cant be executed on it' + ); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Filter.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Filter.php new file mode 100644 index 0000000..3422414 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Filter.php @@ -0,0 +1,90 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Filter.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Relay.php"; + +/** + * Filter out the files that do not respect a given predicat + */ +class File_Archive_Reader_Filter extends File_Archive_Reader_Relay +{ + /** + * @var File_Archive_Reader_Predicat + * @access private + */ + var $predicate; + + /** + * $source is the reader to filter + */ + function File_Archive_Reader_Filter($predicate, &$source) + { + parent::File_Archive_Reader_Relay($source); + $this->predicate = $predicate; + } + + /** + * @see File_Archive_Reader::next() + */ + function next() + { + do { + $error = $this->source->next(); + if ($error !== true) { + return $error; + } + } while (!$this->predicate->isTrue($this->source)); + return true; + } + + /** + * @see File_Archive_Reader::select() + */ + function select($filename, $close = true) + { + if ($close) { + $error = $this->close(); + if (PEAR::isError($error)) { + return $error; + } + } + + do { + $error = $this->source->select($filename, false); + if ($error !== true) { + return $error; + } + } while (!$this->predicate->isTrue($this->source)); + return true; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Gzip.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Gzip.php new file mode 100644 index 0000000..82d190f --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Gzip.php @@ -0,0 +1,276 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Gzip.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Archive.php"; +require_once "File/Archive/Writer/Files.php"; + +/** + * Uncompress a file that was compressed in the Gzip format + */ +class File_Archive_Reader_Gzip extends File_Archive_Reader_Archive +{ + var $nbRead = 0; + var $filePos = 0; + var $gzfile = null; + var $tmpName = null; + + /** + * @see File_Archive_Reader::close() + */ + function close($innerClose = true) + { + if ($this->gzfile !== null) { + gzclose($this->gzfile); + } + if ($this->tmpName !== null) { + unlink($this->tmpName); + } + + $this->nbRead = 0; + $this->filePos = 0; + $this->gzfile = null; + $this->tmpName = null; + + return parent::close($innerClose); + } + + /** + * @see File_Archive_Reader::next() + */ + function next() + { + if (!parent::next()) { + return false; + } + + $this->nbRead++; + $this->filePos = 0; + if ($this->nbRead > 1) { + return false; + } + + $dataFilename = $this->source->getDataFilename(); + if ($dataFilename !== null) + { + $this->tmpName = null; + $this->gzfile = gzopen($dataFilename, 'r'); + } else { + $this->tmpName = tempnam(File_Archive::getOption('tmpDirectory'), 'far'); + + //Generate the tmp data + $dest = new File_Archive_Writer_Files(); + $dest->newFile($this->tmpName); + $this->source->sendData($dest); + $dest->close(); + + $this->gzfile = gzopen($this->tmpName, 'r'); + } + + return true; + } + + /** + * Return the name of the single file contained in the archive + * deduced from the name of the archive (the extension is removed) + * + * @see File_Archive_Reader::getFilename() + */ + function getFilename() + { + $name = $this->source->getFilename(); + $slashPos = strrpos($name, '/'); + if ($slashPos !== false) { + $name = substr($name, $slashPos+1); + } + $dotPos = strrpos($name, '.'); + if ($dotPos !== false && $dotPos > 0) { + $name = substr($name, 0, $dotPos); + } + + return $name; + } + + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($length == -1) { + $data = ''; + do + { + $newData = gzread($this->gzfile, 8192); + $data .= $newData; + } while ($newData != ''); + } else if ($length == 0) { + return ''; + } else { + $data = gzread($this->gzfile, $length); + } + + $this->filePos += strlen($data); + return $data == '' ? null : $data; + } + + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) + { + if($length == -1) { + do + { + $tmp = gzread($this->gzfile, 8192); + $this->filePos += strlen($tmp); + } while ($tmp != ''); + } else { + if (@gzseek($this->gzfile, $this->filePos + $length) === -1) { + return parent::skip($length); + } else { + $this->filePos += $length; + return $length; + } + } + } + + /** + * @see File_Archive_Reader::rewind() + */ + function rewind($length = -1) + { + if ($length == -1) { + if (@gzseek($this->gzfile, 0) === -1) { + return parent::rewind($length); + } else { + $tmp = $this->filePos; + $this->filePos = 0; + return $tmp; + } + } else { + $length = min($length, $this->filePos); + if (@gzseek($this->gzfile, $this->filePos - $length) === -1) { + return parent::rewind($length); + } else { + $this->filePos -= $length; + return $length; + } + } + } + + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + return $this->filePos; + } + + /** + * @see File_Archive_Reader::makeAppendWriter() + */ + function makeAppendWriter() + { + return PEAR::raiseError('Unable to append files to a gzip archive'); + } + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + return PEAR::raiseError('Unable to remove files from a gzip archive'); + } + + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + require_once "File/Archive/Writer/Gzip.php"; + + if ($this->nbRead == 0) { + return PEAR::raiseError('No file selected'); + } + + //Uncompress data to a temporary file + $tmp = tmpfile(); + $expectedPos = $this->filePos + $seek; + $this->rewind(); + + //Read the begining of the file + while ($this->filePos < $expectedPos && + ($data = $this->getData(min($expectedPos - $this->filePos, 8192))) !== null) { + fwrite($tmp, $data); + } + + $keep = false; + foreach ($blocks as $length) { + if ($keep) { + $expectedPos = $this->filePos + $length; + while ($this->filePos < $expectedPos && + ($data = $this->getData(min($expectedPos - $this->filePos, 8192))) !== null) { + fwrite($tmp, $data); + } + } else { + $this->skip($length); + } + $keep = !$keep; + } + if ($keep) { + //Read the end of the file + while(($data = $this->getData(8192)) !== null) { + fwrite($tmp, $data); + } + } + fseek($tmp, 0); + + //Create the writer + $this->source->rewind(); + $innerWriter = $this->source->makeWriterRemoveBlocks(array()); //Truncate the source + unset($this->source); + $writer = new File_Archive_Writer_Gzip(null, $innerWriter); + + //And compress data from the temporary file + while (!feof($tmp)) { + $data = fread($tmp, 8192); + $writer->writeData($data); + } + fclose($tmp); + + //Do not close inner writer since makeWriter was called + $this->close(); + + return $writer; + } + +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Memory.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Memory.php new file mode 100644 index 0000000..3caa2b5 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Memory.php @@ -0,0 +1,227 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Memory.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader.php"; + +/** + * A reader that takes its input from a memory buffer + */ +class File_Archive_Reader_Memory extends File_Archive_Reader +{ + /** + * @var String Name of the file exported by this reader + * @access private + */ + var $filename; + /** + * @var Array Stat of the file exported by this reader + * @access private + */ + var $stat; + /** + * @var String MIME type of the file exported by this reader + * @access private + */ + var $mime; + /** + * @var String Memory buffer that contains the data of the file + * @access private + */ + var $memory; + /** + * @var Int Current position in the file + * @access private + */ + var $offset = 0; + /** + * @var Boolean Has the file already been read + * @access private + */ + var $alreadyRead = false; + + /** + * @param string $memory is the content of the file. + * This parameter is passed as a reference for performance + * reasons. The content should not be changer after the constructor + * @param string $filename is the name of the file + * @param array $stat are the statistics of the file. The size will be + * recomputed from $memory + * @param string $mime is the mime type of the file + */ + function File_Archive_Reader_Memory(&$memory, $filename, + $stat=array(), $mime=null) + { + $this->memory = &$memory; + $this->filename = $this->getStandardURL($filename); + $this->stat = $stat; + $this->stat[7] = $this->stat['size'] = strlen($this->memory); + $this->mime = $mime; + } + + /** + * The subclass should overwrite this function to change the filename, stat + * and memory + */ + function next() + { + if ($this->alreadyRead) { + return false; + } else { + $this->alreadyRead = true; + return true; + } + } + + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() { return $this->filename; } + /** + * @see File_Archive_Reader::getStat() + */ + function getStat() { return $this->stat; } + /** + * @see File_Archive_Reader::getMime() + */ + function getMime() + { + return $this->mime==null ? parent::getMime() : $this->mime; + } + + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($this->offset == strlen($this->memory)) { + return null; + } + if ($length == -1) { + $actualLength = strlen($this->memory) - $this->offset; + } else { + $actualLength = min($length, strlen($this->memory) - $this->offset); + } + $result = substr($this->memory, $this->offset, $actualLength); + $this->offset += $actualLength; + return $result; + } + + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) + { + if ($length == -1) { + $length = strlen($this->memory) - $this->offset; + } else { + $length = min($length, strlen($this->memory) - $this->offset); + } + $this->offset += $length; + return $length; + } + + /** + * @see File_Archive_Reader::rewind() + */ + function rewind($length = -1) + { + if ($length == -1) { + $tmp = $this->offset; + $this->offset = 0; + return $tmp; + } else { + $length = min($length, $this->offset); + $this->offset -= $length; + return $length; + } + } + + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + return $this->offset; + } + + /** + * @see File_Archive_Reader::close() + */ + function close() + { + $this->offset = 0; + $this->alreadyRead = false; + } + + /** + * @see File_Archive_Reader::makeAppendWriter() + */ + function makeAppendWriter() + { + return PEAR::raiseError('Unable to append files to a memory archive'); + } + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + return PEAR::raiseError('Unable to remove files from a memory archive'); + } + + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + require_once "File/Archive/Writer/Memory.php"; + $data = substr($this->memory, 0, $this->offset + $seek); + $this->memory = substr($this->memory, $this->offset + $seek); + + $keep = false; + foreach ($blocks as $length) { + if ($keep) { + $data .= substr($this->memory, 0, $length); + } + $this->memory = substr($this->memory, $length); + $keep = !$keep; + } + if ($keep) { + $this->memory = $data . $this->memory; + } else { + $this->memory = $data; + } + $this->close(); + return new File_Archive_Writer_Memory($this->memory, strlen($this->memory)); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/MimeList.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/MimeList.php new file mode 100644 index 0000000..cc203ef --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/MimeList.php @@ -0,0 +1,939 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: MimeList.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +/** + * Returns the MIME of the filename, deducted from its extension + * If the extension is unknown, returns "application/octet-stream" + */ +function File_Archive_Reader_GetMime($filename) +{ + $pos = strrpos($filename, '.'); + $extension = ""; + if ($pos !== false) { + $extension = strtolower(substr($filename, $pos+1)); + } + + switch($extension) { + case '3dmf': + return 'x-world/x-3dmf'; + case 'a': + return 'application/octet-stream'; + case 'aab': + return 'application/x-authorware-bin'; + case 'aam': + return 'application/x-authorware-map'; + case 'aas': + return 'application/x-authorware-seg'; + case 'abc': + return 'text/vnd.abc'; + case 'acgi': + return 'text/html'; + case 'afl': + return 'video/animaflex'; + case 'ai': + return 'application/postscript'; + case 'aif': + return 'audio/aiff'; + case 'aifc': + return 'audio/aiff'; + case 'aiff': + return 'audio/aiff'; + case 'aim': + return 'application/x-aim'; + case 'aip': + return 'text/x-audiosoft-intra'; + case 'ani': + return 'application/x-navi-animation'; + case 'aos': + return 'application/x-nokia-9000-communicator-add-on-software'; + case 'aps': + return 'application/mime'; + case 'arc': + return 'application/octet-stream'; + case 'arj': + return 'application/arj'; + case 'art': + return 'image/x-jg'; + case 'asf': + return 'video/x-ms-asf'; + case 'asm': + return 'text/x-asm'; + case 'asp': + return 'text/asp'; + case 'asx': + return 'application/x-mplayer2'; + case 'au': + return 'audio/basic'; + case 'avi': + return 'application/x-troff-msvideo'; + case 'avs': + return 'video/avs-video'; + case 'bcpio': + return 'application/x-bcpio'; + case 'bin': + return 'application/x-binary'; + case 'bm': + return 'image/bmp'; + case 'bmp': + return 'image/bmp'; + case 'boo': + return 'application/book'; + case 'book': + return 'application/book'; + case 'boz': + return 'application/x-bzip2'; + case 'bsh': + return 'application/x-bsh'; + case 'bz': + return 'application/x-bzip'; + case 'bz2': + return 'application/x-bzip2'; + case 'c': + return 'text/plain'; + case 'c++': + return 'text/plain'; + case 'cat': + return 'application/vnd.ms-pki.seccat'; + case 'cc': + return 'text/plain'; + case 'ccad': + return 'application/clariscad'; + case 'cco': + return 'application/x-cocoa'; + case 'cdf': + return 'application/cdf'; + case 'cer': + return 'application/pkix-cert'; + case 'cha': + return 'application/x-chat'; + case 'chat': + return 'application/x-chat'; + case 'class': + return 'application/java'; + case 'com': + return 'application/octet-stream'; + case 'conf': + return 'text/plain'; + case 'cpio': + return 'application/x-cpio'; + case 'cpp': + return 'text/x-c'; + case 'cpt': + return 'application/mac-compactpro'; + case 'crl': + return 'application/pkcs-crl'; + case 'csh': + return 'application/x-csh'; + case 'css': + return 'text/css'; + case 'cxx': + return 'text/plain'; + case 'dcr': + return 'application/x-director'; + case 'deepv': + return 'application/x-deepv'; + case 'def': + return 'text/plain'; + case 'der': + return 'application/x-x509-ca-cert'; + case 'dif': + return 'video/x-dv'; + case 'dir': + return 'application/x-director'; + case 'dl': + return 'video/dl'; + case 'doc': + return 'application/msword'; + case 'dot': + return 'application/msword'; + case 'dp': + return 'application/commonground'; + case 'drw': + return 'application/drafting'; + case 'dump': + return 'application/octet-stream'; + case 'dv': + return 'video/x-dv'; + case 'dvi': + return 'application/x-dvi'; + case 'dwf': + return 'drawing/x-dwf (old)'; + case 'dwg': + return 'application/acad'; + case 'dxf': + return 'application/dxf'; + case 'dxr': + return 'application/x-director'; + case 'el': + return 'text/x-script.elisp'; + case 'elc': + return 'application/x-bytecode.elisp (compiled elisp)'; + case 'env': + return 'application/x-envoy'; + case 'eps': + return 'application/postscript'; + case 'es': + return 'application/x-esrehber'; + case 'etx': + return 'text/x-setext'; + case 'evy': + return 'application/envoy'; + case 'exe': + return 'application/octet-stream'; + case 'f': + return 'text/plain'; + case 'f77': + return 'text/x-fortran'; + case 'f90': + return 'text/plain'; + case 'fdf': + return 'application/vnd.fdf'; + case 'fif': + return 'application/fractals'; + case 'fli': + return 'video/fli'; + case 'flo': + return 'image/florian'; + case 'flx': + return 'text/vnd.fmi.flexstor'; + case 'fmf': + return 'video/x-atomic3d-feature'; + case 'for': + return 'text/plain'; + case 'fpx': + return 'image/vnd.fpx'; + case 'frl': + return 'application/freeloader'; + case 'funk': + return 'audio/make'; + case 'g': + return 'text/plain'; + case 'g3': + return 'image/g3fax'; + case 'gif': + return 'image/gif'; + case 'gl': + return 'video/gl'; + case 'gsd': + return 'audio/x-gsm'; + case 'gsm': + return 'audio/x-gsm'; + case 'gsp': + return 'application/x-gsp'; + case 'gss': + return 'application/x-gss'; + case 'gtar': + return 'application/x-gtar'; + case 'gz': + return 'application/x-compressed'; + case 'gzip': + return 'application/x-gzip'; + case 'h': + return 'text/plain'; + case 'hdf': + return 'application/x-hdf'; + case 'help': + return 'application/x-helpfile'; + case 'hgl': + return 'application/vnd.hp-hpgl'; + case 'hh': + return 'text/plain'; + case 'hlb': + return 'text/x-script'; + case 'hlp': + return 'application/hlp'; + case 'hpg': + return 'application/vnd.hp-hpgl'; + case 'hpgl': + return 'application/vnd.hp-hpgl'; + case 'hqx': + return 'application/binhex'; + case 'hta': + return 'application/hta'; + case 'htc': + return 'text/x-component'; + case 'htm': + return 'text/html'; + case 'html': + return 'text/html'; + case 'htmls': + return 'text/html'; + case 'htt': + return 'text/webviewhtml'; + case 'htx': + return 'text/html'; + case 'ice': + return 'x-conference/x-cooltalk'; + case 'ico': + return 'image/x-icon'; + case 'idc': + return 'text/plain'; + case 'ief': + return 'image/ief'; + case 'iefs': + return 'image/ief'; + case 'iges': + return 'application/iges'; + case 'igs': + return 'application/iges'; + case 'ima': + return 'application/x-ima'; + case 'imap': + return 'application/x-httpd-imap'; + case 'inf': + return 'application/inf'; + case 'ins': + return 'application/x-internett-signup'; + case 'ip': + return 'application/x-ip2'; + case 'isu': + return 'video/x-isvideo'; + case 'it': + return 'audio/it'; + case 'iv': + return 'application/x-inventor'; + case 'ivr': + return 'i-world/i-vrml'; + case 'ivy': + return 'application/x-livescreen'; + case 'jam': + return 'audio/x-jam'; + case 'jav': + return 'text/plain'; + case 'java': + return 'text/plain'; + case 'jcm': + return 'application/x-java-commerce'; + case 'jfif': + return 'image/jpeg'; + case 'jfif-tbnl': + return 'image/jpeg'; + case 'jpe': + return 'image/jpeg'; + case 'jpeg': + return 'image/jpeg'; + case 'jpg': + return 'image/jpeg'; + case 'jps': + return 'image/x-jps'; + case 'js': + return 'application/x-javascript'; + case 'jut': + return 'image/jutvision'; + case 'kar': + return 'audio/midi'; + case 'ksh': + return 'application/x-ksh'; + case 'la': + return 'audio/nspaudio'; + case 'lam': + return 'audio/x-liveaudio'; + case 'latex': + return 'application/x-latex'; + case 'lha': + return 'application/lha'; + case 'lhx': + return 'application/octet-stream'; + case 'list': + return 'text/plain'; + case 'lma': + return 'audio/nspaudio'; + case 'log': + return 'text/plain'; + case 'lsp': + return 'application/x-lisp'; + case 'lst': + return 'text/plain'; + case 'lsx': + return 'text/x-la-asf'; + case 'ltx': + return 'application/x-latex'; + case 'lzh': + return 'application/octet-stream'; + case 'lzx': + return 'application/lzx'; + case 'm': + return 'text/plain'; + case 'm1v': + return 'video/mpeg'; + case 'm2a': + return 'audio/mpeg'; + case 'm2v': + return 'video/mpeg'; + case 'm3u': + return 'audio/x-mpequrl'; + case 'man': + return 'application/x-troff-man'; + case 'map': + return 'application/x-navimap'; + case 'mar': + return 'text/plain'; + case 'mbd': + return 'application/mbedlet'; + case 'mc$': + return 'application/x-magic-cap-package-1.0'; + case 'mcd': + return 'application/mcad'; + case 'mcf': + return 'image/vasa'; + case 'mcp': + return 'application/netmc'; + case 'me': + return 'application/x-troff-me'; + case 'mht': + return 'message/rfc822'; + case 'mhtml': + return 'message/rfc822'; + case 'mid': + return 'application/x-midi'; + case 'midi': + return 'audio/midi'; + case 'mif': + return 'application/x-frame'; + case 'mime': + return 'message/rfc822'; + case 'mjf': + return 'audio/x-vnd.audioexplosion.mjuicemediafile'; + case 'mjpg': + return 'video/x-motion-jpeg'; + case 'mm': + return 'application/base64'; + case 'mme': + return 'application/base64'; + case 'mod': + return 'audio/mod'; + case 'moov': + return 'video/quicktime'; + case 'mov': + return 'video/quicktime'; + case 'movie': + return 'video/x-sgi-movie'; + case 'mp2': + return 'video/mpeg'; + case 'mp3': + return 'video/mpeg'; + case 'mpa': + return 'audio/mpeg'; + case 'mpc': + return 'application/x-project'; + case 'mpe': + return 'video/mpeg'; + case 'mpeg': + return 'video/mpeg'; + case 'mpg': + return 'video/mpeg'; + case 'mpga': + return 'audio/mpeg'; + case 'mpp': + return 'application/vnd.ms-project'; + case 'mpt': + return 'application/x-project'; + case 'mpv': + return 'application/x-project'; + case 'mpx': + return 'application/x-project'; + case 'mrc': + return 'application/marc'; + case 'ms': + return 'application/x-troff-ms'; + case 'mv': + return 'video/x-sgi-movie'; + case 'my': + return 'audio/make'; + case 'mzz': + return 'application/x-vnd.audioexplosion.mzz'; + case 'nap': + return 'image/naplps'; + case 'naplps': + return 'image/naplps'; + case 'nc': + return 'application/x-netcdf'; + case 'ncm': + return 'application/vnd.nokia.configuration-message'; + case 'nif': + return 'image/x-niff'; + case 'niff': + return 'image/x-niff'; + case 'nix': + return 'application/x-mix-transfer'; + case 'nsc': + return 'application/x-conference'; + case 'nvd': + return 'application/x-navidoc'; + case 'o': + return 'application/octet-stream'; + case 'oda': + return 'application/oda'; + case 'omc': + return 'application/x-omc'; + case 'omcd': + return 'application/x-omcdatamaker'; + case 'omcr': + return 'application/x-omcregerator'; + case 'p': + return 'text/x-pascal'; + case 'p10': + return 'application/pkcs10'; + case 'p12': + return 'application/pkcs-12'; + case 'p7a': + return 'application/x-pkcs7-signature'; + case 'p7c': + return 'application/pkcs7-mime'; + case 'p7m': + return 'application/pkcs7-mime'; + case 'p7r': + return 'application/x-pkcs7-certreqresp'; + case 'p7s': + return 'application/pkcs7-signature'; + case 'part': + return 'application/pro_eng'; + case 'pas': + return 'text/pascal'; + case 'pbm': + return 'image/x-portable-bitmap'; + case 'pcl': + return 'application/vnd.hp-pcl'; + case 'pct': + return 'image/x-pict'; + case 'pcx': + return 'image/x-pcx'; + case 'pdb': + return 'chemical/x-pdb'; + case 'pdf': + return 'application/pdf'; + case 'pfunk': + return 'audio/make'; + case 'pgm': + return 'image/x-portable-graymap'; + case 'pic': + return 'image/pict'; + case 'pict': + return 'image/pict'; + case 'pkg': + return 'application/x-newton-compatible-pkg'; + case 'pko': + return 'application/vnd.ms-pki.pko'; + case 'pl': + return 'text/plain'; + case 'plx': + return 'application/x-pixclscript'; + case 'pm': + return 'image/x-xpixmap'; + case 'pm4': + return 'application/x-pagemaker'; + case 'pm5': + return 'application/x-pagemaker'; + case 'png': + return 'image/png'; + case 'pnm': + return 'application/x-portable-anymap'; + case 'pot': + return 'application/mspowerpoint'; + case 'pov': + return 'model/x-pov'; + case 'ppa': + return 'application/vnd.ms-powerpoint'; + case 'ppm': + return 'image/x-portable-pixmap'; + case 'pps': + return 'application/mspowerpoint'; + case 'ppt': + return 'application/mspowerpoint'; + case 'ppz': + return 'application/mspowerpoint'; + case 'pre': + return 'application/x-freelance'; + case 'prt': + return 'application/pro_eng'; + case 'ps': + return 'application/postscript'; + case 'psd': + return 'application/octet-stream'; + case 'pvu': + return 'paleovu/x-pv'; + case 'pwz': + return 'application/vnd.ms-powerpoint'; + case 'py': + return 'text/x-script.phyton'; + case 'pyc': + return 'applicaiton/x-bytecode.python'; + case 'qcp': + return 'audio/vnd.qcelp'; + case 'qd3': + return 'x-world/x-3dmf'; + case 'qd3d': + return 'x-world/x-3dmf'; + case 'qif': + return 'image/x-quicktime'; + case 'qt': + return 'video/quicktime'; + case 'qtc': + return 'video/x-qtc'; + case 'qti': + return 'image/x-quicktime'; + case 'qtif': + return 'image/x-quicktime'; + case 'ra': + return 'audio/x-pn-realaudio'; + case 'ram': + return 'audio/x-pn-realaudio'; + case 'ras': + return 'application/x-cmu-raster'; + case 'rast': + return 'image/cmu-raster'; + case 'rexx': + return 'text/x-script.rexx'; + case 'rf': + return 'image/vnd.rn-realflash'; + case 'rgb': + return 'image/x-rgb'; + case 'rm': + return 'application/vnd.rn-realmedia'; + case 'rmi': + return 'audio/mid'; + case 'rmm': + return 'audio/x-pn-realaudio'; + case 'rmp': + return 'audio/x-pn-realaudio'; + case 'rng': + return 'application/ringing-tones'; + case 'rnx': + return 'application/vnd.rn-realplayer'; + case 'roff': + return 'application/x-troff'; + case 'rp': + return 'image/vnd.rn-realpix'; + case 'rpm': + return 'audio/x-pn-realaudio-plugin'; + case 'rt': + return 'text/richtext'; + case 'rtf': + return 'application/rtf'; + case 'rtx': + return 'text/richtext'; + case 'rv': + return 'video/vnd.rn-realvideo'; + case 's': + return 'text/x-asm'; + case 's3m': + return 'audio/s3m'; + case 'saveme': + return 'application/octet-stream'; + case 'sbk': + return 'application/x-tbook'; + case 'scm': + return 'application/x-lotusscreencam'; + case 'sdml': + return 'text/plain'; + case 'sdp': + return 'application/sdp'; + case 'sdr': + return 'application/sounder'; + case 'sea': + return 'application/sea'; + case 'set': + return 'application/set'; + case 'sgm': + return 'text/sgml'; + case 'sgml': + return 'text/sgml'; + case 'sh': + return 'application/x-bsh'; + case 'shar': + return 'application/x-bsh'; + case 'shtml': + return 'text/html'; + case 'sid': + return 'audio/x-psid'; + case 'sit': + return 'application/x-sit'; + case 'skd': + return 'application/x-koan'; + case 'skm': + return 'application/x-koan'; + case 'skp': + return 'application/x-koan'; + case 'skt': + return 'application/x-koan'; + case 'sl': + return 'application/x-seelogo'; + case 'smi': + return 'application/smil'; + case 'smil': + return 'application/smil'; + case 'snd': + return 'audio/basic'; + case 'sol': + return 'application/solids'; + case 'spc': + return 'application/x-pkcs7-certificates'; + case 'spl': + return 'application/futuresplash'; + case 'spr': + return 'application/x-sprite'; + case 'sprite': + return 'application/x-sprite'; + case 'src': + return 'application/x-wais-source'; + case 'ssi': + return 'text/x-server-parsed-html'; + case 'ssm': + return 'application/streamingmedia'; + case 'sst': + return 'application/vnd.ms-pki.certstore'; + case 'step': + return 'application/step'; + case 'stl': + return 'application/sla'; + case 'stp': + return 'application/step'; + case 'sv4cpio': + return 'application/x-sv4cpio'; + case 'sv4crc': + return 'application/x-sv4crc'; + case 'svf': + return 'image/vnd.dwg'; + case 'svr': + return 'application/x-world'; + case 'swf': + return 'application/x-shockwave-flash'; + case 't': + return 'application/x-troff'; + case 'talk': + return 'text/x-speech'; + case 'tar': + return 'application/x-tar'; + case 'tbk': + return 'application/toolbook'; + case 'tcl': + return 'application/x-tcl'; + case 'tcsh': + return 'text/x-script.tcsh'; + case 'tex': + return 'application/x-tex'; + case 'texi': + return 'application/x-texinfo'; + case 'texinfo': + return 'application/x-texinfo'; + case 'text': + return 'text/plain'; + case 'tgz': + return 'application/x-compressed'; + case 'tif': + return 'image/tiff'; + case 'tiff': + return 'image/tiff'; + case 'tr': + return 'application/x-troff'; + case 'tsi': + return 'audio/tsp-audio'; + case 'tsp': + return 'application/dsptype'; + case 'tsv': + return 'text/tab-separated-values'; + case 'turbot': + return 'image/florian'; + case 'txt': + return 'text/plain'; + case 'uil': + return 'text/x-uil'; + case 'uni': + return 'text/uri-list'; + case 'unis': + return 'text/uri-list'; + case 'unv': + return 'application/i-deas'; + case 'uri': + return 'text/uri-list'; + case 'uris': + return 'text/uri-list'; + case 'ustar': + return 'multipart/x-ustar'; + case 'uu': + return 'application/octet-stream'; + case 'uue': + return 'text/x-uuencode'; + case 'vcd': + return 'application/x-cdlink'; + case 'vcs': + return 'text/x-vcalendar'; + case 'vda': + return 'application/vda'; + case 'vdo': + return 'video/vdo'; + case 'vew': + return 'application/groupwise'; + case 'viv': + return 'video/vivo'; + case 'vivo': + return 'video/vivo'; + case 'vmd': + return 'application/vocaltec-media-desc'; + case 'vmf': + return 'application/vocaltec-media-file'; + case 'voc': + return 'audio/voc'; + case 'vos': + return 'video/vosaic'; + case 'vox': + return 'audio/voxware'; + case 'vqe': + return 'audio/x-twinvq-plugin'; + case 'vqf': + return 'audio/x-twinvq'; + case 'vql': + return 'audio/x-twinvq-plugin'; + case 'vrml': + return 'application/x-vrml'; + case 'vrt': + return 'x-world/x-vrt'; + case 'vsd': + return 'application/x-visio'; + case 'vst': + return 'application/x-visio'; + case 'vsw': + return 'application/x-visio'; + case 'w60': + return 'application/wordperfect6.0'; + case 'w61': + return 'application/wordperfect6.1'; + case 'w6w': + return 'application/msword'; + case 'wav': + return 'audio/wav'; + case 'wb1': + return 'application/x-qpro'; + case 'wbmp': + return 'image/vnd.wap.wbmp'; + case 'web': + return 'application/vnd.xara'; + case 'wiz': + return 'application/msword'; + case 'wk1': + return 'application/x-123'; + case 'wmf': + return 'windows/metafile'; + case 'wml': + return 'text/vnd.wap.wml'; + case 'wmlc': + return 'application/vnd.wap.wmlc'; + case 'wmls': + return 'text/vnd.wap.wmlscript'; + case 'wmlsc': + return 'application/vnd.wap.wmlscriptc'; + case 'word': + return 'application/msword'; + case 'wp': + return 'application/wordperfect'; + case 'wp5': + return 'application/wordperfect6.0'; + case 'wp6': + return 'application/wordperfect'; + case 'wpd': + return 'application/x-wpwin'; + case 'wq1': + return 'application/x-lotus'; + case 'wri': + return 'application/mswrite'; + case 'wrl': + return 'model/vrml'; + case 'wrz': + return 'model/vrml'; + case 'wsc': + return 'text/scriplet'; + case 'wsrc': + return 'application/x-wais-source'; + case 'wtk': + return 'application/x-wintalk'; + case 'xbm': + return 'image/x-xbitmap'; + case 'xdr': + return 'video/x-amt-demorun'; + case 'xgz': + return 'xgl/drawing'; + case 'xif': + return 'image/vnd.xiff'; + case 'xl': + return 'application/excel'; + case 'xla': + return 'application/excel'; + case 'xlb': + return 'application/excel'; + case 'xlc': + return 'application/excel'; + case 'xld': + return 'application/excel'; + case 'xlk': + return 'application/excel'; + case 'xll': + return 'application/excel'; + case 'xlm': + return 'application/excel'; + case 'xls': + return 'application/excel'; + case 'xlt': + return 'application/excel'; + case 'xlv': + return 'application/excel'; + case 'xlw': + return 'application/excel'; + case 'xm': + return 'audio/xm'; + case 'xml': + return 'application/xml'; + case 'xmz': + return 'xgl/movie'; + case 'xpix': + return 'application/x-vnd.ls-xpix'; + case 'xpm': + return 'image/xpm'; + case 'x-png': + return 'image/png'; + case 'xsr': + return 'video/x-amt-showrun'; + case 'xwd': + return 'image/x-xwd'; + case 'xyz': + return 'chemical/x-pdb'; + case 'z': + return 'application/x-compress'; + case 'zip': + return 'application/x-compressed'; + case 'zoo': + return 'application/octet-stream'; + case 'zsh': + return 'text/x-script.zsh'; + default: + return 'application/octet-stream'; + } +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Multi.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Multi.php new file mode 100644 index 0000000..1445867 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Multi.php @@ -0,0 +1,95 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Multi.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Relay.php"; + +/** + * Regroups several readers to make them appear as a single one + */ +class File_Archive_Reader_Multi extends File_Archive_Reader_Relay +{ + /** + * @var Array All the sources regrouped in this reader + * @access private + */ + var $sources = array(); + /** + * @var Int Index of the source being read currently + * @access private + */ + var $currentIndex = 0; + + function File_Archive_Reader_Multi() + { + parent::File_Archive_Reader_Relay($tmp = null); + } + + /** + * Add a new reader to the list of readers + * @param File_Archive_Reader $source The source to add + */ + function addSource(&$source) + { + $this->sources[] =& $source; + } + + /** + * @see File_Archive_Reader::next() + */ + function next() + { + while (array_key_exists($this->currentIndex, $this->sources)) { + $this->source =& $this->sources[$this->currentIndex]; + + if (($error = $this->source->next()) === false) { + $error = $this->source->close(); + if (PEAR::isError($error)) { + return $error; + } + $this->currentIndex++; + } else { + return $error; + } + } + return false; + } + /** + * @see File_Archive_Reader::close() + */ + function close() + { + $error = parent::close(); + $this->currentIndex = 0; + return $error; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Relay.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Relay.php new file mode 100644 index 0000000..216a1f0 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Relay.php @@ -0,0 +1,134 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Relay.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader.php"; + +/** + * This reader appear exactly as $source does + * This is usefull if you want to dynamically change $source or change + * its behaviour + */ +class File_Archive_Reader_Relay extends File_Archive_Reader +{ + /** + * @var File_Archive_Reader This reader will have the same comportment as + * $source + * @access protected + */ + var $source; + + function File_Archive_Reader_Relay(&$source) + { + $this->source =& $source; + } + + /** + * @see File_Archive_Reader::next() + */ + function next() { return $this->source->next(); } + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() { return $this->source->getFilename(); } + /** + * @see File_Archive_Reader::getStat() + */ + function getStat() { return $this->source->getStat(); } + /** + * @see File_Archive_Reader::getMime() + */ + function getMime() { return $this->source->getMime(); } + /** + * @see File_Archive_Reader::getDataFilename() + */ + function getDataFilename() { return $this->source->getDataFilename(); } + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) { return $this->source->getData($length); } + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) { return $this->source->skip($length); } + /** + * @see File_Archive_Reader::rewind() + */ + function rewind($length = -1) { return $this->source->rewind($length); } + /** + * @see File_Archive_Reader::tell() + */ + function tell() { return $this->source->tell(); } + + /** + * @see File_Archive_Reader::close() + */ + function close() + { + if ($this->source !== null) { + return $this->source->close(); + } + } + /** + * @see File_Archive_Reader::makeAppendWriter() + */ + function makeAppendWriter() + { + $writer = $this->source->makeAppendWriter(); + if (!PEAR::isError($writer)) { + $this->close(); + } + return $writer; + } + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + $writer = $this->source->makeWriterRemoveFiles($pred); + if (!PEAR::isError($writer)) { + $this->close(); + } + return $writer; + } + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + $writer = $this->source->makeWriterRemoveBlocks($blocks, $seek); + if (!PEAR::isError($writer)) { + $this->close(); + } + return $writer; + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Select.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Select.php new file mode 100644 index 0000000..952a589 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Select.php @@ -0,0 +1,63 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Select.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Relay.php"; + +/** + * Reader that keeps the files selected by File_Archive::select function + */ +class File_Archive_Reader_Select extends File_Archive_Reader_Relay +{ + /** + * @var File_Archive_Reader_Predicat + * @access private + */ + var $filename; + + /** + * $source is the reader to filter + */ + function File_Archive_Reader_Select($filename, &$source) + { + parent::File_Archive_Reader_Relay($source); + $this->filename = $filename; + } + + /** + * @see File_Archive_Reader::next() + */ + function next() + { + return $this->source->select($this->filename, false); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Tar.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Tar.php new file mode 100644 index 0000000..b7c7f8d --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Tar.php @@ -0,0 +1,412 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Tar.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Archive.php"; + +/** + * Read a tar archive + */ +class File_Archive_Reader_Tar extends File_Archive_Reader_Archive +{ + /** + * @var String Name of the file being read + * @access private + */ + var $currentFilename = null; + /** + * @var Array Stats of the file being read + * In TAR reader, indexes 2, 4, 5, 7, 9 are set + * @access private + */ + var $currentStat = null; + /** + * @var int Number of bytes that still have to be read before the end of + * file + * @access private + */ + var $leftLength = 0; + /** + * @var int Size of the footer + * A TAR file is made of chunks of 512 bytes. If 512 does not + * divide the file size a footer is added + * @access private + */ + var $footerLength = 0; + /** + * @var int nb bytes to seek back in order to reach the end of the archive + * or null if the end of the archive has not been reached + */ + var $seekToEnd = null; + + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) + { + if ($length == -1) { + $length = $this->leftLength; + } else { + $length = min($this->leftLength, $length); + } + $skipped = $this->source->skip($length); + if (!PEAR::isError($skipped)) { + $this->leftLength -= $skipped; + } + return $skipped; + } + + /** + * @see File_Archive_Reader::rewind() + */ + function rewind($length = -1) + { + if ($length == -1) { + $length = $this->currentStat[7] - $this->leftLength; + } else { + $length = min($length, $this->currentStat[7] - $this->leftLength); + } + $rewinded = $this->source->rewind($length); + if (!PEAR::isError($rewinded)) { + $this->leftLength += $rewinded; + } + return $rewinded; + } + + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + return $this->currentStat[7] - $this->leftLength; + } + + /** + * @see File_Archive_Reader::close() + */ + function close() + { + $this->leftLength = 0; + $this->currentFilename = null; + $this->currentStat = null; + $this->seekToEnd = null; + return parent::close(); + } + + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() { return $this->currentFilename; } + /** + * @see File_Archive_Reader::getStat() + */ + function getStat() { return $this->currentStat; } + + /** + * @see File_Archive_Reader::next() + */ + function next() + { + $error = parent::next(); + if ($error !== true) { + return $error; + } + + if ($this->seekToEnd !== null) { + return false; + } + + while (true) { + //Advance $this + $header = $this->_nextAdvance(); + if ((!$header) || PEAR::isError($header)) { + return $header; + } + + //Are we looking at a Long Link? + if ($header['type'] == 'L') { + //This is a filepath too long for the tar format. + //So the tar specification puts the name in a special entry just before the real data + //This means the filename is the current piece of data. Grab it. + $filename = ''; + while (($str = $this->getData(256)) !== null) { + if (PEAR::isError($str)) { + return $str; + } + $filename .= $str; + } + + //The actual file data is the next item. Advance there and set the filename to what we just made. + //Everything about the "next" item is correct except the file name. + $header = $this->_nextAdvance(); + if ((!$header) || PEAR::isError($header)) { + return $header; + } + $this->currentFilename = $filename; + } + /** + * Note that actions taken above to handle LongLink may have advanced $this and reset some vars. + * But that just leaves us in a state to actually handle the thing as if it were a normal file. + * So continue as if this never happened... + */ + + //Other than the above we only care about regular files. + //NOTE: Any non-numeric type codes will == 0 + //We handle 'L' above, I don't know what others are out there. + //5 == directory + if ($header['type'] == 0 || $header['type'] == 5) { + break; + } + } + return true; + } + + /** + * Performs the actual advancement to the next item in the underlying structure + * We encapsulate it in a separate function because ot things like @LongLink, where the + * next item is part of the current one. + * + * @access private + * @author Josh Vermette (josh@calydonian.com) + */ + function _nextAdvance() + { + $error = $this->source->skip($this->leftLength + $this->footerLength); + if (PEAR::isError($error)) { + return $error; + } + + $rawHeader = $this->source->getData(512); + if (PEAR::isError($rawHeader)) { + return $rawHeader; + } + + if (strlen($rawHeader)<512 || $rawHeader == pack("a512", "")) { + $this->seekToEnd = strlen($rawHeader); + $this->currentFilename = null; + return false; + } + + $header = unpack( + "a100filename/a8mode/a8uid/a8gid/a12size/a12mtime/". + "a8checksum/a1type/a100linkname/a6magic/a2version/". + "a32uname/a32gname/a8devmajor/a8devminor/a155prefix", + $rawHeader); + + $this->currentStat = array( + 2 => octdec($header['mode']), + 4 => octdec($header['uid']), + 5 => octdec($header['gid']), + 7 => octdec($header['size']), + 9 => octdec($header['mtime']) + ); + $this->currentStat['mode'] = $this->currentStat[2]; + $this->currentStat['uid'] = $this->currentStat[4]; + $this->currentStat['gid'] = $this->currentStat[5]; + $this->currentStat['size'] = $this->currentStat[7]; + $this->currentStat['mtime'] = $this->currentStat[9]; + + if ($header['magic'] == 'ustar') { + $this->currentFilename = $this->getStandardURL( + $header['prefix'] . $header['filename'] + ); + } else { + $this->currentFilename = $this->getStandardURL( + $header['filename'] + ); + } + + $this->leftLength = $this->currentStat[7]; + if ($this->leftLength % 512 == 0) { + $this->footerLength = 0; + } else { + $this->footerLength = 512 - $this->leftLength%512; + } + + $checksum = 8*ord(" "); + for ($i = 0; $i < 148; $i++) { + $checksum += ord($rawHeader{$i}); + } + + for ($i = 156; $i < 512; $i++) { + $checksum += ord($rawHeader{$i}); + } + + if (octdec($header['checksum']) != $checksum) { + die('Checksum error on entry '.$this->currentFilename); + } + + return $header; + } + + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($length == -1) { + $actualLength = $this->leftLength; + } else { + $actualLength = min($this->leftLength, $length); + } + + if ($this->leftLength == 0) { + return null; + } else { + $data = $this->source->getData($actualLength); + if (strlen($data) != $actualLength) { + return PEAR::raiseError('Unexpected end of tar archive'); + } + $this->leftLength -= $actualLength; + return $data; + } + } + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + require_once "File/Archive/Writer/Tar.php"; + + $blocks = array(); + $seek = null; + $gap = 0; + if ($this->currentFilename !== null && $pred->isTrue($this)) { + $seek = 512 + $this->currentStat[7] + $this->footerLength; + $blocks[] = $seek; //Remove this file + } + + while (($error = $this->next()) === true) { + $size = 512 + $this->currentStat[7] + $this->footerLength; + if ($pred->isTrue($this)) { + if ($seek === null) { + $seek = $size; + $blocks[] = $size; + } else if ($gap > 0) { + $blocks[] = $gap; //Don't remove the files between the gap + $blocks[] = $size; + $seek += $size; + } else { + $blocks[count($blocks)-1] += $size; //Also remove this file + $seek += $size; + } + $gap = 0; + } else { + if ($seek !== null) { + $seek += $size; + $gap += $size; + } + } + } + if ($seek === null) { + $seek = $this->seekToEnd; + } else { + $seek += $this->seekToEnd; + if ($gap == 0) { + array_pop($blocks); + } else { + $blocks[] = $gap; + } + } + + $writer = new File_Archive_Writer_Tar(null, + $this->source->makeWriterRemoveBlocks($blocks, -$seek) + ); + $this->close(); + return $writer; + } + + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + if ($this->seekToEnd !== null || $this->currentStat === null) { + return PEAR::raiseError('No file selected'); + } + + $blockPos = $this->currentStat[7] - $this->leftLength + $seek; + + $this->rewind(); + $keep = false; + + $data = $this->getData($blockPos); + foreach ($blocks as $length) { + if ($keep) { + $data .= $this->getData($length); + } else { + $this->skip($length); + } + $keep = !$keep; + } + if ($keep) { + $data .= $this->getData(); + } + + $filename = $this->currentFilename; + $stat = $this->currentStat; + + $writer = $this->makeWriterRemove(); + if (PEAR::isError($writer)) { + return $writer; + } + + unset($stat[7]); + $stat[9] = $stat['mtime'] = time(); + $writer->newFile($filename, $stat); + $writer->writeData($data); + return $writer; + } + + /** + * @see File_Archive_Reader::makeAppendWriter + */ + function makeAppendWriter() + { + require_once "File/Archive/Writer/Tar.php"; + + while (($error = $this->next()) === true) { } + if (PEAR::isError($error)) { + $this->close(); + return $error; + } + + $innerWriter = $this->source->makeWriterRemoveBlocks(array(), -$this->seekToEnd); + if (PEAR::isError($innerWriter)) { + return $innerWriter; + } + + $this->close(); + return new File_Archive_Writer_Tar(null, $innerWriter); + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Uncompress.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Uncompress.php new file mode 100644 index 0000000..096e5e9 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Uncompress.php @@ -0,0 +1,317 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Uncompress.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader.php"; +require_once "File/Archive/Reader/ChangeName/AddDirectory.php"; + +/** + * Recursively uncompress every file it finds + */ +class File_Archive_Reader_Uncompress extends File_Archive_Reader_Relay +{ + /** + * @var Array Stack of readers + * @access private + */ + var $readers = array(); + + /** + * @var array of readers to close when closing $this + * @access private + */ + var $toClose = array(); + + /** + * @var File_Archive_Reader Reader from which all started (usefull to be + * able to close) + * @access private + */ + var $startReader; + + /** + * @var Int Maximum depth of uncompression after the basicDir + * (that may contain some uncompression also) + * -1 means no limit + * @access private + */ + var $uncompressionLevel; + + /** + * @var array Only files starting with $baseDir will be reported + * This array contains explode('/', $directoryName) + * @access private + */ + var $baseDir = ''; + + /** + * @var int Compression level required to go to reach the baseDir + * or null if it is currently being computed + * @access private + */ + var $baseDirCompressionLevel = null; + + /** + * @var int We are selecting substr($baseDir, 0, $baseDirProgression) + */ + var $baseDirProgression = 0; + + /** + * @var boolean Flag set to indicate that the current file has not been + * displayed + */ + var $currentFileNotDisplayed = false; + + function File_Archive_Reader_Uncompress( + &$innerReader, $uncompressionLevel = -1) + { + parent::File_Archive_Reader_Relay($innerReader); + $this->startReader =& $innerReader; + $this->uncompressionLevel = $uncompressionLevel; + } + + /** + * Attempt to change the current source (if the current file is an archive) + * If this is the case, push the current source onto the stack and make the + * good archive reader the current source. A file is considered as an + * archive if its extension is one of tar, gz, zip, tgz + * + * @return bool whether the source has been pushed or not + * @access private + */ + function push() + { + $filename = $this->source->getFilename(); + + if (substr($filename, -1) == '/') { //it's a directory + return false; + } + + + if ($this->uncompressionLevel >= 0 && + $this->baseDirCompressionLevel !== null && + count($this->readers) >= $this->uncompressionLevel + ) { + return false; + } + + // Check the extension of the file (maybe we need to uncompress it?) + $extensions = explode('.', strtolower($filename)); + + $reader =& $this->source; + $nbUncompressions = 0; + + while (($extension = array_pop($extensions)) !== null) { + $nbUncompressions++; + unset($next); + $next = File_Archive::readArchive($extension, $reader, $nbUncompressions == 1); + if ($next === false) { + $extensions = array(); + } else { + unset($reader); + $reader =& $next; + } + } + if ($nbUncompressions == 1) { + return false; + } else { + $this->readers[count($this->readers)] =& $this->source; + unset($this->source); + $this->source = new File_Archive_Reader_ChangeName_AddDirectory( + $filename, $reader + ); + return true; + } + } + /** + * @see File_Archive_Reader::close() + */ + function next() + { + if ($this->currentFileNotDisplayed) { + $this->currentFileNotDisplayed = false; + return true; + } + + do { + do { + $selection = substr($this->baseDir, 0, $this->baseDirProgression); + if ($selection === false) { + $selection = ''; + } + + $error = $this->source->select($selection, false); + if (PEAR::isError($error)) { + return $error; + } + if (!$error) { + if (empty($this->readers)) { + return false; + } + $this->source->close(); + unset($this->source); + $this->source =& $this->readers[count($this->readers)-1]; + unset($this->readers[count($this->readers)-1]); + } + } while (!$error); + + $filename = $this->source->getFilename(); + if (strlen($filename) < strlen($this->baseDir)) { + $goodFile = (strncmp($filename, $this->baseDir, strlen($filename)) == 0 && + $this->baseDir{strlen($filename)} == '/'); + if ($goodFile) { + if (strlen($filename) + 2 < strlen($this->baseDirProgression)) { + $this->baseDirProgression = strpos($this->baseDir, '/', strlen($filename)+2); + if ($this->baseDirProgression === false) { + $this->baseDirProgression = strlen($this->baseDir); + } + } else { + $this->baseDirProgression = strlen($this->baseDir); + } + } + } else { + $goodFile = (strncmp($filename, $this->baseDir, strlen($this->baseDir)) == 0); + if ($goodFile) { + $this->baseDirProgression = strlen($this->baseDir); + } + } + } while (($goodFile && $this->push()) || !$goodFile); + + return true; + } + + /** + * Efficiently filter out the files which URL does not start with $baseDir + * Throws an error if the $baseDir can't be found + * @return bool Whether baseDir was a directory or a file + */ + function setBaseDir($baseDir) + { + $this->baseDir = $baseDir; + $this->baseDirProgression = strpos($baseDir, '/'); + if ($this->baseDirProgression === false) { + $this->baseDirProgression = strlen($baseDir); + } + + $error = $this->next(); + if ($error === false) { + return PEAR::raiseError("No directory $baseDir in inner reader"); + } else if (PEAR::isError($error)) { + return $error; + } + + $this->currentFileNotDisplayed = true; + return strlen($this->getFilename())>strlen($baseDir); + } + /** + * @see File_Archive_Reader::select() + */ + function select($filename, $close = true) + { + if ($close) { + $error = $this->close(); + if (PEAR::isError($close)) { + return $error; + } + } + + $oldBaseDir = $this->baseDir; + $oldProgression = $this->baseDirProgression; + + $this->baseDir = $filename; + $this->baseDirProgression = 0; + + $res = $this->next(); + + $this->baseDir = $oldBaseDir; + $this->baseDirProgression = $oldProgression; + + return $res; + } + + /** + * @see File_Archive_Reader::close() + */ + function close() + { + for ($i=0; $ireaders); ++$i) { + $this->readers[$i]->close(); + } + //var_dump($this->toClose); + for ($i=0; $itoClose); ++$i) { + if ($this->toClose[$i] !== null) { + $this->toClose[$i]->close(); + } + } + + $this->readers = array(); + $this->toClose = array(); + $error = parent::close(); + $this->baseDirCompressionLevel = null; + $this->baseDirProgression = 0; + + unset($this->source); + $this->source =& $this->startReader; + $this->source->close(); + $this->currentFileNotDisplayed = false; + + return $error; + } + + /** + * @see File_Archive_Reader::makeAppendWriter() + */ + function makeAppendWriter() + { + //The reader needs to be open so that the base dir is found + $error = $this->next(); + if (PEAR::isError($error)) { + return $error; + } + + return parent::makeAppendWriter(); + } + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + //The reader needs to be open so that the base dir is found + $error = $this->next(); + if (PEAR::isError($error)) { + return $error; + } + + return parent::makeWriterRemoveFiles($pred); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Zip.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Zip.php new file mode 100644 index 0000000..a9df6ba --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Reader/Zip.php @@ -0,0 +1,482 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Zip.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Reader/Archive.php"; + +/** + * ZIP archive reader + * Currently only allows to browse the archive (getData is not available) + */ +class File_Archive_Reader_Zip extends File_Archive_Reader_Archive +{ + var $currentFilename = null; + var $currentStat = null; + var $header = null; + var $offset = 0; + var $data = null; + var $files = array(); + var $seekToEnd = 0; + + var $centralDirectory = null; + + /** + * @see File_Archive_Reader::close() + */ + function close() + { + $this->currentFilename = null; + $this->currentStat = null; + $this->compLength = 0; + $this->data = null; + $this->seekToEnd = 0; + $this->files = array(); + $this->centralDirectory = null; + + return parent::close(); + } + + /** + * @see File_Archive_Reader::getFilename() + */ + function getFilename() { return $this->currentFilename; } + /** + * @see File_Archive_Reader::getStat() + */ + function getStat() { return $this->currentStat; } + + /** + * Go to next entry in ZIP archive + * + * @see File_Archive_Reader::next() + */ + function next() + { + if ($this->seekToEnd > 0) { + return false; + } + + //Skip the data and the footer if they haven't been uncompressed + if ($this->header !== null && $this->data === null) { + $toSkip = $this->header['CLen']; + $error = $this->source->skip($toSkip); + if (PEAR::isError($error)) { + return $error; + } + } + + $this->offset = 0; + $this->data = null; + + //Read the header + $header = $this->source->getData(4); + // Handle PK00PK archives + if ($header == "\x50\x4b\x30\x30") { //PK00 + $header = $this->source->getData(4); + } + // Sometimes this header is used to tag the data descriptor section + if($header == "\x50\x4b\x07\x08") { + // Read out the data descriptor (always 12 bytes) + $this->source->getData(12); + + // Get a new header from the file + $header = $this->source->getData(4); + } + if (PEAR::isError($header)) { + return $header; + } + if ($header == "\x50\x4b\x03\x04") { + //New entry + $header = $this->source->getData(26); + if (PEAR::isError($header)) { + return $header; + } + $this->header = unpack( + "vVersion/vFlag/vMethod/vTime/vDate/VCRC/VCLen/VNLen/vFile/vExtra", + $header); + + //Check the compression method + if ($this->header['Method'] != 0 && + $this->header['Method'] != 8 && + $this->header['Method'] != 12) { + return PEAR::raiseError("File_Archive_Reader_Zip doesn't ". + "handle compression method {$this->header['Method']}"); + } + if ($this->header['Flag'] & 1) { + return PEAR::raiseError("File_Archive_Reader_Zip doesn't ". + "handle encrypted files"); + } + if ($this->header['Flag'] & 8) { + if ($this->centralDirectory === null) { + $this->readCentralDirectory(); + } + $centralDirEntry = $this->centralDirectory[count($this->files)]; + + $this->header['CRC'] = $centralDirEntry['CRC']; + $this->header['CLen'] = $centralDirEntry['CLen']; + $this->header['NLen'] = $centralDirEntry['NLen']; + } + if ($this->header['Flag'] & 32) { + return PEAR::raiseError("File_Archive_Reader_Zip doesn't ". + "handle compressed patched data"); + } + if ($this->header['Flag'] & 64) { + return PEAR::raiseError("File_Archive_Reader_Zip doesn't ". + "handle strong encrypted files"); + } + + $this->currentStat = array( + 7=>$this->header['NLen'], + 9=>mktime( + ($this->header['Time'] & 0xF800) >> 11, //hour + ($this->header['Time'] & 0x07E0) >> 5, //minute + ($this->header['Time'] & 0x001F) >> 1, //second + ($this->header['Date'] & 0x01E0) >> 5, //month + ($this->header['Date'] & 0x001F) , //day + (($this->header['Date'] & 0xFE00) >> 9) + 1980 //year + ) + ); + $this->currentStat['size'] = $this->currentStat[7]; + $this->currentStat['mtime'] = $this->currentStat[9]; + + $this->currentFilename = $this->source->getData($this->header['File']); + + $error = $this->source->skip($this->header['Extra']); + if (PEAR::isError($error)) { + return $error; + } + + $this->files[] = array('name' => $this->currentFilename, + 'stat' => $this->currentStat, + 'CRC' => $this->header['CRC'], + 'CLen' => $this->header['CLen'] + ); + return true; + } else { + //Begining of central area + $this->seekToEnd = 4; + $this->currentFilename = null; + return false; + } + } + + /** + * @see File_Archive_Reader::getData() + */ + function getData($length = -1) + { + if ($this->offset >= $this->currentStat[7]) { + return null; + } + + if ($length>=0) { + $actualLength = min($length, $this->currentStat[7]-$this->offset); + } else { + $actualLength = $this->currentStat[7]-$this->offset; + } + + $error = $this->uncompressData(); + if (PEAR::isError($error)) { + return $error; + } + $result = substr($this->data, $this->offset, $actualLength); + $this->offset += $actualLength; + return $result; + } + /** + * @see File_Archive_Reader::skip() + */ + function skip($length = -1) + { + $before = $this->offset; + if ($length == -1) { + $this->offset = $this->currentStat[7]; + } else { + $this->offset = min($this->offset + $length, $this->currentStat[7]); + } + return $this->offset - $before; + } + /** + * @see File_Archive_Reader::rewind() + */ + function rewind($length = -1) + { + $before = $this->offset; + if ($length == -1) { + $this->offset = 0; + } else { + $this->offset = min(0, $this->offset - $length); + } + return $before - $this->offset; + } + /** + * @see File_Archive_Reader::tell() + */ + function tell() + { + return $this->offset; + } + + function uncompressData() + { + if ($this->data !== null) + return; + + $this->data = $this->source->getData($this->header['CLen']); + if (PEAR::isError($this->data)) { + return $this->data; + } + if ($this->header['Method'] == 8) { + $this->data = gzinflate($this->data); + } + if ($this->header['Method'] == 12) { + $this->data = bzdecompress($this->data); + } + + if (crc32($this->data) != ($this->header['CRC'] & 0xFFFFFFFF)) { + return PEAR::raiseError("Zip archive: CRC fails on entry ". + $this->currentFilename); + } + } + + /** + * @see File_Archive_Reader::makeWriterRemoveFiles() + */ + function makeWriterRemoveFiles($pred) + { + require_once "File/Archive/Writer/Zip.php"; + + $blocks = array(); + $seek = null; + $gap = 0; + if ($this->currentFilename !== null && $pred->isTrue($this)) { + $seek = 30 + $this->header['File'] + $this->header['Extra'] + $this->header['CLen']; + $blocks[] = $seek; //Remove this file + array_pop($this->files); + } + + while (($error = $this->next()) === true) { + $size = 30 + $this->header['File'] + $this->header['Extra'] + $this->header['CLen']; + if (substr($this->getFilename(), -1) == '/' || $pred->isTrue($this)) { + array_pop($this->files); + if ($seek === null) { + $seek = $size; + $blocks[] = $size; + } else if ($gap > 0) { + $blocks[] = $gap; //Don't remove the files between the gap + $blocks[] = $size; + $seek += $size; + } else { + $blocks[count($blocks)-1] += $size; //Also remove this file + $seek += $size; + } + $gap = 0; + } else { + if ($seek !== null) { + $seek += $size; + $gap += $size; + } + } + } + if (PEAR::isError($error)) { + return $error; + } + + if ($seek === null) { + $seek = 4; + } else { + $seek += 4; + if ($gap == 0) { + array_pop($blocks); + } else { + $blocks[] = $gap; + } + } + + $writer = new File_Archive_Writer_Zip(null, + $this->source->makeWriterRemoveBlocks($blocks, -$seek) + ); + if (PEAR::isError($writer)) { + return $writer; + } + + foreach ($this->files as $file) { + $writer->alreadyWrittenFile($file['name'], $file['stat'], $file['CRC'], $file['CLen']); + } + + $this->close(); + return $writer; + } + + /** + * @see File_Archive_Reader::makeWriterRemoveBlocks() + */ + function makeWriterRemoveBlocks($blocks, $seek = 0) + { + if ($this->currentFilename === null) { + return PEAR::raiseError('No file selected'); + } + + $keep = false; + + $this->uncompressData(); + $newData = substr($this->data, 0, $this->offset + $seek); + $this->data = substr($this->data, $this->offset + $seek); + foreach ($blocks as $length) { + if ($keep) { + $newData .= substr($this->data, 0, $length); + } + $this->data = substr($this->data, $length); + $keep = !$keep; + } + if ($keep) { + $newData .= $this->data; + } + + $filename = $this->currentFilename; + $stat = $this->currentStat; + + $writer = $this->makeWriterRemove(); + if (PEAR::isError($writer)) { + return $writer; + } + + unset($stat[7]); + $stat[9] = $stat['mtime'] = time(); + $writer->newFile($filename, $stat); + $writer->writeData($newData); + return $writer; + } + + /** + * @see File_Archive_Reader::makeAppendWriter + */ + function makeAppendWriter() + { + require_once "File/Archive/Writer/Zip.php"; + + while (($error = $this->next()) === true) { } + if (PEAR::isError($error)) { + $this->close(); + return $error; + } + + $writer = new File_Archive_Writer_Zip(null, + $this->source->makeWriterRemoveBlocks(array(), -4) + ); + + foreach ($this->files as $file) { + $writer->alreadyWrittenFile($file['name'], $file['stat'], $file['CRC'], $file['CLen']); + } + + $this->close(); + return $writer; + } + + /** + * This function seeks to the start of the [end of central directory] field, + * just after the \x50\x4b\x05\x06 signature and returns the number of bytes + * skipped + * + * The stream must initially be positioned before the end of central directory + */ + function seekToEndOfCentralDirectory() + { + $nbSkipped = $this->source->skip(); + + $nbSkipped -= $this->source->rewind(22) - 4; + if ($this->source->getData(4) == "\x50\x4b\x05\x06") { + return $nbSkipped; + } + + while ($nbSkipped > 0) { + + $nbRewind = $this->source->rewind(min(100, $nbSkipped)); + while ($nbRewind >= -4) { + if ($nbRewind-- && $this->source->getData(1) == "\x50" && + $nbRewind-- && $this->source->getData(1) == "\x4b" && + $nbRewind-- && $this->source->getData(1) == "\x05" && + $nbRewind-- && $this->source->getData(1) == "\x06") { + //We finally found it! + return $nbSkipped - $nbRewind; + } + } + $nbSkipped -= $nbRewind; + } + + return PEAR::raiseError('End of central directory not found. The file is probably not a zip archive'); + } + + /** + * This function will fill the central directory variable + * and seek back to where it was called + */ + function readCentralDirectory() + { + $nbSkipped = $this->seekToEndOfCentralDirectory(); + if (PEAR::isError($nbSkipped)) { + return $nbSkipped; + } + + $this->source->skip(12); + $offset = $this->source->getData(4); + $nbSkipped += 16; + if (PEAR::isError($offset)) { + return $offset; + } + + $offset = unpack("Vvalue", $offset); + $offset = $offset['value']; + + $current = $this->source->tell(); + $nbSkipped -= $this->source->rewind($current - $offset); + + //Now we are the right pos to read the central directory + $this->centralDirectory = array(); + while ($this->source->getData(4) == "\x50\x4b\x01\x02") { + $this->source->skip(12); + $header = $this->source->getData(16); + $nbSkipped += 32; + + if (PEAR::isError($header)) { + return $header; + } + + $header = unpack('VCRC/VCLen/VNLen/vFileLength/vExtraLength', $header); + $this->centralDirectory[] = array('CRC' => $header['CRC'], + 'CLen' => $header['CLen'], + 'NLen' => $header['NLen']); + $nbSkipped += $this->source->skip(14 + $header['FileLength'] + $header['ExtraLength']); + } + + $this->source->rewind($nbSkipped+4); + } +} +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer.php new file mode 100644 index 0000000..c349b32 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer.php @@ -0,0 +1,119 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Writer.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "PEAR.php"; + +/** + * Base class for any writer + */ +class File_Archive_Writer +{ + /** + * Create a new file in the writer + * + * @param string $filename Name of the file, eventually including a path + * @param array $stat Its Statistics. None of the indexes are required + * @param string $mime MIME type of the file + */ + function newFile($filename, $stat = array(), $mime = "application/octet-stream") + { + } + + /** + * Create a new file in the writer with the content of the physical file $filename + * and then unlink $filename. + * newFromTempFile($tmpfile, $filename, $stat, $mime) is equivalent to + * newFile($filename, $stat, $mime); writeFile($tmpfile); unlink($tmpfile); + * but can be more efficient. + * A typical use is for File_Archive_Writer_Files: it renames the temporary + * file instead of copy/delete + * + * @param string $tmpfile Name of the physical file that contains data and will be unlinked + * @param string $filename Name of the file, eventually including a path + * @param array $stat Its Statistics. None of the indexes are required + * @param string $mime MIME type of the file + */ + function newFromTempFile($tmpfile, $filename, $stat = array(), $mime = "application/octet-stream") + { + $this->newFile($filename, $stat, $mime); + $this->writeFile($tmpfile); + unlink($tmpfile); + } + + /** + * Returns whether the writer newFile function needs the $mime parameter + * Default is false + */ + function newFileNeedsMIME() + { + return false; + } + + /** + * Append the specified data to the writer + * + * @param String $data the data to append to the writer + */ + function writeData($data) + { + } + + /** + * Append the content of the physical file $filename to the writer + * writeFile($filename) must be equivalent to + * writeData(file_get_contents($filename)) but can be more efficient + * + * @param string $filename Name of the file which content must be appended + * to the writer + */ + function writeFile($filename) + { + $handle = fopen($filename, "r"); + if (!is_resource($handle)) { + return PEAR::raiseError("Unable to write to $filename"); + } + while (!feof($handle)) { + $error = $this->writeData(fread($handle, 102400)); + if (PEAR::isError($error)) { + return $error; + } + } + fclose($handle); + } + + /** + * Close the writer, eventually flush the data, write the footer... + * This function must be called before the end of the script + */ + function close() { } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/AddBaseName.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/AddBaseName.php new file mode 100644 index 0000000..1d8d2ae --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/AddBaseName.php @@ -0,0 +1,102 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: AddBaseName.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; + +/** + * Writer wrapper that adds a directory to the written file + */ +class File_Archive_Writer_AddBaseName +{ + var $writer; + var $baseName; + + function File_Archive_Writer_AddBaseName($baseName, &$writer) + { + if (substr($baseName, -1) == '/') { + $this->baseName = $baseName; + } else { + $this->baseName = $baseName.'/'; + } + + $this->writer =& $writer; + } + + /** + * @see File_Archive_Writer::newFile() + */ + function newFile($filename, $stat = array(), $mime = "application/octet-stream") + { + $this->writer->newFile($this->baseName.$filename, $stat, $mime); + } + + /** + * @see File_Archive_Writer::newFromTempFile() + */ + function newFromTempFile($tmpfile, $filename, $stat = array(), $mime = "application/octet-stream") + { + $this->writer->newFromTempFile($tmpfile, $this->baseName.$filename, $stat, $mime); + } + + /** + * @see File_Archive_Writer::newFileNeedsMIME() + */ + function newFileNeedsMIME() + { + return $this->writer->newFileNeedsMIME(); + } + + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) + { + $this->writer->writeData($data); + } + + /** + * @see File_Archive_Writer::writeFile() + */ + function writeFile($filename) + { + $this->writer->writeFile($filename); + } + + /** + * @see File_Archive_Writer::close() + */ + function close() + { + $this->writer->close(); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Ar.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Ar.php new file mode 100644 index 0000000..113ea24 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Ar.php @@ -0,0 +1,209 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer/Archive.php"; + +/** + * Write the files as an AR archive + */ +class File_Archive_Writer_Ar extends File_Archive_Writer_Archive +{ + + /** + * @var string Current data of the file. + * @access private + */ + var $_buffer = ""; + + /** + * @var string Filename of the current filename + * @access private + */ + var $_currentFilename = null; + + /** + * @var boolean Flag: use buffer or not. + * @access private + */ + var $_useBuffer; + + /** + * @var array Stats of the current filename + * @access private + */ + var $_currentStat = array (); + + /** + * @var boolean Flag: beginning of the archive or not + * @access private + */ + var $_atStart = true; + + /** + * Returns the header of the current file. + * + * More Info: + * http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/files/aixfiles/ar_IA64.htm + * + * @access private + * @param string $filename Name of the current file + * @param array $stat Stat array of the current file + * @return string The built header struct + */ + function arHeader ($filename, $stat) + { + $mode = isset($stat[2]) ? $stat[2] : 0x8000; + $uid = isset($stat[4]) ? $stat[4] : 0; + $gid = isset($stat[5]) ? $stat[5] : 0; + $size = $stat[7]; + $time = isset($stat[9]) ? $stat[9] : time(); + + $struct = ""; + $currentSize = $size; + //if file length is > than 16.. + if (strlen($filename) > 16) { + $currentSize += strlen($filename); + $struct .= sprintf("#1/%-13d", strlen($filename)); + $struct .= sprintf("%-12d%-6d%-6d%-8s%-10d", + $time, $uid, $gid, $mode, $currentSize); + $struct .= "`\n".$filename; + } else { + $struct .= sprintf("%-16s", $filename); + $struct .= sprintf("%-12d%-6d%-6d%-8s%-10d`\n", + $time, $uid, $gid, $mode, $size); + } + return $struct; + } + + /** + * Returns the footer of the current file, the footer depends + * of the size of the file + * + * @access private + * @param string $filename Name of the file, the footer depends on its length + * @param int $size Size of the current file, here the size does matters! + * @return string The footer struct + */ + function arFooter($filename, $size) + { + $size = (strlen ($filename) > 16) ? $size + strlen($filename) : $size; + + return ($size % 2 == 1) ? "\n" : ""; + } + + + /** + * Flush the memory we have in the ar. + * + * Build the buffer if its called at the end or initialize + * it if we are just creating it from the start. + */ + function flush() + { + if ($this->_atStart) { + $this->innerWriter->writeData("!\n"); + $this->_atStart = false; + } + if ($this->_currentFilename !== null) { + $this->_currentStat[7] = strlen($this->_buffer); + if ($this->_useBuffer) { + $this->innerWriter->writeData( + $this->arHeader($this->_currentFilename, $this->_currentStat) + ); + $this->innerWriter->writeData($this->_buffer); + } + $this->innerWriter->writeData($this->arFooter($this->_currentFilename, $this->_currentStat[7])); + } + $this->_buffer = ""; + } + + /** + * @see File_Archive_Writer::newFile() + * + */ + function newFile($filename, $stat = array (), + $mime = "application/octet-stream") + { + // ar file format doesn't support folders + if (substr($filename, -1) == '/') { + return; + } + + $this->flush(); + /* + * If the file is empty, there's no reason to have a buffer + * or use memory + */ + $this->_useBuffer = !isset($stats[7]); + /* + * Becaue ar fileformats doesn't support files in directories, + * then we need to just save with the filename an ommit the + * directory + */ + $this->_currentFilename = basename($filename); + $this->_currentStat = $stat; + + if(!$this->_useBuffer) { + return $this->innerWriter->writeData($this->arHeader($filename, $stat)); + } + } + + /** + * @see File_Archive_Writer::close() + */ + function close() + { + $this->flush(); + parent::close(); + } + + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) + { + if ($this->_useBuffer) { + $this->_buffer .= $data; + } else { + $this->innerWriter->writeData($data); + } + + } + /** + * @see File_Archive_Writer::writeFile() + */ + function writeFile($filename) + { + if ($this->_useBuffer) { + $this->_buffer .= file_get_contents($filename); + } else { + $this->innerWriter->writeFile($filename); + } + } +} \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Archive.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Archive.php new file mode 100644 index 0000000..12bb2c0 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Archive.php @@ -0,0 +1,129 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Archive.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; + +/** + * Base class for all the transformation writers that will generate one single + * file + */ +class File_Archive_Writer_Archive extends File_Archive_Writer +{ + /** + * @var File_Archive_Writer The compressed data will be written to this + * writer + * @access protected + */ + var $innerWriter; + + /** + * @var bool If true, the innerWriter will be closed when closing this + * @access private + */ + var $autoClose; + + /** + * @var Array List of empty folders that will be added when closing the archive + */ + var $emptyFolders = array(); + + /** + * @param String $filename Name to give to the archive (the name will + * be used by the inner writer) + * If $filename is null, the innerWriter is considered already + * opened (and thus newFile will not be called) + * @param File_Archive_Writer $innerWriter The inner writer to which the + * compressed data will be written + * @param array $stat The stat of the archive (see the PHP stat() function). + * No element are required in this array + * @param bool $autoClose Indicate if the inner writer must be closed when + * closing this + */ + function File_Archive_Writer_Archive($filename, &$innerWriter, + $stat = array(), $autoClose = true) + { + $this->innerWriter =& $innerWriter; + $this->autoClose = $autoClose; + if ($filename !== null) { + $this->innerWriter->newFile($filename, $stat, $this->getMime()); + } + } + + function newFile($filename, $stat = array(), + $mime = 'application/octet-stream') + { + if (substr($filename, -1) == '/') { + $this->emptyFolders[$filename] = array($stat, $mime); + } else { + // Remove the folders that may no longer be empty + $current = ''; + foreach (explode('/', $filename) as $folder) { + $current .= $folder.'/'; + unset($this->emptyFolders[$current]); + } + $err = $this->_newFile($filename, $stat, $mime); + if (PEAR::isError($err)) { + return $err; + } + } + } +//MUST REWRITE FUNCTIONS + function _newFile($filename, $stat, $mime) {} + + /** + * @return the MIME extension of the files generated by this writer + */ + function getMime() { return "application/octet-stream"; } + + /** + * @see File_Archive_Writer::close() + */ + function close() + { + foreach ($this->emptyFolders as $folder => $info) { + $err = $this->_newFile($folder, $info[0], $info[1]); + if (PEAR::isError($err)) { + return $err; + } + } + + if ($this->autoClose) { + return $this->innerWriter->close(); + } + } +// function writeData($data) + +//SHOULD REWRITE FUNCTIONS +// function writeFile($filename) +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Bzip2.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Bzip2.php new file mode 100644 index 0000000..f436688 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Bzip2.php @@ -0,0 +1,147 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Bzip2.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; + +/** + * Compress a single file to Bzip2 format + */ +class File_Archive_Writer_Bzip2 extends File_Archive_Writer +{ + + /** + * compressionLevel + * + * @var integer + * @access public + * @deprecated + */ + var $compressionLevel=9; + var $bzfile; + var $tmpName; + var $nbFiles = 0; + + var $innerWriter; + var $autoClose; + var $filename; + var $stat; + + /** + * @param string $filename Name to give to the archive + * @param File_Archive_Writer $innerWriter The inner writer to which the + * compressed data will be written + * @param array $stat The stat of the archive (see the PHP stat() function). + * No element are required in this array + * @param bool $autoClose Indicate if the inner writer must be closed when + * closing this + */ + function File_Archive_Writer_Bzip2($filename, &$innerWriter, + $stat = array(), $autoClose = true) + { + $this->innerWriter =& $innerWriter; + $this->autoClose = $autoClose; + + $this->filename = $filename; + $this->stat = $stat; + + if ($this->filename === null) { + $this->newFile(null); + } + } + + /** + * Set the compression level. Do nothing because PHP bz2 ext doesn't + * support this. + * + * @param int $compressionLevel From 0 (no compression) to 9 (best + * compression) + * @deprecated + */ + function setCompressionLevel($compressionLevel) + { + $this->compressionLevel = $compressionLevel; + } + + /** + * @see File_Archive_Writer::newFile() + * + * Check that one single file is written in the BZip2 archive + */ + function newFile($filename, $stat = array(), + $mime = "application/octet-stream") + { + if ($this->nbFiles > 1) { + return PEAR::raiseError("A Bzip2 archive can only contain one single file.". + "Use Tbz archive to be able to write several files"); + } + $this->nbFiles++; + + $this->tmpName = tempnam(File_Archive::getOption('tmpDirectory'), 'far'); + $this->bzfile = bzopen($this->tmpName, 'w'); + + return true; + } + + /** + * Actually write the tmp file to the inner writer + * Close and delete temporary file + * + * @see File_Archive_Writer::close() + */ + function close() + { + bzclose($this->bzfile); + + if ($this->filename === null) { + //Assume innerWriter is already opened on a file... + $this->innerWriter->writeFile($this->tmpName); + unlink($this->tmpName); + } else { + $this->innerWriter->newFromTempFile( + $this->tmpName, $this->filename, $this->stat, 'application/x-compressed' + ); + } + + if ($this->autoClose) { + return $this->innerWriter->close(); + } + } + + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) + { + bzwrite($this->bzfile, $data); + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Files.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Files.php new file mode 100644 index 0000000..a58cf92 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Files.php @@ -0,0 +1,259 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Files.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; + +/** + * Writer to files + */ +class File_Archive_Writer_Files extends File_Archive_Writer +{ + /** + * @var Object Handle to the file where the data are currently written + * @access private + */ + var $handle = null; + var $basePath; + var $stat = array(); + var $filename; + + function File_Archive_Writer_Files($base = '') + { + if ($base === null || $base == '') { + $this->basePath = ''; + } else { + if (substr($base, -1) == '/') { + $this->basePath = $base; + } else { + $this->basePath = $base.'/'; + } + } + } + + function getFilename($filename) + { + return $this->basePath.$filename; + } + + /** + * Ensure that $pathname exists, or create it if it does not + * @access private + */ + function mkdirr($pathname) + { + // Check if directory already exists + if (is_dir($pathname) || empty($pathname)) { + return; + } + + // Ensure a file does not already exist with the same name + if (is_file($pathname)) { + return PEAR::raiseError( + "File $pathname exists, unable to create directory" + ); + } + + // Crawl up the directory tree + $next_pathname = substr( + $pathname, + 0, strrpos($pathname, "/")); + $error = $this->mkdirr($next_pathname); + if (PEAR::isError($error)) { + return $error; + } + if (!@mkdir($pathname)) { + return PEAR::raiseError("Unable to create directory $pathname"); + } + } + + /** + * Open a file for writing from a given position + * + * @param string $filename The name of the file to open + * @param int $pos the initial position in the file + * @param $stat the stats of the file + */ + function openFile($filename, $pos = 0) + { + $this->close(); + + $this->handle = fopen($filename, 'r+'); + $this->stat = array(); + $this->filename = $filename; + + if (!is_resource($this->handle)) { + return PEAR::raiseError("Unable to open file $filename"); + } + + if ($pos > 0) { + if (fseek($this->handle, $pos) == -1) { + fread($this->handle, $pos); + } + } + } + + /** + * Open a file for appending after having removed a block of data from it + * See File_Archive_Reader::makeWriterRemoveBlocks + */ + function openFileRemoveBlock($filename, $pos, $blocks) + { + $error = $this->openFile($filename, $pos); + if (PEAR::isError($error)) { + return $error; + } + + if (!empty($blocks)) { + //This will be used to read the initial file + //The data, with the unusefull block removed will be written to $this->handle + $read = fopen($filename, 'r'); + if ($pos > 0) { + if (fseek($this->handle, $pos) == -1) { + fread($this->handle, $pos); + } + } + + $keep = false; + $data = ''; + foreach ($blocks as $length) { + if ($keep) { + while ($length > 0 && + ($data = fread($read, min($length, 8192))) != '') { + $length -= strlen($data); + fwrite($this->handle, $data); + } + } else { + fseek($read, $length, SEEK_CUR); + } + $keep = !$keep; + } + if ($keep) { + while(!feof($this->handle)) { + fwrite($this->handle, fread($read, 8196)); + } + } + + fclose($read); + } + + ftruncate($this->handle, ftell($this->handle)); + } + + + /** + * @see File_Archive_Writer::newFile() + */ + function newFile($filename, $stat = array(), $mime = "application/octet-stream") + { + $this->close(); + $this->stat = $stat; + $this->filename = $this->getFilename($filename); + + if (substr($this->filename, -1) == '/') { + $error = $this->mkdirr(substr($this->filename, 0, -1)); + if (PEAR::isError($error)) { + return $error; + } + } else { + $pos = strrpos($this->filename, "/"); + if ($pos !== false) { + $error = $this->mkdirr(substr($this->filename, 0, $pos)); + if (PEAR::isError($error)) { + return $error; + } + } + $this->handle = @fopen($this->filename, "w"); + if (!is_resource($this->handle)) { + return PEAR::raiseError("Unable to write to file $filename"); + } + } + } + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) { fwrite($this->handle, $data); } + /** + * @see File_Archive_Writer::newFromTempFile() + */ + function newFromTempFile($tmpfile, $filename, $stat = array(), $mime = "application/octet-stream") + { + $this->filename = $filename; + $complete = $this->getFilename($filename); + $pos = strrpos($complete, "/"); + if ($pos !== false) { + $error = $this->mkdirr(substr($complete, 0, $pos)); + if (PEAR::isError($error)) { + return $error; + } + } + + if ((file_exists($complete) && !@unlink($complete)) || + !@rename($tmpfile, $complete)) { + return parent::newFromTempFile($tmpfile, $filename, $stat, $mime); + } + } + + + /** + * @see File_Archive_Writer::close() + */ + function close() + { + if (is_resource($this->handle)) { + fclose($this->handle); + $this->handle = null; + + if (isset($this->stat[9])) { + if (isset($this->stat[8])) { + touch($this->filename, $this->stat[9], $this->stat[8]); + } else { + touch($this->filename, $this->stat[9]); + } + } else if (isset($this->stat[8])) { + touch($this->filename, time(), $this->stat[8]); + } + + if (isset($this->stat[2])) { + chmod($this->filename, $this->stat[2]); + } + if (isset($this->stat[5])) { + // I will try, but if I dont have permissions, it will fail + @chgrp($this->filename, $this->stat[5]); + } + if (isset($this->stat[4])) { + // I will try, but if I dont have permissions, it will fail + @chown($this->filename, $this->stat[4]); + } + } + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Gzip.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Gzip.php new file mode 100644 index 0000000..be7132f --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Gzip.php @@ -0,0 +1,139 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Gzip.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; + +/** + * Compress a single file to Gzip format + */ +class File_Archive_Writer_Gzip extends File_Archive_Writer +{ + var $compressionLevel=9; + var $gzfile; + var $tmpName; + var $nbFiles = 0; + + var $innerWriter; + var $autoClose; + var $filename; + var $stat; + + /** + * @param string $filename Name to give to the archive + * @param File_Archive_Writer $innerWriter The inner writer to which the + * compressed data will be written + * @param array $stat The stat of the archive (see the PHP stat() function). + * No element are required in this array + * @param bool $autoClose Indicate if the inner writer must be closed when + * closing this + */ + function File_Archive_Writer_Gzip($filename, &$innerWriter, + $stat = array(), $autoClose = true) + { + $this->innerWriter =& $innerWriter; + $this->autoClose = $autoClose; + + $this->filename = $filename; + $this->stat = $stat; + + if ($this->filename === null) { + $this->newFile(null); + } + + $compressionLevel = File_Archive::getOption('gzCompressionLevel', 9); + } + + /** + * Set the compression level + * + * @param int $compressionLevel From 0 (no compression) to 9 (best + * compression) + */ + function setCompressionLevel($compressionLevel) + { + $this->compressionLevel = $compressionLevel; + } + + /** + * @see File_Archive_Writer::newFile() + * + * Check that one single file is written in the GZip archive + */ + function newFile($filename, $stat = array(), + $mime = "application/octet-stream") + { + if ($this->nbFiles > 1) { + return PEAR::raiseError("A Gz archive can only contain one single file.". + "Use Tgz archive to be able to write several files"); + } + $this->nbFiles++; + + $this->tmpName = tempnam(File_Archive::getOption('tmpDirectory'), 'far'); + $this->gzfile = gzopen($this->tmpName, 'w'.$this->compressionLevel); + + return true; + } + + + /** + * Actually write the tmp file to the inner writer + * Close and delete temporary file + * + * @see File_Archive_Writer::close() + */ + function close() + { + gzclose($this->gzfile); + if ($this->filename === null) { + //Assume innerWriter is already opened on a file... + $this->innerWriter->writeFile($this->tmpName); + unlink($this->tmpName); + } else { + $this->innerWriter->newFromTempFile( + $this->tmpName, $this->filename, $this->stat, 'application/x-compressed' + ); + } + + if ($this->autoClose) { + return $this->innerWriter->close(); + } + } + + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) + { + gzwrite($this->gzfile, $data); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Mail.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Mail.php new file mode 100644 index 0000000..eda6f60 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Mail.php @@ -0,0 +1,204 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Mail.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; +require_once "Mail.php"; +require_once "Mail/mime.php"; + +/** + * Send the files attached to a mail. + */ +class File_Archive_Writer_Mail extends File_Archive_Writer +{ + /** + * @var Mail_mime object + * @access private + */ + var $mime; + + /** + * @var Mail object used to send email (built thanks to the factory) + * @access private + */ + var $mail; + + /** + * @var Array or String An array or a string with comma separated recipients + * @access private + */ + var $to; + + /** + * @var Array The headers that will be passed to the Mail_mime object + * @access private + */ + var $headers; + + /** + * @var String Data read from the current file so far + * @access private + */ + var $currentData = null; + + /** + * @var String Name of the file being attached + * @access private + */ + var $currentFilename = null; + + /** + * @var String MIME of the file being attached + * @access private + */ + var $currentMime = null; + + /** + * @param Mail $mail Object used to send mail (see Mail::factory) + * @param array or string $to An array or a string with comma separated + * recipients + * @param array $headers The headers that will be passed to the Mail_mime + * object + * @param string $message Text body of the mail + */ + function File_Archive_Writer_Mail($to, $headers, $message, &$mail) + { + $this->mime = new Mail_mime(); + $this->mime->setTXTBody($message); + if (!empty($htmlMessage)) { + $this->mime->setHTMLBody($htmlMessage); + } + + if ($mail === null) + $this->mail = Mail::factory("mail"); + else + $this->mail =& $mail; + + $this->to = $to; + $this->headers = $headers; + } + + /** + * @see Mail_Mime::setHTMLBody() + */ + function setHTMLBody($data, $isfile = false) + { + return $this->mime->setHTMLBody($data, $isfile); + } + /** + * @see Mail_Mime::addHTMLImage() + */ + function addHTMLImage($file, $c_type = 'application/octet-stream', + $name = '', $isfile = true) + { + return $this->mime->addHTMLImage($file, $c_type, $name, $isfile); + } + + /** + * @see File_Archive_Writer::writeData() + * + * This function just put the data in $currentData until the end of file + * At that time, addCurrentData is called to attach $currentData to the mail + * and to clear $currentData for a new file + */ + function writeData($data) + { + $this->currentData .= $data; + } + /** + * Called when a file is finished and must be added as attachment to the mail + */ + function addCurrentData() + { + if ($this->currentFilename === null) { + return; + } + + $error = $this->mime->addAttachment( + $this->currentData, + $this->currentMime, + $this->currentFilename, + false); + $this->currentData = ''; + return $error; + } + /** + * @see File_Archive_Writer::newFile() + */ + function newFile($filename, $stat, $mime = "application/octet-stream") + { + $error = $this->addCurrentData(); + if (PEAR::isError($error)) { + return $error; + } + + if (substr($filename, -1) == '/') { + $this->currentFilename = null; + } else { + $this->currentFilename = $filename; + $this->currentMime = $mime; + } + } + /** + * @see File_Archive_Writer::newFileNeedsMIME() + */ + function newFileNeedsMIME() + { + return true; + } + + /** + * @see File_Archive_Writer::close() + */ + function close() + { + $error = parent::close(); + if (PEAR::isError($error)) { + return $error; + } + $error = $this->addCurrentData(); + if (PEAR::isError($error)) { + return $error; + } + + $body = $this->mime->get(); + $headers = $this->mime->headers($this->headers); + + if (!$this->mail->send( + $this->to, + $headers, + $body) + ) { + return PEAR::raiseError("Error sending mail"); + } + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Memory.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Memory.php new file mode 100644 index 0000000..0b54788 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Memory.php @@ -0,0 +1,127 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Memory.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; + +/** + * Write the concatenation of the files in a buffer + */ +class File_Archive_Writer_Memory extends File_Archive_Writer +{ + /** + * @var string $data The buffer + * @access private + */ + var $data = ""; + /** + * Information about the file being written into this writer + * @access private + */ + var $filename; + var $stat; + var $mime; + + /** + * @param reference $data If provided, the data will be output in this + * variable. Any existent data in $data will be overwritten by the + * actual data of the writer. You should not modify manually this + * variable while using this writer (you can safely use all the + * functions of the archive, like clear for example) + * @param int keptData is the offset from where to start writing in $data + * Any data located after $seek will be erased + * The default value is 0 + */ + function File_Archive_Writer_Memory(&$data, $seek = 0) + { + $this->data =& $data; + $this->data = substr($data, 0, $seek); + } + + function writeData($d) { $this->data .= $d; } + + /** + * @see File_Archive_Writer::newFile() + */ + function newFile($filename, $stat, $mime = "application/octet-stream") + { + $this->filename = $filename; + $this->stat = $stat; + $this->mime = $mime; + } + /** + * @see File_Archive_Writer::newFileNeedsMIME + */ + function newFileNeedsMIME() + { + return true; + } + + /** + * Retrieve the concatenated data + * The value is returned by reference for performance problems, but you + * should not manually modify it + * + * @return string buffer + */ + function &getData() { return $this->data; } + + /** + * Clear the buffer + */ + function clear() { $this->data = ""; } + + /** + * Returns true iif the buffer is empty + */ + function isEmpty() { return empty($this->data); } + + /** + * Create a reader from this writer + * + * @param string $filename Name of the file provided by the reader + * @param array $stat Statistics of the file provided by the reader + * @param string $mime Mime type of the file provided by the reader + * + * Any unspecified parameter will be set to the value of the last file + * written in this writer + */ + function makeReader($filename = null, $stat = null, $mime = null) + { + require_once "File/Archive/Reader/Memory.php"; + return new File_Archive_Reader_Memory( + $this->data, + $filename === null ? $this->filename : $filename, + $stat === null ? $this->stat : $stat, + $mime === null ? $this->mime : $mime); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/MemoryArchive.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/MemoryArchive.php new file mode 100644 index 0000000..b668c0a --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/MemoryArchive.php @@ -0,0 +1,213 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: MemoryArchive.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer/Archive.php"; +require_once "File/Archive/Writer/Memory.php"; + +/** + * Base class for all the archiveWriters that can only work on complete files + * (the write data function may be called with small chunks of data) + */ +class File_Archive_Writer_MemoryArchive extends File_Archive_Writer_Archive +{ + /** + * @var File_Archive_Writer_Memory A buffer where the data will be put + * waiting for the file to be complete + * @access private + */ + var $buffer = ''; + /** + * @var string Name of the file which data are coming + * @access private + */ + var $currentFilename = null; + /** + * @var array Stats of the file which data are coming + * @access private + */ + var $currentStat = null; + /** + * @var string URL of the file being treated if it is a physical file + * @access private + */ + var $currentDataFile = null; + /** + * @var int Number of times newFile function has been called + * @access protected + */ + var $nbFiles = 0; + + /** + * @see File_Archive_Writer::File_Archive_Writer() + */ + function File_Archive_Writer_MemoryArchive + ($filename, &$t, $stat = array(), $autoClose = true) + { + parent::File_Archive_Writer_Archive($filename, $t, $stat, $autoClose); + } + + /** + * @see File_Archive_Writer::newFile() + */ + function _newFile($filename, $stat = array(), + $mime = "application/octet-stream") + { + if ($this->nbFiles == 0) { + $error = $this->sendHeader(); + if (PEAR::isError($error)) { + return $error; + } + } else { + $error = $this->flush(); + if (PEAR::isError($error)) { + return $error; + } + } + + $this->nbFiles++; + + $this->currentFilename = $filename; + $this->currentStat = $stat; + + return true; + } + /** + * @see File_Archive_Writer::close() + */ + function close() + { + $error = $this->flush(); + if (PEAR::isError($error)) { + return $error; + } + $error = $this->sendFooter(); + if (PEAR::isError($error)) { + return $error; + } + + return parent::close(); + } + /** + * Indicate that all the data have been read from the current file + * and send it to appendFileData + * Send the current data to the appendFileData function + * + * @access private + */ + function flush() + { + if ($this->currentFilename !== null) { + if ($this->currentDataFile !== null) { + $error = $this->appendFile($this->currentFilename, + $this->currentDataFile); + } else { + $error = $this->appendFileData($this->currentFilename, + $this->currentStat, + $this->buffer); + } + if (PEAR::isError($error)) { + return $error; + } + + $this->currentFilename = null; + $this->currentDataFile = null; + $this->buffer = ''; + } + } + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) + { + if ($this->currentDataFile !== null) { + $this->buffer .= file_get_contents($this->currentDataFile); + $this->currentDataFile = null; + } + $this->buffer .= $data; + } + /** + * @see File_Archive_Writer::writeFile() + */ + function writeFile($filename) + { + if ($this->currentDataFile === null && empty($this->buffer)) { + $this->currentDataFile = $filename; + } else { + if ($this->currentDataFile !== null) { + $this->buffer .= file_get_contents($this->currentDataFile); + $this->currentDataFile = null; + } + $this->buffer .= file_get_contents($filename); + } + } + +//MUST REWRITE FUNCTIONS + /** + * The subclass must treat the data $data + * $data is the entire data of the filename $filename + * $stat is the stat of the file + * + * @access protected + */ + function appendFileData($filename, $stat, &$data) { } + +//SHOULD REWRITE FUNCTIONS + /** + * The subclass may rewrite the sendHeader function if it needs to execute + * code before the first file + * + * @access protected + */ + function sendHeader() { } + /** + * The subclass may rewrite the sendFooter function if it needs to execute + * code before closing the archive + * + * @access protected + */ + function sendFooter() { } + /** + * The subclass may rewrite this class if it knows an efficient way to treat + * a physical file. + * + * @access protected + */ + function appendFile($filename, $dataFilename) + { + return $this->appendFileData( + $filename, + stat($dataFilename), + file_get_contents($dataFilename)); + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Multi.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Multi.php new file mode 100644 index 0000000..55fc25b --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Multi.php @@ -0,0 +1,130 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Multi.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; + +/** + * Write to several writers + */ +class File_Archive_Writer_Multi extends File_Archive_Writer +{ + /** + * @var File_Archive_Writer_Writer Data will be copied to these two writers + * @access private + */ + var $writers; + + function addWriter(&$writer) + { + $this->writers[] =& $writer; + } + + /** + * @see File_Archive_Writer::newFile() + */ + function newFile($filename, $stat = array(), $mime = "application/octet-stream") + { + $globalError = null; + foreach($this->writers as $key => $foo) { + $error = $this->writers[$key]->newFile($filename, $stat, $mime); + if (PEAR::isError($error)) { + $globalError = $error; + } + } + if (PEAR::isError($globalError)) { + return $globalError; + } + } + /** + * @see File_Archive_Writer::newFileNeedsMIME() + */ + function newFileNeedsMIME() + { + foreach($this->writers as $key => $foo) { + if ($this->writers[$key]->newFileNeedsMIME()) { + return true; + } + } + return false; + } + + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) + { + $globalError = null; + foreach($this->writers as $key => $foo) { + $error = $this->writers[$key]->writeData($data); + if (PEAR::isError($error)) { + $globalError = $error; + } + } + if (PEAR::isError($globalError)) { + return $globalError; + } + } + + /** + * @see File_Archive_Writer::writeFile() + */ + function writeFile($filename) + { + $globalError = null; + foreach($this->writers as $key => $foo) { + $error = $this->writers[$key]->writeFile($filename); + if (PEAR::isError($error)) { + $globalError = $error; + } + } + if (PEAR::isError($globalError)) { + return $globalError; + } + } + + /** + * @see File_Archive_Writer::close() + */ + function close() + { + $globalError = null; + foreach($this->writers as $key => $foo) { + $error = $this->writers[$key]->close(); + if (PEAR::isError($error)) { + $globalError = $error; + } + } + if (PEAR::isError($globalError)) { + return $globalError; + } + } +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Output.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Output.php new file mode 100644 index 0000000..67ede45 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Output.php @@ -0,0 +1,93 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Output.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; + +/** + * Writer to the standard output + * It will concatenate the files that it receive + * It may send some headers, but will do so only for the first file + */ +class File_Archive_Writer_Output extends File_Archive_Writer +{ + /** + * @var bool If true, the Content-type and Content-disposition headers + * will be sent. The file will be considered as an attachment and + * the MIME will be deduced from its extension + * @access private + */ + var $sendHeaders; + + /** + * @param $sendHeaders see the variable + */ + function File_Archive_Writer_Output($sendHeaders = true) + { + $this->sendHeaders = $sendHeaders; + } + /** + * @see File_Archive_Writer::newFile() + */ + function newFile($filename, $stat = array(), $mime = "application/octet-stream") + { + if ($this->sendHeaders) { + if(headers_sent()) { + return PEAR::raiseError( + 'The headers have already been sent. '. + 'Use File_Archive::toOutput(false) to write '. + 'to output without sending headers'); + } + + header("Content-type: $mime"); + header("Content-disposition: attachment; filename=\"$filename\""); + $this->sendHeaders = false; + } + } + /** + * @see File_Archive_Writer::newFileNeedsMIME + */ + function newFileNeedsMIME() + { + return $this->sendHeaders; + } + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) { echo $data; } + /** + * @see File_Archive_Writer::writeFile() + */ + function writeFile($filename) { readfile($filename); } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Tar.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Tar.php new file mode 100644 index 0000000..69c0d61 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Tar.php @@ -0,0 +1,244 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Tar.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer/Archive.php"; + +/** + * Write the files as a TAR archive + */ +class File_Archive_Writer_Tar extends File_Archive_Writer_Archive +{ + var $buffer; + var $useBuffer; + + var $filename = null; + var $stats = null; + + + /** + * Creates the TAR header for a file + * + * @param string $filename name of the file + * @param array $stat statistics of the file + * @return string A 512 byte header for the file + * @access private + */ + function tarHeader($filename, $stat) + { + $mode = isset($stat[2]) ? $stat[2] : 0x8000; + $uid = isset($stat[4]) ? $stat[4] : 0; + $gid = isset($stat[5]) ? $stat[5] : 0; + $size = $stat[7]; + $time = isset($stat[9]) ? $stat[9] : time(); + $link = ""; + + if ($mode & 0x4000) { + $type = 5; // Directory + } else if ($mode & 0x8000) { + $type = 0; // Regular + } else if ($mode & 0xA000) { + $type = 1; // Link + $link = @readlink($current); + } else { + $type = 9; // Unknown + } + + $filePrefix = ''; + if (strlen($filename) > 255) { + return PEAR::raiseError( + "$filename is too long to be put in a tar archive" + ); + } else if (strlen($filename) > 100) { + // need a path component of max 155 bytes + $pos = strrpos(substr($filename, 0, 155), '/'); + if(strlen($filename) - $pos > 100) { + // filename-component may not exceed 100 bytes + return PEAR::raiseError( + "$filename is too long to be put in a tar archive"); + } + $filePrefix = substr($filename, 0, $pos); + $filename = substr($filename, $pos+1); + } + + $blockbeg = pack("a100a8a8a8a12a12", + $filename, + decoct($mode), + sprintf("%6s ",decoct($uid)), + sprintf("%6s ",decoct($gid)), + sprintf("%11s ",decoct($size)), + sprintf("%11s ",decoct($time)) + ); + + $blockend = pack("a1a100a6a2a32a32a8a8a155a12", + $type, + $link, + "ustar", + "00", + "Unknown", + "Unknown", + "", + "", + $filePrefix, + ""); + + $checksum = 8*ord(" "); + for ($i = 0; $i < 148; $i++) { + $checksum += ord($blockbeg{$i}); + } + for ($i = 0; $i < 356; $i++) { + $checksum += ord($blockend{$i}); + } + + $checksum = pack("a8",sprintf("%6s ",decoct($checksum))); + + return $blockbeg . $checksum . $blockend; + } + /** + * Creates the TAR footer for a file + * + * @param int $size the size of the data that has been written to the TAR + * @return string A string made of less than 512 characteres to fill the + * last 512 byte long block + * @access private + */ + function tarFooter($size) + { + if ($size % 512 > 0) { + return pack("a".(512 - $size%512), ""); + } else { + return ""; + } + } + + function flush() + { + if ($this->filename !== null) { + if ($this->useBuffer) { + $this->stats[7] = strlen($this->buffer); + + $header = $this->tarHeader($this->filename, $this->stats); + if (PEAR::isError($header)) { + return $header; + } + $this->innerWriter->writeData($header); + $this->innerWriter->writeData( + $this->buffer + ); + } + $this->innerWriter->writeData( + $this->tarFooter($this->stats[7]) + ); + } + $this->buffer = ""; + } + + function _newFile($filename, $stats = array(), + $mime = "application/octet-stream") + { + $err = $this->flush(); + if (PEAR::isError($err)) { + return $err; + } + + $this->useBuffer = !isset($stats[7]); + $this->filename = $filename; + $this->stats = $stats; + + if (!$this->useBuffer) { + $header = $this->tarHeader($filename, $stats); + if (PEAR::isError($header)) { + return $header; + } + return $this->innerWriter->writeData($header); + } + } + + /** + * @see File_Archive_Writer::close() + */ + function close() + { + $err = $this->flush(); + if (PEAR::isError($err)) { + return $err; + } + $this->innerWriter->writeData(pack("a1024", "")); + parent::close(); + } + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) + { + if ($this->useBuffer) { + $this->buffer .= $data; + } else { + $this->innerWriter->writeData($data); + } + + } + /** + * @see File_Archive_Writer::writeFile() + */ + function writeFile($filename) + { + if ($this->useBuffer) { + if (!file_exists($filename)) { + return PEAR::raiseError("File not found: $filename."); + } + $this->buffer .= file_get_contents($filename); + } else { + $this->innerWriter->writeFile($filename); + } + } + /** + * @see File_Archive_Writer::getMime() + */ + function getMime() { return "application/x-tar"; } +} + + +/** + * A tar archive cannot contain files with name of folders longer than 255 chars + * This filter removes them + * + * @see File_Archive_Predicate, File_Archive_Reader_Filter + */ +require_once "File/Archive/Predicate.php"; +class File_Archive_Predicate_TARCompatible extends File_Archive_Predicate +{ + function isTrue($source) + { + return strlen($source->getFilename()) <= 255; + } +} + +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/UniqueAppender.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/UniqueAppender.php new file mode 100644 index 0000000..6f70489 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/UniqueAppender.php @@ -0,0 +1,143 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: UniqueAppender.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer.php"; +require_once "File/Archive/Reader.php"; +require_once "File/Archive/Predicate/Index.php"; + +/** + * A writer wrapper that will remove the files the eventual duplicate + * files from the reader to keep only the new ones + * If there were already some duplications in the provided reader, not + * all duplication will be removed + * If you use newFile with the same filename several file, only the latest + * write will be kept (no time comparision is done) + */ +class File_Archive_Writer_UniqueAppender extends File_Archive_Writer +{ + var $reader; + var $writer; + var $fileList = array(); + var $toDelete = array(); + + /** + * Construct a unique writer that will write to the specified writer + * and remove duplicate files from the reader on close + */ + function File_Archive_Writer_UniqueAppender(&$reader) + { + $reader->close(); + $pos = 0; + while ($reader->next()) { + $this->fileList[$reader->getFilename()] = $pos++; + } + + $this->reader =& $reader; + $this->writer = $reader->makeAppendWriter(); + } + + /** + * @see File_Archive_Writer::newFile() + */ + function newFile($filename, $stat = array(), $mime = "application/octet-stream") + { + if (isset($this->fileList[$filename])) { + $this->toDelete[$this->fileList[$filename]] = true; + } + + return $this->writer->newFile($filename, $stat, $mime); + } + + /** + * @see File_Archive_Writer::newFromTempFile() + */ + function newFromTempFile($tmpfile, $filename, $stat = array(), $mime = "application/octet-stream") + { + if (isset($this->fileList[$filename])) { + $this->toDelete[$this->fileList[$filename]] = true; + } + + return $this->writer->newFromTempFile($tmpfile, $filename, $stat, $mime); + } + + /** + * @see File_Archive_Writer::newFileNeedsMIME() + */ + function newFileNeedsMIME() + { + return $this->writer->newFileNeedsMIME(); + } + + /** + * @see File_Archive_Writer::writeData() + */ + function writeData($data) + { + return $this->writer->writeData($data); + } + + /** + * @see File_Archive_Writer::writeFile() + */ + function writeFile($filename) + { + return $this->writer->writeFile($filename); + } + + /** + * Close the writer, eventually flush the data, write the footer... + * This function must be called before the end of the script + */ + function close() + { + $error = $this->writer->close(); + if (PEAR::isError($error)) { + return $error; + } + + if (!empty($this->toDelete)) { + $tmp = $this->reader->makeWriterRemoveFiles( + new File_Archive_Predicate_Index($this->toDelete) + ); + if (PEAR::isError($tmp)) { + return $tmp; + } + + return $tmp->close(); + } + } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Zip.php b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Zip.php new file mode 100644 index 0000000..af72d3a --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Archive/Writer/Zip.php @@ -0,0 +1,260 @@ + + * @copyright 1997-2005 The PHP Group + * @license http://www.gnu.org/copyleft/lesser.html LGPL + * @version CVS: $Id: Zip.php 2 2010-11-23 14:32:26Z oldperl $ + * @link http://pear.php.net/package/File_Archive + */ + +require_once "File/Archive/Writer/MemoryArchive.php"; + +/** + * ZIP archive writer + */ +class File_Archive_Writer_Zip extends File_Archive_Writer_MemoryArchive +{ + /** + * @var int Compression level + * @access private + */ + var $compressionLevel; + + /** + * @var int Current position in the writer + * @access private + */ + var $offset = 0; + + /** + * @var string Optionnal comment to add to the zip + * @access private + */ + var $comment = ""; + + /** + * @var string Data written at the end of the ZIP file + * @access private + */ + var $central = ""; + + function File_Archive_Writer_Zip($filename, &$innerWriter, + $stat=array(), $autoClose = true) + { + global $_File_Archive_Options; + parent::File_Archive_Writer_MemoryArchive( + $filename, $innerWriter, $stat, $autoClose + ); + + $this->compressionLevel = File_Archive::getOption('zipCompressionLevel', 9); + } + + /** + * Change the level of the compression. This may be done between two files + * + * @param Int $compressionLevel New compression level from 0 to 9 + */ + function setCompressionLevel($compressionLevel) + { + $this->compressionLevel = $compressionLevel; + } + + /** + * Set a comment on the ZIP file + */ + function setComment($comment) { $this->comment = $comment; } + + /** + * @param int $time Unix timestamp of the date to convert + * @return the date formated as a ZIP date + */ + function getMTime($time) + { + $mtime = ($time !== null ? getdate($time) : getdate()); + $mtime = preg_replace( + "/(..){1}(..){1}(..){1}(..){1}/", + "\\x\\4\\x\\3\\x\\2\\x\\1", + dechex(($mtime['year']-1980<<25)| + ($mtime['mon' ]<<21)| + ($mtime['mday' ]<<16)| + ($mtime['hours' ]<<11)| + ($mtime['minutes']<<5)| + ($mtime['seconds']>>1))); + eval('$mtime = "'.$mtime.'";'); + return $mtime; + } + + /** + * Inform the archive that $filename is present. + * Consequences are the same as appendFileData, but no data is output + * to the inner writer. + * This is used by File_Archive_Reader_Zip::makeWriter() + * + * @param string $filename name of the file + * @param array $stat stats of the file, indexes 9 and 7 must be present + * @param int $crc32 checksum of the file + * @param int $compLength length of the compressed data + */ + function alreadyWrittenFile($filename, $stat, $crc32, $complength) + { + $filename = preg_replace("/^(\.{1,2}(\/|\\\))+/","",$filename); + + $mtime = $this->getMTime(isset($stat[9]) ? $stat[9] : null); + $normlength = $stat[7]; + + $this->nbFiles++; + + $this->central .= "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00". + $mtime. + pack("VVVvvvvvVV", + $crc32, $complength, $normlength, + strlen($filename), 0x00,0x00,0x00,0x00, + 0x0000,$this->offset). + $filename; + + $this->offset += 30 + strlen($filename) + $complength; + } + + /** + * @see File_Archive_Writer_MemoryArchive::appendFileData() + * @access protected + */ + function appendFileData($filename, $stat, $data) + { + $crc32 = crc32($data); + $normlength = strlen($data); + $data = gzcompress($data,$this->compressionLevel); + $data = substr($data,2,strlen($data)-6); + + return $this->appendCompressedData($filename, $stat, $data, $crc32, $normlength); + } + + function appendCompressedData($filename, $stat, $data, $crc32, $normlength) + { + $filename = preg_replace("/^(\.{1,2}(\/|\\\))+/","",$filename); + $mtime = $this->getMTime(isset($stat[9]) ? $stat[9] : null); + + $complength = strlen($data); + + $zipData = "\x50\x4b\x03\x04\x14\x00\x00\x00\x08\x00". + $mtime. + pack("VVVvv", + $crc32, + $complength, + $normlength, + strlen($filename), + 0x00). + $filename. + $data; + + $error = $this->innerWriter->writeData($zipData); + if (PEAR::isError($error)) { + return $error; + } + + $this->central .= "\x50\x4b\x01\x02\x00\x00\x14\x00\x00\x00\x08\x00". + $mtime. + pack("VVVvvvvvVV", + $crc32, $complength, $normlength, + strlen($filename), 0x00,0x00,0x00,0x00, + 0x0000,$this->offset). + $filename; + + $this->offset += strlen($zipData); + } + + function appendFile($filename, $dataFilename) + { + //Try to read from the cache + $cache = File_Archive::getOption('cache', null); + if ($cache !== null && $this->compressionLevel > 0) { + $id = realpath($dataFilename); + $id = urlencode($id); + $id = str_replace('_', '%5F', $id); + + $group = 'FileArchiveZip'.$this->compressionLevel; + $mtime = filemtime($dataFilename); + + //Tries to read from cache + if (($data = $cache->get($id, $group)) !== false) { + $info = unpack('Vmtime/Vcrc/Vnlength', substr($data, 0, 12)); + $data = substr($data, 12); + } + + //If cache failed or file modified since then + if ($data === false || + $info['mtime'] != $mtime) { + + $data = file_get_contents($dataFilename); + + $info = array( + 'crc' => crc32($data), + 'nlength' => strlen($data), + 'mtime' => $mtime + ); + + $data = gzcompress($data,$this->compressionLevel); + $data = substr($data,2,strlen($data)-6); + $data = pack('VVV', $info['mtime'], $info['crc'], $info['nlength']).$data; + $cache->save($data, $id, $group); + } + + return $this->appendCompressedData( + $filename, + stat($dataFilename), + $data, + $info['crc'], + $info['nlength'] + ); + + } + + //If no cache system, use the standard way + return parent::appendFile($filename, $dataFilename); + } + + /** + * @see File_Archive_Writer_MemoryArchive::sendFooter() + * @access protected + */ + function sendFooter() + { + return $this->innerWriter->writeData( + $this->central. + "\x50\x4b\x05\x06\x00\x00\x00\x00". + pack("vvVVv", + $this->nbFiles,$this->nbFiles, + strlen($this->central),$this->offset, + strlen($this->comment)). + $this->comment + ); + } + /** + * @see File_Archive_Writer::getMime() + */ + function getMime() { return "application/zip"; } +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Iterator.php b/conlite/plugins/pluginmanager/docs/pear/File/Iterator.php new file mode 100644 index 0000000..e4db9f6 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Iterator.php @@ -0,0 +1,197 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package File + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @since File available since Release 1.0.0 + */ + +/** + * FilterIterator implementation that filters files based on prefix(es) and/or + * suffix(es). Hidden files and files from hidden directories are also filtered. + * + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version Release: 1.2.3 + * @link http://github.com/sebastianbergmann/php-file-iterator/tree + * @since Class available since Release 1.0.0 + */ +class File_Iterator extends FilterIterator +{ + const PREFIX = 0; + const SUFFIX = 1; + + /** + * @var array + */ + protected $suffixes = array(); + + /** + * @var array + */ + protected $prefixes = array(); + + /** + * @var array + */ + protected $exclude = array(); + + /** + * @var string + */ + protected $basepath; + + /** + * @param Iterator $iterator + * @param array $suffixes + * @param array $prefixes + * @param array $exclude + * @param string $basepath + */ + public function __construct(Iterator $iterator, array $suffixes = array(), array $prefixes = array(), array $exclude = array(), $basepath = NULL) + { + $exclude = array_map('realpath', $exclude); + + if ($basepath !== NULL) { + $basepath = realpath($basepath); + } + + if ($basepath === FALSE) { + $basepath = NULL; + } else { + foreach ($exclude as &$_exclude) { + $_exclude = str_replace($basepath, '', $_exclude); + } + } + + $this->prefixes = $prefixes; + $this->suffixes = $suffixes; + $this->exclude = $exclude; + $this->basepath = $basepath; + + parent::__construct($iterator); + } + + /** + * @return boolean + */ + public function accept() + { + $current = $this->getInnerIterator()->current(); + $filename = $current->getFilename(); + $realpath = $current->getRealPath(); + + if ($this->basepath !== NULL) { + $realpath = str_replace($this->basepath, '', $realpath); + } + + // Filter files in hidden directories. + if (preg_match('=/\.[^/]*/=', $realpath)) { + return FALSE; + } + + return $this->acceptPath($realpath) && + $this->acceptPrefix($filename) && + $this->acceptSuffix($filename); + } + + /** + * @param string $path + * @return boolean + * @since Method available since Release 1.1.0 + */ + protected function acceptPath($path) + { + foreach ($this->exclude as $exclude) { + if (strpos($path, $exclude) === 0) { + return FALSE; + } + } + + return TRUE; + } + + /** + * @param string $filename + * @return boolean + * @since Method available since Release 1.1.0 + */ + protected function acceptPrefix($filename) + { + return $this->acceptSubString($filename, $this->prefixes, self::PREFIX); + } + + /** + * @param string $filename + * @return boolean + * @since Method available since Release 1.1.0 + */ + protected function acceptSuffix($filename) + { + return $this->acceptSubString($filename, $this->suffixes, self::SUFFIX); + } + + /** + * @param string $filename + * @param array $subString + * @param integer $type + * @return boolean + * @since Method available since Release 1.1.0 + */ + protected function acceptSubString($filename, array $subStrings, $type) + { + if (empty($subStrings)) { + return TRUE; + } + + $matched = FALSE; + + foreach ($subStrings as $string) { + if (($type == self::PREFIX && strpos($filename, $string) === 0) || + ($type == self::SUFFIX && + substr($filename, -1 * strlen($string)) == $string)) { + $matched = TRUE; + break; + } + } + + return $matched; + } +} +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/File/Iterator/Factory.php b/conlite/plugins/pluginmanager/docs/pear/File/Iterator/Factory.php new file mode 100644 index 0000000..34f7bf5 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/File/Iterator/Factory.php @@ -0,0 +1,155 @@ +. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in + * the documentation and/or other materials provided with the + * distribution. + * + * * Neither the name of Sebastian Bergmann nor the names of his + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + * + * @package File + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @since File available since Release 1.1.0 + */ + +require_once 'File/Iterator.php'; + +/** + * Factory Method implementation that creates a File_Iterator that operates on + * an AppendIterator that contains an RecursiveDirectoryIterator for each given + * path. + * + * @author Sebastian Bergmann + * @copyright 2009-2010 Sebastian Bergmann + * @license http://www.opensource.org/licenses/bsd-license.php BSD License + * @version Release: 1.2.3 + * @link http://github.com/sebastianbergmann/php-file-iterator/tree + * @since Class available since Release 1.1.0 + */ +class File_Iterator_Factory +{ + /** + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes + * @param array $exclude + * @return AppendIterator + */ + public static function getFileIterator($paths, $suffixes = '', $prefixes = '', array $exclude = array()) + { + if (is_string($paths)) { + $paths = array($paths); + } + + $_paths = array(); + + foreach ($paths as $path) { + if ($locals = glob($path, GLOB_ONLYDIR)) { + $_paths = array_merge($_paths, $locals); + } else { + $_paths[] = $path; + } + } + + $paths = $_paths; + unset($_paths); + + if (is_string($prefixes)) { + if ($prefixes != '') { + $prefixes = array($prefixes); + } else { + $prefixes = array(); + } + } + + if (is_string($suffixes)) { + if ($suffixes != '') { + $suffixes = array($suffixes); + } else { + $suffixes = array(); + } + } + + $iterator = new AppendIterator; + + foreach ($paths as $path) { + if (is_dir($path)) { + $iterator->append( + new File_Iterator( + new RecursiveIteratorIterator( + new RecursiveDirectoryIterator($path) + ), + $suffixes, + $prefixes, + $exclude, + $path + ) + ); + } + } + + return $iterator; + } + + /** + * @param array|string $paths + * @param array|string $suffixes + * @param array|string $prefixes + * @param array $exclude + * @return array + */ + public static function getFilesAsArray($paths, $suffixes = '', $prefixes = '', array $exclude = array()) + { + if (is_string($paths)) { + $paths = array($paths); + } + + $result = array(); + + $iterator = self::getFileIterator( + $paths, $suffixes, $prefixes, $exclude + ); + + foreach ($iterator as $file) { + $result[] = $file->getRealPath(); + } + + foreach ($paths as $path) { + if (is_file($path)) { + $result[] = realpath($path); + } + } + + return $result; + } +} +?> diff --git a/conlite/plugins/pluginmanager/docs/pear/MIME/Type.php b/conlite/plugins/pluginmanager/docs/pear/MIME/Type.php new file mode 100644 index 0000000..51fde09 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/MIME/Type.php @@ -0,0 +1,523 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Type.php 6 2010-11-23 17:57:24Z oldperl $ + +require_once 'PEAR.php'; + +$_fileCmd = &PEAR::getStaticProperty('MIME_Type', 'fileCmd'); +$_fileCmd = 'file'; + +/** + * Class for working with MIME types + * + * @category MIME + * @package MIME_Type + * @license PHP License 3.0 + * @version @version@ + * @link http://pear.php.net/package/MIME_Type + * @author Ian Eure + */ +class MIME_Type +{ + /** + * The MIME media type + * + * @var string + */ + var $media = ''; + + /** + * The MIME media sub-type + * + * @var string + */ + var $subType = ''; + + /** + * Optional MIME parameters + * + * @var array + */ + var $parameters = array(); + + /** + * List of valid media types. + * A media type is the string in front of the slash. + * The media type of "text/xml" would be "text". + * + * @var array + */ + var $validMediaTypes = array( + 'text', + 'image', + 'audio', + 'video', + 'application', + 'multipart', + 'message' + ); + + + /** + * Constructor. + * + * If $type is set, if will be parsed and the appropriate class vars set. + * If not, you get an empty class. + * This is useful, but not quite as useful as parsing a type. + * + * @param string $type MIME type + * + * @return void + */ + function MIME_Type($type = false) + { + if ($type) { + $this->parse($type); + } + } + + + /** + * Parse a mime-type and set the class variables. + * + * @param string $type MIME type to parse + * + * @return void + */ + function parse($type) + { + $this->media = $this->getMedia($type); + $this->subType = $this->getSubType($type); + $this->parameters = array(); + if (MIME_Type::hasParameters($type)) { + require_once 'MIME/Type/Parameter.php'; + foreach (MIME_Type::getParameters($type) as $param) { + $param = new MIME_Type_Parameter($param); + $this->parameters[$param->name] = $param; + } + } + } + + + /** + * Does this type have any parameters? + * + * @param string $type MIME type to check + * + * @return boolean true if $type has parameters, false otherwise + * @static + */ + function hasParameters($type) + { + if (strstr($type, ';')) { + return true; + } + return false; + } + + + /** + * Get a MIME type's parameters + * + * @param string $type MIME type to get parameters of + * + * @return array $type's parameters + * @static + */ + function getParameters($type) + { + $params = array(); + $tmp = explode(';', $type); + for ($i = 1; $i < count($tmp); $i++) { + $params[] = trim($tmp[$i]); + } + return $params; + } + + + /** + * Strip parameters from a MIME type string. + * + * @param string $type MIME type string + * + * @return string MIME type with parameters removed + * @static + */ + function stripParameters($type) + { + if (strstr($type, ';')) { + return substr($type, 0, strpos($type, ';')); + } + return $type; + } + + + /** + * Removes comments from a media type, subtype or parameter. + * + * @param string $string String to strip comments from + * @param string &$comment Comment is stored in there. + * + * @return string String without comments + * @static + */ + function stripComments($string, &$comment) + { + if (strpos($string, '(') === false) { + return $string; + } + + $inquote = false; + $quoting = false; + $incomment = 0; + $newstring = ''; + + for ($n = 0; $n < strlen($string); $n++) { + if ($quoting) { + if ($incomment == 0) { + $newstring .= $string[$n]; + } else if ($comment !== null) { + $comment .= $string[$n]; + } + $quoting = false; + } else if ($string[$n] == '\\') { + $quoting = true; + } else if (!$inquote && $incomment > 0 && $string[$n] == ')') { + $incomment--; + if ($incomment == 0 && $comment !== null) { + $comment .= ' '; + } + } else if (!$inquote && $string[$n] == '(') { + $incomment++; + } else if ($string[$n] == '"') { + if ($inquote) { + $inquote = false; + } else { + $inquote = true; + } + } else if ($incomment == 0) { + $newstring .= $string[$n]; + } else if ($comment !== null) { + $comment .= $string[$n]; + } + } + + if ($comment !== null) { + $comment = trim($comment); + } + + return $newstring; + } + + + /** + * Get a MIME type's media + * + * @note 'media' refers to the portion before the first slash + * + * @param string $type MIME type to get media of + * + * @return string $type's media + * @static + */ + function getMedia($type) + { + $tmp = explode('/', $type); + return strtolower(trim(MIME_Type::stripComments($tmp[0], $null))); + } + + + /** + * Get a MIME type's subtype + * + * @param string $type MIME type to get subtype of + * + * @return string $type's subtype, null if invalid mime type + * @static + */ + function getSubType($type) + { + $tmp = explode('/', $type); + if (!isset($tmp[1])) { + return null; + } + $tmp = explode(';', $tmp[1]); + return strtolower(trim(MIME_Type::stripComments($tmp[0], $null))); + } + + + /** + * Create a textual MIME type from object values + * + * This function performs the opposite function of parse(). + * + * @return string MIME type string + */ + function get() + { + $type = strtolower($this->media . '/' . $this->subType); + if (count($this->parameters)) { + foreach ($this->parameters as $key => $null) { + $type .= '; ' . $this->parameters[$key]->get(); + } + } + return $type; + } + + + /** + * Is this type experimental? + * + * @note Experimental types are denoted by a leading 'x-' in the media or + * subtype, e.g. text/x-vcard or x-world/x-vrml. + * + * @param string $type MIME type to check + * + * @return boolean true if $type is experimental, false otherwise + * @static + */ + function isExperimental($type) + { + if (substr(MIME_Type::getMedia($type), 0, 2) == 'x-' || + substr(MIME_Type::getSubType($type), 0, 2) == 'x-') { + return true; + } + return false; + } + + + /** + * Is this a vendor MIME type? + * + * @note Vendor types are denoted with a leading 'vnd. in the subtype. + * + * @param string $type MIME type to check + * + * @return boolean true if $type is a vendor type, false otherwise + * @static + */ + function isVendor($type) + { + if (substr(MIME_Type::getSubType($type), 0, 4) == 'vnd.') { + return true; + } + return false; + } + + + /** + * Is this a wildcard type? + * + * @param string $type MIME type to check + * + * @return boolean true if $type is a wildcard, false otherwise + * @static + */ + function isWildcard($type) + { + if ($type == '*/*' || MIME_Type::getSubtype($type) == '*') { + return true; + } + return false; + } + + + /** + * Perform a wildcard match on a MIME type + * + * Example: + * MIME_Type::wildcardMatch('image/*', 'image/png') + * + * @param string $card Wildcard to check against + * @param string $type MIME type to check + * + * @return boolean true if there was a match, false otherwise + * @static + */ + function wildcardMatch($card, $type) + { + if (!MIME_Type::isWildcard($card)) { + return false; + } + + if ($card == '*/*') { + return true; + } + + if (MIME_Type::getMedia($card) == MIME_Type::getMedia($type)) { + return true; + } + + return false; + } + + + /** + * Add a parameter to this type + * + * @param string $name Attribute name + * @param string $value Attribute value + * @param string $comment Comment for this parameter + * + * @return void + */ + function addParameter($name, $value, $comment = false) + { + $tmp = new MIME_Type_Parameter(); + + $tmp->name = $name; + $tmp->value = $value; + $tmp->comment = $comment; + $this->parameters[$name] = $tmp; + } + + + /** + * Remove a parameter from this type + * + * @param string $name Parameter name + * + * @return void + */ + function removeParameter($name) + { + unset($this->parameters[$name]); + } + + + /** + * Autodetect a file's MIME-type + * + * This function may be called staticly. + * + * @internal Tries to use fileinfo extension at first. If that + * does not work, mime_magic is used. If this is also not available + * or does not succeed, "file" command is tried to be executed with + * System_Command. When that fails, too, then we use our in-built + * extension-to-mimetype-mapping list. + * + * @param string $file Path to the file to get the type of + * @param bool $params Append MIME parameters if true + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + * + * @since 1.0.0beta1 + * @static + */ + function autoDetect($file, $params = false) + { + // Sanity checks + if (!file_exists($file)) { + return PEAR::raiseError("File \"$file\" doesn't exist"); + } + + if (!is_readable($file)) { + return PEAR::raiseError("File \"$file\" is not readable"); + } + + if (function_exists('finfo_file')) { + $finfo = finfo_open(FILEINFO_MIME); + $type = finfo_file($finfo, $file); + finfo_close($finfo); + if ($type !== false && $type !== '') { + return MIME_Type::_handleDetection($type, $params); + } + } + + if (function_exists('mime_content_type')) { + $type = mime_content_type($file); + if ($type !== false && $type !== '') { + return MIME_Type::_handleDetection($type, $params); + } + } + + @include_once 'System/Command.php'; + if (class_exists('System_Command')) { + return MIME_Type::_handleDetection( + MIME_Type::_fileAutoDetect($file), + $params + ); + } + + require_once 'MIME/Type/Extension.php'; + $mte = new MIME_Type_Extension(); + return $mte->getMIMEType($file); + } + + + /** + * Handles a detected MIME type and modifies it if necessary. + * + * @param string $type MIME Type of a file + * @param bool $params Append MIME parameters if true + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + */ + function _handleDetection($type, $params) + { + // _fileAutoDetect() may have returned an error. + if (PEAR::isError($type)) { + return $type; + } + + // Don't return an empty string + if (!$type || !strlen($type)) { + return PEAR::raiseError("Sorry, couldn't determine file type."); + } + + // Strip parameters if present & requested + if (MIME_Type::hasParameters($type) && !$params) { + $type = MIME_Type::stripParameters($type); + } + + return $type; + } + + + /** + * Autodetect a file's MIME-type with 'file' and System_Command + * + * This function may be called staticly. + * + * @param string $file Path to the file to get the type of + * + * @return string $file's MIME-type + * + * @since 1.0.0beta1 + * @static + */ + function _fileAutoDetect($file) + { + $cmd = new System_Command(); + + // Make sure we have the 'file' command. + $fileCmd = PEAR::getStaticProperty('MIME_Type', 'fileCmd'); + if (!$cmd->which($fileCmd)) { + unset($cmd); + return PEAR::raiseError("Can't find file command \"{$fileCmd}\""); + } + + $cmd->pushCommand($fileCmd, "-bi " . escapeshellarg($file)); + $res = $cmd->execute(); + unset($cmd); + + return $res; + } + +} \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/MIME/Type/Extension.php b/conlite/plugins/pluginmanager/docs/pear/MIME/Type/Extension.php new file mode 100644 index 0000000..1fc4f80 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/MIME/Type/Extension.php @@ -0,0 +1,298 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Extension.php 6 2010-11-23 17:57:24Z oldperl $ + +require_once 'PEAR.php'; + +/** + * Class for mapping file extensions to MIME types. + * + * @category MIME + * @package MIME_Type + * @author Christian Schmidt + * @license PHP License 3.0 + * @version @version@ + * @link http://pear.php.net/package/MIME_Type + */ +class MIME_Type_Extension +{ + /** + * Mapping between file extension and MIME type. + * + * @internal The array is sorted alphabetically by value and with primary + * extension first. Be careful about not adding duplicate keys - PHP + * silently ignores duplicates. The following command can be used for + * checking for duplicates: + * grep "=> '" Extension.php | cut -d\' -f2 | sort | uniq -d + * application/octet-stream is generally used as fallback when no other + * MIME-type can be found, but the array does not contain a lot of such + * unknown extension. One entry exists, though, to allow detection of + * file extension for this MIME-type. + * + * @var array + */ + var $extensionToType = array ( + 'ez' => 'application/andrew-inset', + 'atom' => 'application/atom+xml', + 'jar' => 'application/java-archive', + 'hqx' => 'application/mac-binhex40', + 'cpt' => 'application/mac-compactpro', + 'mathml' => 'application/mathml+xml', + 'doc' => 'application/msword', + 'dat' => 'application/octet-stream', + 'oda' => 'application/oda', + 'ogg' => 'application/ogg', + 'pdf' => 'application/pdf', + 'ai' => 'application/postscript', + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + 'rdf' => 'application/rdf+xml', + 'rss' => 'application/rss+xml', + 'smi' => 'application/smil', + 'smil' => 'application/smil', + 'gram' => 'application/srgs', + 'grxml' => 'application/srgs+xml', + 'kml' => 'application/vnd.google-earth.kml+xml', + 'kmz' => 'application/vnd.google-earth.kmz', + 'mif' => 'application/vnd.mif', + 'xul' => 'application/vnd.mozilla.xul+xml', + 'xls' => 'application/vnd.ms-excel', + 'xlb' => 'application/vnd.ms-excel', + 'xlt' => 'application/vnd.ms-excel', + 'xlam' => 'application/vnd.ms-excel.addin.macroEnabled.12', + 'xlsb' => 'application/vnd.ms-excel.sheet.binary.macroEnabled.12', + 'xlsm' => 'application/vnd.ms-excel.sheet.macroEnabled.12', + 'xltm' => 'application/vnd.ms-excel.template.macroEnabled.12', + 'docm' => 'application/vnd.ms-word.document.macroEnabled.12', + 'dotm' => 'application/vnd.ms-word.template.macroEnabled.12', + 'ppam' => 'application/vnd.ms-powerpoint.addin.macroEnabled.12', + 'pptm' => 'application/vnd.ms-powerpoint.presentation.macroEnabled.12', + 'ppsm' => 'application/vnd.ms-powerpoint.slideshow.macroEnabled.12', + 'potm' => 'application/vnd.ms-powerpoint.template.macroEnabled.12', + 'ppt' => 'application/vnd.ms-powerpoint', + 'pps' => 'application/vnd.ms-powerpoint', + 'odc' => 'application/vnd.oasis.opendocument.chart', + 'odb' => 'application/vnd.oasis.opendocument.database', + 'odf' => 'application/vnd.oasis.opendocument.formula', + 'odg' => 'application/vnd.oasis.opendocument.graphics', + 'otg' => 'application/vnd.oasis.opendocument.graphics-template', + 'odi' => 'application/vnd.oasis.opendocument.image', + 'odp' => 'application/vnd.oasis.opendocument.presentation', + 'otp' => 'application/vnd.oasis.opendocument.presentation-template', + 'ods' => 'application/vnd.oasis.opendocument.spreadsheet', + 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', + 'odt' => 'application/vnd.oasis.opendocument.text', + 'odm' => 'application/vnd.oasis.opendocument.text-master', + 'ott' => 'application/vnd.oasis.opendocument.text-template', + 'oth' => 'application/vnd.oasis.opendocument.text-web', + 'potx' => 'application/vnd.openxmlformats-officedocument.presentationml.template', + 'ppsx' => 'application/vnd.openxmlformats-officedocument.presentationml.slideshow', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'xltx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.template', + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'dotx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.template', + 'vsd' => 'application/vnd.visio', + 'wbxml' => 'application/vnd.wap.wbxml', + 'wmlc' => 'application/vnd.wap.wmlc', + 'wmlsc' => 'application/vnd.wap.wmlscriptc', + 'vxml' => 'application/voicexml+xml', + 'bcpio' => 'application/x-bcpio', + 'vcd' => 'application/x-cdlink', + 'pgn' => 'application/x-chess-pgn', + 'cpio' => 'application/x-cpio', + 'csh' => 'application/x-csh', + 'dcr' => 'application/x-director', + 'dir' => 'application/x-director', + 'dxr' => 'application/x-director', + 'dvi' => 'application/x-dvi', + 'spl' => 'application/x-futuresplash', + 'tgz' => 'application/x-gtar', + 'gtar' => 'application/x-gtar', + 'hdf' => 'application/x-hdf', + 'js' => 'application/x-javascript', + 'skp' => 'application/x-koan', + 'skd' => 'application/x-koan', + 'skt' => 'application/x-koan', + 'skm' => 'application/x-koan', + 'latex' => 'application/x-latex', + 'nc' => 'application/x-netcdf', + 'cdf' => 'application/x-netcdf', + 'sh' => 'application/x-sh', + 'shar' => 'application/x-shar', + 'swf' => 'application/x-shockwave-flash', + 'sit' => 'application/x-stuffit', + 'sv4cpio' => 'application/x-sv4cpio', + 'sv4crc' => 'application/x-sv4crc', + 'tar' => 'application/x-tar', + 'tcl' => 'application/x-tcl', + 'tex' => 'application/x-tex', + 'texinfo' => 'application/x-texinfo', + 'texi' => 'application/x-texinfo', + 't' => 'application/x-troff', + 'tr' => 'application/x-troff', + 'roff' => 'application/x-troff', + 'man' => 'application/x-troff-man', + 'me' => 'application/x-troff-me', + 'ms' => 'application/x-troff-ms', + 'ustar' => 'application/x-ustar', + 'src' => 'application/x-wais-source', + 'xhtml' => 'application/xhtml+xml', + 'xht' => 'application/xhtml+xml', + 'xslt' => 'application/xslt+xml', + 'xml' => 'application/xml', + 'xsl' => 'application/xml', + 'dtd' => 'application/xml-dtd', + 'zip' => 'application/zip', + 'au' => 'audio/basic', + 'snd' => 'audio/basic', + 'mid' => 'audio/midi', + 'midi' => 'audio/midi', + 'kar' => 'audio/midi', + 'mpga' => 'audio/mpeg', + 'mp2' => 'audio/mpeg', + 'mp3' => 'audio/mpeg', + 'aif' => 'audio/x-aiff', + 'aiff' => 'audio/x-aiff', + 'aifc' => 'audio/x-aiff', + 'm3u' => 'audio/x-mpegurl', + 'wma' => 'audio/x-ms-wma', + 'wax' => 'audio/x-ms-wax', + 'ram' => 'audio/x-pn-realaudio', + 'ra' => 'audio/x-pn-realaudio', + 'rm' => 'application/vnd.rn-realmedia', + 'wav' => 'audio/x-wav', + 'pdb' => 'chemical/x-pdb', + 'xyz' => 'chemical/x-xyz', + 'bmp' => 'image/bmp', + 'cgm' => 'image/cgm', + 'gif' => 'image/gif', + 'ief' => 'image/ief', + 'jpeg' => 'image/jpeg', + 'jpg' => 'image/jpeg', + 'jpe' => 'image/jpeg', + 'png' => 'image/png', + 'svg' => 'image/svg+xml', + 'tiff' => 'image/tiff', + 'tif' => 'image/tiff', + 'djvu' => 'image/vnd.djvu', + 'djv' => 'image/vnd.djvu', + 'wbmp' => 'image/vnd.wap.wbmp', + 'ras' => 'image/x-cmu-raster', + 'ico' => 'image/x-icon', + 'pnm' => 'image/x-portable-anymap', + 'pbm' => 'image/x-portable-bitmap', + 'pgm' => 'image/x-portable-graymap', + 'ppm' => 'image/x-portable-pixmap', + 'rgb' => 'image/x-rgb', + 'xbm' => 'image/x-xbitmap', + 'psd' => 'image/x-photoshop', + 'xpm' => 'image/x-xpixmap', + 'xwd' => 'image/x-xwindowdump', + 'eml' => 'message/rfc822', + 'igs' => 'model/iges', + 'iges' => 'model/iges', + 'msh' => 'model/mesh', + 'mesh' => 'model/mesh', + 'silo' => 'model/mesh', + 'wrl' => 'model/vrml', + 'vrml' => 'model/vrml', + 'ics' => 'text/calendar', + 'ifb' => 'text/calendar', + 'css' => 'text/css', + 'csv' => 'text/csv', + 'html' => 'text/html', + 'htm' => 'text/html', + 'txt' => 'text/plain', + 'asc' => 'text/plain', + 'rtx' => 'text/richtext', + 'rtf' => 'text/rtf', + 'sgml' => 'text/sgml', + 'sgm' => 'text/sgml', + 'tsv' => 'text/tab-separated-values', + 'wml' => 'text/vnd.wap.wml', + 'wmls' => 'text/vnd.wap.wmlscript', + 'etx' => 'text/x-setext', + 'mpeg' => 'video/mpeg', + 'mpg' => 'video/mpeg', + 'mpe' => 'video/mpeg', + 'qt' => 'video/quicktime', + 'mov' => 'video/quicktime', + 'mxu' => 'video/vnd.mpegurl', + 'm4u' => 'video/vnd.mpegurl', + 'flv' => 'video/x-flv', + 'asf' => 'video/x-ms-asf', + 'asx' => 'video/x-ms-asf', + 'wmv' => 'video/x-ms-wmv', + 'wm' => 'video/x-ms-wm', + 'wmx' => 'video/x-ms-wmx', + 'avi' => 'video/x-msvideo', + 'ogv' => 'video/ogg', + 'movie' => 'video/x-sgi-movie', + 'ice' => 'x-conference/x-cooltalk', + ); + + + + /** + * Autodetect a file's MIME-type. + * + * @param string $file Path to the file to get the type of + * + * @return string $file's MIME-type on success, PEAR_Error otherwise + */ + function getMIMEType($file) + { + $extension = substr(strrchr($file, '.'), 1); + if ($extension === false) { + return PEAR::raiseError("File has no extension."); + } + + if (!isset($this->extensionToType[$extension])) { + return PEAR::raiseError("Sorry, couldn't determine file type."); + } + + return $this->extensionToType[$extension]; + } + + + + /** + * Return default MIME-type for the specified extension. + * + * @param string $type MIME-type + * + * @return string A file extension without leading period. + */ + function getExtension($type) + { + require_once 'MIME/Type.php'; + // Strip parameters and comments. + $type = MIME_Type::getMedia($type) . '/' . MIME_Type::getSubType($type); + + $extension = array_search($type, $this->extensionToType); + if ($extension === false) { + return PEAR::raiseError("Sorry, couldn't determine extension."); + } + return $extension; + } + +} + +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/docs/pear/MIME/Type/Parameter.php b/conlite/plugins/pluginmanager/docs/pear/MIME/Type/Parameter.php new file mode 100644 index 0000000..8058371 --- /dev/null +++ b/conlite/plugins/pluginmanager/docs/pear/MIME/Type/Parameter.php @@ -0,0 +1,163 @@ + | +// +----------------------------------------------------------------------+ +// +// $Id: Parameter.php 6 2010-11-23 17:57:24Z oldperl $ + +/** + * Class for working with MIME type parameters + * + * @version @version@ + * @package MIME_Type + * @author Ian Eure + */ +class MIME_Type_Parameter { + /** + * Parameter name + * + * @var string + */ + var $name; + + /** + * Parameter value + * + * @var string + */ + var $value; + + /** + * Parameter comment + * + * @var string + */ + var $comment; + + + /** + * Constructor. + * + * @param string $param MIME parameter to parse, if set. + * @return void + */ + function MIME_Type_Parameter($param = false) + { + if ($param) { + $this->parse($param); + } + } + + + /** + * Parse a MIME type parameter and set object fields + * + * @param string $param MIME type parameter to parse + * @return void + */ + function parse($param) + { + $comment = ''; + $param = MIME_Type::stripComments($param, $comment); + $this->name = $this->getAttribute($param); + $this->value = $this->getValue($param); + $this->comment = $comment; + } + + + /** + * Get a parameter attribute (e.g. name) + * + * @param string MIME type parameter + * @return string Attribute name + * @static + */ + function getAttribute($param) + { + $tmp = explode('=', $param); + return trim($tmp[0]); + } + + + /** + * Get a parameter value + * + * @param string $param MIME type parameter + * @return string Value + * @static + */ + function getValue($param) + { + $tmp = explode('=', $param, 2); + $value = $tmp[1]; + $value = trim($value); + if ($value[0] == '"' && $value[strlen($value)-1] == '"') { + $value = substr($value, 1, -1); + } + $value = str_replace('\\"', '"', $value); + return $value; + } + + + /** + * Get a parameter comment + * + * @param string $param MIME type parameter + * @return string Parameter comment + * @see getComment() + * @static + */ + function getComment($param) + { + $cs = strpos($param, '('); + $comment = substr($param, $cs); + return trim($comment, '() '); + } + + + /** + * Does this parameter have a comment? + * + * @param string $param MIME type parameter + * @return boolean true if $param has a comment, false otherwise + * @static + */ + function hasComment($param) + { + if (strstr($param, '(')) { + return true; + } + return false; + } + + + /** + * Get a string representation of this parameter + * + * This function performs the oppsite of parse() + * + * @return string String representation of parameter + */ + function get() + { + $val = $this->name . '="' . str_replace('"', '\\"', $this->value) . '"'; + if ($this->comment) { + $val .= ' (' . $this->comment . ')'; + } + return $val; + } +} +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/includes/config.autoloader.php b/conlite/plugins/pluginmanager/includes/config.autoloader.php new file mode 100644 index 0000000..d3dbf7d --- /dev/null +++ b/conlite/plugins/pluginmanager/includes/config.autoloader.php @@ -0,0 +1,30 @@ + $sAutoloadClassPath.'class.pim.plugin.php', + 'pimPlugin' => $sAutoloadClassPath.'class.pim.plugin.php', + 'pimPluginDummy' => $sAutoloadClassPath.'class.pim.plugin.dummy.php', + 'pimPluginRelationCollection' => $sAutoloadClassPath.'class.pim.plugin.relation.php', + 'pimPluginRelation' => $sAutoloadClassPath.'class.pim.plugin.relation.php', + 'pimView' => $sAutoloadClassPath.'class.pim.view.php', + 'pimAjax' => $sAutoloadClassPath.'class.pim.ajax.php', + 'pimSetupBase' => $sAutoloadClassPath.'setup/class.pim.setup.base.php', + 'pimSetupPluginInstall' => $sAutoloadClassPath.'setup/class.pim.setup.plugin.install.php', + 'pimSetupPluginUninstall' => $sAutoloadClassPath.'setup/class.pim.setup.plugin.uninstall.php', + 'PluginmanagerAjax' => $sAutoloadClassPath.'class.pluginmanager.ajax.php', + 'pimPluginHandler' => $sAutoloadClassPath.'class.pim.plugin.handler.php', + 'Plugins' => $sAutoloadClassPath.'plugin/interface.plugins.php', + 'pluginHandlerAbstract' => $sAutoloadClassPath.'plugin/class.plugin.handler.abstract.php', + 'pimExeption' => $sAutoloadClassPath.'exeptions/class.pim.exeption.php', + 'pimXmlStructureException' => $sAutoloadClassPath.'exeptions/class.pim.exeption.php', + 'pimSqlParser' => $sAutoloadClassPath.'Util/class.pim.sql.parser.php' + // the following entries may be deleted after recode of pim + //'Contenido_Plugin_Base' => $sAutoloadClassPath.'Contenido_Plugin_Base.class.php', + //'Contenido_PluginConfig_Settings' => $sAutoloadClassPath.'Config/Contenido_PluginConfig_Settings.class.php', + //'Contenido_PluginSetup' => $sAutoloadClassPath.'Setup/Contenido_PluginSetup.class.php', + //'Contenido_VersionCompare_Exception' => $sAutoloadClassPath.'Exceptions/Contenido_VersionCompare_Exception.php', + //'Contenido_PluginSqlBuilder_Deinstall' => $sAutoloadClassPath.'Sql/Contenido_PluginSqlBuilder_Deinstall.php', + //'Contenido_PluginSqlBuilder_Install' => $sAutoloadClassPath.'Sql/Contenido_PluginSqlBuilder_Install.php', + //'Contenido_ArchiveExtractor' => $sAutoloadClassPath.'Util/Zip/Contenido_ArchiveExtractor.class.php' +); +?> \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/includes/config.plugin.php b/conlite/plugins/pluginmanager/includes/config.plugin.php new file mode 100644 index 0000000..7e48e2f --- /dev/null +++ b/conlite/plugins/pluginmanager/includes/config.plugin.php @@ -0,0 +1,16 @@ + \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/includes/functions/simplexml_dump.php b/conlite/plugins/pluginmanager/includes/functions/simplexml_dump.php new file mode 100644 index 0000000..28e51b7 --- /dev/null +++ b/conlite/plugins/pluginmanager/includes/functions/simplexml_dump.php @@ -0,0 +1,200 @@ +getDocNamespaces(false); + + $dump = ''; + // Note that the header is added at the end, so we can add stats + $dump .= '[' . PHP_EOL; + + // SimpleXML objects can be either a single node, or (more commonly) a list of 0 or more nodes + // I haven't found a reliable way of distinguishing between the two cases + // Note that for a single node, foreach($node) acts like foreach($node->children()) + // Numeric array indexes, however, operate consistently: $node[0] just returns the node + $item_index = 0; + while ( isset($sxml[$item_index]) ) + { + $item = $sxml[$item_index]; + $item_index++; + + // It's surprisingly hard to find something which behaves consistently differently for an attribute and an element within SimpleXML + // The below relies on the fact that the DOM makes a much clearer distinction + // Note that this is not an expensive conversion, as we are only swapping PHP wrappers around an existing LibXML resource + if ( dom_import_simplexml($item) instanceOf DOMAttr ) + { + $dump .= $indent . 'Attribute {' . PHP_EOL; + + // To what namespace does this attribute belong? Returns array( alias => URI ) + $ns = $item->getNamespaces(false); + if ( $ns ) + { + $dump .= $indent . $indent . 'Namespace: \'' . reset($ns) . '\'' . PHP_EOL; + if ( key($ns) == '' ) + { + $dump .= $indent . $indent . '(Default Namespace)' . PHP_EOL; + } + else + { + $dump .= $indent . $indent . 'Namespace Alias: \'' . key($ns) . '\'' . PHP_EOL; + } + } + + $dump .= $indent . $indent . 'Name: \'' . $item->getName() . '\'' . PHP_EOL; + $dump .= $indent . $indent . 'Value: \'' . (string)$item . '\'' . PHP_EOL; + + $dump .= $indent . '}' . PHP_EOL; + } + else + { + $dump .= $indent . 'Element {' . PHP_EOL; + + // To what namespace does this element belong? Returns array( alias => URI ) + $ns = $item->getNamespaces(false); + if ( $ns ) + { + $dump .= $indent . $indent . 'Namespace: \'' . reset($ns) . '\'' . PHP_EOL; + if ( key($ns) == '' ) + { + $dump .= $indent . $indent . '(Default Namespace)' . PHP_EOL; + } + else + { + $dump .= $indent . $indent . 'Namespace Alias: \'' . key($ns) . '\'' . PHP_EOL; + } + } + + $dump .= $indent . $indent . 'Name: \'' . $item->getName() . '\'' . PHP_EOL; + // REMEMBER: ALWAYS CAST TO STRING! :) + $dump .= $indent . $indent . 'String Content: \'' . (string)$item . '\'' . PHP_EOL; + + // Now some statistics about attributes and children, by namespace + + // This returns all namespaces used by this node and all its descendants, + // whether declared in this node, in its ancestors, or in its descendants + $all_ns = $item->getNamespaces(true); + // If the default namespace is never declared, it will never show up using the below code + if ( ! array_key_exists('', $all_ns) ) + { + $all_ns[''] = NULL; + } + + foreach ( $all_ns as $ns_alias => $ns_uri ) + { + $children = $item->children($ns_uri); + $attributes = $item->attributes($ns_uri); + + // Somewhat confusingly, in the case where a parent element is missing the xmlns declaration, + // but a descendant adds it, SimpleXML will look ahead and fill $all_ns[''] incorrectly + if ( + $ns_alias == '' + && + ! is_null($ns_uri) + && + count($children) == 0 + && + count($attributes) == 0 + ) + { + // Try looking for a default namespace without a known URI + $ns_uri = NULL; + $children = $item->children($ns_uri); + $attributes = $item->attributes($ns_uri); + } + + // Don't show zero-counts, as they're not that useful + if ( count($children) == 0 && count($attributes) == 0 ) + { + continue; + } + + $ns_label = (($ns_alias == '') ? 'Default Namespace' : "Namespace $ns_alias"); + $dump .= $indent . $indent . 'Content in ' . $ns_label . PHP_EOL; + + if ( ! is_null($ns_uri) ) + { + $dump .= $indent . $indent . $indent . 'Namespace URI: \'' . $ns_uri . '\'' . PHP_EOL; + } + + // Count occurrence of child element names, rather than listing them all out + $child_names = array(); + foreach ( $children as $sx_child ) + { + // Below is a rather clunky way of saying $child_names[ $sx_child->getName() ]++; + // which avoids Notices about unset array keys + $child_node_name = $sx_child->getName(); + if ( array_key_exists($child_node_name, $child_names) ) + { + $child_names[$child_node_name]++; + } + else + { + $child_names[$child_node_name] = 1; + } + } + ksort($child_names); + $child_name_output = array(); + foreach ( $child_names as $name => $count ) + { + $child_name_output[] = "$count '$name'"; + } + + $dump .= $indent . $indent . $indent . 'Children: ' . count($children); + // Don't output a trailing " - " if there are no children + if ( count($children) > 0 ) + { + $dump .= ' - ' . implode(', ', $child_name_output); + } + $dump .= PHP_EOL; + + // Attributes can't be duplicated, but I'm going to put them in alphabetical order + $attribute_names = array(); + foreach ( $attributes as $sx_attribute ) + { + $attribute_names[] = "'" . $sx_attribute->getName() . "'"; + } + ksort($attribute_names); + $dump .= $indent . $indent . $indent . 'Attributes: ' . count($attributes); + // Don't output a trailing " - " if there are no attributes + if ( count($attributes) > 0 ) + { + $dump .= ' - ' . implode(', ', $attribute_names); + } + $dump .= PHP_EOL; + } + + $dump .= $indent . '}' . PHP_EOL; + } + } + $dump .= ']' . PHP_EOL; + + // Add on the header line, with the total number of items output + $dump = 'SimpleXML object (' . $item_index . ' item' . ($item_index > 1 ? 's' : '') . ')' . PHP_EOL . $dump; + + if ( $return ) + { + return $dump; + } + else + { + echo $dump; + } +} diff --git a/conlite/plugins/pluginmanager/includes/functions/simplexml_tree.php b/conlite/plugins/pluginmanager/includes/functions/simplexml_tree.php new file mode 100644 index 0000000..4348d2f --- /dev/null +++ b/conlite/plugins/pluginmanager/includes/functions/simplexml_tree.php @@ -0,0 +1,249 @@ +getDocNamespaces(false); + + $dump = ''; + // Note that the header is added at the end, so we can add stats + + // The initial object passed in may be a single node or a list of nodes, so we need an outer loop first + // Note that for a single node, foreach($node) acts like foreach($node->children()) + // Numeric array indexes, however, operate consistently: $node[0] just returns the node + $root_item_index = 0; + while ( isset($sxml[$root_item_index]) ) + { + $root_item = $sxml[$root_item_index]; + + // Special case if the root is actually an attribute + // It's surprisingly hard to find something which behaves consistently differently for an attribute and an element within SimpleXML + // The below relies on the fact that the DOM makes a much clearer distinction + // Note that this is not an expensive conversion, as we are only swapping PHP wrappers around an existing LibXML resource + if ( dom_import_simplexml($root_item) instanceOf DOMAttr ) + { + // To what namespace does this attribute belong? Returns array( alias => URI ) + $ns = $root_item->getNamespaces(false); + if ( key($ns) ) + { + $dump .= key($ns) . ':'; + } + $dump .= $root_item->getName() . '="' . (string)$root_item . '"' . PHP_EOL; + } + else + { + // Display the root node as a numeric key reference, plus a hint as to its tag name + // e.g. '[42] // ' + + // To what namespace does this attribute belong? Returns array( alias => URI ) + $ns = $root_item->getNamespaces(false); + if ( key($ns) ) + { + $root_node_name = key($ns) . ':' . $root_item->getName(); + } + else + { + $root_node_name = $root_item->getName(); + } + $dump .= "[$root_item_index] // <$root_node_name>" . PHP_EOL; + + // This function is effectively recursing depth-first through the tree, + // but this is managed manually using a stack rather than actual recursion + // Each item on the stack is of the form array(int $depth, SimpleXMLElement $element, string $header_row) + $dump .= _simplexml_tree_recursively_process_node( + $root_item, 1, + $include_string_content, $indent, $content_extract_size + ); + } + + $root_item_index++; + } + + // Add on the header line, with the total number of items output + $dump = 'SimpleXML object (' . $root_item_index . ' item' . ($root_item_index > 1 ? 's' : '') . ')' . PHP_EOL . $dump; + + if ( $return ) + { + return $dump; + } + else + { + echo $dump; + } +} + +/** + * "Private" function to perform the recursive part of simplexml_tree() + * Do not call this function directly or rely on its function signature remaining stable + */ +function _simplexml_tree_recursively_process_node($item, $depth, $include_string_content, $indent, $content_extract_size) +{ + $dump = ''; + + if ( $include_string_content ) + { + // Show a chunk of the beginning of the content string, collapsing whitespace HTML-style + $string_content = (string)$item; + + $string_extract = preg_replace('/\s+/', ' ', trim($string_content)); + if ( strlen($string_extract) > $content_extract_size ) + { + $string_extract = substr($string_extract, 0, $content_extract_size) + . '...'; + } + + if ( strlen($string_content) > 0 ) + { + $dump .= str_repeat($indent, $depth) + . '(string) ' + . "'$string_extract'" + . ' (' . strlen($string_content) . ' chars)' + . PHP_EOL; + } + } + + // To what namespace does this element belong? Returns array( alias => URI ) + $item_ns = $item->getNamespaces(false); + if ( ! $item_ns ) + { + $item_ns = array('' => NULL); + } + + // This returns all namespaces used by this node and all its descendants, + // whether declared in this node, in its ancestors, or in its descendants + $all_ns = $item->getNamespaces(true); + // If the default namespace is never declared, it will never show up using the below code + if ( ! array_key_exists('', $all_ns) ) + { + $all_ns[''] = NULL; + } + + // Prioritise "current" namespace by merging into onto the beginning of the list + // (it will be added to the beginning and the duplicate entry dropped) + $all_ns = array_merge($item_ns, $all_ns); + + foreach ( $all_ns as $ns_alias => $ns_uri ) + { + $children = $item->children($ns_alias, true); + $attributes = $item->attributes($ns_alias, true); + + // If things are in the current namespace, display them a bit differently + $is_current_namespace = ( $ns_uri == reset($item_ns) ); + + if ( count($attributes) > 0 ) + { + if ( ! $is_current_namespace ) + { + $dump .= str_repeat($indent, $depth) + . "->attributes('$ns_alias', true)" . PHP_EOL; + } + + foreach ( $attributes as $sx_attribute ) + { + // Output the attribute + if ( $is_current_namespace ) + { + // In current namespace + // e.g. ['attribName'] + $dump .= str_repeat($indent, $depth) + . "['" . $sx_attribute->getName() . "']" + . PHP_EOL; + $string_display_depth = $depth+1; + } + else + { + // After a call to ->attributes() + // e.g. ->attribName + $dump .= str_repeat($indent, $depth+1) + . '->' . $sx_attribute->getName() + . PHP_EOL; + $string_display_depth = $depth+2; + } + + if ( $include_string_content ) + { + // Show a chunk of the beginning of the content string, collapsing whitespace HTML-style + $string_content = (string)$sx_attribute; + + $string_extract = preg_replace('/\s+/', ' ', trim($string_content)); + if ( strlen($string_extract) > $content_extract_size ) + { + $string_extract = substr($string_extract, 0, $content_extract_size) + . '...'; + } + + $dump .= str_repeat($indent, $string_display_depth) + . '(string) ' + . "'$string_extract'" + . ' (' . strlen($string_content) . ' chars)' + . PHP_EOL; + } + } + } + + if ( count($children) > 0 ) + { + if ( $is_current_namespace ) + { + $display_depth = $depth; + } + else + { + $dump .= str_repeat($indent, $depth) + . "->children('$ns_alias', true)" . PHP_EOL; + $display_depth = $depth + 1; + } + + // Recurse through the children with headers showing how to access them + $child_names = array(); + foreach ( $children as $sx_child ) + { + // Below is a rather clunky way of saying $child_names[ $sx_child->getName() ]++; + // which avoids Notices about unset array keys + $child_node_name = $sx_child->getName(); + if ( array_key_exists($child_node_name, $child_names) ) + { + $child_names[$child_node_name]++; + } + else + { + $child_names[$child_node_name] = 1; + } + + // e.g. ->Foo[0] + $dump .= str_repeat($indent, $display_depth) + . '->' . $sx_child->getName() + . '[' . ($child_names[$child_node_name]-1) . ']' + . PHP_EOL; + + $dump .= _simplexml_tree_recursively_process_node( + $sx_child, $display_depth+1, + $include_string_content, $indent, $content_extract_size + ); + } + } + } + + return $dump; +} diff --git a/conlite/plugins/pluginmanager/includes/include.right_bottom.php b/conlite/plugins/pluginmanager/includes/include.right_bottom.php new file mode 100644 index 0000000..7ac550c --- /dev/null +++ b/conlite/plugins/pluginmanager/includes/include.right_bottom.php @@ -0,0 +1,221 @@ + + * @author Frederic Schneider + * @copyright (c) 2008-2015, ConLite.org + * + * $Id: include.right_bottom.php 41 2018-05-20 21:55:49Z oldperl $ + */ +if (!defined('CON_FRAMEWORK')) { + die('Illegal call'); +} +/* @var $sess Contenido_Session */ +/* @var $perm Contenido_Perm */ +/* @var $auth Contenido_Challenge_Crypt_Auth */ + +$oNoti = new Contenido_Notification(); +$aMessages = array(); +$oPage = new cPage(); +$oPage->sendNoCacheHeaders(); +$oPage->setHtml5(); +$oPage->addCssFile("plugins/pluginmanager/css/pluginmanager.css"); +$oPage->addJsFile("plugins/pluginmanager/scripts/jquery.plainoverlay.js"); +$oPage->addJsFile("plugins/pluginmanager/scripts/jquery.plainmodal.js"); +$oPage->addJsFile("plugins/pluginmanager/scripts/pluginmanager.js"); + +// give permission only to sysadmin +/* @var $perm Contenido_Perm */ +if (!$perm->isSysadmin()) { + $oPage->setContent($oNoti->returnNotification(Contenido_Notification::LEVEL_ERROR, i18n("Permission denied!"))); + $oPage->render(); + die(); +} + +// check disable plugin var +if ($cfg['debug']['disable_plugins'] === true) { + $oPage->setContent($oNoti->returnNotification(Contenido_Notification::LEVEL_WARNING, i18n('Currently the plugin system is disabled via configuration', "pluginmanager"))); + $oPage->render(); +} + +$oPimPluginCollection = new pimPluginCollection(); +$oView = new pimView(); + +$sViewAction = isset($_REQUEST['plugin_action']) ? $_REQUEST['plugin_action'] : 'overview'; + +switch ($sViewAction) { + case 'uninstall_plugin': + $iPluginId = (int) $_POST['plugin_id']; + $oPluginHandler = new pimPluginHandler(); + if($oPluginHandler->loadPluginFromDb($iPluginId)) { + if($oPluginHandler->uninstallPlugin($_POST['delete_sql'])) { + $aMessages[] = "info:".i18n("Plugin uninstalled!", "pluginmanager"); + } else { + $aMessages[] = "error:".i18n("Cannot uninstall Plugin!", "pluginmanager"); + } + } else { + $aMessages[] = "error:".i18n("Cannot uninstall Plugin! Plugin not found in Db.", "pluginmanager"); + } + break; + case 'upload': + $aMessages[] = "info:".i18n("Feature not implemented right now!", "pluginmanager"); + /* + // name of uploaded file + $sTempFile = Contenido_Security::escapeDB($_FILES['package']['name'], null); + + // path to pluginmanager temp-dir + $sTempFilePath = $cfg['path']['contenido'] . $cfg['path']['plugins'] . 'pluginmanager' . DIRECTORY_SEPARATOR . 'temp' . DIRECTORY_SEPARATOR; + + move_uploaded_file($_FILES['package']['tmp_name'], $sTempFilePath . $sTempFile); + + $oExtractor = new Contenido_ArchiveExtractor($sTempFilePath . $sTempFile); + + // xml file validation + $oSetup->sTempXml = $oExtractor->extractArchiveFileToVariable('plugin.xml'); + $oSetup->checkXml(); + + // load plugin.xml to an xml-string + $oTempXml = simplexml_load_string($oSetup->sTempXml); + + // check min contenido version + if (!empty($oTempXml->general->min_contenido_version) && version_compare($cfg['version'], $oTempXml->general->min_contenido_version, '<')) { + throw new Contenido_VersionCompare_Exception(i18n("You have to installed Contenido ") . $oTempXml->general->min_contenido_version . i18n(" or higher to install this plugin!")); + } + + // build the new plugin dir + $sTempPluginDir = $cfg['path']['contenido'] . $cfg['path']['plugins'] . $oTempXml->general->plugin_foldername . DIRECTORY_SEPARATOR; + + // add sql inserts + $oSql = new Contenido_PluginSqlBuilder_Install($cfg, (int) $client, $oTempXml); + + if ($oSetup->bValid === true) { + $oExtractor->setDestinationPath($sTempPluginDir); + $oExtractor->extractArchive(); + } + + unlink($sTempPluginDir . 'plugin.xml'); + unlink($sTempFilePath . $sTempFile); + */ + break; + default: + break; +} + +// get installed plugins and build view +$aPluginsInstalled = array(); +$sPlugins = ''; +$oPimPluginCollection->select(NULL, NULL, 'executionorder'); +$iNumInstalledPlugins = $oPimPluginCollection->count(); +/* @var $oPimPlugin pimPlugin */ +while ($oPimPlugin = $oPimPluginCollection->next()) { + // initalization new class + $iIdPlugin = $oPimPlugin->get("idplugin"); + $oView2 = new pimView(); + $oPluginHandler = new pimPluginHandler(); + $sPlugins .= $oPluginHandler->getInfoInstalled($iIdPlugin); + $aPluginsInstalled[] = $oPimPlugin->get("folder"); +} + +// get plugins extracted but not installed +if (is_dir($cfg['path']['plugins'])) { + if ($handle = opendir($cfg['path']['plugins'])) { + $iPiExCount = (int) count($aPluginsInstalled) + 1; + $pluginsExtracted = ''; + $aNeededTplVar = array( + "description"=> i18n("This plugin has no description.", "pluginmanager"), + "dependencies" => i18n("This plugin has no dependencies.", "pluginmanager"), + "license" => i18n("License not set.", "pluginmanager") + ); + + $aDefaultLang = array( + 'lang_foldername' => i18n('Foldername', 'pluginmanager'), + 'lang_author' => i18n("Author", "pluginmanager"), + 'lang_contact' => i18n("Contact", "pluginmanager"), + 'lang_license' => i18n("License", "pluginmanager"), + 'lang_dependencies' => i18n("Dependencies", "pluginmanager"), + 'lang_writeable' => i18n("Writable", "pluginmanager") + ); + + while ($pluginFoldername = readdir($handle)) { + $pluginPath = $cfg['path']['contenido'] . $cfg['path']['plugins'] . $pluginFoldername; + $bPiPathWritable = (is_writable($pluginPath))?TRUE:FALSE; + $sPiCfg = $pluginPath . '/cl_plugin.xml'; + + if (is_dir($pluginPath) && file_exists($sPiCfg) && !in_array($pluginFoldername, $aPluginsInstalled)) { + + // get infos from plugin.xml + $oPluginHandler = new pimPluginHandler(); + if(FALSE === $oPluginHandler->loadXmlFile($sPiCfg)) { + $aMessages[] = "error:".sprintf(i18n('Invalid Xml document for %s. Please contact the plugin author.', 'pluginmanager'),$sPiCfg); + continue; + } + //echo "
";
+                //print_r($oPluginHandler->getCfgXmlObject());
+                $aNeededTplVar['writeable'] = ($bPiPathWritable)?i18n("Everything looks fine.", "pluginmanager"):''
+                    .i18n("Pluginfolder not writable!", "pluginmanager").'<\span>';
+                $aInfoGeneral = array_merge($aNeededTplVar, $oPluginHandler->getPiGeneralArray());
+                //echo "
";
+                //print_r($aInfoGeneral);
+                
+                // initalization new template class
+                $oView2 = new pimView();
+                $oView2->setVariable($iPiExCount, "PLUGIN_NUMBER");
+                $oView2->setMultiVariables($aInfoGeneral);
+                $aLang = array(
+                    'FOLDERNAME' => $pluginFoldername,
+                    'INSTALL_LINK' => $sess->url('main.php?area='.$area.'&frame=4&pim_view=install-extracted&pluginFoldername=' . $pluginFoldername)
+                );
+                $oView2->setMultiVariables(array_merge($aLang, $aDefaultLang));                
+                
+                $oView2->setTemplate('pi_manager_extracted_plugins.html');
+                $pluginsExtracted .= $oView2->getRendered(1);
+                $iPiExCount++;
+            }
+        }
+        closedir($handle);
+    }
+}
+
+// if pluginsExtracted var is empty
+if (empty($pluginsExtracted)) {
+    $pluginsExtracted = i18n('No entries', 'pim');
+}
+
+
+// added language vars
+$oView->setVariable(i18n("Add new plugin", 'pluginmanager'), 'LANG_ADD');
+$oView->setVariable(i18n("Please choose a plugin package", 'pluginmanager'), 'LANG_ADD_CHOOSE');
+$oView->setVariable(i18n("Upload plugin package", 'pluginmanager'), 'LANG_ADD_UPLOAD');
+$oView->setVariable(i18n("Remove marked Plugins", 'pluginmanager'), 'LANG_DELETE');
+$oView->setVariable(i18n("Installed Plugins", 'pluginmanager'), 'LANG_INSTALLED');
+$oView->setVariable(i18n("Update marked plugins", 'pluginmanager'), 'LANG_UPDATE');
+$oView->setVariable(i18n('Plugins to install', 'pluginmanager'), 'LANG_EXTRACTED');
+$oView->setVariable(i18n('Drag & Drop plugin to list of installed plugins to install it.', 'pluginmanager'), 'LANG_HINT_EXTRACTED');
+
+// added installed plugins to pi_manager_overview
+$oView->setVariable($iNumInstalledPlugins, 'INSTALLED_PLUGINS');
+$oView->setVariable($sPlugins, 'PLUGINS');
+$oView->setVariable($pluginsExtracted, 'PLUGINS_EXTRACTED');
+
+//print_r($aMessages);
+// show overview page
+$oView->setTemplate('pi_manager_overview.html');
+$sMessages = "";
+if(count($aMessages) > 0 ) {
+    $sMessages .= '";
+}
+//$oView->getRendered();
+$oPage->setContent(array($oView->getRendered(1), $sMessages));
+$oPage->render();
+?>
\ No newline at end of file
diff --git a/conlite/plugins/pluginmanager/locale/de_DE/LC_MESSAGES/pluginmanager.mo b/conlite/plugins/pluginmanager/locale/de_DE/LC_MESSAGES/pluginmanager.mo
new file mode 100644
index 0000000000000000000000000000000000000000..eccc439ec47e782bd61edb945a2a8c2497870428
GIT binary patch
literal 4605
zcmd6qPi!1l9miiGP~rlGwEQc7@-rnNA)V>22_)XQA$Hb5R_w$|?6#mFcsuiU=CQNy
zE%V-ZZ3{S5LU2Gxm8g)oa0#jeS5%?a7vu{?+&FLmAtb~J4oFnuKo5L>^Jl$I6Gcct
zVx;kB=l%V?-{0py9Xs-p!1EN|kKz61Lqc2t|M)ii!Sn043-M9#b@095U%_{RZ-5^L
z{|&al$Bzi{Ft`lPgKMA#zY7k)f7Rd7JA`;QzJCW^1pf?v5Il}WKLCCn{3N&v&VUx=
zb$7w{gD-%OfZ_%(3;T|yA}WAHfm8p!MZ0pxjq2YKEe5`9|uptcx)>l3pXU(_{y2CH?R;OpQ+An!T-@R-lk4Nrl*7Vpopvwc|ig!c?ymK(<}
zpHJa0`_)Oj5M6v4FF$-1-k)_)>KTNqm%3ql!c5{cUYNS{H$Kbs!#bUQV6O6BkW|8+Xt?6w6yG-|?w731!v}9I=_uyse@FdY|}C
z=)RGOD@yvJwv!%?GBt!VVC`HdsfYE(v*L=)VwD>?RAQyFSwoDg{Lf9bIBg;xxg|3l
z)APfOVjC5_EF3VW6Yed>o^rg#)~h;F#wk&=bR(y5xNDZMQqF2xSNTvo2UjF$POErU
zG+vZi3ulsz_C_)q$VA=IU3w#jSYKz{$8p9k5C79!lLj{O;g*R4s~ve-zf@5v-BfnF5AY4%=WwewU*x^8~JRsa2
zW^YkydAh295<&Og!4*AvAR9n`^`@zGPzX$O;Mt+rNbfShMY$evN(sg09f?hBj*uBu
zjYebD4i2rEl}#K_W98>+3y3e-f+8rf2@b@>L^^Ab@z@`&LC)1f1ju@Bzpf%5ti-{y
zDrdjyaI*67t=Q6fY0Wih;7Iw*4K=d457u0w<6x;sT(Ds~G_!tvBe;xo$cG8K5WGWk
zt@gQ~bv|gfseQK7I(Mcu*J@#4a6@fr-eh`QdoF0tQ@hofYoBSgF>X~lA8h2(xJ-JR
zcj$WV$1wc5HUmWqHXQMfOQUoY^x)*S-$&qtvAT$vCMh6mu%YCzLsxQzAl4Dxj8daC
ze$zs~ZVO%0cIj-tcW8D8_WNpgd1h^8ZTUbe?XWddX$HdWjBrY?w*AwiOlot1qEzOH
z)n_(0t_0@~#_=KgDi4-TWMd?!4xR65Kl6OBZdI&(a8>P$Z63Q-{=qhGs)`mqKxs3**3xHOrZlof_33Mu=5@Sr=?B(inMAq)GM?1@p?x
zD=)fQQKH8Cdj?G?1&jBp+==c%dc<#e42ZFk<`4wxD@%7>2I|tiq&?OeR05U?p_|wU
z$Ek%2aCxyg3i$um_SaC`*Wj^tt?mCC4J`K3{o5wd$w1)>WW?r>;02A*cZ)-AMY~$1
zP$DZ6wis#{97b4^1!`2$$JI;`2g~Ft#g2plwMT?v5gzQwVHHnFfihOOP)w>9mw#^R
z@9)95X6hyorH$d63jwb@E07g;6+FUdH+>r@P2OApN*NjvrW;Y%v`2nV_V+Tc;gp1`
z6?E;Oz8dWBRd<8?d(*N(3XBGsf=jLHU1fH~RcMiw!5_$b2wvKgm+bQB-RN~ZMr71WRa5FCym06{k$?=E)
z)mzgIRs0-kJ{0!NzL2ERY^{0(Xs&dxC-Y{EqupXib?8+(hbF4zB-U`1IHY>YPTFFa
zUBEgx#j>N)p^<{|4PD{?fD)7`&R|q32e-~$T+qvyKyWm-jXDdB$^uy3HSTYuI^_SW
zjc?)-@__d3PBJ5<(i6fB{&7e2AtBvu)Dq`Q)_q|TTnegd;UtsSHxY9ejl;}^#4WwD
s%^`\n"
+"Language-Team: Frederic Schneider \n"
+"Language: de_DE\n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=UTF-8\n"
+"Content-Transfer-Encoding: 8bit\n"
+"X-Poedit-KeywordsList: i18n\n"
+"X-Generator: Poedit 1.5.4\n"
+"X-Poedit-Basepath: .\n"
+"X-Poedit-SearchPath-0: C:\\Dateien\\Contenido PluginManager\\includes\n"
+
+#: includes/include.right_bottom.php:37
+msgid "Permission denied!"
+msgstr "Zugriff verweigert!"
+
+#: includes/include.right_bottom.php:44
+msgid "Currently the plugin system is disabled via configuration"
+msgstr "Momentan ist das Pluginsystem via Konfiguration abgeschaltet"
+
+#: includes/include.right_bottom.php:59
+msgid "Plugin uninstalled!"
+msgstr "Plugin deinstalliert!"
+
+# C:\Dateien\Contenido
+#: includes/include.right_bottom.php:61
+msgid "Cannot uninstall Plugin!"
+msgstr "Kann Plugin nicht entfernen!"
+
+#: includes/include.right_bottom.php:64
+msgid "Cannot uninstall Plugin! Plugin not found in Db."
+msgstr "Plugin kann nicht deinstalliert werden! Plugin nicht in der DB."
+
+#: includes/include.right_bottom.php:68
+msgid "Feature not implemented right now!"
+msgstr "Feature noch nicht implementiert!"
+
+#: includes/include.right_bottom.php:131
+msgid "This plugin has no description."
+msgstr "Das Plugin hat keine Beschreibung."
+
+#: includes/include.right_bottom.php:132
+msgid "This plugin has no dependencies."
+msgstr "Das Plugin hat keine Abhängigkeiten."
+
+#: includes/include.right_bottom.php:133
+msgid "License not set."
+msgstr "Keine Lizenzangabe vorhanden."
+
+#: includes/include.right_bottom.php:137
+#: classes/class.pim.plugin.handler.php:162
+msgid "Foldername"
+msgstr "Verzeichnisname"
+
+#: includes/include.right_bottom.php:138
+#: classes/class.pim.plugin.handler.php:163
+msgid "Author"
+msgstr "Autor"
+
+#: includes/include.right_bottom.php:139
+#: classes/class.pim.plugin.handler.php:164
+msgid "Contact"
+msgstr "Kontakt"
+
+#: includes/include.right_bottom.php:140
+#: classes/class.pim.plugin.handler.php:165
+msgid "License"
+msgstr "Lizenz"
+
+#: includes/include.right_bottom.php:141
+#: classes/class.pim.plugin.handler.php:167
+msgid "Dependencies"
+msgstr "Abhängigkeiten"
+
+#: includes/include.right_bottom.php:142
+#: classes/class.pim.plugin.handler.php:168
+msgid "Writable"
+msgstr "Beschreibbar"
+
+#: includes/include.right_bottom.php:156
+#, php-format
+msgid "Invalid Xml document for %s. Please contact the plugin author."
+msgstr ""
+"Ungültiges xml-Dokument für %s. Bitte kontaktieren Sie den Plugin-Entwickler."
+
+#: includes/include.right_bottom.php:161
+msgid "Everything looks fine."
+msgstr "Alles sieht gut aus."
+
+#: includes/include.right_bottom.php:162
+msgid "Pluginfolder not writable!"
+msgstr "Pluginverzeichnis nicht schreibbar!"
+
+#: includes/include.right_bottom.php:188
+msgid "No entries"
+msgstr "Keine Einträge"
+
+# C:\Dateien\Contenido
+#: includes/include.right_bottom.php:193
+msgid "Add new plugin"
+msgstr "Neues Plugin installieren"
+
+# C:\Dateien\Contenido
+#: includes/include.right_bottom.php:194
+msgid "Please choose a plugin package"
+msgstr "Plugin Paket auswählen"
+
+# C:\Dateien\Contenido
+#: includes/include.right_bottom.php:195
+msgid "Upload plugin package"
+msgstr "Plugin Paket hochladen"
+
+# C:\Dateien\Contenido
+#: includes/include.right_bottom.php:196
+msgid "Remove marked Plugins"
+msgstr "Markierte Plugins löschen"
+
+# C:\Dateien\Contenido
+#: includes/include.right_bottom.php:197
+msgid "Installed Plugins"
+msgstr "Installierte Plugins"
+
+# C:\Dateien\Contenido
+#: includes/include.right_bottom.php:198
+msgid "Update marked plugins"
+msgstr "Markierte Plugins aktualisieren"
+
+#: includes/include.right_bottom.php:199
+msgid "Plugins to install"
+msgstr "Plugins zum Installieren"
+
+#: includes/include.right_bottom.php:200
+msgid "Drag & Drop plugin to list of installed plugins to install it."
+msgstr ""
+"Um ein Plugin zu installieren ziehen Sie es mit der Maus auf die Liste "
+"installierter Plugins."
+
+#: classes/class.pluginmanager.ajax.php:37
+#: classes/class.pim.plugin.handler.php:190
+msgid "Plugin is active"
+msgstr "Plugin ist aktiv"
+
+#: classes/class.pluginmanager.ajax.php:39
+#: classes/class.pim.plugin.handler.php:193
+msgid "Plugin not active"
+msgstr "Plugin ist nicht aktiv"
+
+# C:\Dateien\Contenido
+#: classes/class.pim.plugin.handler.php:166
+msgid "Installed since"
+msgstr "Installiert seit"
+
+#: classes/class.pim.plugin.handler.php:169
+msgid "Install"
+msgstr "Installieren"
+
+#: classes/class.pim.plugin.handler.php:170
+msgid "Remove"
+msgstr "Entfernen"
+
+#: classes/class.pim.plugin.handler.php:171
+#: classes/class.pim.plugin.handler.php:173
+msgid "Update"
+msgstr "Updaten"
+
+# C:\Dateien\Contenido
+#: classes/class.pim.plugin.handler.php:172
+msgid "Please choose your new file"
+msgstr "Bitte wählen Sie Ihre neue Datei"
+
+#: classes/class.pim.plugin.handler.php:174
+msgid "Execute uninstall.sql"
+msgstr "uninstall.sql ausführen"
+
+#: classes/class.pim.plugin.handler.php:208
+#, php-format
+msgid "(%s remove database tables)"
+msgstr "(%s DB-Tabellen entfernen)"
+
+# C:\Dateien\Contenido
+#: classes/class.pim.plugin.handler.php:211
+msgid "Uninstall Plugin"
+msgstr "Plugin entfernen"
+
+#: classes/setup/class.pim.setup.plugin.install.php:133
+#: classes/setup/class.pim.setup.plugin.install.php:173
+#, php-format
+msgid ""
+"Defined area %s are not found on your ConLite installation. "
+"Please contact your plugin author."
+msgstr ""
+"Die gewünschte Area %s gibt es nicht in Ihrer ConLite "
+"Installation. Bitte kontaktieren Sie den Plugin-Autor."
+
+#: classes/setup/class.pim.setup.plugin.install.php:219
+msgid ""
+"There seem to be an empty main navigation entry in plugin.xml. Please "
+"contact your plugin author."
+msgstr ""
+"Es gibt anscheinend einen leeren Eintrag zur Main Navigation in der plugin."
+"xml. Bitte kontaktieren Sie den Plugin-Autor."
+
+#: classes/setup/class.pim.setup.plugin.install.php:255
+msgid ""
+"There seem to be an empty sub navigation entry in plugin.xml. Please contact "
+"your plugin author."
+msgstr ""
+"Es gibt anscheinend einen leeren Eintrag zur Sub Navigation in der plugin."
+"xml. Bitte kontaktieren Sie den Plugin-Autor."
+
+#: classes/setup/class.pim.setup.plugin.install.php:300
+msgid "You can install this plugin only for one time."
+msgstr "Sie können dieses Plugin nur ein Mal installieren."
+
+#: classes/setup/class.pim.setup.plugin.install.php:349
+#, php-format
+msgid ""
+"Defined area %s not found on your ConLite installation. "
+"Please contact your plugin author."
+msgstr ""
+"Die gewünschte Area %s gibt es nicht in Ihrer ConLite "
+"Installation. Bitte kontaktieren Sie den Plugin-Autor."
+
+#: classes/setup/class.pim.setup.plugin.install.php:359
+#, php-format
+msgid ""
+"Defined nav main %s not found on your ConLite installation. "
+"Please contact your plugin author."
+msgstr ""
+"Die gewünschte Nav Main %s gibt es nicht in Ihrer ConLite "
+"Installation. Bitte kontaktieren Sie den Plugin-Autor."
diff --git a/conlite/plugins/pluginmanager/locale/pluginmanager.pot b/conlite/plugins/pluginmanager/locale/pluginmanager.pot
new file mode 100644
index 0000000..57f6780
--- /dev/null
+++ b/conlite/plugins/pluginmanager/locale/pluginmanager.pot
@@ -0,0 +1,215 @@
+# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR , YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\n"
+"Report-Msgid-Bugs-To: \n"
+"POT-Creation-Date: 2017-08-11 14:07+0200\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
+"Last-Translator: FULL NAME \n"
+"Language-Team: LANGUAGE \n"
+"Language: \n"
+"MIME-Version: 1.0\n"
+"Content-Type: text/plain; charset=CHARSET\n"
+"Content-Transfer-Encoding: 8bit\n"
+
+#: includes/include.right_bottom.php:37
+msgid "Permission denied!"
+msgstr ""
+
+#: includes/include.right_bottom.php:44
+msgid "Currently the plugin system is disabled via configuration"
+msgstr ""
+
+#: includes/include.right_bottom.php:59
+msgid "Plugin uninstalled!"
+msgstr ""
+
+#: includes/include.right_bottom.php:61
+msgid "Cannot uninstall Plugin!"
+msgstr ""
+
+#: includes/include.right_bottom.php:64
+msgid "Cannot uninstall Plugin! Plugin not found in Db."
+msgstr ""
+
+#: includes/include.right_bottom.php:68
+msgid "Feature not implemented right now!"
+msgstr ""
+
+#: includes/include.right_bottom.php:131
+msgid "This plugin has no description."
+msgstr ""
+
+#: includes/include.right_bottom.php:132
+msgid "This plugin has no dependencies."
+msgstr ""
+
+#: includes/include.right_bottom.php:133
+msgid "License not set."
+msgstr ""
+
+#: includes/include.right_bottom.php:137
+#: classes/class.pim.plugin.handler.php:162
+msgid "Foldername"
+msgstr ""
+
+#: includes/include.right_bottom.php:138
+#: classes/class.pim.plugin.handler.php:163
+msgid "Author"
+msgstr ""
+
+#: includes/include.right_bottom.php:139
+#: classes/class.pim.plugin.handler.php:164
+msgid "Contact"
+msgstr ""
+
+#: includes/include.right_bottom.php:140
+#: classes/class.pim.plugin.handler.php:165
+msgid "License"
+msgstr ""
+
+#: includes/include.right_bottom.php:141
+#: classes/class.pim.plugin.handler.php:167
+msgid "Dependencies"
+msgstr ""
+
+#: includes/include.right_bottom.php:142
+#: classes/class.pim.plugin.handler.php:168
+msgid "Writable"
+msgstr ""
+
+#: includes/include.right_bottom.php:156
+#, php-format
+msgid "Invalid Xml document for %s. Please contact the plugin author."
+msgstr ""
+
+#: includes/include.right_bottom.php:161
+msgid "Everything looks fine."
+msgstr ""
+
+#: includes/include.right_bottom.php:162
+msgid "Pluginfolder not writable!"
+msgstr ""
+
+#: includes/include.right_bottom.php:188
+msgid "No entries"
+msgstr ""
+
+#: includes/include.right_bottom.php:193
+msgid "Add new plugin"
+msgstr ""
+
+#: includes/include.right_bottom.php:194
+msgid "Please choose a plugin package"
+msgstr ""
+
+#: includes/include.right_bottom.php:195
+msgid "Upload plugin package"
+msgstr ""
+
+#: includes/include.right_bottom.php:196
+msgid "Remove marked Plugins"
+msgstr ""
+
+#: includes/include.right_bottom.php:197
+msgid "Installed Plugins"
+msgstr ""
+
+#: includes/include.right_bottom.php:198
+msgid "Update marked plugins"
+msgstr ""
+
+#: includes/include.right_bottom.php:199
+msgid "Plugins to install"
+msgstr ""
+
+#: includes/include.right_bottom.php:200
+msgid "Drag & Drop plugin to list of installed plugins to install it."
+msgstr ""
+
+#: classes/class.pluginmanager.ajax.php:37
+#: classes/class.pim.plugin.handler.php:190
+msgid "Plugin is active"
+msgstr ""
+
+#: classes/class.pluginmanager.ajax.php:39
+#: classes/class.pim.plugin.handler.php:193
+msgid "Plugin not active"
+msgstr ""
+
+#: classes/class.pim.plugin.handler.php:166
+msgid "Installed since"
+msgstr ""
+
+#: classes/class.pim.plugin.handler.php:169
+msgid "Install"
+msgstr ""
+
+#: classes/class.pim.plugin.handler.php:170
+msgid "Remove"
+msgstr ""
+
+#: classes/class.pim.plugin.handler.php:171
+#: classes/class.pim.plugin.handler.php:173
+msgid "Update"
+msgstr ""
+
+#: classes/class.pim.plugin.handler.php:172
+msgid "Please choose your new file"
+msgstr ""
+
+#: classes/class.pim.plugin.handler.php:174
+msgid "Execute uninstall.sql"
+msgstr ""
+
+#: classes/class.pim.plugin.handler.php:208
+#, php-format
+msgid "(%s remove database tables)"
+msgstr ""
+
+#: classes/class.pim.plugin.handler.php:211
+msgid "Uninstall Plugin"
+msgstr ""
+
+#: classes/setup/class.pim.setup.plugin.install.php:133
+#: classes/setup/class.pim.setup.plugin.install.php:173
+#, php-format
+msgid ""
+"Defined area %s are not found on your ConLite installation. "
+"Please contact your plugin author."
+msgstr ""
+
+#: classes/setup/class.pim.setup.plugin.install.php:219
+msgid ""
+"There seem to be an empty main navigation entry in plugin.xml. Please "
+"contact your plugin author."
+msgstr ""
+
+#: classes/setup/class.pim.setup.plugin.install.php:255
+msgid ""
+"There seem to be an empty sub navigation entry in plugin.xml. Please contact "
+"your plugin author."
+msgstr ""
+
+#: classes/setup/class.pim.setup.plugin.install.php:300
+msgid "You can install this plugin only for one time."
+msgstr ""
+
+#: classes/setup/class.pim.setup.plugin.install.php:349
+#, php-format
+msgid ""
+"Defined area %s not found on your ConLite installation. "
+"Please contact your plugin author."
+msgstr ""
+
+#: classes/setup/class.pim.setup.plugin.install.php:359
+#, php-format
+msgid ""
+"Defined nav main %s not found on your ConLite installation. "
+"Please contact your plugin author."
+msgstr ""
diff --git a/conlite/plugins/pluginmanager/locale/potfiles.txt b/conlite/plugins/pluginmanager/locale/potfiles.txt
new file mode 100644
index 0000000..0333d6a
--- /dev/null
+++ b/conlite/plugins/pluginmanager/locale/potfiles.txt
@@ -0,0 +1,26 @@
+./includes/functions/simplexml_dump.php
+./includes/functions/simplexml_tree.php
+./includes/include.right_bottom.php
+./includes/config.autoloader.php
+./includes/config.plugin.php
+./templates/pi_manager_plugins.html
+./templates/pi_manager_install.html
+./templates/pi_manager_extracted_plugins.html
+./templates/pi_manager_overview.html
+./templates/pi_manager_installed_plugins.html
+./classes/exeptions/class.pim.exeption.php
+./classes/class.pluginmanager.ajax.php
+./classes/class.pim.plugin.handler.php
+./classes/class.pim.view.php
+./classes/class.pim.plugin.relation.php
+./classes/class.pim.ajax.php
+./classes/class.pim.plugin.dummy.php
+./classes/plugin/interface.plugins.php
+./classes/plugin/class.plugin.handler.abstract.php
+./classes/Util/Zip/Contenido_ArchiveExtractor.class.php
+./classes/Util/Zip/class.pim.plugin.archive.extractor.php
+./classes/Util/class.pim.sql.parser.php
+./classes/class.pim.plugin.php
+./classes/setup/class.pim.setup.plugin.uninstall.php
+./classes/setup/class.pim.setup.plugin.install.php
+./classes/setup/class.pim.setup.base.php
diff --git a/conlite/plugins/pluginmanager/scripts/jquery.plainmodal.js b/conlite/plugins/pluginmanager/scripts/jquery.plainmodal.js
new file mode 100644
index 0000000..8a3f715
--- /dev/null
+++ b/conlite/plugins/pluginmanager/scripts/jquery.plainmodal.js
@@ -0,0 +1,176 @@
+/*
+ * jQuery.plainModal
+ * https://github.com/anseki/jquery-plainmodal
+ *
+ * Copyright (c) 2014 anseki
+ * Licensed under the MIT license.
+ */
+
+;(function($, undefined) {
+'use strict';
+
+var APP_NAME = 'plainModal',
+    APP_PREFIX = APP_NAME.toLowerCase(),
+    EVENT_TYPE_OPEN = APP_PREFIX + 'open',
+    EVENT_TYPE_CLOSE = APP_PREFIX + 'close',
+
+    jqOpened = null, // jqOpened === null : Not opened / jqOpened === 0 : Fading now
+    jqWin, jqBody, jqOverlay, jqActive, jq1st,
+    orgOverflow, orgMarginR, orgMarginB,
+    winLeft, winTop;
+
+function init(jq, options) {
+  // The options object is shared by all elements in jq.
+  // Therefore, don't change properties later. (Replace options object for new object.)
+  var opt = $.extend(true, {
+        duration:       200,
+        effect:         {open: $.fn.fadeIn, close: $.fn.fadeOut},
+        overlay:        {opacity: 0.6, zIndex: 9000},
+        closeClass:     APP_PREFIX + '-close'
+        // Optional: offset, open, close
+      }, options);
+  opt.overlay.fillColor = opt.overlay.fillColor || opt.overlay.color /* alias */ || '#888';
+  opt.zIndex = opt.zIndex || opt.overlay.zIndex + 1;
+
+  if (!jqWin) { // page init
+    jqWin = $(window);
+    jqOverlay = $('
').css({ + position: 'fixed', + left: 0, + top: 0, + width: '100%', + height: '150%', // for Address Bar of Firefox for Android + display: 'none' + }).appendTo(jqBody = $('body')).click(modalClose) + .on('touchmove', function() { return false; }); // avoid scroll on touch devices + $(document).focusin(function(e) { + if (jqOpened && !jqOpened.has(e.target).length) { + if (jq1st) { jq1st.focus(); } + else { $(document.activeElement).blur(); } + } + }) + .keydown(function(e) { + if (jqOpened && e.keyCode === 27) { // Escape key + return modalClose(e); + } + }); + } + + return jq.each(function() { + var that = $(this), + cssProp = { + position: 'fixed', + display: 'none', + zIndex: opt.zIndex + }; + if (opt.offset) { + if (typeof opt.offset !== 'function') { + cssProp.left = opt.offset.left; + cssProp.top = opt.offset.top; + } + cssProp.marginLeft = cssProp.marginTop = ''; // for change + } else { + cssProp.left = cssProp.top = '50%'; + cssProp.marginLeft = '-' + (that.outerWidth() / 2) + 'px'; + cssProp.marginTop = '-' + (that.outerHeight() / 2) + 'px'; + } + if (opt.closeClass) { + that.find('.' + opt.closeClass).off('click', modalClose).click(modalClose); + } + if (typeof opt.open === 'function') + { that.off(EVENT_TYPE_OPEN, opt.open).on(EVENT_TYPE_OPEN, opt.open); } + if (typeof opt.close === 'function') + { that.off(EVENT_TYPE_CLOSE, opt.close).on(EVENT_TYPE_CLOSE, opt.close); } + that.css(cssProp).data(APP_NAME, opt).appendTo(jqBody) + .on('touchmove', function() { return false; }); // avoid scroll on touch devices + }); +} + +function modalOpen(jq, options) { + var jqTarget, opt, inlineStyles, calMarginR, calMarginB, offset; + if (jqOpened === null && jq.length) { + jqTarget = jq.eq(0); // only 1st + if (options || !(opt = jqTarget.data(APP_NAME))) { + opt = init(jqTarget, options).data(APP_NAME); + } + inlineStyles = jqBody.get(0).style; + + orgOverflow = inlineStyles.overflow; + calMarginR = jqBody.prop('clientWidth'); + calMarginB = jqBody.prop('clientHeight'); + jqBody.css('overflow', 'hidden'); + calMarginR -= jqBody.prop('clientWidth'); + calMarginB -= jqBody.prop('clientHeight'); + orgMarginR = inlineStyles.marginRight; + orgMarginB = inlineStyles.marginBottom; + if (calMarginR < 0) { jqBody.css('marginRight', '+=' + (-calMarginR)); } + if (calMarginB < 0) { jqBody.css('marginBottom', '+=' + (-calMarginB)); } + + jqActive = $(document.activeElement).blur(); // Save activeElement + jq1st = null; + winLeft = jqWin.scrollLeft(); + winTop = jqWin.scrollTop(); + jqWin.scroll(avoidScroll); + + if (typeof opt.offset === 'function') { + offset = opt.offset.call(jqTarget); + jqTarget.css({left: offset.left, top: offset.top}); + } + // If duration is 0, callback is called now. + opt.effect.open.call(jqTarget, opt.duration || 1, function() { + jqTarget.find('a,input,select,textarea,button,object,area,img,map').each(function() { + var that = $(this); + if (that.focus().get(0) === document.activeElement) { // Can focus + jq1st = that; + return false; + } + }); + jqOpened = jqTarget.trigger(EVENT_TYPE_OPEN); + }); + // Re-Style the overlay that is shared by all 'opt'. + jqOverlay.css({backgroundColor: opt.overlay.fillColor, zIndex: opt.overlay.zIndex}) + .fadeTo(opt.duration, opt.overlay.opacity); + jqOpened = 0; + } + return jq; +} + +function modalClose(jq) { // jq: target/event + var isEvent = jq instanceof $.Event, jqTarget, opt; + if (jqOpened) { + jqTarget = isEvent ? jqOpened : (function() { // jqOpened in jq + var index = jq.index(jqOpened); + return index > -1 ? jq.eq(index) : undefined; + })(); + if (jqTarget) { + opt = jqTarget.data(APP_NAME); + // If duration is 0, callback is called now. + opt.effect.close.call(jqTarget, opt.duration || 1, function() { + jqBody.css({overflow: orgOverflow, marginRight: orgMarginR, marginBottom: orgMarginB}); + if (jqActive && jqActive.length) { jqActive.focus(); } // Restore activeElement + jqWin.off('scroll', avoidScroll).scrollLeft(winLeft).scrollTop(winTop); + jqTarget.trigger(EVENT_TYPE_CLOSE); + jqOpened = null; + }); + jqOverlay.fadeOut(opt.duration); + jqOpened = 0; + } + } + if (isEvent) { jq.preventDefault(); return false; } + return jq; +} + +function avoidScroll(e) { + jqWin.scrollLeft(winLeft).scrollTop(winTop); + e.preventDefault(); + return false; +} + +$.fn[APP_NAME] = function(action, options) { + return ( + action === 'open' ? modalOpen(this, options) : + action === 'close' ? modalClose(this) : + init(this, action)); // options. +}; + +})(jQuery); diff --git a/conlite/plugins/pluginmanager/scripts/jquery.plainmodal.min.js b/conlite/plugins/pluginmanager/scripts/jquery.plainmodal.min.js new file mode 100644 index 0000000..0245127 --- /dev/null +++ b/conlite/plugins/pluginmanager/scripts/jquery.plainmodal.min.js @@ -0,0 +1 @@ +!function(o,e){"use strict";function t(e,t){var n=o.extend(!0,{duration:200,effect:{open:o.fn.fadeIn,close:o.fn.fadeOut},overlay:{opacity:.6,zIndex:9e3},closeClass:m+"-close"},t);return n.overlay.fillColor=n.overlay.fillColor||n.overlay.color||"#888",n.zIndex=n.zIndex||n.overlay.zIndex+1,r||(r=o(window),f=o('
').css({position:"fixed",left:0,top:0,width:"100%",height:"150%",display:"none"}).appendTo(c=o("body")).click(l).on("touchmove",function(){return!1}),o(document).focusin(function(e){C&&!C.has(e.target).length&&(s?s.focus():o(document.activeElement).blur())}).keydown(function(o){return C&&27===o.keyCode?l(o):void 0})),e.each(function(){var e=o(this),t={position:"fixed",display:"none",zIndex:n.zIndex};n.offset?("function"!=typeof n.offset&&(t.left=n.offset.left,t.top=n.offset.top),t.marginLeft=t.marginTop=""):(t.left=t.top="50%",t.marginLeft="-"+e.outerWidth()/2+"px",t.marginTop="-"+e.outerHeight()/2+"px"),n.closeClass&&e.find("."+n.closeClass).off("click",l).click(l),"function"==typeof n.open&&e.off(y,n.open).on(y,n.open),"function"==typeof n.close&&e.off(x,n.close).on(x,n.close),e.css(t).data(h,n).appendTo(c).on("touchmove",function(){return!1})})}function n(e,n){var l,m,x,I,w,z;return null===C&&e.length&&(l=e.eq(0),(n||!(m=l.data(h)))&&(m=t(l,n).data(h)),x=c.get(0).style,u=x.overflow,I=c.prop("clientWidth"),w=c.prop("clientHeight"),c.css("overflow","hidden"),I-=c.prop("clientWidth"),w-=c.prop("clientHeight"),d=x.marginRight,p=x.marginBottom,0>I&&c.css("marginRight","+="+-I),0>w&&c.css("marginBottom","+="+-w),a=o(document.activeElement).blur(),s=null,v=r.scrollLeft(),g=r.scrollTop(),r.scroll(i),"function"==typeof m.offset&&(z=m.offset.call(l),l.css({left:z.left,top:z.top})),m.effect.open.call(l,m.duration||1,function(){l.find("a,input,select,textarea,button,object,area,img,map").each(function(){var e=o(this);return e.focus().get(0)===document.activeElement?(s=e,!1):void 0}),C=l.trigger(y)}),f.css({backgroundColor:m.overlay.fillColor,zIndex:m.overlay.zIndex}).fadeTo(m.duration,m.overlay.opacity),C=0),e}function l(t){var n,l,s=t instanceof o.Event;return C&&(n=s?C:function(){var o=t.index(C);return o>-1?t.eq(o):e}(),n&&(l=n.data(h),l.effect.close.call(n,l.duration||1,function(){c.css({overflow:u,marginRight:d,marginBottom:p}),a&&a.length&&a.focus(),r.off("scroll",i).scrollLeft(v).scrollTop(g),n.trigger(x),C=null}),f.fadeOut(l.duration),C=0)),s?(t.preventDefault(),!1):t}function i(o){return r.scrollLeft(v).scrollTop(g),o.preventDefault(),!1}var r,c,f,a,s,u,d,p,v,g,h="plainModal",m=h.toLowerCase(),y=m+"open",x=m+"close",C=null;o.fn[h]=function(o,e){return"open"===o?n(this,e):"close"===o?l(this):t(this,o)}}(jQuery); \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/scripts/jquery.plainoverlay.js b/conlite/plugins/pluginmanager/scripts/jquery.plainoverlay.js new file mode 100644 index 0000000..48714bc --- /dev/null +++ b/conlite/plugins/pluginmanager/scripts/jquery.plainoverlay.js @@ -0,0 +1,426 @@ +/* + * jQuery.plainOverlay + * https://github.com/anseki/jquery-plainoverlay + * + * Copyright (c) 2015 anseki + * Licensed under the MIT license. + */ + +;(function($, undefined) { +'use strict'; + +var APP_NAME = 'plainOverlay', + APP_PREFIX = APP_NAME.toLowerCase(), + EVENT_TYPE_SHOW = APP_PREFIX + 'show', + EVENT_TYPE_HIDE = APP_PREFIX + 'hide', + + // builtin progress element + newProgress = (function() { + function experimental(props, supports, prefix, sep) { // similar to Compass + sep = sep === undefined ? ';' : sep; + return $.map(props, function(prop) { + return $.map(supports, function(support) { + return (prefix || '') + support + prop; + }).join(sep); + }).join(sep); + } + + var isLegacy, domId = 'jQuery-' + APP_NAME, + supports = ['-webkit-','-moz-','-ms-','-o-',''], prefix = domId + '-progress', + cssText = '.'+prefix+'{'+experimental(['box-sizing:border-box'],['-webkit-','-moz-',''])+';width:100%;height:100%;border-top:3px solid #17f29b;'+experimental(['border-radius:50%'],supports)+';-webkit-tap-highlight-color:rgba(0,0,0,0);transform:translateZ(0);box-shadow:0 0 1px rgba(0,0,0,0);'+experimental(['animation-name:'+domId+'-spin','animation-duration:1s','animation-timing-function:linear','animation-iteration-count:infinite'],supports)+'}'+experimental(['keyframes '+domId+'-spin{from{'+experimental(['transform:rotate(0deg)'],supports)+'}to{'+experimental(['transform:rotate(360deg)'],supports)+'}}'],supports,'@','')+'.'+prefix+'-legacy{width:100%;height:50%;padding-top:25%;text-align:center;white-space:nowrap;*zoom:1}.'+prefix+'-legacy:after,.'+prefix+'-legacy:before{content:" ";display:table}.'+prefix+'-legacy:after{clear:both}.'+prefix+'-legacy div{width:18%;height:100%;margin:0 1%;background-color:#17f29b;float:left;visibility:hidden}.'+prefix+'-1 div.'+prefix+'-1,.'+prefix+'-2 div.'+prefix+'-1,.'+prefix+'-2 div.'+prefix+'-2,.'+prefix+'-3 div.'+prefix+'-1,.'+prefix+'-3 div.'+prefix+'-2,.'+prefix+'-3 div.'+prefix+'-3{visibility:visible}', + + adjustProgress = function() { + var progressWH = Math.min(300, // max w/h + (this.isBody ? + Math.min(this.jqWin.width(), this.jqWin.height()) : + Math.min(this.jqTarget.innerWidth(), this.jqTarget.innerHeight())) * 0.9); + this.jqProgress.width(progressWH).height(progressWH); + if (!this.showProgress) { // CSS Animations + this.jqProgress.children('.' + prefix).css('borderTopWidth', + Math.max(3, progressWH / 30)); // min width + } + }, + showProgressLegacy = function(start) { + var that = this; + if (that.timer) { clearTimeout(that.timer); } + if (that.progressCnt) { + that.jqProgress.removeClass(prefix + '-' + that.progressCnt); + } + if (that.isShown) { + that.progressCnt = !start && that.progressCnt < 3 ? that.progressCnt + 1 : 0; + if (that.progressCnt) { + that.jqProgress.addClass(prefix + '-' + that.progressCnt); + } + that.timer = setTimeout(function() { that.showProgress(); }, 500); + } + }; + + return function(overlay) { + var jqProgress, sheet; + + // Graceful Degradation + if (typeof isLegacy !== 'boolean') { + isLegacy = (function() { // similar to Modernizr + function contains(str, substr) { return !!~('' + str).indexOf(substr); } + var res, feature, + modElem = document.createElement('modernizr'), + mStyle = modElem.style, + omPrefixes = 'Webkit Moz O ms', + cssomPrefixes = omPrefixes.split(' '), + tests = {}, + _hasOwnProperty = ({}).hasOwnProperty, + hasOwnProp = _hasOwnProperty !== undefined && + _hasOwnProperty.call !== undefined ? + function (object, property) { + return _hasOwnProperty.call(object, property); + } : + function (object, property) { + return (property in object) && + object.constructor.prototype[property] === undefined; + }; + + function testProps(props) { + var i; + for (i in props) { + if (!contains(props[i], '-') && mStyle[props[i]] !== undefined) { return true; } + } + return false; + } + function testPropsAll(prop) { + var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1), + props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' '); + return testProps(props); + } + + tests.borderradius = function() { + return testPropsAll('borderRadius'); + }; + tests.cssanimations = function() { + return testPropsAll('animationName'); + }; + tests.csstransforms = function() { + return !!testPropsAll('transform'); + }; + + res = false; + for (feature in tests) { + if (hasOwnProp(tests, feature) && !tests[feature]()) { + res = true; + break; + } + } + mStyle.cssText = ''; + modElem = null; + return res; + })(); + } + + if (!overlay.elmDoc.getElementById(domId)) { // Add style rules + if (overlay.elmDoc.createStyleSheet) { // IE + sheet = overlay.elmDoc.createStyleSheet(); + sheet.owningElement.id = domId; + sheet.cssText = cssText; + } else { + sheet = (overlay.elmDoc.getElementsByTagName('head')[0] || overlay.elmDoc.documentElement) + .appendChild(overlay.elmDoc.createElement('style')); + sheet.type = 'text/css'; + sheet.id = domId; + sheet.textContent = cssText; + } + } + + if (isLegacy) { + jqProgress = $('
' + + '
'); + overlay.showProgress = showProgressLegacy; + } else { + jqProgress = $('
'); + } + overlay.adjustProgress = adjustProgress; + return jqProgress; + }; + })(); + +function Overlay(jqTarget, options, curObject) { + var that = this, elmTarget = jqTarget.get(0); + that.duration = options.duration; + that.opacity = options.opacity; + that.isShown = false; + + that.jqTargetOrg = jqTarget; + // that.jqWin = that.jqTarget.ownerDocument.defaultView; // Not supported by IE + if ($.isWindow(elmTarget) || elmTarget.nodeType === 9) { // window or document -> body + that.jqTarget = $('body'); + } else if (elmTarget.nodeName.toLowerCase() === 'iframe' || + elmTarget.nodeName.toLowerCase() === 'frame') { // iframe or frame -> body of it + // contentDocument not supported by IE + that.jqWin = $(elmTarget.contentWindow); + that.elmDoc = elmTarget.contentWindow.document; + that.jqTarget = $('body', that.elmDoc); + that.isFrame = true; + } else { + that.jqTarget = jqTarget; + } + that.jqWin = that.jqWin || $(window); + that.elmDoc = that.elmDoc || document; + that.isBody = that.jqTarget.get(0).nodeName.toLowerCase() === 'body'; + + if (curObject) { + // Remove jqProgress that exists always, because it may be replaced. + if (curObject.jqProgress) { + if (curObject.timer) { clearTimeout(curObject.timer); } + curObject.jqProgress.remove(); delete curObject.jqProgress; + } + curObject.reset(true); // Restore styles + curObject.jqOverlay.stop(); + } + + that.jqOverlay = (curObject && curObject.jqOverlay || + $('
').css({ + position: that.isBody ? 'fixed' : 'absolute', + left: 0, + top: 0, + display: 'none', + cursor: 'wait' + }).appendTo(that.jqTarget) + .on('touchmove', function() { return false; }) // avoid scroll on touch devices + ).css({backgroundColor: options.fillColor, zIndex: options.zIndex}); + + if (that.jqProgress = options.progress === false ? undefined : + (typeof options.progress === 'function' ? + options.progress.call(that.jqTarget, options) : newProgress(that))) { + that.jqProgress.css({ + position: that.isBody ? 'fixed' : 'absolute', + display: 'none', + zIndex: options.zIndex + 1, + cursor: 'wait' + }).appendTo(that.jqTarget) + .on('touchmove', function() { return false; }); // avoid scroll on touch devices; + } + + // Not shared methods for calling per object in event of one element. + that.callAdjust = (function(that) { + return that.adjustProgress ? function() { + that.adjustProgress(); + that.adjust(); + } : function() { that.adjust(); }; + })(that); + that.avoidFocus = (function(that) { + return function(e) { + $(that.elmDoc.activeElement).blur(); + e.preventDefault(); + return false; + }; + })(that); + that.avoidScroll = (function(that) { + return function(e) { + (function(jqView) { + jqView.scrollLeft(that.scrLeft).scrollTop(that.scrTop); + })(that.isBody ? that.jqWin : that.jqTarget); + e.preventDefault(); + return false; + }; + })(that); + + if (curObject) { + if (curObject.timer) { clearTimeout(curObject.timer); } + curObject = undefined; // Erase + } +} + +Overlay.prototype.show = function() { + var that = this, inlineStyles, position, calMarginR, calMarginB, jqActive; + that.reset(true); // Restore styles + inlineStyles = that.jqTarget.get(0).style; + + that.orgPosition = inlineStyles.position; + position = that.jqTarget.css('position'); + if (position !== 'relative' && position !== 'absolute' && position !== 'fixed') { + that.jqTarget.css('position', 'relative'); + } + + that.orgOverflow = inlineStyles.overflow; + calMarginR = that.jqTarget.prop('clientWidth'); + calMarginB = that.jqTarget.prop('clientHeight'); + that.jqTarget.css('overflow', 'hidden'); + calMarginR -= that.jqTarget.prop('clientWidth'); + calMarginB -= that.jqTarget.prop('clientHeight'); + that.addMarginR = that.addMarginB = 0; + if (calMarginR < 0) { that.addMarginR = -calMarginR; } + if (calMarginB < 0) { that.addMarginB = -calMarginB; } + if (that.isBody) { + if (that.addMarginR) { + that.orgMarginR = inlineStyles.marginRight; + that.jqTarget.css('marginRight', '+=' + that.addMarginR); + } + if (that.addMarginB) { + that.orgMarginB = inlineStyles.marginBottom; + that.jqTarget.css('marginBottom', '+=' + that.addMarginB); + } + } else { // change these in adjust() + if (that.addMarginR) { + that.orgMarginR = inlineStyles.paddingRight; + that.orgWidth = inlineStyles.width; + } + if (that.addMarginB) { + that.orgMarginB = inlineStyles.paddingBottom; + that.orgHeight = inlineStyles.height; + } + } + + that.jqActive = undefined; + jqActive = $(that.elmDoc.activeElement); + if (that.isBody && !that.isFrame) { that.jqActive = jqActive.blur(); } // Save activeElement + else if (that.jqTarget.has(jqActive.get(0)).length) { jqActive.blur(); } + that.jqTarget.focusin(that.avoidFocus); + (function(jqView) { + that.scrLeft = jqView.scrollLeft(); + that.scrTop = jqView.scrollTop(); + jqView.scroll(that.avoidScroll); + })(that.isBody ? that.jqWin : that.jqTarget); + that.jqWin.resize(that.callAdjust); + that.callAdjust(); + that.isShown = true; + + that.jqOverlay.stop().fadeTo(that.duration, that.opacity, + function() { that.jqTargetOrg.trigger(EVENT_TYPE_SHOW); }); + if (that.jqProgress) { + if (that.showProgress) { that.showProgress(true); } + that.jqProgress.fadeIn(that.duration); + } +}; + +Overlay.prototype.hide = function() { + var that = this; + if (!that.isShown) { return; } + that.jqOverlay.stop().fadeOut(that.duration, + function() { that.reset(); that.jqTargetOrg.trigger(EVENT_TYPE_HIDE); }); + if (that.jqProgress) { that.jqProgress.fadeOut(that.duration); } +}; + +Overlay.prototype.adjust = function() { + var calW, calH; + if (this.isBody) { + // base of overlay size and progress position is window. + calW = this.jqWin.width(); + calH = this.jqWin.height(); + this.jqOverlay.width(calW).height(calH); + if (this.jqProgress) { + this.jqProgress.css({ + left: (calW - this.jqProgress.outerWidth()) / 2, + top: (calH - this.jqProgress.outerHeight()) / 2 + }); + } + } else { + if (this.addMarginR) { + calW = this.jqTarget.css({paddingRight: this.orgMarginR, width: this.orgWidth}) + .width(); // original size + this.jqTarget.css('paddingRight', '+=' + this.addMarginR).width(calW - this.addMarginR); + } + if (this.addMarginB) { + calH = this.jqTarget.css({paddingBottom: this.orgMarginB, height: this.orgHeight}) + .height(); // original size + this.jqTarget.css('paddingBottom', '+=' + this.addMarginB).height(calH - this.addMarginB); + } + + // base of overlay size is element size that includes hidden area. + calW = Math.max(this.jqTarget.prop('scrollWidth'), this.jqTarget.innerWidth()); // for IE bug + calH = Math.max(this.jqTarget.prop('scrollHeight'), this.jqTarget.innerHeight()); + this.jqOverlay.width(calW).height(calH); + if (this.jqProgress) { + // base of progress position is element size that doesn't include hidden area. + calW = this.jqTarget.innerWidth(); + calH = this.jqTarget.innerHeight(); + this.jqProgress.css({ + left: (calW - this.jqProgress.outerWidth()) / 2 + this.scrLeft, + top: (calH - this.jqProgress.outerHeight()) / 2 + this.scrTop + }); + } + } +}; + +Overlay.prototype.reset = function(forceHide) { + // default: display of jqOverlay and jqProgress is kept + var that = this; + if (forceHide) { + that.jqOverlay.css('display', 'none'); + if (that.jqProgress) { that.jqProgress.css('display', 'none'); } + } + if (!that.isShown) { return; } + that.jqTarget.css({position: that.orgPosition, overflow: that.orgOverflow}); + if (that.isBody) { + if (that.addMarginR) { that.jqTarget.css('marginRight', that.orgMarginR); } + if (that.addMarginB) { that.jqTarget.css('marginBottom', that.orgMarginB); } + } else { + if (that.addMarginR) { + that.jqTarget.css({paddingRight: that.orgMarginR, width: that.orgWidth}); + } + if (that.addMarginB) { + that.jqTarget.css({paddingBottom: that.orgMarginB, height: that.orgHeight}); + } + } + that.jqTarget.off('focusin', that.avoidFocus); + if (that.jqActive && that.jqActive.length) { that.jqActive.focus(); } // Restore activeElement + (function(jqView) { + jqView.off('scroll', that.avoidScroll).scrollLeft(that.scrLeft).scrollTop(that.scrTop); + })(that.isBody ? that.jqWin : that.jqTarget); + that.jqWin.off('resize', that.callAdjust); + that.isShown = false; +}; + +function init(jq, options) { + var opt = $.extend({ + duration: 200, + opacity: 0.6, + zIndex: 9000 + // Optional: progress, show, hide + }, options); + opt.fillColor = opt.fillColor || opt.color /* alias */ || '#888'; + return jq.each(function() { + var that = $(this); + that.data(APP_NAME, new Overlay(that, opt, that.data(APP_NAME))); + if (typeof opt.show === 'function') + { that.off(EVENT_TYPE_SHOW, opt.show).on(EVENT_TYPE_SHOW, opt.show); } + if (typeof opt.hide === 'function') + { that.off(EVENT_TYPE_HIDE, opt.hide).on(EVENT_TYPE_HIDE, opt.hide); } + }); +} + +function overlayShow(jq, options) { + return jq.each(function() { + var that = $(this), overlay; + if (options || !(overlay = that.data(APP_NAME))) { + overlay = init(that, options).data(APP_NAME); + } + overlay.show(); + }); +} + +function overlayHide(jq) { + return jq.each(function() { + var overlay = $(this).data(APP_NAME); + if (overlay) { overlay.hide(); } + }); +} + +function overlaySetOption(jq, name, newValue) { + var jqTarget = jq.length ? jq.eq(0) : undefined, // only 1st + overlay; + if (!jqTarget) { return; } + overlay = jqTarget.data(APP_NAME) || init(jqTarget).data(APP_NAME); + if (!overlay.hasOwnProperty(name)) { return; } +/* jshint eqnull:true */ + if (newValue != null) { overlay[name] = newValue; } +/* jshint eqnull:false */ + return overlay[name]; +} + +$.fn[APP_NAME] = function(action, arg1, arg2) { + return ( + action === 'show' ? overlayShow(this, arg1) : + action === 'hide' ? overlayHide(this) : + action === 'option' ? overlaySetOption(this, arg1, arg2) : + init(this, action)); // action = options. +}; + +})(jQuery); diff --git a/conlite/plugins/pluginmanager/scripts/jquery.plainoverlay.min.js b/conlite/plugins/pluginmanager/scripts/jquery.plainoverlay.min.js new file mode 100644 index 0000000..8b54482 --- /dev/null +++ b/conlite/plugins/pluginmanager/scripts/jquery.plainoverlay.min.js @@ -0,0 +1 @@ +!function(t,r){"use strict";function i(i,e,o){var s=this,n=i.get(0);s.duration=e.duration,s.opacity=e.opacity,s.isShown=!1,s.jqTargetOrg=i,t.isWindow(n)||9===n.nodeType?s.jqTarget=t("body"):"iframe"===n.nodeName.toLowerCase()||"frame"===n.nodeName.toLowerCase()?(s.jqWin=t(n.contentWindow),s.elmDoc=n.contentWindow.document,s.jqTarget=t("body",s.elmDoc),s.isFrame=!0):s.jqTarget=i,s.jqWin=s.jqWin||t(window),s.elmDoc=s.elmDoc||document,s.isBody="body"===s.jqTarget.get(0).nodeName.toLowerCase(),o&&(o.jqProgress&&(o.timer&&clearTimeout(o.timer),o.jqProgress.remove(),delete o.jqProgress),o.reset(!0),o.jqOverlay.stop()),s.jqOverlay=(o&&o.jqOverlay||t('
').css({position:s.isBody?"fixed":"absolute",left:0,top:0,display:"none",cursor:"wait"}).appendTo(s.jqTarget).on("touchmove",function(){return!1})).css({backgroundColor:e.fillColor,zIndex:e.zIndex}),(s.jqProgress=e.progress===!1?r:"function"==typeof e.progress?e.progress.call(s.jqTarget,e):c(s))&&s.jqProgress.css({position:s.isBody?"fixed":"absolute",display:"none",zIndex:e.zIndex+1,cursor:"wait"}).appendTo(s.jqTarget).on("touchmove",function(){return!1}),s.callAdjust=function(t){return t.adjustProgress?function(){t.adjustProgress(),t.adjust()}:function(){t.adjust()}}(s),s.avoidFocus=function(r){return function(i){return t(r.elmDoc.activeElement).blur(),i.preventDefault(),!1}}(s),s.avoidScroll=function(t){return function(r){return function(r){r.scrollLeft(t.scrLeft).scrollTop(t.scrTop)}(t.isBody?t.jqWin:t.jqTarget),r.preventDefault(),!1}}(s),o&&(o.timer&&clearTimeout(o.timer),o=r)}function e(r,e){var o=t.extend({duration:200,opacity:.6,zIndex:9e3},e);return o.fillColor=o.fillColor||o.color||"#888",r.each(function(){var r=t(this);r.data(n,new i(r,o,r.data(n))),"function"==typeof o.show&&r.off(d,o.show).on(d,o.show),"function"==typeof o.hide&&r.off(g,o.hide).on(g,o.hide)})}function o(r,i){return r.each(function(){var r,o=t(this);(i||!(r=o.data(n)))&&(r=e(o,i).data(n)),r.show()})}function s(r){return r.each(function(){var r=t(this).data(n);r&&r.hide()})}var n="plainOverlay",a=n.toLowerCase(),d=a+"show",g=a+"hide",c=function(){function i(i,e,o,s){return s=s===r?";":s,t.map(i,function(r){return t.map(e,function(t){return(o||"")+t+r}).join(s)}).join(s)}var e,o="jQuery-"+n,s=["-webkit-","-moz-","-ms-","-o-",""],a=o+"-progress",d="."+a+"{"+i(["box-sizing:border-box"],["-webkit-","-moz-",""])+";width:100%;height:100%;border-top:3px solid #17f29b;"+i(["border-radius:50%"],s)+";-webkit-tap-highlight-color:rgba(0,0,0,0);transform:translateZ(0);box-shadow:0 0 1px rgba(0,0,0,0);"+i(["animation-name:"+o+"-spin","animation-duration:1s","animation-timing-function:linear","animation-iteration-count:infinite"],s)+"}"+i(["keyframes "+o+"-spin{from{"+i(["transform:rotate(0deg)"],s)+"}to{"+i(["transform:rotate(360deg)"],s)+"}}"],s,"@","")+"."+a+"-legacy{width:100%;height:50%;padding-top:25%;text-align:center;white-space:nowrap;*zoom:1}."+a+"-legacy:after,."+a+'-legacy:before{content:" ";display:table}.'+a+"-legacy:after{clear:both}."+a+"-legacy div{width:18%;height:100%;margin:0 1%;background-color:#17f29b;float:left;visibility:hidden}."+a+"-1 div."+a+"-1,."+a+"-2 div."+a+"-1,."+a+"-2 div."+a+"-2,."+a+"-3 div."+a+"-1,."+a+"-3 div."+a+"-2,."+a+"-3 div."+a+"-3{visibility:visible}",g=function(){var t=Math.min(300,.9*(this.isBody?Math.min(this.jqWin.width(),this.jqWin.height()):Math.min(this.jqTarget.innerWidth(),this.jqTarget.innerHeight())));this.jqProgress.width(t).height(t),this.showProgress||this.jqProgress.children("."+a).css("borderTopWidth",Math.max(3,t/30))},c=function(t){var r=this;r.timer&&clearTimeout(r.timer),r.progressCnt&&r.jqProgress.removeClass(a+"-"+r.progressCnt),r.isShown&&(r.progressCnt=!t&&r.progressCnt<3?r.progressCnt+1:0,r.progressCnt&&r.jqProgress.addClass(a+"-"+r.progressCnt),r.timer=setTimeout(function(){r.showProgress()},500))};return function(i){var s,n;return"boolean"!=typeof e&&(e=function(){function t(t,r){return!!~(""+t).indexOf(r)}function i(i){var e;for(e in i)if(!t(i[e],"-")&&a[i[e]]!==r)return!0;return!1}function e(t){var r=t.charAt(0).toUpperCase()+t.slice(1),e=(t+" "+g.join(r+" ")+r).split(" ");return i(e)}var o,s,n=document.createElement("modernizr"),a=n.style,d="Webkit Moz O ms",g=d.split(" "),c={},h={}.hasOwnProperty,l=h!==r&&h.call!==r?function(t,r){return h.call(t,r)}:function(t,i){return i in t&&t.constructor.prototype[i]===r};c.borderradius=function(){return e("borderRadius")},c.cssanimations=function(){return e("animationName")},c.csstransforms=function(){return!!e("transform")},o=!1;for(s in c)if(l(c,s)&&!c[s]()){o=!0;break}return a.cssText="",n=null,o}()),i.elmDoc.getElementById(o)||(i.elmDoc.createStyleSheet?(n=i.elmDoc.createStyleSheet(),n.owningElement.id=o,n.cssText=d):(n=(i.elmDoc.getElementsByTagName("head")[0]||i.elmDoc.documentElement).appendChild(i.elmDoc.createElement("style")),n.type="text/css",n.id=o,n.textContent=d)),e?(s=t('
'),i.showProgress=c):s=t('
'),i.adjustProgress=g,s}}();i.prototype.show=function(){var i,e,o,s,n,a=this;a.reset(!0),i=a.jqTarget.get(0).style,a.orgPosition=i.position,e=a.jqTarget.css("position"),"relative"!==e&&"absolute"!==e&&"fixed"!==e&&a.jqTarget.css("position","relative"),a.orgOverflow=i.overflow,o=a.jqTarget.prop("clientWidth"),s=a.jqTarget.prop("clientHeight"),a.jqTarget.css("overflow","hidden"),o-=a.jqTarget.prop("clientWidth"),s-=a.jqTarget.prop("clientHeight"),a.addMarginR=a.addMarginB=0,0>o&&(a.addMarginR=-o),0>s&&(a.addMarginB=-s),a.isBody?(a.addMarginR&&(a.orgMarginR=i.marginRight,a.jqTarget.css("marginRight","+="+a.addMarginR)),a.addMarginB&&(a.orgMarginB=i.marginBottom,a.jqTarget.css("marginBottom","+="+a.addMarginB))):(a.addMarginR&&(a.orgMarginR=i.paddingRight,a.orgWidth=i.width),a.addMarginB&&(a.orgMarginB=i.paddingBottom,a.orgHeight=i.height)),a.jqActive=r,n=t(a.elmDoc.activeElement),a.isBody&&!a.isFrame?a.jqActive=n.blur():a.jqTarget.has(n.get(0)).length&&n.blur(),a.jqTarget.focusin(a.avoidFocus),function(t){a.scrLeft=t.scrollLeft(),a.scrTop=t.scrollTop(),t.scroll(a.avoidScroll)}(a.isBody?a.jqWin:a.jqTarget),a.jqWin.resize(a.callAdjust),a.callAdjust(),a.isShown=!0,a.jqOverlay.stop().fadeTo(a.duration,a.opacity,function(){a.jqTargetOrg.trigger(d)}),a.jqProgress&&(a.showProgress&&a.showProgress(!0),a.jqProgress.fadeIn(a.duration))},i.prototype.hide=function(){var t=this;t.isShown&&(t.jqOverlay.stop().fadeOut(t.duration,function(){t.reset(),t.jqTargetOrg.trigger(g)}),t.jqProgress&&t.jqProgress.fadeOut(t.duration))},i.prototype.adjust=function(){var t,r;this.isBody?(t=this.jqWin.width(),r=this.jqWin.height(),this.jqOverlay.width(t).height(r),this.jqProgress&&this.jqProgress.css({left:(t-this.jqProgress.outerWidth())/2,top:(r-this.jqProgress.outerHeight())/2})):(this.addMarginR&&(t=this.jqTarget.css({paddingRight:this.orgMarginR,width:this.orgWidth}).width(),this.jqTarget.css("paddingRight","+="+this.addMarginR).width(t-this.addMarginR)),this.addMarginB&&(r=this.jqTarget.css({paddingBottom:this.orgMarginB,height:this.orgHeight}).height(),this.jqTarget.css("paddingBottom","+="+this.addMarginB).height(r-this.addMarginB)),t=Math.max(this.jqTarget.prop("scrollWidth"),this.jqTarget.innerWidth()),r=Math.max(this.jqTarget.prop("scrollHeight"),this.jqTarget.innerHeight()),this.jqOverlay.width(t).height(r),this.jqProgress&&(t=this.jqTarget.innerWidth(),r=this.jqTarget.innerHeight(),this.jqProgress.css({left:(t-this.jqProgress.outerWidth())/2+this.scrLeft,top:(r-this.jqProgress.outerHeight())/2+this.scrTop})))},i.prototype.reset=function(t){var r=this;t&&(r.jqOverlay.css("display","none"),r.jqProgress&&r.jqProgress.css("display","none")),r.isShown&&(r.jqTarget.css({position:r.orgPosition,overflow:r.orgOverflow}),r.isBody?(r.addMarginR&&r.jqTarget.css("marginRight",r.orgMarginR),r.addMarginB&&r.jqTarget.css("marginBottom",r.orgMarginB)):(r.addMarginR&&r.jqTarget.css({paddingRight:r.orgMarginR,width:r.orgWidth}),r.addMarginB&&r.jqTarget.css({paddingBottom:r.orgMarginB,height:r.orgHeight})),r.jqTarget.off("focusin",r.avoidFocus),r.jqActive&&r.jqActive.length&&r.jqActive.focus(),function(t){t.off("scroll",r.avoidScroll).scrollLeft(r.scrLeft).scrollTop(r.scrTop)}(r.isBody?r.jqWin:r.jqTarget),r.jqWin.off("resize",r.callAdjust),r.isShown=!1)},t.fn[n]=function(t,r){return"show"===t?o(this,r):"hide"===t?s(this):e(this,t)}}(jQuery); \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/scripts/pluginmanager.js b/conlite/plugins/pluginmanager/scripts/pluginmanager.js new file mode 100644 index 0000000..40a4b0c --- /dev/null +++ b/conlite/plugins/pluginmanager/scripts/pluginmanager.js @@ -0,0 +1,275 @@ + +// Ignore console on platforms where it is not available +if (typeof (window.console) == "undefined") { + console = {}; + console.log = console.warn = console.error = function (a) { + }; +} + +function showMessage(Msg, Level) { + var msgClass = ""; + switch (Level) { + case 'info': + msgClass = "info"; + break; + case 'warning': + msgClass = "warning"; + break; + case 'error': + msgClass = "error"; + break; + case 'success': + msgClass = "success"; + break; + default: + msgClass = "info"; + } + if ($("#pimmsg").length == 0) { + $("body").append('
' + Msg + '
'); + } else { + $("#pimmsg").attr('class', msgClass).html(Msg); + } + + $("#pimmsg").plainModal( + 'open', + { + duration: 500, + offset: { + left: 20, + top: 10 + }, + overlay: { + fillColor: '#fff', + opacity: 0.5 + } + }); + + setTimeout(function () { + $("#pimmsg").plainModal('close'); + }, 3000); +} + +function togglePluginInfo(tableId) { + var collapseButton = 'images/close_all.gif'; + var expandButton = 'images/open_all.gif'; + var curDiv = document.getElementById(tableId); + var curButton = document.getElementById(tableId + '_img'); + + if (curDiv.style.display == "table-row" || curDiv.style.display == "" || curDiv.style.display == "block") { + curDiv.style.display = "none"; + curButton.src = expandButton; + } else if (curDiv.style.display == "none") { + if (ie == 7) { + $('#' + tableId).css('display', 'block'); + } else { + curDiv.style.display = "table-row"; + } + curButton.src = collapseButton; + } +} + +// Read a page's GET URL variables and return them as an associative array. +function getUrlVars() { + var vars = [], hash; + var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&'); + for (var i = 0; i < hashes.length; i++) { + hash = hashes[i].split('='); + if (hash[0] == "contenido" || hash[0] == "plugin_action" || hash[0] == "plugin_id" || hash[0] == "delete_sql") { + continue; + } + vars.push(hash[0]); + vars[hash[0]] = hash[1]; + } + console.log(vars); + return vars; +} + + +$(function () { + + if ($("ul#pim_messages").length) { + var tmp = $("ul#pim_messages li").html(); + var message = tmp.split(':'); + showMessage(message[1], message[0]); + $("ul#pim_messages").remove(); + } + + $(document) + .ajaxStart(function () { + $('body').plainOverlay('show'); + }) + .ajaxStop(function () { + $('body').plainOverlay('hide'); + }); + + // add custom event before start to ui sortable + var oldMouseStart = $.ui.sortable.prototype._mouseStart; + $.ui.sortable.prototype._mouseStart = function (event, overrideHandle, noActivation) { + this._trigger("CustomBeforeStart", event, this._uiHash()); + oldMouseStart.apply(this, [event, overrideHandle, noActivation]); + }; + + $("#pimPluginsExtracted").sortable({ + connectWith: "#pimPluginsInstalled", + cursor: "move", + opacity: 0.5, + placeholder: "ui-state-highlight", + forceHelperSize: true, + forcePlaceholderSize: true, + CustomBeforeStart: function (event, ui) { + console.log(ui.item); + if (ui.item.find("div.pimInfo").is(":visible")) { + togglePluginInfo(ui.item.attr('id').replace("_", "-")); + } + } + }); + $("#pimPluginsInstalled").sortable({ + placeholder: "ui-state-highlight", + axis: "y", + containment: "parent", + forcePlaceholderSize: true, + CustomBeforeStart: function (event, ui) { + console.log(ui.item); + if (ui.item.find("div.pimInfo").is(":visible")) { + togglePluginInfo(ui.item.attr('id').replace("_", "-")); + } + }, + update: function (event, ui) { + console.log({plugins: $("#pimPluginsInstalled").sortable("serialize")}); + $.post("ajaxmain.php", { + plugins: $("#pimPluginsInstalled").sortable("serialize"), + ajax: 'plugin_request', + plugin: 'pluginmanager', + plugin_ajax_action: 'pim_save_sort', + contenido: cSessionId + }); + }, + receive: function (event, ui) { + console.log(ui.item); + $.ajax({ + type: "POST", + url: "ajaxmain.php", + data: { + plugin_folder: ui.item.data('plugin-foldername'), + new_position: ui.item.index(), + ajax: 'plugin_request', + plugin: 'pluginmanager', + plugin_ajax_action: 'pim_install', + contenido: cSessionId + }, + beforeSend: function (xhr, obj) { + //alert("Before"); + }, + success: function (data, textStatus, xhr) { + console.log(data); + var answer = data.split(":"); + console.log(answer); + if (answer[0] == "Ok") { + $.ajax({ + type: "POST", + url: "ajaxmain.php", + data: { + ajax: 'plugin_request', + plugin: 'pluginmanager', + plugin_ajax_action: 'pim_get_info_installed', + plugin_id: answer[1], + contenido: cSessionId + } + }).done(function (data) { + console.log(data); + $(ui.item).replaceWith(data); + }); + showMessage(answer[2], 'info'); + } else if (answer[0] == "Error") { + console.log("Remove New List Item."); + ui.sender.sortable("cancel"); // send back entry to sender :) + showMessage(answer[2], 'error'); + } else { + //window.location.replace("index.php"); // redirect to index if answer not correct or not set + } + $("span#plugin_count").html($("#pimPluginsInstalled").children().length); + }, + error: function (xhr, textStatus, errorThrown) { + showMessage(textStatus, 'error'); + console.log('a' + textStatus); + } + }); + return true; + } + }); + $("#pimPluginsInstalled li, #pimPluginsExtracted li").disableSelection(); + // actions for buttons in plugin info + var labelID; + $('label.pimButLabel').click(function (e) { + if ($(e.target).is('input')) { + return; + } + labelID = $(this).attr('for'); + $('#' + labelID).trigger('click'); + }); + $("ul#pimPluginsInstalled").on("click", "input.pimImgBut", function () { + var pluginID = $(this).attr('id'); + var thisInput = $(this); + switch ($(this).attr('name')) { + case "toggle_active": + $('body').plainOverlay('show'); + $.ajax({ + type: "POST", + url: "ajaxmain.php", + data: { + ajax: 'plugin_request', + plugin: 'pluginmanager', + plugin_ajax_action: 'toggle_active', + plugin_id: pluginID.split('-')[3], + contenido: cSessionId + }, + success: function (data, textStatus, xhr) { + console.log(data); + var aData = data.split(":"); + console.log(aData); + if (aData[0] == "Ok") { + if (aData[1] == '1') { + thisInput.attr('src', 'images/online.gif'); + } else { + thisInput.attr('src', 'images/offline.gif'); + } + $("label[for=" + pluginID + "]").html(aData[2]); + } + } + }); + break; + case "uninstall_plugin": + $('body').plainOverlay('show'); + var hiddenFields = []; + hiddenFields.push("plugin_id"); + hiddenFields["plugin_id"] = pluginID.split('-')[3]; + hiddenFields.push("plugin_action"); + hiddenFields["plugin_action"] = "uninstall_plugin"; + /* + hiddenFields.push("contenido"); + hiddenFields["contenido"] = cSessionId; */ + hiddenFields.push("delete_sql"); + console.log($(this).parent().children("label:eq(1)").children("input")); + if ($(this).parent().children("label:eq(1)").children("input").prop('checked') == true) { + hiddenFields["delete_sql"] = "delete"; + } else { + hiddenFields["delete_sql"] = "hold"; + } + $("#pim_uninstall").remove(); + var form = document.createElement('form'); + form.id = "pim_uninstall"; + form.method = 'post'; + $.each(hiddenFields, function (index, name) { + var input = document.createElement('input'); + input.type = 'hidden'; + input.name = name; + input.value = hiddenFields[name]; + form.appendChild(input); + }); + form.action = window.location.protocol + '//' + window.location.hostname + window.location.pathname + window.location.search; + document.body.appendChild(form); + form.submit(); + break; + } + }); +}); \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/temp/conlite_sample_plugin.zip b/conlite/plugins/pluginmanager/temp/conlite_sample_plugin.zip new file mode 100644 index 0000000000000000000000000000000000000000..e1c9d41198ccea1a468b81e4b6da4e3a75df35b5 GIT binary patch literal 1784 zcmWIWW@h1H0D*%g*WJJjD8b1f!;qPmoKu>TTC5)$!pXp_8Qd9a0>q^i+zgB?FPIq^ zSWuSMKgjOXhcqaa$-&@!~jR2W)Q|{z!fwDxPS(fq~;dnB$mJ&;Sbag!Z^*i zfo28^(2R=Q9Eb}dfI2}Kr|!2v-ILj_8!-Ys2Esz%P$(`*%*lx_$SFQkA$ruAhJUAkSj2z==U|92_6zv}&Yl{|HRk zb8g0k{}cYPNODY=qC87u+hqfJbGg^H3CWJd9bqQ=mJ8Igxjb%P?Y|*n!oHro-W z=!KdgoheH%U4InhHrr`YrJ|h4%!VzAYcrx&=PE9KAGG&Hhq~IojGW}0gOe8B@t&4@ zb))tA36Tzd&&14@_qezEzL=`^P+a}kuILZq5#hHabEj(A3qDHd-zT;w(81(W+dTt? zRH^;&$U`{--{BI!`}4rm=jz?&MIJ%=Sa8nG)}u z=rKO>b6?dnS)W6hp{DSv+WiJ?CrfH;o!4JZG}?8@?{eM#%;ZbIH=bGdS}%>>1kqp*HkW;-8NUgwD+pas#EKd)AM;AMis8yPr8+nA`b& z)8%KMuOCcgw>jc`IAi+vR@X19I}5ZnS+xFmedzM>^HL{`veNwcPA}+?PLAT~Xp8ka zY=22&AA7G;{_nnnr&UAvF8|UmdDFGa=gm&j^$jKS_0B$aTOH7SeC8HIm(RyT_w1^! zsfu7ZB6Y`R`G&{dR=>Cr<-%vQOeF%$2c>zLd04Ym7cfhKFj|fPW-CS}Ic8jCl>{)u zfq($RUq=v)RC2LGN-i|>kd4DEw~&o<1{w!1#ejxkEx{0O!z{y)4Ql}!hFqcn&BI!n zA7R0N7`WYAiCJr;t literal 0 HcmV?d00001 diff --git a/conlite/plugins/pluginmanager/temp/linkchecker.zip b/conlite/plugins/pluginmanager/temp/linkchecker.zip new file mode 100644 index 0000000000000000000000000000000000000000..d84bc1620f78298762b58a872563477a4e4b0438 GIT binary patch literal 21026 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 z +
+
+ + + + {PLUGIN_NAME} {VERSION} +
+ +
+ \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/templates/pi_manager_install.html b/conlite/plugins/pluginmanager/templates/pi_manager_install.html new file mode 100644 index 0000000..f122870 --- /dev/null +++ b/conlite/plugins/pluginmanager/templates/pi_manager_install.html @@ -0,0 +1,11 @@ + + + + + Untitled Document + + + + + diff --git a/conlite/plugins/pluginmanager/templates/pi_manager_installed_plugins.html b/conlite/plugins/pluginmanager/templates/pi_manager_installed_plugins.html new file mode 100644 index 0000000..55a0ac9 --- /dev/null +++ b/conlite/plugins/pluginmanager/templates/pi_manager_installed_plugins.html @@ -0,0 +1,94 @@ +
  • +
    +
    + + + + {NAME} {VERSION} + +
    + + +
    +
  • \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/templates/pi_manager_overview.html b/conlite/plugins/pluginmanager/templates/pi_manager_overview.html new file mode 100644 index 0000000..50043d9 --- /dev/null +++ b/conlite/plugins/pluginmanager/templates/pi_manager_overview.html @@ -0,0 +1,31 @@ + + +

    ConLite Plugin Manager

    +

    {LANG_EXTRACTED}

    +

    {LANG_HINT_EXTRACTED}

    +
      {PLUGINS_EXTRACTED}
    +
     
    +
    +

    {LANG_INSTALLED} ({INSTALLED_PLUGINS})

    + +
      {PLUGINS}
    +
    \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/templates/pi_manager_plugins.html b/conlite/plugins/pluginmanager/templates/pi_manager_plugins.html new file mode 100644 index 0000000..ca5a1f7 --- /dev/null +++ b/conlite/plugins/pluginmanager/templates/pi_manager_plugins.html @@ -0,0 +1,12 @@ + + {NAME} {VERSION} + + + + + +
    + \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/xml/lang_de_DE.xml b/conlite/plugins/pluginmanager/xml/lang_de_DE.xml new file mode 100644 index 0000000..31b2252 --- /dev/null +++ b/conlite/plugins/pluginmanager/xml/lang_de_DE.xml @@ -0,0 +1,10 @@ + + + + + +
    PluginManager
    +
    +
    +
    +
    \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/xml/lang_en_EN.xml b/conlite/plugins/pluginmanager/xml/lang_en_EN.xml new file mode 100644 index 0000000..31b2252 --- /dev/null +++ b/conlite/plugins/pluginmanager/xml/lang_en_EN.xml @@ -0,0 +1,10 @@ + + + + + +
    PluginManager
    +
    +
    +
    +
    \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/xml/lang_en_US.xml b/conlite/plugins/pluginmanager/xml/lang_en_US.xml new file mode 100644 index 0000000..31b2252 --- /dev/null +++ b/conlite/plugins/pluginmanager/xml/lang_en_US.xml @@ -0,0 +1,10 @@ + + + + + +
    PluginManager
    +
    +
    +
    +
    \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/xml/plugin_default.xml b/conlite/plugins/pluginmanager/xml/plugin_default.xml new file mode 100644 index 0000000..67ecf46 --- /dev/null +++ b/conlite/plugins/pluginmanager/xml/plugin_default.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/conlite/plugins/pluginmanager/xml/plugin_info.xml b/conlite/plugins/pluginmanager/xml/plugin_info.xml new file mode 100644 index 0000000..be67288 --- /dev/null +++ b/conlite/plugins/pluginmanager/xml/plugin_info.xml @@ -0,0 +1,44 @@ + + + + + Linkchecker + linkchecker + a9613eabde004e1ad4daad30a25def6555f4ac28 + Checks intern and extern links to valid + Frederic Schneider (4fb) + four for business AG + 2.0.0 + 4.6.0 + + + + b9653ztdbde004e1ad4dbbd90a25def6555f4gh40 + + + + + + linkchecker + lc_whitelist + + + + linkchecker + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/conlite/plugins/pluginmanager/xml/plugin_info.xsd b/conlite/plugins/pluginmanager/xml/plugin_info.xsd new file mode 100644 index 0000000..356b5ac --- /dev/null +++ b/conlite/plugins/pluginmanager/xml/plugin_info.xsd @@ -0,0 +1,179 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/data/cronlog/move_articles.php.job b/data/cronlog/move_articles.php.job new file mode 100644 index 0000000..1c57515 --- /dev/null +++ b/data/cronlog/move_articles.php.job @@ -0,0 +1 @@ +1582131720 \ No newline at end of file diff --git a/data/cronlog/move_old_stats.php.job b/data/cronlog/move_old_stats.php.job new file mode 100644 index 0000000..4954459 --- /dev/null +++ b/data/cronlog/move_old_stats.php.job @@ -0,0 +1 @@ +1580511600 \ No newline at end of file diff --git a/data/cronlog/optimize_database.php.job b/data/cronlog/optimize_database.php.job new file mode 100644 index 0000000..2ed1f0d --- /dev/null +++ b/data/cronlog/optimize_database.php.job @@ -0,0 +1 @@ +1582066800 \ No newline at end of file diff --git a/data/cronlog/send_reminder.php.job b/data/cronlog/send_reminder.php.job new file mode 100644 index 0000000..8d3a7e1 --- /dev/null +++ b/data/cronlog/send_reminder.php.job @@ -0,0 +1 @@ +1582131600 \ No newline at end of file diff --git a/data/cronlog/session_cleanup.php.job b/data/cronlog/session_cleanup.php.job new file mode 100644 index 0000000..8d3a7e1 --- /dev/null +++ b/data/cronlog/session_cleanup.php.job @@ -0,0 +1 @@ +1582131600 \ No newline at end of file diff --git a/data/cronlog/setfrontenduserstate.php.job b/data/cronlog/setfrontenduserstate.php.job new file mode 100644 index 0000000..8d3a7e1 --- /dev/null +++ b/data/cronlog/setfrontenduserstate.php.job @@ -0,0 +1 @@ +1582131600 \ No newline at end of file diff --git a/data/logs/.gitignore b/data/logs/.gitignore new file mode 100644 index 0000000..2323280 --- /dev/null +++ b/data/logs/.gitignore @@ -0,0 +1,5 @@ +# Ignore everything in this directory +* +# Except this file +!.gitignore +!index.php \ No newline at end of file