Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
138 changes: 135 additions & 3 deletions admin/controller/module/tawkto.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@

class Tawkto extends Controller
{
public const CREDENTIALS_FILE = DIR_EXTENSION . 'tawkto/system/config/credentials.json';
public const NO_CHANGE = 'nochange';

/**
* __construct
*/
Expand Down Expand Up @@ -148,6 +151,7 @@ private function getStoreHierarchy()
'display_opts' => $this->getDisplayOpts($currentSettings),
'privacy_opts' => $this->getPrivacyOpts($currentSettings),
'cart_opts' => $this->getCartOpts($currentSettings),
'security_opts' => $this->getSecurityOpts($currentSettings),
);

foreach ($stores as $store) {
Expand All @@ -160,6 +164,7 @@ private function getStoreHierarchy()
'display_opts' => $this->getDisplayOpts($currentSettings),
'privacy_opts' => $this->getPrivacyOpts($currentSettings),
'cart_opts' => $this->getCartOpts($currentSettings),
'security_opts' => $this->getSecurityOpts($currentSettings),
);
}

Expand Down Expand Up @@ -250,6 +255,8 @@ public function setoptions()

$cartOpts = $this->config->get('tawkto_cart');

$securityOpts = $this->config->get('tawkto_security');

if (isset($_POST['options']) && !empty($_POST['options'])) {
$options = explode('&', $_POST['options']);

Expand Down Expand Up @@ -286,14 +293,63 @@ public function setoptions()
case 'monitor_customer_cart':
$cartOpts[$key] = true;
break;

case 'secure_mode_enabled':
$securityOpts[$key] = true;
break;

case 'js_api_key':
if ($value === self::NO_CHANGE) {
unset($securityOpts['js_api_key']);
break;
}

if ($value === '') {
break;
}

$value = trim($value);

if (strlen($value) !== 40) {
throw new \Exception('Invalid API key.');
}

try {
$securityOpts['js_api_key'] = $this->encryptData($value);
} catch (\Exception $e) {
error_log($e->getMessage());

unset($securityOpts['js_api_key']);

echo json_encode(array('success' => false, 'message' => 'Error saving Javascript API Key.'));
die();
}
}
}
}

$currentSettings = $this->getCurrentSettingsFor($store_id);
$currentSettings['module_tawkto_visibility'] = $visibilityOpts;
$currentSettings['module_tawkto_privacy'] = $privacyOpts;
$currentSettings['module_tawkto_cart'] = $cartOpts;
if (!isset($currentSettings['module_tawkto_visibility'])) {
$currentSettings['module_tawkto_visibility'] = array();
}
if (!isset($currentSettings['module_tawkto_privacy'])) {
$currentSettings['module_tawkto_privacy'] = array();
}
if (!isset($currentSettings['module_tawkto_cart'])) {
$currentSettings['module_tawkto_cart'] = array();
}
if (!isset($currentSettings['module_tawkto_security'])) {
$currentSettings['module_tawkto_security'] = array();
}
if (!isset($currentSettings['module_tawkto_config_version'])) {
$currentSettings['module_tawkto_config_version'] = $this->config->get('tawkto_config_version');
}

$currentSettings['module_tawkto_visibility'] = array_merge($currentSettings['module_tawkto_visibility'], $visibilityOpts);
$currentSettings['module_tawkto_privacy'] = array_merge($currentSettings['module_tawkto_privacy'], $privacyOpts);
$currentSettings['module_tawkto_cart'] = array_merge($currentSettings['module_tawkto_cart'], $cartOpts);
$currentSettings['module_tawkto_security'] = array_merge($currentSettings['module_tawkto_security'], $securityOpts);
$currentSettings['module_tawkto_config_version'] = $currentSettings['module_tawkto_config_version'] + 1;
$this->model_setting_setting->editSetting('module_tawkto', $currentSettings, $store_id);

echo json_encode(array('success' => true));
Expand Down Expand Up @@ -396,4 +452,80 @@ public function getCartOpts($settings)

return $options;
}

/**
* Get security options from setting
*
* @return Array
*/
public function getSecurityOpts($settings) {
$options = $this->config->get('tawkto_security');

if (isset($settings['module_tawkto_security'])) {
$options = $settings['module_tawkto_security'];
}

if (!empty($options['js_api_key'])) {
$options['js_api_key'] = self::NO_CHANGE;
}

return $options;
}


/**
* Get credentials
*
* @return Array
*/
private function getCredentials() {
if (file_exists(self::CREDENTIALS_FILE)) {
return json_decode(file_get_contents(self::CREDENTIALS_FILE), true);
}

$credentials = array(
'encryption_key' => bin2hex(random_bytes(32)),
);

file_put_contents(self::CREDENTIALS_FILE, json_encode($credentials));

return $credentials;
}

/**
* Encrypt data
*
* @param string $data Data to encrypt
*
* @return string Encrypted data
*
* @throws \Exception Error encrypting data
*/
private function encryptData($data) {
try {
$encryptionKey = $this->getCredentials()['encryption_key'];
} catch (\Exception $e) {
throw new \Exception('Failed to get encryption key');
}

try {
$iv = random_bytes(16);
} catch (\Exception $e) {
throw new \Exception('Failed to generate IV');
}

$encrypted = openssl_encrypt($data, 'AES-256-CBC', $encryptionKey, 0, $iv);

if ($encrypted === false) {
throw new \Exception('Failed to encrypt data');
}

$encrypted = base64_encode($iv . $encrypted);

if ($encrypted === false) {
throw new \Exception('Failed to encode data');
}

return $encrypted;
}
}
11 changes: 7 additions & 4 deletions admin/view/stylesheet/index.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,16 @@ html {
display: none;
}

#optionsSuccessMessage {
.alert {
position: absolute;
font-weight: bold;
display: none;
}

#optionsSuccessMessage {
background-color: #dff0d8;
color: #3c763d;
border-color: #d6e9c6;
font-weight: bold;
display: none;
}

.pull-right {
Expand All @@ -50,7 +53,7 @@ html {
}

@media only screen and (max-width: 1200px) {
#optionsSuccessMessage {
.alert {
position: relative;
margin-top: 1rem;
}
Expand Down
126 changes: 80 additions & 46 deletions admin/view/template/module/tawkto.twig
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,34 @@
</div>
</div>
<br></br>
<div class="col-lg-12">
<div class="panel-heading"><strong>Security Settings</strong></div>
</div>
<div class="form-group col-lg-12 row">
<label for="secure_mode_enabled" class="col-xs-6 control-label">
Secure Mode Enabled
<br>
<span class="attrib_desc">If Secure Mode is enabled on your property, please enter your
Javascript API Key to ensure visitor recognition works correctly.</span>
</label>
<div class="col-xs-6 control-label ">
<input type="checkbox" class="control-label col-xs-6" name="secure_mode_enabled"
id="secure_mode_enabled" value="1"
{{(hierarchy[0].security_opts.secure_mode_enabled)?'checked':''}} />
</div>
</div>
<div class="form-group col-lg-12 row">
<label for="js_api_key" class="col-xs-6 control-label">
Javascript API Key
<br>
<span class="attrib_desc">Used to authenticate visitor recognition when secure mode is enabled</span>
</label>
<div class="col-xs-6 control-label ">
<input type="password" class="form-control" name="js_api_key"
id="js_api_key" value="{{hierarchy[0].security_opts.js_api_key}}" />
</div>
</div>
<br></br>
<div class="col-lg-12">
<div class="panel-heading"><strong>Cart Integration</strong></div>
</div>
Expand All @@ -219,6 +247,9 @@
<div id="optionsSuccessMessage" class="alert alert-success col-lg-12">
Successfully set widget options to your site
</div>
<div id="optionsErrorMessage" class="alert alert-danger col-lg-12">
Failed to set widget options to your site
</div>
</div>
</div>
</form>
Expand Down Expand Up @@ -402,59 +433,62 @@
store: store_id,
options: form.serialize(),
}, function (r) {
if (r.success) {
$('#optionsSuccessMessage').toggle().delay(3000).fadeOut();
if (!r.success) {
$('#optionsErrorMessage').text(r.message).toggle().delay(3000).fadeOut();
return;
}

// Update saved options
var fields = form.serializeArray();
for (store of storeHierarchy) {
if (store.id !== store_id) {
continue;
}
$('#optionsSuccessMessage').toggle().delay(3000).fadeOut();

// Update saved options
var fields = form.serializeArray();
for (store of storeHierarchy) {
if (store.id !== store_id) {
continue;
}

store.display_opts = {
'always_display': false,
'show_onfrontpage': false,
'show_oncategory': false,
'show_oncustom': [],
'hide_oncustom': [],
};

store.display_opts = {
'always_display': false,
'show_onfrontpage': false,
'show_oncategory': false,
'show_oncustom': [],
'hide_oncustom': [],
};
store.privacy_opts = {
'enable_visitor_recognition': false,
}

store.privacy_opts = {
'enable_visitor_recognition': false,
store.cart_opts = {
'monitor_customer_cart': false,
}

for (field of fields) {
if (field.name === 'show_oncustom') {
store.display_opts['show_oncustom'] = field.value.replaceAll('\r', '\n').split('\n').filter(Boolean);
continue;
}

store.cart_opts = {
'monitor_customer_cart': false,
if (field.name === 'hide_oncustom') {
store.display_opts['hide_oncustom'] = field.value.replaceAll('\r', '\n').split('\n').filter(Boolean);
continue;
}

for (field of fields) {
if (field.name === 'show_oncustom') {
store.display_opts['show_oncustom'] = field.value.replaceAll('\r', '\n').split('\n').filter(Boolean);
continue;
}

if (field.name === 'hide_oncustom') {
store.display_opts['hide_oncustom'] = field.value.replaceAll('\r', '\n').split('\n').filter(Boolean);
continue;
}

// serializeArray() only includes "successful controls"
switch (field.name) {
case 'always_display':
case 'show_onfrontpage':
case 'show_oncategory':
store.display_opts[field.name] = true;
break;

case 'enable_visitor_recognition':
store.privacy_opts[field.name] = true;
break;

case 'monitor_customer_cart':
store.cart_opts[field.name] = true;
break;
}
// serializeArray() only includes "successful controls"
switch (field.name) {
case 'always_display':
case 'show_onfrontpage':
case 'show_oncategory':
store.display_opts[field.name] = true;
break;

case 'enable_visitor_recognition':
store.privacy_opts[field.name] = true;
break;

case 'monitor_customer_cart':
store.cart_opts[field.name] = true;
break;
}
}
}
Expand Down
Loading