diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6373079 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +.DS_STORE \ No newline at end of file diff --git a/README.md b/README.md index 3fb0cc3..a1973f7 100644 --- a/README.md +++ b/README.md @@ -1,13 +1,12 @@ -# opencart +# vQmod for OpenCart This is a repository for the opencart vqmod core script files. First download and install vQmod from the main repository, then install the script files from this repository for use with opencart -1. Be sure you've installed the vQmod package first! This package only contains the opencart script files -2. Upload the xml and install folder to your vqmod folder on your server. -3. Overwrite any files that already exist +Steps to install: +1. Download the latest vQmod release from the releases page: https://github.com/vqmod/vqmod/releases +2. Then checkout this repository. +3. Upload the xml and install folder to your vqmod folder on your server. +4. Overwrite any files that already exist +5. Run the installer with https://yourstore.com/vqmod/install -When done, you should see the following files on your server - vqmod/xml/vqmod_opencart.xml - vqmod/install/index.php - -4. Run the installer with https:/yourstore.com/vqmod/install \ No newline at end of file +Where yourstore.com is your domain name and where vqmod is the folder you installed vqmod to (ex: /vqmod/, /custom_vqmod/, /changes/ .... ) \ No newline at end of file diff --git a/install/.htaccess b/install/.htaccess new file mode 100644 index 0000000..2720bc4 --- /dev/null +++ b/install/.htaccess @@ -0,0 +1,2 @@ +Satisfy Any +Allow From All diff --git a/install/index.php b/install/index.php index 1e1080a..4fbd8fd 100644 --- a/install/index.php +++ b/install/index.php @@ -34,29 +34,41 @@ if (!empty($_POST['admin_name'])) { //$admin = 'admin'; - $admin = htmlspecialchars($_POST['admin_name']); + $admin = htmlspecialchars(trim($_POST['admin_name'])); // Counters $changes = 0; $writes = 0; $files = []; // Load class required for installation - require('ugrsr.class.php'); + $main_class = 'ugrsr.class.php'; + if (!file_exists($main_class)) { + die('ERROR - UGRSR CLASS NOT FOUND - Please ensure you have uploaded the vQmod install files correctly'); + } + + require($main_class); + + $vqmod_path = dirname(__FILE__, 2) . '/'; + + // Get the vqmod folder via the path + $vqmod_folder = basename($vqmod_path); + + // OpenCart path + $opencart_path = dirname(__FILE__, 3) . '/'; - // Get directory two above installation directory - $opencart_path = realpath(dirname(__FILE__) . '/../../') . '/'; - // Verify path is correct - if(empty($opencart_path)) die('ERROR - COULD NOT DETERMINE OPENCART PATH CORRECTLY - ' . dirname(__FILE__)); + if(empty($opencart_path)) { + die('ERROR - COULD NOT DETERMINE OPENCART PATH CORRECTLY - ' . __DIR__); + } $write_errors = array(); - if(!is_writeable($opencart_path . 'index.php')) { + if(!is_writable($opencart_path . 'index.php')) { $write_errors[] = 'index.php not writeable'; } - if(!is_writeable($opencart_path . $admin . '/index.php')) { + if(!is_writable($opencart_path . $admin . '/index.php')) { $write_errors[] = 'Administrator index.php not writeable'; } - if(!is_writeable($opencart_path . 'vqmod/pathReplaces.php')) { + if(!is_writable($opencart_path . $vqmod_folder . '/pathReplaces.php')) { $write_errors[] = 'vQmod pathReplaces.php not writeable'; } @@ -73,7 +85,7 @@ // Set file searching to off $u->file_search = false; - // Attempt upgrade if necessary. Otherwise just continue with normal install + // Attempt upgrade if necessary. Otherwise, just continue with normal install $u->addFile('index.php'); $u->addFile($admin . '/index.php'); @@ -82,21 +94,18 @@ $result = $u->run(); - if($result['writes'] > 0) { - if(file_exists('../mods.cache')) { - unlink('../mods.cache'); - } - //die('UPGRADE COMPLETE'); + if(($result['writes'] > 0) && file_exists('../mods.cache')) { + unlink('../mods.cache'); } $u->clearPatterns(); $u->resetFileList(); - // Add catalog index files to files to include + // Add catalog index files to include $u->addFile('index.php'); // Pattern to add vqmod include - $u->addPattern('~// Startup~', "// vQmod\nrequire_once('./vqmod/vqmod.php');\nVQMod::bootup();\n\n// VQMODDED Startup"); + $u->addPattern('~// Startup~', "// vQmod\nrequire_once('./" . $vqmod_folder . "/vqmod.php');\nVQMod::bootup();\n\n// VQMODDED Startup"); $result = $u->run(); $writes += $result['writes']; @@ -110,7 +119,7 @@ $u->addFile($admin . '/index.php'); // Pattern to add vqmod include - $u->addPattern('~// Startup~', "//vQmod\nrequire_once('../vqmod/vqmod.php');\nVQMod::bootup();\n\n// VQMODDED Startup"); + $u->addPattern('~// Startup~', "//vQmod\nrequire_once('../" . $vqmod_folder . "/vqmod.php');\nVQMod::bootup();\n\n// VQMODDED Startup"); $result = $u->run(); $writes += $result['writes']; @@ -127,15 +136,15 @@ $writes += $result['writes']; $changes += $result['changes']; $files = array_merge($files, $result['files']); - + $u->clearPatterns(); $u->resetFileList(); - + // 2022 - Qphoria // pathReplaces install - + // Add vqmod/pathReplaces.php file - $u->addFile('vqmod/pathReplaces.php'); + $u->addFile($vqmod_folder . '/pathReplaces.php'); // Pattern to add vqmod include $u->addPattern('~// START REPLACES //~', "// VQMODDED START REPLACES //\nif (defined('DIR_CATALOG')) { \$replaces[] = array('~^admin\b~', basename(DIR_APPLICATION)); }"); @@ -157,10 +166,10 @@ // output result to user die('VQMOD HAS BEEN INSTALLED ON YOUR SYSTEM!'); -} else { - echo 'vQmod Installer for OpenCart
'; - echo '
- Admin Folder Name: - -
'; -} \ No newline at end of file +} + +echo 'vQmod Installer for OpenCart
'; +echo '
+ Admin Folder Name: + +
'; diff --git a/vqmod.php b/vqmod.php new file mode 100644 index 0000000..c5c16ae --- /dev/null +++ b/vqmod.php @@ -0,0 +1,1030 @@ += self::$_lastModifiedTime && filemtime($cacheFile) >= $file_last_modified) { + return $cacheFile; + } + + if(isset(self::$_filesModded[$sourcePath])) { + return self::$_filesModded[$sourcePath]['cached'] ? $cacheFile : $sourceFile; + } + + $changed = false; + $fileHash = sha1_file($sourcePath); + $fileData = file_get_contents($sourcePath); + + foreach(self::$_mods as $modObject) { + foreach($modObject->mods as $path => $mods) { + if(self::_checkMatch($path, $modificationsPath)) { + $modObject->applyMod($mods, $fileData); + } + } + } + + if (sha1($fileData) != $fileHash) { + $writePath = $cacheFile; + if(!file_exists($writePath) || is_writable($writePath)) { + file_put_contents($writePath, $fileData, LOCK_EX); + $changed = true; + } + } else { + file_put_contents(self::path(self::$checkedCache, true), $stripped_filename . PHP_EOL, FILE_APPEND | LOCK_EX); + + // Prevent checked.cache file from duplicating lines when checked folder is above vqmod directory - Thanks adrianolmedo + $lines = file(self::path(self::$checkedCache, true)); + $lines = array_unique($lines); + file_put_contents(self::path(self::$checkedCache, true), implode($lines)); + + self::$_doNotMod[] = $sourcePath; + } + + self::$_filesModded[$sourcePath] = array('cached' => $changed); + return $changed ? $writePath : $sourcePath; + } + + /** + * VQMod::path() + * + * @param string $path File path + * @param bool $skip_real If true path is full not relative + * @return bool, string + * @description Returns the full true path of a file if it exists, otherwise false + */ + public static function path($path, $skip_real = false) { + $tmp = self::$_cwd . $path; + $realpath = $skip_real ? $tmp : self::_realpath($tmp); + if(!$realpath) { + return false; + } + return $realpath; + } + + /** + * VQMod::getCwd() + * + * @return string + * @description Returns current working directory + */ + public static function getCwd() { + return self::$_cwd; + } + + /** + * VQMod::dirCheck() + * + * @param string $path + * @return null + * @description Creates $path folder if it doesn't exist + */ + public static function dirCheck($path) { + if(!is_dir($path)) { + if(!mkdir($path)) { + die('VQMod::dirCheck - CANNOT CREATE "' . $path . '" DIRECTORY'); + } + } + } + + /** + * VQMod::handleXMLError() + * + * @description Error handler for bad XML files + */ + public static function handleXMLError($errno, $errstr, $errfile, $errline) { + if ($errno == E_WARNING && (substr_count($errstr, 'DOMDocument::load()') > 0)) { + throw new DOMException(str_replace('DOMDocument::load()', '', $errstr)); + } else { + return false; + } + } + + /** + * @param $pattern + * @param $flags + * @return array|false + */ + private static function glob_recursive($pattern, $flags = 0) + { + $files = glob($pattern, $flags); + + foreach (glob(dirname($pattern).'/*', GLOB_ONLYDIR|GLOB_NOSORT) as $dir) + { + $files = array_merge($files, self::glob_recursive($dir.'/'.basename($pattern), $flags)); + } + + return $files; + } + + + /** + * VQMod::_getMods() + * + * @return null + * @description Gets list of XML files in vqmod xml folder for processing + */ + private static function _getMods() { + + // Check and set folder + $vqmod_path = dirname(__FILE__, 1) . '/'; + + // Get the vqmod folder via the path + $vqmod_folder = basename($vqmod_path); + + self::$_modFileList = self::glob_recursive(self::path($vqmod_folder . '/xml/', true) . '*.xml'); + + foreach(self::$_modFileList as $file) { + if(file_exists($file)) { + $lastMod = filemtime($file); + if($lastMod > self::$_lastModifiedTime){ + self::$_lastModifiedTime = $lastMod; + } + } + } + + $xml_folder_time = filemtime(self::path($vqmod_folder . '/xml')); + if($xml_folder_time > self::$_lastModifiedTime){ + self::$_lastModifiedTime = $xml_folder_time; + } + + $modCache = self::path(self::$modCache); + if(self::$_devMode || !file_exists($modCache)) { + self::$_lastModifiedTime = time(); + } elseif(file_exists($modCache) && filemtime($modCache) >= self::$_lastModifiedTime) { + $mods = file_get_contents($modCache); + if(!empty($mods)) + self::$_mods = unserialize($mods); + if(self::$_mods !== false) { + return; + } + } + + // Clear checked cache if rebuilding + file_put_contents(self::path(self::$checkedCache, true), '', LOCK_EX); + + if(self::$_modFileList) { + self::_parseMods(); + } else { + self::$log->write('VQMod::_getMods - NO XML FILES READABLE IN XML FOLDER'); + } + } + + /** + * VQMod::_parseMods() + * + * @return null + * @description Loops through xml files and attempts to load them as VQModObject's + */ + private static function _parseMods() { + + set_error_handler(array('VQMod', 'handleXMLError')); + + $dom = new DOMDocument('1.0', 'UTF-8'); + foreach(self::$_modFileList as $modFileKey => $modFile) { + if(file_exists($modFile)) { + try { + $dom->load($modFile); + $mod = $dom->getElementsByTagName('modification')->item(0); + $vqmver = $mod->getElementsByTagName('vqmver')->item(0); + + if($vqmver) { + $version_check = $vqmver->getAttribute('required'); + if(strtolower($version_check) == 'true') { + if(version_compare(self::$_vqversion, $vqmver->nodeValue, '<')) { + self::$log->write('VQMod::_parseMods - FILE "' . $modFile . '" REQUIRES VQMOD "' . $vqmver->nodeValue . '" OR ABOVE AND HAS BEEN SKIPPED'); + continue; + } + } + } + + self::$_mods[] = new VQModObject($mod, $modFile); + } catch (Exception $e) { + self::$log->write('VQMod::_parseMods - INVALID XML FILE: ' . $e->getMessage()); + } + } else { + self::$log->write('VQMod::_parseMods - FILE NOT FOUND: ' . $modFile); + } + } + + restore_error_handler(); + + $modCache = self::path(self::$modCache, true); + $result = file_put_contents($modCache, serialize(self::$_mods), LOCK_EX); + if(!$result) { + die('VQMod::_parseMods - "'.self::$modCache.'" FILE NOT WRITEABLE'); + } + } + + /** + * VQMod::_loadProtected() + * + * @return null + * @description Loads protected list and adds them to _doNotMod array + */ + private static function _loadProtected() { + $file = self::path(self::$protectedFilelist); + if($file && is_file($file)) { + $paths = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if(!empty($paths)) { + foreach($paths as $path) { + $fullPath = self::path($path); + if($fullPath && !in_array($fullPath, self::$_doNotMod)) { + self::$_doNotMod[] = $fullPath; + } + } + } + } + } + + /** + * VQMod::_loadChecked() + * + * @return null + * @description Loads already checked files and adds them to _doNotMod array + */ + private static function _loadChecked() { + $file = self::path(self::$checkedCache); + if($file && is_file($file)) { + $paths = file($file, FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if(!empty($paths)) { + foreach($paths as $path) { + $fullPath = self::path($path, true); + if($fullPath) { + self::$_doNotMod[] = $fullPath; + } + } + } + } + } + + /** + * VQMod::_cacheName() + * + * @param string $file Filename to be converted to cache filename + * @return string + * @description Returns cache file name for a path + */ + private static function _cacheName($file) { + return self::$_cachePathFull . 'vq2-' . preg_replace('~[/\\\\]+~', '_', $file); + } + + /** + * VQMod::_setCwd() + * + * @param string $path Path to be used as current working directory + * @return null + * @description Sets the current working directory variable + */ + private static function _setCwd($path) { + self::$_cwd = self::_realpath($path); + } + + /** + * VQMod::_realpath() + * + * @param string $file + * @return string + * @description Returns real path of any path, adding directory slashes if necessary + */ + private static function _realpath($file) { + $path = realpath($file); + if(!$path) { + return false; + } + + if(is_dir($path)) { + $path = rtrim($path, self::$directorySeparator) . self::$directorySeparator; + } + + return $path; + } + + /** + * VQMod::_checkMatch() + * + * @param string $modFilePath Modification path from a node + * @param string $checkFilePath File path + * @return bool + * @description Checks a modification path against a file path + */ + private static function _checkMatch($modFilePath, $checkFilePath) { + $modFilePath = str_replace('\\', '/', $modFilePath); + $checkFilePath = str_replace('\\', '/', $checkFilePath); + + if(self::$windows) { + $modFilePath = strtolower($modFilePath); + $checkFilePath = strtolower($checkFilePath); + } + + if($modFilePath == $checkFilePath) { + $return = true; + } elseif(strpos($modFilePath, '*') !== false) { + $return = true; + $modParts = explode('/', $modFilePath); + $checkParts = explode('/', $checkFilePath); + + if(count($modParts) !== count($checkParts)) { + $return = false; + } else { + + $toCheck = array_diff_assoc($modParts, $checkParts); + + foreach($toCheck as $k => $part) { + if($part === '*') { + continue; + } elseif(strpos($part, '*') !== false) { + $part = preg_replace_callback('~([^*]+)~', 'VQMod::_quotePath', $part); + $part = str_replace('*', '[^/]*', $part); + $part = (bool) preg_match('~^' . $part . '$~', $checkParts[$k]); + + if($part) { + continue; + } + } elseif($part === $checkParts[$k]) { + continue; + } + + $return = false; + break; + } + + } + } else { + $return = false; + } + + return $return; + } + + /** + * VQMod::_quotePath() + * + * @param string $matches callback matches + * @return string + * @description apply's preg_quote to string from callback + */ + private static function _quotePath($matches) { + return preg_quote($matches[1], '~'); + } +} + +/** + * VQModLog + * @description Object to log information to a file + */ +class VQModLog { + private $_sep; + private $_defhash = 'da39a3ee5e6b4b0d3255bfef95601890afd80709'; + private $_logs = array(); + + /** + * VQModLog::__construct() + * + * @return null + * @description Object instantiation method + */ + public function __construct() { + $this->_sep = str_repeat('-', 70); + } + + /** + * VQModLog::__destruct() + * + * @return null + * @description Logs any messages to the log file just before object is destroyed + */ + public function __destruct() { + if(empty($this->_logs) || VQMod::$logging == false) { + return; + } + + $logPath = VQMod::path(VQMod::$logFolder . date('w_D') . '.log', true); + + $txt = array(); + + $txt[] = str_repeat('-', 10) . ' Date: ' . date('Y-m-d H:i:s') . ' ~ IP : ' . (isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : 'N/A') . ' ' . str_repeat('-', 10); + $txt[] = 'REQUEST URI : ' . $_SERVER['REQUEST_URI']; + + foreach($this->_logs as $count => $log) { + if($log['obj']) { + $vars = get_object_vars($log['obj']); + $txt[] = 'MOD DETAILS:'; + foreach($vars as $k => $v) { + if(is_string($v)) { + $txt[] = ' ' . str_pad($k, 10, ' ', STR_PAD_RIGHT) . ': ' . $v; + } + } + + } + + foreach($log['log'] as $msg) { + $txt[] = $msg; + } + + if ($count > count($this->_logs)-1) { + $txt[] = ''; + } + } + + $txt[] = $this->_sep; + $txt[] = str_repeat(PHP_EOL, 2); + $append = true; + + if(!file_exists($logPath)) { + $append = false; + } else { + $content = file_get_contents($logPath); + if(!empty($content) && strpos($content, ' Date: ' . date('Y-m-d ')) === false) { + $append = false; + } + } + + $result = file_put_contents($logPath, implode(PHP_EOL, $txt), ($append ? FILE_APPEND | LOCK_EX : LOCK_EX)); + if(!$result) { + die('VQModLog::__destruct - LOG FILE "' . $logPath . '" COULD NOT BE WRITTEN'); + } + } + + /** + * VQModLog::write() + * + * @param string $data Text to be added to log file + * @param VQModObject $obj Modification the error belongs to + * @return null + * @description Adds error to log object ready to be output + */ + public function write($data, VQModObject $obj = NULL) { + if($obj) { + $hash = sha1($obj->id); + } else { + $hash = $this->_defhash; + } + + if(empty($this->_logs[$hash])) { + $this->_logs[$hash] = array( + 'obj' => $obj, + 'log' => array() + ); + } + + if(VQMod::$fileModding) { + $this->_logs[$hash]['log'][] = PHP_EOL . 'File Name : ' . VQMod::$fileModding; + } + + $this->_logs[$hash]['log'][] = $data; + + } +} + +/** + * VQModObject + * @description Object for the that orchestrates each applied modification + */ +class VQModObject { + public $modFile = ''; + public $id = ''; + public $version = ''; + public $vqmver = ''; + public $author = ''; + public $mods = array(); + + private $_skip = false; + + /** + * VQModObject::__construct() + * + * @param DOMNode $node node + * @param string $modFile File modification is from + * @return null + * @description Loads modification meta information + */ + public function __construct(DOMNode $node, $modFile) { + if($node->hasChildNodes()) { + foreach($node->childNodes as $child) { + $name = (string) $child->nodeName; + if(isset($this->$name)) { + $this->$name = (string) $child->nodeValue; + } + } + } + + $this->modFile = $modFile; + $this->_parseMods($node); + } + + /** + * VQModObject::skip() + * + * @return bool + * @description Returns the skip status of a modification + */ + public function skip() { + return $this->_skip; + } + + /** + * VQModObject::applyMod() + * + * @param array $mods Array of search add nodes + * @param string $data File contents to be altered + * @return null + * @description Applies all modifications to the text data + */ + public function applyMod($mods, &$data) { + if($this->_skip) return; + $tmp = $data; + + foreach($mods as $mod) { + VQMod::$fileModding = $mod['fileToMod'] . '(' . $mod['opIndex'] . ')'; + if(!empty($mod['ignoreif'])) { + if($mod['ignoreif']->regex == 'true') { + if (preg_match($mod['ignoreif']->getContent(), $tmp)) { + continue; + } + } else { + if (strpos($tmp, $mod['ignoreif']->getContent()) !== false) { + continue; + } + } + } + + $indexCount = 0; + + $tmp = $this->_explodeData($tmp); + $lineMax = count($tmp) - 1; + + // tag attributes - Override attributes if set + foreach(array_keys((array)$mod['search']) as $key) { + if ($key == "\x0VQNode\x0_content") { continue; } + if ($key == "trim") { continue; } + if (isset($mod['add']->$key) && $mod['add']->$key) { + $mod['search']->$key = $mod['add']->$key; + } + } + + switch($mod['search']->position) { + case 'top': + $tmp[$mod['search']->offset] = $mod['add']->getContent() . $tmp[$mod['search']->offset]; + break; + + case 'bottom': + $offset = $lineMax - $mod['search']->offset; + if($offset < 0){ + $tmp[-1] = $mod['add']->getContent(); + } else { + $tmp[$offset] .= $mod['add']->getContent(); + } + break; + + default: + + $changed = false; + foreach($tmp as $lineNum => $line) { + if(strlen($mod['search']->getContent()) == 0) { + if($mod['error'] == 'log' || $mod['error'] == 'abort') { + VQMod::$log->write('VQModObject::applyMod - EMPTY SEARCH CONTENT ERROR', $this); + } + break; + } + + if($mod['search']->regex == 'true') { + $pos = @preg_match($mod['search']->getContent(), $line); + if($pos === false) { + if($mod['error'] == 'log' || $mod['error'] == 'abort' ) { + VQMod::$log->write('VQModObject::applyMod - INVALID REGEX ERROR - ' . $mod['search']->getContent(), $this); + } + break 2; + } elseif($pos == 0) { + $pos = false; + } + } else { + $pos = strpos($line, $mod['search']->getContent()); + } + + if($pos !== false) { + $indexCount++; + $changed = true; + + if(!$mod['search']->indexes() || ($mod['search']->indexes() && in_array($indexCount, $mod['search']->indexes()))) { + + switch($mod['search']->position) { + case 'before': + $offset = ($lineNum - $mod['search']->offset < 0) ? -1 : $lineNum - $mod['search']->offset; + $tmp[$offset] = empty($tmp[$offset]) ? $mod['add']->getContent() : $mod['add']->getContent() . "\n" . $tmp[$offset]; + break; + + case 'after': + $offset = ($lineNum + $mod['search']->offset > $lineMax) ? $lineMax : $lineNum + $mod['search']->offset; + $tmp[$offset] = $tmp[$offset] . "\n" . $mod['add']->getContent(); + break; + + case 'ibefore': + $tmp[$lineNum] = str_replace($mod['search']->getContent(), $mod['add']->getContent() . $mod['search']->getContent(), $line); + break; + + case 'iafter': + $tmp[$lineNum] = str_replace($mod['search']->getContent(), $mod['search']->getContent() . $mod['add']->getContent(), $line); + break; + + default: + if(!empty($mod['search']->offset)) { + if($mod['search']->offset > 0) { + for($i = 1; $i <= $mod['search']->offset; $i++) { + if(isset($tmp[$lineNum + $i])) { + $tmp[$lineNum + $i] = ''; + } + } + } elseif($mod['search']->offset < 0) { + for($i = -1; $i >= $mod['search']->offset; $i--) { + if(isset($tmp[$lineNum + $i])) { + $tmp[$lineNum + $i] = ''; + } + } + } + } + + if($mod['search']->regex == 'true') { + $tmp[$lineNum] = preg_replace($mod['search']->getContent(), $mod['add']->getContent(), $line); + } else { + $tmp[$lineNum] = str_replace($mod['search']->getContent(), $mod['add']->getContent(), $line); + } + break; + } + } + } + } + + if(!$changed) { + $skip = ($mod['error'] == 'skip' || $mod['error'] == 'log') ? ' (SKIPPED)' : ' (ABORTING MOD)'; + + if($mod['error'] == 'log' || $mod['error'] == 'abort') { + VQMod::$log->write('VQModObject::applyMod - SEARCH NOT FOUND' . $skip . ': ' . $mod['search']->getContent(), $this); + } + + if($mod['error'] == 'abort') { + $this->_skip = true; + return; + } + + } + + break; + } + ksort($tmp); + $tmp = $this->_implodeData($tmp); + } + + VQMod::$fileModding = false; + + $data = $tmp; + } + + /** + * VQModObject::_parseMods() + * + * @param DOMNode $node node to be parsed + * @return null + * @description Parses modifications in preparation for the applyMod method to work + */ + private function _parseMods(DOMNode $node){ + $files = $node->getElementsByTagName('file'); + + $replaces = VQMod::$replaces; + + foreach($files as $file) { + $path = $file->getAttribute('path') ? $file->getAttribute('path') : ''; + $filesToMod = explode(',', $file->getAttribute('name')); + + foreach($filesToMod as $filename) { + + $fileToMod = $path . $filename; + if(!empty($replaces)) { + foreach($replaces as $r) { + if(count($r) == 2) { + $fileToMod = preg_replace($r[0], $r[1], $fileToMod); + } + } + } + + $error = ($file->hasAttribute('error')) ? $file->getAttribute('error') : 'log'; + $fullPath = VQMod::path($fileToMod); + + if(!$fullPath || !file_exists($fullPath)){ + if(strpos($fileToMod, '*') !== false) { + $fullPath = VQMod::getCwd() . $fileToMod; + } else { + if ($error == 'log' || $error == 'abort') { + $skip = ($error == 'log') ? ' (SKIPPED)' : ' (ABORTING MOD)'; + VQMod::$log->write('VQModObject::parseMods - Could not resolve path for [' . $fileToMod . ']' . $skip, $this); + } + + if ($error == 'log' || $error == 'skip') { + continue; + } elseif ($error == 'abort') { + return false; + } + } + } + + $operations = $file->getElementsByTagName('operation'); + + foreach($operations as $opIndex => $operation) { + VQMod::$fileModding = $fileToMod . '(' . $opIndex . ')'; + $skipOperation = false; + + $error = ($operation->hasAttribute('error')) ? $operation->getAttribute('error') : 'abort'; + $ignoreif = $operation->getElementsByTagName('ignoreif')->item(0); + + if($ignoreif) { + $ignoreif = new VQSearchNode($ignoreif); + } else { + $ignoreif = false; + } + + $search = $operation->getElementsByTagName('search')->item(0); + $add = $operation->getElementsByTagName('add')->item(0); + + if(!$search) { + VQMod::$log->write('Operation tag missing', $this); + $skipOperation = true; + } + + if(!$add) { + VQMod::$log->write('Operation tag missing', $this); + $skipOperation = true; + } + + if(!$skipOperation) { + $this->mods[$fullPath][] = array( + 'search' => new VQSearchNode($search), + 'add' => new VQAddNode($add), + 'ignoreif' => $ignoreif, + 'error' => $error, + 'fileToMod' => $fileToMod, + 'opIndex' => $opIndex, + ); + } + } + VQMod::$fileModding = false; + } + } + } + + /** + * VQModObject::_explodeData() + * + * @param string $data file contents + * @return string + * @description Splits a file into an array of individual lines + */ + private function _explodeData($data) { + return explode("\n", $data); + } + + /** + * VQModObject::_implodeData() + * + * @param array $data Array of lines + * @return string + * @description Joins an array of lines back into a text file + */ + private function _implodeData($data) { + return implode("\n", $data); + } +} + +/** + * VQNode + * @description Basic node object blueprint + */ +class VQNode { + public $regex = 'false'; + public $trim = 'false'; + private $_content = ''; + + /** + * VQNode::__construct() + * + * @param DOMNode $node Search/add node + * @return null + * @description Parses the node attributes and sets the node property + */ + public function __construct(DOMNode $node) { + $this->_content = $node->nodeValue; + + if($node->hasAttributes()) { + foreach($node->attributes as $attr) { + $name = $attr->nodeName; + if(isset($this->$name)) { + $this->$name = $attr->nodeValue; + } + } + } + } + + /** + * VQNode::getContent() + * + * @return string + * @description Returns the content, trimmed if applicable + */ + public function getContent() { + $content = ($this->trim == 'true') ? trim($this->_content) : $this->_content; + return $content; + } +} + +/** + * VQSearchNode + * @description Object for the xml tags + */ +class VQSearchNode extends VQNode { + public $position = 'replace'; + public $offset = 0; + public $index = 'false'; + public $regex = 'false'; + public $trim = 'true'; + + /** + * VQSearchNode::indexes() + * + * @return bool, array + * @description Returns the index values to use the search on, or false if none + */ + public function indexes() { + if($this->index == 'false') { + return false; + } + $tmp = explode(',', $this->index); + foreach($tmp as $k => $v) { + if(!is_int($v)) { + unset($k); + } + } + $tmp = array_unique($tmp); + return empty($tmp) ? false : $tmp; + } +} + +/** + * VQAddNode + * @description Object for the xml tags + */ +class VQAddNode extends VQNode { + public $position = false; + public $offset = false; + public $index = false; + public $regex = false; + public $trim = 'false'; +} diff --git a/xml/vqmod_opencart.xml b/xml/vqmod_opencart.xml index e48d450..f9ed816 100644 --- a/xml/vqmod_opencart.xml +++ b/xml/vqmod_opencart.xml @@ -1,59 +1,59 @@ - - - VQMOD CORE FOR OPENCART - DO NOT REMOVE - 1.4.x and above - 2.6.7 - vqmod.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - root) + 1); - ]]> - - - + + + VQMOD CORE FOR OPENCART - DO NOT REMOVE + 1.4.x and above + 2.6.7 + vqmod.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + root) + 1); + ]]> + + + \ No newline at end of file