View file install/controllers/ManageController.php

File size: 53.58Kb
<?php
/**
 * SocialEngine
 *
 * @category   Application_Core
 * @package    Install
 * @copyright  Copyright 2006-2020 Webligo Developments
 * @license    http://www.socialengine.com/license/
 * @version    $Id: ManageController.php 9747 2012-07-26 02:08:08Z john $
 * @author     John
 */

/**
 * @category   Application_Core
 * @package    Install
 * @copyright  Copyright 2006-2020 Webligo Developments
 * @license    http://www.socialengine.com/license/
 */
class ManageController extends Zend_Controller_Action
{
    /**
     * @var Engine_Package_Manager
     */
    protected $_packageManager;

    /**
     * @var Zend_Session_Namespace
     */
    protected $_session;

    /**
     * @var Zend_Cache_Core
     */
    protected $_cache;

    protected $_settings;

    protected $_vfsSettings;

    public $contexts = array(
        'upload' => array(
            'json',
        ),
        'extract' => array(
            'json',
        ),
        'select-delete' => array(
            'json',
        ),
    );

    public function init()
    {
        // Check if already logged in
        if (!Zend_Registry::get('Zend_Auth')->getIdentity()) {
            return $this->_helper->redirector->gotoRoute(array(), 'default', true);
        }

        // Get manager
        $this->_packageManager = Zend_Registry::get('Engine_Package_Manager');

        // Check if related folders are writeable
        $this->_packageManager->checkTemporaryPaths();

        if (!is_writeable(APPLICATION_PATH . '/application/packages')) {
            throw new Engine_Exception('application/packages folder is not writeable; Please CHMOD this directory to 777 and refresh this page.');
        }

        // Get cache
        $this->_cache = Zend_Registry::get('Cache');
        $this->_packageManager->setCache($this->_cache);

        // Create session namespace
        $this->_session = new Zend_Session_Namespace('InstallManageController');

        // Set db and vfs if available
        if (Zend_Registry::isRegistered('Zend_Db') && Zend_Registry::get('Zend_Db') instanceof Zend_Db_Adapter_Abstract) {
            $this->_packageManager->setDb(Zend_Registry::get('Zend_Db'));
        }
        if (isset($this->_session->vfsInstance) && $this->_session->vfsInstance instanceof Engine_Vfs_Adapter_Abstract) {
            $this->_packageManager->setVfs($this->_session->vfsInstance);
        }

        // Get settings
        $config = array();
        $settingsFile = APPLICATION_PATH . '/install/config/general.php';
        if (file_exists($settingsFile)) {
            $config = include $settingsFile;
        }
        $this->view->settings = $this->_settings = array_merge(array(
            'force' => '0',
            'verbose' => '0',
            'automated' => '0',
        ), $config);

        // Get vfs config
        $vfsConfig = array();
        $vfsFile = APPLICATION_PATH . '/install/config/vfs.php';
        if (file_exists($vfsFile)) {
            $vfsConfig = include $vfsFile;
        }
        $this->_vfsSettings = $vfsConfig;

        // Add manage socialengine title
        $this->view->headTitle()->prepend('Manage SocialEngine');

        // Set time limit
        @set_time_limit(600);
    }

    public function indexAction()
    {
        /**
         * @var Zend_Db_Adapter_Abstract $db
         */
        $db = Zend_Registry::get('Zend_Db');
        
        $generalSiteTitle = $db->select()->from('engine4_core_settings')
                    ->where('name = ? ', 'core.general.site.url');
        $generalSiteTitle = $db->fetchRow($generalSiteTitle);
        if(!empty($generalSiteTitle['value'])) {
          $this->view->siteURL = $generalSiteTitle['value'];
        } else { 
          $this->view->siteURL = _ENGINE_SITE_URL;
        }

        // Check for updates
        try {
            $repo = $this->_packageManager->getRepository('socialengine.com');
            if ($repo) {
                $remoteVersions = $repo->queryList();
            }
        } catch (Exception $e) {
            // Silence
            $remoteVersions = array();
        }
        //$remoteVersions['core-base']['version'] = '4.0.0';
        $this->view->remoteVersions = $remoteVersions;

        /*
        $parts = explode('?', $_SERVER['REQUEST_URI']);
        $currentPage = (isset($_SERVER['HTTPS']) ? 'https' : 'http') . '://';
        $currentPage .= $_SERVER['HTTP_HOST'];
        $currentPage .= $parts[0];

        $settings = array();
        $rows = $db->select()->from('engine4_core_settings')
            ->where('name = ? ', 'core.client.token')
            ->orWhere('name = ? ', 'core.client.guid')
            ->query()->fetchAll();
        foreach ($rows as $row) {
            $settings[$row['name']] = $row['value'];
        }

        $warehouse = Engine_Settings::get('warehouse');
        $websiteUrl = $warehouse['website_url'];
        $websiteUrl .= '/login?return=' . urlencode($currentPage);

        if ($this->getParam('product')) {
            if (empty($settings['core.client.guid'])) {
                exit('<a href="' . $websiteUrl . '">Login</a>');
            }
            $account = new Engine_Warehouse('/accounts/' . $settings['core.client.guid']);
            $account->setToken($settings['core.client.token']);
            print_r($account->get(array('include' => 'products'))); exit;
        }

        if ($this->getParam('token')) {
            $api = new Engine_Warehouse('/php-site/auth');
            $response = $api->get(array(
                'token' => $this->getParam('token')
            ));

            if (empty($response['token'])) {
                exit('auth expired');
            }

            $db->delete('engine4_core_settings', 'name = \'core.client.token\'');
            $db->delete('engine4_core_settings', 'name = \'core.client.guid\'');
            $db->insert('engine4_core_settings', array(
                'name' => 'core.client.token',
                'value' => $response['token']
            ));
            $db->insert('engine4_core_settings', array(
                'name' => 'core.client.guid',
                'value' => $response['guid']
            ));

            header('Location: ' . $currentPage);
            exit;
        }

        $this->view->websiteUrl = $websiteUrl;
        $this->view->licenseError = false;
        $this->view->licenseKeyUrl = $this->view->htmlLink('/admin/index/license-key/', 'here');
        */

        // Try to get a list of installed modules and themes from the database
        $dbState = array();
        if ($db instanceof Zend_Db_Adapter_Abstract) {
            /*
            try {
                $row = $db->select()->from('engine4_core_settings')
                    ->where('name = ?', 'core.license.key')
                    ->query()->fetch();
                if (empty($row['value'])) {
                    throw new Exception('Missing license key.');
                }

                $warehouse = new Engine_Warehouse('/php-site/license');
                $response = $warehouse->get(array(
                    'key' => $row['value']
                ));
                if ($response === false) {
                    throw new \Exception('License key is not valid.');
                }
            } catch (Exception $e) {
                $this->view->licenseError = $e->getMessage();
            }
            */

            try {
                $table = new Zend_Db_Table(array(
                    'adapter' => $db,
                    'name' => 'engine4_core_modules',
                ));
                foreach ($table->fetchAll() as $row) {
                    $dbState['module'][$row->name] = $row->toArray();
                }
            } catch (Exception $e) {
            }
            try {
                $table = new Zend_Db_Table(array(
                    'adapter' => $db,
                    'name' => 'engine4_core_themes',
                ));
                foreach ($table->fetchAll() as $row) {
                    $dbState['theme'][$row->name] = $row->toArray();
                }
            } catch (Exception $e) {
            }
        }
        $this->view->dbState = $dbState;



        // List installed packages
        $upgradeablePackages = array();
        $pendingPackages = array();
        $installedPackages = array();
        foreach ($this->_packageManager->listInstalledPackages(array('caching' => false)) as $installedPackage) {

            // Get database state
            $databaseInfo = array();
            if (isset($dbState[$installedPackage->getType()][$installedPackage->getName()])) {
                $databaseInfo = $dbState[$installedPackage->getType()][$installedPackage->getName()];
            }

            // Get remote version info
            $remoteInfo = array();
            if (isset($remoteVersions[$installedPackage->getGuid()])) {
                $remoteInfo = $remoteVersions[$installedPackage->getGuid()];
            }

            //
            if ($installedPackage->getVersion() == '4.0.0beta3' && !empty($databaseInfo['version']) && $databaseInfo['version'] == '4.0.0') {
                $this->_forward('beta-update');
                return;
            }

            // Init
            $upgradeable = false;
            $pending = false;
            $navigation = array();

            // Navigation

            // Disable
            if (isset($databaseInfo['enabled']) && $databaseInfo['enabled'] && $installedPackage->hasAction('disable')) {
                $navigation[] = array(
                    'label' => 'disable',
                    'href' => $this->view->url(array('action' => 'disable')) . '?package=' . $installedPackage->getKey(),
                    'class' => "package_disable",
                );
            }

            // Enable
            if (isset($databaseInfo['enabled']) && !$databaseInfo['enabled'] && $installedPackage->hasAction('enable')) {
                $navigation[] = array(
                    'label' => 'enable',
                    'href' => $this->view->url(array('action' => 'enable')) . '?package=' . $installedPackage->getKey(),
                    'class' => "package_enable",
                );
            }

            // Install
            if ($installedPackage->getType() == 'module' && empty($databaseInfo['version'])) {
                $pending = true;
                $navigation[] = array(
                    'label' => 'install',
                    'href' => $this->view->url(array('action' => 'install')) . '?package=' . $installedPackage->getKey(),
                    'class' => "package_install",
                );
            }

            // Update/downgrade/refresh
            if ($installedPackage->getType() == 'module' && !empty($databaseInfo['version'])) {
                switch (version_compare($databaseInfo['version'], $installedPackage->getVersion())) {
                    case 1:
                        break;
                    case 0:
                        break;
                    case -1:
                        $pending = true;
                        $navigation[] = array(
                            'label' => 'upgrade',
                            //'href' => $this->view->url(array('action' => 'prepare')) . '?packages[]=' . $installedPackage->getKey(),
                            'href' => $this->view->url(array('action' => 'install')) . '?package=' . $installedPackage->getKey(),
                            'class' => "package_upgrade",
                        );
                        break;
                }
            }

            // Remove
            if ($installedPackage->hasAction('remove')) {
                $navigation[] = array(
                    'label' => 'delete',
                    'href' => $this->view->url(array('action' => 'prepare')) . '?packages[]=' . $installedPackage->getKey() . '&actions[]=remove',
                    'class' => "package_delete",
                );
            }

            // Add get upgrade
            if (!empty($remoteInfo) && version_compare($remoteInfo['version'], $installedPackage->getVersion(), '>')) {
                $upgradeable = true;
                $navigation[] = array(
                    'label' => 'get update (' . $remoteInfo['version'] . ')',
                    'href' => 'http://www.socialengine.com/client',
                );
            }

            $installedPackageInfo = array(
                'package' => $installedPackage,
                'database' => $databaseInfo,
                'remote' => $remoteInfo,
                'navigation' => $navigation,
                'upgradeable' => $upgradeable,
                'pending' => $pending,
            );

            if ($upgradeable) {
                $upgradeablePackages[$installedPackage->getMeta()->getTitle()] = $installedPackageInfo;
            } elseif ($pending) {
                $pendingPackages[] = $installedPackageInfo;
            } else {
                $installedPackages[$installedPackage->getMeta()->getTitle()] = $installedPackageInfo;
            }
        }
        $this->view->installedPackages = (array_merge($upgradeablePackages, $pendingPackages, $installedPackages));
        ksort($this->view->installedPackages);
    }

    public function selectAction()
    {

        $this->view->installNavigation = $this->getInstallNavigation('select');

        // Get extracted packages
        $this->view->extractedPackages = $extractedPackages = $this->_packageManager->listExtractedPackages();

        // Get available packages
        $toExtractPackages = array();
        $archiveDir = $this->_packageManager->getTemporaryPath(Engine_Package_Manager::PATH_ARCHIVES);
        $extractDir = $this->_packageManager->getTemporaryPath(Engine_Package_Manager::PATH_PACKAGES);
        foreach (scandir($archiveDir) as $file) {
            if (strtolower(substr($file, -4, 4)) != '.tar') {
                continue;
            }
            $partFile = substr($file, 0, -4);
            if (!is_dir($extractDir . DIRECTORY_SEPARATOR . $partFile)) {
                $toExtractPackages[] = $file;
            }
        }
        $this->view->toExtractPackages = $toExtractPackages;
    }

    public function selectDeleteAction()
    {
        $this->_helper->contextSwitch->initContext();

        $package = $this->_getParam('package');

        // Setup
        $archiveDir = $this->_packageManager->getTemporaryPath(Engine_Package_Manager::PATH_ARCHIVES);
        $extractDir = $this->_packageManager->getTemporaryPath(Engine_Package_Manager::PATH_PACKAGES);

        if (!is_string($package)) {
            $this->view->error = 'Not a string';
            return;
        }

        if (strpos($package, '/') !== false || strpos($package, '\\') !== false) {
            $this->view->error = 'No directory traversal!';
            return;
        }

        $packageArchive = $archiveDir . DIRECTORY_SEPARATOR . $package . '.tar';
        $packageDirectory = $extractDir . DIRECTORY_SEPARATOR . $package;

        if (file_exists($packageArchive) && is_file($packageArchive)) {
            if (!@unlink($packageArchive)) {
                $this->view->error = 'Could not remove archive file';
                return;
            }
        }

        if (file_exists($packageDirectory) && is_dir($packageDirectory)) {
            try {
                Engine_Package_Utilities::fsRmdirRecursive($packageDirectory, true);
            } catch (Exception $e) {
                $this->view->error = 'Could not remove extracted archive directory';
                return;
            }
        }

        $this->view->status = true;
    }

    public function extractAction()
    {
        $this->_helper->contextSwitch->initContext();

        $package = $this->_getParam('package');

        // Setup
        try {
            $archiveDir = $this->_packageManager->getTemporaryPath(Engine_Package_Manager::PATH_ARCHIVES);
            $extractDir = $this->_packageManager->getTemporaryPath(Engine_Package_Manager::PATH_PACKAGES);
        } catch (Exception $e) {
            $this->view->error = $e->getMessage();
            return;
        }

        // Check if archive is a tar file
        $targetFile = $archiveDir . DIRECTORY_SEPARATOR . $package;
        if (strtolower(substr($targetFile, -4, 4)) != '.tar') {
            $this->view->error = 'Package is not a TAR archive.';
            return;
        }

        // Check if archive exists
        if (!file_exists($targetFile)) {
            $this->view->error = 'Package does not exist.';
            return;
        }

        // Try to deflate archive?
        $extractFiles = array($targetFile);
        $packagesInfo = array();
        @set_time_limit(300);

        $toRemove = array();

        try {
            while (count($extractFiles) > 0) {
                $current = array_shift($extractFiles);
                $hadPackage = false;
                $hadArchive = false;

                // Try to extract
                $outputPath = Engine_Package_Archive::inflate($current, $extractDir);

                // Check for tar files or package files
                foreach (scandir($outputPath) as $child) {
                    // Package file
                    if (strtolower($child) == 'package.json') {
                        $packageFile = new Engine_Package_Manifest($outputPath . DIRECTORY_SEPARATOR . $child);
                        $packagesInfo[] = array(
                            'key' => $packageFile->getKey(),
                            'data' => $packageFile->toArray(),
                            'html' => $this->view->packageSelect($packageFile),
                        );
                        $hadPackage = true;
                    } elseif (strtolower(substr($child, -4)) === '.tar') {
                        $extractFiles[] = $outputPath . DIRECTORY_SEPARATOR . $child;
                        $hadArchive = true;
                    }
                }

                // Add to remove after extraction
                $toRemove[] = $current;
                if (!$hadPackage) {
                    $toRemove[] = $outputPath;
                }
            }
        } catch (Exception $e) {
            $this->view->error = $e->getMessage();
            return;
        }

        if (empty($packagesInfo)) {
            $this->view->error = 'No packages found in archive';
            return;
        }

        // Remove to remove
        foreach ($toRemove as $removeFile) {
            if (is_dir($removeFile)) {
                try {
                    Engine_Package_Utilities::fsRmdirRecursive($removeFile, true);
                } catch (Exception $e) {
                }
            } elseif (is_file($removeFile)) {
                @unlink($removeFile);
            }
        }

        $this->view->status = 1;
        $this->view->packagesInfo = $packagesInfo;
    }

    public function uploadAction()
    {
        $this->view->installNavigation = $this->getInstallNavigation('select');

        $this->_helper->contextSwitch->initContext();

        // Check method
        if (!$this->getRequest()->isPost()) {
            return;
        }

        // Prepare
        $info = $_FILES['Filedata'];

        try {
            $archiveDir = $this->_packageManager->getTemporaryPath(Engine_Package_Manager::PATH_ARCHIVES);
            $extractDir = $this->_packageManager->getTemporaryPath(Engine_Package_Manager::PATH_PACKAGES);
        } catch (Exception $e) {
            $this->view->error = $e->getMessage();
            return;
        }

        $targetFile = $archiveDir . '/' . $info['name'];

        // Check extension
        if (strtolower(substr($info['name'], -4, 4)) != '.tar') {
            $this->view->error = 'The file uploaded was not a TAR archive.';
            return;
        }

        // Check if already exists
        if (file_exists($targetFile) && !@unlink($targetFile)) {
            $this->view->error = 'This file has already been uploaded, and the previous file could not be removed. Please try removing it manually in temporary/package/archives';
            return;
        }

        // Check if already extracted
        $outputPath = $extractDir . DIRECTORY_SEPARATOR . substr($info['name'], 0, -4);
        if (file_exists($outputPath) && is_dir($outputPath)) {
            try {
                Engine_Package_Utilities::fsRmdirRecursive($outputPath, true);
            } catch (Exception $e) {
                $this->view->error = 'Extract path already exists and could not be removed. Please try removing it manually in temporary/package/packages';
                return;
            }
        }

        // Try to move uploaded file
        if (!move_uploaded_file($info['tmp_name'], $targetFile)) {
            $this->view->error = 'Unable to move file to packages directory. Please set chmod 0777 on the temporary/package/archives directory.';
            return;
        }

        $this->view->status = 1;
        $this->view->file = $info['name'];
    }

    /* INSTALL PROCESS -------------------------------------------------------- */

    public function prepareAction()
    {
        // Skip
        $skip = $this->_getParam('skip');
        if (null !== $skip) {
            $this->_session->skipDiffErrorFiles = (bool) $skip;
            return $this->_helper->redirector->gotoRoute(array('action' => 'vfs'));
        }

        // Get navigation
        $this->view->installNavigation = $this->getInstallNavigation('prepare');

        // Clean cache
        $this->_packageManager->getCache()->clean();

        $this->view->warehouseErrors = array();
        $packages = $this->_packageManager->listExtractedPackages();
        $httpHost = $_SERVER['HTTP_HOST'];
        if (preg_match('/^(.*?)\:([0-9]+)$/i', $httpHost, $matches)) {
            $httpHost = $matches[1];
        }
//         if ($packages && !preg_match('/^([a-z]+)\.socialenginedev\.com$/i', $httpHost)) {
//             foreach ($packages as $package) {
//                 if (!method_exists($package, 'getSku')) {
//                     continue;
//                 }
//                 $sku = $package->getSku();
// 
//                 if (!$sku) {
//                     $manifestFile = $package->getBasePath() . '/' . $package->getPath() . '/settings/manifest.php';
//                     if (file_exists($manifestFile)) {
//                         $manifest = require($manifestFile);
//                         if (isset($manifest['package']) && isset($manifest['package']['sku'])) {
//                             $sku = $manifest['package']['sku'];
//                         }
//                     }
//                 }
//             }
//         }

        // Check for modifications to installer (to prevent problems)
        $this->_checkForModifications(true);

        // Get db
        if (Zend_Registry::isRegistered('Zend_Db')
            && ($db = Zend_Registry::get('Zend_Db')) instanceof Zend_Db_Adapter_Abstract) {
            Engine_Sanity::setDefaultDbAdapter($db);
        }

        // Get packages
        $this->view->transaction = $transaction =
            $this->_packageManager->decide((array) $this->_getParam('packages'), $this->_getParam('actions'));

        // Whoops, didn't select anything?
        if (!$transaction) {
            $this->view->selectError = true;
            return;
        }

        // Get dependencies
        $this->view->dependencies = $dependencies =
            $transaction->getDependencies();

        $dependencyError = false;
        foreach ($dependencies as $dependency) {
            $dependencyError |= $dependency->hasErrors();
        }
        $this->view->dependencyError = $dependencyError;

        // Get sanity
        $this->view->tests = $tests = $transaction->getTests();
        $this->view->testsMaxErrorLevel = $testsMaxErrorLevel = $tests->getMaxErrorLevel();
        $this->view->testsError = $testsError = ($testsMaxErrorLevel >= 4);

        // Get file operations
        $this->view->fileOperations = $fileOperations = $transaction->getFileOperations();

        $diffError = false;
        foreach ($fileOperations as $fileOperation) {
            $diffError |= $fileOperation['error'];
        }
        $this->view->diffError = $diffError;

        // Check if we can install
        $this->view->prepareError = $prepareError = ($dependencyError || $testsError || $diffError);
        $this->view->diffErrorOnly = $diffErrorOnly = (!$dependencyError && !$testsError && $diffError);


        // Store the transaction in the cache
        $this->_saveTransaction($transaction);
    }

    public function vfsAction()
    {
        // Check for modifications to installer (to prevent problems)
        if (!$this->_checkForModifications()) {
            return;
        }

        // Load the transaction from the cache
        $transaction = $this->_loadTransaction();


        // Get navigation
        $this->view->installNavigation = $this->getInstallNavigation('vfs');

        $adapterType = $this->_getParam('adapter');
        if (null === $adapterType) {
            $adapterType = $this->_session->vfsAdapter;
            if (null === $adapterType) {
                $adapterType = @$this->_vfsSettings['adapter'];
            }
        }
        $previousAdapterType = $this->_getParam('previousAdapter');

        $this->view->form = $form = new Install_Form_VfsInfo(array(
            'adapterType' => $adapterType,
        ));

        if (!$this->getRequest()->isPost() || $adapterType != $previousAdapterType) {
            if (!$adapterType) {
                // Ignore
            } elseif ($adapterType == @$this->_session->vfsAdapter) {
                // Load from session
                $form->populate(array(
                    'adapter' => $adapterType,
                    'config'  => $this->_session->vfsConfig
                ));
            } elseif ($adapterType == @$this->_vfsSettings['adapter']) {
                // Load from settings file
                $form->populate($this->_vfsSettings);
            } else {
                $form->populate(array(
                    'adapter' => $adapterType,
                ));
            }
            return;
        }

        if (!$form->isValid($this->getRequest()->getPost())) {
            return;
        }

        // Process
        $values = $form->getValues();
        $vfsAdapter = $values['adapter'];
        $vfsConfig = $values['config'];

        // Try to load adapter
        try {
            $vfs = Engine_Vfs::factory($vfsAdapter, $vfsConfig);
        } catch (Exception $e) {
            $form->addError('Connection error: ' . $e->getMessage());
            return;
        }

        // Try to connect (getResource will force connect)
        try {
            $vfs->getResource();
        } catch (Exception $e) {
            $form->addError('Connection error: ' . $e->getMessage());
            return;
        }

        // Search for target
        $path = null;
        if (!empty($vfsConfig['search']) && $vfsConfig['search'] !== '0') {
            $path = $vfs->findJailedPath($vfsConfig['path'], APPLICATION_PATH . '/install/ftp_search_will_look_for_this_file');
            if (!$path) {
                $form->addError('Your installation could not be found. Please double check your connection info and starting path. Your starting path needs to be your SocialEngine path, or a parent directory of it.');
                return;
            }
            $path = dirname(dirname($path));
        } else {
            $path = $vfsConfig['path'];
        }

        // Verify path
        if (!$vfs->exists($path . '/install/ftp_search_will_look_for_this_file')) {
            $form->addError('Specified path is not a SocialEngine install directory.');
            return;
        }

        // Save config
        $vfsConfig['path'] = $values['config']['path'] = $path;

        $vfs->changeDirectory($path);
        $this->_session->vfsInstance = $vfs;

        $this->_session->vfsAdapter = $vfsAdapter;
        $this->_session->vfsConfig = $vfsConfig;

        $this->_vfsSettings = $values;

        // Save for later
        $vfsSettingsData = array(
            'adapter' => $vfsAdapter,
            'config' => array_diff_key($vfsConfig, array(
                'password' => null,
                'location' => null,
                'search' => null,
            )),
        );
        @file_put_contents(APPLICATION_PATH . '/install/config/vfs.php', '<?php return ' . var_export($vfsSettingsData, true) . '?>');

        // Redirect to next step
        return $this->_helper->redirector->gotoRoute(array('action' => 'perms'));
    }

    public function permsAction()
    {
        // Check for modifications to installer (to prevent problems)
        if (!$this->_checkForModifications()) {
            return;
        }

        // Load the transaction from the cache
        $transaction = $this->_loadTransaction();


        // Get navigation
        $this->view->installNavigation = $this->getInstallNavigation('perms');

        // Build file todo-list
        $files = $transaction->getFileOperations($this->_session->skipDiffErrorFiles);

        // Get the operation count
        $operationCount = 0;
        foreach ($files as $packageKey => $packageSummary) {
            $operationCount += count($packageSummary['operations']);
        }

        $this->view->files = $files;
        $this->view->operationCount = $operationCount;

        // Get vfs object
        //$this->view->vfsPath = $this->_session->vfsConfig['path'];
        $vfs = $this->_session->vfsInstance;

        // Check permissions
        $statCache = array();
        $permResults = array();
        foreach ((array) $files as $packageKey => $packageSummary) {
            $packageFiles = $packageSummary['operations'];
            foreach ((array) $packageFiles as $packageFile => $code) {
                // Check stat cache
                $tmpParts = explode('/', $packageFile);
                array_pop($tmpParts);
                do {
                    $tmpPath = join('/', $tmpParts);
                    if (isset($statCache[$tmpPath])) {
                        //var_dump(sprintf('Found %d in cache for %s', $statCache[$tmpPath], $tmpPath));
                        $permResults[$packageKey][$tmpPath] = $statCache[$tmpPath];
                        continue 2;
                    }
                    //var_dump(sprintf('Nothing Found in cache for %s', $tmpPath));
                    array_pop($tmpParts);
                } while (count($tmpParts) > 0);

                // Actually check now
                $tmpParts = explode('/', $packageFile);
                $tmpCache = null;
                $first = true;
                do {
                    $tmpPath = join('/', $tmpParts);
                    try {
                        $tmpInfo = $vfs->info($tmpPath);
                        if (!$tmpInfo->exists()) {
                            if ($first) {
                                $first = false;
                            } else {
                                $tmpCache = $tmpPath;
                            }
                            //var_dump(sprintf('Nothing Found on fs for %s', $tmpPath));
                        } else {
                            $isWritable = $tmpInfo->isWritable();
                            $permResults[$packageKey][$tmpPath] = $isWritable;
                            //var_dump(sprintf('Found %d for %s', $isWritable, $tmpPath));
                            if ($tmpCache) {
                                $statCache[$tmpCache] = $isWritable;
                            }
                            continue 2;
                        }
                    } catch (Exception $e) {
                        // Silence
                    }
                    array_pop($tmpParts);
                } while (count($tmpParts) > 0);

                // Check the root path
                $rootPath = $vfs->path();
                if (isset($tmpCache[$rootPath])) {
                    $permResults[$packageKey][$tmpPath] = $tmpCache[$rootPath];
                } else {
                    $tmpInfo = $vfs->info($rootPath);
                    $isWritable = $tmpInfo->isWritable();
                    $tmpCache[$rootPath] = $isWritable;
                    $permResults[$packageKey][$tmpPath] = $isWritable;
                }

                // Couldn't find anything?
                //throw new Engine_Exception('could not find parent path');
            }
        }

        $this->view->permResults = $permResults;

        // Build summary
        $notWritableCount = 0;
        $permSummary = array();
        foreach ((array) $permResults as $packageKey => $packageFiles) {
            $permSummary[$packageKey]['writable'] = 0;
            $permSummary[$packageKey]['not-writable'] = 0;
            foreach ($packageFiles as $packageFile => $isWritable) {
                $permSummary[$packageKey][$isWritable ? 'writable' : 'not-writable']++;
                if (!$isWritable) {
                    $notWritableCount++;
                }
            }
        }
        $this->view->permSummary = $permSummary;
        $this->view->notWritableCount = $notWritableCount;
    }

    public function placeAction()
    {
        // Check for modifications to installer (to prevent problems)
        if (!$this->_checkForModifications()) {
            return;
        }

        // Load the transaction from the cache
        $transaction = $this->_loadTransaction();


        // Get navigation
        $this->view->installNavigation = $this->getInstallNavigation('place');

        // Get vfs object
        $vfs = $this->_session->vfsInstance;

        $this->view->placeError = false;

        // Place files
        $actionSummary = array();

        foreach ($transaction as $operation) {
            $packageKey = $operation->getKey();
            $batchSummary = $operation->getFileOperations($this->_session->skipDiffErrorFiles);

            foreach ($batchSummary['operations'] as $diffSummary) {
                // Copy files
                $source = $diffSummary['rightPath'];
                $dest = $diffSummary['relPath']; // $diffSummary['leftPath'];
                //$dest = ltrim(str_replace(APPLICATION_PATH, '', $dest), '/\\');

                // What to do, what to do
                $code = $diffSummary['key'];
                try {
                    switch ($code) {
                        // Ignore
                        case 'identical':
                        case 'ignore':
                            break;

                        case 'add':
                        case 'added':
                            $vfs->makeDirectory(dirname($dest), true);
                            $vfs->put($dest, $source);
                            $actionSummary[$packageKey][$dest] = 'added';
                            break;

                        case 'different':
                        case 'different_different':
                          if($this->_session->skipDiffErrorFiles > 0)
                          break;

                        case 'added_added':
                        case 'replace':
                            $vfs->put($dest, $source);
                            $actionSummary[$packageKey][$dest] = 'replaced';
                            break;

                        case 'different_removed':
                        case 'remove':
                        case 'removed':
                            $vfs->unlink($dest);
                            $actionSummary[$packageKey][$dest] = 'removed';
                            break;

                        default:
                            throw new Exception($code);
                            break;
                    }
                } catch (Exception $e) {
                    // something bad happened -_-
                    $this->view->placeError = true;
                    $actionSummary[$packageKey][$dest] = 'failed (' . $e->getMessage() . ')';
                }
            }
        }
        $this->view->actionSummary = $actionSummary;


        // Also apply permissions
        $permSummary = array();
        foreach ($transaction as $operation) {
            $package = $operation->getTargetPackage();
            if (!method_exists($package, 'getPermissions')) {
                continue;
            }
            $permissions = $package->getPermissions();
            if (!is_array($permissions) || empty($permissions)) {
                continue;
            }
            foreach ($permissions as $permission) {
                try {
                    $info = $vfs->info($permission->getPath());
                    if (!$info->exists()) {
                        $permSummary[] = $permission->getPath() . ' - Does not exist';
                    } elseif ($info->isFile()) {
                        $vfs->mode($permission->getPath(), $permission->getMode());
                    } elseif ($info->isDirectory()) {
                        if ($permission->getInclusive()) {
                            $vfs->mode($permission->getPath(), $permission->getMode(), $permission->getRecursive());
                        } else {
                            foreach ($info->getChildren() as $child) {
                                $vfs->mode($child->getPath(), $permission->getMode(), $permission->getRecursive());
                            }
                        }
                    } else {
                        $permSummary[] = $permission->getPath() . ' - Unknown file type or missing';
                    }
                } catch (Exception $e) {
                    $permSummary[] = $permission->getPath() . ' - Error - ' . $e->getMessage();
                }
            }
        }
        $this->view->permSummary = $permSummary;
    }

    public function queryAction()
    {


       

        // Check for modifications to installer (to prevent problems)
        if (!$this->_checkForModifications()) {
            return;
        }

        // Load the transaction from the cache
        $transaction = $this->_loadTransaction();


        // Get navigation
        $this->view->installNavigation = $this->getInstallNavigation('query');

        $db = Zend_Registry::get('Zend_Db');
        $vfs = $this->_session->vfsInstance;
        $queryError = false;
        $results = array();

        // Run all them delicious database queries

        // Run pre install
        $packageTitles = array();
        foreach ($this->_packageManager->callback($transaction, 'preinstall') as $result) {
            if (!empty($result['errors'])) {
                $queryError = true;
            }
            // Add to results
            $key = $result['key'];
            $packageTitles[$key] = $result['title'] . ' (' . $result['version'] . ')';
            $results[$key][] = $result;
        }
        if ($queryError) {
            $this->view->queryError = true;
            $this->view->results = $results;
            $this->view->packageTitles = $packageTitles;
            return;
        }

        // Run install
        foreach ($this->_packageManager->callback($transaction, 'install') as $result) {
            if (!empty($result['errors'])) {
                $queryError = true;
            }
            // Add to results
            $key = $result['key'];
            $packageTitles[$key] = $result['title'] . ' (' . $result['version'] . ')';
            $results[$key][] = $result;
        }
        if ($queryError) {
            $this->view->queryError = true;
            $this->view->results = $results;
            $this->view->packageTitles = $packageTitles;
            return;
        }

        // Run post install
        foreach ($this->_packageManager->callback($transaction, 'postinstall') as $result) {
            if (!empty($result['errors'])) {
                $queryError = true;
            }
            // Add to results
            $key = $result['key'];
            $packageTitles[$key] = $result['title'] . ' (' . $result['version'] . ')';
            $results[$key][] = $result;
        }
        if ($queryError) {
            $this->view->queryError = true;
            $this->view->results = $results;
            $this->view->packageTitles = $packageTitles;
            return;
        }
        $this->view->results = $results;
        $this->view->packageTitles = $packageTitles;
    }

    public function completeAction()
    {
        // Check for modifications to installer (to prevent problems)
        if (!$this->_checkForModifications()) {
            return;
        }

        // Load the transaction from the cache
        $transaction = $this->_loadTransaction();


        $this->view->installNavigation = $this->getInstallNavigation('complete');

        $vfs = $this->_session->vfsInstance;

        // Let's do some clean-up
        try {
            $this->_packageManager->cleanup($transaction);
        } catch (Exception $e) {
            $path = APPLICATION_PATH . '/temporary/package/packages';
            if (is_dir($path)) {
                rename($path, $path . '-remove-' . time());
            }
            mkdir($path, 0777);
        }

        // Try to flush the scaffold cache
        try {
            Engine_Package_Utilities::fsRmdirRecursive(APPLICATION_PATH . '/temporary/scaffold', false, array('index.html'));
        } catch (Exception $e) {

        }
        
        // Try to increment the site counter
        // try {
            
            // $db->update('engine4_core_settings', array(
                // 'value' => new Zend_Db_Expr('value + 1'),
            // ), array(
                // 'name = ?' => 'core.site.counter',
            // ));
        // } catch (Exception $e) {
           // Silence
        // }

        $this->_session->unsetAll();
    }

    public function enableAction()
    {
        $this->view->form = $form = new Install_Form_Confirm(array(
            'title' => 'Enable Package?',
            'description' => 'Are you sure you want to enable this package?',
            'submitLabel' => 'Enable Package',
            'cancelHref' => $this->view->url(array('action' => 'index')),
            'useToken' => true,
        ));

        if (!$this->getRequest()->isPost()) {
            return;
        }

        if (!$form->isValid($this->getRequest()->getPost())) {
            return;
        }

        // Do the enable
        $packageName = $this->_getParam('package');
        $package = null;
        foreach ($this->_packageManager->listInstalledPackages() as $installedPackage) {
            if ($installedPackage->getKey() == $packageName) {
                $package = $installedPackage;
            }
        }

        // Enable/disable
        if ($package->hasAction('enable')) {
            $operation = new Engine_Package_Manager_Operation_Enable($this->_packageManager, $package);
            $ret = $this->_packageManager->execute($operation, 'enable');

            // Try to flush the scaffold cache
            try {
                Engine_Package_Utilities::fsRmdirRecursive(APPLICATION_PATH . '/temporary/scaffold', false, array('index.html'));
            } catch (Exception $e) {
            }

            // Try to increment the site counter
            try {
                $db = Zend_Registry::get('Zend_Db');
                $db->update('engine4_core_settings', array(
                    'value' => new Zend_Db_Expr('value + 1'),
                ), array(
                    'name = ?' => 'core.site.counter',
                ));
            } catch (Exception $e) {
                // Silence
            }
        }

        return $this->_helper->redirector->gotoRoute(array('action' => 'index'));
    }

    public function disableAction()
    {
        $this->view->form = $form = new Install_Form_Confirm(array(
            'title' => 'Disable Package?',
            'description' => 'Are you sure you want to disable this package?',
            'submitLabel' => 'Disable Package',
            'cancelHref' => $this->view->url(array('action' => 'index')),
            'useToken' => true,
        ));

        if (!$this->getRequest()->isPost()) {
            return;
        }

        if (!$form->isValid($this->getRequest()->getPost())) {
            return;
        }

        // Do the disable
        $packageName = $this->_getParam('package');
        $package = null;
        foreach ($this->_packageManager->listInstalledPackages() as $installedPackage) {
            if ($installedPackage->getKey() == $packageName) {
                $package = $installedPackage;
            }
        }

        // Enable/disable
        if ($package->hasAction('disable')) {
            $operation = new Engine_Package_Manager_Operation_Disable($this->_packageManager, $package);
            $ret = $this->_packageManager->execute($operation, 'disable');
        }

        return $this->_helper->redirector->gotoRoute(array('action' => 'index'));
    }

    public function installAction()
    {
        $this->view->form = $form = new Install_Form_Confirm(array(
            'title' => 'Install Package?',
            'description' => 'Are you sure you want to install this package?',
            'submitLabel' => 'Install Package',
            'cancelHref' => $this->view->url(array('action' => 'index')),
            'useToken' => true,
        ));

        if (!$this->getRequest()->isPost()) {
            return;
        }

        if (!$form->isValid($this->getRequest()->getPost())) {
            return;
        }

        // Do the disable
        $packageName = $this->_getParam('package');
        $package = null;
        foreach ($this->_packageManager->listInstalledPackages() as $installedPackage) {
            if ($installedPackage->getKey() == $packageName) {
                $package = $installedPackage;
            }
        }

        // Enable/disable
        $operation = new Engine_Package_Manager_Operation_Install($this->_packageManager, $package);

        $errors = array();
        $queryError = false;

        // Run preinstall
        $result = $this->_packageManager->execute($operation, 'preinstall');
        if (!empty($result['errors'])) {
            $queryError = true;
            $errors = array_merge($errors, $result['errors']);
        }
        if (isset($queryError) && $queryError) {
            $this->view->queryError = true;
            $this->view->errors = $errors;
            return;
        }

        // Run install
        $result = $this->_packageManager->execute($operation, 'install');
        if (!empty($result['errors'])) {
            $queryError = true;
            $errors = array_merge($errors, $result['errors']);
        }
        if (isset($queryError) && $queryError) {
            $this->view->queryError = true;
            $this->view->errors = $errors;
            return;
        }

        // Run postinstall
        $result = $this->_packageManager->execute($operation, 'postinstall');
        if (!empty($result['errors'])) {
            $queryError = true;
            $errors = array_merge($errors, $result['errors']);
        }
        if (isset($queryError) && $queryError) {
            $this->view->queryError = true;
            $this->view->errors = $errors;
            return;
        }


        // Redirect if no error
        if (!isset($queryError) || !$queryError) {
            return $this->_helper->redirector->gotoRoute(array('action' => 'index'));
        }
    }






    // This is used to make the beta3 appear correctly

    public function betaUpdateAction()
    {
        if (!$this->getRequest()->isPost()) {
            return;
        }

        $db = Zend_Registry::get('Zend_Db');
        //$db = new Zend_Db_Adapter_Abstract();

        // Get all module info
        $select = new Zend_Db_Select($db);
        $select
            ->from('engine4_core_modules')
        ;

        $modules = array();
        foreach ($select->query()->fetchAll() as $row) {
            $modules[$row['name']] = $row;
        }

        $errors = array();
        $installedPackages = $this->_packageManager->listInstalledPackages();
        foreach ($installedPackages as $installedPackage) {
            if ($installedPackage->getType() !== 'module') {
                continue;
            }

            // Remove modules that have not been installed
            if (!isset($modules[$installedPackage->getName()])) {
                $packageFile = $installedPackage->getSourcePath();
                if (!@unlink($packageFile)) {
                    $errors[] = sprintf('Unable to remove package file "%s" for not installed module "%s"', $packageFile, $modules[$installedPackage->getName()]['title']);
                }
            }

            // Remove modules from content system that have not been installed
            if (!isset($modules[$installedPackage->getName()])) {
                $db->delete('engine4_core_content', array(
                    'name LIKE ?' => $installedPackage->getName().'.%',
                ));
            }
        }
        $this->view->errors = $errors;

        // Update database
        try {
            $db->query('ALTER TABLE `engine4_core_modules` CHANGE `version` `version` varchar(32) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL');
        } catch (Exception $e) {
        }

        $db->update('engine4_core_modules', array(
            'version' => '4.0.0beta3',
        ), array(
            'version = ?' => '4.0.0',
        ));

        // Delete pages for Groups and Events if not installed
        if (!array_key_exists('group', $modules)) {
            $page = new Zend_Db_Select($db);
            $page->from('engine4_core_pages')
                ->where('name = ?', 'group_profile_index');
            $page = $db->fetchRow($page);
            if ($page) {
                $page_id = $page['page_id'];
                $db->delete('engine4_core_pages', array(
                    'page_id = ?' => $page_id
                ));
                $db->delete('engine4_core_content', array(
                    'page_id = ?' => $page_id,
                ));
            }
        }
        if (!array_key_exists('event', $modules)) {
            $page = new Zend_Db_Select($db);
            $page->from('engine4_core_pages')
                ->where('name = ?', 'event_profile_index');
            $page = $db->fetchRow($page);
            if ($page) {
                $page_id = $page['page_id'];
                $db->delete('engine4_core_pages', array(
                    'page_id = ?' => $page_id
                ));
                $db->delete('engine4_core_content', array(
                    'page_id = ?' => $page_id
                ));
            }
        }

        $this->view->status = true;
        $this->view->modules = $modules;
    }



    // Utility

    public function getInstallNavigation($active = 0)
    {
        return new Zend_Navigation(array(
            array(
                'label' => 'Choose Packages',
                'uri' => 'javascript:void(0);',
                'active' => ($active == 'select'),
            ),
            array(
                'label' => 'Run Pre-install Check',
                'uri' => 'javascript:void(0);',
                'active' => ($active == 'prepare'),
            ),
            array(
                'label' => 'Enter FTP Info',
                'uri' => 'javascript:void(0);',
                'active' => ($active == 'vfs'),
            ),
            array(
                'label' => 'Run Permissions Check',
                'uri' => 'javascript:void(0);',
                'active' => ($active == 'perms'),
            ),
            array(
                'label' => 'Copy Files',
                'uri' => 'javascript:void(0);',
                'active' => ($active == 'place'),
            ),
            array(
                'label' => 'Update Database',
                'uri' => 'javascript:void(0);',
                'active' => ($active == 'query'),
            ),
            array(
                'label' => 'Complete!',
                'uri' => 'javascript:void(0);',
                'active' => ($active == 'complete'),
            ),
        ));
    }

    /**
     * @param Engine_Package_Manager_Transaction $transaction
     * @return void
     */
    protected function _saveTransaction(Engine_Package_Manager_Transaction $transaction)
    {
        $id = $transaction->getId();
        $this->_session->transactionIdentity = $id;
        $this->_cache->save($transaction, 'transaction' . $id);
    }

    /**
     * @return Engine_Package_Manager_Transaction
     */
    protected function _loadTransaction()
    {
        $id = $this->_session->transactionIdentity;
        if (!$id) {
            throw new Engine_Exception('No transaction id is stored in the session.');
        }

        $transaction = $this->_cache->load('transaction' . $id);
        if (!$transaction || !($transaction instanceof Engine_Package_Manager_Transaction)) {
            throw new Engine_Exception('No transaction was found in the cache.');
        }

        $transaction->setManager($this->_packageManager);

        return $transaction;
    }

    protected function _checkForModifications($initialize = false)
    {
        return true; // Just disable it for now

        $currentSignature = $this->_session->currentInstallerSignature;

        $installedPackages = $this->_packageManager->listInstalledPackages();
        $currentInstallerPackage = $installedPackages->offsetGet('core-install');
        if (!$currentInstallerPackage || strlen($currentSignature) == 40) {
            $signature = sha1(file_get_contents(__FILE__));
        } else {
            $signature = $currentInstallerPackage->getVersion();
        }

        // Initialize
        if (true === $initialize) {
            $this->_session->currentInstallerSignature = $signature;
            return true;
        }

        // Was updated
        elseif ($signature != $this->_session->currentInstallerSignature) {
            $extractedPackages = $this->_packageManager->listExtractedPackages();
            $this->view->extractedPackageKeys = $extractedPackages->getArrayKeys();
            $this->view->extractedPackageKeys = array_diff($this->view->extractedPackageKeys, array(
                $extractedPackages->getKeyByGuid('core-install'),
                $extractedPackages->getKeyByGuid('library-engine'),
            ));
            $this->_session->unsetAll();
            $this->_helper->viewRenderer->renderScript('_installerUpdated.tpl');
            return false;
        }

        // Was not updated
        return true;
    }
}