Changeset 3354997
- Timestamp:
- 09/03/2025 03:20:28 AM (7 months ago)
- Location:
- linguise
- Files:
-
- 4 added
- 24 edited
- 1 copied
-
tags/2.1.67 (copied) (copied from linguise/trunk)
-
tags/2.1.67/linguise.php (modified) (1 diff)
-
tags/2.1.67/readme.txt (modified) (2 diffs)
-
tags/2.1.67/src/FragmentHandler.php (modified) (3 diffs)
-
tags/2.1.67/src/constants.php (modified) (1 diff)
-
tags/2.1.67/src/thirdparty/wc/gateway-stripe.php (modified) (4 diffs)
-
tags/2.1.67/vendor/composer/autoload_classmap.php (modified) (1 diff)
-
tags/2.1.67/vendor/composer/autoload_static.php (modified) (1 diff)
-
tags/2.1.67/vendor/composer/installed.json (modified) (2 diffs)
-
tags/2.1.67/vendor/composer/installed.php (modified) (3 diffs)
-
tags/2.1.67/vendor/linguise/script-php/.version (modified) (1 diff)
-
tags/2.1.67/vendor/linguise/script-php/src/Helper.php (modified) (2 diffs)
-
tags/2.1.67/vendor/linguise/script-php/src/HttpResponse.php (added)
-
tags/2.1.67/vendor/linguise/script-php/src/Management.php (modified) (21 diffs)
-
tags/2.1.67/vendor/linguise/script-php/src/OobeManager.php (added)
-
trunk/linguise.php (modified) (1 diff)
-
trunk/readme.txt (modified) (2 diffs)
-
trunk/src/FragmentHandler.php (modified) (3 diffs)
-
trunk/src/constants.php (modified) (1 diff)
-
trunk/src/thirdparty/wc/gateway-stripe.php (modified) (4 diffs)
-
trunk/vendor/composer/autoload_classmap.php (modified) (1 diff)
-
trunk/vendor/composer/autoload_static.php (modified) (1 diff)
-
trunk/vendor/composer/installed.json (modified) (2 diffs)
-
trunk/vendor/composer/installed.php (modified) (3 diffs)
-
trunk/vendor/linguise/script-php/.version (modified) (1 diff)
-
trunk/vendor/linguise/script-php/src/Helper.php (modified) (2 diffs)
-
trunk/vendor/linguise/script-php/src/HttpResponse.php (added)
-
trunk/vendor/linguise/script-php/src/Management.php (modified) (21 diffs)
-
trunk/vendor/linguise/script-php/src/OobeManager.php (added)
Legend:
- Unmodified
- Added
- Removed
-
linguise/tags/2.1.67/linguise.php
r3350018 r3354997 5 5 * Plugin URI: https://www.linguise.com/ 6 6 * Description: Linguise translation plugin 7 * Version:2.1.6 67 * Version:2.1.67 8 8 * Text Domain: linguise 9 9 * Domain Path: /languages -
linguise/tags/2.1.67/readme.txt
r3350018 r3354997 4 4 Requires at least: 4.0 5 5 Tested up to: 6.8 6 Stable tag:2.1.6 66 Stable tag:2.1.67 7 7 Requires PHP: 7.0 8 8 License: GPLv2 or later … … 104 104 105 105 == Changelog == 106 = 2.1.67 = 107 - Fix: WooCommerce Stripe Gateway Issue: the credit card form not showing up 108 106 109 = 2.1.66 = 107 110 - Fix: Double slash in redirect URL -
linguise/tags/2.1.67/src/FragmentHandler.php
r3339453 r3354997 206 206 'key' => 'paymentMethodData.*?stripe\.plugin_url', 207 207 'mode' => 'regex_full', 208 'kind' => 'deny', 209 ], 210 [ 211 'key' => 'currency', 212 'mode' => 'exact', 208 213 'kind' => 'deny', 209 214 ], … … 1292 1297 1293 1298 $replaced_json = $json_data->getJson(); 1299 1300 if (function_exists('apply_filters')) { 1301 $replaced_json = apply_filters('linguise_after_apply_translated_fragments_override', $fragment_name, $replaced_json); 1302 } 1303 1294 1304 if ($should_encode) { 1295 1305 $replaced_json = rawurlencode($replaced_json); … … 1392 1402 1393 1403 $replaced_json = self::applyTranslatedFragmentsForAuto(json_decode('{' . $html_matches[3] . '}', true), $fragment_list['fragments']); 1404 1405 if (function_exists('apply_filters')) { 1406 $replaced_json = apply_filters('linguise_after_apply_translated_fragments_auto', $fragment_name, $replaced_json); 1407 } 1408 1394 1409 if ($replaced_json === false) { 1395 1410 throw new \LogicException('FragmentHandler -> Injection -> ' . $fragment_name . '/' . $fragment_param . ' -> JSON data is empty!'); -
linguise/tags/2.1.67/src/constants.php
r3350018 r3354997 1 1 <?php 2 2 if (!defined('LINGUISE_SCRIPT_TRANSLATION_VERSION')) { 3 define('LINGUISE_SCRIPT_TRANSLATION_VERSION', 'wordpress_plugin/2.1.6 6');3 define('LINGUISE_SCRIPT_TRANSLATION_VERSION', 'wordpress_plugin/2.1.67'); 4 4 } 5 5 6 6 if (!defined('LINGUISE_VERSION')) { 7 define('LINGUISE_VERSION', '2.1.6 6');7 define('LINGUISE_VERSION', '2.1.67'); 8 8 } -
linguise/tags/2.1.67/src/thirdparty/wc/gateway-stripe.php
r3291510 r3354997 22 22 23 23 /** 24 * A collection of fragment keys that will be translated 25 * 26 * @var array{key:string, mode:'exact'|'path'|'wildcard'|'regex'|'regex_full',kind:'allow'|'deny'} 27 */ 28 protected static $fragment_keys = [ 29 [ 30 'key' => 'allowed_shipping_countries\..*', 31 'mode' => 'regex_full', 32 'kind' => 'deny', 33 ], 34 [ 35 'key' => 'checkout\.((?:country|currency)_code|needs_shipping)$', 36 'mode' => 'regex_full', 37 'kind' => 'deny', 38 ], 39 [ 40 'key' => 'baseLocation\.(country|state)$', 41 'mode' => 'regex_full', 42 'kind' => 'deny', 43 ], 44 [ 45 'key' => 'blocksAppearance\.rules\..*', 46 'mode' => 'regex_full', 47 'kind' => 'deny', 48 ] 49 ]; 50 51 /** 24 52 * Decides if the integration should be loaded. 25 53 * … … 42 70 // Block checkout 43 71 add_filter('wc_stripe_params', [$this, 'hookTranslateParams'], 10, 1); 72 73 add_filter('linguise_after_apply_translated_fragments_auto', [$this, 'restoreOriginalConfigClassicCheckout'], 10, 2); 74 add_filter('linguise_after_apply_translated_fragments_override', [$this, 'restoreOriginalConfigBlockCheckout'], 10, 2); 44 75 } 45 76 … … 55 86 // Block checkout 56 87 remove_filter('wc_stripe_params', [$this, 'hookTranslateParams'], 10); 88 89 remove_filter('linguise_after_apply_translated_fragments_auto', [$this, 'restoreOriginalConfigClassicCheckout'], 10, 2); 90 remove_filter('linguise_after_apply_translated_fragments_override', [$this, 'restoreOriginalConfigBlockCheckout'], 10, 2); 57 91 } 58 92 … … 149 183 return $params; 150 184 } 185 186 /** 187 * Restore the original block appearance structure that was accidentally 188 * changed into an array 189 * 190 * @param string $fragment_name The name of the fragment being translated. 191 * @param array $replaced_json The fragment being translated. 192 * 193 * @return array The original block appearance structure. 194 */ 195 public function restoreOriginalConfigClassicCheckout($fragment_name, $replaced_json) 196 { 197 if ($fragment_name === 'wc-stripe-upe-classic') { 198 if (array_key_exists('blocksAppearance', $replaced_json)) { 199 $replaced_json['blocksAppearance'] = $this->arrayToObject($replaced_json['blocksAppearance']); 200 } 201 } 202 203 return $replaced_json; 204 } 205 206 /** 207 * Restore the original block appearance structure that was accidentally 208 * changed into an array 209 * 210 * @param string $fragment_name The name of the fragment being translated. 211 * @param string $replaced_json The fragment being translated. 212 * 213 * @return array The original block appearance structure. 214 */ 215 public function restoreOriginalConfigBlockCheckout($fragment_name, $replaced_json) 216 { 217 $replaced_json = json_decode($replaced_json); 218 if ($fragment_name === 'wc-settings-encoded') { 219 if (isset($replaced_json->paymentMethodData->stripe->blocksAppearance)) { 220 $rules = $replaced_json->paymentMethodData->stripe->blocksAppearance->rules; 221 $replaced_json->paymentMethodData->stripe->blocksAppearance->rules = $this->arrayToObject($rules); 222 } 223 } 224 225 return json_encode($replaced_json); 226 } 227 228 /** 229 * Recursive convert an array into an object. 230 * 231 * @param array $data The data to convert. 232 * 233 * @return object The converted object. 234 */ 235 public function arrayToObject($data) 236 { 237 if (is_array($data)) { 238 if (empty($data)) { 239 return new \stdClass(); // convert empty array to object 240 } 241 foreach ($data as $key => $value) { 242 $data[$key] = $this->arrayToObject($value); 243 } 244 return (object) $data; 245 } elseif ($data instanceof \stdClass) { 246 foreach ($data as $key => $value) { 247 $data->$key = $this->arrayToObject($value); 248 } 249 return $data; 250 } 251 252 return $data; 253 } 151 254 } -
linguise/tags/2.1.67/vendor/composer/autoload_classmap.php
r3316163 r3354997 39 39 'Linguise\\Vendor\\Linguise\\Script\\Core\\Helper' => $vendorDir . '/linguise/script-php/src/Helper.php', 40 40 'Linguise\\Vendor\\Linguise\\Script\\Core\\Hook' => $vendorDir . '/linguise/script-php/src/Hook.php', 41 'Linguise\\Vendor\\Linguise\\Script\\Core\\HttpResponse' => $vendorDir . '/linguise/script-php/src/HttpResponse.php', 41 42 'Linguise\\Vendor\\Linguise\\Script\\Core\\JsonWalker' => $vendorDir . '/linguise/script-php/src/JsonWalker.php', 42 43 'Linguise\\Vendor\\Linguise\\Script\\Core\\Management' => $vendorDir . '/linguise/script-php/src/Management.php', 44 'Linguise\\Vendor\\Linguise\\Script\\Core\\OobeManager' => $vendorDir . '/linguise/script-php/src/OobeManager.php', 43 45 'Linguise\\Vendor\\Linguise\\Script\\Core\\Platforms\\OpenCart' => $vendorDir . '/linguise/script-php/src/Platforms/OpenCart.php', 44 46 'Linguise\\Vendor\\Linguise\\Script\\Core\\Platforms\\PrestaShop' => $vendorDir . '/linguise/script-php/src/Platforms/PrestaShop.php', -
linguise/tags/2.1.67/vendor/composer/autoload_static.php
r3321566 r3354997 107 107 'Linguise\\Vendor\\Linguise\\Script\\Core\\Helper' => __DIR__ . '/..' . '/linguise/script-php/src/Helper.php', 108 108 'Linguise\\Vendor\\Linguise\\Script\\Core\\Hook' => __DIR__ . '/..' . '/linguise/script-php/src/Hook.php', 109 'Linguise\\Vendor\\Linguise\\Script\\Core\\HttpResponse' => __DIR__ . '/..' . '/linguise/script-php/src/HttpResponse.php', 109 110 'Linguise\\Vendor\\Linguise\\Script\\Core\\JsonWalker' => __DIR__ . '/..' . '/linguise/script-php/src/JsonWalker.php', 110 111 'Linguise\\Vendor\\Linguise\\Script\\Core\\Management' => __DIR__ . '/..' . '/linguise/script-php/src/Management.php', 112 'Linguise\\Vendor\\Linguise\\Script\\Core\\OobeManager' => __DIR__ . '/..' . '/linguise/script-php/src/OobeManager.php', 111 113 'Linguise\\Vendor\\Linguise\\Script\\Core\\Platforms\\OpenCart' => __DIR__ . '/..' . '/linguise/script-php/src/Platforms/OpenCart.php', 112 114 'Linguise\\Vendor\\Linguise\\Script\\Core\\Platforms\\PrestaShop' => __DIR__ . '/..' . '/linguise/script-php/src/Platforms/PrestaShop.php', -
linguise/tags/2.1.67/vendor/composer/installed.json
r3349428 r3354997 57 57 { 58 58 "name": "linguise/script-php", 59 "version": "v1.3.3 2",60 "version_normalized": "1.3.3 2.0",59 "version": "v1.3.33", 60 "version_normalized": "1.3.33.0", 61 61 "source": { 62 62 "type": "git", 63 63 "url": "git@bitbucket.org:linguise/script-php.git", 64 "reference": " 031e108bbc9e337b0a36a813404a35217b556cac"64 "reference": "9e65bd71800e1ae003c6929b23c2d24de167f7c5" 65 65 }, 66 66 "require": { … … 73 73 "phpunit/phpunit": "^9" 74 74 }, 75 "time": "2025-0 7-29T08:11:46+00:00",75 "time": "2025-08-27T05:12:16+00:00", 76 76 "type": "library", 77 77 "installation-source": "source", -
linguise/tags/2.1.67/vendor/composer/installed.php
r3350018 r3354997 4 4 'pretty_version' => 'dev-master', 5 5 'version' => 'dev-master', 6 'reference' => ' 2b6bbeb52204d2861b41ccaef60b7dcdce8e6193',6 'reference' => 'a2c6891d602a1aa5ffa31d073b38f968aa1199d0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 21 21 ), 22 22 'linguise/script-php' => array( 23 'pretty_version' => 'v1.3.3 2',24 'version' => '1.3.3 2.0',25 'reference' => ' 031e108bbc9e337b0a36a813404a35217b556cac',23 'pretty_version' => 'v1.3.33', 24 'version' => '1.3.33.0', 25 'reference' => '9e65bd71800e1ae003c6929b23c2d24de167f7c5', 26 26 'type' => 'library', 27 27 'install_path' => __DIR__ . '/../linguise/script-php', … … 32 32 'pretty_version' => 'dev-master', 33 33 'version' => 'dev-master', 34 'reference' => ' 2b6bbeb52204d2861b41ccaef60b7dcdce8e6193',34 'reference' => 'a2c6891d602a1aa5ffa31d073b38f968aa1199d0', 35 35 'type' => 'library', 36 36 'install_path' => __DIR__ . '/../../', -
linguise/tags/2.1.67/vendor/linguise/script-php/.version
r3339453 r3354997 1 1.3.3 21 1.3.33 -
linguise/tags/2.1.67/vendor/linguise/script-php/src/Helper.php
r3324083 r3354997 243 243 */ 244 244 public static function transformToLocalConfig($local_config, $remote_config) { 245 /** 246 * If API-JS return language setting null, just return local config 247 */ 248 if (!isset($remote_config['language_settings']) || is_null($remote_config['language_settings']) || empty($remote_config['language_settings'])) { 249 return $local_config; 250 } 251 245 252 $language_settings = $remote_config['language_settings']; 246 253 $local_config['flag_display_type'] = $language_settings['display']; … … 282 289 return $local_config; 283 290 } 291 292 /** 293 * Convert a boolean value to an integer (0 or 1) 294 * 295 * @param mixed $value The value to convert 296 * 297 * @return mixed The converted value (0 or 1), or original 298 */ 299 static function boolInt($value) 300 { 301 if (is_bool($value)) { 302 return $value ? 1 : 0; 303 } 304 305 return $value; 306 } 307 308 /** 309 * Convert an integer (0 or 1) to a boolean value 310 * 311 * @param mixed $value The value to convert 312 * 313 * @return mixed The converted value (true or false), or original 314 */ 315 static function intBool($value) 316 { 317 if (is_bool($value)) { 318 return $value; 319 } 320 if (is_int($value)) { 321 return $value === 1; 322 } 323 324 return $value; 325 } 326 327 static function defineConstants($is_logged_in = false) 328 { 329 if (!defined('LINGUISE_MANAGEMENT')) { 330 define('LINGUISE_MANAGEMENT', 1); 331 } 332 333 if ($is_logged_in && !defined('LINGUISE_AUTHORIZED')) { 334 define('LINGUISE_AUTHORIZED', 1); 335 } 336 337 // Silent debug noises 338 $request = Request::getInstance(true); 339 $base_dir = rtrim($request->getBaseDir(), '/'); 340 if (!defined('LINGUISE_BASE_URL')) { 341 define('LINGUISE_BASE_URL', $base_dir . '/linguise'); 342 } 343 } 284 344 } -
linguise/tags/2.1.67/vendor/linguise/script-php/src/Management.php
r3339453 r3354997 4 4 5 5 defined('LINGUISE_SCRIPT_TRANSLATION') or die(); 6 use Linguise\Vendor\Linguise\Script\Core\HttpResponse; 6 7 7 8 class Management { … … 12 13 13 14 /** 14 * @var string15 */16 private static $token_key = 'linguise_token';17 /**18 15 * Mapping data from the API to the local config 19 16 * 20 17 * @var array 21 18 */ 22 p rivatestatic $flag_options_map = [19 public static $flag_options_map = [ 23 20 'display' => 'flag_display_type', 24 21 'position' => 'display_position', … … 49 46 } 50 47 51 public function run($html_message = \null, $api_web_errors = []) {52 // Start session53 $sess = Session::getInstance()->start();54 // Set our CSRF token, always overrides55 $sess->generateCsrfToken();56 57 // We define this constant so user can't do direct access to the template58 $this->defineConstants(false);59 60 if (isset($_GET['linguise_action'])) {61 switch ($_GET['linguise_action']) {62 case 'download-debug':63 $this->downloadDebug();64 break;65 case 'update-config':66 break;67 default:68 if (empty($_SESSION[self::$token_key])) {69 $this->rejectGET();70 } else {71 $this->unknownGETAction();72 }73 break;74 }75 }76 77 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Helper.php';78 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'stubs.php';79 80 if (!$sess->hasSession()) {81 // Render login page82 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'header.php';83 if (!empty($html_message)) {84 echo $html_message;85 }86 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'oobe.php';87 } else {88 // Logged in? we send it!89 $this->defineConstants(true);90 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'header.php';91 if (!empty($html_message)) {92 echo $html_message;93 }94 $view_mode = isset($_GET['ling_mode']) ? $_GET['ling_mode'] : 'default';95 if ($view_mode === 'expert') {96 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'management-expert.php';97 } else {98 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'management.php';99 }100 }101 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'footer.php';102 die();103 }104 105 public function oobeRun($html_message = \null) {106 // Start session107 $sess = Session::getInstance()->start();108 // Set our CSRF token, always overrides109 $sess->generateCsrfToken();110 111 $this->defineConstants(false);112 113 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'stubs.php';114 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'header.php';115 if (!empty($html_message)) {116 echo $html_message;117 }118 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'oobe.php';119 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'footer.php';120 }121 122 48 public function editorRun() { 123 $this->defineConstants(false);49 Helper::defineConstants(false); 124 50 125 51 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'stubs.php'; … … 139 65 if (!$sess->hasSession()) { 140 66 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Not authorized</div>'; 141 $this->run($message, $api_web_errors); 67 $oobe = OobeManager::getInstance(); 68 $oobe->run($message, $api_web_errors); 142 69 } 143 70 if (!isset($_POST['_token'])) { 144 $this->errorJSON('Missing CSRF token', 400);71 HttpResponse::errorJSON('Missing CSRF token', 400); 145 72 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Missing CSRF token</div>'; 146 $this->run($message, $api_web_errors); 73 $oobe = OobeManager::getInstance(); 74 $oobe->run($message, $api_web_errors); 147 75 } 148 76 if (!$sess->verifyCsrfToken('linguise_config', $_POST['_token'])) { 149 77 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Invalid CSRF token</div>'; 150 $this->run($message, $api_web_errors); 78 $oobe = OobeManager::getInstance(); 79 $oobe->run($message, $api_web_errors); 151 80 } 152 81 … … 187 116 $dynamic_translations['public_key'] = $api_result['data']['public_key']; 188 117 $linguise_options = Helper::transformToLocalConfig($linguise_options, $api_result['data']); 189 $dynamic_translations['enabled'] = $this->boolInt($api_result['data']['dynamic_translations']['enabled']);118 $dynamic_translations['enabled'] = Helper::boolInt($api_result['data']['dynamic_translations']['enabled']); 190 119 $token_changed = true; 191 120 } else if ($api_result !== false && isset($api_result['status_code'])) { … … 234 163 if ($token_changed) { 235 164 // If token changed, we update enabled state 236 $dynamic_translations['enabled'] = $this->boolInt($api_result['data']['dynamic_translations']['enabled']);165 $dynamic_translations['enabled'] = Helper::boolInt($api_result['data']['dynamic_translations']['enabled']); 237 166 } 238 167 } else if ($api_result !== false && isset($api_result['status_code'])) { … … 277 206 'display_position' => isset($linguise_options['display_position']) ? $linguise_options['display_position'] : 'no', 278 207 'flag_display_type' => isset($linguise_options['flag_display_type']) ? $linguise_options['flag_display_type'] : 'popup', 279 'enable_flag' => $this->boolInt($enable_flag),280 'enable_language_name' => $this->boolInt($enable_language_name),281 'enable_language_name_popup' => $this->boolInt($enable_language_name_popup),282 'enable_language_short_name' => $this->boolInt($enable_language_short_name),208 'enable_flag' => Helper::boolInt($enable_flag), 209 'enable_language_name' => Helper::boolInt($enable_language_name), 210 'enable_language_name_popup' => Helper::boolInt($enable_language_name_popup), 211 'enable_language_short_name' => Helper::boolInt($enable_language_short_name), 283 212 'language_name_display' => isset($linguise_options['language_name_display']) ? $linguise_options['language_name_display'] : 'en', 284 213 'flag_shape' => isset($linguise_options['flag_shape']) ? $linguise_options['flag_shape'] : 'rounded', … … 321 250 'language' => $default_language, 322 251 'allowed_languages' => $translate_languages, 323 'dynamic_translations' => $this->intBool($dynamic_translations['enabled']),252 'dynamic_translations' => Helper::intBool($dynamic_translations['enabled']), 324 253 'language_settings' => [ 325 254 // Flag related 326 255 'display' => $linguise_options['flag_display_type'], 327 256 'position' => $linguise_options['display_position'], 328 'enabled_flag' => $this->intBool($enable_flag),329 'enabled_lang_name' => $this->intBool($enable_language_name),330 'enabled_lang_name_popup' => $this->intBool($enable_language_name_popup),331 'enabled_lang_short_name' => $this->intBool($enable_language_short_name),257 'enabled_flag' => Helper::intBool($enable_flag), 258 'enabled_lang_name' => Helper::intBool($enable_language_name), 259 'enabled_lang_name_popup' => Helper::intBool($enable_language_name_popup), 260 'enabled_lang_short_name' => Helper::intBool($enable_language_short_name), 332 261 'lang_name_display' => $linguise_options['language_name_display'], 333 262 'flag_shape' => $linguise_options['flag_shape'], … … 410 339 411 340 // Re-render 412 $this->run($notification_popup_msg, $api_web_errors); 341 $oobe = OobeManager::getInstance(); 342 $oobe->run($notification_popup_msg, $api_web_errors); 413 343 } 414 344 … … 418 348 $sess = Session::getInstance()->start(); 419 349 if (!$sess->hasSession()) { 420 $this->errorJSON('Unauthorized', 401);350 HttpResponse::errorJSON('Unauthorized', 401); 421 351 } 422 352 if (!isset($_POST['nonce'])) { 423 $this->errorJSON('Missing nonce token', 400);353 HttpResponse::errorJSON('Missing nonce token', 400); 424 354 } 425 355 if (!$sess->verifyCsrfToken('linguise_config_iframe', $_POST['nonce'])) { 426 $this->errorJSON('Invalid nonce token', 403);356 HttpResponse::errorJSON('Invalid nonce token', 403); 427 357 } 428 358 … … 441 371 if (!isset($data[$field])) { 442 372 // response with error 443 $this->errorJSON('Missing required field: ' . $field, 400);373 HttpResponse::errorJSON('Missing required field: ' . $field, 400); 444 374 } 445 375 } … … 475 405 476 406 $db->saveOtherParam('linguise_options', $options); 477 $this->successJSON(true, 'Configuration updated successfully', 200);407 HttpResponse::successJSON(true, 'Configuration updated successfully', 200); 478 408 } 479 409 … … 484 414 $jwt_token = isset($_SERVER['HTTP_X_LINGUISE_HASH']) ? $_SERVER['HTTP_X_LINGUISE_HASH'] : null; 485 415 if (empty($jwt_token)) { 486 $this->errorJSON('Missing Hash header', 400);416 HttpResponse::errorJSON('Missing Hash header', 400); 487 417 } 488 418 … … 490 420 $input_data = file_get_contents('php://input'); 491 421 if (empty($input_data)) { 492 $this->errorJSON('Invalid request', 400);422 HttpResponse::errorJSON('Invalid request', 400); 493 423 } 494 424 495 425 $input_data = json_decode($input_data, true); 496 426 if (json_last_error() !== JSON_ERROR_NONE) { 497 $this->errorJSON('Invalid JSON data', 400);427 HttpResponse::errorJSON('Invalid JSON data', 400); 498 428 } 499 429 … … 503 433 504 434 if ($input_data['token'] !== $options['token']) { 505 $this->errorJSON('Invalid token', 401);435 HttpResponse::errorJSON('Invalid token', 401); 506 436 } 507 437 … … 525 455 $flag_real_key = self::$flag_options_map[$flag_key]; 526 456 } 527 $options[$flag_real_key] = $this->boolInt($flag_value);457 $options[$flag_real_key] = Helper::boolInt($flag_value); 528 458 } 529 459 } … … 531 461 // Save 532 462 $db->saveOtherParam('linguise_options', $options); 533 $this->successJSON(true, 'Configuration updated successfully', 200); 534 } 535 536 public function login() 537 { 538 if (!isset($_POST['_token'])) { 539 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Missing CSRF token</div>'; 540 $this->oobeRun($message); 541 } 542 $sess = Session::getInstance()->start(); 543 if (!$sess->verifyCsrfToken('linguise_oobe_login', $_POST['_token'])) { 544 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Invalid CSRF token</div>'; 545 $this->oobeRun($message); 546 } 547 548 // Authenticate ourself, get the token or password 549 if ($sess->oobeComplete() && !empty($_POST['password'])) { 550 $existing_password = Database::getInstance()->ensureConnection()->retrieveOtherParam('linguise_password'); 551 if ($existing_password && password_verify($_POST['password'], $existing_password)) { 552 // Set session 553 $sess->setSession($existing_password, true); 554 $this->run(\null, []); 555 } else { 556 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Invalid password</div>'; 557 $this->oobeRun($message); 558 } 559 } else { 560 $sess->unsetSession(); 561 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Invalid request</div>'; 562 $this->oobeRun($message); 563 } 564 } 565 566 public function mergeConfig($skip_missing = \false) 567 { 568 /** 569 * @disregard P1011 - already checked 570 */ 571 $use_mysql = defined('LINGUISE_OOBE_MYSQL') && LINGUISE_OOBE_MYSQL; 572 573 // Each config for the mysql, LINGUISE_OOBE_MYSQL_DB_{HOST|USER|PASSWORD|NAME|PREFIX|PORT} 574 $oobe_config = [ 575 'DB_HOST', 576 'DB_USER', 577 'DB_PASSWORD', 578 'DB_NAME', 579 'DB_PREFIX', 580 'DB_PORT', 581 'DB_FLAGS', 582 ]; 583 584 if ($use_mysql) { 585 foreach ($oobe_config as $config) { 586 if (defined('LINGUISE_OOBE_MYSQL_' . $config)) { 587 Configuration::getInstance()->set(strtolower($config), constant('LINGUISE_OOBE_MYSQL_' . $config)); 588 } 589 } 590 } 591 592 // Connect to database and install options 593 Helper::prepareDataDir(); 594 $db = Database::getInstance(true); 595 596 if (!LINGUISE_OOBE_DONE) { 597 // Not yet ready 598 return; 599 } 600 601 $db->ensureConnection()->installOptions(); 602 603 // Get metadata 604 $existing_options = $db->retrieveOtherParam('linguise_options'); 605 if (!empty($existing_options)) { 606 // Merge existing options 607 Configuration::getInstance()->set('token', $existing_options['token']); 608 Configuration::getInstance()->set('cache_enabled', $existing_options['cache_enabled']); 609 Configuration::getInstance()->set('cache_max_size', $existing_options['cache_max_size']); 610 Configuration::getInstance()->set('search_translations', $existing_options['search_translations']); 611 Configuration::getInstance()->set('debug', $existing_options['debug']); 612 613 foreach ($existing_options['expert_mode'] as $key => $value) { 614 Configuration::getInstance()->set($key, $value); 615 } 616 } else { 617 if ($skip_missing) { 618 // Skip if missing 619 return; 620 } 621 622 // We create new options 623 $current_token = Configuration::getInstance()->get('token'); 624 if ($current_token === 'REPLACE_BY_YOUR_TOKEN') { 625 $current_token = ''; 626 } 627 $this->createOptionsWithToken($current_token); 628 } 629 } 630 631 private function oobeRunError($message_str, $status_code = 200) 632 { 633 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>' . $message_str . '</div>'; 634 http_response_code($status_code); 635 $this->oobeRun($message); 636 die(); 637 } 638 639 public function activateLinguise() 640 { 641 // Check if OOBE 642 if (defined('LINGUISE_OOBE_DONE') && LINGUISE_OOBE_DONE) { 643 $this->oobeRunError('Not allowed', 403); 644 } elseif (!defined('LINGUISE_OOBE_DONE')) { 645 // Missing data 646 $this->oobeRunError('Unknown status', 500); 647 } 648 649 if (!isset($_POST['_token'])) { 650 $this->oobeRunError('Missing CSRF token', 400); 651 } 652 $sess = Session::getInstance()->start(); 653 if (!$sess->verifyCsrfToken('linguise_oobe_register', $_POST['_token'])) { 654 $this->oobeRunError('Invalid CSRF token', 403); 655 } 656 657 $new_pass = isset($_POST['password']) ? $_POST['password'] : null; 658 if (empty($new_pass)) { 659 $this->oobeRunError('Missing password', 400); 660 } 661 662 // == Password check == 663 // Must be at least 10 characters long 664 if (strlen($new_pass) < 10) { 665 $this->oobeRunError('Password must be at least 10 characters long', 400); 666 } 667 668 if ($sess->hasSession()) { 669 $this->oobeRunError('Already activated', 403); 670 } 671 672 $is_token = defined('LINGUISE_OOBE_TOKEN_EXIST') && LINGUISE_OOBE_TOKEN_EXIST; 673 if ($is_token) { 674 if (!isset($_POST['token'])) { 675 $this->oobeRunError('Missing token', 400); 676 } 677 678 $token = $_POST['token']; 679 if ($token !== Configuration::getInstance()->get('token')) { 680 $this->oobeRunError('Invalid token provided in session', 401); 681 } 682 683 // This will automatically return error response 684 $database_store = $this->storeDatabaseConnection(false); 685 // set configuration 686 if ($database_store['MYSQL']) { 687 Configuration::getInstance()->set('db_host', $database_store['MYSQL_DB_HOST']); 688 Configuration::getInstance()->set('db_user', $database_store['MYSQL_DB_USER']); 689 Configuration::getInstance()->set('db_password', $database_store['MYSQL_DB_PASSWORD']); 690 Configuration::getInstance()->set('db_name', $database_store['MYSQL_DB_NAME']); 691 Configuration::getInstance()->set('db_prefix', $database_store['MYSQL_DB_PREFIX']); 692 Configuration::getInstance()->set('db_port', $database_store['MYSQL_DB_PORT']); 693 } else { 694 // SQLite 695 Configuration::getInstance()->set('db_host', ''); 696 Configuration::getInstance()->set('db_user', ''); 697 Configuration::getInstance()->set('db_password', ''); 698 Configuration::getInstance()->set('db_name', ''); 699 Configuration::getInstance()->set('db_prefix', ''); 700 701 $sqlite_test = $this->prepareRootDatabaseSQLite(); 702 if ($sqlite_test !== true) { 703 $this->oobeRunError($sqlite_test, 500); 704 } 705 } 706 707 // Init database 708 Helper::prepareDataDir(); 709 $db = Database::getInstance(true, true)->ensureConnection(); 710 $db->installOptions(); 711 712 // Valid, then let's set the password 713 $hashed_pass = $this->hashPassword($new_pass); 714 $db->saveOtherParam('linguise_password', $hashed_pass); 715 // Create the options 716 $db_options = $this->createOptionsWithToken($token); 717 718 // Write OOBE config 719 $this->writeOOBE($database_store); 720 721 $api_result = $this->getRemoteData($token); 722 if ($api_result !== false && isset($api_result['data'])) { 723 $default_language = Helper::sanitizeKey($api_result['data']['language']); 724 725 $translation_languages = $api_result['data']['languages']; 726 $translate_languages = []; 727 if (!empty($translation_languages)) { 728 foreach ($translation_languages as $translation_language) { 729 $translate_languages[] = Helper::sanitizeKey($translation_language['code']); 730 } 731 } 732 733 $db_options['enabled_languages'] = $translate_languages; 734 $db_options['default_language'] = $default_language; 735 $db_options['dynamic_translations']['enabled'] = $this->intBool($api_result['data']['dynamic_translations']['enabled']); 736 $db_options['dynamic_translations']['public_key'] = $api_result['data']['public_key']; 737 738 if (!empty($api_result['data']['language_settings'])) { 739 foreach ($api_result['data']['language_settings'] as $flag_key => $flag_value) { 740 $flag_real_key = $flag_key; 741 if (isset(self::$flag_options_map[$flag_key])) { 742 $flag_real_key = self::$flag_options_map[$flag_key]; 743 } 744 $db_options[$flag_real_key] = $this->boolInt($flag_value); 745 } 746 } 747 748 // Re-save with API data 749 $db->saveOtherParam('linguise_options', $db_options); 750 } 751 752 // Set session, and login the user. 753 $sess->setOobeForced(); 754 $sess->setSession($hashed_pass, true); // Set session with password mode 755 $this->run(); 756 die(); 757 } else { 758 // This will automatically return error response 759 $database_store = $this->storeDatabaseConnection(false); 760 // set configuration 761 if ($database_store['MYSQL']) { 762 Configuration::getInstance()->set('db_host', $database_store['MYSQL_DB_HOST']); 763 Configuration::getInstance()->set('db_user', $database_store['MYSQL_DB_USER']); 764 Configuration::getInstance()->set('db_password', $database_store['MYSQL_DB_PASSWORD']); 765 Configuration::getInstance()->set('db_name', $database_store['MYSQL_DB_NAME']); 766 Configuration::getInstance()->set('db_prefix', $database_store['MYSQL_DB_PREFIX']); 767 Configuration::getInstance()->set('db_port', $database_store['MYSQL_DB_PORT']); 768 } else { 769 // SQLite 770 Configuration::getInstance()->set('db_host', ''); 771 Configuration::getInstance()->set('db_user', ''); 772 Configuration::getInstance()->set('db_password', ''); 773 Configuration::getInstance()->set('db_name', ''); 774 Configuration::getInstance()->set('db_prefix', ''); 775 776 $sqlite_test = $this->prepareRootDatabaseSQLite(); 777 if ($sqlite_test !== true) { 778 $this->oobeRunError($sqlite_test, 500); 779 } 780 } 781 782 // Init database 783 Helper::prepareDataDir(); 784 $db = Database::getInstance(true, true)->ensureConnection(); 785 $db->installOptions(); 786 787 // No token, we just set it immediately 788 $existing_password = $db->retrieveOtherParam('linguise_password'); 789 if ($existing_password) { 790 $this->oobeRunError('Password already set', 400); 791 } 792 793 // Set the password 794 $hashed_pass = $this->hashPassword($new_pass); 795 796 // Create 797 $db->saveOtherParam('linguise_password', $hashed_pass); 798 // Create the options 799 $this->createOptionsWithToken(''); // Empty token since no token provided yet. 800 801 // Write OOBE config 802 $this->writeOOBE($database_store); 803 804 805 // Set session, and login the user. 806 $sess->setOobeForced(); 807 $sess->setSession($hashed_pass, true); // Set session with password mode 808 $this->run(); 809 die(); 810 } 811 } 812 813 public function storeDatabaseConnection($testMode = \false) 814 { 815 if ($testMode) { 816 if (!isset($_POST['_token'])) { 817 $this->errorJSON('Missing CSRF token', 400); 818 } 819 $sess = Session::getInstance()->start(); 820 if (!$sess->verifyCsrfToken('linguise_oobe_register', $_POST['_token'])) { 821 $this->errorJSON('Invalid CSRF token', 403); 822 } 823 } 824 825 // Get from POST data 826 $mode = $_POST['db_mode'] ?? null; 827 $host = $_POST['db_host'] ?? null; 828 $user = $_POST['db_user'] ?? null; 829 $password = $_POST['db_password'] ?? null; 830 $name = $_POST['db_name'] ?? null; 831 $port = $_POST['db_port'] ?? 3306; 832 $prefix = $_POST['db_prefix'] ?? null; 833 834 if (empty($mode)) { 835 $this->errorJSON('Missing `db_mode` data', 400); 836 } 837 838 switch ($mode) { 839 case 'mysql': 840 if (empty($host) || empty($user) || empty($name)) { 841 $this->errorJSON('Missing `db_host`, `db_user` or `db_name` data', 400); 842 } 843 if (!$testMode && (empty($prefix))) { 844 $this->errorJSON('Missing `db_prefix` data', 400); 845 } 846 847 if ($testMode) { 848 $result = $this->testMySQL($host, $user, $password, $name, $port, $prefix); 849 if ($result !== true) { 850 $this->errorJSON($result, 500); 851 } 852 $this->successJSON(true, 'MySQL connection test successful', 200); 853 } 854 855 return [ 856 'MYSQL' => true, 857 'MYSQL_DB_HOST' => $host, 858 'MYSQL_DB_USER' => $user, 859 'MYSQL_DB_PASSWORD' => $password, 860 'MYSQL_DB_NAME' => $name, 861 'MYSQL_DB_PORT' => $port, 862 'MYSQL_DB_PREFIX' => $prefix, 863 ]; 864 case 'sqlite': 865 // Check if SQLite3 is enabled 866 if ($testMode) { 867 $result = $this->testSqlite(); 868 if ($result !== true) { 869 $this->errorJSON($result, 500); 870 } 871 $this->successJSON(true, 'SQLite connection test successful', 200); 872 } 873 874 // Store the SQLite connection 875 return [ 876 'MYSQL' => false, 877 ]; 878 default: 879 $this->errorJSON('Invalid `db_mode` data', 400); 880 } 881 } 882 883 private function testMySQL($host, $user, $password, $name, $port = 3306, $prefix = '') 884 { 885 // Check if MySQLi is enabled 886 if (!extension_loaded('mysqli')) { 887 return 'MySQLi extension not loaded'; 888 } 889 890 // Check if MySQLi class exist 891 if (!class_exists('mysqli')) { 892 return 'MySQLi class not found'; 893 } 894 895 // Attempt to connect to the database 896 $connection = \null; 897 try { 898 $connection = new \mysqli($host, $user, $password, $name, $port); 899 } catch (\mysqli_sql_exception $e) { 900 return 'Connection failed: ' . $e->getMessage(); 901 } 902 903 if (empty($connection)) { 904 return 'Connection failed: No connection object created'; 905 } 906 907 if ($connection->connect_error) { 908 return 'Connection failed: ' . $connection->connect_error; 909 } 910 911 // Close the connection 912 $connection->close(); 913 914 return true; 915 } 916 917 private function testSqlite() 918 { 919 // Check if SQLite3 is enabled 920 if (!extension_loaded('sqlite3')) { 921 return 'SQLite3 extension not loaded'; 922 } 923 924 // Check if SQLite3 class exist 925 if (!class_exists('SQLite3')) { 926 return 'SQLite3 class not found'; 927 } 928 929 // Check if we can write in LINGUISE_BASE_DIR / .linguise-main.db 930 $sqlite_test = $this->prepareRootDatabaseSQLite(); 931 if ($sqlite_test !== true) { 932 return $sqlite_test; 933 } 934 935 if (!Helper::checkDataDirAvailable()) { 936 return 'Cannot write to data directory'; 937 } 938 939 return true; 940 } 941 942 private function prepareRootDatabaseSQLite() 943 { 944 $databases_dir = LINGUISE_BASE_DIR . '.databases' . DIRECTORY_SEPARATOR; 945 if (!file_exists($databases_dir)) { 946 if (!mkdir($databases_dir, 0766, true)) { 947 return 'Cannot create database directory: ' . $databases_dir; 948 } 949 } 950 951 $htaccess_file = $databases_dir . '.htaccess'; 952 if (!file_exists($htaccess_file)) { 953 $written = file_put_contents($htaccess_file, 'deny from all'); 954 if ($written === false) { 955 return 'Cannot write to database directory: ' . $databases_dir; 956 } 957 } 958 959 $db_path = $databases_dir . 'linguise-main.db'; 960 if (!file_exists($db_path)) { 961 // touch the file 962 $db_touch = touch($db_path); 963 if (!$db_touch) { 964 return 'Cannot create database file: ' . $db_path; 965 } 966 unlink($db_path); 967 } else { 968 if (!is_writable($db_path)) { 969 return 'Cannot write database to ' . $db_path; 970 } 971 } 972 973 return true; 974 } 975 976 private function createOptionsWithToken($token) 977 { 978 $db = Database::getInstance()->ensureConnection(); 979 $options = [ 980 'token' => $token, 981 'cache_enabled' => Configuration::getInstance()->get('cache_enabled'), 982 'cache_max_size' => Configuration::getInstance()->get('cache_max_size'), 983 'search_translations' => Configuration::getInstance()->get('search_translations'), 984 'debug' => Configuration::getInstance()->get('debug'), 985 'dynamic_translations' => [ 986 'enabled' => 0, 987 'public_key' => null, 988 ], 989 990 // Languages related 991 'default_language' => 'en', 992 'enabled_languages' => [], 993 994 // Flag related 995 'display_position' => 'bottom_right', 996 'flag_display_type' => 'popup', 997 'enable_flag' => 1, 998 'enable_language_name' => 1, 999 'enable_language_short_name' => 0, 1000 'language_name_display' => 'en', 1001 'flag_shape' => 'round', 1002 'flag_en_type' => 'en-us', 1003 'flag_de_type' => 'de', 1004 'flag_es_type' => 'es', 1005 'flag_pt_type' => 'pt', 1006 'flag_tw_type' => 'zh-tw', 1007 'flag_border_radius' => 0, 1008 'flag_width' => 24, 1009 'pre_text' => '', 1010 'post_text' => '', 1011 'custom_css' => '', 1012 'language_name_color' => '#222', 1013 'language_name_hover_color' => '#222', 1014 'popup_language_name_color' => '#222', 1015 'popup_language_name_hover_color' => '#222', 1016 'flag_shadow_h' => 2, 1017 'flag_shadow_v' => 2, 1018 'flag_shadow_blur' => 12, 1019 'flag_shadow_spread' => 0, 1020 'flag_shadow_color' => '#eee', 1021 'flag_shadow_color_alpha' => (float)1.0, // we use 100% scaling, 0.0-1.0 1022 'flag_hover_shadow_h' => 3, 1023 'flag_hover_shadow_v' => 3, 1024 'flag_hover_shadow_blur' => 6, 1025 'flag_hover_shadow_spread' => 0, 1026 'flag_hover_shadow_color' => '#bfbfbf', 1027 'flag_hover_shadow_color_alpha' => (float)1.0, 1028 1029 'expert_mode' => [], 1030 ]; 1031 1032 $db->saveOtherParam('linguise_options', $options); 1033 1034 return $options; 1035 } 1036 1037 private function errorJSON($message, $code = 500) 1038 { 1039 header('Content-Type: application/json; charset=utf-8'); 1040 http_response_code($code); 1041 echo json_encode([ 1042 'error' => true, 1043 'message' => $message 1044 ]); 1045 exit; 1046 } 1047 1048 private function successJSON($data, $message = '', $code = 200) 1049 { 1050 header('Content-Type: application/json; charset=utf-8'); 1051 http_response_code($code); 1052 echo json_encode([ 1053 'error' => false, 1054 'message' => $message, 1055 'data' => $data 1056 ]); 1057 exit; 1058 } 1059 1060 private function hashPassword($input) 1061 { 1062 $hashed = password_hash($input, PASSWORD_BCRYPT, [ 1063 'cost' => 12, 1064 ]); 1065 1066 return $hashed; 1067 } 1068 1069 private function writeOOBE($database_store) 1070 { 1071 // Modify OOBE status in ui-config.php 1072 $ui_config = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..') . DIRECTORY_SEPARATOR . 'ui-config.php'; 1073 $content = file_get_contents($ui_config); 1074 1075 $replaced_content = preg_replace('/define\([\'"]LINGUISE_OOBE_DONE[\'"], .*\);/m', 'define(\'LINGUISE_OOBE_DONE\', true);', $content); 1076 if (empty($replaced_content)) { 1077 $this->errorJSON('Failed to update ui-config.php', 500); 1078 } 1079 1080 // Update the database connection 1081 foreach ($database_store as $key => $value) { 1082 // Push content 1083 $value_wrap = json_encode($value); 1084 $replaced_content .= "\ndefine('LINGUISE_OOBE_" . $key . "', " . $value_wrap . ");\n"; 1085 } 1086 1087 file_put_contents($ui_config, $replaced_content); 1088 } 1089 1090 public function clearDebug() 1091 { 1092 // Verify session 1093 $sess = Session::getInstance()->start(); 1094 if (!$sess->hasSession()) { 1095 $this->errorJSON('Unauthorized', 401); 1096 } 1097 if (!isset($_GET['nonce'])) { 1098 $this->errorJSON('Missing nonce token', 400); 1099 } 1100 if (!$sess->verifyCsrfToken('linguise_clear_debug', $_GET['nonce'])) { 1101 $this->errorJSON('Invalid nonce token', 403); 1102 } 1103 1104 // Clear the debug file 1105 $debug_file = LINGUISE_BASE_DIR . 'debug.php'; 1106 $last_errors_file = LINGUISE_BASE_DIR . 'errors.php'; 1107 if (file_exists($debug_file)) { 1108 file_put_contents($debug_file, "<?php die(); ?>" . PHP_EOL); 1109 } 1110 if (file_exists($last_errors_file)) { 1111 file_put_contents($last_errors_file, "<?php die(); ?>" . PHP_EOL); 1112 } 1113 1114 $this->successJSON(true, 'Log truncated!', 200); 1115 } 1116 1117 public function clearCache() 1118 { 1119 // Verify session 1120 $sess = Session::getInstance()->start(); 1121 if (!$sess->hasSession()) { 1122 $this->errorJSON('Unauthorized', 401); 1123 } 1124 if (!isset($_GET['nonce'])) { 1125 $this->errorJSON('Missing nonce token', 400); 1126 } 1127 if (!$sess->verifyCsrfToken('linguise_clear_cache', $_GET['nonce'])) { 1128 $this->errorJSON('Invalid nonce token', 403); 1129 } 1130 1131 // Clear the cache 1132 Cache::getInstance()->clearAll(); 1133 } 1134 1135 private function downloadDebug() 1136 { 1137 // Verify session 1138 $sess = Session::getInstance()->start(); 1139 if (!$sess->hasSession()) { 1140 die('Unauthorized'); 1141 } 1142 1143 $debug_file = LINGUISE_BASE_DIR . 'debug.php'; 1144 if (file_exists($debug_file)) { 1145 header('Content-Description: File Transfer'); 1146 header('Content-Type: application/octet-stream'); 1147 header('Content-Disposition: attachment; filename="debug.txt"'); 1148 header('Content-Transfer-Encoding: binary'); 1149 header('Expires: 0'); 1150 header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 1151 header('Pragma: public'); 1152 header('Content-Length: ' . filesize($debug_file)); 1153 ob_clean(); 1154 ob_end_flush(); 1155 $handle = fopen($debug_file, 'rb'); 1156 while (!feof($handle)) { 1157 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 1158 echo fread($handle, 1000); 1159 } 1160 1161 die(); 1162 } else { 1163 die('No debug file found'); 1164 } 1165 } 1166 1167 private function defineConstants($is_logged_in = false) 1168 { 1169 if (!defined('LINGUISE_MANAGEMENT')) { 1170 define('LINGUISE_MANAGEMENT', 1); 1171 } 1172 1173 if ($is_logged_in && !defined('LINGUISE_AUTHORIZED')) { 1174 define('LINGUISE_AUTHORIZED', 1); 1175 } 1176 1177 // Silent debug noises 1178 $request = Request::getInstance(true); 1179 $base_dir = rtrim($request->getBaseDir(), '/'); 1180 if (!defined('LINGUISE_BASE_URL')) { 1181 define('LINGUISE_BASE_URL', $base_dir . '/linguise'); 1182 } 463 HttpResponse::successJSON(true, 'Configuration updated successfully', 200); 1183 464 } 1184 465 … … 1226 507 1227 508 if ($response_code !== 200) { 1228 $this->errorJSON('Invalid JWT verification token: ' . print_r($response_code, true), 403);509 HttpResponse::errorJSON('Invalid JWT verification token: ' . print_r($response_code, true), 403); 1229 510 } 1230 511 … … 1233 514 } 1234 515 1235 p rivatefunction getRemoteData($token)516 public function getRemoteData($token) 1236 517 { 1237 518 // Config get is api.linguise.com/api/config … … 1333 614 } 1334 615 1335 public function logout()1336 {1337 // Verify session1338 $sess = Session::getInstance()->start();1339 // Verify session1340 if ($sess->hasSession()) {1341 // Unset session1342 $sess->unsetSession();1343 $this->successJSON(true, 'Logout successful', 200);1344 } else {1345 // No session, we just redirect to login page1346 $this->successJSON(true, 'No session found', 200);1347 exit;1348 }1349 }1350 1351 616 private function getApiRoot() 1352 617 { … … 1369 634 return $protocol . '://' . $api_host . (in_array($api_port, $api_port_base) ? '' : ':' . $api_port) . ''; 1370 635 } 1371 1372 public function rejectGET()1373 {1374 header('Content-Type: text/html; charset=utf-8');1375 http_response_code(403);1376 echo '<h1>403 Forbidden</h1>';1377 echo '<p>You are not allowed to access this page.</p>';1378 die();1379 }1380 1381 private function unknownGETAction()1382 {1383 header('Content-Type: text/html; charset=utf-8');1384 http_response_code(400);1385 echo '<h1>400 Bad Request</h1>';1386 echo '<p>Unknown action.</p>';1387 die();1388 }1389 1390 /**1391 * Convert a boolean value to an integer (0 or 1)1392 *1393 * @param mixed $value The value to convert1394 *1395 * @return mixed The converted value (0 or 1), or original1396 */1397 private function boolInt($value)1398 {1399 if (is_bool($value)) {1400 return $value ? 1 : 0;1401 }1402 1403 return $value;1404 }1405 1406 /**1407 * Convert an integer (0 or 1) to a boolean value1408 *1409 * @param mixed $value The value to convert1410 *1411 * @return mixed The converted value (true or false), or original1412 */1413 private function intBool($value)1414 {1415 if (is_bool($value)) {1416 return $value;1417 }1418 if (is_int($value)) {1419 return $value === 1;1420 }1421 1422 return $value;1423 }1424 636 } -
linguise/trunk/linguise.php
r3350018 r3354997 5 5 * Plugin URI: https://www.linguise.com/ 6 6 * Description: Linguise translation plugin 7 * Version:2.1.6 67 * Version:2.1.67 8 8 * Text Domain: linguise 9 9 * Domain Path: /languages -
linguise/trunk/readme.txt
r3350018 r3354997 4 4 Requires at least: 4.0 5 5 Tested up to: 6.8 6 Stable tag:2.1.6 66 Stable tag:2.1.67 7 7 Requires PHP: 7.0 8 8 License: GPLv2 or later … … 104 104 105 105 == Changelog == 106 = 2.1.67 = 107 - Fix: WooCommerce Stripe Gateway Issue: the credit card form not showing up 108 106 109 = 2.1.66 = 107 110 - Fix: Double slash in redirect URL -
linguise/trunk/src/FragmentHandler.php
r3339453 r3354997 206 206 'key' => 'paymentMethodData.*?stripe\.plugin_url', 207 207 'mode' => 'regex_full', 208 'kind' => 'deny', 209 ], 210 [ 211 'key' => 'currency', 212 'mode' => 'exact', 208 213 'kind' => 'deny', 209 214 ], … … 1292 1297 1293 1298 $replaced_json = $json_data->getJson(); 1299 1300 if (function_exists('apply_filters')) { 1301 $replaced_json = apply_filters('linguise_after_apply_translated_fragments_override', $fragment_name, $replaced_json); 1302 } 1303 1294 1304 if ($should_encode) { 1295 1305 $replaced_json = rawurlencode($replaced_json); … … 1392 1402 1393 1403 $replaced_json = self::applyTranslatedFragmentsForAuto(json_decode('{' . $html_matches[3] . '}', true), $fragment_list['fragments']); 1404 1405 if (function_exists('apply_filters')) { 1406 $replaced_json = apply_filters('linguise_after_apply_translated_fragments_auto', $fragment_name, $replaced_json); 1407 } 1408 1394 1409 if ($replaced_json === false) { 1395 1410 throw new \LogicException('FragmentHandler -> Injection -> ' . $fragment_name . '/' . $fragment_param . ' -> JSON data is empty!'); -
linguise/trunk/src/constants.php
r3350018 r3354997 1 1 <?php 2 2 if (!defined('LINGUISE_SCRIPT_TRANSLATION_VERSION')) { 3 define('LINGUISE_SCRIPT_TRANSLATION_VERSION', 'wordpress_plugin/2.1.6 6');3 define('LINGUISE_SCRIPT_TRANSLATION_VERSION', 'wordpress_plugin/2.1.67'); 4 4 } 5 5 6 6 if (!defined('LINGUISE_VERSION')) { 7 define('LINGUISE_VERSION', '2.1.6 6');7 define('LINGUISE_VERSION', '2.1.67'); 8 8 } -
linguise/trunk/src/thirdparty/wc/gateway-stripe.php
r3291510 r3354997 22 22 23 23 /** 24 * A collection of fragment keys that will be translated 25 * 26 * @var array{key:string, mode:'exact'|'path'|'wildcard'|'regex'|'regex_full',kind:'allow'|'deny'} 27 */ 28 protected static $fragment_keys = [ 29 [ 30 'key' => 'allowed_shipping_countries\..*', 31 'mode' => 'regex_full', 32 'kind' => 'deny', 33 ], 34 [ 35 'key' => 'checkout\.((?:country|currency)_code|needs_shipping)$', 36 'mode' => 'regex_full', 37 'kind' => 'deny', 38 ], 39 [ 40 'key' => 'baseLocation\.(country|state)$', 41 'mode' => 'regex_full', 42 'kind' => 'deny', 43 ], 44 [ 45 'key' => 'blocksAppearance\.rules\..*', 46 'mode' => 'regex_full', 47 'kind' => 'deny', 48 ] 49 ]; 50 51 /** 24 52 * Decides if the integration should be loaded. 25 53 * … … 42 70 // Block checkout 43 71 add_filter('wc_stripe_params', [$this, 'hookTranslateParams'], 10, 1); 72 73 add_filter('linguise_after_apply_translated_fragments_auto', [$this, 'restoreOriginalConfigClassicCheckout'], 10, 2); 74 add_filter('linguise_after_apply_translated_fragments_override', [$this, 'restoreOriginalConfigBlockCheckout'], 10, 2); 44 75 } 45 76 … … 55 86 // Block checkout 56 87 remove_filter('wc_stripe_params', [$this, 'hookTranslateParams'], 10); 88 89 remove_filter('linguise_after_apply_translated_fragments_auto', [$this, 'restoreOriginalConfigClassicCheckout'], 10, 2); 90 remove_filter('linguise_after_apply_translated_fragments_override', [$this, 'restoreOriginalConfigBlockCheckout'], 10, 2); 57 91 } 58 92 … … 149 183 return $params; 150 184 } 185 186 /** 187 * Restore the original block appearance structure that was accidentally 188 * changed into an array 189 * 190 * @param string $fragment_name The name of the fragment being translated. 191 * @param array $replaced_json The fragment being translated. 192 * 193 * @return array The original block appearance structure. 194 */ 195 public function restoreOriginalConfigClassicCheckout($fragment_name, $replaced_json) 196 { 197 if ($fragment_name === 'wc-stripe-upe-classic') { 198 if (array_key_exists('blocksAppearance', $replaced_json)) { 199 $replaced_json['blocksAppearance'] = $this->arrayToObject($replaced_json['blocksAppearance']); 200 } 201 } 202 203 return $replaced_json; 204 } 205 206 /** 207 * Restore the original block appearance structure that was accidentally 208 * changed into an array 209 * 210 * @param string $fragment_name The name of the fragment being translated. 211 * @param string $replaced_json The fragment being translated. 212 * 213 * @return array The original block appearance structure. 214 */ 215 public function restoreOriginalConfigBlockCheckout($fragment_name, $replaced_json) 216 { 217 $replaced_json = json_decode($replaced_json); 218 if ($fragment_name === 'wc-settings-encoded') { 219 if (isset($replaced_json->paymentMethodData->stripe->blocksAppearance)) { 220 $rules = $replaced_json->paymentMethodData->stripe->blocksAppearance->rules; 221 $replaced_json->paymentMethodData->stripe->blocksAppearance->rules = $this->arrayToObject($rules); 222 } 223 } 224 225 return json_encode($replaced_json); 226 } 227 228 /** 229 * Recursive convert an array into an object. 230 * 231 * @param array $data The data to convert. 232 * 233 * @return object The converted object. 234 */ 235 public function arrayToObject($data) 236 { 237 if (is_array($data)) { 238 if (empty($data)) { 239 return new \stdClass(); // convert empty array to object 240 } 241 foreach ($data as $key => $value) { 242 $data[$key] = $this->arrayToObject($value); 243 } 244 return (object) $data; 245 } elseif ($data instanceof \stdClass) { 246 foreach ($data as $key => $value) { 247 $data->$key = $this->arrayToObject($value); 248 } 249 return $data; 250 } 251 252 return $data; 253 } 151 254 } -
linguise/trunk/vendor/composer/autoload_classmap.php
r3316163 r3354997 39 39 'Linguise\\Vendor\\Linguise\\Script\\Core\\Helper' => $vendorDir . '/linguise/script-php/src/Helper.php', 40 40 'Linguise\\Vendor\\Linguise\\Script\\Core\\Hook' => $vendorDir . '/linguise/script-php/src/Hook.php', 41 'Linguise\\Vendor\\Linguise\\Script\\Core\\HttpResponse' => $vendorDir . '/linguise/script-php/src/HttpResponse.php', 41 42 'Linguise\\Vendor\\Linguise\\Script\\Core\\JsonWalker' => $vendorDir . '/linguise/script-php/src/JsonWalker.php', 42 43 'Linguise\\Vendor\\Linguise\\Script\\Core\\Management' => $vendorDir . '/linguise/script-php/src/Management.php', 44 'Linguise\\Vendor\\Linguise\\Script\\Core\\OobeManager' => $vendorDir . '/linguise/script-php/src/OobeManager.php', 43 45 'Linguise\\Vendor\\Linguise\\Script\\Core\\Platforms\\OpenCart' => $vendorDir . '/linguise/script-php/src/Platforms/OpenCart.php', 44 46 'Linguise\\Vendor\\Linguise\\Script\\Core\\Platforms\\PrestaShop' => $vendorDir . '/linguise/script-php/src/Platforms/PrestaShop.php', -
linguise/trunk/vendor/composer/autoload_static.php
r3321566 r3354997 107 107 'Linguise\\Vendor\\Linguise\\Script\\Core\\Helper' => __DIR__ . '/..' . '/linguise/script-php/src/Helper.php', 108 108 'Linguise\\Vendor\\Linguise\\Script\\Core\\Hook' => __DIR__ . '/..' . '/linguise/script-php/src/Hook.php', 109 'Linguise\\Vendor\\Linguise\\Script\\Core\\HttpResponse' => __DIR__ . '/..' . '/linguise/script-php/src/HttpResponse.php', 109 110 'Linguise\\Vendor\\Linguise\\Script\\Core\\JsonWalker' => __DIR__ . '/..' . '/linguise/script-php/src/JsonWalker.php', 110 111 'Linguise\\Vendor\\Linguise\\Script\\Core\\Management' => __DIR__ . '/..' . '/linguise/script-php/src/Management.php', 112 'Linguise\\Vendor\\Linguise\\Script\\Core\\OobeManager' => __DIR__ . '/..' . '/linguise/script-php/src/OobeManager.php', 111 113 'Linguise\\Vendor\\Linguise\\Script\\Core\\Platforms\\OpenCart' => __DIR__ . '/..' . '/linguise/script-php/src/Platforms/OpenCart.php', 112 114 'Linguise\\Vendor\\Linguise\\Script\\Core\\Platforms\\PrestaShop' => __DIR__ . '/..' . '/linguise/script-php/src/Platforms/PrestaShop.php', -
linguise/trunk/vendor/composer/installed.json
r3349428 r3354997 57 57 { 58 58 "name": "linguise/script-php", 59 "version": "v1.3.3 2",60 "version_normalized": "1.3.3 2.0",59 "version": "v1.3.33", 60 "version_normalized": "1.3.33.0", 61 61 "source": { 62 62 "type": "git", 63 63 "url": "git@bitbucket.org:linguise/script-php.git", 64 "reference": " 031e108bbc9e337b0a36a813404a35217b556cac"64 "reference": "9e65bd71800e1ae003c6929b23c2d24de167f7c5" 65 65 }, 66 66 "require": { … … 73 73 "phpunit/phpunit": "^9" 74 74 }, 75 "time": "2025-0 7-29T08:11:46+00:00",75 "time": "2025-08-27T05:12:16+00:00", 76 76 "type": "library", 77 77 "installation-source": "source", -
linguise/trunk/vendor/composer/installed.php
r3350018 r3354997 4 4 'pretty_version' => 'dev-master', 5 5 'version' => 'dev-master', 6 'reference' => ' 2b6bbeb52204d2861b41ccaef60b7dcdce8e6193',6 'reference' => 'a2c6891d602a1aa5ffa31d073b38f968aa1199d0', 7 7 'type' => 'library', 8 8 'install_path' => __DIR__ . '/../../', … … 21 21 ), 22 22 'linguise/script-php' => array( 23 'pretty_version' => 'v1.3.3 2',24 'version' => '1.3.3 2.0',25 'reference' => ' 031e108bbc9e337b0a36a813404a35217b556cac',23 'pretty_version' => 'v1.3.33', 24 'version' => '1.3.33.0', 25 'reference' => '9e65bd71800e1ae003c6929b23c2d24de167f7c5', 26 26 'type' => 'library', 27 27 'install_path' => __DIR__ . '/../linguise/script-php', … … 32 32 'pretty_version' => 'dev-master', 33 33 'version' => 'dev-master', 34 'reference' => ' 2b6bbeb52204d2861b41ccaef60b7dcdce8e6193',34 'reference' => 'a2c6891d602a1aa5ffa31d073b38f968aa1199d0', 35 35 'type' => 'library', 36 36 'install_path' => __DIR__ . '/../../', -
linguise/trunk/vendor/linguise/script-php/.version
r3339453 r3354997 1 1.3.3 21 1.3.33 -
linguise/trunk/vendor/linguise/script-php/src/Helper.php
r3324083 r3354997 243 243 */ 244 244 public static function transformToLocalConfig($local_config, $remote_config) { 245 /** 246 * If API-JS return language setting null, just return local config 247 */ 248 if (!isset($remote_config['language_settings']) || is_null($remote_config['language_settings']) || empty($remote_config['language_settings'])) { 249 return $local_config; 250 } 251 245 252 $language_settings = $remote_config['language_settings']; 246 253 $local_config['flag_display_type'] = $language_settings['display']; … … 282 289 return $local_config; 283 290 } 291 292 /** 293 * Convert a boolean value to an integer (0 or 1) 294 * 295 * @param mixed $value The value to convert 296 * 297 * @return mixed The converted value (0 or 1), or original 298 */ 299 static function boolInt($value) 300 { 301 if (is_bool($value)) { 302 return $value ? 1 : 0; 303 } 304 305 return $value; 306 } 307 308 /** 309 * Convert an integer (0 or 1) to a boolean value 310 * 311 * @param mixed $value The value to convert 312 * 313 * @return mixed The converted value (true or false), or original 314 */ 315 static function intBool($value) 316 { 317 if (is_bool($value)) { 318 return $value; 319 } 320 if (is_int($value)) { 321 return $value === 1; 322 } 323 324 return $value; 325 } 326 327 static function defineConstants($is_logged_in = false) 328 { 329 if (!defined('LINGUISE_MANAGEMENT')) { 330 define('LINGUISE_MANAGEMENT', 1); 331 } 332 333 if ($is_logged_in && !defined('LINGUISE_AUTHORIZED')) { 334 define('LINGUISE_AUTHORIZED', 1); 335 } 336 337 // Silent debug noises 338 $request = Request::getInstance(true); 339 $base_dir = rtrim($request->getBaseDir(), '/'); 340 if (!defined('LINGUISE_BASE_URL')) { 341 define('LINGUISE_BASE_URL', $base_dir . '/linguise'); 342 } 343 } 284 344 } -
linguise/trunk/vendor/linguise/script-php/src/Management.php
r3339453 r3354997 4 4 5 5 defined('LINGUISE_SCRIPT_TRANSLATION') or die(); 6 use Linguise\Vendor\Linguise\Script\Core\HttpResponse; 6 7 7 8 class Management { … … 12 13 13 14 /** 14 * @var string15 */16 private static $token_key = 'linguise_token';17 /**18 15 * Mapping data from the API to the local config 19 16 * 20 17 * @var array 21 18 */ 22 p rivatestatic $flag_options_map = [19 public static $flag_options_map = [ 23 20 'display' => 'flag_display_type', 24 21 'position' => 'display_position', … … 49 46 } 50 47 51 public function run($html_message = \null, $api_web_errors = []) {52 // Start session53 $sess = Session::getInstance()->start();54 // Set our CSRF token, always overrides55 $sess->generateCsrfToken();56 57 // We define this constant so user can't do direct access to the template58 $this->defineConstants(false);59 60 if (isset($_GET['linguise_action'])) {61 switch ($_GET['linguise_action']) {62 case 'download-debug':63 $this->downloadDebug();64 break;65 case 'update-config':66 break;67 default:68 if (empty($_SESSION[self::$token_key])) {69 $this->rejectGET();70 } else {71 $this->unknownGETAction();72 }73 break;74 }75 }76 77 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'Helper.php';78 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'stubs.php';79 80 if (!$sess->hasSession()) {81 // Render login page82 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'header.php';83 if (!empty($html_message)) {84 echo $html_message;85 }86 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'oobe.php';87 } else {88 // Logged in? we send it!89 $this->defineConstants(true);90 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'header.php';91 if (!empty($html_message)) {92 echo $html_message;93 }94 $view_mode = isset($_GET['ling_mode']) ? $_GET['ling_mode'] : 'default';95 if ($view_mode === 'expert') {96 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'management-expert.php';97 } else {98 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'management.php';99 }100 }101 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'footer.php';102 die();103 }104 105 public function oobeRun($html_message = \null) {106 // Start session107 $sess = Session::getInstance()->start();108 // Set our CSRF token, always overrides109 $sess->generateCsrfToken();110 111 $this->defineConstants(false);112 113 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'stubs.php';114 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'header.php';115 if (!empty($html_message)) {116 echo $html_message;117 }118 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'oobe.php';119 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'footer.php';120 }121 122 48 public function editorRun() { 123 $this->defineConstants(false);49 Helper::defineConstants(false); 124 50 125 51 require_once LINGUISE_BASE_DIR . 'src' . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . 'stubs.php'; … … 139 65 if (!$sess->hasSession()) { 140 66 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Not authorized</div>'; 141 $this->run($message, $api_web_errors); 67 $oobe = OobeManager::getInstance(); 68 $oobe->run($message, $api_web_errors); 142 69 } 143 70 if (!isset($_POST['_token'])) { 144 $this->errorJSON('Missing CSRF token', 400);71 HttpResponse::errorJSON('Missing CSRF token', 400); 145 72 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Missing CSRF token</div>'; 146 $this->run($message, $api_web_errors); 73 $oobe = OobeManager::getInstance(); 74 $oobe->run($message, $api_web_errors); 147 75 } 148 76 if (!$sess->verifyCsrfToken('linguise_config', $_POST['_token'])) { 149 77 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Invalid CSRF token</div>'; 150 $this->run($message, $api_web_errors); 78 $oobe = OobeManager::getInstance(); 79 $oobe->run($message, $api_web_errors); 151 80 } 152 81 … … 187 116 $dynamic_translations['public_key'] = $api_result['data']['public_key']; 188 117 $linguise_options = Helper::transformToLocalConfig($linguise_options, $api_result['data']); 189 $dynamic_translations['enabled'] = $this->boolInt($api_result['data']['dynamic_translations']['enabled']);118 $dynamic_translations['enabled'] = Helper::boolInt($api_result['data']['dynamic_translations']['enabled']); 190 119 $token_changed = true; 191 120 } else if ($api_result !== false && isset($api_result['status_code'])) { … … 234 163 if ($token_changed) { 235 164 // If token changed, we update enabled state 236 $dynamic_translations['enabled'] = $this->boolInt($api_result['data']['dynamic_translations']['enabled']);165 $dynamic_translations['enabled'] = Helper::boolInt($api_result['data']['dynamic_translations']['enabled']); 237 166 } 238 167 } else if ($api_result !== false && isset($api_result['status_code'])) { … … 277 206 'display_position' => isset($linguise_options['display_position']) ? $linguise_options['display_position'] : 'no', 278 207 'flag_display_type' => isset($linguise_options['flag_display_type']) ? $linguise_options['flag_display_type'] : 'popup', 279 'enable_flag' => $this->boolInt($enable_flag),280 'enable_language_name' => $this->boolInt($enable_language_name),281 'enable_language_name_popup' => $this->boolInt($enable_language_name_popup),282 'enable_language_short_name' => $this->boolInt($enable_language_short_name),208 'enable_flag' => Helper::boolInt($enable_flag), 209 'enable_language_name' => Helper::boolInt($enable_language_name), 210 'enable_language_name_popup' => Helper::boolInt($enable_language_name_popup), 211 'enable_language_short_name' => Helper::boolInt($enable_language_short_name), 283 212 'language_name_display' => isset($linguise_options['language_name_display']) ? $linguise_options['language_name_display'] : 'en', 284 213 'flag_shape' => isset($linguise_options['flag_shape']) ? $linguise_options['flag_shape'] : 'rounded', … … 321 250 'language' => $default_language, 322 251 'allowed_languages' => $translate_languages, 323 'dynamic_translations' => $this->intBool($dynamic_translations['enabled']),252 'dynamic_translations' => Helper::intBool($dynamic_translations['enabled']), 324 253 'language_settings' => [ 325 254 // Flag related 326 255 'display' => $linguise_options['flag_display_type'], 327 256 'position' => $linguise_options['display_position'], 328 'enabled_flag' => $this->intBool($enable_flag),329 'enabled_lang_name' => $this->intBool($enable_language_name),330 'enabled_lang_name_popup' => $this->intBool($enable_language_name_popup),331 'enabled_lang_short_name' => $this->intBool($enable_language_short_name),257 'enabled_flag' => Helper::intBool($enable_flag), 258 'enabled_lang_name' => Helper::intBool($enable_language_name), 259 'enabled_lang_name_popup' => Helper::intBool($enable_language_name_popup), 260 'enabled_lang_short_name' => Helper::intBool($enable_language_short_name), 332 261 'lang_name_display' => $linguise_options['language_name_display'], 333 262 'flag_shape' => $linguise_options['flag_shape'], … … 410 339 411 340 // Re-render 412 $this->run($notification_popup_msg, $api_web_errors); 341 $oobe = OobeManager::getInstance(); 342 $oobe->run($notification_popup_msg, $api_web_errors); 413 343 } 414 344 … … 418 348 $sess = Session::getInstance()->start(); 419 349 if (!$sess->hasSession()) { 420 $this->errorJSON('Unauthorized', 401);350 HttpResponse::errorJSON('Unauthorized', 401); 421 351 } 422 352 if (!isset($_POST['nonce'])) { 423 $this->errorJSON('Missing nonce token', 400);353 HttpResponse::errorJSON('Missing nonce token', 400); 424 354 } 425 355 if (!$sess->verifyCsrfToken('linguise_config_iframe', $_POST['nonce'])) { 426 $this->errorJSON('Invalid nonce token', 403);356 HttpResponse::errorJSON('Invalid nonce token', 403); 427 357 } 428 358 … … 441 371 if (!isset($data[$field])) { 442 372 // response with error 443 $this->errorJSON('Missing required field: ' . $field, 400);373 HttpResponse::errorJSON('Missing required field: ' . $field, 400); 444 374 } 445 375 } … … 475 405 476 406 $db->saveOtherParam('linguise_options', $options); 477 $this->successJSON(true, 'Configuration updated successfully', 200);407 HttpResponse::successJSON(true, 'Configuration updated successfully', 200); 478 408 } 479 409 … … 484 414 $jwt_token = isset($_SERVER['HTTP_X_LINGUISE_HASH']) ? $_SERVER['HTTP_X_LINGUISE_HASH'] : null; 485 415 if (empty($jwt_token)) { 486 $this->errorJSON('Missing Hash header', 400);416 HttpResponse::errorJSON('Missing Hash header', 400); 487 417 } 488 418 … … 490 420 $input_data = file_get_contents('php://input'); 491 421 if (empty($input_data)) { 492 $this->errorJSON('Invalid request', 400);422 HttpResponse::errorJSON('Invalid request', 400); 493 423 } 494 424 495 425 $input_data = json_decode($input_data, true); 496 426 if (json_last_error() !== JSON_ERROR_NONE) { 497 $this->errorJSON('Invalid JSON data', 400);427 HttpResponse::errorJSON('Invalid JSON data', 400); 498 428 } 499 429 … … 503 433 504 434 if ($input_data['token'] !== $options['token']) { 505 $this->errorJSON('Invalid token', 401);435 HttpResponse::errorJSON('Invalid token', 401); 506 436 } 507 437 … … 525 455 $flag_real_key = self::$flag_options_map[$flag_key]; 526 456 } 527 $options[$flag_real_key] = $this->boolInt($flag_value);457 $options[$flag_real_key] = Helper::boolInt($flag_value); 528 458 } 529 459 } … … 531 461 // Save 532 462 $db->saveOtherParam('linguise_options', $options); 533 $this->successJSON(true, 'Configuration updated successfully', 200); 534 } 535 536 public function login() 537 { 538 if (!isset($_POST['_token'])) { 539 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Missing CSRF token</div>'; 540 $this->oobeRun($message); 541 } 542 $sess = Session::getInstance()->start(); 543 if (!$sess->verifyCsrfToken('linguise_oobe_login', $_POST['_token'])) { 544 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Invalid CSRF token</div>'; 545 $this->oobeRun($message); 546 } 547 548 // Authenticate ourself, get the token or password 549 if ($sess->oobeComplete() && !empty($_POST['password'])) { 550 $existing_password = Database::getInstance()->ensureConnection()->retrieveOtherParam('linguise_password'); 551 if ($existing_password && password_verify($_POST['password'], $existing_password)) { 552 // Set session 553 $sess->setSession($existing_password, true); 554 $this->run(\null, []); 555 } else { 556 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Invalid password</div>'; 557 $this->oobeRun($message); 558 } 559 } else { 560 $sess->unsetSession(); 561 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>Invalid request</div>'; 562 $this->oobeRun($message); 563 } 564 } 565 566 public function mergeConfig($skip_missing = \false) 567 { 568 /** 569 * @disregard P1011 - already checked 570 */ 571 $use_mysql = defined('LINGUISE_OOBE_MYSQL') && LINGUISE_OOBE_MYSQL; 572 573 // Each config for the mysql, LINGUISE_OOBE_MYSQL_DB_{HOST|USER|PASSWORD|NAME|PREFIX|PORT} 574 $oobe_config = [ 575 'DB_HOST', 576 'DB_USER', 577 'DB_PASSWORD', 578 'DB_NAME', 579 'DB_PREFIX', 580 'DB_PORT', 581 'DB_FLAGS', 582 ]; 583 584 if ($use_mysql) { 585 foreach ($oobe_config as $config) { 586 if (defined('LINGUISE_OOBE_MYSQL_' . $config)) { 587 Configuration::getInstance()->set(strtolower($config), constant('LINGUISE_OOBE_MYSQL_' . $config)); 588 } 589 } 590 } 591 592 // Connect to database and install options 593 Helper::prepareDataDir(); 594 $db = Database::getInstance(true); 595 596 if (!LINGUISE_OOBE_DONE) { 597 // Not yet ready 598 return; 599 } 600 601 $db->ensureConnection()->installOptions(); 602 603 // Get metadata 604 $existing_options = $db->retrieveOtherParam('linguise_options'); 605 if (!empty($existing_options)) { 606 // Merge existing options 607 Configuration::getInstance()->set('token', $existing_options['token']); 608 Configuration::getInstance()->set('cache_enabled', $existing_options['cache_enabled']); 609 Configuration::getInstance()->set('cache_max_size', $existing_options['cache_max_size']); 610 Configuration::getInstance()->set('search_translations', $existing_options['search_translations']); 611 Configuration::getInstance()->set('debug', $existing_options['debug']); 612 613 foreach ($existing_options['expert_mode'] as $key => $value) { 614 Configuration::getInstance()->set($key, $value); 615 } 616 } else { 617 if ($skip_missing) { 618 // Skip if missing 619 return; 620 } 621 622 // We create new options 623 $current_token = Configuration::getInstance()->get('token'); 624 if ($current_token === 'REPLACE_BY_YOUR_TOKEN') { 625 $current_token = ''; 626 } 627 $this->createOptionsWithToken($current_token); 628 } 629 } 630 631 private function oobeRunError($message_str, $status_code = 200) 632 { 633 $message = '<div class="linguise-notification-popup"><span class="material-icons fail">check</span>' . $message_str . '</div>'; 634 http_response_code($status_code); 635 $this->oobeRun($message); 636 die(); 637 } 638 639 public function activateLinguise() 640 { 641 // Check if OOBE 642 if (defined('LINGUISE_OOBE_DONE') && LINGUISE_OOBE_DONE) { 643 $this->oobeRunError('Not allowed', 403); 644 } elseif (!defined('LINGUISE_OOBE_DONE')) { 645 // Missing data 646 $this->oobeRunError('Unknown status', 500); 647 } 648 649 if (!isset($_POST['_token'])) { 650 $this->oobeRunError('Missing CSRF token', 400); 651 } 652 $sess = Session::getInstance()->start(); 653 if (!$sess->verifyCsrfToken('linguise_oobe_register', $_POST['_token'])) { 654 $this->oobeRunError('Invalid CSRF token', 403); 655 } 656 657 $new_pass = isset($_POST['password']) ? $_POST['password'] : null; 658 if (empty($new_pass)) { 659 $this->oobeRunError('Missing password', 400); 660 } 661 662 // == Password check == 663 // Must be at least 10 characters long 664 if (strlen($new_pass) < 10) { 665 $this->oobeRunError('Password must be at least 10 characters long', 400); 666 } 667 668 if ($sess->hasSession()) { 669 $this->oobeRunError('Already activated', 403); 670 } 671 672 $is_token = defined('LINGUISE_OOBE_TOKEN_EXIST') && LINGUISE_OOBE_TOKEN_EXIST; 673 if ($is_token) { 674 if (!isset($_POST['token'])) { 675 $this->oobeRunError('Missing token', 400); 676 } 677 678 $token = $_POST['token']; 679 if ($token !== Configuration::getInstance()->get('token')) { 680 $this->oobeRunError('Invalid token provided in session', 401); 681 } 682 683 // This will automatically return error response 684 $database_store = $this->storeDatabaseConnection(false); 685 // set configuration 686 if ($database_store['MYSQL']) { 687 Configuration::getInstance()->set('db_host', $database_store['MYSQL_DB_HOST']); 688 Configuration::getInstance()->set('db_user', $database_store['MYSQL_DB_USER']); 689 Configuration::getInstance()->set('db_password', $database_store['MYSQL_DB_PASSWORD']); 690 Configuration::getInstance()->set('db_name', $database_store['MYSQL_DB_NAME']); 691 Configuration::getInstance()->set('db_prefix', $database_store['MYSQL_DB_PREFIX']); 692 Configuration::getInstance()->set('db_port', $database_store['MYSQL_DB_PORT']); 693 } else { 694 // SQLite 695 Configuration::getInstance()->set('db_host', ''); 696 Configuration::getInstance()->set('db_user', ''); 697 Configuration::getInstance()->set('db_password', ''); 698 Configuration::getInstance()->set('db_name', ''); 699 Configuration::getInstance()->set('db_prefix', ''); 700 701 $sqlite_test = $this->prepareRootDatabaseSQLite(); 702 if ($sqlite_test !== true) { 703 $this->oobeRunError($sqlite_test, 500); 704 } 705 } 706 707 // Init database 708 Helper::prepareDataDir(); 709 $db = Database::getInstance(true, true)->ensureConnection(); 710 $db->installOptions(); 711 712 // Valid, then let's set the password 713 $hashed_pass = $this->hashPassword($new_pass); 714 $db->saveOtherParam('linguise_password', $hashed_pass); 715 // Create the options 716 $db_options = $this->createOptionsWithToken($token); 717 718 // Write OOBE config 719 $this->writeOOBE($database_store); 720 721 $api_result = $this->getRemoteData($token); 722 if ($api_result !== false && isset($api_result['data'])) { 723 $default_language = Helper::sanitizeKey($api_result['data']['language']); 724 725 $translation_languages = $api_result['data']['languages']; 726 $translate_languages = []; 727 if (!empty($translation_languages)) { 728 foreach ($translation_languages as $translation_language) { 729 $translate_languages[] = Helper::sanitizeKey($translation_language['code']); 730 } 731 } 732 733 $db_options['enabled_languages'] = $translate_languages; 734 $db_options['default_language'] = $default_language; 735 $db_options['dynamic_translations']['enabled'] = $this->intBool($api_result['data']['dynamic_translations']['enabled']); 736 $db_options['dynamic_translations']['public_key'] = $api_result['data']['public_key']; 737 738 if (!empty($api_result['data']['language_settings'])) { 739 foreach ($api_result['data']['language_settings'] as $flag_key => $flag_value) { 740 $flag_real_key = $flag_key; 741 if (isset(self::$flag_options_map[$flag_key])) { 742 $flag_real_key = self::$flag_options_map[$flag_key]; 743 } 744 $db_options[$flag_real_key] = $this->boolInt($flag_value); 745 } 746 } 747 748 // Re-save with API data 749 $db->saveOtherParam('linguise_options', $db_options); 750 } 751 752 // Set session, and login the user. 753 $sess->setOobeForced(); 754 $sess->setSession($hashed_pass, true); // Set session with password mode 755 $this->run(); 756 die(); 757 } else { 758 // This will automatically return error response 759 $database_store = $this->storeDatabaseConnection(false); 760 // set configuration 761 if ($database_store['MYSQL']) { 762 Configuration::getInstance()->set('db_host', $database_store['MYSQL_DB_HOST']); 763 Configuration::getInstance()->set('db_user', $database_store['MYSQL_DB_USER']); 764 Configuration::getInstance()->set('db_password', $database_store['MYSQL_DB_PASSWORD']); 765 Configuration::getInstance()->set('db_name', $database_store['MYSQL_DB_NAME']); 766 Configuration::getInstance()->set('db_prefix', $database_store['MYSQL_DB_PREFIX']); 767 Configuration::getInstance()->set('db_port', $database_store['MYSQL_DB_PORT']); 768 } else { 769 // SQLite 770 Configuration::getInstance()->set('db_host', ''); 771 Configuration::getInstance()->set('db_user', ''); 772 Configuration::getInstance()->set('db_password', ''); 773 Configuration::getInstance()->set('db_name', ''); 774 Configuration::getInstance()->set('db_prefix', ''); 775 776 $sqlite_test = $this->prepareRootDatabaseSQLite(); 777 if ($sqlite_test !== true) { 778 $this->oobeRunError($sqlite_test, 500); 779 } 780 } 781 782 // Init database 783 Helper::prepareDataDir(); 784 $db = Database::getInstance(true, true)->ensureConnection(); 785 $db->installOptions(); 786 787 // No token, we just set it immediately 788 $existing_password = $db->retrieveOtherParam('linguise_password'); 789 if ($existing_password) { 790 $this->oobeRunError('Password already set', 400); 791 } 792 793 // Set the password 794 $hashed_pass = $this->hashPassword($new_pass); 795 796 // Create 797 $db->saveOtherParam('linguise_password', $hashed_pass); 798 // Create the options 799 $this->createOptionsWithToken(''); // Empty token since no token provided yet. 800 801 // Write OOBE config 802 $this->writeOOBE($database_store); 803 804 805 // Set session, and login the user. 806 $sess->setOobeForced(); 807 $sess->setSession($hashed_pass, true); // Set session with password mode 808 $this->run(); 809 die(); 810 } 811 } 812 813 public function storeDatabaseConnection($testMode = \false) 814 { 815 if ($testMode) { 816 if (!isset($_POST['_token'])) { 817 $this->errorJSON('Missing CSRF token', 400); 818 } 819 $sess = Session::getInstance()->start(); 820 if (!$sess->verifyCsrfToken('linguise_oobe_register', $_POST['_token'])) { 821 $this->errorJSON('Invalid CSRF token', 403); 822 } 823 } 824 825 // Get from POST data 826 $mode = $_POST['db_mode'] ?? null; 827 $host = $_POST['db_host'] ?? null; 828 $user = $_POST['db_user'] ?? null; 829 $password = $_POST['db_password'] ?? null; 830 $name = $_POST['db_name'] ?? null; 831 $port = $_POST['db_port'] ?? 3306; 832 $prefix = $_POST['db_prefix'] ?? null; 833 834 if (empty($mode)) { 835 $this->errorJSON('Missing `db_mode` data', 400); 836 } 837 838 switch ($mode) { 839 case 'mysql': 840 if (empty($host) || empty($user) || empty($name)) { 841 $this->errorJSON('Missing `db_host`, `db_user` or `db_name` data', 400); 842 } 843 if (!$testMode && (empty($prefix))) { 844 $this->errorJSON('Missing `db_prefix` data', 400); 845 } 846 847 if ($testMode) { 848 $result = $this->testMySQL($host, $user, $password, $name, $port, $prefix); 849 if ($result !== true) { 850 $this->errorJSON($result, 500); 851 } 852 $this->successJSON(true, 'MySQL connection test successful', 200); 853 } 854 855 return [ 856 'MYSQL' => true, 857 'MYSQL_DB_HOST' => $host, 858 'MYSQL_DB_USER' => $user, 859 'MYSQL_DB_PASSWORD' => $password, 860 'MYSQL_DB_NAME' => $name, 861 'MYSQL_DB_PORT' => $port, 862 'MYSQL_DB_PREFIX' => $prefix, 863 ]; 864 case 'sqlite': 865 // Check if SQLite3 is enabled 866 if ($testMode) { 867 $result = $this->testSqlite(); 868 if ($result !== true) { 869 $this->errorJSON($result, 500); 870 } 871 $this->successJSON(true, 'SQLite connection test successful', 200); 872 } 873 874 // Store the SQLite connection 875 return [ 876 'MYSQL' => false, 877 ]; 878 default: 879 $this->errorJSON('Invalid `db_mode` data', 400); 880 } 881 } 882 883 private function testMySQL($host, $user, $password, $name, $port = 3306, $prefix = '') 884 { 885 // Check if MySQLi is enabled 886 if (!extension_loaded('mysqli')) { 887 return 'MySQLi extension not loaded'; 888 } 889 890 // Check if MySQLi class exist 891 if (!class_exists('mysqli')) { 892 return 'MySQLi class not found'; 893 } 894 895 // Attempt to connect to the database 896 $connection = \null; 897 try { 898 $connection = new \mysqli($host, $user, $password, $name, $port); 899 } catch (\mysqli_sql_exception $e) { 900 return 'Connection failed: ' . $e->getMessage(); 901 } 902 903 if (empty($connection)) { 904 return 'Connection failed: No connection object created'; 905 } 906 907 if ($connection->connect_error) { 908 return 'Connection failed: ' . $connection->connect_error; 909 } 910 911 // Close the connection 912 $connection->close(); 913 914 return true; 915 } 916 917 private function testSqlite() 918 { 919 // Check if SQLite3 is enabled 920 if (!extension_loaded('sqlite3')) { 921 return 'SQLite3 extension not loaded'; 922 } 923 924 // Check if SQLite3 class exist 925 if (!class_exists('SQLite3')) { 926 return 'SQLite3 class not found'; 927 } 928 929 // Check if we can write in LINGUISE_BASE_DIR / .linguise-main.db 930 $sqlite_test = $this->prepareRootDatabaseSQLite(); 931 if ($sqlite_test !== true) { 932 return $sqlite_test; 933 } 934 935 if (!Helper::checkDataDirAvailable()) { 936 return 'Cannot write to data directory'; 937 } 938 939 return true; 940 } 941 942 private function prepareRootDatabaseSQLite() 943 { 944 $databases_dir = LINGUISE_BASE_DIR . '.databases' . DIRECTORY_SEPARATOR; 945 if (!file_exists($databases_dir)) { 946 if (!mkdir($databases_dir, 0766, true)) { 947 return 'Cannot create database directory: ' . $databases_dir; 948 } 949 } 950 951 $htaccess_file = $databases_dir . '.htaccess'; 952 if (!file_exists($htaccess_file)) { 953 $written = file_put_contents($htaccess_file, 'deny from all'); 954 if ($written === false) { 955 return 'Cannot write to database directory: ' . $databases_dir; 956 } 957 } 958 959 $db_path = $databases_dir . 'linguise-main.db'; 960 if (!file_exists($db_path)) { 961 // touch the file 962 $db_touch = touch($db_path); 963 if (!$db_touch) { 964 return 'Cannot create database file: ' . $db_path; 965 } 966 unlink($db_path); 967 } else { 968 if (!is_writable($db_path)) { 969 return 'Cannot write database to ' . $db_path; 970 } 971 } 972 973 return true; 974 } 975 976 private function createOptionsWithToken($token) 977 { 978 $db = Database::getInstance()->ensureConnection(); 979 $options = [ 980 'token' => $token, 981 'cache_enabled' => Configuration::getInstance()->get('cache_enabled'), 982 'cache_max_size' => Configuration::getInstance()->get('cache_max_size'), 983 'search_translations' => Configuration::getInstance()->get('search_translations'), 984 'debug' => Configuration::getInstance()->get('debug'), 985 'dynamic_translations' => [ 986 'enabled' => 0, 987 'public_key' => null, 988 ], 989 990 // Languages related 991 'default_language' => 'en', 992 'enabled_languages' => [], 993 994 // Flag related 995 'display_position' => 'bottom_right', 996 'flag_display_type' => 'popup', 997 'enable_flag' => 1, 998 'enable_language_name' => 1, 999 'enable_language_short_name' => 0, 1000 'language_name_display' => 'en', 1001 'flag_shape' => 'round', 1002 'flag_en_type' => 'en-us', 1003 'flag_de_type' => 'de', 1004 'flag_es_type' => 'es', 1005 'flag_pt_type' => 'pt', 1006 'flag_tw_type' => 'zh-tw', 1007 'flag_border_radius' => 0, 1008 'flag_width' => 24, 1009 'pre_text' => '', 1010 'post_text' => '', 1011 'custom_css' => '', 1012 'language_name_color' => '#222', 1013 'language_name_hover_color' => '#222', 1014 'popup_language_name_color' => '#222', 1015 'popup_language_name_hover_color' => '#222', 1016 'flag_shadow_h' => 2, 1017 'flag_shadow_v' => 2, 1018 'flag_shadow_blur' => 12, 1019 'flag_shadow_spread' => 0, 1020 'flag_shadow_color' => '#eee', 1021 'flag_shadow_color_alpha' => (float)1.0, // we use 100% scaling, 0.0-1.0 1022 'flag_hover_shadow_h' => 3, 1023 'flag_hover_shadow_v' => 3, 1024 'flag_hover_shadow_blur' => 6, 1025 'flag_hover_shadow_spread' => 0, 1026 'flag_hover_shadow_color' => '#bfbfbf', 1027 'flag_hover_shadow_color_alpha' => (float)1.0, 1028 1029 'expert_mode' => [], 1030 ]; 1031 1032 $db->saveOtherParam('linguise_options', $options); 1033 1034 return $options; 1035 } 1036 1037 private function errorJSON($message, $code = 500) 1038 { 1039 header('Content-Type: application/json; charset=utf-8'); 1040 http_response_code($code); 1041 echo json_encode([ 1042 'error' => true, 1043 'message' => $message 1044 ]); 1045 exit; 1046 } 1047 1048 private function successJSON($data, $message = '', $code = 200) 1049 { 1050 header('Content-Type: application/json; charset=utf-8'); 1051 http_response_code($code); 1052 echo json_encode([ 1053 'error' => false, 1054 'message' => $message, 1055 'data' => $data 1056 ]); 1057 exit; 1058 } 1059 1060 private function hashPassword($input) 1061 { 1062 $hashed = password_hash($input, PASSWORD_BCRYPT, [ 1063 'cost' => 12, 1064 ]); 1065 1066 return $hashed; 1067 } 1068 1069 private function writeOOBE($database_store) 1070 { 1071 // Modify OOBE status in ui-config.php 1072 $ui_config = realpath(__DIR__ . DIRECTORY_SEPARATOR . '..') . DIRECTORY_SEPARATOR . 'ui-config.php'; 1073 $content = file_get_contents($ui_config); 1074 1075 $replaced_content = preg_replace('/define\([\'"]LINGUISE_OOBE_DONE[\'"], .*\);/m', 'define(\'LINGUISE_OOBE_DONE\', true);', $content); 1076 if (empty($replaced_content)) { 1077 $this->errorJSON('Failed to update ui-config.php', 500); 1078 } 1079 1080 // Update the database connection 1081 foreach ($database_store as $key => $value) { 1082 // Push content 1083 $value_wrap = json_encode($value); 1084 $replaced_content .= "\ndefine('LINGUISE_OOBE_" . $key . "', " . $value_wrap . ");\n"; 1085 } 1086 1087 file_put_contents($ui_config, $replaced_content); 1088 } 1089 1090 public function clearDebug() 1091 { 1092 // Verify session 1093 $sess = Session::getInstance()->start(); 1094 if (!$sess->hasSession()) { 1095 $this->errorJSON('Unauthorized', 401); 1096 } 1097 if (!isset($_GET['nonce'])) { 1098 $this->errorJSON('Missing nonce token', 400); 1099 } 1100 if (!$sess->verifyCsrfToken('linguise_clear_debug', $_GET['nonce'])) { 1101 $this->errorJSON('Invalid nonce token', 403); 1102 } 1103 1104 // Clear the debug file 1105 $debug_file = LINGUISE_BASE_DIR . 'debug.php'; 1106 $last_errors_file = LINGUISE_BASE_DIR . 'errors.php'; 1107 if (file_exists($debug_file)) { 1108 file_put_contents($debug_file, "<?php die(); ?>" . PHP_EOL); 1109 } 1110 if (file_exists($last_errors_file)) { 1111 file_put_contents($last_errors_file, "<?php die(); ?>" . PHP_EOL); 1112 } 1113 1114 $this->successJSON(true, 'Log truncated!', 200); 1115 } 1116 1117 public function clearCache() 1118 { 1119 // Verify session 1120 $sess = Session::getInstance()->start(); 1121 if (!$sess->hasSession()) { 1122 $this->errorJSON('Unauthorized', 401); 1123 } 1124 if (!isset($_GET['nonce'])) { 1125 $this->errorJSON('Missing nonce token', 400); 1126 } 1127 if (!$sess->verifyCsrfToken('linguise_clear_cache', $_GET['nonce'])) { 1128 $this->errorJSON('Invalid nonce token', 403); 1129 } 1130 1131 // Clear the cache 1132 Cache::getInstance()->clearAll(); 1133 } 1134 1135 private function downloadDebug() 1136 { 1137 // Verify session 1138 $sess = Session::getInstance()->start(); 1139 if (!$sess->hasSession()) { 1140 die('Unauthorized'); 1141 } 1142 1143 $debug_file = LINGUISE_BASE_DIR . 'debug.php'; 1144 if (file_exists($debug_file)) { 1145 header('Content-Description: File Transfer'); 1146 header('Content-Type: application/octet-stream'); 1147 header('Content-Disposition: attachment; filename="debug.txt"'); 1148 header('Content-Transfer-Encoding: binary'); 1149 header('Expires: 0'); 1150 header('Cache-Control: must-revalidate, post-check=0, pre-check=0'); 1151 header('Pragma: public'); 1152 header('Content-Length: ' . filesize($debug_file)); 1153 ob_clean(); 1154 ob_end_flush(); 1155 $handle = fopen($debug_file, 'rb'); 1156 while (!feof($handle)) { 1157 // phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped 1158 echo fread($handle, 1000); 1159 } 1160 1161 die(); 1162 } else { 1163 die('No debug file found'); 1164 } 1165 } 1166 1167 private function defineConstants($is_logged_in = false) 1168 { 1169 if (!defined('LINGUISE_MANAGEMENT')) { 1170 define('LINGUISE_MANAGEMENT', 1); 1171 } 1172 1173 if ($is_logged_in && !defined('LINGUISE_AUTHORIZED')) { 1174 define('LINGUISE_AUTHORIZED', 1); 1175 } 1176 1177 // Silent debug noises 1178 $request = Request::getInstance(true); 1179 $base_dir = rtrim($request->getBaseDir(), '/'); 1180 if (!defined('LINGUISE_BASE_URL')) { 1181 define('LINGUISE_BASE_URL', $base_dir . '/linguise'); 1182 } 463 HttpResponse::successJSON(true, 'Configuration updated successfully', 200); 1183 464 } 1184 465 … … 1226 507 1227 508 if ($response_code !== 200) { 1228 $this->errorJSON('Invalid JWT verification token: ' . print_r($response_code, true), 403);509 HttpResponse::errorJSON('Invalid JWT verification token: ' . print_r($response_code, true), 403); 1229 510 } 1230 511 … … 1233 514 } 1234 515 1235 p rivatefunction getRemoteData($token)516 public function getRemoteData($token) 1236 517 { 1237 518 // Config get is api.linguise.com/api/config … … 1333 614 } 1334 615 1335 public function logout()1336 {1337 // Verify session1338 $sess = Session::getInstance()->start();1339 // Verify session1340 if ($sess->hasSession()) {1341 // Unset session1342 $sess->unsetSession();1343 $this->successJSON(true, 'Logout successful', 200);1344 } else {1345 // No session, we just redirect to login page1346 $this->successJSON(true, 'No session found', 200);1347 exit;1348 }1349 }1350 1351 616 private function getApiRoot() 1352 617 { … … 1369 634 return $protocol . '://' . $api_host . (in_array($api_port, $api_port_base) ? '' : ':' . $api_port) . ''; 1370 635 } 1371 1372 public function rejectGET()1373 {1374 header('Content-Type: text/html; charset=utf-8');1375 http_response_code(403);1376 echo '<h1>403 Forbidden</h1>';1377 echo '<p>You are not allowed to access this page.</p>';1378 die();1379 }1380 1381 private function unknownGETAction()1382 {1383 header('Content-Type: text/html; charset=utf-8');1384 http_response_code(400);1385 echo '<h1>400 Bad Request</h1>';1386 echo '<p>Unknown action.</p>';1387 die();1388 }1389 1390 /**1391 * Convert a boolean value to an integer (0 or 1)1392 *1393 * @param mixed $value The value to convert1394 *1395 * @return mixed The converted value (0 or 1), or original1396 */1397 private function boolInt($value)1398 {1399 if (is_bool($value)) {1400 return $value ? 1 : 0;1401 }1402 1403 return $value;1404 }1405 1406 /**1407 * Convert an integer (0 or 1) to a boolean value1408 *1409 * @param mixed $value The value to convert1410 *1411 * @return mixed The converted value (true or false), or original1412 */1413 private function intBool($value)1414 {1415 if (is_bool($value)) {1416 return $value;1417 }1418 if (is_int($value)) {1419 return $value === 1;1420 }1421 1422 return $value;1423 }1424 636 }
Note: See TracChangeset
for help on using the changeset viewer.