diff --git a/.gitignore b/.gitignore
index d23f387..05edbfb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -40,3 +40,6 @@ releases/
!.gitignore
!index.html
!index.php
+
+setup/packages
+setup/tmp
\ No newline at end of file
diff --git a/README.md b/README.md
index 6ab730e..028d6ce 100644
--- a/README.md
+++ b/README.md
@@ -1,2 +1,21 @@
#Quix Launcher
-Quix Launcher to install and validate quix
\ No newline at end of file
+Quix Launcher to install and validate quix
+
+
+### Release Instructions
+
+**Update `mainfest.xml`:**
+1. Update the version number.
+2. Update the download link.
+
+**Update `iquix.php`:**
+1. Update the version number.
+
+**Create a ZIP Archive**:
+1. Zip the entire directory, excluding `mainfest.xml`, `todo`, `.gitignore`, and `README.md`.
+
+
+### **Upload ZIP Archive**:
+```cmd
+zip -r com_iquix_1.8.0.zip . -x "mainfest.xml" "todo" ".gitignore" "README.md"
+```
diff --git a/TODO b/TODO
new file mode 100644
index 0000000..e69de29
diff --git a/access.xml b/access.xml
new file mode 100644
index 0000000..2506145
--- /dev/null
+++ b/access.xml
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/config.xml b/config.xml
new file mode 100644
index 0000000..065d56e
--- /dev/null
+++ b/config.xml
@@ -0,0 +1,6 @@
+
+
+
+
\ No newline at end of file
diff --git a/iquix.php b/iquix.php
new file mode 100644
index 0000000..37b525c
--- /dev/null
+++ b/iquix.php
@@ -0,0 +1,55 @@
+ 'iquix.log.php'), Log::ALL, array('iquix'));
+}
+
+$app = Factory::getApplication();
+$input = $app->input;
+$exitInstallation = $input->get('exitInstallation', false, 'bool');
+
+// Check if there's a file initiated for installation
+$file = JPATH_ROOT . '/tmp/quix.installation';
+if ($exitInstallation) {
+ if (File::exists($file)) {
+ File::delete($file);
+ return $app->redirect('index.php?option=com_quix');
+ }
+}
+
+// check if we need to proceed with installation
+$launchInstaller = $input->get('launchInstaller', false, 'bool');
+if ($launchInstaller) {
+ // Determines if the installation is a new installation or old installation.
+ $obj = new stdClass();
+ $obj->new = false;
+ $obj->step = 1;
+ $obj->status = 'installing';
+
+ $contents = json_encode($obj);
+
+ if (!File::exists($file)) {
+ File::write($file, $contents);
+ }
+}
+
+// finally check for setup view or not
+require_once(dirname(__FILE__) . '/setup/bootstrap.php');
+JExit();
\ No newline at end of file
diff --git a/iquix.xml b/iquix.xml
new file mode 100644
index 0000000..17b4f11
--- /dev/null
+++ b/iquix.xml
@@ -0,0 +1,37 @@
+
+
+ com_iquix
+ 11th April 2018
+ Copyright (C) 2015. All rights reserved.
+ GNU General Public License version 2 or later; see LICENSE.txt
+ ThemeXpert
+ info@themexpert.com
+ http://www.themexpert.com
+ 1.8.0
+ Page builder for Joomla
+ script.php
+
+
+ en-GB.com_iquix.ini
+
+
+
+
+ setup
+ iquix.php
+ script.php
+ setup.html
+ config.xml
+ iquix.xml
+ access.xml
+
+
+ en-GB.com_iquix.ini
+ en-GB.com_iquix.sys.ini
+
+
+
+
+
+
+
diff --git a/languages/admin/en-GB.com_iquix.ini b/languages/admin/en-GB.com_iquix.ini
new file mode 100644
index 0000000..688ac7b
--- /dev/null
+++ b/languages/admin/en-GB.com_iquix.ini
@@ -0,0 +1,25 @@
+; @package Quix
+; @copyright Copyright (C) 2009 - 2017 Stack Ideas Private Limited. All rights reserved.
+; @license GNU/GPL, see LICENSE.php
+; Quix is free software. 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. See COPYRIGHT.php for copyright notices and details.
+
+COM_IQUIX="Quix installer"
+COM_IQUIX_INSTALLATION_COMPLETE = "Complete"
+COM_IQUIX_INSTALLATION_CACHECLEAN_SUCCESS = "Cache cleaned successfully!"
+
+COM_QUIX_INSTALLATION_FIX_THIS = "Fix this"
+COM_QUIX_INSTALLATION_PHP_VERSION_TIPS = "Quix recommand PHP7, but minimum requirement is: 5.6.0"
+COM_QUIX_INSTALLATION_PHP_GD_TIPS = "GB library required!"
+COM_QUIX_INSTALLATION_PHP_CURL_TIPS = "CURL support required"
+COM_QUIX_INSTALLATION_PHP_MAGICQUOTES_TIPS = "PHP Magic quote should be disabled!"
+COM_QUIX_INSTALLATION_PHP_MEMORYLIMIT_TIPS = "memory limit must be atleast 64MB"
+COM_QUIX_INSTALLATION_MYSQL_VERSION_TIPS = "Minimum required version 5.0.4"
+COM_QUIX_INSTALLATION_PHP_ZIP_TIPS = "ZIP extension is required for archive operation."
+COM_QUIX_INSTALLATION_PHP_CTYPE_TIPS = "ctype function support is required!"
+COM_QUIX_INSTALLATION_PHP_POST_MAX_SIZE_TIPS = "post_max_size php settings min 5MB"
+COM_QUIX_INSTALLATION_PHP_MAX_EXECUTION_TIPS = "max_execution_time time config should be minimum 60"
+COM_QUIX_INSTALLATION_ALLOW_URL_FOPEN_TIPS="allow_url_fopen is required to Install the package from Online Server."
+COM_QUIX_INSTALLATION_FILEINFO_TIPS = "Reading fileinfo is required for installation process."
\ No newline at end of file
diff --git a/languages/admin/en-GB.com_iquix.sys.ini b/languages/admin/en-GB.com_iquix.sys.ini
new file mode 100644
index 0000000..4949baa
--- /dev/null
+++ b/languages/admin/en-GB.com_iquix.sys.ini
@@ -0,0 +1,10 @@
+; @package Quix
+; @copyright Copyright (C) 2009 - 2017 Stack Ideas Private Limited. All rights reserved.
+; @license GNU/GPL, see LICENSE.php
+; Quix is free software. 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. See COPYRIGHT.php for copyright notices and details.
+
+COM_IQUIX="Quix Installer"
+COM_IQUIX_INSTALLATION_COMPLETE="Complete"
\ No newline at end of file
diff --git a/languages/site/en-GB.com_iquix.ini b/languages/site/en-GB.com_iquix.ini
new file mode 100755
index 0000000..0f8b496
--- /dev/null
+++ b/languages/site/en-GB.com_iquix.ini
@@ -0,0 +1,9 @@
+; @package Quix
+; @copyright Copyright (C) 2009 - 2017 Stack Ideas Private Limited. All rights reserved.
+; @license GNU/GPL, see LICENSE.php
+; Quix is free software. 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. See COPYRIGHT.php for copyright notices and details.
+
+COM_IQUIX="Quix Installer"
\ No newline at end of file
diff --git a/mainfest.xml b/mainfest.xml
index 5c768d8..5c74aa1 100644
--- a/mainfest.xml
+++ b/mainfest.xml
@@ -1,5 +1,21 @@
+
+ Quix Launcher
+ iQuix is an installer extension
+ com_iquix
+ component
+ 1.8.0
+ site
+ https://github.com/themexpert/iquix/releases/tag/v1.8.0
+
+ https://github.com/themexpert/iquix/releases/download/v1.8.0/com_iquix_1.8.0.zip
+
+
+ stable
+
+
+ Quix LauncheriQuix is an installer extension
diff --git a/script.php b/script.php
new file mode 100644
index 0000000..ac3adba
--- /dev/null
+++ b/script.php
@@ -0,0 +1,103 @@
+new = false;
+ $obj->step = 1;
+ $obj->status = 'installing';
+
+ $contents = json_encode($obj);
+
+ if (!JFile::exists($file)) {
+ JFile::write($file, $contents);
+ }
+ }
+
+ /**
+ * Responsible to perform the installation
+ *
+ * @since 1.0
+ * @access public
+ */
+ public function install()
+ {
+ $this->createTableConfig();
+ }
+
+ /**
+ * Responsible to perform the uninstallation
+ *
+ * @since 1.0
+ * @access public
+ */
+ public function uninstall()
+ {
+ // @TODO: Disable modules
+ // @TODO: Disable plugins
+ }
+
+ /**
+ * Responsible to perform component updates
+ *
+ * @since 1.0
+ * @access public
+ */
+ public function update()
+ {
+ $this->createTableConfig();
+ }
+
+ function createTableConfig()
+ {
+ $db = JFactory::getDbo();
+ $sql = "
+ CREATE TABLE IF NOT EXISTS `#__quix_configs` (
+ `name` varchar(255) NOT NULL,
+ `params` text NOT NULL
+ ) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 COMMENT='Store any configuration in key => params maps';
+ ";
+ $db->setQuery($sql);
+ return $db->execute();
+ }
+}
diff --git a/setup.html b/setup.html
new file mode 100644
index 0000000..ffbaabd
--- /dev/null
+++ b/setup.html
@@ -0,0 +1,219 @@
+
+
+
+
+ The Most Advanced Responsive First Website Builder For Joomla! In The World. Build anything, simple to complex with drag & drop layout builder. No coding required.
+
' + result.message + '<.div>');
+ return;
+ }
+
+ $('[data-update-checking]').addClass('hide');
+ $('[data-installation-form]').removeClass('hide');
+
+ });
+ },
+ ajaxCall: function(task, properties, callback) {
+
+ var prop = $.extend({
+ "path": qx.installation.path
+ }, properties);
+
+ $.ajax({
+ type: "POST",
+ url: qx.ajaxUrl + "&controller=update&task=" + task ,
+ data: prop
+ }).done(function(result) {
+ callback.apply(this, [result]);
+ });
+ },
+ }
+}
+
+/**
+ * Download debug log functionality
+ */
+qx.debug = {
+ /**
+ * Initialize debug log download functionality
+ * @returns {void}
+ */
+ init: function() {
+ $(document).on('click', '#debug[data-installation-debug]', function(e) {
+ e.preventDefault();
+ qx.debug.downloadLog();
+ });
+ },
+
+ /**
+ * Download the debug log file directly
+ * @returns {void}
+ */
+ downloadLog: function() {
+ // Create a download link and navigate directly to file download URL
+ window.location.href = qx.ajaxUrl + "&controller=license&task=downloadDebugLog";
+ }
+}
+
+// Initialize debug functionality when document is ready
+$(document).ready(function() {
+ // Initialize debug log download
+ qx.debug.init();
+});
diff --git a/setup/assets/styles/style.css b/setup/assets/styles/style.css
new file mode 100644
index 0000000..625f665
--- /dev/null
+++ b/setup/assets/styles/style.css
@@ -0,0 +1,59 @@
+body{
+ background-color: #f8f9fa;
+ font-family: 'Poppins', sans-serif;
+ font-size: 14px;
+ font-weight: 300;
+ padding-top: 40px;
+ padding-bottom: 40px;
+}
+/* Steps */
+.steps{ margin-top: 2rem; margin-left: -3rem; }
+.steps .badge{
+ width: 40px; height: 40px;
+ background-color: #343a40;
+ color: #fff;
+ padding: 0 !important;
+ font-size: 16px;
+ line-height: 40px;
+}
+.steps > .current > .badge{ background-color: #007bff; }
+.steps > .current.done > .badge{ background-color: #20c997; }
+
+.content{
+ position: relative;
+ min-height: 550px;
+ padding-bottom: 6rem !important;
+}
+
+/* Footer */
+.footer{
+ position: absolute;
+ left: 0; bottom: 0;
+ width: 100%;
+ padding-top: 20px;
+ padding: 1rem;
+ background: #fff;
+ box-shadow: 0px -3px 4px 0px #eee;
+}
+
+.text-error{ color : #ec5f16; }
+.list-group-item .text-error{ color : #f3ea66; }
+.list-group-item .text-success{ color: #85fba0!important}
+
+span.label.label-info {
+ -webkit-font-smoothing: antialiased;
+ background: #a5b2bd;
+ border-radius: 2px;
+ color: #fff;
+ display: inline-block;
+ font-size: 11px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: 10px;
+ padding: 4px 5px;
+ text-transform: uppercase;
+ vertical-align: text-top;
+ -webkit-transition: background-color .25s ease-in-out;
+ -o-transition: background-color .25s ease-in-out;
+ transition: background-color .25s ease-in-out;
+}
\ No newline at end of file
diff --git a/setup/bootstrap.php b/setup/bootstrap.php
new file mode 100644
index 0000000..fee3234
--- /dev/null
+++ b/setup/bootstrap.php
@@ -0,0 +1,229 @@
+ 'iquix.log.php'], Log::ALL, ['iquix']);
+ Log::add('iQuix - Bootstrap initialization started', Log::DEBUG, 'iquix');
+}
+
+// Get application and input objects
+$app = Factory::getApplication();
+$input = $app->input;
+
+// Ensure that the Joomla sections don't appear
+$input->set('tmpl', 'component');
+
+// Get Joomla version
+$jVersion = (new Version())->getShortVersion();
+$isJoomla4OrAbove = version_compare($jVersion, '4.0', '>=');
+$isJoomla5OrAbove = version_compare($jVersion, '5.0', '>=');
+
+if (defined('JDEBUG') && JDEBUG) {
+ Log::add('iQuix - Joomla Version: ' . $jVersion, Log::DEBUG, 'iquix');
+ Log::add('iQuix - Is Joomla 4+: ' . ($isJoomla4OrAbove ? 'Yes' : 'No'), Log::DEBUG, 'iquix');
+ Log::add('iQuix - Is Joomla 5+: ' . ($isJoomla5OrAbove ? 'Yes' : 'No'), Log::DEBUG, 'iquix');
+}
+
+// Determines if we are now in developer mode
+$developer = $input->get('developer', false, 'bool');
+
+if ($developer) {
+ $session = $isJoomla4OrAbove ? $app->getSession() : Factory::getSession();
+ $session->set('quix.developer', true);
+
+ if (defined('JDEBUG') && JDEBUG) {
+ Log::add('iQuix - Developer mode enabled', Log::DEBUG, 'iquix');
+ }
+}
+
+############################################################
+#### Constants
+############################################################
+$path = __DIR__;
+define('QX_PACKAGES', $path . '/packages');
+define('QX_CONFIG', $path . '/config');
+define('QX_THEMES', $path . '/views');
+define('QX_LIB', $path . '/libraries');
+define('QX_CONTROLLERS', $path . '/controllers');
+define('QX_TMP', $path . '/tmp');
+
+// Use Uri class for Joomla 4/5 compatibility
+define('QX_SETUP_URL', rtrim(Uri::root(), '/') . '/administrator/components/com_iquix/setup');
+define('QX_INSTALLER', 'launcher');
+define('QX_PACKAGE', '');
+define('QX_BETA', '');
+
+// Add constant for product id
+define('QX_PRO_ID', '116');
+define('QX_FREE_ID', '117');
+define('QX_EXT_ID', '118');
+define('QX_AGENCY_ID', '127');
+define('QX_BUS_ID', '202');
+define('QX_PRO_LT_ID', '220');
+// Category
+define('QX_CATID', '38');
+
+// Quix API URLs
+define('QX_SERVER', 'https://www.themexpert.com/index.php?option=com_digicom&task=responses');
+define('QX_API_DOWNLOAD', QX_SERVER . '&source=release&format=xml&provider=joomla');
+define('QX_API_UPDATE', QX_SERVER . '&source=release&format=xml&provider=joomla');
+define('QX_API_LICENSE', QX_SERVER . '&source=authapi');
+
+############################################################
+#### Process ajax calls
+############################################################
+if ($input->get('ajax', false, 'bool')) {
+ $controller = $input->get('controller', '', 'cmd');
+ $task = $input->get('task', '', 'cmd');
+
+ $controllerFile = QX_CONTROLLERS . '/' . strtolower($controller) . '.php';
+
+ if (defined('JDEBUG') && JDEBUG) {
+ Log::add('iQuix - Processing AJAX request: ' . $controller . '/' . $task, Log::DEBUG, 'iquix');
+ }
+
+ try {
+ if (File::exists($controllerFile)) {
+ require_once($controllerFile);
+
+ $controllerName = 'iQuixController' . ucfirst($controller);
+
+ if (class_exists($controllerName)) {
+ $controllerInstance = new $controllerName();
+
+ if (method_exists($controllerInstance, $task)) {
+ return $controllerInstance->$task();
+ } else {
+ throw new Exception('Task not found: ' . $task);
+ }
+ } else {
+ throw new Exception('Controller class not found: ' . $controllerName);
+ }
+ } else {
+ throw new Exception('Controller file not found: ' . $controllerFile);
+ }
+ } catch (Exception $e) {
+ if (defined('JDEBUG') && JDEBUG) {
+ Log::add('iQuix - AJAX Error: ' . $e->getMessage(), Log::ERROR, 'iquix');
+ }
+
+ // Send JSON error response
+ header('Content-Type: application/json; charset=utf-8');
+ echo json_encode([
+ 'status' => false,
+ 'message' => 'Error: ' . $e->getMessage()
+ ]);
+ $app->close();
+ }
+}
+
+############################################################
+#### Process controller
+############################################################
+$controller = $input->get('controller', '', 'cmd');
+
+if (!empty($controller)) {
+ $controllerFile = QX_CONTROLLERS . '/' . strtolower($controller) . '.php';
+
+ if (defined('JDEBUG') && JDEBUG) {
+ Log::add('iQuix - Processing controller request: ' . $controller, Log::DEBUG, 'iquix');
+ }
+
+ try {
+ if (File::exists($controllerFile)) {
+ require_once($controllerFile);
+
+ $controllerName = 'iQuixController' . ucfirst($controller);
+
+ if (class_exists($controllerName)) {
+ $controllerInstance = new $controllerName();
+ return $controllerInstance->execute();
+ } else {
+ throw new Exception('Controller class not found: ' . $controllerName);
+ }
+ } else {
+ throw new Exception('Controller file not found: ' . $controllerFile);
+ }
+ } catch (Exception $e) {
+ if (defined('JDEBUG') && JDEBUG) {
+ Log::add('iQuix - Controller Error: ' . $e->getMessage(), Log::ERROR, 'iquix');
+ }
+
+ $app->enqueueMessage('Error: ' . $e->getMessage(), 'error');
+ }
+}
+
+############################################################
+#### Initialization
+############################################################
+try {
+ $contents = file_get_contents(QX_CONFIG . '/installation.json');
+ $steps = json_decode($contents);
+
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ throw new Exception('Failed to parse installation steps: ' . json_last_error_msg());
+ }
+} catch (Exception $e) {
+ if (defined('JDEBUG') && JDEBUG) {
+ Log::add('iQuix - Initialization Error: ' . $e->getMessage(), Log::ERROR, 'iquix');
+ }
+
+ $app->enqueueMessage('Error loading installation configuration: ' . $e->getMessage(), 'error');
+ $steps = [];
+}
+
+############################################################
+#### Workflow
+############################################################
+$active = $input->get('active', 0, 'int');
+
+if ($active == 0) {
+ $active = 1;
+ $stepIndex = 0;
+} else {
+ $active += 1;
+ $stepIndex = $active - 1;
+}
+
+if ($active > count($steps)) {
+ $active = 'complete';
+ $activeStep = new stdClass();
+
+ $activeStep->title = Text::_('Installation Completed');
+ $activeStep->template = 'complete';
+} else {
+ $activeStep = $steps[$stepIndex];
+}
+
+// Register variables for main template
+$template = $activeStep->template ?? 'default';
+$title = $activeStep->title ?? '';
+$description = $activeStep->description ?? '';
+
+// Include the main template
+$themeFile = QX_THEMES . '/default.php';
+if (File::exists($themeFile)) {
+ include($themeFile);
+} else {
+ $app->enqueueMessage('Theme file not found: ' . $themeFile, 'error');
+}
\ No newline at end of file
diff --git a/setup/config/installation.json b/setup/config/installation.json
new file mode 100644
index 0000000..657a8ac
--- /dev/null
+++ b/setup/config/installation.json
@@ -0,0 +1,26 @@
+[
+ {
+ "index": 1,
+ "title": "Requirements",
+ "desc" : "Server must satisfy Quix requirements",
+ "template": "requirements"
+ },
+ {
+ "index": 2,
+ "title": "Validation",
+ "desc" : "Authenticate yourself",
+ "template": "source"
+ },
+ {
+ "index": 3,
+ "title": "Installation",
+ "desc" : "Ignite the thruster",
+ "template": "installing"
+ },
+ {
+ "index": 4,
+ "title": "Maintenance",
+ "desc" : "Cleaning up the system",
+ "template": "maintenance"
+ }
+]
diff --git a/setup/controllers/controller.php b/setup/controllers/controller.php
new file mode 100644
index 0000000..78fffe1
--- /dev/null
+++ b/setup/controllers/controller.php
@@ -0,0 +1,532 @@
+app = Factory::getApplication();
+ $this->input = $this->app->input;
+
+ // Initialize debug logging
+ if ($this->isDebugEnabled()) {
+ $this->initDebugLogging();
+ }
+ }
+
+ /**
+ * Check if debug is enabled
+ *
+ * @return bool
+ */
+ protected function isDebugEnabled()
+ {
+ return defined('JDEBUG') && JDEBUG;
+ }
+
+ /**
+ * Initialize debug logging
+ */
+ protected function initDebugLogging()
+ {
+ Log::addLogger(['text_file' => 'iquix.log.php'], Log::ALL, ['iquix']);
+ $this->debug('Initialized debug logging');
+ }
+
+ /**
+ * Add debug log entry
+ *
+ * @param string $message
+ * @param mixed $data Optional data to include in log
+ */
+ protected function debug($message, $data = null)
+ {
+ if ($this->isDebugEnabled()) {
+ $logMessage = "iQuix - " . $message;
+ if ($data !== null) {
+ $logMessage .= ': ' . (is_string($data) ? $data : json_encode($data));
+ }
+ Log::add($logMessage, Log::DEBUG, 'iquix');
+ }
+ }
+
+ /**
+ * Add data to the result
+ *
+ * @param string $key
+ * @param mixed $value
+ */
+ protected function data($key, $value)
+ {
+ $obj = new \stdClass();
+ $obj->$key = $value;
+
+ $this->result[] = $obj;
+ }
+
+ /**
+ * Renders a response with proper headers
+ *
+ * @param array $data
+ * @return void
+ * @since 2.1.0
+ */
+ public function output($data = [])
+ {
+ header('Content-Type: application/json; UTF-8');
+
+ if (empty($data)) {
+ $data = $this->result;
+ }
+
+ // Add debug information for AJAX responses if debug is enabled
+ if ($this->isDebugEnabled() && is_array($data)) {
+ $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 2);
+ $caller = isset($trace[1]['function']) ? $trace[1]['function'] : '';
+ $this->debug("AJAX Response from {$caller}", $data);
+ }
+
+ echo json_encode($data);
+ exit;
+ }
+
+ /**
+ * Generates a result object that can be json encoded
+ *
+ * @param string $message
+ * @param bool $state
+ * @param string $stateMessage
+ * @return \stdClass
+ * @since 2.1.0
+ */
+ public function getResultObj($message, $state, $stateMessage = '')
+ {
+ $obj = new \stdClass();
+ $obj->state = $state;
+ $obj->stateMessage = $stateMessage;
+
+ // Use namespaced Text for Joomla 4/5, fall back to JText for Joomla 3
+ if (class_exists('\\Joomla\\CMS\\Language\\Text')) {
+ $obj->message = Text::_($message);
+ } else {
+ $obj->message = \JText::_($message);
+ }
+
+ return $obj;
+ }
+
+ /**
+ * Get's the version of this launcher so we know which to install
+ *
+ * @return string
+ * @since 1.0
+ */
+ public function getVersion()
+ {
+ static $version = null;
+
+ // Get the version from the manifest file
+ if (is_null($version)) {
+ $manifestPath = JPATH_ROOT . '/administrator/components/com_iquix/iquix.xml';
+
+ // Use File class for Joomla 4/5 compatibility
+ if (class_exists('\\Joomla\\CMS\\Filesystem\\File')) {
+ $contents = File::exists($manifestPath) ? file_get_contents($manifestPath) : '';
+ } else {
+ $contents = \JFile::exists($manifestPath) ? file_get_contents($manifestPath) : '';
+ }
+
+ if (empty($contents)) {
+ $this->debug('Manifest file not found or empty: ' . $manifestPath);
+ return '0.0.0';
+ }
+
+ $parser = simplexml_load_string($contents);
+ $version = $parser->xpath('version');
+ $version = (string) $version[0];
+ }
+
+ $this->debug("Version", $version);
+ return $version;
+ }
+
+ /**
+ * Retrieve the Joomla Version
+ *
+ * @return string
+ * @since 2.0
+ */
+ public function getJoomlaVersion()
+ {
+ // For Joomla 4 and 5, use Version class
+ if (class_exists('\\Joomla\\CMS\\Version')) {
+ $version = new Version();
+ $jVersion = $version->getShortVersion();
+ } else {
+ // Legacy method for Joomla 3
+ $jVerArr = explode('.', JVERSION);
+ $jVersion = $jVerArr[0] . '.' . $jVerArr[1];
+ }
+
+ $this->debug("Joomla Version", $jVersion);
+ return $jVersion;
+ }
+
+ /**
+ * Check if current Joomla version is at least version 4
+ *
+ * @return bool
+ */
+ protected function isJoomla4OrHigher()
+ {
+ $version = $this->getJoomlaVersion();
+ return version_compare($version, '4.0', '>=');
+ }
+
+ /**
+ * Check if current Joomla version is at least version 5
+ *
+ * @return bool
+ */
+ protected function isJoomla5OrHigher()
+ {
+ $version = $this->getJoomlaVersion();
+ return version_compare($version, '5.0', '>=');
+ }
+
+ /**
+ * Retrieves the current site's domain information
+ *
+ * @return string
+ * @since 2.0.9
+ */
+ public function getDomain()
+ {
+ static $domain = null;
+
+ if (is_null($domain)) {
+ // Use Uri class for Joomla 4/5 compatibility
+ if (class_exists('\\Joomla\\CMS\\Uri\\Uri')) {
+ $domain = Uri::root();
+ } else {
+ $domain = \JURI::root();
+ }
+
+ $domain = str_ireplace(['http://', 'https://'], '', $domain);
+ $domain = rtrim($domain, '/');
+ }
+
+ $this->debug("Domain", $domain);
+ return $domain;
+ }
+
+ /**
+ * Retrieves the information about the latest version
+ *
+ * @return object|false
+ * @since 2.0.9
+ */
+ public function getInfo()
+ {
+ $session = $this->isJoomla4OrHigher() ? Factory::getApplication()->getSession() : Factory::getSession();
+ $username = $session->get('quix.username', '');
+ $key = $session->get('quix.key', '');
+ $id = $session->get('quix.id', '');
+
+ $url = QX_API_LICENSE . '&pid=' . $id . '&username=' . $username . '&key=' . $key;
+
+ $this->debug("Retrieving information from", $url);
+
+ try {
+ // Use Http class for Joomla 4/5 compatibility
+ if ($this->isJoomla4OrHigher()) {
+ $httpOptions = new Registry();
+ $http = HttpFactory::getHttp($httpOptions);
+ $response = $http->get($url);
+ $result = $response->body;
+ } else {
+ // Legacy method for Joomla 3
+ $resource = curl_init();
+ curl_setopt($resource, CURLOPT_URL, $url);
+ curl_setopt($resource, CURLOPT_POST, false);
+ curl_setopt($resource, CURLOPT_TIMEOUT, 120);
+ curl_setopt($resource, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($resource, CURLOPT_SSL_VERIFYPEER, false);
+ $result = curl_exec($resource);
+ curl_close($resource);
+ }
+
+ $this->debug("Server response", $result);
+
+ if (empty($result)) {
+ return false;
+ }
+
+ $obj = json_decode($result);
+ if (json_last_error() !== JSON_ERROR_NONE) {
+ $this->debug("JSON decode error", json_last_error_msg());
+ return false;
+ }
+
+ return $obj;
+ } catch (\Exception $e) {
+ $this->debug("Error retrieving information", $e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Get authentication info from database
+ *
+ * @return void
+ */
+ public function getAuthInfo()
+ {
+ $db = Factory::getDbo();
+ $query = $db->getQuery(true);
+ $query->select('*')->from('#__quix_configs');
+ $db->setQuery($query);
+
+ try {
+ $result = $db->loadObjectList();
+ $this->debug("User info", $result);
+ return $this->output($result);
+ } catch (\Exception $e) {
+ $this->debug("Error getting auth info", $e->getMessage());
+ return $this->output($this->getResultObj("Error retrieving authentication info: " . $e->getMessage(), false, 'error'));
+ }
+ }
+
+ /**
+ * Loads the installed version of Quix
+ *
+ * @return string
+ * @since 1.0
+ */
+ public function getInstalledVersion()
+ {
+ $manifestPath = JPATH_ADMINISTRATOR . '/components/com_quix/quix.xml';
+
+ if (!file_exists($manifestPath)) {
+ $this->debug("Manifest file does not exist", $manifestPath);
+ return '0.0.0';
+ }
+
+ try {
+ $xml = new \SimpleXMLElement(file_get_contents($manifestPath));
+ $version = (string) $xml->version;
+ $this->debug("COM_QUIX version", $version);
+ return $version;
+ } catch (\Exception $e) {
+ $this->debug("Error reading installed version", $e->getMessage());
+ return '0.0.0';
+ }
+ }
+
+ /**
+ * Get previous installed version
+ *
+ * @return string
+ * @since 1.0
+ */
+ public function getPreviousVersion()
+ {
+ return $this->getInstalledVersion();
+ }
+
+ /**
+ * Determines if we are in development mode
+ *
+ * @return bool
+ * @since 1.2
+ */
+ public function isDevelopment()
+ {
+ $session = $this->isJoomla4OrHigher() ? Factory::getApplication()->getSession() : Factory::getSession();
+ $developer = $session->get('quix.developer');
+ return $developer;
+ }
+
+ /**
+ * Verifies the api key
+ *
+ * @param string $username
+ * @param string $key
+ * @return mixed
+ * @since 2.1.0
+ */
+ public function verifyApiKey($username, $key)
+ {
+ $url = QX_API_LICENSE . '&catid=' . QX_CATID . '&username=' . $username . '&key=' . $key;
+ $this->debug("API Auth URL", $url);
+
+ try {
+ // Use Http class for Joomla 4/5 compatibility
+ if ($this->isJoomla4OrHigher()) {
+ $httpOptions = new Registry();
+ $http = HttpFactory::getHttp($httpOptions);
+ $response = $http->get($url);
+
+ if ($response->code != 200 && $response->code != 310) {
+ $this->debug("API Auth failed with code", $response->code);
+ return false;
+ }
+
+ $result = json_decode($response->body);
+ } else {
+ // Legacy method for Joomla 3
+ $httpOption = new Registry();
+ $http = HttpFactory::getHttp($httpOption);
+ $str = $http->get($url);
+
+ if ($str->code != 200 && $str->code != 310) {
+ $this->debug("API Auth failed with code", $str->code);
+ return false;
+ }
+
+ $result = json_decode($str->body);
+ }
+
+ $this->debug("API Auth result", $result);
+ return $result;
+ } catch (\Exception $e) {
+ $this->debug("API Auth error", $e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Retrieves the extension id
+ *
+ * @param string $ext
+ * @return int|null
+ * @since 2.0.10
+ */
+ public function getExtensionId($ext = 'pkg_quix')
+ {
+ try {
+ $db = Factory::getDbo();
+ $query = $db->getQuery(true)
+ ->select('extension_id')
+ ->from($db->quoteName('#__extensions'))
+ ->where($db->quoteName('element') . ' = ' . $db->quote($ext));
+ $db->setQuery($query);
+
+ $extensionId = $db->loadResult();
+ $this->debug("{$ext} extension ID", $extensionId);
+
+ return $extensionId;
+ } catch (\Exception $e) {
+ $this->debug("Error getting extension ID", $e->getMessage());
+ return null;
+ }
+ }
+
+ /**
+ * Retrieves the information about the latest version
+ *
+ * @return string|false
+ * @since 2.0.9
+ */
+ public function getReleaseInfo()
+ {
+ $session = $this->isJoomla4OrHigher() ? Factory::getApplication()->getSession() : Factory::getSession();
+ $username = $session->get('quix.username', '');
+ $key = $session->get('quix.key', '');
+ $id = $session->get('quix.id', '');
+
+ if (empty($id)) {
+ $this->debug("No product ID found in session");
+ return false;
+ }
+
+ try {
+ // For Joomla 4/5
+ if ($this->isJoomla4OrHigher()) {
+ $updateClass = '\\Joomla\\CMS\\Updater\\Update';
+ $update = new $updateClass;
+ } else {
+ // For Joomla 3
+ $update = new \JUpdate;
+ }
+
+ $update->loadFromXml(QX_API_UPDATE . '&pid=' . $id, \JUpdater::STABILITY_STABLE);
+ $this->debug("Release XML info", $update);
+
+ // Handle different property access between Joomla versions
+ if ($this->isJoomla4OrHigher()) {
+ $downloadUrl = $update->get('downloadurl', []);
+ $downloadUrl = is_object($downloadUrl) && isset($downloadUrl->_data) ? $downloadUrl->_data : '';
+ } else {
+ $downloadUrl = $update->get('downloadurl')->_data ?? '';
+ }
+
+ if (empty($downloadUrl)) {
+ $this->debug("No download URL found in update XML");
+ return false;
+ }
+
+ $downloadUrl = $downloadUrl . '&username=' . $username . '&key=' . $key;
+ $this->debug("Download URL", $downloadUrl);
+
+ return $downloadUrl;
+ } catch (\Exception $e) {
+ $this->debug("Error getting release info", $e->getMessage());
+ return false;
+ }
+ }
+}
diff --git a/setup/controllers/installation.php b/setup/controllers/installation.php
new file mode 100644
index 0000000..9d9a635
--- /dev/null
+++ b/setup/controllers/installation.php
@@ -0,0 +1,848 @@
+cachecleaner('com_quix');
+ $this->cachecleaner('mod_quix');
+ $this->cachecleaner('libquix', 1);
+ $this->cachecleaner('lib_quix', 1);
+
+ $this->debug('Cache cleared successfully');
+ return $this->output($this->getResultObj('Cache cleared successfully', true, 'success'));
+ } catch (\Exception $e) {
+ $this->debug('Error clearing cache', $e->getMessage());
+ return $this->output($this->getResultObj('Error clearing cache: ' . $e->getMessage(), false, 'error'));
+ }
+ }
+
+ /**
+ * Clean cache for specific extension
+ *
+ * @param string $group
+ * @param int $client_id
+ * @return bool
+ */
+ public function cachecleaner($group = 'com_quix', $client_id = 0)
+ {
+ $conf = Factory::getConfig();
+
+ try {
+ $options = [
+ 'defaultgroup' => $group,
+ 'cachebase' => ($client_id) ? JPATH_ADMINISTRATOR . '/cache' : $conf->get('cache_path', JPATH_SITE . '/cache'),
+ 'result' => true,
+ ];
+
+ $cache = Factory::getCache($group, '');
+ $cache->clean();
+
+ $this->debug("Cache cleaned for group $group, client_id $client_id");
+ return true;
+ } catch (\Exception $e) {
+ $this->debug("Error cleaning cache for group $group", $e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Check if package extension exists
+ *
+ * @return void
+ */
+ public function checkPackageExtension()
+ {
+ try {
+ $db = Factory::getDbo();
+ $query = $db->getQuery(true)
+ ->select('*')
+ ->from($db->quoteName('#__extensions'))
+ ->where($db->quoteName('element') . ' = ' . $db->quote('pkg_quix'));
+ $db->setQuery($query);
+ $pkg = $db->loadObject();
+
+ if (!$pkg) {
+ $this->debug('pkg_quix does not exist');
+ return $this->output($this->getResultObj('Fresh installation, continuing.', true));
+ }
+
+ // Check if com_quix exists
+ $query = $db->getQuery(true)
+ ->select('extension_id')
+ ->from($db->quoteName('#__extensions'))
+ ->where($db->quoteName('element') . ' = ' . $db->quote('com_quix'));
+ $db->setQuery($query);
+ $com = $db->loadResult();
+
+ if (!$com) {
+ $this->debug('com_quix does not exist, but pkg_quix does - damaged installation');
+ return $this->output($this->getResultObj('The Quix installation seems to be damaged. We need to install it fresh.', false));
+ }
+
+ // Everything looks good
+ $this->debug('pkg_quix exists and valid');
+ return $this->output(
+ $this->getResultObj('Quix is already installed. Do you want to update?', true)
+ );
+ } catch (\Exception $e) {
+ $this->debug('Error checking package extension', $e->getMessage());
+ return $this->output($this->getResultObj('Error checking installation status: ' . $e->getMessage(), false, 'error'));
+ }
+ }
+
+ /**
+ * Downloads the file from the server
+ *
+ * @since 2.0.9
+ * @access public
+ */
+ public function download()
+ {
+ $getLatestRelease = $this->getReleaseInfo();
+
+ if ( ! $getLatestRelease) {
+ $info = $this->getInfo();
+
+ if ( ! $info->success) {
+ $result = new stdClass();
+ $result->state = false;
+ $result->message = $info->message;
+
+ $this->output($result);
+ exit;
+ }
+ if (JDEBUG) {
+ \JLog::add("iQuix - server FIle Info : ".json_encode($info), JLog::DEBUG, 'iquix');
+ }
+
+ // Download the component installer.
+ $data = $info->data;
+
+ } else {
+ $data = new stdClass;
+ $data->download_url = $getLatestRelease;
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - download url from release : ".json_encode($data), JLog::DEBUG, 'iquix');
+ }
+ }
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - Lets download : ".json_encode($data), JLog::DEBUG, 'iquix');
+ }
+
+ $storage = $this->getDownloadFile($data);
+
+ // This only happens when there is no result returned from the server
+ if ($storage === false) {
+ $result = new stdClass();
+ $result->state = false;
+ $result->message = 'There was some errors when downloading the file from the server.';
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - downloading failed : ".$result->message, JLog::ERROR, 'iquix');
+ }
+
+ $this->output($result);
+ }
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - Downloads completed!", JLog::DEBUG, 'iquix');
+ }
+
+ // Extract files here.
+ $tmp = QX_TMP.'/pkg_quix';
+
+ if (JFolder::exists($tmp)) {
+ JFolder::delete($tmp);
+ }
+
+ try {
+ // Try to extract the files
+ // $state = JArchive::extract($storage, $tmp);
+
+ // The archive instance
+ $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path')));
+ // Extract the archive
+ $state = $archive->extract($storage, $tmp);
+
+ } catch (Exception $e) {
+ $result = new stdClass();
+ $result->state = false;
+ $result->message = 'File extracting error: '.$e->getMessage();
+
+ $this->output($result);
+ }
+
+ // If there is an error extracting the zip file, then there is a possibility that the server returned a json string
+ if ( ! $state) {
+
+ $contents = file_get_contents($storage);
+ $result = json_decode($contents);
+
+ if (is_object($result)) {
+ $result->state = false;
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - File extraction error : ".json_encode($result), JLog::ERROR, 'iquix');
+ }
+
+ $this->output($result);
+ exit;
+ }
+
+ $result = new stdClass();
+ $result->state = false;
+ $result->message = 'There was some errors when extracting the archive from the server. If the problem still persists, please contact our support team.
Contact Support';
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - File extraction error : ".$result->message, JLog::ERROR, 'iquix');
+ }
+
+ $this->output($result);
+ exit;
+ }
+
+
+ // Get the md5 hash of the stored file
+ $hash = md5_file($storage);
+
+ // @TODO: update server license plugin to generate md5hash for the file
+ // Check if the md5 check sum matches the one provided from the server.
+ // if (!in_array($hash, $info->md5)) {
+ // $result = new stdClass();
+ // $result->state = false;
+ // $result->message = 'The MD5 hash of the downloaded file does not match. Please contact our support team to look into this.
Contact Support';
+
+ // $this->output($result);
+ // exit;
+ // }
+
+ // After installation is completed, cleanup all zip files from the site
+ $this->cleanupZipFiles(dirname($storage));
+ if (JDEBUG) {
+ \JLog::add("iQuix - Installation file downloaded successfully", JLog::DEBUG, 'iquix');
+ }
+
+ $result = new stdClass();
+ $result->message = 'Installation file downloaded successfully';
+ $result->state = $state;
+ $result->path = $tmp;
+
+ $this->output($result);
+ }
+
+ /**
+ * Downloads the installation files from our installation API
+ *
+ * @since 2.0.9
+ * @access public
+ */
+ public function getDownloadFile($info)
+ {
+ // Set the storage page
+ $storage = QX_PACKAGES.'/pkg_quix.zip';
+
+ // Delete zip archive if it already exists.
+ if (JFile::exists($storage)) {
+ JFile::delete($storage);
+ }
+
+ $download_url = preg_replace("/^http:/i", "https:", $info->download_url);
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - downloading ".$download_url, JLog::DEBUG, 'iquix');
+ \JLog::add("iQuix - downloading to location ".$storage, JLog::DEBUG, 'iquix');
+ }
+
+ set_time_limit(0);
+
+ //This is the file where we save the information
+ $fp = fopen($storage, 'w+');
+
+ //Here is the file we are downloading, replace spaces with %20
+ $ch = curl_init($download_url);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 35000);
+ // write curl response to file
+ curl_setopt($ch, CURLOPT_FILE, $fp);
+ curl_setopt($ch, CURLOPT_FOLLOWLOCATION, true);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+ // get curl response
+ $result = curl_exec($ch);
+ curl_close($ch);
+
+ fclose($fp);
+
+ return $result ? $storage : false;
+
+ }
+
+
+ /**
+ * For users who uploaded the installer and needs a manual extraction
+ *
+ * @since 2.0.9
+ * @access public
+ */
+ public function extract()
+ {
+ // Check the api key from the request
+ $apiKey = JRequest::getVar('apikey', '');
+
+ // Construct the storage path
+ $storage = QX_PACKAGES.'/'.QX_PACKAGE;
+ $exists = JFile::exists($storage);
+
+ // Test if package really exists
+ if ( ! $exists) {
+ $result = new stdClass();
+ $result->state = false;
+ $result->message = 'The component package does not exist on the site. Please contact our support team to look into this.';
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - $result->message", JLog::ERROR, 'iquix');
+ }
+ $this->output($result);
+ exit;
+ }
+
+ // Get the folder name
+ $folderName = basename($storage);
+ $folderName = str_ireplace('.zip', '', $folderName);
+
+ // Extract files here.
+ $tmp = QX_TMP.'/'.$folderName;
+
+ // Ensure that there is no such folders exists on the site
+ if (JFolder::exists($tmp)) {
+ JFolder::delete($tmp);
+ }
+
+ // Try to extract the files
+ //$state = JArchive::extract($storage, $tmp);
+ // The archive instance
+ $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path')));
+ // Extract the archive
+ $state = $archive->extract($storage, $tmp);
+
+ // Regardless of the extraction state, delete the zip file otherwise anyone can download the zip file.
+ @JFile::delete($storage);
+
+ if ( ! $state) {
+ $result = new stdClass();
+ $result->state = false;
+ $result->message = 'There was some errors when extracting the zip file';
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - $result->message", JLog::ERROR, 'iquix');
+ }
+
+ $this->output($result);
+ exit;
+ }
+
+ $result = new stdClass();
+
+ $result->message = 'Installation archive extracted successfully';
+ $result->state = $state;
+ $result->path = $tmp;
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - $result->message", JLog::DEBUG, 'iquix');
+ }
+
+ $this->output($result);
+ }
+
+
+ public function getInstallableVersion()
+ {
+ try {
+ $path = QX_TMP.'/pkg_quix/'.'pkg_quix.xml';
+ // echo $path;die;
+ if (JFile::exists($path)) {
+ $content = file_get_contents($path);
+ $xml = simplexml_load_string($content);
+
+ return (string) $xml->version;
+ } else {
+ return '2.0.0';
+ }
+ } catch (Exception $e) {
+ return '2.0.0';
+ }
+ }
+
+
+ public function installComponent()
+ {
+ // Try to extract the files
+ $storage = QX_TMP.'/pkg_quix/com_quix.zip';
+ $tmp = QX_TMP.'/pkg_quix/com_quix';
+ // $state = JArchive::extract($storage, $tmp);
+
+ // The archive instance
+ $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path')));
+ // Extract the archive
+ $state = $archive->extract($storage, $tmp);
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - Installing component", JLog::DEBUG, 'iquix');
+ }
+
+ if ( ! $state) {
+
+ if (JDEBUG) {
+ $fileExist = JFile::exists($storage);
+ \JLog::add("iQuix - Installing component failed! fileurl: $storage, isFileExist: $fileExist", JLog::DEBUG, 'iquix');
+ }
+
+ return $this->output($this->getResultObj(JText::_('COM_QUIX_INSTALLATION_ERROR_EXTRACT_COMPONENT'), false));
+ }
+
+ try {
+ $app = JFactory::getApplication();
+ $app->input->set('installtype', 'folder');
+ $app->input->set('install_directory', $tmp);
+ JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_installer/models');
+ $installerModel = JModelLegacy::getInstance('Install', 'InstallerModel');
+ $result = $installerModel->install();
+
+ if ($result) {
+ if (JDEBUG) {
+ \JLog::add("iQuix - Component Installed", JLog::DEBUG, 'iquix');
+ }
+
+ return $this->output($this->getResultObj(JText::_('Component installation success!'), true));
+ } else {
+ if (JDEBUG) {
+ \JLog::add("iQuix - Component Installation failed. Error: ".end($app->getMessageQueue()), JLog::DEBUG, 'iquix');
+ }
+
+ return $this->output($this->getResultObj(JText::_('Installation failed! Error: '.end($app->getMessageQueue())), false));
+ }
+
+ if (JDEBUG) {
+ $fileExist = JFile::exists($storage);
+ \JLog::add("iQuix - Installing component failed! fileurl: $storage, isFileExist: $fileExist", JLog::DEBUG, 'iquix');
+ }
+
+ } catch (Exception $e) {
+ if (JDEBUG) {
+ \JLog::add("iQuix - Component Installation failed. Error: ".end($app->getMessageQueue()), JLog::DEBUG, 'iquix');
+ }
+
+ return $this->output($this->getResultObj(JText::_('Error: '.$e->getMessage()), false));
+ }
+ }
+
+ public function installLibrary()
+ {
+ // Try to extract the files
+ $storage = QX_TMP.'/pkg_quix/lib_quix.zip';
+ if ( ! JFile::exists($storage)) {
+ $storage = QX_TMP.'/pkg_quix/lib_quixnxt.zip';
+
+ if ( ! JFile::exists($storage)) {
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - lib_quix does not exist: ".$storage, JLog::DEBUG, 'iquix');
+ }
+
+ return $this->output(JText::_('Installation failed! lib_quix does not exist'));
+ }
+
+ }
+
+ $tmp = QX_TMP.'/pkg_quix/lib_quix';
+ //$state = JArchive::extract($storage, $tmp);
+ // The archive instance
+ $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path')));
+ // Extract the archive
+ $state = $archive->extract($storage, $tmp);
+
+ if ( ! $state) {
+ if ( ! JFile::exists($storage)) {
+ if (JDEBUG) {
+ \JLog::add("iQuix - FIle does not exist: ".$storage, JLog::DEBUG, 'iquix');
+ }
+ }
+
+ return $this->output($this->getResultObj(JText::_('COM_QUIX_INSTALLATION_ERROR_EXTRACT_LIBRARY'), false));
+ }
+
+ try {
+ $app = JFactory::getApplication();
+ $app->input->set('installtype', 'folder');
+ $app->input->set('install_directory', $tmp);
+ JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_installer/models');
+ $installerModel = JModelLegacy::getInstance('Install', 'InstallerModel');
+ $result = $installerModel->install();
+
+ if (JDEBUG) {
+ \JLog::add("iQuix - Library installation status: ".$result, JLog::DEBUG, 'iquix');
+ }
+
+ if ($result) {
+ return $this->output($this->getResultObj(JText::_('Library installation success!'), true));
+ } else {
+ return $this->output($this->getResultObj(JText::_('Installation failed! Error: '.end($app->getMessageQueue())), false));
+ }
+
+ } catch (Exception $e) {
+ if (JDEBUG) {
+ \JLog::add("iQuix - Library installation error: ".$e->getMessage(), JLog::DEBUG, 'iquix');
+ }
+
+ return $this->output($this->getResultObj(JText::_('Error: '.$e->getMessage()), false));
+ }
+ }
+
+ public function installModules()
+ {
+ $app = JFactory::getApplication();
+ $modules = ['mod_quix_menu', 'mod_quix', 'mod_quix_info'];
+ foreach ($modules as $key => $module) {
+ // Try to extract the files
+ $storage = QX_TMP.'/pkg_quix/'.$module.'.zip';
+ $tmp = QX_TMP.'/pkg_quix/'.$module;
+ //$state = JArchive::extract($storage, $tmp);
+ // The archive instance
+ $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path')));
+ // Extract the archive
+ $state = $archive->extract($storage, $tmp);
+
+ if ( ! $state) {
+ return $this->output($this->getResultObj(JText::_('COM_QUIX_INSTALLATION_ERROR_EXTRACT_'.strtoupper($module)), false));
+ }
+
+ try {
+
+ $app->input->set('installtype', 'folder');
+ $app->input->set('install_directory', $tmp);
+ JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_installer/models');
+ $installerModel = JModelLegacy::getInstance('Install', 'InstallerModel');
+ $result = $installerModel->install();
+
+ if ( ! $result) {
+ return $this->output($this->getResultObj(JText::_(strtoupper($module).' installation failed! Error: '.end($app->getMessageQueue())),
+ false));
+ }
+
+ } catch (Exception $e) {
+ return $this->output($this->getResultObj(JText::_('Error: '.$e->getMessage()), false));
+ }
+
+ }
+
+ return $this->output($this->getResultObj(JText::sprintf('%s Modules installation success!', count($modules)), true));
+ }
+
+
+ public function installPlugins()
+ {
+ $app = JFactory::getApplication();
+ $plugins = [
+ 'plg_content_quix',
+ 'plg_editors_xtd_quix',
+ 'plg_finder_quix',
+ 'plg_quickicon_quix',
+ 'plg_quix_content',
+ 'plg_system_quix',
+ 'plg_system_seositeattributes'
+ ];
+ foreach ($plugins as $key => $plugin) {
+ // Try to extract the files
+ $storage = QX_TMP.'/pkg_quix/'.$plugin.'.zip';
+ if ( ! JFile::exists($storage)) {
+ continue;
+ }
+
+ $tmp = QX_TMP.'/pkg_quix/'.$plugin;
+ //$state = JArchive::extract($storage, $tmp);
+ // The archive instance
+ $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path')));
+ // Extract the archive
+ $state = $archive->extract($storage, $tmp);
+
+ if ( ! $state) {
+ return $this->output($this->getResultObj(JText::_('COM_QUIX_INSTALLATION_ERROR_EXTRACT_'.strtoupper($plugin)), false));
+ }
+
+ try {
+
+ $app->input->set('installtype', 'folder');
+ $app->input->set('install_directory', $tmp);
+ JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_installer/models');
+ $installerModel = JModelLegacy::getInstance('Install', 'InstallerModel');
+ $result = $installerModel->install();
+
+ if ( ! $result) {
+ return $this->output($this->getResultObj(JText::_(strtoupper($plugin).' installation failed! Error: '.end($app->getMessageQueue())),
+ false));
+ }
+
+ } catch (Exception $e) {
+ return $this->output($this->getResultObj(JText::_('Error: '.$e->getMessage()), false));
+ }
+
+ }
+
+ return $this->output($this->getResultObj(JText::sprintf('%s Plugins installation success!', count($plugins)), true));
+ }
+
+ public function installTemplates()
+ {
+ $app = JFactory::getApplication();
+ $templates = [
+ 'tpl_atom'
+ ];
+ foreach ($templates as $key => $template) {
+ // Try to extract the files
+ $storage = QX_TMP.'/pkg_quix/'.$template.'.zip';
+ if ( ! JFile::exists($storage)) {
+ continue;
+ }
+
+ $tmp = QX_TMP.'/pkg_quix/'.$template;
+ //$state = JArchive::extract($storage, $tmp);
+
+ // The archive instance
+ $archive = new Archive(array('tmp_path' => JFactory::getConfig()->get('tmp_path')));
+ // Extract the archive
+ $state = $archive->extract($storage, $tmp);
+
+ if ( ! $state) {
+ return $this->output($this->getResultObj(JText::_('COM_QUIX_INSTALLATION_ERROR_EXTRACT_'.strtoupper($template)), false));
+ }
+
+ try {
+
+ $app->input->set('installtype', 'folder');
+ $app->input->set('install_directory', $tmp);
+ JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_installer/models');
+ $installerModel = JModelLegacy::getInstance('Install', 'InstallerModel');
+ $result = $installerModel->install();
+
+ if ( ! $result) {
+ return $this->output($this->getResultObj(JText::_(strtoupper($template).' installation failed! Error: '.end($app->getMessageQueue())),
+ false));
+ }
+
+ } catch (Exception $e) {
+ return $this->output($this->getResultObj(JText::_('Error: '.$e->getMessage()), false));
+ }
+
+ }
+
+ return $this->output($this->getResultObj(JText::sprintf('%s Templates installation success!', count($templates)), true));
+ }
+
+ /**
+ * Post installation process
+ *
+ * @since 1.0
+ * @access public
+ */
+ public function syncDb()
+ {
+ include QX_TMP."/pkg_quix/pkg.script.php";
+
+ try {
+ $script = new pkg_QuixInstallerScript();
+ ob_start();
+ $script->postflight(array());
+ $data = ob_get_contents();
+ ob_end_clean();
+
+ return $this->output($this->getResultObj(JText::_('Updating Database complete!'), true));
+
+ } catch (Exception $e) {
+ if (JDEBUG) {
+ \JLog::add("iQuix - syncDb failed cause: : ".JText::_('Error: '.$e->getMessage()), JLog::DEBUG, 'iquix');
+ }
+
+ return $this->output($this->getResultObj(JText::_('Error: '.$e->getMessage()), false));
+ }
+ }
+
+ /**
+ * Post installation process
+ *
+ * @since 1.0
+ * @access public
+ */
+ public function installPost()
+ {
+ $results = array();
+
+ // Update the api key on the server with the one from the bootstrap
+ // $this->updateConfig();
+
+ // update package version
+ $this->updateJoomlaUpdater();
+
+ try {
+ // Cleanup temporary files from the tmp folder
+ $tmp = dirname(dirname(__FILE__)).'/tmp';
+ $folders = JFolder::folders($tmp, '.', false, true);
+
+ if ($folders) {
+ foreach ($folders as $folder) {
+ @JFolder::delete($folder);
+ }
+ }
+
+ // Update installation package to 'launcher'
+ $this->updatePackage();
+
+ $result = new stdClass();
+ $result->state = true;
+ $result->message = "Post operation completed!";
+
+
+ } catch (Exception $e) {
+ $result = new stdClass();
+ $result->state = false;
+ $result->message = "Post operation failed! but you proceed...";
+
+ }
+
+
+ return $this->output($result);
+ }
+
+ /**
+ * Update installation package to launcher package to update issue via update button
+ *
+ * @since 2.1.3
+ * @access public
+ */
+ public function updatePackage()
+ {
+ // now we need to update the QX_INSTALLER to launcher to that the update button will
+ // work correctly. #1558
+ $path = JPATH_ADMINISTRATOR.'/components/com_iquix/setup/bootstrap.php';
+
+ // Read the contents
+ $contents = file_get_contents($path);
+
+ $contents = str_ireplace("define('QX_INSTALLER', 'full');", "define('QX_INSTALLER', 'launcher');", $contents);
+ $contents = preg_replace('/define\(\'QX_PACKAGE\', \'.*\'\);/i', "define('QX_PACKAGE', '');", $contents);
+
+ JFile::write($path, $contents);
+ }
+
+ /**
+ * Allows cleanup of installation files
+ *
+ * @since 1.3
+ * @access public
+ */
+ private function cleanupZipFiles($path)
+ {
+ return true;
+
+ $zipFiles = JFolder::files($path, '.zip', false, true);
+
+ if ($zipFiles) {
+ foreach ($zipFiles as $file) {
+ @JFile::delete($file);
+ }
+ }
+
+ return true;
+ }
+
+
+ public function updateJoomlaUpdater()
+ {
+ $version = $this->getInstallableVersion();
+
+ $db = JFactory::getDBO();
+ // Update installed version
+ $query = "SELECT * FROM `#__extensions` WHERE `name` = 'pkg_quix' and `type` = 'package'";
+ $db->setQuery($query);
+ $result = $db->loadObject();
+ if(!$result) return;
+
+ $manifest = json_decode($result->manifest_cache);
+ $manifest->version = $version;
+
+ $result->manifest_cache = json_encode($manifest);
+
+ return JFactory::getDbo()->updateObject('#__extensions', $result, 'extension_id');
+ }
+
+}
diff --git a/setup/controllers/license.php b/setup/controllers/license.php
new file mode 100644
index 0000000..5ff43ae
--- /dev/null
+++ b/setup/controllers/license.php
@@ -0,0 +1,289 @@
+input->get('task', '');
+
+ // Check if method exists
+ if (!method_exists($this, $task)) {
+ $this->debug('Task not found', $task);
+ return $this->output($this->getResultObj('Invalid task specified.', false, 'error'));
+ }
+
+ // Execute the task
+ return $this->$task();
+ }
+
+ /**
+ * Verify the license
+ *
+ * @return mixed
+ */
+ public function verify()
+ {
+ $username = $this->input->get('username', '', 'string');
+ $key = $this->input->get('key', '', 'string');
+
+ if (empty($username) || empty($key)) {
+ $this->debug('Missing username or key');
+ return $this->output($this->getResultObj('Please provide both username and license key.', false, 'error'));
+ }
+
+ try {
+ $result = $this->verifyApiKey($username, $key);
+
+ if (!$result) {
+ return $this->output($this->getResultObj('Unable to verify the license. Please try again.', false, 'error'));
+ }
+
+ if (!isset($result->success) || $result->success !== true) {
+ $message = isset($result->message) ? $result->message : 'License verification failed.';
+ $this->debug('License verification failed', $message);
+ return $this->output($this->getResultObj($message, false, 'error'));
+ }
+
+ // We need to store this into our session
+ $session = $this->isJoomla4OrHigher() ? Factory::getApplication()->getSession() : Factory::getSession();
+ $session->set('quix.license.status', $result->success);
+ $session->set('quix.username', $username);
+ $session->set('quix.key', $key);
+
+ // Store the user information
+ if (isset($result->data) && is_array($result->data)) {
+ // Get a valid license from the response
+ $license = $this->getValidLicense($result);
+ $license->id = 117;
+
+ if (!$license) {
+ $this->debug('No valid license found');
+ return $this->output($this->getResultObj('No valid license found for Quix.', false, 'error'));
+ }
+
+ $session->set('quix.license', $license->name);
+ $session->set('quix.id', $license->id);
+
+ // Store license info in database
+ $this->storeLicenseInfo($username, $key);
+
+ $this->debug('License verified', [
+ 'username' => $username,
+ 'product' => $license->name,
+ 'id' => $license->id
+ ]);
+
+ // Create a custom response object with an HTML field for the license ID input
+ $responseObj = $this->getResultObj(
+ $license->name ? 'Great! ' . $license->name . ' license found. Please click next to continue installation.' : 'License verified successfully!',
+ true,
+ 'success'
+ );
+
+ // Add the HTML input field that was in the original code
+ $responseObj->html = '';
+
+ return $this->output($responseObj);
+ }
+
+ $this->debug('Invalid license response format');
+ return $this->output($this->getResultObj('Invalid license response format.', false, 'error'));
+ } catch (\Exception $e) {
+ $this->debug('License verification error', $e->getMessage());
+ return $this->output($this->getResultObj('Error: ' . $e->getMessage(), false, 'error'));
+ }
+ }
+
+ /**
+ * Store license information in the database
+ *
+ * @param string $username
+ * @param string $authkey
+ * @return bool
+ */
+ private function storeLicenseInfo($username, $authkey)
+ {
+ try {
+ $db = Factory::getDbo();
+ $query = $db->getQuery(true)
+ ->select('*')
+ ->from('#__quix_configs');
+ $db->setQuery($query);
+ $result = $db->loadObjectList();
+
+ if (empty($result)) {
+ // Insert new records
+ $obj = new \stdClass();
+ $obj->name = 'username';
+ $obj->params = $username;
+ $db->insertObject('#__quix_configs', $obj);
+
+ $obj = new \stdClass();
+ $obj->name = 'key';
+ $obj->params = $authkey;
+ $db->insertObject('#__quix_configs', $obj);
+ } else {
+ // Update existing records
+ foreach ($result as $item) {
+ if ($item->name == 'username') {
+ $obj = new \stdClass();
+ $obj->name = 'username';
+ $obj->params = $username;
+ $db->updateObject('#__quix_configs', $obj, 'name');
+ }
+
+ if ($item->name == 'key') {
+ $obj = new \stdClass();
+ $obj->name = 'key';
+ $obj->params = $authkey;
+ $db->updateObject('#__quix_configs', $obj, 'name');
+ }
+ }
+ }
+
+ $this->debug('License info stored in database');
+ return true;
+ } catch (\Exception $e) {
+ $this->debug('Error storing license info', $e->getMessage());
+ return false;
+ }
+ }
+
+ /**
+ * Get valid license from the response
+ *
+ * @param object $data
+ * @return object|false
+ */
+ public function getValidLicense($data)
+ {
+ if (!isset($data->data) || !is_array($data->data)) {
+ $this->debug('Invalid license data format');
+ return false;
+ }
+
+ $products = $data->data;
+ $quixPro = [QX_AGENCY_ID, QX_PRO_ID, QX_EXT_ID, QX_BUS_ID, QX_PRO_LT_ID];
+ $hasPro = false;
+ $hasFree = false;
+ $proProduct = null;
+ $proID = 0;
+
+ foreach ($products as $product) {
+ if (in_array($product->id, $quixPro, true) && ($product->has_access === true)) {
+ $hasPro = true;
+ $proProduct = $product;
+ $proID = $product->id;
+ break;
+ }
+
+ if ($product->id == QX_FREE_ID && $product->has_access === true) {
+ $hasFree = true;
+ $freeProduct = $product;
+ }
+ }
+
+ if ($hasPro && $proProduct) {
+ $this->debug('Found valid Pro license', $proProduct->name);
+ return $proProduct;
+ }
+
+ if ($hasFree) {
+ $this->debug('Found valid Free license');
+ return $freeProduct;
+ }
+
+ $this->debug('No valid license found');
+ return false;
+ }
+
+ /**
+ * Get release information
+ *
+ * @return mixed
+ */
+ public function getReleaseInfo()
+ {
+ $url = $this->getReleaseInfo();
+
+ if (!$url) {
+ $this->debug('Failed to get release info');
+ return $this->output($this->getResultObj('Failed to get release information.', false, 'error'));
+ }
+
+ $this->debug('Release info retrieved', $url);
+ return $this->output($this->getResultObj($url, true, 'success'));
+ }
+
+ /**
+ * Download the debug log file
+ *
+ * This method allows users to download the iquix.log.php file from the logs directory
+ *
+ * @return void
+ */
+ public function downloadDebugLog()
+ {
+ try {
+ $this->debug('Attempting to download debug log file');
+
+ // Define the path to the log file - using JPATH_ROOT and DIRECTORY_SEPARATOR for compatibility
+ $logFile = JPATH_ADMINISTRATOR . DIRECTORY_SEPARATOR . 'logs' . DIRECTORY_SEPARATOR . 'iquix.log.php';
+
+ // Check if the file exists
+ if (!file_exists($logFile)) {
+ $this->debug('Debug log file not found', $logFile);
+ return $this->output($this->getResultObj('Debug log file not found.', false, 'error'));
+ }
+
+ // Directly serve the file for download
+ // Clear any buffered output
+ while (ob_get_level()) {
+ ob_end_clean();
+ }
+
+ // Set headers for file download
+ header('Content-Description: File Transfer');
+ header('Content-Type: application/octet-stream');
+ header('Content-Disposition: attachment; filename="iquix_debug.log"');
+ header('Content-Transfer-Encoding: binary');
+ header('Expires: 0');
+ header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
+ header('Pragma: public');
+ header('Content-Length: ' . filesize($logFile));
+
+ // Output file content
+ readfile($logFile);
+ $this->debug('Debug log file downloaded successfully');
+ exit;
+ } catch (\Exception $e) {
+ $this->debug('Error downloading debug log', $e->getMessage());
+ return $this->output($this->getResultObj('Error: ' . $e->getMessage(), false, 'error'));
+ }
+ }
+}
\ No newline at end of file
diff --git a/setup/controllers/maintenance.php b/setup/controllers/maintenance.php
new file mode 100644
index 0000000..56d901b
--- /dev/null
+++ b/setup/controllers/maintenance.php
@@ -0,0 +1,150 @@
+debug('Temporary installation file removed', $tempFile);
+ } else {
+ $this->debug('Temporary installation file not found', $tempFile);
+ }
+
+ return $this->output($this->getResultObj('Installation files cleaned successfully.', true, 'success'));
+ } catch (\Exception $e) {
+ $this->debug('Error cleaning installation files', $e->getMessage());
+ return $this->output($this->getResultObj('Error cleaning installation files: ' . $e->getMessage(), false, 'error'));
+ }
+ }
+
+ /**
+ * Removes the update record from the database
+ *
+ * @return mixed
+ */
+ public function removeUpdateRecord()
+ {
+ try {
+ $db = Factory::getDbo();
+ $extensionId = $this->getExtensionId();
+
+ if (!$extensionId) {
+ $this->debug('No extension ID found for pkg_quix');
+ return $this->output($this->getResultObj('No extension record found for Quix.', false, 'warning'));
+ }
+
+ $query = $db->getQuery(true)
+ ->delete($db->quoteName('#__updates'))
+ ->where($db->quoteName('extension_id') . '=' . $db->quote($extensionId));
+ $db->setQuery($query);
+ $result = $db->execute();
+
+ if ($result) {
+ $this->debug('Update record cleaned successfully');
+ return $this->output($this->getResultObj('Update record cleaned successfully!', true, 'success'));
+ } else {
+ $this->debug('Failed to clean update record');
+ return $this->output($this->getResultObj('Unable to clean update record!', false, 'fail'));
+ }
+ } catch (\Exception $e) {
+ $this->debug('Error removing update record', $e->getMessage());
+ return $this->output($this->getResultObj('Error removing update record: ' . $e->getMessage(), false, 'error'));
+ }
+ }
+
+ /**
+ * Updates Quix assets
+ *
+ * @return mixed
+ */
+ public function updateAssets()
+ {
+ try {
+ // Generate token for Joomla 3/4/5 compatibility
+ if ($this->isJoomla4OrHigher()) {
+ $token = Factory::getApplication()->getFormToken() . '=1';
+ } else {
+ $token = \JSession::getFormToken() . '=1';
+ }
+
+ $ajax_url = 'index.php?option=com_quix&task=updateAjax&' . $token;
+ $this->debug('Updating assets via', $ajax_url);
+
+ // Use Http class for Joomla 4/5 compatibility
+ if ($this->isJoomla4OrHigher()) {
+ $options = new Registry();
+ $http = new Http($options);
+
+ try {
+ $response = $http->get($ajax_url);
+ $result = ($response->code == 200 || $response->code == 310);
+ $this->debug('Asset update response code', $response->code);
+ } catch (\Exception $e) {
+ // If failed, log but return success (non-critical)
+ $this->debug('HTTP request failed but continuing', $e->getMessage());
+ $result = true;
+ }
+ } else {
+ // Legacy method for Joomla 3
+ try {
+ $http = new \JHttp();
+ $str = $http->get($ajax_url);
+ $result = ($str->code == 200 || $str->code == 310);
+ $this->debug('Asset update response code', $str->code);
+ } catch (\Exception $e) {
+ // If failed, log but return success (non-critical)
+ $this->debug('HTTP request failed but continuing', $e->getMessage());
+ $result = true;
+ }
+ }
+
+ if ($result) {
+ return $this->output($this->getResultObj('Assets updated successfully!', true, 'success'));
+ } else {
+ return $this->output($this->getResultObj('Unable to update assets!', false, 'fail'));
+ }
+ } catch (\Exception $e) {
+ $this->debug('Error updating assets', $e->getMessage());
+ return $this->output($this->getResultObj('Error updating assets: ' . $e->getMessage(), false, 'error'));
+ }
+ }
+}
\ No newline at end of file
diff --git a/setup/controllers/update.php b/setup/controllers/update.php
new file mode 100644
index 0000000..11fc903
--- /dev/null
+++ b/setup/controllers/update.php
@@ -0,0 +1,254 @@
+getShortVersion();
+ } else {
+ $jVerArr = explode('.', JVERSION);
+ $jVersion = $jVerArr[0] . '.' . $jVerArr[1];
+ }
+
+ $isJoomla4OrHigher = version_compare($jVersion, '4.0', '>=');
+ $this->debug('Updating Joomla updater for version', $jVersion);
+
+ // Get the extension ID
+ $extensionId = $this->getExtensionId();
+
+ if (!$extensionId) {
+ $this->debug('No extension ID found for pkg_quix');
+ return $this->output($this->getResultObj('No extension record found for Quix.', false, 'warning'));
+ }
+
+ // Get license ID from session
+ $session = $isJoomla4OrHigher ? Factory::getApplication()->getSession() : Factory::getSession();
+ $id = $session->get('quix.id', '');
+
+ if (empty($id)) {
+ $this->debug('No license ID found in session');
+ return $this->output($this->getResultObj('No license information found.', false, 'error'));
+ }
+
+ // Build the update URL
+ $update_site = QX_API_UPDATE . '&pid=' . $id;
+ $this->debug('Update site URL', $update_site);
+
+ // Update the update site information in the database
+ $db = Factory::getDbo();
+
+ // First check if the update site exists
+ $query = $db->getQuery(true)
+ ->select('update_site_id')
+ ->from($db->quoteName('#__update_sites'))
+ ->where($db->quoteName('name') . ' = ' . $db->quote('Quix'))
+ ->where($db->quoteName('type') . ' = ' . $db->quote('extension'));
+
+ $db->setQuery($query);
+ $update_site_id = $db->loadResult();
+
+ if ($update_site_id) {
+ // Update existing update site
+ $query = $db->getQuery(true)
+ ->update($db->quoteName('#__update_sites'))
+ ->set($db->quoteName('location') . ' = ' . $db->quote($update_site))
+ ->where($db->quoteName('update_site_id') . ' = ' . $db->quote($update_site_id));
+ $db->setQuery($query);
+ $db->execute();
+
+ $this->debug('Updated existing update site record', $update_site_id);
+ } else {
+ // Insert new update site
+ $updateSiteObject = new \stdClass;
+ $updateSiteObject->name = 'Quix';
+ $updateSiteObject->type = 'extension';
+ $updateSiteObject->location = $update_site;
+ $updateSiteObject->enabled = 1;
+ $updateSiteObject->last_check_timestamp = 0;
+
+ $db->insertObject('#__update_sites', $updateSiteObject);
+ $update_site_id = $db->insertid();
+
+ // Link the update site to the extension
+ if ($update_site_id && $extensionId) {
+ $updateSiteExtObject = new \stdClass;
+ $updateSiteExtObject->update_site_id = $update_site_id;
+ $updateSiteExtObject->extension_id = $extensionId;
+
+ $db->insertObject('#__update_sites_extensions', $updateSiteExtObject);
+ $this->debug('Created new update site record', $update_site_id);
+ }
+ }
+
+ // Refresh the update information
+ if ($isJoomla4OrHigher) {
+ // For Joomla 4 and 5
+ $query = $db->getQuery(true)
+ ->delete($db->quoteName('#__updates'))
+ ->where($db->quoteName('extension_id') . ' = ' . $db->quote($extensionId));
+ $db->setQuery($query);
+ $db->execute();
+
+ // Trigger update check
+ $updateSites = [$update_site_id];
+ $modelFile = JPATH_ADMINISTRATOR . '/components/com_installer/models/update.php';
+
+ if (File::exists($modelFile)) {
+ require_once $modelFile;
+ $model = new \Joomla\Component\Installer\Administrator\Model\UpdateModel(['update_site_id' => $updateSites]);
+ $model->findUpdates($extensionId, 0);
+ $this->debug('Triggered update check via model');
+ } else {
+ // Alternative method if model not available
+ $app = Factory::getApplication();
+ $app->triggerEvent('onExtensionAfterUpdate', ['installer.updatecache', null, ['update_site_id' => $updateSites]]);
+ $this->debug('Triggered update check via event');
+ }
+ } else {
+ // For Joomla 3
+ $query = $db->getQuery(true)
+ ->delete($db->quoteName('#__updates'))
+ ->where($db->quoteName('extension_id') . ' = ' . $db->quote($extensionId));
+ $db->setQuery($query);
+ $db->execute();
+
+ // Trigger update check
+ Factory::getApplication()->triggerEvent('onExtensionAfterUpdate',
+ ['installer.updatecache', null]
+ );
+ $this->debug('Triggered update check via event (J3)');
+ }
+
+ return $this->output($this->getResultObj('Joomla updater updated successfully!', true, 'success'));
+ } catch (\Exception $e) {
+ $this->debug('Error updating Joomla updater', $e->getMessage());
+ return $this->output($this->getResultObj('Error updating Joomla updater: ' . $e->getMessage(), false, 'error'));
+ }
+ }
+
+ /**
+ * Verifies the user's license
+ *
+ * @since 2.1.0
+ * @access public
+ */
+ public function updateScript()
+ {
+ $session = JFactory::getSession();
+ // $session->set('quix.scriptupdate', false);
+ $scriptupdate = $session->get('quix.scriptupdate', false);
+ if($scriptupdate)
+ {
+ $this->output($this->getResultObj( 'Already checked!' , true ));
+ }
+
+ // 1. get current version
+ $localVersion = $this->getVersion();
+ // 2. get latest version info from server
+ $xml = $this->getLatestVersion();
+
+ $update = $xml->update[0]; // first one is the latest one
+ if(isset($update->version))
+ {
+ $onlineVersion = (string) $update->version;
+ }
+ else
+ {
+ $onlineVersion = '1.0.0';
+ }
+
+ $session->set('quix.scriptupdate', true);
+
+ // 3. match versions
+ if(version_compare($onlineVersion, $localVersion) == '1')
+ {
+ $result = $this->installScriptComponent($update);
+ if($result)
+ {
+ $this->output($this->getResultObj( 'Updated successfully!' , true , 302));
+ }
+ else
+ {
+ $this->output($this->getResultObj( 'Something wrong to update the script! please manually install the Installer.' , false ));
+ }
+ }
+ else
+ {
+ // need to update the script
+ // either we have updated or same
+ $this->output($this->getResultObj( 'You have the latest version' , true ));
+ }
+ }
+
+ public function getLatestVersion()
+ {
+ $ch = curl_init('https://raw.githubusercontent.com/themexpert/iquix/master/mainfest.xml');
+
+ curl_setopt($ch, CURLOPT_POST, false);
+ // curl_setopt($ch, CURLOPT_POSTFIELDS, 'key=' . QX_KEY . '&version=' . $info->version);
+ curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
+ curl_setopt($ch, CURLOPT_TIMEOUT, 35000);
+ curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
+
+ $contents = curl_exec($ch);
+ curl_close($ch);
+
+ $xml = simplexml_load_string( $contents );
+
+ return $xml;
+ }
+
+ public function installScriptComponent($update)
+ {
+ try {
+ $app = JFactory::getApplication();
+ $app->input->set('installtype', 'url');
+ $app->input->set('install_url', $update->downloads->downloadurl); //install_directory
+ JModelLegacy::addIncludePath(JPATH_ADMINISTRATOR.'/components/com_installer/models');
+ $installerModel = JModelLegacy::getInstance('Install', 'InstallerModel');
+ return $installerModel->install();
+ } catch (Exception $e) {
+ return $this->output($this->getResultObj( JText::_( 'Error: ' . $e->getMessage() ) , false ));
+ }
+ }
+}
\ No newline at end of file
diff --git a/setup/packages/index.html b/setup/packages/index.html
new file mode 100644
index 0000000..e69de29
diff --git a/setup/views/default.footer.php b/setup/views/default.footer.php
new file mode 100644
index 0000000..f0baf5c
--- /dev/null
+++ b/setup/views/default.footer.php
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/setup/views/steps/complete.php b/setup/views/steps/complete.php
new file mode 100644
index 0000000..daf2fdc
--- /dev/null
+++ b/setup/views/steps/complete.php
@@ -0,0 +1,27 @@
+
+
+
🎊
+
+
Installation Completed
+
+
+
Congratulations! Quix has been successfully installed on your site and you may start using it.
diff --git a/setup/views/steps/maintenance.php b/setup/views/steps/maintenance.php
new file mode 100644
index 0000000..b54d472
--- /dev/null
+++ b/setup/views/steps/maintenance.php
@@ -0,0 +1,57 @@
+
+
+
+
\ No newline at end of file
diff --git a/setup/views/steps/requirements.php b/setup/views/steps/requirements.php
new file mode 100644
index 0000000..2c17842
--- /dev/null
+++ b/setup/views/steps/requirements.php
@@ -0,0 +1,500 @@
+';
+ $crossIcon = '';
+
+ if ($iconType === 'checkmark')
+ {
+ echo $checkmarkIcon;
+ }
+ elseif ($iconType === 'cross')
+ {
+ echo $crossIcon;
+ }
+}
+
+$gd = function_exists('gd_info');
+$curl = is_callable('curl_init');
+$ctype = extension_loaded('ctype');
+$fileinfo = extension_loaded('fileinfo');
+//###########################################
+//# MySQL info
+//###########################################
+$db = JFactory::getDBO();
+$mysqlVersion = $db->getVersion();
+// first 3 characters of the version
+$mysqlVersion = substr($mysqlVersion, 0, 5);
+
+//###########################################
+//# PHP info
+//###########################################
+$phpVersion = phpversion();
+
+$memoryLimit = ini_get('memory_limit');
+// convert the value to MB without unit
+if (stripos($memoryLimit, 'M') !== false) {
+ list($memoryLimit) = explode('M', $memoryLimit);
+} elseif (stripos($memoryLimit, 'K') !== false) {
+ list($memoryLimit) = explode('K', $memoryLimit);
+ $memoryLimit = $memoryLimit / 1024;
+}
+
+$postSize = (int) ini_get('post_max_size');
+$max_execution = ini_get('max_execution_time');
+$allow_url_fopen = ini_get('allow_url_fopen');
+
+$hasErrors = false;
+
+if (stripos($memoryLimit, 'G') !== false) {
+ list($memoryLimit) = explode('G', $memoryLimit);
+ $memoryLimit = $memoryLimit * 1024;
+}
+
+if (!$gd || !$curl || !$ctype || !$fileinfo || !$allow_url_fopen || $postSize < 5 || $max_execution < 30) {
+ $hasErrors = true;
+}
+
+//#########################################
+//# Paths
+//#########################################
+$files = [];
+
+$files['admin'] = new stdClass();
+$files['admin']->path = JPATH_ROOT . '/administrator/components';
+
+$files['admin_modules'] = new stdClass();
+$files['admin_modules']->path = JPATH_ROOT . '/administrator/modules';
+
+$files['site'] = new stdClass();
+$files['site']->path = JPATH_ROOT . '/components';
+
+$files['tmp'] = new stdClass();
+$files['tmp']->path = JPATH_ROOT . '/tmp';
+
+$files['media'] = new stdClass();
+$files['media']->path = JPATH_ROOT . '/media';
+
+$files['libraries'] = new stdClass();
+$files['libraries']->path = JPATH_ROOT . '/libraries';
+
+$files['system'] = new stdClass();
+$files['system']->path = JPATH_ROOT . '/plugins/system';
+
+$files['content'] = new stdClass();
+$files['content']->path = JPATH_ROOT . '/plugins/content';
+
+$files['finder'] = new stdClass();
+$files['finder']->path = JPATH_ROOT . '/plugins/finder';
+
+$files['quickicon'] = new stdClass();
+$files['quickicon']->path = JPATH_ROOT . '/plugins/quickicon';
+
+$files['editors_xtd'] = new stdClass();
+$files['editors_xtd']->path = JPATH_ROOT . '/plugins/editors_xtd';
+
+$files['auth'] = new stdClass();
+$files['auth']->path = JPATH_ROOT . '/modules';
+
+$files['cache'] = new stdClass();
+$files['cache']->path = JPATH_ROOT . '/cache';
+
+//#########################################
+//# Determine states
+//#########################################
+// $hasErrors = false;
+
+foreach ($files as $file) {
+ // The only proper way to test this is to not use is_writable
+ $contents = '';
+ $state = JFile::write($file->path . '/tmp.html', $contents);
+
+ // Initialize this to false by default
+ $file->writable = false;
+
+ if ($state) {
+ JFile::delete($file->path . '/tmp.html');
+
+ $file->writable = true;
+ }
+
+ if (!$file->writable) {
+ $hasErrors = true;
+ }
+}
+?>
+
+
+
+
+
+ Checking update script...
+
+
+
+
+
Quix Auto Installer
+
+ Quix Installer - v#
+
+
+
+
Requirements
+
+
+get('quix.scriptupdate', false);
+if (!$checkUpdate): ?>
+
+get('quix.hasLicense', false);
+$hasFree = $session->get('quix.hasFree', false);
+$hasLicense = $hasLicensepro;
+
+// we have to check for free license only for now, as this is the only version
+// if($hasLicensepro && $hasFree){
+// $hasLicense = true;
+// }
+?>
+
+
+