<?php
/**
  * Ahead WebSoft Technologies Pvt Ltd.
  * @copyright  Copyright 2006-2026 Ahead WebSoft Technologies Pvt Ltd.
  * @license    https://www.socialengine.com/policies
*/
class SdkController extends Zend_Controller_Action
{
    protected $_outputPath;

    protected $_skeletonPath;

    protected $_includeNative = false;

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

        // Set up output path
        $this->_outputPath = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'temporary'
            . DIRECTORY_SEPARATOR . 'package' . DIRECTORY_SEPARATOR . 'sdk';

        if (!is_writable($this->_outputPath)) {
            throw new Engine_Exception(sprintf('Package output path "%s" is not writable. Please create and chmod 777.', $this->_outputPath));
        }

        // Set up skeleton path
        $this->_skeletonPath = APPLICATION_PATH . DIRECTORY_SEPARATOR . 'install'
            . DIRECTORY_SEPARATOR . 'data' . DIRECTORY_SEPARATOR . 'skeletons';
    }

    public function indexAction()
    {
    }

    public function createAction()
    {
        // Require in advance
        require_once 'PEAR.php';
        require_once 'Archive/Tar.php';

        // Form
        $this->view->form = $form = new Install_Form_Sdk_Create();

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

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

        // Process
        $values = $form->getValues();

        if (false == ($structureData = $this->_getStructure($values['type']))) {
            return $form->addError('Invalid type');
        }

        // Build manifest
        $manifestData = array();

        // General
        $manifestData['type'] = $structureData['singular'];
        $manifestData['name'] = $values['name'];
        $manifestData['version'] = $values['version'];
//         $manifestData['sku'] = $values['sku'];
//         if (empty($manifestData['sku'])) {
//           unset($manifestData['sku']);
//         }

        // Path
        if ($structureData['inflect']) {
            $manifestData['path'] = $structureData['path'] . '/' . $this->_inflect($values['name']);
        } else {
            $manifestData['path'] = $structureData['path'] . '/' . $values['name'];
        }

        // Meta
        $manifestData['title'] = $values['title'];
        $manifestData['description'] = $values['description'];
        $manifestData['author'] = $values['author'];

        // Callback
        if (@$structureData['defaultCallback']) {
            $manifestData['callback'] = $structureData['defaultCallback'];
        }

        // Actions
        $manifestData['actions'] = $structureData['defaultActions'];

        // Directories
        if ($structureData['inflect']) {
            $manifestData['directories'] = array(
                $structureData['path'] . '/' . $this->_inflect($values['name'])
            );
        } else {
            $manifestData['directories'] = array(
                $structureData['path'] . '/' . $values['name']
            );
        }


        // Language
        if ($manifestData['type'] == 'module') {
            $manifestData['files'] = array(
                'application/languages/en/' . $values['name'] . '.csv',
            );
        } elseif ($manifestData['type'] == 'language') {
            $manifestData['files'] = array(
                // 'application/languages/' . $values['name'] . '/' . $values['name'] . '.csv',
            );
        } elseif ($manifestData['type'] == 'theme') {
            $manifestData['files'] = array(
              'application/themes/' . $values['name'] . '/theme.css',
              'application/themes/' . $values['name'] . '/constants.css',
            );
        }

        // Now let's build it
        $archiveDirectory = $this->_outputPath . DIRECTORY_SEPARATOR .
            $manifestData['type'] . '-' . $manifestData['name'] . '-' . $manifestData['version'];

        if (file_exists($archiveDirectory)) {
            Engine_Package_Utilities::fsRmdirRecursive($archiveDirectory, true);
        }

        $archiveFilename = $this->_outputPath . DIRECTORY_SEPARATOR .
            $manifestData['type'] . '-' . $manifestData['name'] . '-' . $manifestData['version'] . '.tar';

        if (file_exists($archiveFilename)) {
            if (!@unlink($archiveFilename)) {
                throw new Engine_Exception(sprintf('Unable to remove file: %s', $archiveFilename));
            }
        }

        // Construct archive
        $archive = new Archive_Tar($archiveFilename);

        // Prepare search and replace
        $searchAndReplace = array(
            '%%type%%' => $structureData['singular'],
            '%%typePlural%%' => $values['type'],
            '%%name%%' => $values['name'],
            '%%nameInflected%%' => $this->_inflect($values['name']),
            '%%version%%' => $manifestData['version'],
        );
        $search = array_keys($searchAndReplace);
        $replace = array_values($searchAndReplace);

        // Build skeleton directory
        $path = $manifestData['path'];
        $skeletonPath = $this->_skeletonPath . '/' . $manifestData['type'];
        if (file_exists($skeletonPath)) {
            $it = new RecursiveIteratorIterator(
                new RecursiveDirectoryIterator($skeletonPath),
                RecursiveIteratorIterator::CHILD_FIRST
            );
            foreach ($it as $file) {
                if (!$file->isFile()) {
                    continue;
                }

                $filename = $file->getPathname();

                $targetFilename = ltrim(str_replace($skeletonPath, '', $filename), '/\\');
                $targetFilename = str_replace('.template', '', $targetFilename);
                $targetFilename = str_replace($search, $replace, $targetFilename);

                $targetData = file_get_contents($filename);
                $targetData = str_replace($search, $replace, $targetData);

                $targetPath = $archiveDirectory . '/' . $path . '/' . $targetFilename;

                if (!is_dir(dirname($targetPath))) {
                    if (!mkdir(dirname($targetPath), 0777, true)) {
                        throw new Engine_Exception(sprintf('Unable to create folder: %s', dirname($targetPath)));
                    }
                }

                if (false === file_put_contents($targetPath, $targetData)) {
                    throw new Engine_Exception(sprintf('Unable to put data to file: %s', $targetPath));
                }
            }
        }
        
        $fullManifestData = array(
            'package' => $manifestData,
        );
        if ($manifestData['type'] == 'module') { // add SQL to insert into modules table

            $sql  = 'INSERT IGNORE INTO `engine4_core_modules` (`name`, `title`, `description`, `version`, `enabled`, `type`) VALUES ';
            $sql .= sprintf(" ('%s', '%s', '%s', '%s', 1, 'extra') ",
                $manifestData['name'],
                $manifestData['title'],
                $manifestData['description'],
                $manifestData['version']);
            $targetPath = $archiveDirectory . DIRECTORY_SEPARATOR . $path . DIRECTORY_SEPARATOR . 'settings' . DIRECTORY_SEPARATOR . 'my-install.sql';
            if (false === file_put_contents($targetPath, "$sql;")) {
                throw new Engine_Exception(sprintf('Unable to put SQL data to file: %s', $targetPath));
            }
        }
        if ($manifestData['type'] == 'widget') { //b/c
            $fullManifestData['type'] = @$manifestData['type'];
            $fullManifestData['name'] = @$manifestData['name'];
            $fullManifestData['version'] = @$manifestData['version'];
            $fullManifestData['title'] = @$manifestData['title'];
            $fullManifestData['description'] = @$manifestData['description'];
            $fullManifestData['category'] = 'Widgets';
        }
        $targetPath = $archiveDirectory . '/' . $path . '/' . $structureData['manifest'];
        
        $targetData = '<?php return ' . var_export($fullManifestData, true) . '; ?>';
        if (!is_dir(dirname($targetPath))) {
            if (!mkdir(dirname($targetPath), 0777, true)) {
                throw new Engine_Exception(sprintf('Unable to create folder: %s', dirname($targetPath)));
            }
        }
        if (false === file_put_contents($targetPath, $targetData)) {
            throw new Engine_Exception(sprintf('Unable to put data to file: %s', $targetPath));
        }
        $manifestPath = $targetPath;

        if ($manifestData['type'] == 'language'  || $manifestData['type'] == 'module') {
            $name = $manifestData['type'] == 'language' ? $values['name'] : 'en';
            $targetPath = $archiveDirectory . '/' . 'application/languages/' . $name . '/' . $values['name'] . '.csv';
            $targetData = '';
            if (!is_dir(dirname($targetPath))) {
                if (!mkdir(dirname($targetPath), 0777, true)) {
                    throw new Engine_Exception(sprintf('Unable to create folder: %s', dirname($targetPath)));
                }
            }
            if (false === file_put_contents($targetPath, $targetData)) {
                throw new Engine_Exception(sprintf('Unable to put data to file: %s', $targetPath));
            }
        }

        $manifestDataCopy = array();
        $manifestDataCopy['basePath'] = $archiveDirectory;
        $manifestDataCopy = array_merge($manifestDataCopy, $manifestData);
        $archiveFilename = $this->_buildPackage($manifestDataCopy);

        // Output the archive
        header('content-type: application/x-tar');
        header('content-disposition: attachment; filename=' . urlencode(basename($archiveFilename)));
        echo file_get_contents($archiveFilename);
        @unlink($archiveFilename);
        try {
            Engine_Package_Utilities::fsRmdirRecursive($archiveDirectory, true);
        } catch (Exception $e) {
        }
        exit();
    }

    public function buildAction()
    {
        $availablePackages = array();

        // Iterate over types
        foreach ($this->_getStructure() as $type => $info) {
            // Check if action set to be run
            //if( in_array($type, $actions) )
            //{
            // Get current path and list of dirs
            $path = APPLICATION_PATH . DIRECTORY_SEPARATOR . str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, trim($info['path'], '/\\'));

            $dirs = scandir($path);
            foreach ($dirs as $dir) {
                $dirPath = $path . DIRECTORY_SEPARATOR . $dir;
                if ($dir[0] == '.' || !is_dir($dirPath)) {
                    continue;
                }

                // Get manifest data
                $manifestPath = $dirPath . DIRECTORY_SEPARATOR . str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, trim($info['manifest']));
                $manifestData = include $manifestPath;
                $availablePackages[] = array(
                    'type' => $manifestData['package']['type'],
                    'name' => $manifestData['package']['name'],
                    'guid' => $manifestData['package']['type'] . '-' . $manifestData['package']['name'],
                    'key'  => $manifestData['package']['type'] . '-' . $manifestData['package']['name'] . '-' . $manifestData['package']['version'],
                    'manifestPath' => $manifestPath,
                    'manifest' => $manifestData,
                );
            }
            //} // END IF ACTION
        } // END STRUCTURE

        // Fiter out ours
        $buildPackages = array();
        foreach ($availablePackages as $availablePackage) {
            if (empty($availablePackage['manifest']['package']['author']) ||
                $availablePackage['manifest']['package']['author'] != 'SocialEngine Core' ||
                $this->_includeNative) {
                $buildPackages[] = $availablePackage;
            }
        }
        $this->view->buildPackages = $buildPackages;


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

        // BUILD!
        $selected = $this->_getParam('build');
        if (empty($selected)) {
            $this->view->error = 'No package selected.';
            return;
        }

        // Check if selected exist
        $toBuildPackages = array();
        foreach ($buildPackages as $buildPackage) {
            if (in_array($buildPackage['key'], $selected)) {
                $toBuildPackages[] = $buildPackage;
            }
        }
        if (empty($toBuildPackages)) {
            $this->view->error = 'No valid package selected.';
            return;
        }

        // Build 'em
        foreach ($toBuildPackages as $toBuildPackage) {
            try {
                $this->_buildPackage($toBuildPackage['manifestPath']);
            } catch (Exception $e) {
                $this->view->error = $e->getMessage();
                return;
            }
        }

        $this->view->status = true;

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

    public function manageAction()
    {
        require_once 'PEAR.php';
        require_once 'Archive/Tar.php';

        // Get built packages
        $builtPackages = array();
        $builtPackageFiles = array();
        foreach (scandir($this->_outputPath) as $file) {
            $path = $this->_outputPath . '/' . $file;
            if (!is_file($path)) {
                continue;
            }
            if (substr($file, -4) !== '.tar') {
                continue;
            }

            // Read package.json
            $archive = new Archive_Tar($path);
            $string = $archive->extractInString('package.json');

            if ($string) {
                $package = new Engine_Package_Manifest();
                $parser = Engine_Package_Manifest_Parser::factory('package.json');
                $package->fromArray($parser->fromString($string));
            } else {
                $package = null;
            }

            $builtPackages[] = $package;
            $builtPackageFiles[] = $path;
        }
        $this->view->packages = $builtPackages;
        $this->view->packageFiles = $builtPackageFiles;
    }

    public function downloadAction()
    {
        $file = $this->_getParam('file');
        $path = $this->_outputPath . '/' . $file;
        if (!file_exists($path) || !is_file($path)) {
            echo 'No file';
            exit();
            return;
        }

        // Close output buffering
        while (ob_get_level() > 0) {
            ob_end_clean();
        }

        // Send headers
        header('content-disposition: attachment; filename=' . urlencode($file));
        header('content-length: ' . filesize($path));

        // Open file
        $handle = fopen($this->_outputPath . '/' . $file, 'r');
        while ('' !== ($str = fread($handle, 256))) {
            echo $str;
        }
        exit();
    }

    public function deleteAction()
    {
        $this->view->form = $form = new Install_Form_Confirm(array(
            'action' => $_SERVER['REQUEST_URI'],
            'title' => 'Delete Packages?',
            'description' => 'Are you sure you want to delete these packages?',
            'submitLabel' => 'Delete Packages',
            'cancelHref' => $this->view->url(array('action' => 'manage')),
            'useToken' => true,
        ));

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

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

        $actions = (array) $this->_getParam('actions');
        foreach ($actions as $action) {
            $path = $this->_outputPath . '/' . $action;
            if (file_exists($path) && is_file($path)) {
                @unlink($path);
            }
        }

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

    public function combineAction()
    {
        require_once 'PEAR.php';
        require_once 'Archive/Tar.php';

        $this->view->form = $form = new Install_Form_Sdk_Combine();
        $form->name->setValue('combined_' . time() . '.tar');

        $actions = (array) $this->_getParam('actions');
        foreach ($actions as $index => $action) {
            $path = $this->_outputPath . '/' . $action;
            if (!file_exists($path) || !is_file($path)) {
                unset($actions[$index]);
            }
        }
        if (empty($actions) || !is_array($actions)) {
            return $form->addError('No packages selected.');
        } elseif (count($actions) == 1) {
            return $form->addError('Cannot combine only one package.');
        }

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

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

        $archiveFilename = $form->getValue('name');
        $archiveFilename = preg_replace('/[^a-zA-Z0-9_.-]/', '', $archiveFilename);
        if (strtolower(substr($archiveFilename, -4)) != '.tar') {
            $archiveFilename .= '.tar';
        }

        $archive = new Archive_Tar($this->_outputPath . '/' . $archiveFilename);

        foreach ($actions as $action) {
            $path = $this->_outputPath . '/' . $action;
            $archive->addModify($path, null, $this->_outputPath);
        }

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


    protected function _inflect($string)
    {
        $string = preg_replace('/[^a-z0-9]+/', ' ', $string);
        $string = ucwords($string);
        $string = str_replace(' ', '', $string);
        return $string;
    }

    protected function _deflect($string)
    {
        $string = preg_replace('/([A-Z])/', '-\1', $string);
        $string = strtolower($string);
        $string = trim($string, '- ');
        return $string;
    }

    protected function _buildPackage($manifest, $date = null)
    {
        if (null === $date) {
            $date = time();
        }

        // Get manifest data
        if (is_string($manifest)) {
            $manifestData = require $manifest;
            if (empty($manifestData['package'])) {
                throw new Exception(sprintf('Missing package data for package in path: %s', $manifestPath));
            }
            $manifestData = $manifestData['package'];
        } elseif (is_array($manifest)) {
            $manifestData = $manifest;
        } else {
            throw new Exception('Invalid manifest data type');
        }

        // Override date (for now at least)
        $manifestData['date'] = $date;

        // Build package file
        $package = new Engine_Package_Manifest($manifestData);

        // Build archive
        $archiveFile = Engine_Package_Archive::deflate($package, $this->_outputPath);

        // Verify archive for integrity
        $extractedPath = Engine_Package_Archive::inflate($archiveFile, $this->_outputPath);
        $loaded = new Engine_Package_Manifest($extractedPath);

        // Remove verified archive
        Engine_Package_Utilities::fsRmdirRecursive($extractedPath, true);

        return $archiveFile;
    }

    protected function _getStructure($type = null)
    {
        // Generate structure
        $structure = array(
            'externals' => array(
                'path' => 'externals',
                'manifest' => 'manifest.php',
                'array' => true,
                'singular' => 'external',
                'inflect' => false,
                'defaultActions' => array(
                    'install',
                    'upgrade',
                    'refresh',
                    //'enable',
                    //'disable',
                ),
            ),
            'languages' => array(
                'path' => 'application/languages',
                'manifest' => 'manifest.php',
                'array' => true,
                'singular' => 'language',
                'inflect' => false,
                'defaultActions' => array(
                    'install',
                    'upgrade',
                    'refresh',
                    'remove',
                ),
            ),
            'libraries' => array(
                'path' => 'application/libraries',
                'manifest' => 'manifest.php',
                'array' => true,
                'singular' => 'library',
                'inflect' => true,
                'defaultActions' => array(
                    'install',
                    'upgrade',
                    'refresh',
                    'remove',
                ),
            ),
            'modules' => array(
                'path' => 'application/modules',
                'manifest' => 'settings/manifest.php',
                'array' => true,
                'singular' => 'module',
                'inflect' => true,
                'language' => true,
                'defaultActions' => array(
                    'install',
                    'upgrade',
                    'refresh',
                    'enable',
                    'disable',
                ),
                'defaultCallback' => array(
                    'class' => 'Engine_Package_Installer_Module',
                ),
            ),
            'plugins' => array(
                'path' => 'application/plugins',
                'manifest' => 'manifest.php',
                'array' => true,
                'singular' => 'plugin',
                'inflect' => false,
                'defaultActions' => array(
                    'install',
                    'upgrade',
                    'refresh',
                    'remove',
                ),
            ),
            'themes' => array(
                'path' => 'application/themes',
                'manifest' => 'manifest.php',
                'array' => true,
                'singular' => 'theme',
                'inflect' => false,
                'defaultActions' => array(
                    'install',
                    'upgrade',
                    'refresh',
                    'remove',
                ),
                'defaultCallback' => array(
                    'class' => 'Engine_Package_Installer_Theme',
                ),
            ),
            'widgets' => array(
                'path' => 'application/widgets',
                'manifest' => 'manifest.php',
                'array' => true,
                'singular' => 'widget',
                'inflect' => false,
                'defaultActions' => array(
                    'install',
                    'upgrade',
                    'refresh',
                    'remove',
                ),
            ),
        );

        if (func_num_args() <= 0) {
            return $structure;
        } elseif (isset($structure[$type])) {
            return $structure[$type];
        } else {
            return false;
        }
    }

    protected function _sys_get_temp_dir()
    {
        if (!function_exists('sys_get_temp_dir')) {
            function sys_get_temp_dir()
            {
                if (false != ($temp = getenv('TMP'))) {
                    return $temp;
                }
                if (false != ($temp = getenv('TEMP'))) {
                    return $temp;
                }
                if (false != ($temp = getenv('TMPDIR'))) {
                    return $temp;
                }
                $temp = tempnam(__FILE__, '');
                if (file_exists($temp)) {
                    unlink($temp);
                    return dirname($temp);
                }
                return null;
            }
        }

        return sys_get_temp_dir();
    }
}
