docs: plan implementacji modulu Releases (dwukanalowy system aktualizacji)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
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)
|
||||
Reference in New Issue
Block a user