added CSV export
This commit is contained in:
@@ -1,13 +1,14 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Product Link Checker Product Data Generate Controller
|
* Product Link Checker Category Data Generate Controller
|
||||||
*
|
*
|
||||||
* This controller generates a JSON feed of all products and their attribute combinations
|
* This controller generates a JSON or CSV feed of all categories.
|
||||||
* with detailed information for external services.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Serializer\Encoder\CsvEncoder;
|
||||||
|
|
||||||
class ProductLinkCheckerCategoryModuleFrontController extends ModuleFrontController
|
class ProductLinkCheckerCategoryModuleFrontController extends ModuleFrontController
|
||||||
{
|
{
|
||||||
@@ -26,15 +27,17 @@ class ProductLinkCheckerCategoryModuleFrontController extends ModuleFrontControl
|
|||||||
{
|
{
|
||||||
parent::initContent();
|
parent::initContent();
|
||||||
|
|
||||||
|
$format = Tools::getValue('format', 'json');
|
||||||
$categoriesData = [];
|
$categoriesData = [];
|
||||||
$collection = new PrestaShopCollection('Category');
|
$collection = new PrestaShopCollection('Category');
|
||||||
|
|
||||||
foreach ((array)Tools::getValue('plc_id_shop', Shop::getShops(true, null, true)) as $id_shop) {
|
foreach ((array)Tools::getValue('plc_id_shop', Shop::getShops(true, null, true)) as $id_shop) {
|
||||||
foreach ((array)Tools::getValue('plc_id_lang', Language::getLanguages(false, false, true)) as $id_lang) {
|
foreach ((array)Tools::getValue('plc_id_lang', Language::getLanguages(false, false, true)) as $id_lang) {
|
||||||
|
|
||||||
foreach ($collection as $cat) {
|
foreach ($collection as $cat) {
|
||||||
|
|
||||||
$category = new Category((int)$cat->id, $id_lang, $id_shop);
|
$category = new Category((int)$cat->id, $id_lang, $id_shop);
|
||||||
|
if (!Validate::isLoadedObject($category)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (Tools::getValue('plc_only_active') && !$category->active) {
|
if (Tools::getValue('plc_only_active') && !$category->active) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -52,14 +55,40 @@ class ProductLinkCheckerCategoryModuleFrontController extends ModuleFrontControl
|
|||||||
Tools::getValue('plc_meta_title') ? $data['meta_title'] = $category->meta_title : null;
|
Tools::getValue('plc_meta_title') ? $data['meta_title'] = $category->meta_title : null;
|
||||||
Tools::getValue('plc_meta_description') ? $data['meta_description'] = $category->meta_description : null;
|
Tools::getValue('plc_meta_description') ? $data['meta_description'] = $category->meta_description : null;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
$categoriesData[] = $data;
|
$categoriesData[] = $data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($format === 'csv') {
|
||||||
|
$this->sendCsvResponse($categoriesData, 'categories.csv');
|
||||||
|
} else {
|
||||||
$response = new JsonResponse($categoriesData);
|
$response = new JsonResponse($categoriesData);
|
||||||
$response->send();
|
$response->send();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes data as CSV and sends it as a downloadable file.
|
||||||
|
*/
|
||||||
|
private function sendCsvResponse(array $data, $filename)
|
||||||
|
{
|
||||||
|
if (empty($data)) {
|
||||||
|
$response = new Response('', 204); // No Content
|
||||||
|
$response->send();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$csvEncoder = new CsvEncoder();
|
||||||
|
$csvContent = $csvEncoder->encode($data, 'csv', [
|
||||||
|
CsvEncoder::DELIMITER_KEY => ';', // Semicolon for better Excel compatibility
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = new Response($csvContent);
|
||||||
|
$response->headers->set('Content-Type', 'text/csv');
|
||||||
|
$response->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '"');
|
||||||
|
$response->send();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -3,11 +3,13 @@
|
|||||||
/**
|
/**
|
||||||
* Product Link Checker Product Data Generate Controller
|
* Product Link Checker Product Data Generate Controller
|
||||||
*
|
*
|
||||||
* This controller generates a JSON feed of all products and their attribute combinations
|
* This controller generates a JSON or CSV feed of all products and their attribute combinations
|
||||||
* with detailed information for external services.
|
* with detailed information for external services.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use Symfony\Component\HttpFoundation\JsonResponse;
|
use Symfony\Component\HttpFoundation\JsonResponse;
|
||||||
|
use Symfony\Component\HttpFoundation\Response;
|
||||||
|
use Symfony\Component\Serializer\Encoder\CsvEncoder;
|
||||||
|
|
||||||
class ProductLinkCheckerProductModuleFrontController extends ModuleFrontController
|
class ProductLinkCheckerProductModuleFrontController extends ModuleFrontController
|
||||||
{
|
{
|
||||||
@@ -18,7 +20,7 @@ class ProductLinkCheckerProductModuleFrontController extends ModuleFrontControll
|
|||||||
{
|
{
|
||||||
parent::init();
|
parent::init();
|
||||||
|
|
||||||
// Security check: Validate the token, same as the other controller
|
// Security check
|
||||||
if (!Tools::getValue('token') || Tools::getValue('token') !== Configuration::get('PLC_SECURITY_TOKEN')) {
|
if (!Tools::getValue('token') || Tools::getValue('token') !== Configuration::get('PLC_SECURITY_TOKEN')) {
|
||||||
$response = new JsonResponse(['error' => 'Not Authorized'], 403);
|
$response = new JsonResponse(['error' => 'Not Authorized'], 403);
|
||||||
$response->send();
|
$response->send();
|
||||||
@@ -33,12 +35,15 @@ class ProductLinkCheckerProductModuleFrontController extends ModuleFrontControll
|
|||||||
{
|
{
|
||||||
parent::initContent();
|
parent::initContent();
|
||||||
|
|
||||||
|
$format = Tools::getValue('format', 'json');
|
||||||
$productsData = [];
|
$productsData = [];
|
||||||
$collection = new PrestaShopCollection('Product');
|
$collection = new PrestaShopCollection('Product');
|
||||||
|
|
||||||
|
// This temporary array will help flatten attributes for CSV
|
||||||
|
$attributes_list = [];
|
||||||
|
|
||||||
foreach ((array)Tools::getValue('plc_id_shop', Shop::getShops(true, null, true)) as $id_shop) {
|
foreach ((array)Tools::getValue('plc_id_shop', Shop::getShops(true, null, true)) as $id_shop) {
|
||||||
foreach ((array)Tools::getValue('plc_id_lang', Language::getLanguages(false, false, true)) as $id_lang) {
|
foreach ((array)Tools::getValue('plc_id_lang', Language::getLanguages(false, false, true)) as $id_lang) {
|
||||||
|
|
||||||
foreach ($collection as $p) {
|
foreach ($collection as $p) {
|
||||||
$product = new Product((int)$p->id, false, $id_lang, $id_shop);
|
$product = new Product((int)$p->id, false, $id_lang, $id_shop);
|
||||||
|
|
||||||
@@ -55,80 +60,105 @@ class ProductLinkCheckerProductModuleFrontController extends ModuleFrontControll
|
|||||||
foreach ($combinations as $combination) {
|
foreach ($combinations as $combination) {
|
||||||
$index = $product->id . '_' . $combination['id_product_attribute'];
|
$index = $product->id . '_' . $combination['id_product_attribute'];
|
||||||
if (!isset($productsData[$index])) {
|
if (!isset($productsData[$index])) {
|
||||||
$productsData[$index] = [
|
$productsData[$index] = $this->getBaseProductData($product, $id_lang, $id_shop, (int)$combination['id_product_attribute']);
|
||||||
'id_lang' => (int)$id_lang,
|
|
||||||
'id_shop' => (int)$id_shop,
|
|
||||||
'id_product' => (int)$product->id,
|
|
||||||
'id_product_attribute' => (int)$combination['id_product_attribute'],
|
|
||||||
'active' => (bool)$product->active,
|
|
||||||
'link' => $this->context->link->getProductLink(
|
|
||||||
$product,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
(int)$id_lang,
|
|
||||||
(int)$id_shop,
|
|
||||||
(int)$combination['id_product_attribute'],
|
|
||||||
false
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Conditionally add data based on URL flags
|
|
||||||
Tools::getValue('plc_name') ? $productsData[$index]['name'] = $product->name : null;
|
|
||||||
Tools::getValue('plc_link_rewrite') ? $productsData[$index]['link_rewrite'] = $product->link_rewrite : null;
|
|
||||||
Tools::getValue('plc_description') ? $productsData[$index]['description'] = $product->description : null;
|
|
||||||
Tools::getValue('plc_description_short') ? $productsData[$index]['description_short'] = $product->description_short : null;
|
|
||||||
Tools::getValue('plc_meta_title') ? $productsData[$index]['meta_title'] = $product->meta_title : null;
|
|
||||||
Tools::getValue('plc_meta_description') ? $productsData[$index]['meta_description'] = $product->meta_description : null;
|
|
||||||
Tools::getValue('plc_reference') ? $productsData[$index]['reference'] = $combination['reference'] : null;
|
|
||||||
Tools::getValue('plc_ean13') ? $productsData[$index]['ean13'] = $combination['ean13'] : null;
|
|
||||||
Tools::getValue('plc_upc') ? $productsData[$index]['upc'] = $combination['upc'] : null;
|
|
||||||
Tools::getValue('plc_mpn') ? $productsData[$index]['mpn'] = $combination['mpn'] : null;
|
|
||||||
}
|
}
|
||||||
|
// Store attributes for later processing (both JSON and CSV)
|
||||||
$productsData[$index]['attributes'][] = [
|
$attributes_list[$index][] = [
|
||||||
'group_name' => $combination['group_name'] ?? null,
|
'group_name' => $combination['group_name'] ?? null,
|
||||||
'attribute_name' => $combination['attribute_name'] ?? null,
|
'attribute_name' => $combination['attribute_name'] ?? null,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
} else { // Handle simple products (without combinations)
|
} else { // Handle simple products (without combinations)
|
||||||
$index = $product->id . '_' . 0;
|
$index = $product->id . '_' . 0;
|
||||||
$productsData[$index] = [
|
$productsData[$index] = $this->getBaseProductData($product, $id_lang, $id_shop, 0);
|
||||||
'id_lang' => (int)$id_lang,
|
|
||||||
'id_shop' => (int)$id_shop,
|
|
||||||
'id_product' => (int)$product->id,
|
|
||||||
'id_product_attribute' => 0,
|
|
||||||
'active' => (bool)$product->active,
|
|
||||||
'link' => $this->context->link->getProductLink(
|
|
||||||
$product,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
null,
|
|
||||||
(int)$id_lang,
|
|
||||||
(int)$id_shop,
|
|
||||||
0,
|
|
||||||
false
|
|
||||||
),
|
|
||||||
];
|
|
||||||
|
|
||||||
// Conditionally add data based on URL flags
|
|
||||||
Tools::getValue('plc_name') ? $productsData[$index]['name'] = $product->name : null;
|
|
||||||
Tools::getValue('plc_link_rewrite') ? $productsData[$index]['link_rewrite'] = $product->link_rewrite : null;
|
|
||||||
Tools::getValue('plc_description') ? $productsData[$index]['description'] = $product->description : null;
|
|
||||||
Tools::getValue('plc_description_short') ? $productsData[$index]['description_short'] = $product->description_short : null;
|
|
||||||
Tools::getValue('plc_meta_title') ? $productsData[$index]['meta_title'] = $product->meta_title : null;
|
|
||||||
Tools::getValue('plc_meta_description') ? $productsData[$index]['meta_description'] = $product->meta_description : null;
|
|
||||||
Tools::getValue('plc_reference') ? $productsData[$index]['reference'] = $product->reference : null;
|
|
||||||
Tools::getValue('plc_ean13') ? $productsData[$index]['ean13'] = $product->ean13 : null;
|
|
||||||
Tools::getValue('plc_upc') ? $productsData[$index]['upc'] = $product->upc : null;
|
|
||||||
Tools::getValue('plc_mpn') ? $productsData[$index]['mpn'] = $product->mpn : null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Post-process the data based on the requested format
|
||||||
|
foreach ($productsData as $index => &$data) {
|
||||||
|
if (isset($attributes_list[$index])) {
|
||||||
|
if ($format === 'csv') {
|
||||||
|
// Flatten attributes into a single string for CSV
|
||||||
|
$flat_attributes = [];
|
||||||
|
foreach ($attributes_list[$index] as $attr) {
|
||||||
|
$flat_attributes[] = $attr['group_name'] . ':' . $attr['attribute_name'];
|
||||||
|
}
|
||||||
|
$data['attributes'] = implode(' | ', $flat_attributes);
|
||||||
|
} else {
|
||||||
|
// Keep the structured array for JSON
|
||||||
|
$data['attributes'] = $attributes_list[$index];
|
||||||
|
}
|
||||||
|
} elseif ($format === 'csv') {
|
||||||
|
// Ensure the attributes column exists for simple products in CSV
|
||||||
|
$data['attributes'] = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
unset($data);
|
||||||
|
|
||||||
|
|
||||||
|
if ($format === 'csv') {
|
||||||
|
$this->sendCsvResponse(array_values($productsData), 'products.csv');
|
||||||
|
} else {
|
||||||
$response = new JsonResponse($productsData);
|
$response = new JsonResponse($productsData);
|
||||||
$response->send();
|
$response->send();
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to get common product data fields.
|
||||||
|
*/
|
||||||
|
private function getBaseProductData(Product $product, $id_lang, $id_shop, $id_product_attribute)
|
||||||
|
{
|
||||||
|
$combination = new Combination($id_product_attribute);
|
||||||
|
$data = [
|
||||||
|
'id_lang' => (int)$id_lang,
|
||||||
|
'id_shop' => (int)$id_shop,
|
||||||
|
'id_product' => (int)$product->id,
|
||||||
|
'id_product_attribute' => $id_product_attribute,
|
||||||
|
'active' => (bool)$product->active,
|
||||||
|
'link' => $this->context->link->getProductLink($product, null, null, null, (int)$id_lang, (int)$id_shop, $id_product_attribute, false),
|
||||||
|
];
|
||||||
|
|
||||||
|
// Conditionally add data based on URL flags
|
||||||
|
Tools::getValue('plc_name') ? $data['name'] = $product->name : null;
|
||||||
|
Tools::getValue('plc_link_rewrite') ? $data['link_rewrite'] = $product->link_rewrite : null;
|
||||||
|
Tools::getValue('plc_description') ? $data['description'] = $product->description : null;
|
||||||
|
Tools::getValue('plc_description_short') ? $data['description_short'] = $product->description_short : null;
|
||||||
|
Tools::getValue('plc_meta_title') ? $data['meta_title'] = $product->meta_title : null;
|
||||||
|
Tools::getValue('plc_meta_description') ? $data['meta_description'] = $product->meta_description : null;
|
||||||
|
|
||||||
|
// Use combination specific data if it exists, otherwise fallback to product
|
||||||
|
Tools::getValue('plc_reference') ? $data['reference'] = ($id_product_attribute && !empty($combination->reference)) ? $combination->reference : $product->reference : null;
|
||||||
|
Tools::getValue('plc_ean13') ? $data['ean13'] = ($id_product_attribute && !empty($combination->ean13)) ? $combination->ean13 : $product->ean13 : null;
|
||||||
|
Tools::getValue('plc_upc') ? $data['upc'] = ($id_product_attribute && !empty($combination->upc)) ? $combination->upc : $product->upc : null;
|
||||||
|
Tools::getValue('plc_mpn') ? $data['mpn'] = ($id_product_attribute && !empty($combination->mpn)) ? $combination->mpn : $product->mpn : null;
|
||||||
|
|
||||||
|
return $data;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encodes data as CSV and sends it as a downloadable file.
|
||||||
|
*/
|
||||||
|
private function sendCsvResponse(array $data, $filename)
|
||||||
|
{
|
||||||
|
if (empty($data)) {
|
||||||
|
$response = new Response('', 204); // No Content
|
||||||
|
$response->send();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
|
|
||||||
|
$csvEncoder = new CsvEncoder();
|
||||||
|
$csvContent = $csvEncoder->encode($data, 'csv', [
|
||||||
|
CsvEncoder::DELIMITER_KEY => ';', // Semicolon for better Excel compatibility
|
||||||
|
]);
|
||||||
|
|
||||||
|
$response = new Response($csvContent);
|
||||||
|
$response->headers->set('Content-Type', 'text/csv');
|
||||||
|
$response->headers->set('Content-Disposition', 'attachment; filename="' . $filename . '"');
|
||||||
|
$response->send();
|
||||||
|
exit;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
{* views/templates/admin/configure.tpl *}
|
{* views/templates/admin/configure.tpl *}
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
|
|
||||||
<div class="panel-heading">
|
<div class="panel-heading">
|
||||||
<i class="icon-link"></i> {l s='Product Link Checker Configuration' mod='productlinkchecker'}
|
<i class="icon-link"></i> {l s='Product Link Checker Configuration' mod='productlinkchecker'}
|
||||||
</div>
|
</div>
|
||||||
@@ -62,6 +63,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{* --- NEW FORMAT SELECTOR --- *}
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-lg-3 control-label">{l s='Output Format' mod='productlinkchecker'}</label>
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<span class="switch prestashop-switch fixed-width-lg">
|
||||||
|
<input type="radio" name="format" id="format_prod_json" value="json" class="plc-builder-input" data-type="product" checked="checked">
|
||||||
|
<label for="format_prod_json">JSON</label>
|
||||||
|
<input type="radio" name="format" id="format_prod_csv" value="csv" class="plc-builder-input" data-type="product">
|
||||||
|
<label for="format_prod_csv">CSV</label>
|
||||||
|
<a class="slide-button btn"></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<h4>{l s='2. Select Data Fields to Include' mod='productlinkchecker'}</h4>
|
<h4>{l s='2. Select Data Fields to Include' mod='productlinkchecker'}</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -135,6 +150,20 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{* --- NEW FORMAT SELECTOR --- *}
|
||||||
|
<div class="form-group">
|
||||||
|
<label class="col-lg-3 control-label">{l s='Output Format' mod='productlinkchecker'}</label>
|
||||||
|
<div class="col-lg-9">
|
||||||
|
<span class="switch prestashop-switch fixed-width-lg">
|
||||||
|
<input type="radio" name="format" id="format_cat_json" value="json" class="plc-builder-input" data-type="category" checked="checked">
|
||||||
|
<label for="format_cat_json">JSON</label>
|
||||||
|
<input type="radio" name="format" id="format_cat_csv" value="csv" class="plc-builder-input" data-type="category">
|
||||||
|
<label for="format_cat_csv">CSV</label>
|
||||||
|
<a class="slide-button btn"></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
<h4>{l s='2. Select Data Fields to Include' mod='productlinkchecker'}</h4>
|
<h4>{l s='2. Select Data Fields to Include' mod='productlinkchecker'}</h4>
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
@@ -174,14 +203,12 @@
|
|||||||
|
|
||||||
<script>
|
<script>
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function() {
|
||||||
// Base URLs and token from Smarty
|
|
||||||
const baseUrls = {
|
const baseUrls = {
|
||||||
product: '{$product_controller_url|escape:'javascript':'UTF-8'}',
|
product: '{$product_controller_url|escape:'javascript':'UTF-8'}',
|
||||||
category: '{$category_controller_url|escape:'javascript':'UTF-8'}'
|
category: '{$category_controller_url|escape:'javascript':'UTF-8'}'
|
||||||
};
|
};
|
||||||
const token = '{$security_token|escape:'javascript':'UTF-8'}';
|
const token = '{$security_token|escape:'javascript':'UTF-8'}';
|
||||||
|
|
||||||
// Function to build the URL based on selected options
|
|
||||||
const buildUrl = (type) => {
|
const buildUrl = (type) => {
|
||||||
let baseUrl = baseUrls[type];
|
let baseUrl = baseUrls[type];
|
||||||
let params = new URLSearchParams();
|
let params = new URLSearchParams();
|
||||||
@@ -195,7 +222,7 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
if (input.checked && input.value) {
|
if (input.checked && input.value) {
|
||||||
params.append(input.name, input.value);
|
params.append(input.name, input.value);
|
||||||
}
|
}
|
||||||
} else { // select
|
} else {
|
||||||
if (input.value) {
|
if (input.value) {
|
||||||
params.append(input.name, input.value);
|
params.append(input.name, input.value);
|
||||||
}
|
}
|
||||||
@@ -208,24 +235,20 @@ document.addEventListener('DOMContentLoaded', function() {
|
|||||||
document.getElementById('open-url-' + type).href = finalUrl;
|
document.getElementById('open-url-' + type).href = finalUrl;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Attach event listeners to all inputs
|
|
||||||
document.querySelectorAll('.plc-builder-input').forEach(input => {
|
document.querySelectorAll('.plc-builder-input').forEach(input => {
|
||||||
input.addEventListener('change', (event) => {
|
input.addEventListener('change', (event) => {
|
||||||
buildUrl(event.target.dataset.type);
|
buildUrl(event.target.dataset.type);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initial build for both tabs on page load
|
|
||||||
buildUrl('product');
|
buildUrl('product');
|
||||||
buildUrl('category');
|
buildUrl('category');
|
||||||
});
|
});
|
||||||
|
|
||||||
// Simple copy to clipboard function
|
|
||||||
function copyToClipboard(type) {
|
function copyToClipboard(type) {
|
||||||
const urlInput = document.getElementById('generated-url-' + type);
|
const urlInput = document.getElementById('generated-url-' + type);
|
||||||
urlInput.select();
|
urlInput.select();
|
||||||
document.execCommand('copy');
|
document.execCommand('copy');
|
||||||
// Optional: show a small feedback message
|
|
||||||
showSuccessMessage('{l s="URL copied to clipboard!" mod='productlinkchecker' js=1}');
|
showSuccessMessage('{l s="URL copied to clipboard!" mod='productlinkchecker' js=1}');
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
Reference in New Issue
Block a user