Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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
24 changes: 24 additions & 0 deletions Api/ConfigInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<?php
/**
* Tawk.to
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to support@tawk.to so we can send you a copy immediately.
*
* @copyright Copyright (c) 2024 Tawk.to
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/

namespace Tawk\Widget\Api;

interface ConfigInterface
{
const JS_API_KEY_NO_CHANGE = 'no_change';
}
52 changes: 50 additions & 2 deletions Block/Embed.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,16 @@
use Magento\Framework\View\Element\Template;
use Magento\Framework\Escaper;
use Magento\Customer\Model\SessionFactory;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Exception\LocalizedException;

use Tawk\Modules\UrlPatternMatcher;
use Tawk\Widget\Model\WidgetFactory;

class Embed extends Template
{
public const TAWKTO_JS_API_KEY = 'TAWKTO_JS_API_KEY';

/**
* Tawk.to Widget Model instance
*
Expand Down Expand Up @@ -76,20 +80,29 @@ class Embed extends Template
*/
protected $escaper;

/**
* Encryptor instance
*
* @var EncryptorInterface $encryptor
*/
protected $encryptor;

/**
* Constructor
*
* @param SessionFactory $sessionFactory Session Factory instance
* @param WidgetFactory $modelFactory Tawk.to Widget Model instance
* @param Template\Context $context Template Context
* @param Escaper $escaper Escaper instance
* @param EncryptorInterface $encryptor Encryptor instance
* @param array $data Template data
*/
public function __construct(
SessionFactory $sessionFactory,
WidgetFactory $modelFactory,
Template\Context $context,
Escaper $escaper,
EncryptorInterface $encryptor,
array $data = []
) {
parent::__construct($context, $data);
Expand All @@ -100,6 +113,7 @@ public function __construct(
$this->request = $context->getRequest();
$this->modelSessionFactory = $sessionFactory->create();
$this->escaper = $escaper;
$this->encryptor = $encryptor;
}

/**
Expand Down Expand Up @@ -146,7 +160,8 @@ private function getWidgetModel()
*
* @return array {
* name: string,
* email: string
* email: string,
* hash: string
* }
*/
public function getCurrentCustomerDetails()
Expand All @@ -160,12 +175,45 @@ public function getCurrentCustomerDetails()
}

$customerSession = $this->modelSessionFactory->getCustomer();

try {
$jsApiKey = $this->decryptJsApiKey($this->model->getJsApiKey());
$hash = hash_hmac('sha256', $customerSession->getEmail(), $jsApiKey);
} catch (\Exception $e) {
$hash = '';
}

return [
'name' => $customerSession->getName(),
'email' => $customerSession->getEmail()
'email' => $customerSession->getEmail(),
'hash' => $hash
];
}

/**
* Retrieve JS API key
*
* @param string $js_api_key Encrypted JS API key
* @return string
* @throws \Exception error retrieving JS API key
*/
private function decryptJsApiKey(string $js_api_key)
{
if (empty($js_api_key)) {
throw new LocalizedException(__('JS API key is empty'));
}

if ($this->modelSessionFactory->hasData(self::TAWKTO_JS_API_KEY)) {
return $this->modelSessionFactory->getData(self::TAWKTO_JS_API_KEY);
}

$key = $this->encryptor->decrypt($js_api_key);

$this->modelSessionFactory->setData(self::TAWKTO_JS_API_KEY, $key);

return $key;
}

/**
* To or to not display the selected widget.
*/
Expand Down
51 changes: 48 additions & 3 deletions Controller/Adminhtml/SaveWidget/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,13 @@

use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Backend\App\Action\Context;
use Magento\Framework\Encryption\EncryptorInterface;
use Magento\Framework\Exception\LocalizedException;

use Psr\Log\LoggerInterface;
use Tawk\Widget\Model\WidgetFactory;
use Tawk\Widget\Helper\StringUtil;
use Tawk\Widget\Api\ConfigInterface;

class Index extends \Magento\Backend\App\Action
{
Expand Down Expand Up @@ -61,6 +65,13 @@ class Index extends \Magento\Backend\App\Action
*/
protected $helper;

/**
* Encryptor instance
*
* @var EncryptorInterface $encryptor
*/
protected $encryptor;

/**
* Constructor
*
Expand All @@ -69,20 +80,23 @@ class Index extends \Magento\Backend\App\Action
* @param JsonFactory $resultJsonFactory Json Factory instance
* @param LoggerInterface $logger PSR Logger
* @param StringUtil $helper String util helper
* @param EncryptorInterface $encryptor Encryptor instance
*/
public function __construct(
WidgetFactory $modelFactory,
Context $context,
JsonFactory $resultJsonFactory,
LoggerInterface $logger,
StringUtil $helper
StringUtil $helper,
EncryptorInterface $encryptor
) {
parent::__construct($context);
$this->resultJsonFactory = $resultJsonFactory;
$this->logger = $logger;
$this->modelWidgetFactory = $modelFactory->create();
$this->request = $this->getRequest();
$this->helper = $helper;
$this->encryptor = $encryptor;
}

/**
Expand All @@ -106,13 +120,14 @@ public function execute()
}

$alwaysdisplay = filter_var($this->request->getParam('alwaysdisplay'), FILTER_SANITIZE_NUMBER_INT);
$excludeurl = $this->request->getParam('excludeurl');
$excludeurl = $this->helper->stripTagsandQuotes($this->request->getParam('excludeurl'));
$donotdisplay = filter_var($this->request->getParam('donotdisplay'), FILTER_SANITIZE_NUMBER_INT);
$includeurl = $this->request->getParam('includeurl');
$includeurl = $this->helper->stripTagsAndQuotes($this->request->getParam('includeurl'));
$enableVisitorRecognition = filter_var(
$this->request->getParam('enableVisitorRecognition'),
FILTER_SANITIZE_NUMBER_INT
);
$jsApiKey = $this->helper->stripTagsandQuotes($this->request->getParam('jsApiKey'));

$model = $this->modelWidgetFactory->loadByForStoreId($storeId);

Expand All @@ -134,8 +149,38 @@ public function execute()

$model->setEnableVisitorRecognition($enableVisitorRecognition);

try {
$this->setJsApiKey($model, $jsApiKey);
} catch (\Exception $e) {
return $response->setData(['success' => false, 'message' => $e->getMessage()]);
}

$model->save();

return $response->setData(['success' => true]);
}

/**
* Sets the JS API key for the widget.
*
* @param \Tawk\Widget\Model\Widget $model The widget model
* @param string $jsApiKey The JS API key
* @return void
*/
private function setJsApiKey($model, $jsApiKey)
{
if ($jsApiKey === ConfigInterface::JS_API_KEY_NO_CHANGE) {
return;
}

if ($jsApiKey === '') {
return $model->setJsApiKey(null);
}

if (strlen(trim($jsApiKey)) !== 40) {
throw new LocalizedException(__('Invalid API key. Please provide value with 40 characters'));
}

return $model->setJsApiKey($this->encryptor->encrypt($jsApiKey));
}
}
13 changes: 11 additions & 2 deletions Controller/Adminhtml/StoreWidget/Index.php
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@

use Magento\Backend\App\Action\Context;
use Magento\Framework\Controller\Result\JsonFactory;

use Psr\Log\LoggerInterface;
use Tawk\Widget\Model\WidgetFactory;
use Tawk\Widget\Helper\StringUtil;
use Tawk\Widget\Api\ConfigInterface;

class Index extends \Magento\Backend\App\Action
{
Expand Down Expand Up @@ -96,7 +98,8 @@ public function __construct(
* excludeurl: string,
* donotdisplay: int,
* includeurl: string,
* enableVisitorRecognition: int
* enableVisitorRecognition: int,
* jsApiKey: string
* }
*/
public function execute()
Expand Down Expand Up @@ -126,6 +129,11 @@ public function execute()

$enableVisitorRecognition = $model->getEnableVisitorRecognition();

$jsApiKey = $model->getJsApiKey();
if (!empty($jsApiKey)) {
$jsApiKey = ConfigInterface::JS_API_KEY_NO_CHANGE;
}

return $response->setData([
'success' => true,
'pageid' => $pageId,
Expand All @@ -134,7 +142,8 @@ public function execute()
'excludeurl' => $excludeurl,
'donotdisplay' => $donotdisplay,
'includeurl' => $includeurl,
'enableVisitorRecognition' => $enableVisitorRecognition
'enableVisitorRecognition' => $enableVisitorRecognition,
'jsApiKey' => $jsApiKey
]);
}
}
18 changes: 18 additions & 0 deletions build/build-package.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/sh

Comment on lines +1 to +2
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Proper Shebang & Error Handling Consideration

The script begins with a proper shebang (#!/bin/sh). Consider adding error handling options (e.g., set -e, set -u, and/or set -o pipefail) immediately after the shebang to ensure the script exits on failure, which can help avoid silent failures in the build process.

composer run clean

mkdir -p ./tmp/tawkmagento2
cp -r ./view ./tmp/tawkmagento2
cp -r ./etc ./tmp/tawkmagento2
cp -r ./Setup ./tmp/tawkmagento2
cp -r ./Model ./tmp/tawkmagento2
cp -r ./Controller ./tmp/tawkmagento2
cp -r ./Block ./tmp/tawkmagento2
cp -r ./Helper ./tmp/tawkmagento2
cp -r ./Api ./tmp/tawkmagento2
cp ./registration.php ./tmp/tawkmagento2
cp ./composer.json ./tmp/tawkmagento2
cp README.md ./tmp/tawkmagento2

(cd ./tmp && zip -9 -rq ./tawkmagento2.zip ./tawkmagento2)
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
"build:prod": "composer install --no-dev",
"lint": "phpcs -p -s -v --runtime-set ignore_warnings_on_exit true .",
"lint:fix": "phpcbf -p -s -v .; err=$?; if [ $err -eq 1 ]; then exit 0; else exit $err; fi;",
"package": "composer run clean && mkdir -p ./tmp/tawkmagento2 && cp -r ./view ./tmp/tawkmagento2 && cp -r ./etc ./tmp/tawkmagento2 && cp -r ./Setup ./tmp/tawkmagento2 && cp -r ./Model ./tmp/tawkmagento2 && cp -r ./Controller ./tmp/tawkmagento2 && cp -r ./Block ./tmp/tawkmagento2 && cp -r ./Helper ./tmp/tawkmagento2 && cp ./registration.php ./tmp/tawkmagento2 && cp ./composer.json ./tmp/tawkmagento2 && cp README.md ./tmp/tawkmagento2 && (cd ./tmp && zip -9 -rq ./tawkmagento2.zip ./tawkmagento2)",
"package": "./build/build-package.sh",
"clean": "rm -rf ./tmp"
},
"repositories": {
Expand Down
1 change: 1 addition & 0 deletions etc/db_schema.xml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ xsi:noNamespaceSchemaLocation="urn:magento:framework:Setup/Declaration/Schema/et
comment="Comma-separated list of url patterns where widget should be displayed"/>
<column name="enable_visitor_recognition" xsi:type="smallint" unsigned="true" nullable="false" default="1"
comment="Enable visitor recognition feature"/>
<column name="js_api_key" xsi:type="text" nullable="true" comment="JS API key"/>
</table>
</schema>
23 changes: 23 additions & 0 deletions view/adminhtml/templates/selectwidget.phtml
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,30 @@
</div>
</div>

<div id="security" style>
<h3>Security Options</h3>
<div class="tawk-fields">
<p class="tawk-notice">
Note: If Secure Mode is enabled on your property, please enter
your Javascript API Key to ensure visitor recognition works correctly.
</p>
<label for="js_api_key" class="websiteids-label">Javascript API Key</label>
<input type="password" id="js_api_key" name="js_api_key">
</div>
</div>

<div class="tawk-fields">
<a class="savesettingsbtn" href="#">Save Settings</a>
<div
id="optionsSuccessMessage"
class="options-alert"
style="background-color: #dff0d8; color: #3c763d; border-color: #d6e9c6;">
Settings saved successfully
</div>
<div
id="optionsFailureMessage"
class="options-alert"
style="background-color: #f2dede; color: #a94442; border-color: #ebccd1;">
</div>
</div>
</div>
Loading