- Create SQL migration for prompt templates used in article and image generation. - Add migration to change publish interval from days to hours in the sites table. - Implement InstallerController to handle installation requests and validation. - Develop FtpService for FTP connections and file uploads. - Create InstallerService to manage the WordPress installation process, including downloading, extracting, and configuring WordPress. - Add index view for the installer with form inputs for FTP, database, and WordPress admin settings. - Implement progress tracking for the installation process with AJAX polling.
293 lines
18 KiB
PHP
293 lines
18 KiB
PHP
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h2>Edytuj stronę: <?= htmlspecialchars($site['name']) ?></h2>
|
|
<div class="d-flex gap-2">
|
|
<form method="post" action="/publish/site/<?= $site['id'] ?>" class="d-inline">
|
|
<button type="submit" class="btn btn-success" onclick="return confirm('Opublikować artykuł na tej stronie teraz?')">
|
|
<i class="bi bi-play-circle me-1"></i>Publikuj teraz
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="row g-4">
|
|
<div class="col-lg-7">
|
|
<div class="card">
|
|
<div class="card-header"><h5 class="mb-0">Ustawienia strony</h5></div>
|
|
<div class="card-body">
|
|
<form method="post" action="/sites/<?= $site['id'] ?>">
|
|
<div class="mb-3">
|
|
<label for="name" class="form-label">Nazwa strony</label>
|
|
<input type="text" class="form-control" id="name" name="name" value="<?= htmlspecialchars($site['name']) ?>" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="url" class="form-label">URL WordPressa</label>
|
|
<input type="url" class="form-control" id="url" name="url" value="<?= htmlspecialchars($site['url']) ?>" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="api_user" class="form-label">Użytkownik API</label>
|
|
<input type="text" class="form-control" id="api_user" name="api_user" value="<?= htmlspecialchars($site['api_user']) ?>" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="api_token" class="form-label">Application Password</label>
|
|
<input type="text" class="form-control" id="api_token" name="api_token" value="<?= htmlspecialchars($site['api_token']) ?>" required>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<label for="publish_interval_hours" class="form-label">Interwał publikacji (godziny)</label>
|
|
<input type="number" class="form-control" id="publish_interval_hours" name="publish_interval_hours" value="<?= $site['publish_interval_hours'] ?>" min="1" max="720">
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input type="checkbox" class="form-check-input" id="is_multisite" name="is_multisite" value="1" <?= $site['is_multisite'] ? 'checked' : '' ?>>
|
|
<label class="form-check-label" for="is_multisite">Strona wielotematyczna</label>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="mb-3">
|
|
<div class="form-check">
|
|
<input type="checkbox" class="form-check-input" id="is_active" name="is_active" value="1" <?= $site['is_active'] ? 'checked' : '' ?>>
|
|
<label class="form-check-label" for="is_active">Aktywna</label>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Credentials accordion -->
|
|
<?php $hasCredentials = !empty($site['ftp_host']) || !empty($site['db_host']) || !empty($site['wp_admin_user']); ?>
|
|
<div class="accordion mb-3" id="credentialsAccordion">
|
|
<div class="accordion-item border-0">
|
|
<h2 class="accordion-header">
|
|
<button class="accordion-button collapsed bg-light px-0" type="button" data-bs-toggle="collapse" data-bs-target="#credentialsPanel">
|
|
<i class="bi bi-key me-2"></i>Dane dostępowe (FTP, baza danych, panel WP)
|
|
<?php if ($hasCredentials): ?>
|
|
<span class="badge bg-success ms-2">zapisane</span>
|
|
<?php else: ?>
|
|
<span class="badge bg-secondary ms-2">brak</span>
|
|
<?php endif; ?>
|
|
</button>
|
|
</h2>
|
|
<div id="credentialsPanel" class="accordion-collapse collapse">
|
|
<div class="accordion-body px-0 pb-0">
|
|
|
|
<!-- FTP -->
|
|
<fieldset class="border rounded p-3 mb-3">
|
|
<legend class="float-none w-auto px-2 fs-6 fw-bold mb-0">
|
|
<i class="bi bi-hdd-network me-1"></i>FTP
|
|
</legend>
|
|
<div class="row mb-2">
|
|
<div class="col-md-8">
|
|
<label for="ftp_host" class="form-label small">Host</label>
|
|
<input type="text" class="form-control form-control-sm" id="ftp_host" name="ftp_host" value="<?= htmlspecialchars($site['ftp_host'] ?? '') ?>" placeholder="ftp.example.com">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="ftp_port" class="form-label small">Port</label>
|
|
<input type="number" class="form-control form-control-sm" id="ftp_port" name="ftp_port" value="<?= htmlspecialchars($site['ftp_port'] ?? '21') ?>">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-2">
|
|
<div class="col-md-6">
|
|
<label for="ftp_user" class="form-label small">Użytkownik</label>
|
|
<input type="text" class="form-control form-control-sm" id="ftp_user" name="ftp_user" value="<?= htmlspecialchars($site['ftp_user'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="ftp_pass" class="form-label small">Hasło</label>
|
|
<input type="password" class="form-control form-control-sm" id="ftp_pass" name="ftp_pass" value="<?= htmlspecialchars($site['ftp_pass'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="ftp_path" class="form-label small">Ścieżka</label>
|
|
<input type="text" class="form-control form-control-sm" id="ftp_path" name="ftp_path" value="<?= htmlspecialchars($site['ftp_path'] ?? '') ?>" placeholder="/public_html">
|
|
</div>
|
|
</fieldset>
|
|
|
|
<!-- Database -->
|
|
<fieldset class="border rounded p-3 mb-3">
|
|
<legend class="float-none w-auto px-2 fs-6 fw-bold mb-0">
|
|
<i class="bi bi-database me-1"></i>Baza danych
|
|
</legend>
|
|
<div class="row mb-2">
|
|
<div class="col-md-6">
|
|
<label for="db_host" class="form-label small">Host</label>
|
|
<input type="text" class="form-control form-control-sm" id="db_host" name="db_host" value="<?= htmlspecialchars($site['db_host'] ?? '') ?>" placeholder="localhost">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="db_name" class="form-label small">Nazwa bazy</label>
|
|
<input type="text" class="form-control form-control-sm" id="db_name" name="db_name" value="<?= htmlspecialchars($site['db_name'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
<div class="row mb-2">
|
|
<div class="col-md-4">
|
|
<label for="db_user" class="form-label small">Użytkownik</label>
|
|
<input type="text" class="form-control form-control-sm" id="db_user" name="db_user" value="<?= htmlspecialchars($site['db_user'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="db_pass" class="form-label small">Hasło</label>
|
|
<input type="password" class="form-control form-control-sm" id="db_pass" name="db_pass" value="<?= htmlspecialchars($site['db_pass'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-4">
|
|
<label for="db_prefix" class="form-label small">Prefix</label>
|
|
<input type="text" class="form-control form-control-sm" id="db_prefix" name="db_prefix" value="<?= htmlspecialchars($site['db_prefix'] ?? 'wp_') ?>">
|
|
</div>
|
|
</div>
|
|
</fieldset>
|
|
|
|
<!-- WP Admin -->
|
|
<fieldset class="border rounded p-3">
|
|
<legend class="float-none w-auto px-2 fs-6 fw-bold mb-0">
|
|
<i class="bi bi-wordpress me-1"></i>Panel administratora
|
|
</legend>
|
|
<div class="row mb-2">
|
|
<div class="col-md-6">
|
|
<label for="wp_admin_user" class="form-label small">Login</label>
|
|
<input type="text" class="form-control form-control-sm" id="wp_admin_user" name="wp_admin_user" value="<?= htmlspecialchars($site['wp_admin_user'] ?? '') ?>">
|
|
</div>
|
|
<div class="col-md-6">
|
|
<label for="wp_admin_pass" class="form-label small">Hasło</label>
|
|
<input type="password" class="form-control form-control-sm" id="wp_admin_pass" name="wp_admin_pass" value="<?= htmlspecialchars($site['wp_admin_pass'] ?? '') ?>">
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<label for="wp_admin_email" class="form-label small">E-mail</label>
|
|
<input type="email" class="form-control form-control-sm" id="wp_admin_email" name="wp_admin_email" value="<?= htmlspecialchars($site['wp_admin_email'] ?? '') ?>">
|
|
</div>
|
|
</fieldset>
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="d-flex gap-2">
|
|
<button type="submit" class="btn btn-primary">Zapisz zmiany</button>
|
|
<a href="/sites" class="btn btn-outline-secondary">Anuluj</a>
|
|
<button class="btn btn-outline-success btn-test-connection ms-auto" data-site-id="<?= $site['id'] ?>" type="button">
|
|
<i class="bi bi-wifi me-1"></i>Test
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="col-lg-5">
|
|
<!-- Assigned topics -->
|
|
<div class="card mb-4">
|
|
<div class="card-header d-flex justify-content-between align-items-center">
|
|
<h5 class="mb-0">Tematy (<?= count($topics) ?>)</h5>
|
|
<a href="/sites/<?= $site['id'] ?>/topics" class="btn btn-sm btn-outline-info">
|
|
<i class="bi bi-pencil me-1"></i>Zarządzaj
|
|
</a>
|
|
</div>
|
|
<div class="card-body p-0">
|
|
<?php if (empty($topics)): ?>
|
|
<div class="p-3 text-muted text-center">Brak tematów. Dodaj z biblioteki poniżej.</div>
|
|
<?php else: ?>
|
|
<table class="table table-hover table-sm mb-0">
|
|
<thead>
|
|
<tr>
|
|
<th>Temat</th>
|
|
<th>Kategoria</th>
|
|
<th>Art.</th>
|
|
<th></th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<?php foreach ($topics as $topic): ?>
|
|
<tr>
|
|
<td>
|
|
<?= htmlspecialchars($topic['name']) ?>
|
|
<?php if (!$topic['is_active']): ?>
|
|
<span class="badge bg-secondary">off</span>
|
|
<?php endif; ?>
|
|
</td>
|
|
<td class="small text-muted"><?= htmlspecialchars($topic['global_category_name'] ?? '') ?></td>
|
|
<td><span class="badge bg-primary"><?= $topic['article_count'] ?></span></td>
|
|
<td>
|
|
<button class="btn btn-sm btn-outline-danger py-0 px-1 btn-delete-topic" data-id="<?= $topic['id'] ?>" data-name="<?= htmlspecialchars($topic['name']) ?>">
|
|
<i class="bi bi-x"></i>
|
|
</button>
|
|
</td>
|
|
</tr>
|
|
<?php endforeach; ?>
|
|
</tbody>
|
|
</table>
|
|
<?php endif; ?>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Quick add from library -->
|
|
<div class="card">
|
|
<div class="card-header"><h5 class="mb-0">Dodaj temat z biblioteki</h5></div>
|
|
<div class="card-body">
|
|
<form method="post" action="/sites/<?= $site['id'] ?>/topics">
|
|
<div class="mb-3">
|
|
<select class="form-select" name="global_topic_id" id="quick_global_topic" onchange="quickFillTopic(this)" required>
|
|
<option value="">Wybierz temat...</option>
|
|
<?php foreach ($globalTopics as $cat): ?>
|
|
<optgroup label="<?= htmlspecialchars($cat['name']) ?>">
|
|
<?php foreach ($cat['children'] as $child): ?>
|
|
<?php $alreadyAssigned = in_array($child['id'], $assignedGlobalIds); ?>
|
|
<option value="<?= $child['id'] ?>"
|
|
data-name="<?= htmlspecialchars($child['name']) ?>"
|
|
data-desc="<?= htmlspecialchars($child['description'] ?? '') ?>"
|
|
<?= $alreadyAssigned ? 'disabled' : '' ?>>
|
|
<?= htmlspecialchars($child['name']) ?><?= $alreadyAssigned ? ' (dodany)' : '' ?>
|
|
</option>
|
|
<?php endforeach; ?>
|
|
</optgroup>
|
|
<?php endforeach; ?>
|
|
</select>
|
|
</div>
|
|
<input type="hidden" name="name" id="quick_topic_name">
|
|
<input type="hidden" name="description" id="quick_topic_desc">
|
|
<input type="hidden" name="is_active" value="1">
|
|
<button type="submit" class="btn btn-success btn-sm w-100">
|
|
<i class="bi bi-plus-lg me-1"></i>Dodaj
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
function quickFillTopic(select) {
|
|
var opt = select.options[select.selectedIndex];
|
|
document.getElementById('quick_topic_name').value = opt.dataset.name || '';
|
|
document.getElementById('quick_topic_desc').value = opt.dataset.desc || '';
|
|
}
|
|
|
|
document.querySelectorAll('.btn-delete-topic').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
var id = this.dataset.id;
|
|
var name = this.dataset.name;
|
|
if (!confirm('Usunąć temat "' + name + '"?')) return;
|
|
|
|
var row = this.closest('tr');
|
|
btn.disabled = true;
|
|
|
|
fetch('/topics/' + id + '/delete', {
|
|
method: 'POST',
|
|
headers: { 'X-Requested-With': 'XMLHttpRequest' }
|
|
})
|
|
.then(function(r) { return r.json(); })
|
|
.then(function(data) {
|
|
if (data.success) {
|
|
row.style.transition = 'opacity .3s';
|
|
row.style.opacity = '0';
|
|
setTimeout(function() { row.remove(); }, 300);
|
|
} else {
|
|
alert(data.message || 'Błąd usuwania');
|
|
btn.disabled = false;
|
|
}
|
|
})
|
|
.catch(function() {
|
|
alert('Błąd połączenia');
|
|
btn.disabled = false;
|
|
});
|
|
});
|
|
});
|
|
</script>
|