--------------------------------------------------------------------------------
/views/templates/admin/dummy/pre_tab.tpl:
--------------------------------------------------------------------------------
1 | {*
2 | * NOTICE OF LICENSE
3 | *
4 | * This file is licenced under the Software License Agreement.
5 | * With the purchase or the installation of the software in your application
6 | * you accept the licence agreement.
7 | *
8 | * You must not modify, adapt or create derivative works of this source code
9 | *
10 | * @author Doofinder
11 | * @copyright Doofinder
12 | * @license GPLv3
13 | *}
14 |
--------------------------------------------------------------------------------
/views/templates/admin/configure_footer.tpl:
--------------------------------------------------------------------------------
1 | {*
2 | * NOTICE OF LICENSE
3 | *
4 | * This file is licenced under the Software License Agreement.
5 | * With the purchase or the installation of the software in your application
6 | * you accept the licence agreement.
7 | *
8 | * You must not modify, adapt or create derivative works of this source code
9 | *
10 | * @author Doofinder
11 | * @copyright Doofinder
12 | * @license GPLv3
13 | *}
14 | {if $configured}
15 |
28 |
--------------------------------------------------------------------------------
/vendor/composer/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Copyright (c) Nils Adermann, Jordi Boggiano
3 |
4 | Permission is hereby granted, free of charge, to any person obtaining a copy
5 | of this software and associated documentation files (the "Software"), to deal
6 | in the Software without restriction, including without limitation the rights
7 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | copies of the Software, and to permit persons to whom the Software is furnished
9 | to do so, subject to the following conditions:
10 |
11 | The above copyright notice and this permission notice shall be included in all
12 | copies or substantial portions of the Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | THE SOFTWARE.
21 |
22 |
--------------------------------------------------------------------------------
/views/templates/admin/configure_administration_panel.tpl:
--------------------------------------------------------------------------------
1 | {*
2 | * NOTICE OF LICENSE
3 | *
4 | * This file is licenced under the Software License Agreement.
5 | * With the purchase or the installation of the software in your application
6 | * you accept the licence agreement.
7 | *
8 | * You must not modify, adapt or create derivative works of this source code
9 | *
10 | * @author Doofinder
11 | * @copyright Doofinder
12 | * @license GPLv3
13 | *}
14 |
15 |
16 |
{l s='Administration panel' mod='doofinder'}
17 |
18 |
19 |
20 |
21 |
{l s='Config Doofinder in my shop' mod='doofinder'}
22 |
{l s='Access the Doofinder dashboard and discover all the functionalities to increase your sales.' mod='doofinder'}
{l s='The product feed is being processed. Depending on the size of the product catalog in the store, this process may take a few minutes.' mod='doofinder'}
27 |
28 |
29 |
{l s='Your products may not appear correctly updated in the search results until the process has been completed.' mod='doofinder'}
30 |
31 |
32 |
33 |
34 |
50 |
51 |
82 |
--------------------------------------------------------------------------------
/src/Api/DoofinderApiIndex.php:
--------------------------------------------------------------------------------
1 | apiKey = $apiKey;
51 | $this->apiUrl = UrlManager::getRegionalUrl(DoofinderConstants::DOOPLUGINS_REGION_URL, $region);
52 | }
53 |
54 | /**
55 | * Invoke reindexing for all feeds in the store.
56 | *
57 | * Sends a request to the Doofinder Plugins API to reprocess all feeds.
58 | *
59 | * @param string $installationId Unique identifier of the store installation
60 | * @param string $callbackUrl Optional callback URL to receive notifications once the process completes
61 | *
62 | * @return array|null Returns the decoded JSON response from the API, or null on failure
63 | */
64 | public function invokeReindexing($installationId, $callbackUrl = '')
65 | {
66 | $apiEndpoint = $this->apiUrl . '/process-feed';
67 | $jsonData = json_encode(['store_id' => $installationId, 'callback_url' => $callbackUrl]);
68 |
69 | return $this->post($apiEndpoint, $jsonData);
70 | }
71 |
72 | /**
73 | * Execute a POST request to a given URL with a JSON payload.
74 | *
75 | * @param string $url The API endpoint URL
76 | * @param string $payload JSON-encoded string to send in the request body
77 | *
78 | * @return array|null Returns the decoded JSON response, or null if the request fails
79 | */
80 | private function post($url, $payload)
81 | {
82 | $client = new EasyREST();
83 |
84 | $response = $client->post(
85 | $url,
86 | $payload,
87 | null,
88 | null,
89 | 'application/json',
90 | ['Authorization: Token ' . $this->apiKey]
91 | );
92 |
93 | return json_decode($response->response, true);
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/views/templates/front/scriptV9.tpl:
--------------------------------------------------------------------------------
1 | {*
2 | * NOTICE OF LICENSE
3 | *
4 | * This file is licenced under the Software License Agreement.
5 | * With the purchase or the installation of the software in your application
6 | * you accept the licence agreement.
7 | *
8 | * You must not modify, adapt or create derivative works of this source code
9 | *
10 | * @author Doofinder
11 | * @copyright Doofinder
12 | * @license GPLv3
13 | *}
14 | {if isset($installation_ID) && $installation_ID}
15 |
16 |
45 |
46 |
47 |
48 |
54 |
55 |
56 |
57 |
70 |
71 |
72 | {/if}
73 |
--------------------------------------------------------------------------------
/src/Autoloader.php:
--------------------------------------------------------------------------------
1 |
22 | * @copyright 2007-2022 PrestaShop SA and Contributors
23 | * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
24 | * International Registered Trademark & Property of PrestaShop SA
25 | */
26 | if (!defined('_PS_VERSION_')) {
27 | exit;
28 | }
29 |
30 | /**
31 | * Upgrade the module to version 4.6.4.
32 | *
33 | * This upgrade step:
34 | * - Replaces the old doofinder_product table with a new unified doofinder_updates table.
35 | * - Adds support for tracking updates from multiple indexable objects (products, CMS pages, categories).
36 | * - Registers hooks for CMS and category CRUD operations.
37 | *
38 | * @param Doofinder $module the module instance being upgraded
39 | *
40 | * @return bool true if all steps succeed, false otherwise
41 | */
42 | function upgrade_module_4_6_4($module)
43 | {
44 | return installDb_4_6_4()
45 | && $module->registerHook('actionObjectCmsAddAfter')
46 | && $module->registerHook('actionObjectCmsUpdateAfter')
47 | && $module->registerHook('actionObjectCmsDeleteAfter')
48 | && $module->registerHook('actionObjectCategoryAddAfter')
49 | && $module->registerHook('actionObjectCategoryUpdateAfter')
50 | && $module->registerHook('actionObjectCategoryDeleteAfter');
51 | }
52 |
53 | /**
54 | * Install database structure for module version 4.6.4.
55 | *
56 | * Drops the old `doofinder_product` table (used only for products)
57 | * and creates a new `doofinder_updates` table to handle multiple item types.
58 | *
59 | * @return bool true if the new table is created successfully, false otherwise
60 | */
61 | function installDb_4_6_4()
62 | {
63 | Db::getInstance()->execute('DROP TABLE `' . _DB_PREFIX_ . 'doofinder_product`');
64 |
65 | return Db::getInstance()->execute(
66 | '
67 | CREATE TABLE IF NOT EXISTS `' . _DB_PREFIX_ . 'doofinder_updates` (
68 | `id_doofinder_update` INT UNSIGNED NOT NULL AUTO_INCREMENT,
69 | `id_shop` INT(10) UNSIGNED NOT NULL,
70 | `object` varchar(45) NOT NULL,
71 | `id_object` INT(10) UNSIGNED NOT NULL,
72 | `action` VARCHAR(45) NOT NULL,
73 | `date_upd` DATETIME NOT NULL,
74 | PRIMARY KEY (`id_doofinder_update`),
75 | CONSTRAINT uc_shop_update UNIQUE KEY (id_shop,object,id_object)
76 | ) ENGINE=' . _MYSQL_ENGINE_ . ' DEFAULT CHARSET=utf8 ;'
77 | );
78 | }
79 |
--------------------------------------------------------------------------------
/upgrade/upgrade-6.0.0.php:
--------------------------------------------------------------------------------
1 |
22 | * @copyright 2007-2022 PrestaShop SA and Contributors
23 | * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
24 | * International Registered Trademark & Property of PrestaShop SA
25 | */
26 |
27 | use PrestaShop\Module\Doofinder\Configuration\DoofinderConfig;
28 |
29 | if (!defined('_PS_VERSION_')) {
30 | exit;
31 | }
32 |
33 | /**
34 | * Upgrades the Doofinder module to version 6.0.0.
35 | *
36 | * This upgrade step updates the file paths to the new namespace structure deleting the old ones.
37 | *
38 | * @param Doofinder $module the Doofinder module instance being upgraded
39 | *
40 | * @return bool true on success, false if an exception occurs during the update
41 | */
42 | function upgrade_module_6_0_0($module)
43 | {
44 | DoofinderConfig::debug('Initiating 6.0.0 upgrade');
45 |
46 | $files = [
47 | 'src/Entity/DfCategoryBuild.php',
48 | 'src/Entity/DfCmsBuild.php',
49 | 'src/Entity/DfDb.php',
50 | 'src/Entity/DfProductBuild.php',
51 | 'src/Entity/DfTools.php',
52 | 'src/Entity/DoofinderAdminPanelView.php',
53 | 'src/Entity/DoofinderApi.php',
54 | 'src/Entity/DoofinderApiIndex.php',
55 | 'src/Entity/DoofinderApiItems.php',
56 | 'src/Entity/DoofinderApiLanding.php',
57 | 'src/Entity/DoofinderApiSingleScript.php',
58 | 'src/Entity/DoofinderConfig.php',
59 | 'src/Entity/DoofinderConstants.php',
60 | 'src/Entity/DoofinderException.php',
61 | 'src/Entity/DoofinderInstallation.php',
62 | 'src/Entity/DoofinderLayerApi.php',
63 | 'src/Entity/DoofinderResults.php',
64 | 'src/Entity/DoofinderScript.php',
65 | 'src/Entity/EasyREST.php',
66 | 'src/Entity/FormManager.php',
67 | 'src/Entity/HookManager.php',
68 | 'src/Entity/LanguageManager.php',
69 | 'src/Entity/SearchEngine.php',
70 | 'src/Entity/UpdateOnSave.php',
71 | 'src/Entity/UrlManager.php'];
72 |
73 | foreach ($files as $fileName) {
74 | $filePath = realpath(_PS_MODULE_DIR_ . 'doofinder' . DIRECTORY_SEPARATOR . $fileName);
75 | if (!file_exists($filePath) || is_dir($filePath)) {
76 | continue;
77 | }
78 |
79 | if (!unlink($filePath)) {
80 | DoofinderConfig::debug('Couldn\'t delete file: ' . $filePath);
81 | continue;
82 | }
83 |
84 | DoofinderConfig::debug('Deleted file: ' . $filePath);
85 | }
86 |
87 | return true;
88 | }
89 |
--------------------------------------------------------------------------------
/views/js/admin-panel.js:
--------------------------------------------------------------------------------
1 | /**
2 | * NOTICE OF LICENSE
3 | *
4 | * This file is licenced under the Software License Agreement.
5 | * With the purchase or the installation of the software in your application
6 | * you accept the licence agreement.
7 | *
8 | * You must not modify, adapt or create derivative works of this source code
9 | *
10 | * @author Doofinder
11 | * @copyright Doofinder
12 | * @license GPLv3
13 | */
14 |
15 | $(document).ready(function() {
16 |
17 | addVisibilityDependency('DF_GS_DISPLAY_PRICES', 'DF_GS_PRICES_USE_TAX_on');
18 | addVisibilityDependency('DF_SHOW_PRODUCT_FEATURES', 'DF_FEATURES_SHOWN[]');
19 | addVisibilityDependency('DF_SHOW_PRODUCT_VARIATIONS', 'DF_GROUP_ATTRIBUTES_SHOWN[]');
20 |
21 | function addVisibilityDependency(triggeringElementId, targetElementId) {
22 | var trigeringElementOnId = "#" + triggeringElementId + "_on";
23 | var trigeringElementOffId = "#" + triggeringElementId + "_off";
24 | hideOrShowElement(trigeringElementOnId, targetElementId);
25 |
26 | $(trigeringElementOnId + "," + trigeringElementOffId).change(function() {
27 | hideOrShowElement(trigeringElementOnId, targetElementId);
28 | });
29 | }
30 |
31 | function hideOrShowElement(trigeringElementOnId, targetElementId) {
32 | var value = $(trigeringElementOnId).is(':checked');
33 | var parent = getParent(targetElementId);
34 |
35 | if (value) {
36 | parent.show()
37 | } else {
38 | parent.hide();
39 | }
40 | }
41 |
42 | /* We get the element from the dom and then convert it to jquery
43 | because jquery has problems with some ids that prestashop sets,
44 | for example DF_FEATURES_SHOWN[]. */
45 |
46 | function getParent(targetElementId) {
47 | var targetElement = $(document.getElementById(targetElementId));
48 | var parent = targetElement.parents().filter(function() {
49 | return $(this).is('.form-group');
50 | });
51 | return parent;
52 | }
53 |
54 | const $apiKeyNode = $('#DF_API_KEY');
55 | const $regionNode = $('#DF_REGION');
56 | $apiKeyNode.on('change', function() {
57 | if (0 === $apiKeyNode.length || 0 === $regionNode.length) {
58 | return;
59 | }
60 | const apiKey = $apiKeyNode.val().trim();
61 | if (!/eu1-|ap1-|us1-/.test(apiKey)) {
62 | return;
63 | }
64 | const region = apiKey.split('-').shift();
65 | const previousRegion = $regionNode.val();
66 | $regionNode.val(region);
67 |
68 | if (previousRegion === region) {
69 | return;
70 | }
71 |
72 | // Small animation to catch user attention
73 | $regionNode.animate({
74 | opacity:"0.5"
75 | }, 1000, function() {
76 | $regionNode.animate({
77 | opacity:"1"
78 | }, 1000);
79 | });
80 | });
81 |
82 | $('.df-checkboxes-table [data-checkboxes-toggle]').on('click', function(event){
83 | const $parentTable = $(this).closest('.df-checkboxes-table');
84 | const isChecked = $(this).is(':checked');
85 | $parentTable.find('input[type="checkbox"]').prop('checked', isChecked);
86 | });
87 |
88 | const commonCheckboxSelector = 'input[type="checkbox"]:not([data-checkboxes-toggle])';
89 |
90 | $('.df-checkboxes-table ' + commonCheckboxSelector).on('click', function(event){
91 | const $parentTable = $(this).closest('.df-checkboxes-table');
92 | const checkboxesTotalCount = $parentTable.find(commonCheckboxSelector).length;
93 | const checkboxesCheckedcount = $parentTable.find(commonCheckboxSelector + ':checked').length;
94 | const allChecked = checkboxesTotalCount === checkboxesCheckedcount;
95 | $parentTable.find('[data-checkboxes-toggle]').prop('checked', allChecked);
96 | });
97 | });
98 |
--------------------------------------------------------------------------------
/src/Core/SearchEngine.php:
--------------------------------------------------------------------------------
1 | language_code);
49 | $shopGroupId = isset($shopGroupId) ? $shopGroupId : $context->shop->id_shop_group;
50 | $shopId = isset($shopId) ? $shopId : $context->shop->id;
51 | $hashid = \Configuration::get($hashidKey, $idLang, $shopGroupId, $shopId);
52 |
53 | if (!$hashid) {
54 | // If not found, try to obtain hashid without context
55 | $hashid = \Configuration::get($hashidKey, $idLang);
56 | }
57 |
58 | if (!$hashid) {
59 | // If not found, try to obtain hashid without idLang
60 | $hashid = \Configuration::get($hashidKey);
61 | }
62 |
63 | if (!$hashid) {
64 | $hashidKey = 'DF_HASHID_' . $currIso . '_' . strtoupper(LanguageManager::getLanguageCode($lang->language_code));
65 | $hashid = \Configuration::get($hashidKey);
66 | }
67 |
68 | return $hashid;
69 | }
70 |
71 | /**
72 | * Update the hashid of the search engines of the store in the configuration
73 | *
74 | * @param int|null $idShopGroup
75 | * @param int|null $idShop
76 | *
77 | * @return true
78 | */
79 | public static function setSearchEnginesByConfig($idShopGroup = null, $idShop = null)
80 | {
81 | $context = \Context::getContext();
82 | $idShopGroup = isset($idShopGroup) ? $idShopGroup : $context->shop->id_shop_group;
83 | $idShop = isset($idShop) ? $idShop : $context->shop->id;
84 | $installationID = \Configuration::get('DF_INSTALLATION_ID', null, $idShopGroup, $idShop);
85 | $apiKey = \Configuration::get('DF_API_KEY');
86 | $region = \Configuration::get('DF_REGION');
87 |
88 | $data = DoofinderLayerApi::getInstallationData($installationID, $apiKey, $region);
89 |
90 | foreach ($data['config']['search_engines'] as $lang => $currencies) {
91 | foreach ($currencies as $currency => $hashid) {
92 | $hashidKey = 'DF_HASHID_' . strtoupper($currency) . '_' . strtoupper($lang);
93 | \Configuration::updateValue($hashidKey, $hashid, false, $idShopGroup, $idShop);
94 | }
95 | }
96 |
97 | return true;
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/Feed/DfCmsBuild.php:
--------------------------------------------------------------------------------
1 | idShop = $idShop;
56 | $this->idLang = $idLang;
57 | }
58 |
59 | /**
60 | * Sets the CMS page IDs to be included in the payload.
61 | *
62 | * @param array $arrayCmsPages list of CMS page IDs
63 | *
64 | * @return void
65 | */
66 | public function setCmsPages($arrayCmsPages)
67 | {
68 | $this->cmsPages = $arrayCmsPages;
69 | }
70 |
71 | /**
72 | * Builds the CMS pages payload.
73 | *
74 | * - Iterates over each provided CMS page ID.
75 | * - Retrieves and sanitizes CMS data.
76 | * - Generates an array or JSON structure containing page information.
77 | *
78 | * @param bool $json whether to return the payload as JSON (true) or array (false)
79 | *
80 | * @return string|array JSON string or array containing CMS page data
81 | */
82 | public function build($json = true)
83 | {
84 | $this->assign();
85 |
86 | $payload = [];
87 |
88 | foreach ($this->cmsPages as $cms) {
89 | $payload[] = $this->buildCms($cms);
90 | }
91 |
92 | return $json ? json_encode($payload) : $payload;
93 | }
94 |
95 | /**
96 | * Assigns required PrestaShop context properties (e.g., link builder).
97 | *
98 | * @return void
99 | */
100 | private function assign()
101 | {
102 | $this->link = \Context::getContext()->link;
103 | }
104 |
105 | /**
106 | * Builds a single CMS page's data array.
107 | *
108 | * - Cleans text fields using DfTools.
109 | * - Retrieves CMS link and content.
110 | *
111 | * @param int $idCms CMS page ID
112 | *
113 | * @return array associative array with CMS page details
114 | */
115 | private function buildCms($idCms)
116 | {
117 | $cms = new \CMS($idCms, $this->idLang, $this->idShop);
118 |
119 | $c = [];
120 | $c['id'] = (string) $cms->id;
121 | $c['title'] = DfTools::cleanString($cms->meta_title);
122 | $c['description'] = DfTools::cleanString($cms->meta_description);
123 | $c['meta_title'] = DfTools::cleanString($cms->meta_title);
124 | $c['meta_description'] = DfTools::cleanString($cms->meta_description);
125 | $c['content'] = DfTools::cleanString($cms->content);
126 | $c['link'] = $this->link->getCMSLink($cms);
127 |
128 | return $c;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/src/Api/DoofinderApiItems.php:
--------------------------------------------------------------------------------
1 | hashid = $hashid;
64 | $this->apiKey = $apiKey;
65 | $this->apiUrl = UrlManager::getRegionalUrl(DoofinderConstants::DOOPLUGINS_REGION_URL, $region);
66 | $this->type = $type;
67 | }
68 |
69 | /**
70 | * Make a request to the API to update the specified items
71 | *
72 | * @param array|string $payload Items data to update. This can be an associative array or a JSON string.
73 | *
74 | * @return array Response from the API
75 | */
76 | public function updateBulk($payload)
77 | {
78 | $endpoint = '/item/' . $this->hashid . '/' . $this->type . '?platform=prestashop&action=update';
79 |
80 | $url = $this->apiUrl . $endpoint;
81 |
82 | return $this->post($url, $payload);
83 | }
84 |
85 | /**
86 | * Make a request to the API to delete the specified items
87 | *
88 | * @param string|null $payload Items IDs to delete
89 | *
90 | * @return array Response from the API
91 | */
92 | public function deleteBulk($payload)
93 | {
94 | $endpoint = '/item/' . $this->hashid . '/' . $this->type . '?platform=prestashop&action=delete';
95 |
96 | $url = $this->apiUrl . $endpoint;
97 |
98 | return $this->post($url, $payload);
99 | }
100 |
101 | /**
102 | * Execute a POST request to the Doofinder API with a JSON payload.
103 | *
104 | * @param string $url Full API endpoint URL
105 | * @param mixed $payload Data to send in the request body
106 | *
107 | * @return array|null Decoded JSON response from the API, or null if the request fails
108 | */
109 | private function post($url, $payload)
110 | {
111 | $client = new EasyREST();
112 |
113 | $response = $client->post(
114 | $url,
115 | $payload,
116 | null,
117 | null,
118 | 'application/json',
119 | ['Authorization: Token ' . $this->apiKey]
120 | );
121 |
122 | return json_decode($response->response, true);
123 | }
124 | }
125 |
--------------------------------------------------------------------------------
/src/Feed/DfCategoryBuild.php:
--------------------------------------------------------------------------------
1 | idShop = $idShop;
56 | $this->idLang = $idLang;
57 | }
58 |
59 | /**
60 | * Set the categories to be included in the payload
61 | *
62 | * @param array $arrayCategories categories ids
63 | */
64 | public function setCategories($arrayCategories)
65 | {
66 | $this->categories = $arrayCategories;
67 | }
68 |
69 | /**
70 | * Sets the category IDs to be included in the payload.
71 | *
72 | * @param bool $json whether to return the payload as JSON (true) or array (false)
73 | *
74 | * @return string|array JSON string or array containing category data
75 | */
76 | public function build($json = true)
77 | {
78 | $this->assign();
79 |
80 | $payload = [];
81 |
82 | foreach ($this->categories as $category) {
83 | if (\Category::categoryExists($category)) {
84 | $payload[] = $this->buildCategory($category);
85 | }
86 | }
87 |
88 | return $json ? json_encode($payload) : $payload;
89 | }
90 |
91 | /**
92 | * Assigns required PrestaShop context properties (e.g., link builder).
93 | *
94 | * @return void
95 | */
96 | private function assign()
97 | {
98 | $this->link = \Context::getContext()->link;
99 | }
100 |
101 | /**
102 | * Builds a single category's data array.
103 | *
104 | * - Cleans text fields using DfTools.
105 | * - Retrieves category link and image link.
106 | *
107 | * @param int $idCategory category ID
108 | *
109 | * @return array Associative array with category details
110 | */
111 | private function buildCategory($idCategory)
112 | {
113 | $category = new \Category($idCategory, $this->idLang, $this->idShop);
114 |
115 | $c = [];
116 |
117 | $tableName = 'category';
118 | $tableName = (method_exists(get_class(new \ImageType()), 'getFormattedName')) ? \ImageType::getFormattedName($tableName) : $tableName . '_default';
119 |
120 | $c['id'] = (string) $category->id;
121 | $c['title'] = DfTools::cleanString($category->name);
122 | $c['description'] = DfTools::cleanString($category->description);
123 | $c['meta_title'] = DfTools::cleanString($category->meta_title);
124 | $c['meta_description'] = DfTools::cleanString($category->meta_description);
125 | $c['link'] = $this->link->getCategoryLink($category);
126 | $c['image_link'] = $category->id_image ? $this->link->getCatImageLink($category->link_rewrite, $category->id_image, $tableName) : '';
127 |
128 | return $c;
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/vendor/composer/autoload_static.php:
--------------------------------------------------------------------------------
1 |
11 | array (
12 | 'PrestaShop\\Module\\Doofinder\\' => 28,
13 | ),
14 | );
15 |
16 | public static $prefixDirsPsr4 = array (
17 | 'PrestaShop\\Module\\Doofinder\\' =>
18 | array (
19 | 0 => __DIR__ . '/../..' . '/src',
20 | ),
21 | );
22 |
23 | public static $classMap = array (
24 | 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php',
25 | 'Doofinder' => __DIR__ . '/../..' . '/doofinder.php',
26 | 'PrestaShop\\Module\\Doofinder\\Api\\DoofinderApi' => __DIR__ . '/../..' . '/src/Api/DoofinderApi.php',
27 | 'PrestaShop\\Module\\Doofinder\\Api\\DoofinderApiIndex' => __DIR__ . '/../..' . '/src/Api/DoofinderApiIndex.php',
28 | 'PrestaShop\\Module\\Doofinder\\Api\\DoofinderApiItems' => __DIR__ . '/../..' . '/src/Api/DoofinderApiItems.php',
29 | 'PrestaShop\\Module\\Doofinder\\Api\\DoofinderApiSingleScript' => __DIR__ . '/../..' . '/src/Api/DoofinderApiSingleScript.php',
30 | 'PrestaShop\\Module\\Doofinder\\Api\\DoofinderLayerApi' => __DIR__ . '/../..' . '/src/Api/DoofinderLayerApi.php',
31 | 'PrestaShop\\Module\\Doofinder\\Api\\EasyREST' => __DIR__ . '/../..' . '/src/Api/EasyREST.php',
32 | 'PrestaShop\\Module\\Doofinder\\Autoloader' => __DIR__ . '/../..' . '/src/Autoloader.php',
33 | 'PrestaShop\\Module\\Doofinder\\Configuration\\DoofinderConfig' => __DIR__ . '/../..' . '/src/Configuration/DoofinderConfig.php',
34 | 'PrestaShop\\Module\\Doofinder\\Core\\DoofinderConstants' => __DIR__ . '/../..' . '/src/Core/DoofinderConstants.php',
35 | 'PrestaShop\\Module\\Doofinder\\Core\\DoofinderScript' => __DIR__ . '/../..' . '/src/Core/DoofinderScript.php',
36 | 'PrestaShop\\Module\\Doofinder\\Core\\SearchEngine' => __DIR__ . '/../..' . '/src/Core/SearchEngine.php',
37 | 'PrestaShop\\Module\\Doofinder\\Core\\UpdateOnSave' => __DIR__ . '/../..' . '/src/Core/UpdateOnSave.php',
38 | 'PrestaShop\\Module\\Doofinder\\Exception\\DoofinderException' => __DIR__ . '/../..' . '/src/Exception/DoofinderException.php',
39 | 'PrestaShop\\Module\\Doofinder\\Feed\\DfCategoryBuild' => __DIR__ . '/../..' . '/src/Feed/DfCategoryBuild.php',
40 | 'PrestaShop\\Module\\Doofinder\\Feed\\DfCmsBuild' => __DIR__ . '/../..' . '/src/Feed/DfCmsBuild.php',
41 | 'PrestaShop\\Module\\Doofinder\\Feed\\DfProductBuild' => __DIR__ . '/../..' . '/src/Feed/DfProductBuild.php',
42 | 'PrestaShop\\Module\\Doofinder\\Installer\\DoofinderInstallation' => __DIR__ . '/../..' . '/src/Installer/DoofinderInstallation.php',
43 | 'PrestaShop\\Module\\Doofinder\\Manager\\FormManager' => __DIR__ . '/../..' . '/src/Manager/FormManager.php',
44 | 'PrestaShop\\Module\\Doofinder\\Manager\\HookManager' => __DIR__ . '/../..' . '/src/Manager/HookManager.php',
45 | 'PrestaShop\\Module\\Doofinder\\Manager\\LanguageManager' => __DIR__ . '/../..' . '/src/Manager/LanguageManager.php',
46 | 'PrestaShop\\Module\\Doofinder\\Manager\\UrlManager' => __DIR__ . '/../..' . '/src/Manager/UrlManager.php',
47 | 'PrestaShop\\Module\\Doofinder\\Utils\\DfDb' => __DIR__ . '/../..' . '/src/Utils/DfDb.php',
48 | 'PrestaShop\\Module\\Doofinder\\Utils\\DfTools' => __DIR__ . '/../..' . '/src/Utils/DfTools.php',
49 | 'PrestaShop\\Module\\Doofinder\\View\\DoofinderAdminPanelView' => __DIR__ . '/../..' . '/src/View/DoofinderAdminPanelView.php',
50 | );
51 |
52 | public static function getInitializer(ClassLoader $loader)
53 | {
54 | return \Closure::bind(function () use ($loader) {
55 | $loader->prefixLengthsPsr4 = ComposerStaticInitddb45daa7f46128bfeb9153853763869::$prefixLengthsPsr4;
56 | $loader->prefixDirsPsr4 = ComposerStaticInitddb45daa7f46128bfeb9153853763869::$prefixDirsPsr4;
57 | $loader->classMap = ComposerStaticInitddb45daa7f46128bfeb9153853763869::$classMap;
58 |
59 | }, null, ClassLoader::class);
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/Utils/DfDb.php:
--------------------------------------------------------------------------------
1 | _DB_SERVER_, 'user' => _DB_USER_, 'password' => _DB_PASSWD_, 'database' => _DB_NAME_], /* MySQL Master server */
64 | ];
65 | }
66 |
67 | if (!$master) {
68 | self::loadSlaveServers();
69 | }
70 |
71 | $totalServers = count(self::$_servers);
72 | if ($master || $totalServers == 1) {
73 | $idServer = 0;
74 | } else {
75 | ++$id;
76 | $idServer = ($totalServers > 2 && ($id % $totalServers) != 0) ? $id % $totalServers : 1;
77 | }
78 |
79 | $class = \Db::getClass();
80 | $instance = new $class(
81 | self::$_servers[$idServer]['server'],
82 | self::$_servers[$idServer]['user'],
83 | self::$_servers[$idServer]['password'],
84 | self::$_servers[$idServer]['database'],
85 | false
86 | );
87 | $link = $instance->connect();
88 | if ('DbPDO' === $class && method_exists($link, 'setAttribute')) {
89 | // Set the PDO attribute to not use buffered queries
90 | // This is needed to avoid memory issues with large datasets
91 |
92 | $link->setAttribute(\PDO::MYSQL_ATTR_USE_BUFFERED_QUERY, false);
93 | }
94 |
95 | return $instance;
96 | }
97 |
98 | /**
99 | * Loads configuration settings for slave servers if needed.
100 | *
101 | * @return void
102 | */
103 | protected static function loadSlaveServers()
104 | {
105 | if (self::$_slave_servers_loaded) {
106 | return;
107 | }
108 |
109 | // Add here your slave(s) server(s) in this file
110 | if (file_exists(_PS_ROOT_DIR_ . '/config/db_slave_server.inc.php')) {
111 | self::$_servers = array_merge(self::$_servers, require (_PS_ROOT_DIR_ . '/config/db_slave_server.inc.php'));
112 | }
113 |
114 | self::$_slave_servers_loaded = true;
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/upgrade/upgrade-4.12.0.php:
--------------------------------------------------------------------------------
1 |
22 | * @copyright 2007-2022 PrestaShop SA and Contributors
23 | * @license https://opensource.org/licenses/AFL-3.0 Academic Free License 3.0 (AFL-3.0)
24 | * International Registered Trademark & Property of PrestaShop SA
25 | */
26 |
27 | use PrestaShop\Module\Doofinder\Configuration\DoofinderConfig;
28 |
29 | if (!defined('_PS_VERSION_')) {
30 | exit;
31 | }
32 |
33 | /**
34 | * Upgrades the Doofinder module to version 4.12.0.
35 | *
36 | * This upgrade step removes obsolete PHP files from previous versions
37 | * to prevent conflicts, dead code, or deprecated behavior.
38 | * Logs progress using DoofinderConfig::debug.
39 | *
40 | * @param Doofinder $module the Doofinder module instance being upgraded
41 | *
42 | * @return bool always returns true, regardless of whether files were successfully deleted
43 | */
44 | function upgrade_module_4_12_0($module)
45 | {
46 | DoofinderConfig::debug('Initiating 4.12.0 upgrade');
47 |
48 | // Delete old *.php files
49 | unlinkFiles();
50 | DoofinderConfig::debug('Old files deleted successfully.');
51 |
52 | return true;
53 | }
54 |
55 | /**
56 | * Deletes legacy files no longer needed by the Doofinder module.
57 | *
58 | * Iterates through a predefined list of obsolete files, checks their existence,
59 | * and attempts to remove them from the module directory. Each deletion attempt
60 | * is logged, including failures.
61 | *
62 | * @return void
63 | */
64 | function unlinkFiles()
65 | {
66 | $files = [
67 | 'autoloader.php',
68 | 'cache.php',
69 | 'config.php',
70 | 'doofinder-ajax.php',
71 | 'feed.php',
72 | 'landing.php',
73 | 'controllers/front/landingEntrypoint.php',
74 | /* Files from older versions. */
75 | 'lib/doofinder_api.php',
76 | 'lib/doofinder_api_landing.php',
77 | 'views/templates/front/script.tpl',
78 | 'lib/doofinder_api_index.php',
79 | 'lib/doofinder_api_items.php',
80 | 'lib/doofinder_layer_api.php',
81 | 'lib/doofinder_api_unique_script.php',
82 | 'lib/doofinder_installation.php',
83 | 'lib/dfTools.class.php',
84 | 'lib/dfCategory_build.php',
85 | 'lib/dfCms_build.php',
86 | 'lib/dfProduct_build.php',
87 | 'lib/DfCategoryBuild.php',
88 | 'lib/DfCmsBuild.php',
89 | 'lib/DfProductBuild.php',
90 | 'lib/DfTools.php',
91 | 'lib/DoofinderAdminPanelView.php',
92 | 'lib/DoofinderApi.php',
93 | 'lib/DoofinderApiIndex.php',
94 | 'lib/DoofinderApiItems.php',
95 | 'lib/DoofinderApiLanding.php',
96 | 'lib/DoofinderApiUniqueScript.php',
97 | 'lib/DoofinderConfig.php',
98 | 'lib/DoofinderConstants.php',
99 | 'lib/DoofinderException.php',
100 | 'lib/DoofinderInstallation.php',
101 | 'lib/DoofinderLayerApi.php',
102 | 'lib/DoofinderResults.php',
103 | 'lib/DoofinderScript.php',
104 | 'lib/EasyREST.php',
105 | 'lib/FormManager.php',
106 | 'lib/HookManager.php',
107 | 'lib/LanguageManager.php',
108 | 'lib/SearchEngine.php',
109 | 'lib/UpdateOnSave.php',
110 | 'lib/UrlManager.php',
111 | ];
112 |
113 | foreach ($files as $fileName) {
114 | $filePath = realpath(_PS_MODULE_DIR_ . 'doofinder' . DIRECTORY_SEPARATOR . $fileName);
115 | if (!file_exists($filePath) || is_dir($filePath)) {
116 | continue;
117 | }
118 |
119 | if (!unlink($filePath)) {
120 | DoofinderConfig::debug('Couldn\'t delete file: ' . $filePath);
121 | continue;
122 | }
123 |
124 | DoofinderConfig::debug('Deleted file: ' . $filePath);
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/Manager/UrlManager.php:
--------------------------------------------------------------------------------
1 | domain_ssl : 'http://' . $shop->domain;
45 |
46 | return $url . self::_getShopBaseURI($shop);
47 | }
48 |
49 | /**
50 | * Build feed urls
51 | *
52 | * @param int $shopId
53 | * @param int $language
54 | *
55 | * @return string
56 | */
57 | public static function getFeedUrl($shopId, $language, $currency = null)
58 | {
59 | $shop = new \Shop($shopId);
60 | \Context::getContext()->shop = $shop;
61 |
62 | $params = [
63 | 'language' => \Tools::strtoupper($language),
64 | 'dfsec_hash' => \Configuration::get('DF_API_KEY'),
65 | ];
66 |
67 | if ($currency) {
68 | $params['currency'] = \Tools::strtoupper($currency);
69 | }
70 |
71 | $link = \Context::getContext()->link->getModuleLink(DoofinderConstants::NAME, 'feed', $params);
72 |
73 | /*
74 | * Cleans redundant parameters in URLs for PrestaShop 1.5.3
75 | *
76 | * In PrestaShop 1.5.3, 'module' and 'controller' parameters are maintained in URLs even when friendly URLs are
77 | * enabled, which causes navigation errors.
78 | * This function removes these parameters when they are not necessary, specifically when the 'fc=module'
79 | */
80 | if (\Configuration::get('PS_REWRITING_SETTINGS')) {
81 | $link = preg_replace('/[&?](module|controller)=[^&]+/', '', $link);
82 | }
83 |
84 | return $link;
85 | }
86 |
87 | /**
88 | * Get Process Callback URL
89 | *
90 | * @return string
91 | */
92 | public static function getProcessCallbackUrl($shopId)
93 | {
94 | $shop = new \Shop($shopId);
95 | \Context::getContext()->shop = $shop;
96 |
97 | return \Context::getContext()->link->getModuleLink(DoofinderConstants::NAME, 'callback', []);
98 | }
99 |
100 | /**
101 | * Get install endpoint URL
102 | *
103 | * @param string $region
104 | *
105 | * @return string
106 | */
107 | public static function getInstallUrl($region)
108 | {
109 | return self::getRegionalUrl(DoofinderConstants::DOOPLUGINS_REGION_URL, $region, '/install');
110 | }
111 |
112 | /**
113 | * Get update feed endpoint URL
114 | *
115 | * @param string $region
116 | *
117 | * @return string
118 | */
119 | public static function getUpdateFeedUrl($region)
120 | {
121 | return self::getRegionalUrl(DoofinderConstants::DOOPLUGINS_REGION_URL, $region, '/prestashop/feed-url-update');
122 | }
123 |
124 | /**
125 | * Gets an URL with its region filled in. You can also append a path (optional).
126 | * If the region is provided as '' it will return a regionless URL.
127 | *
128 | * @return string
129 | */
130 | public static function getRegionalUrl($url, $region, $pathToAppend = '')
131 | {
132 | if (empty($region)) {
133 | return sprintf($url, '') . $pathToAppend;
134 | }
135 |
136 | return sprintf($url, $region . '-') . $pathToAppend;
137 | }
138 |
139 | /**
140 | * Get shop base URI
141 | *
142 | * @param \Shop $shop
143 | *
144 | * @return string
145 | */
146 | private static function _getShopBaseURI($shop)
147 | {
148 | return $shop->physical_uri . $shop->virtual_uri;
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/views/js/doofinder-onboarding.js:
--------------------------------------------------------------------------------
1 | /**
2 | * NOTICE OF LICENSE
3 | *
4 | * This file is licenced under the Software License Agreement.
5 | * With the purchase or the installation of the software in your application
6 | * you accept the licence agreement.
7 | *
8 | * You must not modify, adapt or create derivative works of this source code
9 | *
10 | * @author Doofinder
11 | * @copyright Doofinder
12 | * @license GPLv3
13 | */
14 |
15 | const shopDomain =
16 | location.protocol +
17 | "//" +
18 | location.hostname +
19 | (location.port ? ":" + location.port : "");
20 |
21 | $(document).ready(function () {
22 | window.addEventListener(
23 | "message",
24 | (event) => {
25 | const doofinder_regex = /.*\.doofinder\.com/gm;
26 | //Check that the sender is doofinder
27 | if (!doofinder_regex.test(event.origin)) return;
28 | if (event.data && 'string' === typeof event.data) {
29 | data = event.data.split("|");
30 | event_name = data[0];
31 | event_data = JSON.parse(atob(data[1]));
32 | processMessage(event_name, event_data);
33 | }
34 | },
35 | false
36 | );
37 | });
38 |
39 | function popupDoofinder(type) {
40 | var params =
41 | "?" +
42 | paramsPopup +
43 | "&mktcod=PSHOP&utm_source=prestashop_module&utm_campaing=freetrial&utm_content=autoinstaller";
44 | if ('undefined' === typeof doofinderAdminUrl) {
45 | doofinderAdminUrl = 'https://admin.doofinder.com';
46 | }
47 | var domain = doofinderAdminUrl + "/plugins/" + type + "/prestashop";
48 | popupCenter(domain + params, "Doofinder", 400, 850);
49 | }
50 |
51 | function initializeAutoinstallerMessages() {
52 | $(".choose-installer").hide();
53 | $(".loading-installer").show();
54 | var loop = setInterval(function () {
55 | if (!$(".loading-installer ul li.active").is(":last-child")) {
56 | $(".loading-installer ul li.active")
57 | .removeClass("active")
58 | .next()
59 | .addClass("active");
60 | } else {
61 | clearInterval(loop);
62 | }
63 | }, 4000);
64 | }
65 |
66 | function launchAutoinstaller() {
67 | $("#installation-errors").empty();
68 | initializeAutoinstallerMessages();
69 | let post_data = {
70 | autoinstaller: 1,
71 | token: installerToken,
72 | };
73 |
74 | if (typeof shop_id != "undefined") {
75 | post_data["shop_id"] = shop_id;
76 | }
77 |
78 | $.post(ajaxUrl, post_data, function (data) {
79 | if (data.success) {
80 | //reload without resending post data
81 | history.go(0);
82 | } else {
83 | if ("string" === typeof data.errors) {
84 | $(".loading-installer").hide();
85 | $("#installation-errors").append("
" + data.errors + "
");
86 | } else if (data.errors && data.errors.length > 0) {
87 | $(".loading-installer").hide();
88 | for (const error in data.errors) {
89 | if (Object.hasOwnProperty.call(data.errors, error)) {
90 | $("#installation-errors").append("
" + data.errors[error] + "
");
91 | }
92 | }
93 | }
94 | showErrorMessage();
95 | }
96 | });
97 | }
98 |
99 | function popupCenter(url, title, w, h) {
100 | const dualScreenLeft =
101 | window.screenLeft !== undefined ? window.screenLeft : window.screenX;
102 | const dualScreenTop =
103 | window.screenTop !== undefined ? window.screenTop : window.screenY;
104 |
105 | const width = window.innerWidth
106 | ? window.innerWidth
107 | : document.documentElement.clientWidth
108 | ? document.documentElement.clientWidth
109 | : screen.width;
110 | const height = window.innerHeight
111 | ? window.innerHeight
112 | : document.documentElement.clientHeight
113 | ? document.documentElement.clientHeight
114 | : screen.height;
115 |
116 | const systemZoom = width / window.screen.availWidth;
117 | const left = (width - w) / 2 / systemZoom + dualScreenLeft;
118 | const top = (height - h) / 2 / systemZoom + dualScreenTop;
119 |
120 | const newWindow = window.open(
121 | url,
122 | title,
123 | `
124 | scrollbars=yes,
125 | width=${w / systemZoom},
126 | height=${h / systemZoom},
127 | top=${top},
128 | left=${left},
129 | status=0,
130 | toolbar=0,
131 | location=0
132 | `
133 | );
134 |
135 | if (window.focus) newWindow.focus();
136 | return newWindow;
137 | }
138 |
139 | function processMessage(name, data) {
140 | if (name === "set_doofinder_data") send_connect_data(data);
141 | }
142 |
143 | function send_connect_data(data) {
144 | $.ajax({
145 | type: "POST",
146 | dataType: "json",
147 | url: configUrl,
148 | data: data,
149 | success: function (response) {
150 | if (response.success) {
151 | launchAutoinstaller();
152 | } else {
153 | showConnectionError();
154 | }
155 | },
156 | error: function (data) {
157 | showConnectionError();
158 | },
159 | });
160 | }
161 |
162 | function showConnectionError() {
163 | $(".message-popup").show();
164 | setTimeout(function () {
165 | $(".message-popup").hide();
166 | }, 10000);
167 | }
168 |
169 | function showErrorMessage() {
170 | $(".loading-installer").hide();
171 | $(".message-error").show();
172 | }
173 |
--------------------------------------------------------------------------------
/controllers/front/config.php:
--------------------------------------------------------------------------------
1 | ajax = true;
52 |
53 | header('Content-Type:application/json; charset=utf-8');
54 |
55 | $module = Module::getInstanceByName('doofinder');
56 | $autoinstallerToken = Tools::getValue('token');
57 | if ($autoinstallerToken) {
58 | $link = Context::getContext()->link;
59 | $redirect = $link->getPageLink('module-doofinder-config');
60 | $tmpToken = DfTools::encrypt($redirect);
61 | if ($tmpToken == $autoinstallerToken) {
62 | $apiToken = Tools::getValue('api_token');
63 | $apiEndpoint = Tools::getValue('api_endpoint');
64 | $adminEndpoint = Tools::getValue('admin_endpoint');
65 | if ($apiToken) {
66 | DoofinderConfig::saveApiConfig($apiToken, $apiEndpoint, $adminEndpoint);
67 | }
68 | echo json_encode(['success' => true]);
69 | exit;
70 | } else {
71 | header('HTTP/1.1 403 Forbidden', true, 403);
72 | $msgError = 'Forbidden access.'
73 | . ' Token for autoinstaller invalid.';
74 | exit($msgError);
75 | }
76 | }
77 |
78 | $languages = [];
79 | $configurations = [];
80 | $currencies = array_keys(DfTools::getAvailableCurrencies());
81 |
82 | $display_prices = (bool) Configuration::get('DF_GS_DISPLAY_PRICES');
83 | $prices_with_taxes = (bool) Configuration::get('DF_GS_PRICES_USE_TAX');
84 |
85 | foreach (Language::getLanguages(true, $this->context->shop->id) as $lang) {
86 | $lang = Tools::strtoupper($lang['iso_code']);
87 | $currency = DfTools::getCurrencyForLanguage($lang);
88 |
89 | $languages[] = $lang;
90 | $configurations[$lang] = [
91 | 'language' => $lang,
92 | 'currency' => Tools::strtoupper($currency->iso_code),
93 | 'prices' => $display_prices,
94 | 'taxes' => $prices_with_taxes,
95 | ];
96 | }
97 |
98 | $force_ssl = (Configuration::get('PS_SSL_ENABLED') && Configuration::get('PS_SSL_ENABLED_EVERYWHERE'));
99 | $shop = $this->context->shop;
100 | $base = (($force_ssl) ? 'https://' . $shop->domain_ssl : 'http://' . $shop->domain);
101 |
102 | $cfg = [
103 | 'platform' => [
104 | 'name' => 'Prestashop',
105 | 'version' => _PS_VERSION_,
106 | ],
107 | 'module' => [
108 | 'version' => DoofinderConstants::VERSION,
109 | 'feed' => $base . $shop->getBaseURI() . 'module/doofinder/feed',
110 | 'options' => [
111 | 'language' => $languages,
112 | 'currency' => $currencies,
113 | ],
114 | 'configuration' => $configurations,
115 | ],
116 | ];
117 |
118 | $jsonCfg = DfTools::jsonEncode($cfg);
119 | if (method_exists($this, 'ajaxRender')) {
120 | $this->ajaxRender($jsonCfg);
121 | exit;
122 | } elseif (method_exists($this, 'ajaxDie')) {
123 | // Workaround for PS 1.6 as ajaxRender is not available
124 | $this->ajaxDie($jsonCfg);
125 | } else {
126 | // Workaround for PS 1.5
127 | echo $jsonCfg;
128 | exit;
129 | }
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # doofinder-prestashop
2 |
3 | Plugin that allows to configure the [Doofinder](https://www.doofinder.com) search service in a Prestashop 1.5 store with less effort than configuring it from scratch.
4 |
5 | > [!IMPORTANT]
6 | > If you experience any issue with the module, please [contact Doofinder Support](https://support.doofinder.com/pages/contact-us) from the Doofinder website.
7 |
8 | ## How to install and configure Doofinder
9 |
10 | Refer to [Doofinder Support Documentation for Prestashop](https://support.doofinder.com/plugins/prestashop/installation-guide/installation-steps-prestashop) for the latest and up to date instructions.
11 |
12 | ## Module Compatibility
13 |
14 | ### PHP
15 |
16 | The minimum php required for this module is php 5.4
17 | The maximum php version tested is 8.4
18 |
19 | ### PrestaShop
20 |
21 | From 1.5.0.17 to latest version
22 |
23 | For more compatibility details check the following documentation
24 |
25 | - [Prestashop 1.x](https://devdocs.prestashop-project.org/1.7/basics/installation/system-requirements/) system requirements.
26 | - [Prestashop 8](https://devdocs.prestashop-project.org/8/basics/installation/system-requirements/) system requirements.
27 | - [Prestashop 9](https://devdocs.prestashop-project.org/9/basics/installation/system-requirements/) system requirements.
28 |
29 | ## Docker Environment
30 |
31 | ### Configure ngrok
32 |
33 | In order to be able to create an account or login to an existing Doofinder account during the module initial setup, you will have to expose your local webserver to the internet (to receive a callback).
34 |
35 | To do so, you can use, for example, the utility ngrok: https://dashboard.ngrok.com/get-started/setup
36 |
37 | Once the external URL is created, simply set the `BASE_URL` environment variable (see [Environment Variables](#environment-variables)).
38 |
39 | So, when the installation process finished, instead of accessing to `https://localhost:4011` you will use your url, for example, `https://forcibly-ethical-apple.ngrok-free.app`).
40 | Notice that you'll need to specify the 4011 port when executing ngrok.
41 |
42 | ### Environment variables
43 |
44 | > [!TIP]
45 | > You can create an `.env.local` file to override the environment variables defined in `.env` such as PrestaShop installation data to fit your needs.
46 |
47 | For example, below is a base `.env.local` file:
48 |
49 | ```bash
50 | #PrestaShop setup configuration data
51 | BASE_URL=your-url.ngrok-free.app
52 | PS_ENV=dev
53 |
54 | ```
55 |
56 | The `Makefile` automatically overrides `.env` vars with the ones found in `.env.local`.
57 |
58 | > [!IMPORTANT]
59 | > The `Makefile` internally appends `--env-file .env --env-file .env.local` to `docker compose` command for properly configuring container environment. So take it into account when interacting directly with `docker compose`.
60 |
61 | ### Initial setup
62 |
63 | You can set up a fresh PrestaShop installation using the provided `Makefile` target `init`. This command will:
64 |
65 | - Pulls and build a PrestaShop docker image with xdebug extension and maybe other tweaks. This build is configurable using the environment variables `PHP_VERSION` and `PS_VERSION` environment variables.
66 | - Starts the containers
67 | - Runs the installer script with the defined environment variables.
68 |
69 | Finally, PrestaShop is installed and will be running at `https://BASE_URL`.
70 |
71 | You can install the Doofinder module through the admin or execute `make doofinder-upgrade`.
72 |
73 | The admin panel will be available at `https://BASE_URL/PS_FOLDER_ADMIN`. Admin credentials are defined in the `.env`, if you used the `env.example` would be:
74 |
75 | - User: `test@example.com`
76 | - Pass: `admin123`
77 |
78 | > [!NOTE]
79 | > Keep in mind that for versions prior to 1.7 PrestaShop will ask you to delete the `install` folder and rename the `admin` folder located in the `html` directory.
80 | > For newer versions this is done automatically, using the value on the environment variable `PS_FOLDER_ADMIN` (by default, `/4dm1n`).
81 |
82 | ## Autoload
83 |
84 | Starting from version 6.0.0, this plugin uses the Composer autoloader, which is generated by running `composer dump-autoload` via the `make dump-autoload` command. This target is always executed after `make doofinder-configure`. If you create a new class, you must run `make dump-autoload` manually to make the class discoverable by PrestaShop.
85 |
86 | ## Version Upgrade
87 |
88 | To upgrade the package version, simply edit the `PLUGIN_VERSION` environment variable in the `.env` file and run `make doofinder-configure`. This will update all the necessary files. However, you must manually edit the `doofinder.php` file, as PrestaShop requires that `$this->version` be a hardcoded value.
89 |
90 | ## Xdebug ready to use
91 |
92 | If you wish to debug your new PrestaShop installation, the `XDEBUG_CONFIG` and `XDEBUG_MODE` environment variables are already configured in `docker-compose.yml`. Simply configure your IDE accordingly and have fun!
93 |
94 | ## Uninstall the module
95 |
96 | You can remove the Doofinder module using this straightforward method:
97 |
98 | ```sh
99 | make doofinder-uninstall
100 | ```
101 |
102 | ## Test another versions of the module
103 |
104 | Change your branch to the tag that you want inside package directory
105 |
106 | ```sh
107 | make doofinder-upgrade
108 | ```
109 |
110 | ## Backup and Restore Database
111 |
112 | During development, it is sometimes useful to create a data snapshot before performing an action.
113 |
114 | - To create a database dump, use:
115 | ```sh
116 | make db-backup [prefix=_some_state]
117 | ```
118 | - To restore a previous state, run:
119 | ```sh
120 | make db-restore file=backup_file.sql.gz
121 | ```
122 |
123 | ## Test other PrestaShop versions
124 |
125 | You can test different Prestashop versions along with different PHP versions. These are the latest combinations available gathered from [PrestaShop Docker Hub](https://hub.docker.com/r/prestashop/prestashop/tags)
126 |
127 | | PrestaShop | PHP |
128 | | ---------- | ----------------------- |
129 | | 8.2.1 | 8.1, 8.0, 7.4, 7.3, 7.2 |
130 | | 8.1.7 | 8.1, 8.0, 7.4, 7.3, 7.2 |
131 | | 8.0.5 | 8.1, 8.0, 7.4, 7.3, 7.2 |
132 | | 1.7.8.9 | 7.4, 7.3, 7.2, 7.1 |
133 | | 1.6 | 7.2, 7.1, 7.0, 5.6 |
134 | | 1.5[^ps15] | 7.2, 7.1, 7.0, 5.6, 5.5 |
135 |
136 | [^ps15]: Prestashop 1.5: This version is patched to allow auto installation (See Dockerfile). MySQL version must be 5.5. Must be used without SSL.
137 |
--------------------------------------------------------------------------------
/views/js/add-to-cart/doofinder-add_to_cart_ps17.js:
--------------------------------------------------------------------------------
1 | /**
2 | * NOTICE OF LICENSE
3 | *
4 | * This file is licenced under the Software License Agreement.
5 | * With the purchase or the installation of the software in your application
6 | * you accept the licence agreement.
7 | *
8 | * You must not modify, adapt or create derivative works of this source code
9 | *
10 | * @author Doofinder
11 | * @copyright Doofinder
12 | * @license GPLv3
13 | */
14 |
15 | class DoofinderAddToCartError extends Error {
16 | constructor(reason, status = "") {
17 | const message = "Error adding an item to the cart. Reason: " + reason + ". Status code: " + status;
18 | super(message);
19 | this.name = "DoofinderAddToCartError";
20 | }
21 | }
22 |
23 | //implementation of "add to cart" functionality for prestashop 1.7.x
24 |
25 | let dfAddToCart = (cartOptions) => {
26 |
27 | let IdProductCart;
28 | let IdCustomization;
29 | if (cartOptions.productID.includes('VAR-')) {
30 | IdProductCart = cartOptions.group_id;
31 | IdCustomization = cartOptions.productID.replace('VAR-', '');
32 | } else {
33 | IdProductCart = cartOptions.productID;
34 | IdCustomization = cartOptions.customizationID;
35 | }
36 |
37 | /**
38 | * 1º create the necessary form to add to cart with the required inputs.
39 | * (the "name" attribute of the inputs is necessary to trigger the event that prestashop uses natively
40 | * for "add to cart").
41 | */
42 | let form = document.createElement("form");
43 | Object.assign(form, {
44 | method: "post",
45 | action: cartOptions.cartURL,
46 | style: "display: none;"
47 | });
48 |
49 | let quantityInput = document.createElement("input");
50 | Object.assign(quantityInput, {
51 | type : "number",
52 | name : "qty",
53 | value : cartOptions.quantity,
54 | min : 1
55 | });
56 |
57 | let productAttributeInput = document.createElement("input");
58 | Object.assign(productAttributeInput, {
59 | type : "hidden",
60 | name : "id_product_attribute",
61 | value : IdCustomization
62 | });
63 |
64 | let productInput = document.createElement("input");
65 | Object.assign(productInput, {
66 | type : "hidden",
67 | name : "id_product",
68 | value : IdProductCart
69 | });
70 |
71 | let tokenInput = document.createElement("input");
72 | Object.assign(tokenInput, {
73 | type : "hidden",
74 | name : "token",
75 | value : cartOptions.cartToken
76 | });
77 |
78 | let submit = document.createElement("input");
79 | submit.setAttribute("type", "submit");
80 | submit.setAttribute("data-button-action", "add-to-cart");
81 |
82 | form.appendChild(quantityInput);
83 | form.appendChild(productAttributeInput);
84 | form.appendChild(productInput);
85 | form.appendChild(tokenInput);
86 | form.appendChild(submit);
87 |
88 | /**
89 | * 2º Add to DOM the form.
90 | */
91 | document.body.appendChild(form);
92 |
93 | if (typeof prestashop !== 'undefined' && typeof cartOptions.statusPromise !== 'undefined') {
94 |
95 | // It's triggered everytime the cart is updated.
96 | prestashop.on('updateCart', function (event) {
97 | // This should be trigger only if the cart has been updated from the layer. Otherwise, the statusPromise will be undefined.
98 | if (typeof cartOptions.statusPromise === 'undefined' ||
99 | !event ||
100 | !event.reason ||
101 | !event.resp) {
102 | return;
103 | }
104 |
105 | if (event.resp.success) {
106 | cartOptions.statusPromise.resolve("The item has been successfully added to the cart.");
107 | } else {
108 | // The event.resp.errors can be "" or an array
109 | errorList = Array.isArray(event.resp.errors) ? event.resp.errors.join(", ") : event.resp.errors;
110 | console.warn("Error adding to the cart: " + errorList);
111 | cartOptions.statusPromise.reject(new DoofinderAddToCartError(errorList));
112 | /* Special case: the product cannot be added because is customizable.
113 | I don't know if it's the best way to detect it, but at least it's better than
114 | detecting it from the error message. In this case, the user should be redirected
115 | to the product detail page */
116 | if (0 === event.resp.quantity && 'undefined' === typeof event.reason.cart) {
117 | window.location.href = cartOptions.itemLink;
118 | }
119 | }
120 | });
121 |
122 | prestashop.on('handleError', function (event) {
123 | // This should be trigger only if the cart has been updated from the layer. Otherwise, the statusPromise will be undefined.
124 | if (typeof cartOptions.statusPromise === 'undefined') {
125 | return;
126 | }
127 |
128 | // Possible errors variations coming from Add to Cart
129 | if (event && event.eventType && ('updateProductInCart' === event.eventType ||
130 | 'updateShoppingCart' === event.eventType ||
131 | 'updateCart' === event.eventType ||
132 | 'updateProductInCart' === event.eventType ||
133 | 'updateProductQuantityInCart' === event.eventType ||
134 | 'addProductToCart' === event.eventType)) {
135 |
136 | console.group(`Error of type '${event.eventType}' adding item to the cart.`);
137 |
138 | // If the event is addProductToCart, redirect to the item link.
139 | // This will prevent silent errors from both customizable products and products with minimal quantity.
140 | if (event.eventType === 'addProductToCart') {
141 | console.error("Redirecting user to the item page.");
142 | window.location.href = item_link;
143 | }
144 |
145 | /* Empirically I've have reproduced this error after loading another page in a specific moment */
146 | if (0 === event.resp.readyState) {
147 | console.error("The connection was interrupted while adding to the cart");
148 | }
149 |
150 | console.groupEnd();
151 | cartOptions.statusPromise.reject(new DoofinderAddToCartError(message));
152 | }
153 | });
154 | }
155 |
156 | /**
157 | * 3º clicks on the submit (does not work if you invoke the submit event)
158 | * and removes the form from the DOM
159 | */
160 | submit.click();
161 | form.remove();
162 | }
163 |
164 | let doofinderManageCart = (cartOptions) => {
165 | dfAddToCart(cartOptions);
166 | }
167 |
--------------------------------------------------------------------------------
/views/templates/admin/onboarding_tab.tpl:
--------------------------------------------------------------------------------
1 | {*
2 | * NOTICE OF LICENSE
3 | *
4 | * This file is licenced under the Software License Agreement.
5 | * With the purchase or the installation of the software in your application
6 | * you accept the licence agreement.
7 | *
8 | * You must not modify, adapt or create derivative works of this source code
9 | *
10 | * @author Doofinder
11 | * @copyright Doofinder
12 | * @license GPLv3
13 | *}
14 |
15 |
16 |
17 |
18 |
19 |
20 | {if $checkConnection}
21 | {l s='Connection with Doofinder successful.' mod='doofinder'}
22 | {else}
23 | {l s='You cannot connect with Doofinder. Please contact your server provider to check your web server internet connection or firewall.' mod='doofinder'}
24 | {/if}
25 |
26 |
27 |
28 |
29 |
30 | {l s='You don\'t receive the Api Key or you close too quickly the popup window. Please try again. If
31 | you think this is an error, please contact our support or try the module manual configuration
32 | option.' mod='doofinder'}
33 |
{l s='Add a smart search engine to your e-commerce in 5 minutes and with no programming' mod='doofinder'}
83 |
84 |
85 |
{l s='ABOUT DOOFINDER' mod='doofinder'}:
86 |
87 |
{l s='Doofinder provides you with an instant search layer to display your products when your visitors use the Doofinder script' mod='doofinder'}.
88 |
89 |
{l s='Your customers then have the opportunity to preview your products, filter them and choose the desired product. Upon hitting enter, doofinder will also power the results page' mod='doofinder'}.
90 |
91 |
{l s='Among our features are' mod='doofinder'}:
92 |
93 |
94 |
· {l s='Detailed reports on visitor search behaviour' mod='doofinder'}.
95 |
· {l s='Faceted search option' mod='doofinder'}.
96 |
· {l s='Learning behaviour' mod='doofinder'}.
97 |
· {l s='Merchandising power to set a products positioning' mod='doofinder'}.
98 |
· {l s='Banner feature for advertising and promoting products, brands and events' mod='doofinder'}.
99 |
· {l s='Options to redirect users to content pages' mod='doofinder'}.
100 |
· {l s='Held on our servers for a faster page load time' mod='doofinder'}.
101 |
102 |
103 |
{l s='More than 5000 e-commerce sites over 35 countries are already using it' mod='doofinder'}.
104 |