Skip to content

Enhance SWJProjects Licensing: Domain Storage Plugin, Template Override & AJAX Validation #115

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions ajax_license_checker/wt_license_checker.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
<?php
defined('_JEXEC') or die;

use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\CMS\Response\JsonResponse;
use Joomla\CMS\Factory;
use Joomla\Database\DatabaseDriver;

class PlgAjaxWt_license_checker extends CMSPlugin
{
public function onAjaxWt_license_checker()
{
Factory::getApplication()->setHeader('Content-Type', 'application/json', true);

// Get license key and domain from the request
$input = Factory::getApplication()->input;
$licenseKey = $input->getString('key', '');
$domain = $input->getString('domain', '');

// Validate input
if (empty($licenseKey) || empty($domain)) {
echo json_encode(['error' => 'Missing license key or domain']);
return;
}

// Query license key from the database
$db = Factory::getContainer()->get(DatabaseDriver::class);
$query = $db->getQuery(true)
->select('*')
->from($db->quoteName('#__swjprojects_keys'))
->where($db->quoteName('key') . ' = ' . $db->quote($licenseKey));
$db->setQuery($query);
$result = $db->loadAssoc();

// If license key not found
if (!$result) {
echo json_encode(['error' => 'Invalid license key']);
return;
}

$dateEnd = $result['date_end'];
$allowedPlugins = isset($result['allowed_plugins']) ? $result['allowed_plugins'] : '*';
$registeredDomain = $result['domain'];

// Validate domain
$isDomainValid = $this->isAllowedDomain($registeredDomain, $domain);

// Check if license is still valid
$isValid = $isDomainValid && ($dateEnd == '0000-00-00 00:00:00' || strtotime($dateEnd) > time());

// Prepare response (fix it as you want)
$data = [
'domain' => $registeredDomain,
'date_start' => $result['date_start'],
'date_end' => $result['date_end'],
'project_id' => $result['project_id'],
'state' => $result['state'],
'license_name' => $result['license_name'] ?? 'Standard',
'expires' => ($isValid ? 'never' : $result['date_end']),
'key_status' => ($isValid ? 'Active' : 'Expired'),
'owner' => $result['owner'] ?? 'N/A',
'license_valid' => ($isValid ? '1' : '0'),
'allows_plugins' => ($isValid ? '1' : '0'),
'is_trial_license' => $result['is_trial_license'] ?? '0',
'allowed_plugins' => ($isValid ? $allowedPlugins : '')
];

echo json_encode($data);
return;
}

/**
* Check if current domain is allowed (supports subdomains)
*/
private function isAllowedDomain($registeredDomain, $currentDomain)
{
if (empty($registeredDomain)) {
return false;
}

$registeredDomain = parse_url('http://' . $registeredDomain, PHP_URL_HOST);
$currentDomain = parse_url('http://' . $currentDomain, PHP_URL_HOST);

if ($registeredDomain === $currentDomain) {
return true;
}

if (str_ends_with($currentDomain, '.' . $registeredDomain)) {
return true;
}

return false;
}
}
11 changes: 11 additions & 0 deletions ajax_license_checker/wt_license_checker.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="ajax" method="upgrade" version="4.0">
<name>WT License Checker</name>
<author>Nick Giannelis</author>
<version>1.0.0</version>
<creationDate>March 2025</creationDate>
<description>AJAX plugin for checking license keys in SW JProjects</description>
<files>
<filename plugin="wt_license_checker">wt_license_checker.php</filename>
</files>
</extension>
1 change: 1 addition & 0 deletions plugins/system/save_domain/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@

37 changes: 37 additions & 0 deletions plugins/system/save_domain/save_domain.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php
defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\Plugin\CMSPlugin;
use Joomla\Input\Json;

class PlgSystemSave_Domain extends CMSPlugin
{
public function onAjaxSave_domain()
{
$input = Factory::getApplication()->input;
$json = json_decode(file_get_contents('php://input'));

if (!isset($json->key_id) || !isset($json->domain)) {
return json_encode(['success' => false, 'message' => 'Missing parameters']);
}

// Debug log
file_put_contents(JPATH_SITE . '/logs/save_domain_log.txt', "Key ID: {$json->key_id}, Domain: {$json->domain}\n", FILE_APPEND);

$db = Factory::getDbo();
$query = $db->getQuery(true)
->update($db->quoteName('#__swjprojects_keys'))
->set($db->quoteName('domain') . ' = ' . $db->quote($json->domain))
->where($db->quoteName('id') . ' = ' . (int) $json->key_id);

$db->setQuery($query);

try {
$db->execute();
return json_encode(['success' => true, 'message' => 'Domain saved successfully']);
} catch (Exception $e) {
return json_encode(['success' => false, 'message' => $e->getMessage()]);
}
}
}
12 changes: 12 additions & 0 deletions plugins/system/save_domain/save_domain.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<extension type="plugin" group="system" method="upgrade" version="4.0">
<name>System - Save Domain</name>
<author>Nick Giannelis</author>
<version>1.0.0</version>
<description>Plugin to save domain for user keys</description>
<files>
<filename plugin="save_domain">save_domain.php</filename>
</files>
<config>
</config>
</extension>
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<?php
/*
* @package SW JProjects
* @version 2.3.0
* @author Sergey Tolkachyov
* @сopyright Copyright (c) 2018 - 2025 Sergey Tolkachyov. All rights reserved.
* @license GNU/GPL license: https://www.gnu.org/copyleft/gpl.html
* @link https://web-tolk.ru
*/

defined('_JEXEC') or die;

use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Language\Text;
use Joomla\CMS\Router\Route;
use Joomla\CMS\Date\Date;
use Joomla\Component\SWJProjects\Administrator\Helper\KeysHelper;

Text::script('ERROR');
Text::script('MESSAGE');
Text::script('COM_SWJPROJECTS_USER_KEYS_DOMAIN_SAVED');

$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
$wa->registerAndUseScript('com_swjprojects.userkeys.copykey', 'com_swjprojects/copy-userkey.js', ['version' => 'auto', 'relative' => true]);

?>
<div id="SWJProjects" class="userkeys">
<h1><?php echo Text::_('COM_SWJPROJECTS_USER_KEYS'); ?></h1>
<?php if (empty($this->items)) : ?>
<div class="alert alert-info">
<span class="icon-info-circle" aria-hidden="true"></span>
<span class="visually-hidden"><?php echo Text::_('INFO'); ?></span>
<?php echo Text::_('JGLOBAL_NO_MATCHING_RESULTS'); ?>
</div>
<?php else : ?>
<div class="userkeyslist">
<div class="d-flex flex-column">
<?php foreach ($this->items as $item) :
$downloadAble = ($item->limit && $item->limit_count == 0) ? false : true;
$date_expired = ((new Date())->toUnix() > (new Date($item->date_end))->toUnix()) ? false : true;
if (!$date_expired) $downloadAble = false;
?>
<div class="card mb-3 <?php echo(!$downloadAble ? 'border-danger' : ''); ?>">
<div class="card-body">
<div class="d-flex justify-content-between mb-3">
<div class="fs-5 fw-bolder bg-light p-2">
<?php if (!$downloadAble) : ?>
<i class="fa-solid fa-lock"></i> <s>
<?php else : ?>
<i class="fa-solid fa-key"></i>
<?php endif; ?>
<span><?php echo KeysHelper::maskKey($item->key); ?></span>
<?php if (!$downloadAble): ?>
</s>
<?php else : ?>
<button type="button" class="btn btn-outline-secondary btn-sm copy-key" data-key="<?php echo $item->key; ?>">
<i class="fa-solid fa-copy"></i>
</button>
<?php endif; ?>
</div>
</div>

<!-- NEW DOMAIN FIELD -->
<div class="mb-3">
<label for="domain-<?php echo $item->id; ?>" class="form-label">
<?php echo Text::_('COM_SWJPROJECTS_USER_KEYS_DOMAIN'); ?>
</label>
<input type="text" class="form-control domain-input" id="domain-<?php echo $item->id; ?>"
value="<?php echo htmlspecialchars($item->domain); ?>" data-keyid="<?php echo $item->id; ?>">
<button class="btn btn-success mt-2 save-domain" data-keyid="<?php echo $item->id; ?>">
<?php echo Text::_('JSAVE'); ?>
</button>
</div>

</div>
</div>
<?php endforeach ?>
</div>
</div>
<?php endif; ?>
</div>

<script>
document.addEventListener('DOMContentLoaded', function () {
document.querySelectorAll('.save-domain').forEach(button => {
button.addEventListener('click', function () {
let keyId = this.getAttribute('data-keyid');
let domainInput = document.querySelector('#domain-' + keyId);
let domain = domainInput.value.trim();

if (domain === '') {
alert('<?php echo Text::_('COM_SWJPROJECTS_USER_KEYS_DOMAIN_REQUIRED'); ?>');
return;
}

fetch('index.php?option=com_ajax&plugin=save_domain&format=json', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
key_id: keyId,
domain: domain
})
})
.then(response => response.json())
.then(data => {
if (data.success) {
alert('<?php echo Text::_('COM_SWJPROJECTS_USER_KEYS_DOMAIN_SAVED'); ?>');
} else {
alert('<?php echo Text::_('COM_SWJPROJECTS_USER_KEYS_DOMAIN_ERROR'); ?>');
}
})
.catch(error => console.error('Error:', error));
});
});
});
</script>