<?php
/**
 * @version		$Id: extension.php 97 2009-06-21 19:18:07Z happynoodleboy $
 * @package		JCE
 * @copyright	Copyright (C) 2009 Ryan Demmer. All rights reserved.
 * @copyright	Copyright (C) 2005 - 2007 Open Source Matters. All rights reserved.
 * @license		GNU/GPL
 * This version may have been modified pursuant
 * to the GNU General Public License, and as distributed it includes or
 * is derivative of works licensed under the GNU General Public License or
 * other free or open source software licenses.
 */

// Check to ensure this file is within the rest of the framework
defined('JPATH_BASE') or die ();

require_once (JPATH_ADMINISTRATOR.DS.'components'.DS.'com_jce'.DS.'plugins'.DS.'extension.php');

/**
 * Extension installer
 *
 * @package		JCE
 * @subpackage	Installer
 * @since		1.5
 */
class JCEInstallerExtension extends JObject
{
    /**
     * Constructor
     *
     * @access	protected
     * @param	object	$parent	Parent object [JInstaller instance]
     * @return	void
     * @since	1.5
     */
    function __construct( & $parent)
    {
        $this->parent = & $parent;
    }

    /**
     * Custom install method
     *
     * @access	public
     * @return	boolean	True on success
     * @since	1.5
     */
    function install()
    {
        // Get a database connector object
        $db = & $this->parent->getDBO();

        // Get the extension manifest object
        $manifest = & $this->parent->getManifest();
        $this->manifest = & $manifest->document;

        /**
         * ---------------------------------------------------------------------------------------------
         * Manifest Document Setup Section
         * ---------------------------------------------------------------------------------------------
         */

        // Set the component name
        $name = & $this->manifest->getElementByPath('name');
        $this->set('name', $name->data());

        // Get the component description
        $description = & $this->manifest->getElementByPath('description');
        if (is_a($description, 'JSimpleXMLElement')) {
            $this->parent->set('message', $description->data());
        }
        else {
            $this->parent->set('message', '');
        }

        $element = & $this->manifest->getElementByPath('files');

        $extension = $this->manifest->attributes('extension');
        $plugin = $this->manifest->attributes('plugin');
        $folder = $this->manifest->attributes('folder');

        if (! empty($plugin)) {
            $this->parent->setPath('extension_root', JPATH_PLUGINS.DS.'editors'.DS.'jce'.DS.'tiny_mce'.DS.'plugins'.DS.$plugin.DS.'extensions'.DS.$folder);
        }
        else {
            $this->parent->abort('Extension Install: '.JText::_('No JCE Plugin file specified'));
            return false;
        }

        /**
         * ---------------------------------------------------------------------------------------------
         * Filesystem Processing Section
         * ---------------------------------------------------------------------------------------------
         */

        // Set overwrite flag if not set by Manifest
        $this->parent->setOverwrite(true);

        // If the extension directory does not exist, lets create it
        $created = false;
        if (!file_exists($this->parent->getPath('extension_root'))) {
            if (!$created = JFolder::create($this->parent->getPath('extension_root'))) {
                $this->parent->abort('Extension Install: '.JText::_('Failed to create directory').': "'.$this->parent->getPath('extension_root').'"');
                return false;
            }
        }

        /*
         * If we created the extension directory and will want to remove it if we
         * have to roll back the installation, lets add it to the installation
         * step stack
         */
        if ($created) {
            $this->parent->pushStep( array ('type'=>'folder', 'path'=>$this->parent->getPath('extension_root')));
        }

        // Copy all necessary files
        if ($this->parent->parseFiles($element, -1) === false) {
            // Install failed, roll back changes
            $this->parent->abort();
            return false;
        }

        // Parse optional tags -- language files for plugins
        $this->parent->parseLanguages($this->manifest->getElementByPath('languages'), 0);

        /**
         * ---------------------------------------------------------------------------------------------
         * Database Processing Section
         * ---------------------------------------------------------------------------------------------
         */

        // Check to see if an extension by the same name is already installed
        $query = 'SELECT id'
        .' FROM #__jce_extensions'
        .' WHERE extension = '.$db->Quote($extension)
        ;
        $db->setQuery($query);


        $row = & JTable::getInstance('extension', 'JCETable');

        if (!$db->Query()) {
            // Install failed, roll back changes
            $this->parent->abort('Extension Install: '.$db->stderr(true));
            return false;
        }
        $id = $db->loadResult();

        // Was there an extension already installed with the same name?
        if ($id) {

            if (!$this->parent->getOverwrite())
            {
                // Install failed, roll back changes
                $this->parent->abort('Extension Install: '.JText::_('Extension').' "'.$ename.'" '.JText::_('already exists!'));
                return false;
            }
            else {
                $row->load($id);
            }
        }
        // Get the associated plugin
        $query = 'SELECT id'
        .' FROM #__jce_plugins'
        .' WHERE name = '.$db->Quote($plugin);
        $db->setQuery($query);

        if (!$db->Query()) {
            // Install failed, roll back changes
            $this->parent->abort('Plugin not installed: '.$db->stderr(true));
            return false;
        }
        $id = $db->loadResult();

        $row->pid = $id;
        $row->name = $this->get('name');
        $row->extension = $this->manifest->attributes('extension');
        $row->folder = $this->manifest->attributes('folder');
        $row->published = 1;

        if (!$row->store()) {
            // Install failed, roll back changes
            $this->parent->abort('Extension Install: '.$db->stderr(true));
            return false;
        }

        // Since we have created an extension item, we add it to the installation step stack
        // so that if we have to rollback the changes we can undo it.
        $this->parent->pushStep( array ('type'=>'extension', 'id'=>$row->id));

        /**
         * ---------------------------------------------------------------------------------------------
         * Finalization and Cleanup Section
         * ---------------------------------------------------------------------------------------------
         */

        // Lastly, we will copy the manifest file to its appropriate place.
        if (!$this->parent->copyManifest(-1)) {
            // Install failed, rollback changes
            $this->parent->abort('Extension Install: '.JText::_('Could not copy setup file'));
            return false;
        }
        return true;
    }

    /**
     * Custom uninstall method
     *
     * @access	public
     * @param	int		$cid	The id of the plugin to uninstall
     * @param	int		$clientId	The id of the client (unused)
     * @return	boolean	True on success
     * @since	1.5
     */
    function uninstall($id, $clientId)
    {
        // Initialize variables
        $row = null;
        $retval = true;
        $db = & $this->parent->getDBO();

        // First order of business will be to load the module object table from the database.
        // This should give us the necessary information to proceed.

        $row = & JTable::getInstance('extension', 'JCETable');
        $row->load((int)$id);

        $query = 'SELECT name'
        .' FROM #__jce_plugins'
        .' WHERE id = '.$row->pid
        ;

        $db->setQuery($query);
        if (!$db->Query()) {
            // Install failed, roll back changes
            $this->parent->abort('Plugin not installed: '.$db->stderr(true));
            return false;
        }
        $file = $db->loadResult();

        // Get the extension folder so we can properly build the plugin path
        if (trim($row->extension) == '') {
            JError::raiseWarning(100, 'Extension Uninstall: '.JText::_('Extension field empty, cannot remove files'));
            return false;
        }

        // Set the plugin root path
        $this->parent->setPath('extension_root', JPATH_PLUGINS.DS.'editors'.DS.'jce'.DS.'tiny_mce'.DS.'plugins'.DS.$file.DS.'extensions'.DS.$row->folder);

        $manifestFile = $this->parent->getPath('extension_root').DS.$row->extension.'.xml';

        if (file_exists($manifestFile))
        {
            $xml = & JFactory::getXMLParser('Simple');

            // If we cannot load the xml file return null
            if (!$xml->loadFile($manifestFile)) {
                JError::raiseWarning(100, 'Extension Uninstall: '.JText::_('Could not load manifest file'));
                return false;
            }

            /*
             * Check for a valid XML root tag.
             */
            $root = & $xml->document;
            if ($root->name() != 'install') {
                JError::raiseWarning(100, 'Extension Uninstall: '.JText::_('Invalid manifest file'));
                return false;
            }

            // Remove the extension files
            $this->parent->removeFiles($root->getElementByPath('files'), -1);

            // Remove all media and languages as well
            $this->parent->removeFiles($root->getElementByPath('languages'), 0);


            JFile::delete($manifestFile);
        }
        else {
            JError::raiseWarning(100, 'Extension Uninstall: Manifest File invalid or not found. Extension entry removed from database.');

            $row->delete($row->id);
            unset ($row);
            return false;
        }

        // Now we will no longer need the extension object, so lets delete it
        $row->delete($row->id);
        unset ($row);

        // If the folder is empty, let's delete it
        $files = JFolder::files($this->parent->getPath('extension_root'));
        if (!count($files)) {
            JFolder::delete($this->parent->getPath('extension_root'));
        }

        return $retval;
    }

    /**
     * Custom rollback method
     * 	- Roll back the plugin item
     *
     * @access	public
     * @param	array	$arg	Installation step to rollback
     * @return	boolean	True on success
     * @since	1.5
     * Minor changes to the db query
     */
    function _rollback_plugin($arg)
    {
        // Get database connector object
        $db = & $this->parent->getDBO();

        // Remove the entry from the #__jce_extensions table
        $query = 'DELETE'
        .' FROM #__jce_extensions'
        .' WHERE id='.(int)$arg['id'];

        $db->setQuery($query);

        return ($db->query() !== false);
    }
}
