Compare commits
14 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| dd31c062ad | |||
| 869f25d6db | |||
| b41fa58488 | |||
| 1b4c6fe66a | |||
| 320710fd02 | |||
| 11d720aa25 | |||
| 08bd6d23c9 | |||
| 28de4e88b7 | |||
| 0c1e916ed6 | |||
| 1bebdff3ac | |||
| 5e6c3e46fc | |||
| ff227fa6e0 | |||
| 2e715e803e | |||
| 8e6b29976c |
@@ -48,6 +48,15 @@ backups/
|
||||
cache/
|
||||
cron/
|
||||
|
||||
# Moduł zarządzania releaseami (tylko serwer dewelopera)
|
||||
autoload/admin/controls/class.Releases.php
|
||||
autoload/admin/factory/class.Releases.php
|
||||
autoload/admin/view/class.Releases.php
|
||||
admin/templates/releases/
|
||||
|
||||
# Menu dewelopera
|
||||
admin/templates/additional-menu.php
|
||||
|
||||
# IDE
|
||||
.vscode/
|
||||
.serena/
|
||||
|
||||
11
admin/templates/additional-menu.php
Normal file
11
admin/templates/additional-menu.php
Normal file
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
// Menu tylko na serwerze dewelopera — wykluczone z .updateignore
|
||||
?>
|
||||
<div class="title">Developer</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/admin/releases/main_view/">
|
||||
<img src="/admin/css/icons/settings-20-filled.svg">Releases & Licencje
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
312
admin/templates/releases/main-view.php
Normal file
312
admin/templates/releases/main-view.php
Normal file
@@ -0,0 +1,312 @@
|
||||
<?php
|
||||
global $gdb;
|
||||
ob_start();
|
||||
?>
|
||||
<style>
|
||||
.releases-tabs-nav { margin-bottom: 0; border-bottom: 1px solid #ddd; }
|
||||
.releases-tabs-nav li { display: inline-block; margin-bottom: -1px; }
|
||||
.releases-tabs-nav li a {
|
||||
display: block; padding: 8px 16px; text-decoration: none; color: #555;
|
||||
border: 1px solid transparent; border-radius: 3px 3px 0 0;
|
||||
background: #f5f5f5; margin-right: 2px; cursor: pointer;
|
||||
}
|
||||
.releases-tabs-nav li.active a {
|
||||
color: #333; background: #fff;
|
||||
border-color: #ddd #ddd #fff;
|
||||
}
|
||||
.releases-tab-pane { display: none; padding: 18px 0 0; }
|
||||
.releases-tab-pane.active { display: block; }
|
||||
.license-form-wrap { display: none; margin-bottom: 20px; }
|
||||
</style>
|
||||
|
||||
<ul class="releases-tabs-nav" id="releases-tabs-nav">
|
||||
<li class="active"><a href="#" data-tab="tab-versions">Wersje</a></li>
|
||||
<li><a href="#" data-tab="tab-licenses">Licencje</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- TAB: Wersje -->
|
||||
<div class="releases-tab-pane active" id="tab-versions">
|
||||
<div style="margin-bottom: 12px;">
|
||||
<form method="post" action="/admin/releases/discover_versions/" style="display:inline"
|
||||
onsubmit="return confirm('Wykryć wersje z dysku i zarejestrować jako stable?')">
|
||||
<button type="submit" class="btn btn-info btn-sm">
|
||||
<i class="fa fa-search"></i> Wykryj wersje z dysku
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
<table class="table table-bordered table-striped table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Wersja</th>
|
||||
<th class="text-center" style="width:100px;">Kanał</th>
|
||||
<th style="width:150px;">Data dodania</th>
|
||||
<th style="width:150px;">Data promocji</th>
|
||||
<th class="text-center" style="width:60px;">ZIP</th>
|
||||
<th class="text-center" style="width:140px;">Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($this->versions)): ?>
|
||||
<tr><td colspan="6" class="text-center text-muted">Brak wersji w bazie.</td></tr>
|
||||
<?php else: foreach ($this->versions as $v): ?>
|
||||
<tr>
|
||||
<td><strong><?= htmlspecialchars($v['version']) ?></strong></td>
|
||||
<td class="text-center">
|
||||
<?php if ($v['channel'] === 'stable'): ?>
|
||||
<span class="label label-success">stable</span>
|
||||
<?php else: ?>
|
||||
<span class="label label-warning">beta</span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($v['created_at'] ?? '') ?></td>
|
||||
<td><?= $v['promoted_at'] ? htmlspecialchars($v['promoted_at']) : '<span class="text-muted">—</span>' ?></td>
|
||||
<td class="text-center">
|
||||
<?php if ($v['zip_exists']): ?>
|
||||
<span class="text-success"><i class="fa fa-check"></i></span>
|
||||
<?php else: ?>
|
||||
<span class="text-danger"><i class="fa fa-times"></i></span>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<?php if ($v['channel'] === 'beta'): ?>
|
||||
<form method="post" action="/admin/releases/promote/" style="display:inline">
|
||||
<input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
|
||||
<button type="submit" class="btn btn-success btn-xs"
|
||||
onclick="return confirm('Promować <?= htmlspecialchars($v['version'], ENT_QUOTES) ?> do stable?')">
|
||||
Promuj →stable
|
||||
</button>
|
||||
</form>
|
||||
<?php else: ?>
|
||||
<form method="post" action="/admin/releases/demote/" style="display:inline">
|
||||
<input type="hidden" name="version" value="<?= htmlspecialchars($v['version']) ?>">
|
||||
<button type="submit" class="btn btn-warning btn-xs"
|
||||
onclick="return confirm('Cofnąć <?= htmlspecialchars($v['version'], ENT_QUOTES) ?> do beta?')">
|
||||
Cofnij →beta
|
||||
</button>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<!-- TAB: Licencje -->
|
||||
<div class="releases-tab-pane" id="tab-licenses">
|
||||
|
||||
<div style="margin-bottom: 12px;">
|
||||
<a href="#" class="btn btn-success btn-sm" id="btn-add-license">
|
||||
<i class="fa fa-plus-circle"></i> Dodaj licencję
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Formularz dodawania / edycji -->
|
||||
<div class="license-form-wrap panel panel-default" id="license-form-wrap">
|
||||
<div class="panel-heading">
|
||||
<strong id="license-form-title">Nowa licencja</strong>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
<form method="post" action="/admin/releases/save_license/" id="license-form">
|
||||
<input type="hidden" name="id" id="lic-id" value="">
|
||||
<div class="row">
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label>Domena</label>
|
||||
<input type="text" name="domain" id="lic-domain" class="form-control" placeholder="np. example.com" required>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-6">
|
||||
<div class="form-group">
|
||||
<label>Klucz licencji</label>
|
||||
<input type="text" name="key" id="lic-key" class="form-control" placeholder="Klucz UUID / losowy ciąg (pusty = domyślny)">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label>Ważna do daty</label>
|
||||
<input type="date" name="valid_to_date" id="lic-valid-date" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label>Ważna do wersji</label>
|
||||
<input type="text" name="valid_to_version" id="lic-valid-ver" class="form-control" placeholder="np. 1.700">
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<div class="form-group">
|
||||
<label>Dostęp beta</label>
|
||||
<select name="beta" id="lic-beta" class="form-control">
|
||||
<option value="0">Nie</option>
|
||||
<option value="1">Tak</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>Notatka</label>
|
||||
<input type="text" name="note" id="lic-note" class="form-control" placeholder="Opcjonalna notatka">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-system btn-sm">
|
||||
<i class="fa fa-save"></i> Zapisz licencję
|
||||
</button>
|
||||
<a href="#" class="btn btn-default btn-sm" id="btn-cancel-license">Anuluj</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Tabela licencji -->
|
||||
<table class="table table-bordered table-striped table-hover table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domena</th>
|
||||
<th style="width:120px;">Klucz</th>
|
||||
<th style="width:120px;">Do daty</th>
|
||||
<th style="width:100px;">Do wersji</th>
|
||||
<th class="text-center" style="width:70px;">Beta</th>
|
||||
<th>Notatka</th>
|
||||
<th class="text-center" style="width:60px;">Edytuj</th>
|
||||
<th class="text-center" style="width:60px;">Usuń</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<?php if (empty($this->licenses)): ?>
|
||||
<tr><td colspan="8" class="text-center text-muted">Brak licencji w bazie.</td></tr>
|
||||
<?php else: foreach ($this->licenses as $lic): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($lic['domain']) ?></td>
|
||||
<td>
|
||||
<code title="<?= htmlspecialchars($lic['key']) ?>">
|
||||
<?= htmlspecialchars(substr($lic['key'], 0, 8)) ?>…
|
||||
</code>
|
||||
</td>
|
||||
<td><?= $lic['valid_to_date'] ? htmlspecialchars($lic['valid_to_date']) : '<span class="text-muted">—</span>' ?></td>
|
||||
<td><?= $lic['valid_to_version'] ? htmlspecialchars($lic['valid_to_version']) : '<span class="text-muted">—</span>' ?></td>
|
||||
<td class="text-center">
|
||||
<form method="post" action="/admin/releases/toggle_beta/" style="display:inline">
|
||||
<input type="hidden" name="id" value="<?= (int)$lic['id'] ?>">
|
||||
<button type="submit"
|
||||
class="label <?= $lic['beta'] ? 'label-info' : 'label-default' ?>"
|
||||
title="Kliknij, aby przełączyć"
|
||||
style="cursor:pointer;border:none;background:none">
|
||||
<?= $lic['beta'] ? 'tak' : 'nie' ?>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($lic['note'] ?? '') ?></td>
|
||||
<td class="text-center">
|
||||
<a href="#"
|
||||
class="btn btn-default btn-xs btn-edit-license"
|
||||
data-id="<?= (int)$lic['id'] ?>"
|
||||
data-domain="<?= htmlspecialchars($lic['domain'], ENT_QUOTES) ?>"
|
||||
data-key="<?= htmlspecialchars($lic['key'], ENT_QUOTES) ?>"
|
||||
data-valid-date="<?= htmlspecialchars($lic['valid_to_date'] ?? '', ENT_QUOTES) ?>"
|
||||
data-valid-ver="<?= htmlspecialchars($lic['valid_to_version'] ?? '', ENT_QUOTES) ?>"
|
||||
data-beta="<?= (int)$lic['beta'] ?>"
|
||||
data-note="<?= htmlspecialchars($lic['note'] ?? '', ENT_QUOTES) ?>">
|
||||
<i class="fa fa-pencil"></i>
|
||||
</a>
|
||||
</td>
|
||||
<td class="text-center">
|
||||
<form method="post" action="/admin/releases/delete_license/" style="display:inline"
|
||||
onsubmit="return confirm('Usunąć licencję dla <?= htmlspecialchars($lic['domain'], ENT_QUOTES) ?>?')">
|
||||
<input type="hidden" name="id" value="<?= (int)$lic['id'] ?>">
|
||||
<button type="submit" class="btn btn-danger btn-xs">
|
||||
<i class="fa fa-trash"></i>
|
||||
</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<?php endforeach; endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
// Tab switching
|
||||
document.querySelectorAll('#releases-tabs-nav a[data-tab]').forEach(function (link) {
|
||||
link.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
var targetId = this.getAttribute('data-tab');
|
||||
document.querySelectorAll('#releases-tabs-nav li').forEach(function (li) {
|
||||
li.classList.remove('active');
|
||||
});
|
||||
document.querySelectorAll('.releases-tab-pane').forEach(function (pane) {
|
||||
pane.classList.remove('active');
|
||||
});
|
||||
this.parentElement.classList.add('active');
|
||||
document.getElementById(targetId).classList.add('active');
|
||||
});
|
||||
});
|
||||
|
||||
// Show add-license form
|
||||
document.getElementById('btn-add-license').addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
resetLicenseForm();
|
||||
document.getElementById('license-form-title').textContent = 'Nowa licencja';
|
||||
var wrap = document.getElementById('license-form-wrap');
|
||||
if (wrap.style.display === 'none' || wrap.style.display === '') {
|
||||
wrap.style.display = 'block';
|
||||
$(wrap).slideDown(200);
|
||||
}
|
||||
});
|
||||
|
||||
// Cancel
|
||||
document.getElementById('btn-cancel-license').addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
$(document.getElementById('license-form-wrap')).slideUp(200);
|
||||
});
|
||||
|
||||
// Edit buttons
|
||||
document.querySelectorAll('.btn-edit-license').forEach(function (btn) {
|
||||
btn.addEventListener('click', function (e) {
|
||||
e.preventDefault();
|
||||
var d = this.dataset;
|
||||
document.getElementById('lic-id').value = d.id;
|
||||
document.getElementById('lic-domain').value = d.domain;
|
||||
document.getElementById('lic-key').value = d.key;
|
||||
document.getElementById('lic-valid-date').value = d.validDate;
|
||||
document.getElementById('lic-valid-ver').value = d.validVer;
|
||||
document.getElementById('lic-beta').value = d.beta;
|
||||
document.getElementById('lic-note').value = d.note;
|
||||
document.getElementById('license-form-title').textContent = 'Edytuj licencję: ' + d.domain;
|
||||
var wrap = document.getElementById('license-form-wrap');
|
||||
wrap.style.display = 'block';
|
||||
$(wrap).slideDown(200);
|
||||
wrap.scrollIntoView({ behavior: 'smooth', block: 'start' });
|
||||
});
|
||||
});
|
||||
|
||||
function resetLicenseForm() {
|
||||
document.getElementById('lic-id').value = '';
|
||||
document.getElementById('lic-domain').value = '';
|
||||
document.getElementById('lic-key').value = '';
|
||||
document.getElementById('lic-valid-date').value = '';
|
||||
document.getElementById('lic-valid-ver').value = '';
|
||||
document.getElementById('lic-beta').value = '0';
|
||||
document.getElementById('lic-note').value = '';
|
||||
}
|
||||
|
||||
// If URL hash indicates licenses tab, switch to it on load
|
||||
if (window.location.hash === '#licenses') {
|
||||
document.querySelector('[data-tab="tab-licenses"]').click();
|
||||
}
|
||||
})();
|
||||
</script>
|
||||
<?php
|
||||
$out = ob_get_clean();
|
||||
|
||||
$grid = new \gridEdit;
|
||||
$grid->id = 'releases-view';
|
||||
$grid->gdb_opt = $gdb;
|
||||
$grid->include_plugins = true;
|
||||
$grid->title = 'Releases & Licencje';
|
||||
$grid->default_buttons = false;
|
||||
$grid->form = false;
|
||||
$grid->external_code = $out;
|
||||
echo $grid->draw();
|
||||
?>
|
||||
@@ -18,12 +18,13 @@ class SettingsRepository
|
||||
{
|
||||
if ( !$settings = \Shared\Cache\CacheHandler::fetch( 'settings_details' ) )
|
||||
{
|
||||
$results = $this->db->select( 'pp_settings', '*' );
|
||||
$settings = [];
|
||||
$results = $this->db->select( 'pp_settings', '*' );
|
||||
if ( is_array( $results ) )
|
||||
foreach ( $results as $row )
|
||||
$settings[ $row['param'] ] = $row['value'];
|
||||
|
||||
\Shared\Cache\CacheHandler::store( 'settings_details', $settings ?? [] );
|
||||
\Shared\Cache\CacheHandler::store( 'settings_details', $settings );
|
||||
}
|
||||
|
||||
return $settings ?? [];
|
||||
|
||||
@@ -68,6 +68,11 @@ class Tpl
|
||||
$this->vars[ $name ] = $value;
|
||||
}
|
||||
|
||||
public function __isset( $name )
|
||||
{
|
||||
return isset( $this->vars[ $name ] );
|
||||
}
|
||||
|
||||
public function __get( $name )
|
||||
{
|
||||
return $this->vars[ $name ];
|
||||
|
||||
62
autoload/admin/controls/class.Releases.php
Normal file
62
autoload/admin/controls/class.Releases.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function main_view(): string
|
||||
{
|
||||
return \admin\view\Releases::main_view();
|
||||
}
|
||||
|
||||
public static function promote(): void
|
||||
{
|
||||
$version = trim(\S::get('version'));
|
||||
if ($version)
|
||||
\admin\factory\Releases::promote($version);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function demote(): void
|
||||
{
|
||||
$version = trim(\S::get('version'));
|
||||
if ($version)
|
||||
\admin\factory\Releases::demote($version);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function discover_versions(): void
|
||||
{
|
||||
$added = \admin\factory\Releases::discover_versions();
|
||||
\S::set_message("Wykryto i dodano {$added} nowych wersji jako stable.");
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function save_license(): void
|
||||
{
|
||||
\admin\factory\Releases::save_license($_POST);
|
||||
\S::set_message('Licencja została zapisana.');
|
||||
header('Location: /admin/releases/main_view/#licenses');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function delete_license(): void
|
||||
{
|
||||
$id = (int)\S::get('id');
|
||||
if ($id)
|
||||
\admin\factory\Releases::delete_license($id);
|
||||
header('Location: /admin/releases/main_view/#licenses');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function toggle_beta(): void
|
||||
{
|
||||
$id = (int)\S::get('id');
|
||||
if ($id)
|
||||
\admin\factory\Releases::toggle_beta($id);
|
||||
header('Location: /admin/releases/main_view/#licenses');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
103
autoload/admin/factory/class.Releases.php
Normal file
103
autoload/admin/factory/class.Releases.php
Normal file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
namespace admin\factory;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function get_versions(): array
|
||||
{
|
||||
global $mdb;
|
||||
$rows = $mdb->select('pp_update_versions', '*', ['ORDER' => ['version' => 'DESC']]);
|
||||
if (!$rows) return [];
|
||||
foreach ($rows as &$row)
|
||||
$row['zip_exists'] = file_exists('../updates/' . self::zip_dir($row['version']) . '/ver_' . $row['version'] . '.zip');
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public static function promote(string $version): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->update('pp_update_versions',
|
||||
['channel' => 'stable', 'promoted_at' => date('Y-m-d H:i:s')],
|
||||
['version' => $version]
|
||||
);
|
||||
}
|
||||
|
||||
public static function demote(string $version): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->update('pp_update_versions',
|
||||
['channel' => 'beta', 'promoted_at' => null],
|
||||
['version' => $version]
|
||||
);
|
||||
}
|
||||
|
||||
public static function discover_versions(): int
|
||||
{
|
||||
global $mdb;
|
||||
$known = array_flip($mdb->select('pp_update_versions', 'version', []) ?: []);
|
||||
$zips = glob('../updates/*/ver_*.zip') ?: [];
|
||||
$added = 0;
|
||||
foreach ($zips as $path) {
|
||||
preg_match('/ver_([0-9.]+)\.zip$/', $path, $m);
|
||||
if (!$m) continue;
|
||||
$ver = $m[1];
|
||||
if (isset($known[$ver])) continue;
|
||||
$mdb->insert('pp_update_versions', [
|
||||
'version' => $ver,
|
||||
'channel' => 'beta',
|
||||
'created_at' => date('Y-m-d H:i:s'),
|
||||
]);
|
||||
$known[$ver] = true;
|
||||
$added++;
|
||||
}
|
||||
return $added;
|
||||
}
|
||||
|
||||
public static function get_licenses(): array
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->select('pp_update_licenses', '*', ['ORDER' => ['domain' => 'ASC']]) ?: [];
|
||||
}
|
||||
|
||||
public static function get_license(int $id): array
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->get('pp_update_licenses', '*', ['id' => $id]) ?: [];
|
||||
}
|
||||
|
||||
public static function save_license(array $data): void
|
||||
{
|
||||
global $mdb;
|
||||
$row = [
|
||||
'key' => trim($data['key'] ?? ''),
|
||||
'domain' => trim($data['domain'] ?? ''),
|
||||
'valid_to_date' => $data['valid_to_date'] ?: null,
|
||||
'valid_to_version' => $data['valid_to_version'] ?: null,
|
||||
'beta' => (int)(bool)($data['beta'] ?? 0),
|
||||
'note' => trim($data['note'] ?? ''),
|
||||
];
|
||||
if (!empty($data['id']))
|
||||
$mdb->update('pp_update_licenses', $row, ['id' => (int)$data['id']]);
|
||||
else
|
||||
$mdb->insert('pp_update_licenses', $row);
|
||||
}
|
||||
|
||||
public static function delete_license(int $id): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->delete('pp_update_licenses', ['id' => $id]);
|
||||
}
|
||||
|
||||
public static function toggle_beta(int $id): void
|
||||
{
|
||||
global $mdb;
|
||||
$license = $mdb->get('pp_update_licenses', ['id', 'beta'], ['id' => $id]);
|
||||
if ($license)
|
||||
$mdb->update('pp_update_licenses', ['beta' => $license['beta'] ? 0 : 1], ['id' => $id]);
|
||||
}
|
||||
|
||||
private static function zip_dir(string $version): string
|
||||
{
|
||||
return substr($version, 0, strlen($version) - (strlen($version) == 5 ? 2 : 1)) . '0';
|
||||
}
|
||||
}
|
||||
13
autoload/admin/view/class.Releases.php
Normal file
13
autoload/admin/view/class.Releases.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
namespace admin\view;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function main_view(): string
|
||||
{
|
||||
$tpl = new \Tpl;
|
||||
$tpl->versions = \admin\factory\Releases::get_versions();
|
||||
$tpl->licenses = \admin\factory\Releases::get_licenses();
|
||||
return $tpl->render('releases/main-view');
|
||||
}
|
||||
}
|
||||
10
composer.json
Normal file
10
composer.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^10.5"
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4": {
|
||||
"Tests\\": "tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
1688
composer.lock
generated
Normal file
1688
composer.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,6 @@
|
||||
<?php
|
||||
$database['host'] = 'localhost';
|
||||
$database['host_remote'] = 'host117523.hostido.net.pl';
|
||||
$database['user'] = 'host117523_cmspro';
|
||||
$database['password'] = '3sJADeqKHLqHddfavDeR';
|
||||
$database['name'] = 'host117523_cmspro';
|
||||
|
||||
120
docs/TESTING.md
120
docs/TESTING.md
@@ -1,106 +1,54 @@
|
||||
# Testowanie shopPRO
|
||||
# Testowanie cmsPRO
|
||||
|
||||
## Szybki start
|
||||
|
||||
```bash
|
||||
# Pelny suite (PowerShell — rekomendowane)
|
||||
./test.ps1
|
||||
# Instalacja PHPUnit (jednorazowo)
|
||||
composer install
|
||||
|
||||
# Uruchomienie testów
|
||||
./vendor/bin/phpunit
|
||||
|
||||
# Konkretny plik
|
||||
./test.ps1 tests/Unit/Domain/Product/ProductRepositoryTest.php
|
||||
./vendor/bin/phpunit tests/Unit/Domain/Settings/SettingsRepositoryTest.php
|
||||
|
||||
# Konkretny test
|
||||
./test.ps1 --filter testGetQuantityReturnsCorrectValue
|
||||
|
||||
# Alternatywne
|
||||
composer test # standard
|
||||
./test.bat # testdox (czytelna lista)
|
||||
./test-simple.bat # kropki
|
||||
./test-debug.bat # debug
|
||||
./test.sh # Git Bash
|
||||
./vendor/bin/phpunit --filter testAllSettingsReturnsMappedArray
|
||||
```
|
||||
|
||||
## Aktualny stan
|
||||
|
||||
```text
|
||||
OK (805 tests, 2253 assertions)
|
||||
Testy jednostkowe dla Domain\ (Faza 2 DDD)
|
||||
```
|
||||
|
||||
Zweryfikowano: 2026-02-24 (ver. 0.318)
|
||||
|
||||
## Konfiguracja
|
||||
|
||||
- **PHPUnit 9.6** via `phpunit.phar`
|
||||
- **PHPUnit 10** via `composer`
|
||||
- **Bootstrap:** `tests/bootstrap.php`
|
||||
- **Config:** `phpunit.xml`
|
||||
|
||||
## Struktura testow
|
||||
## Struktura testów
|
||||
|
||||
```
|
||||
tests/
|
||||
|-- bootstrap.php
|
||||
|-- stubs/
|
||||
| |-- CacheHandler.php (inline w bootstrap)
|
||||
| |-- Helpers.php (Shared\Helpers\Helpers stub)
|
||||
| `-- ShopProduct.php (shop\Product stub)
|
||||
|-- Unit/
|
||||
| |-- Domain/
|
||||
| | |-- Article/ArticleRepositoryTest.php
|
||||
| | |-- Attribute/AttributeRepositoryTest.php
|
||||
| | |-- Banner/BannerRepositoryTest.php
|
||||
| | |-- Basket/BasketCalculatorTest.php
|
||||
| | |-- Cache/CacheRepositoryTest.php
|
||||
| | |-- Category/CategoryRepositoryTest.php
|
||||
| | |-- Coupon/CouponRepositoryTest.php
|
||||
| | |-- CronJob/CronJobTypeTest.php
|
||||
| | |-- CronJob/CronJobRepositoryTest.php
|
||||
| | |-- CronJob/CronJobProcessorTest.php
|
||||
| | |-- Dictionaries/DictionariesRepositoryTest.php
|
||||
| | |-- Integrations/IntegrationsRepositoryTest.php
|
||||
| | |-- Languages/LanguagesRepositoryTest.php
|
||||
| | |-- Layouts/LayoutsRepositoryTest.php
|
||||
| | |-- Newsletter/NewsletterRepositoryTest.php
|
||||
| | |-- Pages/PagesRepositoryTest.php
|
||||
| | |-- PaymentMethod/PaymentMethodRepositoryTest.php
|
||||
| | |-- Producer/ProducerRepositoryTest.php
|
||||
| | |-- Product/ProductRepositoryTest.php
|
||||
| | |-- ProductSet/ProductSetRepositoryTest.php
|
||||
| | |-- Promotion/PromotionRepositoryTest.php
|
||||
| | |-- Settings/SettingsRepositoryTest.php
|
||||
| | |-- ShopStatus/ShopStatusRepositoryTest.php
|
||||
| | |-- Transport/TransportRepositoryTest.php
|
||||
| | |-- Update/UpdateRepositoryTest.php
|
||||
| | `-- User/UserRepositoryTest.php
|
||||
| `-- admin/
|
||||
| `-- Controllers/
|
||||
| |-- ArticlesControllerTest.php
|
||||
| |-- DictionariesControllerTest.php
|
||||
| |-- IntegrationsControllerTest.php
|
||||
| |-- ProductArchiveControllerTest.php
|
||||
| |-- SettingsControllerTest.php
|
||||
| |-- ShopAttributeControllerTest.php
|
||||
| |-- ShopCategoryControllerTest.php
|
||||
| |-- ShopCouponControllerTest.php
|
||||
| |-- ShopPaymentMethodControllerTest.php
|
||||
| |-- ShopProducerControllerTest.php
|
||||
| |-- ShopProductControllerTest.php
|
||||
| |-- ShopProductSetsControllerTest.php
|
||||
| |-- ShopPromotionControllerTest.php
|
||||
| |-- ShopStatusesControllerTest.php
|
||||
| |-- ShopTransportControllerTest.php
|
||||
| `-- UsersControllerTest.php
|
||||
| `-- api/
|
||||
| |-- ApiRouterTest.php
|
||||
| `-- Controllers/
|
||||
| |-- OrdersApiControllerTest.php
|
||||
| |-- ProductsApiControllerTest.php
|
||||
| `-- DictionariesApiControllerTest.php
|
||||
`-- Integration/ (puste — zarezerwowane)
|
||||
├── bootstrap.php ← autoloader + stuby (CacheHandler, S)
|
||||
└── Unit/
|
||||
└── Domain/
|
||||
├── Languages/LanguagesRepositoryTest.php
|
||||
├── Settings/SettingsRepositoryTest.php
|
||||
└── User/UserRepositoryTest.php
|
||||
```
|
||||
|
||||
## Dodawanie nowych testow
|
||||
## Stuby (bootstrap.php)
|
||||
|
||||
1. Plik w `tests/Unit/Domain/<Module>/<Class>Test.php`, `tests/Unit/admin/Controllers/<Class>Test.php` lub `tests/Unit/api/Controllers/<Class>Test.php`.
|
||||
- `\Shared\Cache\CacheHandler` — in-memory stub z `fetch()`/`store()`/`delete()`/`reset()`
|
||||
- `\S` — stub z `delete_cache()`, `htacces()`, `get_domain()`, `send_email()`
|
||||
- `medoo` — mockowany przez PHPUnit (`$this->createMock(\medoo::class)`)
|
||||
|
||||
## Dodawanie nowych testów
|
||||
|
||||
1. Plik w `tests/Unit/Domain/<Modul>/<Klasa>Test.php`.
|
||||
2. Rozszerz `PHPUnit\Framework\TestCase`.
|
||||
3. Nazwy metod zaczynaj od `test`.
|
||||
4. Wzorzec AAA: Arrange, Act, Assert.
|
||||
@@ -108,19 +56,11 @@ tests/
|
||||
## Mockowanie Medoo
|
||||
|
||||
```php
|
||||
$mockDb = $this->createMock(\medoo::class);
|
||||
$mockDb->method('get')->willReturn(42);
|
||||
$db = $this->createMock(\medoo::class);
|
||||
$db->method('get')->willReturn(['id' => 1]);
|
||||
|
||||
$repo = new ProductRepository($mockDb);
|
||||
$value = $repo->getQuantity(123);
|
||||
$repo = new SettingsRepository($db);
|
||||
$value = $repo->visitCounter();
|
||||
|
||||
$this->assertEquals(42, $value);
|
||||
$this->assertSame('1', $value);
|
||||
```
|
||||
|
||||
## Bootstrap — stuby
|
||||
|
||||
`tests/bootstrap.php` rejestruje autoloader i definiuje stuby:
|
||||
- `Redis`, `RedisConnection` — klasy Redis (aby nie wymagac rozszerzenia)
|
||||
- `Shared\Cache\CacheHandler` — inline stub z `get()`/`set()`/`exists()`/`delete()`/`deletePattern()`
|
||||
- `Shared\Helpers\Helpers` — z `tests/stubs/Helpers.php`
|
||||
- `shop\Product` — z `tests/stubs/ShopProduct.php`
|
||||
|
||||
113
docs/plans/2026-02-28-update-channels-design.md
Normal file
113
docs/plans/2026-02-28-update-channels-design.md
Normal file
@@ -0,0 +1,113 @@
|
||||
# Design: Dwukanałowy system aktualizacji + zarządzanie licencjami
|
||||
|
||||
**Data:** 2026-02-28
|
||||
**Status:** Zatwierdzony
|
||||
|
||||
## Kontekst
|
||||
|
||||
cmsPRO jest używany przez wielu klientów. Celem jest wprowadzenie dwustopniowej dystrybucji aktualizacji:
|
||||
- Kanał **beta** — tylko wybrane testowe instalacje (1–2 strony dewelopera)
|
||||
- Kanał **stable** — wszyscy pozostali klienci
|
||||
|
||||
Zarządzanie odbywa się z panelu admina na `cmspro.project-dc.pl` (ten sam serwer i DB co serwer aktualizacji).
|
||||
|
||||
**Kluczowe ograniczenie:** nowy moduł admina oraz nowe tabele DB nigdy nie trafiają do paczek aktualizacyjnych wysyłanych klientom.
|
||||
|
||||
---
|
||||
|
||||
## Sekcja 1: Baza danych
|
||||
|
||||
Dwie nowe tabele tworzone **ręcznie tylko na serwerze dewelopera**. Nie mogą pojawić się w żadnym SQL migracyjnym dla klientów.
|
||||
|
||||
```sql
|
||||
CREATE TABLE pp_update_licenses (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`key` VARCHAR(64) NOT NULL UNIQUE,
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
valid_to_date DATE NULL,
|
||||
valid_to_version VARCHAR(10) NULL,
|
||||
beta TINYINT(1) NOT NULL DEFAULT 0,
|
||||
note TEXT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
CREATE TABLE pp_update_versions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
version VARCHAR(10) NOT NULL UNIQUE,
|
||||
channel ENUM('beta','stable') NOT NULL DEFAULT 'beta',
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
promoted_at DATETIME NULL
|
||||
);
|
||||
```
|
||||
|
||||
Dane z `updates/versions.php` (tablica `$license`) migrujemy jednorazowo do `pp_update_licenses`.
|
||||
|
||||
---
|
||||
|
||||
## Sekcja 2: Plik `updates/versions.php`
|
||||
|
||||
Plik zostaje w tym samym miejscu, zmienia się tylko logika (hardkodowane tablice zastępowane odczytem z DB).
|
||||
|
||||
### Nowy przepływ:
|
||||
|
||||
1. `require_once '../config.php'` → dostęp do `$mdb` (Medoo)
|
||||
2. Skan filesystem — lista istniejących ZIPów (jak dotychczas)
|
||||
3. **Auto-discovery:** każdy ZIP nieobecny w `pp_update_versions` → `INSERT IGNORE` z `channel='beta'`. Wrzucenie nowego ZIPa na serwer automatycznie rejestruje go jako beta — bez zmian w `build-update.ps1`.
|
||||
4. Walidacja klucza z `pp_update_licenses` (zamiast `$license[...]`)
|
||||
5. Filtrowanie wersji według kanału:
|
||||
- klient z `beta=1` → wersje gdzie `channel IN ('beta','stable')`
|
||||
- klient z `beta=0` → tylko `channel='stable'`
|
||||
6. Dalsze filtrowanie po `valid_to_date` i `valid_to_version` (bez zmian)
|
||||
7. Wypisanie listy wersji (jak dotychczas)
|
||||
|
||||
---
|
||||
|
||||
## Sekcja 3: Nowy moduł admina `Releases`
|
||||
|
||||
Nowa nazwa `Releases` (odróżnienie od istniejącego modułu `Update` używanego przez klientów do aktualizacji własnej instalacji).
|
||||
|
||||
### Zakładka "Wersje"
|
||||
|
||||
- Tabela z `pp_update_versions` + status istnienia ZIPa na dysku
|
||||
- Kolumny: wersja, kanał, data dodania, data promocji
|
||||
- Akcje: **Promuj do stable** / **Cofnij do beta**
|
||||
|
||||
### Zakładka "Licencje"
|
||||
|
||||
- Tabela z `pp_update_licenses`
|
||||
- Kolumny: domena, klucz (skrócony hash), ważna do daty, ważna do wersji, beta (toggle), notatka
|
||||
- Pełny CRUD: dodaj / edytuj / usuń
|
||||
- Szybki toggle flagi `beta` bezpośrednio z listy
|
||||
|
||||
### Pliki modułu
|
||||
|
||||
Wszystkie wykluczone z paczek ZIP dla klientów:
|
||||
|
||||
```
|
||||
autoload/admin/controls/class.Releases.php
|
||||
autoload/admin/factory/class.Releases.php
|
||||
autoload/admin/view/class.Releases.php
|
||||
admin/templates/releases/main-view.php
|
||||
admin/templates/releases/licenses-view.php
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Sekcja 4: Wykluczenie z aktualizacji klientów
|
||||
|
||||
### Pliki PHP
|
||||
W `build-update.ps1` — dodanie powyższych ścieżek do listy wykluczeń.
|
||||
|
||||
### Tabele DB
|
||||
`pp_update_licenses` i `pp_update_versions` **nigdy** nie pojawiają się w:
|
||||
- plikach `_sql.txt` dołączanych do paczek
|
||||
- sekcji `sql` w plikach `_manifest.json`
|
||||
|
||||
Tabele tworzone jednorazowo ręcznie na serwerze dewelopera.
|
||||
|
||||
---
|
||||
|
||||
## Migracja danych
|
||||
|
||||
Jednorazowy skrypt PHP (uruchamiany ręcznie na serwerze) przenosi dane z `$license[...]` z `versions.php` do `pp_update_licenses`. Po migracji tablica `$license` w `versions.php` jest usuwana.
|
||||
766
docs/plans/2026-02-28-update-channels.md
Normal file
766
docs/plans/2026-02-28-update-channels.md
Normal file
@@ -0,0 +1,766 @@
|
||||
# Releases Module Implementation Plan
|
||||
|
||||
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
|
||||
|
||||
**Goal:** Wdrożyć dwukanałowy system aktualizacji (beta/stable) z zarządzaniem licencjami w panelu admina.
|
||||
|
||||
**Architecture:** Dwie nowe tabele MySQL (`pp_update_licenses`, `pp_update_versions`) dostępne tylko na serwerze dewelopera. `updates/versions.php` czyta z DB przez Medoo. Nowy moduł `Releases` w panelu admina zarządza wersjami i licencjami. Całość wykluczona z paczek klientów przez `.updateignore`.
|
||||
|
||||
**Tech Stack:** PHP 8.x, Medoo 1.x (`$mdb` global), jQuery UI (dialog), Bootstrap 3 (klasy CSS), `\Tpl` (admin templates z `admin/templates/`), `\S::get()` (POST→GET params).
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Dodaj wykluczenia do `.updateignore`
|
||||
|
||||
**Files:**
|
||||
- Modify: `.updateignore`
|
||||
|
||||
**Step 1: Dopisz nowe wykluczenia**
|
||||
|
||||
Otwórz `.updateignore` i dopisz na końcu:
|
||||
|
||||
```
|
||||
# Moduł zarządzania releaseami (tylko serwer dewelopera)
|
||||
autoload/admin/controls/class.Releases.php
|
||||
autoload/admin/factory/class.Releases.php
|
||||
autoload/admin/view/class.Releases.php
|
||||
admin/templates/releases/
|
||||
|
||||
# Menu dewelopera
|
||||
templates/additional-menu.php
|
||||
```
|
||||
|
||||
**Step 2: Zweryfikuj manualnie**
|
||||
|
||||
Uruchom build w trybie dry-run i sprawdź, że powyższe pliki NIE pojawiają się na liście:
|
||||
```powershell
|
||||
./build-update.ps1 -ToTag v9.999 -DryRun
|
||||
```
|
||||
|
||||
**Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add .updateignore
|
||||
git commit -m "chore: wyklucz modul Releases i menu dewelopera z paczek klientow"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Utwórz tabele DB (jednorazowo na serwerze)
|
||||
|
||||
**Files:**
|
||||
- Create: `_db_releases_setup.sql` (uruchom raz w phpMyAdmin, nie commituj)
|
||||
|
||||
**Step 1: Utwórz plik SQL**
|
||||
|
||||
```sql
|
||||
CREATE TABLE pp_update_licenses (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
`key` VARCHAR(64) NOT NULL UNIQUE,
|
||||
domain VARCHAR(255) NOT NULL,
|
||||
valid_to_date DATE NULL,
|
||||
valid_to_version VARCHAR(10) NULL,
|
||||
beta TINYINT(1) NOT NULL DEFAULT 0,
|
||||
note TEXT NULL,
|
||||
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE pp_update_versions (
|
||||
id INT AUTO_INCREMENT PRIMARY KEY,
|
||||
version VARCHAR(10) NOT NULL UNIQUE,
|
||||
channel ENUM('beta','stable') NOT NULL DEFAULT 'beta',
|
||||
created_at DATETIME NULL,
|
||||
promoted_at DATETIME NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
```
|
||||
|
||||
**Step 2: Uruchom w phpMyAdmin na serwerze `cmspro.project-dc.pl`**
|
||||
|
||||
Wklej SQL do phpMyAdmin → Execute. Obie tabele muszą być widoczne.
|
||||
|
||||
**Step 3: Nie commituj** — ten plik jest tylko pomocniczy, możesz go usunąć po wykonaniu.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Utwórz `admin\factory\Releases`
|
||||
|
||||
**Files:**
|
||||
- Create: `autoload/admin/factory/class.Releases.php`
|
||||
|
||||
**Step 1: Napisz klasę**
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace admin\factory;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function get_versions(): array
|
||||
{
|
||||
global $mdb;
|
||||
$rows = $mdb->select('pp_update_versions', '*', ['ORDER' => ['version' => 'DESC']]);
|
||||
if (!$rows) return [];
|
||||
foreach ($rows as &$row)
|
||||
$row['zip_exists'] = file_exists('../updates/' . self::zip_dir($row['version']) . '/ver_' . $row['version'] . '.zip');
|
||||
return $rows;
|
||||
}
|
||||
|
||||
public static function promote(string $version): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->update('pp_update_versions',
|
||||
['channel' => 'stable', 'promoted_at' => date('Y-m-d H:i:s')],
|
||||
['version' => $version]
|
||||
);
|
||||
}
|
||||
|
||||
public static function demote(string $version): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->update('pp_update_versions',
|
||||
['channel' => 'beta', 'promoted_at' => null],
|
||||
['version' => $version]
|
||||
);
|
||||
}
|
||||
|
||||
public static function get_licenses(): array
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->select('pp_update_licenses', '*', ['ORDER' => ['domain' => 'ASC']]) ?: [];
|
||||
}
|
||||
|
||||
public static function get_license(int $id): array
|
||||
{
|
||||
global $mdb;
|
||||
return $mdb->get('pp_update_licenses', '*', ['id' => $id]) ?: [];
|
||||
}
|
||||
|
||||
public static function save_license(array $data): void
|
||||
{
|
||||
global $mdb;
|
||||
$row = [
|
||||
'key' => trim($data['key'] ?? ''),
|
||||
'domain' => trim($data['domain'] ?? ''),
|
||||
'valid_to_date' => $data['valid_to_date'] ?: null,
|
||||
'valid_to_version' => $data['valid_to_version'] ?: null,
|
||||
'beta' => (int)(bool)($data['beta'] ?? 0),
|
||||
'note' => trim($data['note'] ?? ''),
|
||||
];
|
||||
if (!empty($data['id']))
|
||||
$mdb->update('pp_update_licenses', $row, ['id' => (int)$data['id']]);
|
||||
else
|
||||
$mdb->insert('pp_update_licenses', $row);
|
||||
}
|
||||
|
||||
public static function delete_license(int $id): void
|
||||
{
|
||||
global $mdb;
|
||||
$mdb->delete('pp_update_licenses', ['id' => $id]);
|
||||
}
|
||||
|
||||
public static function toggle_beta(int $id): void
|
||||
{
|
||||
global $mdb;
|
||||
$license = $mdb->get('pp_update_licenses', ['id', 'beta'], ['id' => $id]);
|
||||
if ($license)
|
||||
$mdb->update('pp_update_licenses', ['beta' => $license['beta'] ? 0 : 1], ['id' => $id]);
|
||||
}
|
||||
|
||||
private static function zip_dir(string $version): string
|
||||
{
|
||||
return substr($version, 0, strlen($version) - (strlen($version) == 5 ? 2 : 1)) . '0';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Weryfikacja składni**
|
||||
|
||||
```bash
|
||||
php -l autoload/admin/factory/class.Releases.php
|
||||
```
|
||||
Oczekiwane: `No syntax errors detected`
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Utwórz `admin\controls\Releases`
|
||||
|
||||
**Files:**
|
||||
- Create: `autoload/admin/controls/class.Releases.php`
|
||||
|
||||
**Step 1: Napisz klasę**
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace admin\controls;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function main_view(): string
|
||||
{
|
||||
return \admin\view\Releases::main_view();
|
||||
}
|
||||
|
||||
public static function promote(): void
|
||||
{
|
||||
$version = trim(\S::get('version'));
|
||||
if ($version)
|
||||
\admin\factory\Releases::promote($version);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function demote(): void
|
||||
{
|
||||
$version = trim(\S::get('version'));
|
||||
if ($version)
|
||||
\admin\factory\Releases::demote($version);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function save_license(): void
|
||||
{
|
||||
\admin\factory\Releases::save_license($_POST);
|
||||
\S::set_message('Licencja została zapisana.');
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function delete_license(): void
|
||||
{
|
||||
$id = (int)\S::get('id');
|
||||
if ($id)
|
||||
\admin\factory\Releases::delete_license($id);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
|
||||
public static function toggle_beta(): void
|
||||
{
|
||||
$id = (int)\S::get('id');
|
||||
if ($id)
|
||||
\admin\factory\Releases::toggle_beta($id);
|
||||
header('Location: /admin/releases/main_view/');
|
||||
exit;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Weryfikacja składni**
|
||||
|
||||
```bash
|
||||
php -l autoload/admin/controls/class.Releases.php
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Utwórz `admin\view\Releases`
|
||||
|
||||
**Files:**
|
||||
- Create: `autoload/admin/view/class.Releases.php`
|
||||
|
||||
**Step 1: Napisz klasę**
|
||||
|
||||
```php
|
||||
<?php
|
||||
namespace admin\view;
|
||||
|
||||
class Releases
|
||||
{
|
||||
public static function main_view(): string
|
||||
{
|
||||
$tpl = new \Tpl;
|
||||
$tpl->versions = \admin\factory\Releases::get_versions();
|
||||
$tpl->licenses = \admin\factory\Releases::get_licenses();
|
||||
return $tpl->render('releases/main-view');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Weryfikacja składni**
|
||||
|
||||
```bash
|
||||
php -l autoload/admin/view/class.Releases.php
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Utwórz szablon `admin/templates/releases/main-view.php`
|
||||
|
||||
**Files:**
|
||||
- Create: `admin/templates/releases/main-view.php`
|
||||
|
||||
**Step 1: Utwórz katalog i plik**
|
||||
|
||||
```php
|
||||
<?php global $user; ?>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
|
||||
<!-- TABS NAV -->
|
||||
<ul class="nav nav-tabs releases-tabs" style="margin-bottom:20px">
|
||||
<li class="active"><a href="#" data-tab="versions">Wersje</a></li>
|
||||
<li><a href="#" data-tab="licenses">Licencje</a></li>
|
||||
</ul>
|
||||
|
||||
<!-- TAB: WERSJE -->
|
||||
<div id="tab-versions">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading"><h3 class="panel-title">Wersje</h3></div>
|
||||
<div class="panel-body">
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Wersja</th><th>Kanał</th><th>Dodana</th>
|
||||
<th>Promocja do stable</th><th>ZIP</th><th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<? foreach ($this->versions as $v): ?>
|
||||
<tr>
|
||||
<td><strong><?= htmlspecialchars($v['version']) ?></strong></td>
|
||||
<td>
|
||||
<? if ($v['channel'] == 'stable'): ?>
|
||||
<span class="label label-success">stable</span>
|
||||
<? else: ?>
|
||||
<span class="label label-warning">beta</span>
|
||||
<? endif; ?>
|
||||
</td>
|
||||
<td><?= $v['created_at'] ? substr($v['created_at'], 0, 10) : '-' ?></td>
|
||||
<td><?= $v['promoted_at'] ? substr($v['promoted_at'], 0, 10) : '-' ?></td>
|
||||
<td><?= $v['zip_exists'] ? '<span class="text-success">✓</span>' : '<span class="text-danger">✗</span>' ?></td>
|
||||
<td>
|
||||
<? if ($v['channel'] == 'beta'): ?>
|
||||
<a href="/admin/releases/promote/?version=<?= urlencode($v['version']) ?>"
|
||||
class="btn btn-xs btn-success"
|
||||
onclick="return confirm('Promować <?= $v['version'] ?> do stable?')">
|
||||
Promuj →stable
|
||||
</a>
|
||||
<? else: ?>
|
||||
<a href="/admin/releases/demote/?version=<?= urlencode($v['version']) ?>"
|
||||
class="btn btn-xs btn-default"
|
||||
onclick="return confirm('Cofnąć <?= $v['version'] ?> do beta?')">
|
||||
Cofnij →beta
|
||||
</a>
|
||||
<? endif; ?>
|
||||
</td>
|
||||
</tr>
|
||||
<? endforeach; ?>
|
||||
<? if (!$this->versions): ?>
|
||||
<tr><td colspan="6" class="text-center text-muted">Brak wersji w bazie. Wersje będą rejestrowane automatycznie przy pierwszym odpytaniu versions.php.</td></tr>
|
||||
<? endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /tab-versions -->
|
||||
|
||||
<!-- TAB: LICENCJE -->
|
||||
<div id="tab-licenses" style="display:none">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading" style="display:flex;justify-content:space-between;align-items:center">
|
||||
<h3 class="panel-title">Licencje</h3>
|
||||
<button class="btn btn-sm btn-system" id="btn-add-license">+ Dodaj licencję</button>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<!-- Formularz dodawania/edycji (ukryty) -->
|
||||
<div id="license-form-box" style="display:none;border:1px solid #ddd;padding:15px;margin-bottom:20px;background:#f9f9f9">
|
||||
<h4 id="license-form-title">Nowa licencja</h4>
|
||||
<form method="post" action="/admin/releases/save_license/">
|
||||
<input type="hidden" name="id" id="f-id" value="">
|
||||
<div class="row">
|
||||
<div class="form-group col-lg-3">
|
||||
<label>Klucz (hash)</label>
|
||||
<input type="text" name="key" id="f-key" class="form-control" placeholder="md5/hash lub pusty dla domyślnego">
|
||||
</div>
|
||||
<div class="form-group col-lg-3">
|
||||
<label>Domena</label>
|
||||
<input type="text" name="domain" id="f-domain" class="form-control" required>
|
||||
</div>
|
||||
<div class="form-group col-lg-2">
|
||||
<label>Ważna do daty</label>
|
||||
<input type="date" name="valid_to_date" id="f-valid-date" class="form-control">
|
||||
</div>
|
||||
<div class="form-group col-lg-2">
|
||||
<label>Ważna do wersji</label>
|
||||
<input type="text" name="valid_to_version" id="f-valid-ver" class="form-control" placeholder="np. 1.618">
|
||||
</div>
|
||||
<div class="form-group col-lg-1">
|
||||
<label>Beta</label><br>
|
||||
<input type="checkbox" name="beta" id="f-beta" value="1">
|
||||
</div>
|
||||
<div class="form-group col-lg-2">
|
||||
<label>Notatka</label>
|
||||
<input type="text" name="note" id="f-note" class="form-control">
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-system btn-sm">Zapisz</button>
|
||||
<button type="button" class="btn btn-default btn-sm" id="btn-cancel-license">Anuluj</button>
|
||||
</form>
|
||||
</div><!-- /license-form-box -->
|
||||
|
||||
<table class="table table-striped table-condensed">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Domena</th><th>Klucz</th><th>Do daty</th>
|
||||
<th>Do wersji</th><th>Beta</th><th>Notatka</th><th>Akcje</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<? foreach ($this->licenses as $lic): ?>
|
||||
<tr>
|
||||
<td><?= htmlspecialchars($lic['domain']) ?></td>
|
||||
<td><code title="<?= htmlspecialchars($lic['key']) ?>">
|
||||
<?= $lic['key'] === '' ? '<em>(domyślny)</em>' : htmlspecialchars(substr($lic['key'], 0, 8)) . '…' ?>
|
||||
</code></td>
|
||||
<td><?= $lic['valid_to_date'] ?: '<em>∞</em>' ?></td>
|
||||
<td><?= $lic['valid_to_version'] ?: '<em>∞</em>' ?></td>
|
||||
<td>
|
||||
<a href="/admin/releases/toggle_beta/?id=<?= $lic['id'] ?>"
|
||||
class="label <?= $lic['beta'] ? 'label-info' : 'label-default' ?>"
|
||||
title="Kliknij aby przełączyć">
|
||||
<?= $lic['beta'] ? 'beta' : 'stable' ?>
|
||||
</a>
|
||||
</td>
|
||||
<td><?= htmlspecialchars($lic['note'] ?? '') ?></td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-default btn-edit-license"
|
||||
data-id="<?= $lic['id'] ?>"
|
||||
data-key="<?= htmlspecialchars($lic['key']) ?>"
|
||||
data-domain="<?= htmlspecialchars($lic['domain']) ?>"
|
||||
data-valid-date="<?= $lic['valid_to_date'] ?? '' ?>"
|
||||
data-valid-ver="<?= $lic['valid_to_version'] ?? '' ?>"
|
||||
data-beta="<?= $lic['beta'] ?>"
|
||||
data-note="<?= htmlspecialchars($lic['note'] ?? '') ?>">Edytuj</button>
|
||||
<a href="/admin/releases/delete_license/?id=<?= $lic['id'] ?>"
|
||||
class="btn btn-xs btn-danger"
|
||||
onclick="return confirm('Usunąć licencję <?= htmlspecialchars($lic['domain']) ?>?')">Usuń</a>
|
||||
</td>
|
||||
</tr>
|
||||
<? endforeach; ?>
|
||||
<? if (!$this->licenses): ?>
|
||||
<tr><td colspan="7" class="text-center text-muted">Brak licencji. Dodaj pierwszą lub uruchom skrypt migracji.</td></tr>
|
||||
<? endif; ?>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div><!-- /tab-licenses -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Przełączanie tabów
|
||||
$('.releases-tabs a').on('click', function(e) {
|
||||
e.preventDefault();
|
||||
$('.releases-tabs li').removeClass('active');
|
||||
$(this).parent().addClass('active');
|
||||
$('[id^="tab-"]').hide();
|
||||
$('#tab-' + $(this).data('tab')).show();
|
||||
});
|
||||
|
||||
// Formularz: Dodaj nową licencję
|
||||
$('#btn-add-license').on('click', function() {
|
||||
$('#license-form-title').text('Nowa licencja');
|
||||
$('#f-id').val('');
|
||||
$('#f-key, #f-domain, #f-valid-date, #f-valid-ver, #f-note').val('');
|
||||
$('#f-beta').prop('checked', false);
|
||||
$('#license-form-box').slideDown();
|
||||
});
|
||||
|
||||
// Formularz: Edytuj istniejącą licencję
|
||||
$('.btn-edit-license').on('click', function() {
|
||||
var d = $(this).data();
|
||||
$('#license-form-title').text('Edytuj licencję: ' + d.domain);
|
||||
$('#f-id').val(d.id);
|
||||
$('#f-key').val(d.key);
|
||||
$('#f-domain').val(d.domain);
|
||||
$('#f-valid-date').val(d.validDate);
|
||||
$('#f-valid-ver').val(d.validVer);
|
||||
$('#f-beta').prop('checked', d.beta == 1);
|
||||
$('#f-note').val(d.note);
|
||||
$('#license-form-box').slideDown();
|
||||
$('html, body').animate({ scrollTop: $('#license-form-box').offset().top - 20 }, 300);
|
||||
});
|
||||
|
||||
// Anuluj formularz
|
||||
$('#btn-cancel-license').on('click', function() {
|
||||
$('#license-form-box').slideUp();
|
||||
});
|
||||
</script>
|
||||
```
|
||||
|
||||
**Step 2: Zweryfikuj, że szablon jest poprawny**
|
||||
|
||||
Otwórz w przeglądarce: `https://cmspro.project-dc.pl/admin/releases/main_view/`
|
||||
|
||||
Oczekiwane: strona ładuje się bez błędów PHP, widoczne dwa taby, tabela wersji pusta lub z danymi.
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Dodaj pozycję menu dla dewelopera
|
||||
|
||||
**Files:**
|
||||
- Create: `templates/additional-menu.php` (ten plik nie trafia do klientów — dodany do `.updateignore` w Task 1)
|
||||
|
||||
**Step 1: Utwórz plik**
|
||||
|
||||
```php
|
||||
<?php
|
||||
// Menu tylko na serwerze dewelopera — wykluczone z .updateignore
|
||||
?>
|
||||
<div class="title">Developer</div>
|
||||
<ul>
|
||||
<li>
|
||||
<a href="/admin/releases/main_view/">
|
||||
<img src="/admin/css/icons/settings-20-filled.svg">Releases & Licencje
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
```
|
||||
|
||||
**Step 2: Sprawdź w przeglądarce**
|
||||
|
||||
Po odświeżeniu panelu admina powinna pojawić się sekcja "Developer" z linkiem "Releases & Licencje" w menu bocznym.
|
||||
|
||||
---
|
||||
|
||||
### Task 8: Migracja danych licencji do DB
|
||||
|
||||
Licencje z hardkodowanej tablicy `$license` w `updates/versions.php` muszą trafić do `pp_update_licenses` **przed** przejściem na nową wersję `versions.php`.
|
||||
|
||||
**Files:**
|
||||
- Create: `_migrate_licenses.php` (tymczasowy, uruchom raz przez przeglądarkę, potem usuń)
|
||||
|
||||
**Step 1: Utwórz skrypt migracji**
|
||||
|
||||
```php
|
||||
<?php
|
||||
// UWAGA: Uruchom JEDEN raz przez przeglądarkę, potem natychmiast usuń!
|
||||
// URL: https://cmspro.project-dc.pl/_migrate_licenses.php
|
||||
|
||||
require_once 'config.php';
|
||||
require_once 'libraries/medoo/medoo.php';
|
||||
|
||||
$mdb = new medoo([
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $database['name'],
|
||||
'server' => $database['host'],
|
||||
'username' => $database['user'],
|
||||
'password' => $database['password'],
|
||||
'charset' => 'utf8'
|
||||
]);
|
||||
|
||||
// Wyodrębnij $license z pliku versions.php bez uruchamiania die()
|
||||
$source = file_get_contents('updates/versions.php');
|
||||
|
||||
// Wyciągnij wszystkie wpisy $license['key']['domain'] itd.
|
||||
preg_match_all('/\$license\[\'([^\']*)\'\]\[\'domain\'\]\s*=\s*\'([^\']*)\';/', $source, $m_domain);
|
||||
preg_match_all('/\$license\[\'([^\']*)\'\]\[\'valid_to_date\'\]\s*=\s*\'([^\']*)\';/', $source, $m_date);
|
||||
preg_match_all('/\$license\[\'([^\']*)\'\]\[\'valid_to_version\'\]\s*=\s*\'([^\']*)\';/', $source, $m_ver);
|
||||
|
||||
// Zbuduj mapę po kluczu
|
||||
$dates = array_combine($m_date[1], $m_date[2]);
|
||||
$vers = array_combine($m_ver[1], $m_ver[2]);
|
||||
|
||||
$count = 0;
|
||||
foreach ($m_domain[1] as $i => $key) {
|
||||
$domain = $m_domain[2][$i];
|
||||
$row = [
|
||||
'key' => $key,
|
||||
'domain' => $domain,
|
||||
'valid_to_date' => ($dates[$key] ?? '') ?: null,
|
||||
'valid_to_version' => ($vers[$key] ?? '') ?: null,
|
||||
'beta' => 0,
|
||||
];
|
||||
// Pomiń jeśli już istnieje
|
||||
if ($mdb->has('pp_update_licenses', ['key' => $key])) {
|
||||
echo "SKIP (już istnieje): $domain ($key)<br>";
|
||||
continue;
|
||||
}
|
||||
$mdb->insert('pp_update_licenses', $row);
|
||||
echo "OK: $domain ($key)<br>";
|
||||
$count++;
|
||||
}
|
||||
echo "<hr><strong>Zmigrowano $count licencji.</strong>";
|
||||
echo "<br><strong style='color:red'>USUŃ ten plik z serwera!</strong>";
|
||||
```
|
||||
|
||||
**Step 2: Wgraj na serwer i uruchom**
|
||||
|
||||
```
|
||||
https://cmspro.project-dc.pl/_migrate_licenses.php
|
||||
```
|
||||
|
||||
Oczekiwane: lista "OK: domena (klucz)" dla każdej licencji, na końcu podsumowanie.
|
||||
|
||||
**Step 3: Usuń skrypt z serwera**
|
||||
|
||||
Skasuj `_migrate_licenses.php` — nie commituj go do repozytorium.
|
||||
|
||||
**Step 4: Ustaw flagę beta dla swoich testowych stron**
|
||||
|
||||
W panelu admina `/admin/releases/main_view/` → zakładka Licencje → przy swoich testowych domenach kliknij "stable" aby przełączyć na "beta".
|
||||
|
||||
---
|
||||
|
||||
### Task 9: Przebuduj `updates/versions.php`
|
||||
|
||||
**Files:**
|
||||
- Modify: `updates/versions.php`
|
||||
|
||||
> **WAŻNE:** Wykonaj ten krok dopiero po Task 8 (migracja danych do DB).
|
||||
|
||||
**Step 1: Zastąp całą zawartość pliku**
|
||||
|
||||
```php
|
||||
<?
|
||||
require_once '../config.php';
|
||||
require_once '../libraries/medoo/medoo.php';
|
||||
|
||||
$mdb = new medoo( [
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $database['name'],
|
||||
'server' => $database['host'],
|
||||
'username' => $database['user'],
|
||||
'password' => $database['password'],
|
||||
'charset' => 'utf8'
|
||||
] );
|
||||
|
||||
$current_ver = 1691; // aktualizowane automatycznie przez build-update.ps1
|
||||
|
||||
// 1. Skan filesystem — lista istniejących ZIPów
|
||||
$versions = [];
|
||||
for ( $i = 1; $i <= $current_ver; $i++ )
|
||||
{
|
||||
$dir = substr( number_format( $i / 1000, 3 ), 0, strlen( number_format( $i / 1000, 3 ) ) - 2 ) . '0';
|
||||
$version_old = number_format( $i / 1000, 2 );
|
||||
$version_new = number_format( $i / 1000, 3 );
|
||||
|
||||
if ( file_exists( '../updates/' . $dir . '/ver_' . $version_old . '.zip' ) )
|
||||
$versions[] = $version_old;
|
||||
|
||||
if ( file_exists( '../updates/' . $dir . '/ver_' . $version_new . '.zip' ) )
|
||||
$versions[] = $version_new;
|
||||
}
|
||||
$versions = array_unique( $versions );
|
||||
|
||||
// 2. Walidacja klucza licencji
|
||||
$license = $mdb->get( 'pp_update_licenses', '*', [ 'key' => ( $_GET['key'] ?? '' ) ] );
|
||||
if ( !$license )
|
||||
die();
|
||||
|
||||
// 3. Sprawdź ważność daty
|
||||
if ( $license['valid_to_date'] && $license['valid_to_date'] < date( 'Y-m-d' ) )
|
||||
die();
|
||||
|
||||
// 4. Auto-discovery: rejestruj nowe ZIPy jako beta
|
||||
$known = array_flip( $mdb->select( 'pp_update_versions', 'version', [] ) ?: [] );
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
if ( !isset( $known[$ver] ) )
|
||||
{
|
||||
@$mdb->insert( 'pp_update_versions', [
|
||||
'version' => $ver,
|
||||
'channel' => 'beta',
|
||||
'created_at' => date( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
$known[$ver] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// 5. Filtruj wersje wg kanału (beta widzi beta+stable, reszta tylko stable)
|
||||
$channels = $license['beta'] ? [ 'beta', 'stable' ] : [ 'stable' ];
|
||||
$allowed = array_flip( $mdb->select( 'pp_update_versions', 'version', [ 'channel' => $channels ] ) ?: [] );
|
||||
|
||||
// 6. Wypisz dostępne wersje
|
||||
$valid_to_version = $license['valid_to_version'];
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
if ( !isset( $allowed[$ver] ) )
|
||||
continue;
|
||||
|
||||
if ( $valid_to_version && $ver > $valid_to_version )
|
||||
continue;
|
||||
|
||||
echo $ver . PHP_EOL;
|
||||
}
|
||||
```
|
||||
|
||||
**Step 2: Weryfikacja składni**
|
||||
|
||||
```bash
|
||||
php -l updates/versions.php
|
||||
```
|
||||
|
||||
**Step 3: Test ręczny**
|
||||
|
||||
Otwórz w przeglądarce (podaj klucz jednej z migrowanych licencji):
|
||||
```
|
||||
https://cmspro.project-dc.pl/updates/versions.php?key=TWOJ_KLUCZ
|
||||
```
|
||||
|
||||
Oczekiwane:
|
||||
- Dla klucza z `beta=0`: zwraca TYLKO wersje `channel='stable'` (pusta lista jeśli żadna jeszcze niepromowana)
|
||||
- Dla klucza z `beta=1`: zwraca wersje `channel='beta'` i `'stable'`
|
||||
- Dla nieprawidłowego klucza: brak odpowiedzi (die())
|
||||
|
||||
**Step 4: Sprawdź panel aktualizacji u klienta**
|
||||
|
||||
Na swojej testowej stronie (beta=1) otwórz `/admin/update/main_view/` — powinien widzieć dostępne wersje.
|
||||
|
||||
---
|
||||
|
||||
### Task 10: Commit końcowy
|
||||
|
||||
**Step 1: Sprawdź status**
|
||||
|
||||
```bash
|
||||
git status
|
||||
git diff updates/versions.php
|
||||
```
|
||||
|
||||
**Step 2: Commit**
|
||||
|
||||
```bash
|
||||
git add \
|
||||
.updateignore \
|
||||
autoload/admin/factory/class.Releases.php \
|
||||
autoload/admin/controls/class.Releases.php \
|
||||
autoload/admin/view/class.Releases.php \
|
||||
admin/templates/releases/main-view.php \
|
||||
templates/additional-menu.php \
|
||||
updates/versions.php
|
||||
|
||||
git commit -m "$(cat <<'EOF'
|
||||
feat: dwukanalowy system aktualizacji (beta/stable) + zarzadzanie licencjami
|
||||
|
||||
- Nowy modul admin\Releases: lista wersji z promocja beta→stable,
|
||||
CRUD licencji z flaga beta
|
||||
- versions.php czyta z DB (pp_update_licenses, pp_update_versions)
|
||||
zamiast hardkodowanej tablicy $license
|
||||
- Auto-discovery: nowe ZIPy automatycznie rejestrowane jako 'beta'
|
||||
- Calosc wykluczona z paczek klientow przez .updateignore
|
||||
|
||||
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
||||
EOF
|
||||
)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Weryfikacja end-to-end
|
||||
|
||||
Po wdrożeniu sprawdź ręcznie:
|
||||
|
||||
1. **Nowy ZIP (beta):** Wrzuć nowy ZIP na serwer → odpytaj `versions.php` kluczem beta → pojawia się nowa wersja → w panelu admina widoczna jako `beta`
|
||||
2. **Promocja:** Kliknij "Promuj →stable" → odpytaj kluczem `beta=0` → wersja pojawia się na liście
|
||||
3. **Cofnięcie:** Kliknij "Cofnij →beta" → klient `beta=0` nie widzi wersji
|
||||
4. **Licencja:** Dodaj nową licencję przez formularz → weryfikuj w phpMyAdmin
|
||||
5. **Toggle beta:** Kliknij "stable" przy licencji → zmienia się na "beta"
|
||||
6. **Zabezpieczenie:** Wgraj nową wersję CMS do klienta → sprawdź czy `admin/templates/releases/` i `autoload/admin/*/class.Releases.php` NIE znalazły się w ZIPie (build dry-run)
|
||||
14
phpunit.xml
Normal file
14
phpunit.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/10.5/phpunit.xsd" bootstrap="tests/bootstrap.php" colors="true">
|
||||
<testsuites>
|
||||
<testsuite name="Unit">
|
||||
<directory>tests/Unit</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
<source>
|
||||
<include>
|
||||
<directory>autoload/Domain</directory>
|
||||
<directory>autoload/Shared</directory>
|
||||
</include>
|
||||
</source>
|
||||
</phpunit>
|
||||
123
tests/Unit/Domain/Languages/LanguagesRepositoryTest.php
Normal file
123
tests/Unit/Domain/Languages/LanguagesRepositoryTest.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
namespace Tests\Unit\Domain\Languages;
|
||||
|
||||
use Domain\Languages\LanguagesRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class LanguagesRepositoryTest extends TestCase
|
||||
{
|
||||
private function mockDb(): object
|
||||
{
|
||||
return $this->createMock(\medoo::class);
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
\Shared\Cache\CacheHandler::reset();
|
||||
}
|
||||
|
||||
// --- languagesList ---
|
||||
|
||||
public function testLanguagesListReturnsArray(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn([['id' => 'pl', 'name' => 'Polski']]);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame([['id' => 'pl', 'name' => 'Polski']], $repo->languagesList());
|
||||
}
|
||||
|
||||
public function testLanguagesListReturnsEmptyWhenNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn(null);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame([], $repo->languagesList());
|
||||
}
|
||||
|
||||
// --- languageDetails ---
|
||||
|
||||
public function testLanguageDetailsReturnsRowWhenFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(['id' => 'pl', 'name' => 'Polski']);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame('pl', $repo->languageDetails('pl')['id']);
|
||||
}
|
||||
|
||||
public function testLanguageDetailsReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertNull($repo->languageDetails('xx'));
|
||||
}
|
||||
|
||||
// --- activeLanguages ---
|
||||
|
||||
public function testActiveLanguagesQueriesDbAndCaches(): void
|
||||
{
|
||||
$expected = [['id' => 'pl', 'name' => 'Polski', 'domain' => null]];
|
||||
$db = $this->mockDb();
|
||||
$db->expects($this->once())->method('select')->willReturn($expected);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame($expected, $repo->activeLanguages());
|
||||
// Drugi odczyt — z cache (mock select nie zostanie wywołany drugi raz)
|
||||
$this->assertSame($expected, $repo->activeLanguages());
|
||||
}
|
||||
|
||||
public function testActiveLanguagesReturnsEmptyWhenNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn(null);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame([], $repo->activeLanguages());
|
||||
}
|
||||
|
||||
// --- maxOrder ---
|
||||
|
||||
public function testMaxOrderReturnsInteger(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('max')->willReturn('5');
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame(5, $repo->maxOrder());
|
||||
}
|
||||
|
||||
// --- translationDelete ---
|
||||
|
||||
public function testTranslationDeleteReturnsTrueOnSuccess(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(1);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertTrue($repo->translationDelete(1));
|
||||
}
|
||||
|
||||
public function testTranslationDeleteReturnsFalseOnFailure(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(0);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertFalse($repo->translationDelete(1));
|
||||
}
|
||||
|
||||
// --- translationDetails ---
|
||||
|
||||
public function testTranslationDetailsReturnsRowOrNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(['id' => 1, 'text' => 'hello']);
|
||||
|
||||
$repo = new LanguagesRepository($db);
|
||||
$this->assertSame(['id' => 1, 'text' => 'hello'], $repo->translationDetails(1));
|
||||
}
|
||||
}
|
||||
101
tests/Unit/Domain/Settings/SettingsRepositoryTest.php
Normal file
101
tests/Unit/Domain/Settings/SettingsRepositoryTest.php
Normal file
@@ -0,0 +1,101 @@
|
||||
<?php
|
||||
namespace Tests\Unit\Domain\Settings;
|
||||
|
||||
use Domain\Settings\SettingsRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class SettingsRepositoryTest extends TestCase
|
||||
{
|
||||
private function mockDb(): object
|
||||
{
|
||||
return $this->createMock(\medoo::class);
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
\Shared\Cache\CacheHandler::reset();
|
||||
}
|
||||
|
||||
// --- allSettings ---
|
||||
|
||||
public function testAllSettingsReturnsMappedArray(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn([
|
||||
['param' => 'site_name', 'value' => 'Test CMS'],
|
||||
['param' => 'email', 'value' => 'admin@test.pl'],
|
||||
]);
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$result = $repo->allSettings();
|
||||
|
||||
$this->assertSame('Test CMS', $result['site_name']);
|
||||
$this->assertSame('admin@test.pl', $result['email']);
|
||||
}
|
||||
|
||||
public function testAllSettingsReturnsEmptyArrayWhenDbReturnsNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn(null);
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$this->assertSame([], $repo->allSettings());
|
||||
}
|
||||
|
||||
public function testAllSettingsUsesCache(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->expects($this->never())->method('select');
|
||||
|
||||
\Shared\Cache\CacheHandler::store('settings_details', ['cached' => '1']);
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$result = $repo->allSettings();
|
||||
|
||||
$this->assertSame('1', $result['cached']);
|
||||
}
|
||||
|
||||
// --- update ---
|
||||
|
||||
public function testUpdateCallsDbUpdateWhenParamExists(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('count')->willReturn(1);
|
||||
$db->expects($this->once())->method('update')->willReturn(true);
|
||||
$db->expects($this->never())->method('insert');
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$this->assertTrue($repo->update('site_name', 'Nowa Nazwa'));
|
||||
}
|
||||
|
||||
public function testUpdateCallsDbInsertWhenParamMissing(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('count')->willReturn(0);
|
||||
$db->expects($this->once())->method('insert')->willReturn(true);
|
||||
$db->expects($this->never())->method('update');
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$repo->update('new_param', 'value');
|
||||
}
|
||||
|
||||
// --- visitCounter ---
|
||||
|
||||
public function testVisitCounterReturnsValue(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn('1234');
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$this->assertSame('1234', $repo->visitCounter());
|
||||
}
|
||||
|
||||
public function testVisitCounterReturnsNullWhenEmpty(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new SettingsRepository($db);
|
||||
$this->assertNull($repo->visitCounter());
|
||||
}
|
||||
}
|
||||
252
tests/Unit/Domain/User/UserRepositoryTest.php
Normal file
252
tests/Unit/Domain/User/UserRepositoryTest.php
Normal file
@@ -0,0 +1,252 @@
|
||||
<?php
|
||||
namespace Tests\Unit\Domain\User;
|
||||
|
||||
use Domain\User\UserRepository;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class UserRepositoryTest extends TestCase
|
||||
{
|
||||
private function mockDb(): object
|
||||
{
|
||||
return $this->createMock(\medoo::class);
|
||||
}
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
\Shared\Cache\CacheHandler::reset();
|
||||
}
|
||||
|
||||
// --- find ---
|
||||
|
||||
public function testFindReturnsUserArray(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(['id' => 1, 'login' => 'admin']);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame('admin', $repo->find(1)['login']);
|
||||
}
|
||||
|
||||
public function testFindReturnsNullWhenNotFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertNull($repo->find(99));
|
||||
}
|
||||
|
||||
// --- findByLogin ---
|
||||
|
||||
public function testFindByLoginReturnsUser(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(['id' => 1, 'login' => 'admin']);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertNotNull($repo->findByLogin('admin'));
|
||||
}
|
||||
|
||||
// --- all ---
|
||||
|
||||
public function testAllReturnsArray(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn([['id' => 1], ['id' => 2]]);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertCount(2, $repo->all());
|
||||
}
|
||||
|
||||
public function testAllReturnsEmptyArrayWhenNull(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('select')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame([], $repo->all());
|
||||
}
|
||||
|
||||
// --- hasPrivilege ---
|
||||
|
||||
public function testHasPrivilegeReturnsTrueForAdminUser(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$repo = new UserRepository($db);
|
||||
// userId === 1 zawsze ma uprawnienia, bez zapytania do DB
|
||||
$db->expects($this->never())->method('count');
|
||||
$this->assertTrue($repo->hasPrivilege('articles', 1));
|
||||
}
|
||||
|
||||
public function testHasPrivilegeReturnsTrueWhenPrivilegeExists(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('count')->willReturn(1);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertTrue($repo->hasPrivilege('articles', 2));
|
||||
}
|
||||
|
||||
public function testHasPrivilegeReturnsFalseWhenPrivilegeMissing(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('count')->willReturn(0);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->hasPrivilege('articles', 2));
|
||||
}
|
||||
|
||||
// --- logon ---
|
||||
|
||||
public function testLogonReturnsZeroWhenUserNotFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
// Pierwsze get() (sprawdź czy login istnieje) → null
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame(0, $repo->logon('unknown', 'pass'));
|
||||
}
|
||||
|
||||
public function testLogonReturnsMinusOneWhenAccountBlocked(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
// Pierwsze get() → użytkownik istnieje, drugie → konto zablokowane (null)
|
||||
$db->method('get')->willReturnOnConsecutiveCalls(
|
||||
['id' => 2, 'login' => 'user'],
|
||||
null
|
||||
);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame(-1, $repo->logon('user', 'pass'));
|
||||
}
|
||||
|
||||
public function testLogonReturnsOneOnSuccess(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturnOnConsecutiveCalls(
|
||||
['id' => 2, 'login' => 'user'], // login istnieje
|
||||
['id' => 2, 'status' => 1, 'error_logged_count' => 0], // nie zablokowany
|
||||
['id' => 2] // hasło poprawne
|
||||
);
|
||||
$db->method('update')->willReturn(true);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertSame(1, $repo->logon('user', 'pass'));
|
||||
}
|
||||
|
||||
// --- isLoginTaken ---
|
||||
|
||||
public function testIsLoginTakenReturnsTrueWhenExists(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn('user');
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertTrue($repo->isLoginTaken('user'));
|
||||
}
|
||||
|
||||
public function testIsLoginTakenReturnsFalseWhenFree(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->isLoginTaken('newuser'));
|
||||
}
|
||||
|
||||
// --- verifyTwofaCode ---
|
||||
|
||||
public function testVerifyTwofaCodeReturnsFalseWhenUserNotFound(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('get')->willReturn(null);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->verifyTwofaCode(1, '123456'));
|
||||
}
|
||||
|
||||
public function testVerifyTwofaCodeReturnsFalseWhenTooManyFailedAttempts(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$user = [
|
||||
'id' => 2,
|
||||
'twofa_failed_attempts' => 5,
|
||||
'twofa_expires_at' => date('Y-m-d H:i:s', time() + 600),
|
||||
'twofa_code_hash' => password_hash('123456', PASSWORD_DEFAULT),
|
||||
];
|
||||
$db->method('get')->willReturn($user);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->verifyTwofaCode(2, '123456'));
|
||||
}
|
||||
|
||||
public function testVerifyTwofaCodeReturnsFalseWhenExpired(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$user = [
|
||||
'id' => 2,
|
||||
'twofa_failed_attempts' => 0,
|
||||
'twofa_expires_at' => date('Y-m-d H:i:s', time() - 1),
|
||||
'twofa_code_hash' => password_hash('123456', PASSWORD_DEFAULT),
|
||||
];
|
||||
$db->method('get')->willReturn($user);
|
||||
$db->method('update')->willReturn(true);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertFalse($repo->verifyTwofaCode(2, '123456'));
|
||||
}
|
||||
|
||||
public function testVerifyTwofaCodeReturnsTrueOnValidCode(): void
|
||||
{
|
||||
$code = '123456';
|
||||
$db = $this->mockDb();
|
||||
$user = [
|
||||
'id' => 2,
|
||||
'twofa_failed_attempts' => 0,
|
||||
'twofa_expires_at' => date('Y-m-d H:i:s', time() + 600),
|
||||
'twofa_code_hash' => password_hash($code, PASSWORD_DEFAULT),
|
||||
];
|
||||
// find() wywołuje get() dwa razy (raz przez verifyTwofaCode, raz przez update)
|
||||
$db->method('get')->willReturn($user);
|
||||
$db->method('update')->willReturn(true);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertTrue($repo->verifyTwofaCode(2, $code));
|
||||
}
|
||||
|
||||
// --- delete ---
|
||||
|
||||
public function testDeleteReturnsTrueOnSuccess(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(1);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$this->assertTrue($repo->delete(2));
|
||||
}
|
||||
|
||||
// --- save — walidacja ---
|
||||
|
||||
public function testSaveReturnsErrorWhenPasswordTooShort(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(1);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$result = $repo->save(0, 'newuser', 'on', '', '123', '123', 0, []);
|
||||
|
||||
$this->assertSame('error', $result['status']);
|
||||
}
|
||||
|
||||
public function testSaveReturnsErrorWhenPasswordsMismatch(): void
|
||||
{
|
||||
$db = $this->mockDb();
|
||||
$db->method('delete')->willReturn(1);
|
||||
|
||||
$repo = new UserRepository($db);
|
||||
$result = $repo->save(0, 'newuser', 'on', '', 'password1', 'password2', 0, []);
|
||||
|
||||
$this->assertSame('error', $result['status']);
|
||||
}
|
||||
}
|
||||
21
tests/bootstrap.php
Normal file
21
tests/bootstrap.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
// Medoo ORM
|
||||
require_once __DIR__ . '/../libraries/medoo/medoo.php';
|
||||
|
||||
// Stuby — muszą być załadowane PRZED autoloaderem PSR-4,
|
||||
// żeby nie zostały nadpisane przez prawdziwe klasy
|
||||
require_once __DIR__ . '/stubs/CacheHandler.php';
|
||||
require_once __DIR__ . '/stubs/S.php';
|
||||
|
||||
// PSR-4 autoloader dla Domain\
|
||||
// Shared\ jest obsłużona przez stub powyżej — pomijamy w autoloaderze
|
||||
spl_autoload_register(function (string $class): void {
|
||||
if (strncmp($class, 'Domain\\', 7) === 0) {
|
||||
$rel = substr($class, 7);
|
||||
$file = __DIR__ . '/../autoload/Domain/' . str_replace('\\', '/', $rel) . '.php';
|
||||
if (file_exists($file)) {
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
});
|
||||
24
tests/stubs/CacheHandler.php
Normal file
24
tests/stubs/CacheHandler.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
namespace Shared\Cache;
|
||||
|
||||
class CacheHandler
|
||||
{
|
||||
private static array $store = [];
|
||||
|
||||
public static function reset(): void { self::$store = []; }
|
||||
|
||||
public static function fetch(string $key): mixed
|
||||
{
|
||||
return self::$store[$key] ?? false;
|
||||
}
|
||||
|
||||
public static function store(string $key, mixed $value, int $ttl = 0): void
|
||||
{
|
||||
self::$store[$key] = $value;
|
||||
}
|
||||
|
||||
public static function delete(string $key): void
|
||||
{
|
||||
unset(self::$store[$key]);
|
||||
}
|
||||
}
|
||||
8
tests/stubs/S.php
Normal file
8
tests/stubs/S.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
class S
|
||||
{
|
||||
public static function delete_cache(): void {}
|
||||
public static function htacces(): void {}
|
||||
public static function get_domain(string $domain = ''): ?string { return $domain ?: null; }
|
||||
public static function send_email(string $to, string $subject, string $body): bool { return true; }
|
||||
}
|
||||
BIN
updates/1.60/ver_1.691.zip
Normal file
BIN
updates/1.60/ver_1.691.zip
Normal file
Binary file not shown.
4
updates/1.60/ver_1.691_files.txt
Normal file
4
updates/1.60/ver_1.691_files.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
F: ../backup_20250512_232458.zip
|
||||
F: ../backup_tmp.json
|
||||
F: ../sitemap_cmsenproject-dcpl.xml
|
||||
F: ../sitemap_cmsproproject-dcpl.xml
|
||||
49
updates/1.60/ver_1.691_manifest.json
Normal file
49
updates/1.60/ver_1.691_manifest.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"changelog": "Refaktoryzacja DDD Faza 0+1: PSR-4 autoloader, Shared (CacheHandler, Helpers, Html, ImageManipulator, Tpl), Domain (LanguagesRepository, SettingsRepository, UserRepository), testy jednostkowe Domain\\, docs/",
|
||||
"version": "1.691",
|
||||
"files": {
|
||||
"added": [
|
||||
"autoload/Domain/Languages/LanguagesRepository.php",
|
||||
"autoload/Domain/Settings/SettingsRepository.php",
|
||||
"autoload/Domain/User/UserRepository.php",
|
||||
"autoload/Shared/Cache/CacheHandler.php",
|
||||
"autoload/Shared/Helpers/Helpers.php",
|
||||
"autoload/Shared/Html/Html.php",
|
||||
"autoload/Shared/Image/ImageManipulator.php",
|
||||
"autoload/Shared/Tpl/Tpl.php"
|
||||
],
|
||||
"deleted": [
|
||||
"backup_20250512_232458.zip",
|
||||
"backup_tmp.json",
|
||||
"sitemap_cmsenproject-dcpl.xml",
|
||||
"sitemap_cmsproproject-dcpl.xml"
|
||||
],
|
||||
"modified": [
|
||||
"admin/ajax.php",
|
||||
"admin/index.php",
|
||||
"ajax.php",
|
||||
"api.php",
|
||||
"autoload/admin/class.Site.php",
|
||||
"autoload/admin/factory/class.Languages.php",
|
||||
"autoload/admin/factory/class.Settings.php",
|
||||
"autoload/admin/factory/class.Users.php",
|
||||
"autoload/class.Cache.php",
|
||||
"autoload/class.Html.php",
|
||||
"autoload/class.Image.php",
|
||||
"autoload/class.S.php",
|
||||
"autoload/class.Tpl.php",
|
||||
"autoload/front/factory/class.Languages.php",
|
||||
"autoload/front/factory/class.Settings.php",
|
||||
"cron.php",
|
||||
"index.php"
|
||||
]
|
||||
},
|
||||
"checksum_zip": "sha256:f53230f36d391828f4e368f3fc3420d8f9430ca507a1d4f57a0988823ac22192",
|
||||
"sql": [
|
||||
|
||||
],
|
||||
"date": "2026-02-27",
|
||||
"directories_deleted": [
|
||||
|
||||
]
|
||||
}
|
||||
@@ -1,458 +1,71 @@
|
||||
<?
|
||||
$current_ver = 1690;
|
||||
require_once '../config.php';
|
||||
require_once '../libraries/medoo/medoo.php';
|
||||
|
||||
for ($i = 1; $i <= $current_ver; $i++)
|
||||
$mdb = new medoo( [
|
||||
'database_type' => 'mysql',
|
||||
'database_name' => $database['name'],
|
||||
'server' => $database['host'],
|
||||
'username' => $database['user'],
|
||||
'password' => $database['password'],
|
||||
'charset' => 'utf8'
|
||||
] );
|
||||
|
||||
$current_ver = 1691; // aktualizowane automatycznie przez build-update.ps1
|
||||
|
||||
// 1. Skan filesystem — lista istniejących ZIPów
|
||||
$versions = [];
|
||||
for ( $i = 1; $i <= $current_ver; $i++ )
|
||||
{
|
||||
$dir = substr(number_format($i / 1000, 3), 0, strlen(number_format($i / 1000, 3)) - 2) . '0';
|
||||
$version_old = number_format($i / 1000, 2);
|
||||
$version_new = number_format($i / 1000, 3);
|
||||
$dir = substr( number_format( $i / 1000, 3 ), 0, strlen( number_format( $i / 1000, 3 ) ) - 2 ) . '0';
|
||||
$version_old = number_format( $i / 1000, 2 );
|
||||
$version_new = number_format( $i / 1000, 3 );
|
||||
|
||||
if (file_exists('../updates/' . $dir . '/ver_' . $version_old . '.zip'))
|
||||
if ( file_exists( '../updates/' . $dir . '/ver_' . $version_old . '.zip' ) )
|
||||
$versions[] = $version_old;
|
||||
|
||||
if (file_exists('../updates/' . $dir . '/ver_' . $version_new . '.zip'))
|
||||
if ( file_exists( '../updates/' . $dir . '/ver_' . $version_new . '.zip' ) )
|
||||
$versions[] = $version_new;
|
||||
}
|
||||
$versions = array_unique( $versions );
|
||||
|
||||
$license['']['domain'] = 'project-pro.pl';
|
||||
$license['']['valid_to_date'] = '';
|
||||
$license['']['valid_to_version'] = '1.519';
|
||||
|
||||
$license['cd9c8211255303fbacb4add2129caff9']['domain'] = 'template01';
|
||||
$license['cd9c8211255303fbacb4add2129caff9']['valid_to_date'] = '';
|
||||
$license['cd9c8211255303fbacb4add2129caff9']['valid_to_version'] = '';
|
||||
|
||||
$license['fc32d79ec43ac1a5bdf2de69b65773f2']['domain'] = 'szablony';
|
||||
$license['fc32d79ec43ac1a5bdf2de69b65773f2']['valid_to_date'] = '';
|
||||
$license['fc32d79ec43ac1a5bdf2de69b65773f2']['valid_to_version'] = '';
|
||||
|
||||
$license['E79DB4882828CCB767DFEA0AADF185BA']['domain'] = 'project-pro.pl';
|
||||
$license['E79DB4882828CCB767DFEA0AADF185BA']['valid_to_date'] = '';
|
||||
$license['E79DB4882828CCB767DFEA0AADF185BA']['valid_to_version'] = '';
|
||||
|
||||
$license['644DD48CEA53091AEE926A7EAE7B0653']['domain'] = 'tele-maco.com';
|
||||
$license['644DD48CEA53091AEE926A7EAE7B0653']['valid_to_date'] = '';
|
||||
$license['644DD48CEA53091AEE926A7EAE7B0653']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['D1137E156E2196846D8FB10B71EB6B49']['domain'] = 'kangoor.pl';
|
||||
$license['D1137E156E2196846D8FB10B71EB6B49']['valid_to_date'] = '';
|
||||
$license['D1137E156E2196846D8FB10B71EB6B49']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['093BB09C91A9E94D7C5336547748CDAF']['domain'] = 'projektowanieogrodow-kanczugowska.pl';
|
||||
$license['093BB09C91A9E94D7C5336547748CDAF']['valid_to_date'] = '';
|
||||
$license['093BB09C91A9E94D7C5336547748CDAF']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['DFA5463207E86DD8D525340A7B63FD35']['domain'] = 'globelus.pl';
|
||||
$license['DFA5463207E86DD8D525340A7B63FD35']['valid_to_date'] = '';
|
||||
$license['DFA5463207E86DD8D525340A7B63FD35']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['35C16BE1111EEB36355E1C5AE43F8373']['domain'] = 'domkidarbeskidu.pl';
|
||||
$license['35C16BE1111EEB36355E1C5AE43F8373']['valid_to_date'] = '';
|
||||
$license['35C16BE1111EEB36355E1C5AE43F8373']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['E7750D7395576D424ABA08C81504AF65']['domain'] = 'wesolek-ropczyce.pl';
|
||||
$license['E7750D7395576D424ABA08C81504AF65']['valid_to_date'] = '';
|
||||
$license['E7750D7395576D424ABA08C81504AF65']['valid_to_version'] = '1.548';
|
||||
|
||||
$license['410ed13e1c99b03b0ef7067a96cddd2e']['domain'] = 'nexdiag.com';
|
||||
$license['410ed13e1c99b03b0ef7067a96cddd2e']['valid_to_date'] = '';
|
||||
$license['410ed13e1c99b03b0ef7067a96cddd2e']['valid_to_version'] = '';
|
||||
|
||||
$license['7448547CE8DB5FB02594D60C828282AF']['domain'] = 'sdprog.com';
|
||||
$license['7448547CE8DB5FB02594D60C828282AF']['valid_to_date'] = '';
|
||||
$license['7448547CE8DB5FB02594D60C828282AF']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['8A75B8079231AF2C164B87A9945D9DA0']['domain'] = 'olihouse.pl';
|
||||
$license['8A75B8079231AF2C164B87A9945D9DA0']['valid_to_date'] = '';
|
||||
$license['8A75B8079231AF2C164B87A9945D9DA0']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['D8C47C064A3B117512873DE29346397D']['domain'] = 'zaufany-ksiegowy.com.pl';
|
||||
$license['D8C47C064A3B117512873DE29346397D']['valid_to_date'] = '';
|
||||
$license['D8C47C064A3B117512873DE29346397D']['valid_to_version'] = '';
|
||||
|
||||
$license['B29764D197531EF8C8D39867034433DB']['domain'] = 'bziuk.pl';
|
||||
$license['B29764D197531EF8C8D39867034433DB']['valid_to_date'] = '';
|
||||
$license['B29764D197531EF8C8D39867034433DB']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['0FD74F515F6C294BAEC97226C60FF173']['domain'] = 'feb.net.pl';
|
||||
$license['0FD74F515F6C294BAEC97226C60FF173']['valid_to_date'] = '';
|
||||
$license['0FD74F515F6C294BAEC97226C60FF173']['valid_to_version'] = '';
|
||||
|
||||
$license['9EE3E126BBC6E001F0F0184BDB36ECB3']['domain'] = 'lekkopijani.pl';
|
||||
$license['9EE3E126BBC6E001F0F0184BDB36ECB3']['valid_to_date'] = '';
|
||||
$license['9EE3E126BBC6E001F0F0184BDB36ECB3']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['B7DCF327FDA377C13E0DDC267A3CAAC2']['domain'] = 'studniazyczen.pl';
|
||||
$license['B7DCF327FDA377C13E0DDC267A3CAAC2']['valid_to_date'] = '';
|
||||
$license['B7DCF327FDA377C13E0DDC267A3CAAC2']['valid_to_version'] = '1.607';
|
||||
|
||||
$license['387DD19D13F0AB85B20B4D1F16D0C0DF']['domain'] = 'kubak.com.pl';
|
||||
$license['387DD19D13F0AB85B20B4D1F16D0C0DF']['valid_to_date'] = '';
|
||||
$license['387DD19D13F0AB85B20B4D1F16D0C0DF']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['81e85da9375bf2ad6594a5300f859c71']['domain'] = 'ctpoland.com.pl';
|
||||
$license['81e85da9375bf2ad6594a5300f859c71']['valid_to_date'] = '';
|
||||
$license['81e85da9375bf2ad6594a5300f859c71']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['677A5C20CC99261A41D30C112A0C3441']['domain'] = 'bmk-ck.pl';
|
||||
$license['677A5C20CC99261A41D30C112A0C3441']['valid_to_date'] = '';
|
||||
$license['677A5C20CC99261A41D30C112A0C3441']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['7835eb439a571df1ce63c56dd464a90a']['domain'] = 'demo.pro24.com.pl';
|
||||
$license['7835eb439a571df1ce63c56dd464a90a']['valid_to_date'] = '';
|
||||
$license['7835eb439a571df1ce63c56dd464a90a']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['4e87d09851053a1d35dfb3dd0653504d']['domain'] = 'lipinskipawel.pl';
|
||||
$license['4e87d09851053a1d35dfb3dd0653504d']['valid_to_date'] = '';
|
||||
$license['4e87d09851053a1d35dfb3dd0653504d']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['0918AB95878AD6EEE2936A77278B5C4E']['domain'] = 'iweda.pl';
|
||||
$license['0918AB95878AD6EEE2936A77278B5C4E']['valid_to_date'] = '';
|
||||
$license['0918AB95878AD6EEE2936A77278B5C4E']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['7d16b320128c1eafd43637a28272ce5a']['domain'] = 'gpb-group.pl';
|
||||
$license['7d16b320128c1eafd43637a28272ce5a']['valid_to_date'] = '';
|
||||
$license['7d16b320128c1eafd43637a28272ce5a']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['cd4aee2bce3827f446de7a60874fcc78']['domain'] = 'proxima.waw.pl';
|
||||
$license['cd4aee2bce3827f446de7a60874fcc78']['valid_to_date'] = '';
|
||||
$license['cd4aee2bce3827f446de7a60874fcc78']['valid_to_version'] = '';
|
||||
|
||||
$license['ca12bdf8718169341e2a2bd60b9b6718']['domain'] = 'kopalniarakszawa.pl';
|
||||
$license['ca12bdf8718169341e2a2bd60b9b6718']['valid_to_date'] = '';
|
||||
$license['ca12bdf8718169341e2a2bd60b9b6718']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['caeeb6ff0580a30f23dbf5d74c50021f']['domain'] = 'konfiguratorgbp.pl';
|
||||
$license['caeeb6ff0580a30f23dbf5d74c50021f']['valid_to_date'] = '';
|
||||
$license['caeeb6ff0580a30f23dbf5d74c50021f']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['f775aa50ba64a63a8e4fa6d27a75f7b5']['domain'] = 'agrofurdyna.pl';
|
||||
$license['f775aa50ba64a63a8e4fa6d27a75f7b5']['valid_to_date'] = '';
|
||||
$license['f775aa50ba64a63a8e4fa6d27a75f7b5']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['118cf28359bb9a4e9892a5fe22f934b7']['domain'] = 'ankiety.pl';
|
||||
$license['118cf28359bb9a4e9892a5fe22f934b7']['valid_to_date'] = '';
|
||||
$license['118cf28359bb9a4e9892a5fe22f934b7']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['2d9a32bb73802ebf5a5ff03cbad2fee5']['domain'] = 'dendron.net.pl';
|
||||
$license['2d9a32bb73802ebf5a5ff03cbad2fee5']['valid_to_date'] = '';
|
||||
$license['2d9a32bb73802ebf5a5ff03cbad2fee5']['valid_to_version'] = '';
|
||||
|
||||
$license['e8ddc0cc8a4292fde817506bbceea25e']['domain'] = 'orange.pl';
|
||||
$license['e8ddc0cc8a4292fde817506bbceea25e']['valid_to_date'] = '';
|
||||
$license['e8ddc0cc8a4292fde817506bbceea25e']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['e7b112bcf7da625993537913205aeb90']['domain'] = 'kobcrane-montaze.pl';
|
||||
$license['e7b112bcf7da625993537913205aeb90']['valid_to_date'] = '';
|
||||
$license['e7b112bcf7da625993537913205aeb90']['valid_to_version'] = '';
|
||||
|
||||
$license['6582e3ac1215189e745cffa6f14242b7']['domain'] = 'landeo.pl';
|
||||
$license['6582e3ac1215189e745cffa6f14242b7']['valid_to_date'] = '';
|
||||
$license['6582e3ac1215189e745cffa6f14242b7']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['8df0e9b3455b5de1d7bfbfd4924cf16a']['domain'] = '4est.pl';
|
||||
$license['8df0e9b3455b5de1d7bfbfd4924cf16a']['valid_to_date'] = '';
|
||||
$license['8df0e9b3455b5de1d7bfbfd4924cf16a']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['bfd57185b0202b23d5a2cae1ddcfbcf2']['domain'] = 'pbawers.pl';
|
||||
$license['bfd57185b0202b23d5a2cae1ddcfbcf2']['valid_to_date'] = '';
|
||||
$license['bfd57185b0202b23d5a2cae1ddcfbcf2']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['e82f945eeea9647022ab04821a8ca47d']['domain'] = 'uczciwawycena.pl';
|
||||
$license['e82f945eeea9647022ab04821a8ca47d']['valid_to_date'] = '';
|
||||
$license['e82f945eeea9647022ab04821a8ca47d']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['3936b399e94b91829e1851b2fecb5977']['domain'] = 'm3rent.pl';
|
||||
$license['3936b399e94b91829e1851b2fecb5977']['valid_to_date'] = '';
|
||||
$license['3936b399e94b91829e1851b2fecb5977']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['cbc2a0890d9974e03206e836e245c311']['domain'] = 'dhtpolska.pl';
|
||||
$license['cbc2a0890d9974e03206e836e245c311']['valid_to_date'] = '';
|
||||
$license['cbc2a0890d9974e03206e836e245c311']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['aa1177a9715b5663ba4b251082679065']['domain'] = 'recosolar.pl';
|
||||
$license['aa1177a9715b5663ba4b251082679065']['valid_to_date'] = '';
|
||||
$license['aa1177a9715b5663ba4b251082679065']['valid_to_version'] = '';
|
||||
|
||||
$license['edd8ce704a8c04940d333b0b53266610']['domain'] = 'wonderloft.pl';
|
||||
$license['edd8ce704a8c04940d333b0b53266610']['valid_to_date'] = '';
|
||||
$license['edd8ce704a8c04940d333b0b53266610']['valid_to_version'] = '1.618';
|
||||
|
||||
$license['9adf305a9439e8f9fd78f7618e652978']['domain'] = 'kancelariaskoczek.pl';
|
||||
$license['9adf305a9439e8f9fd78f7618e652978']['valid_to_date'] = '';
|
||||
$license['9adf305a9439e8f9fd78f7618e652978']['valid_to_version'] = '';
|
||||
|
||||
$license['72b050effb57632584204046b6a298de']['domain'] = 'imperialogistyka.pl';
|
||||
$license['72b050effb57632584204046b6a298de']['valid_to_date'] = '';
|
||||
$license['72b050effb57632584204046b6a298de']['valid_to_version'] = '';
|
||||
|
||||
$license['6674cbb6c4d1edc9277b3c924a9a5005']['domain'] = 'dobrodomski.pl';
|
||||
$license['6674cbb6c4d1edc9277b3c924a9a5005']['valid_to_date'] = '';
|
||||
$license['6674cbb6c4d1edc9277b3c924a9a5005']['valid_to_version'] = '';
|
||||
|
||||
$license['fcb769550dae95039c25439e42b97077']['domain'] = 'mikro-domki.pl';
|
||||
$license['fcb769550dae95039c25439e42b97077']['valid_to_date'] = '';
|
||||
$license['fcb769550dae95039c25439e42b97077']['valid_to_version'] = '';
|
||||
|
||||
$license['d15c0512b8511c5a2bad7e02d8a74110']['domain'] = 'innovationprzeworsk.net';
|
||||
$license['d15c0512b8511c5a2bad7e02d8a74110']['valid_to_date'] = '';
|
||||
$license['d15c0512b8511c5a2bad7e02d8a74110']['valid_to_version'] = '';
|
||||
|
||||
$license['9158a44e52e5b24bd84419deebee051a']['domain'] = 'mpmkotly.pl';
|
||||
$license['9158a44e52e5b24bd84419deebee051a']['valid_to_date'] = '';
|
||||
$license['9158a44e52e5b24bd84419deebee051a']['valid_to_version'] = '';
|
||||
|
||||
$license['0bbc77cf87cde41d4761d57b0c7fe923']['domain'] = 'medologic.com';
|
||||
$license['0bbc77cf87cde41d4761d57b0c7fe923']['valid_to_date'] = '';
|
||||
$license['0bbc77cf87cde41d4761d57b0c7fe923']['valid_to_version'] = '';
|
||||
|
||||
$license['e428a539d475907921cd526ae88c9f47']['domain'] = 'dzieciusiowo.pl';
|
||||
$license['e428a539d475907921cd526ae88c9f47']['valid_to_date'] = '';
|
||||
$license['e428a539d475907921cd526ae88c9f47']['valid_to_version'] = '';
|
||||
|
||||
$license['2d0479b8cdb00fdb6a58105bc55e6cfa']['domain'] = 'nataliaroch.pl';
|
||||
$license['2d0479b8cdb00fdb6a58105bc55e6cfa']['valid_to_date'] = '';
|
||||
$license['2d0479b8cdb00fdb6a58105bc55e6cfa']['valid_to_version'] = '';
|
||||
|
||||
$license['454e89c0922aa91ce79ec4c1a5ac6054']['domain'] = 'softimi.pl';
|
||||
$license['454e89c0922aa91ce79ec4c1a5ac6054']['valid_to_date'] = '';
|
||||
$license['454e89c0922aa91ce79ec4c1a5ac6054']['valid_to_version'] = '';
|
||||
|
||||
$license['8c85f7f6aa5442e5f42cef1903f25f0b']['domain'] = 'nosmoke.pl';
|
||||
$license['8c85f7f6aa5442e5f42cef1903f25f0b']['valid_to_date'] = '';
|
||||
$license['8c85f7f6aa5442e5f42cef1903f25f0b']['valid_to_version'] = '';
|
||||
|
||||
$license['e688622c70a439ab650eacd0add35032']['domain'] = 'kudlatypies.pl';
|
||||
$license['e688622c70a439ab650eacd0add35032']['valid_to_date'] = '';
|
||||
$license['e688622c70a439ab650eacd0add35032']['valid_to_version'] = '';
|
||||
|
||||
$license['99593574ef188006af7f53715431623d']['domain'] = 'magdapara.pl';
|
||||
$license['99593574ef188006af7f53715431623d']['valid_to_date'] = '';
|
||||
$license['99593574ef188006af7f53715431623d']['valid_to_version'] = '';
|
||||
|
||||
$license['07c9408bae288ce6a7bf2b4189760960']['domain'] = 'verdavivo.pl';
|
||||
$license['07c9408bae288ce6a7bf2b4189760960']['valid_to_date'] = '';
|
||||
$license['07c9408bae288ce6a7bf2b4189760960']['valid_to_version'] = '';
|
||||
|
||||
$license['1cb534040cb2649a9b15b0becfc693da']['domain'] = 'swiatpaneli.rzeszow.pl';
|
||||
$license['1cb534040cb2649a9b15b0becfc693da']['valid_to_date'] = '';
|
||||
$license['1cb534040cb2649a9b15b0becfc693da']['valid_to_version'] = '';
|
||||
|
||||
$license['2cfdb0f74dbb7748dd06a67f076a2b70']['domain'] = 'fibnet.com.pl';
|
||||
$license['2cfdb0f74dbb7748dd06a67f076a2b70']['valid_to_date'] = '';
|
||||
$license['2cfdb0f74dbb7748dd06a67f076a2b70']['valid_to_version'] = '';
|
||||
|
||||
$license['1b1a48cdfc259963b1687d1ac88195e3']['domain'] = 'safarikamper.pl';
|
||||
$license['1b1a48cdfc259963b1687d1ac88195e3']['valid_to_date'] = '';
|
||||
$license['1b1a48cdfc259963b1687d1ac88195e3']['valid_to_version'] = '';
|
||||
|
||||
$license['8e148b983ff683837783f4ea4ccf2f71']['domain'] = 'rampol.net';
|
||||
$license['8e148b983ff683837783f4ea4ccf2f71']['valid_to_date'] = '';
|
||||
$license['8e148b983ff683837783f4ea4ccf2f71']['valid_to_version'] = '';
|
||||
|
||||
$license['40976930ccc19f6c0f08c903d68d2f85']['domain'] = 'vidok.pl';
|
||||
$license['40976930ccc19f6c0f08c903d68d2f85']['valid_to_date'] = '';
|
||||
$license['40976930ccc19f6c0f08c903d68d2f85']['valid_to_version'] = '';
|
||||
|
||||
$license['7f2e9bae197a7ca68b8ffa19fe116d14']['domain'] = 'pubmed.pl';
|
||||
$license['7f2e9bae197a7ca68b8ffa19fe116d14']['valid_to_date'] = '';
|
||||
$license['7f2e9bae197a7ca68b8ffa19fe116d14']['valid_to_version'] = '';
|
||||
|
||||
$license['a31397f5c0f290533a5418b3a7cd68f7']['domain'] = 'skoczek.pro24.link';
|
||||
$license['a31397f5c0f290533a5418b3a7cd68f7']['valid_to_date'] = '';
|
||||
$license['a31397f5c0f290533a5418b3a7cd68f7']['valid_to_version'] = '';
|
||||
|
||||
$license['599191bf2871cbf4bbf663a071c33cc1']['domain'] = 'marina-pallatium.eu';
|
||||
$license['599191bf2871cbf4bbf663a071c33cc1']['valid_to_date'] = '';
|
||||
$license['599191bf2871cbf4bbf663a071c33cc1']['valid_to_version'] = '';
|
||||
|
||||
$license['07117d04a12904e25dfe0b37e52176c7']['domain'] = 'zaufane.pl';
|
||||
$license['07117d04a12904e25dfe0b37e52176c7']['valid_to_date'] = '';
|
||||
$license['07117d04a12904e25dfe0b37e52176c7']['valid_to_version'] = '';
|
||||
|
||||
$license['3038ab96e469229478bd4d4266484a59']['domain'] = 'conflo.pl';
|
||||
$license['3038ab96e469229478bd4d4266484a59']['valid_to_date'] = '';
|
||||
$license['3038ab96e469229478bd4d4266484a59']['valid_to_version'] = '';
|
||||
|
||||
$license['25ce54d28231faad3a5854df9651e61f']['domain'] = 'inwestprofil.pl';
|
||||
$license['25ce54d28231faad3a5854df9651e61f']['valid_to_date'] = '';
|
||||
$license['25ce54d28231faad3a5854df9651e61f']['valid_to_version'] = '';
|
||||
|
||||
$license['79080dc0f7b03b50d74f00ba1f87825b']['domain'] = 'grzesiek.pro24.link';
|
||||
$license['79080dc0f7b03b50d74f00ba1f87825b']['valid_to_date'] = '';
|
||||
$license['79080dc0f7b03b50d74f00ba1f87825b']['valid_to_version'] = '';
|
||||
|
||||
$license['dec5859d7bd0eeccb8cf7016da184d5a']['domain'] = 'emerch.pl';
|
||||
$license['dec5859d7bd0eeccb8cf7016da184d5a']['valid_to_date'] = '';
|
||||
$license['dec5859d7bd0eeccb8cf7016da184d5a']['valid_to_version'] = '';
|
||||
|
||||
$license['7b1782067d011cc2d991b18e26f3e82b']['domain'] = 'enology.pl';
|
||||
$license['7b1782067d011cc2d991b18e26f3e82b']['valid_to_date'] = '';
|
||||
$license['7b1782067d011cc2d991b18e26f3e82b']['valid_to_version'] = '';
|
||||
|
||||
$license['8928ad9031cef96302c5c2f2e7686ed3']['domain'] = 'choinki-lancut.eu';
|
||||
$license['8928ad9031cef96302c5c2f2e7686ed3']['valid_to_date'] = '';
|
||||
$license['8928ad9031cef96302c5c2f2e7686ed3']['valid_to_version'] = '';
|
||||
|
||||
$license['64c01e72bff323a6dc5984f57074e393']['domain'] = 'choinki-lancut.eu';
|
||||
$license['64c01e72bff323a6dc5984f57074e393']['valid_to_date'] = '';
|
||||
$license['64c01e72bff323a6dc5984f57074e393']['valid_to_version'] = '';
|
||||
|
||||
$license['5f91caf697dfbb174bb4b7e80f57578e']['domain'] = 'wobistal.pl';
|
||||
$license['5f91caf697dfbb174bb4b7e80f57578e']['valid_to_date'] = '';
|
||||
$license['5f91caf697dfbb174bb4b7e80f57578e']['valid_to_version'] = '';
|
||||
|
||||
$license['4818078c90ac4e88c687c531c730f398']['domain'] = 'monika.pro24.link';
|
||||
$license['4818078c90ac4e88c687c531c730f398']['valid_to_date'] = '';
|
||||
$license['4818078c90ac4e88c687c531c730f398']['valid_to_version'] = '';
|
||||
|
||||
$license['fd51610b1f93c8ba96cd83c42c11777a']['domain'] = 'rodzic.eu';
|
||||
$license['fd51610b1f93c8ba96cd83c42c11777a']['valid_to_date'] = '';
|
||||
$license['fd51610b1f93c8ba96cd83c42c11777a']['valid_to_version'] = '';
|
||||
|
||||
$license['c957408692c36f05e1d12f8197aa54ae']['domain'] = 'emifloor.pl';
|
||||
$license['c957408692c36f05e1d12f8197aa54ae']['valid_to_date'] = '';
|
||||
$license['c957408692c36f05e1d12f8197aa54ae']['valid_to_version'] = '';
|
||||
|
||||
$license['cacae1bd64fe16d22a17c31941ba58a2']['domain'] = 'inwestor-zastepczy.rzeszow.pl';
|
||||
$license['cacae1bd64fe16d22a17c31941ba58a2']['valid_to_date'] = '';
|
||||
$license['cacae1bd64fe16d22a17c31941ba58a2']['valid_to_version'] = '';
|
||||
|
||||
$license['e3a8caa70a968f25bf097ad8623727c1']['domain'] = 'klimawent-lancut.pl';
|
||||
$license['e3a8caa70a968f25bf097ad8623727c1']['valid_to_date'] = '';
|
||||
$license['e3a8caa70a968f25bf097ad8623727c1']['valid_to_version'] = '';
|
||||
|
||||
$license['d41d8cd98f00b204e9800998ecf8427e']['domain'] = 'kurierolkuski.pl';
|
||||
$license['d41d8cd98f00b204e9800998ecf8427e']['valid_to_date'] = '';
|
||||
$license['d41d8cd98f00b204e9800998ecf8427e']['valid_to_version'] = '';
|
||||
|
||||
$license['a6ab4706022bd3000e48ce33d25d5081']['domain'] = 'pipir.pl';
|
||||
$license['a6ab4706022bd3000e48ce33d25d5081']['valid_to_date'] = '';
|
||||
$license['a6ab4706022bd3000e48ce33d25d5081']['valid_to_version'] = '';
|
||||
|
||||
$license['ecd142a4b8aab63383d762d4929341c8']['domain'] = 'hat-bud.pl';
|
||||
$license['ecd142a4b8aab63383d762d4929341c8']['valid_to_date'] = '';
|
||||
$license['ecd142a4b8aab63383d762d4929341c8']['valid_to_version'] = '';
|
||||
|
||||
$license['6badbcb418d51fc3d3b4f3121890c862']['domain'] = 'bip.rops.rzeszow.pl';
|
||||
$license['6badbcb418d51fc3d3b4f3121890c862']['valid_to_date'] = '';
|
||||
$license['6badbcb418d51fc3d3b4f3121890c862']['valid_to_version'] = '';
|
||||
|
||||
$license['8df2b842d2421be883c17b98b3c62eb8']['domain'] = 'haft.ertim.pl';
|
||||
$license['8df2b842d2421be883c17b98b3c62eb8']['valid_to_date'] = '';
|
||||
$license['8df2b842d2421be883c17b98b3c62eb8']['valid_to_version'] = '';
|
||||
|
||||
$license['c26efc849339d628d9d8cdc33158a9af']['domain'] = 'roximplast.pl';
|
||||
$license['c26efc849339d628d9d8cdc33158a9af']['valid_to_date'] = '';
|
||||
$license['c26efc849339d628d9d8cdc33158a9af']['valid_to_version'] = '';
|
||||
|
||||
$license['cf1fd02b4a287e235bc6ab5c2891f14d']['domain'] = 'tomaszpasko.pl';
|
||||
$license['cf1fd02b4a287e235bc6ab5c2891f14d']['valid_to_date'] = '';
|
||||
$license['cf1fd02b4a287e235bc6ab5c2891f14d']['valid_to_version'] = '';
|
||||
|
||||
$license['deb9fa5ac0c1ead8ba2a4224a4e3bd8f']['domain'] = 'symbiana.com.pl';
|
||||
$license['deb9fa5ac0c1ead8ba2a4224a4e3bd8f']['valid_to_date'] = '';
|
||||
$license['deb9fa5ac0c1ead8ba2a4224a4e3bd8f']['valid_to_version'] = '';
|
||||
|
||||
$license['549895ba2d09af4b938e41de51814ebb']['domain'] = 'pianfol.pl';
|
||||
$license['549895ba2d09af4b938e41de51814ebb']['valid_to_date'] = '';
|
||||
$license['549895ba2d09af4b938e41de51814ebb']['valid_to_version'] = '';
|
||||
|
||||
$license['076f0718e06bac1bcff90867eb0accf2']['domain'] = 'zsjasionka.szkola.pl';
|
||||
$license['076f0718e06bac1bcff90867eb0accf2']['valid_to_date'] = '';
|
||||
$license['076f0718e06bac1bcff90867eb0accf2']['valid_to_version'] = '';
|
||||
|
||||
$license['c377620734fa98b2418797f06326c718']['domain'] = 'de.vidok.com';
|
||||
$license['c377620734fa98b2418797f06326c718']['valid_to_date'] = '';
|
||||
$license['c377620734fa98b2418797f06326c718']['valid_to_version'] = '';
|
||||
|
||||
$license['2a5d8f9c8cb1423be0c366433252ddd1']['domain'] = 'kingstorage.pl';
|
||||
$license['2a5d8f9c8cb1423be0c366433252ddd1']['valid_to_date'] = '';
|
||||
$license['2a5d8f9c8cb1423be0c366433252ddd1']['valid_to_version'] = '';
|
||||
|
||||
$license['6d7813ea72268dd3a062e92be33adc59']['domain'] = 'dtf.centrumdruku24.pl';
|
||||
$license['6d7813ea72268dd3a062e92be33adc59']['valid_to_date'] = '';
|
||||
$license['6d7813ea72268dd3a062e92be33adc59']['valid_to_version'] = '';
|
||||
|
||||
$license['9ee5d052380f7b78a30656948e773705']['domain'] = 'dtf.centrumdruku24.pl';
|
||||
$license['9ee5d052380f7b78a30656948e773705']['valid_to_date'] = '';
|
||||
$license['9ee5d052380f7b78a30656948e773705']['valid_to_version'] = '';
|
||||
|
||||
$license['5ef6db1855e3d585fbbbb545034bbaf3']['domain'] = 'oknoland.mielec.pl';
|
||||
$license['5ef6db1855e3d585fbbbb545034bbaf3']['valid_to_date'] = '';
|
||||
$license['5ef6db1855e3d585fbbbb545034bbaf3']['valid_to_version'] = '';
|
||||
|
||||
$license['7fb7c4f62c94d24ff75f7931540ec75a']['domain'] = 'oknoland.mielec.pl';
|
||||
$license['7fb7c4f62c94d24ff75f7931540ec75a']['valid_to_date'] = '';
|
||||
$license['7fb7c4f62c94d24ff75f7931540ec75a']['valid_to_version'] = '';
|
||||
|
||||
$license['e2b0df429491afda870f30d4bdb313a0']['domain'] = 'przedszkole.pro24.com.pl';
|
||||
$license['e2b0df429491afda870f30d4bdb313a0']['valid_to_date'] = '';
|
||||
$license['e2b0df429491afda870f30d4bdb313a0']['valid_to_version'] = '';
|
||||
|
||||
$license['fb50f1b15be6a8af1178a2aab9a4dbdf']['domain'] = 'chmielowiec.eu';
|
||||
$license['fb50f1b15be6a8af1178a2aab9a4dbdf']['valid_to_date'] = '';
|
||||
$license['fb50f1b15be6a8af1178a2aab9a4dbdf']['valid_to_version'] = '';
|
||||
|
||||
$license['333f4c17de255dfec941fcdc60ea273a']['domain'] = 'kampery.brzezovka.pl';
|
||||
$license['333f4c17de255dfec941fcdc60ea273a']['valid_to_date'] = '';
|
||||
$license['333f4c17de255dfec941fcdc60ea273a']['valid_to_version'] = '';
|
||||
|
||||
$license['2f564150024940551b31a19b32ae9734']['domain'] = 'mogegarden.pl';
|
||||
$license['2f564150024940551b31a19b32ae9734']['valid_to_date'] = '';
|
||||
$license['2f564150024940551b31a19b32ae9734']['valid_to_version'] = '';
|
||||
|
||||
$license['78c454de6a7c40c55fbcd21440ef75a6']['domain'] = 'mogegarden.pl';
|
||||
$license['78c454de6a7c40c55fbcd21440ef75a6']['valid_to_date'] = '';
|
||||
$license['78c454de6a7c40c55fbcd21440ef75a6']['valid_to_version'] = '';
|
||||
|
||||
$license['2b353dfc72ad989727296019078a5939']['domain'] = 'manver.pl';
|
||||
$license['2b353dfc72ad989727296019078a5939']['valid_to_date'] = '';
|
||||
$license['2b353dfc72ad989727296019078a5939']['valid_to_version'] = '';
|
||||
|
||||
$license['7965db1a2cc68663077aa335aaf00d6e']['domain'] = 'dpsbabica.pl';
|
||||
$license['7965db1a2cc68663077aa335aaf00d6e']['valid_to_date'] = '';
|
||||
$license['7965db1a2cc68663077aa335aaf00d6e']['valid_to_version'] = '';
|
||||
|
||||
$license['1ac91c8663f2e371b9d3acbaeb45f184']['domain'] = 'eng.vidok.com';
|
||||
$license['1ac91c8663f2e371b9d3acbaeb45f184']['valid_to_date'] = '';
|
||||
$license['1ac91c8663f2e371b9d3acbaeb45f184']['valid_to_version'] = '';
|
||||
|
||||
$license['32967a10e80e258873376c9650206ff1']['domain'] = 'ertim.pl';
|
||||
$license['32967a10e80e258873376c9650206ff1']['valid_to_date'] = '';
|
||||
$license['32967a10e80e258873376c9650206ff1']['valid_to_version'] = '';
|
||||
|
||||
$license['797d5ede8228fff47151ec2555a71b29']['domain'] = 'patekaparments.pl';
|
||||
$license['797d5ede8228fff47151ec2555a71b29']['valid_to_date'] = '';
|
||||
$license['797d5ede8228fff47151ec2555a71b29']['valid_to_version'] = '';
|
||||
|
||||
$license['32764e943176bdd2563547046e2745cf']['domain'] = 'se.min-pan.krakow.pl';
|
||||
$license['32764e943176bdd2563547046e2745cf']['valid_to_date'] = '';
|
||||
$license['32764e943176bdd2563547046e2745cf']['valid_to_version'] = '';
|
||||
|
||||
$license['1802633572f03a814b86189b711d6979']['domain'] = 'kontrans.pl';
|
||||
$license['1802633572f03a814b86189b711d6979']['valid_to_date'] = '';
|
||||
$license['1802633572f03a814b86189b711d6979']['valid_to_version'] = '';
|
||||
|
||||
$update_key = $_GET['key'];
|
||||
if (!isset($license[$_GET['key']]))
|
||||
// 2. Walidacja klucza licencji
|
||||
$license = $mdb->get( 'pp_update_licenses', '*', [ 'key' => ( $_GET['key'] ?? '' ) ] );
|
||||
if ( !$license )
|
||||
die();
|
||||
|
||||
$valid_to_date = $license[$_GET['key']]['valid_to'];
|
||||
if ($valid_to && $valid_to < date('Y-m-d'))
|
||||
// 3. Sprawdź ważność daty
|
||||
if ( $license['valid_to_date'] && $license['valid_to_date'] < date( 'Y-m-d' ) )
|
||||
die();
|
||||
|
||||
$versions = array_unique($versions);
|
||||
|
||||
$valid_to_version = $license[$_GET['key']]['valid_to_version'];
|
||||
if ($valid_to_version)
|
||||
// 4. Auto-discovery: rejestruj nowe ZIPy jako beta
|
||||
$known = array_flip( $mdb->select( 'pp_update_versions', 'version', [] ) ?: [] );
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
foreach ($versions as $ver)
|
||||
if ($ver <= $valid_to_version)
|
||||
echo $ver . PHP_EOL;
|
||||
if ( !isset( $known[$ver] ) )
|
||||
{
|
||||
@$mdb->insert( 'pp_update_versions', [
|
||||
'version' => $ver,
|
||||
'channel' => 'beta',
|
||||
'created_at' => date( 'Y-m-d H:i:s' )
|
||||
] );
|
||||
$known[$ver] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
// 5. Filtruj wersje wg kanału (beta widzi beta+stable, reszta tylko stable)
|
||||
$channels = $license['beta'] ? [ 'beta', 'stable' ] : [ 'stable' ];
|
||||
$allowed = array_flip( $mdb->select( 'pp_update_versions', 'version', [ 'channel' => $channels ] ) ?: [] );
|
||||
|
||||
// 6. Wypisz dostępne wersje
|
||||
$valid_to_version = $license['valid_to_version'];
|
||||
foreach ( $versions as $ver )
|
||||
{
|
||||
foreach ($versions as $ver)
|
||||
echo $ver . PHP_EOL;
|
||||
}
|
||||
if ( !isset( $allowed[$ver] ) )
|
||||
continue;
|
||||
|
||||
if ( $valid_to_version && $ver > $valid_to_version )
|
||||
continue;
|
||||
|
||||
echo $ver . PHP_EOL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user