feat: SQLite DB with migrations replaces all JSON file storage
- Add db.php with PDO singleton, migration runner, and all helper functions - Add migrations/001_initial_schema.sql (full schema) - Add migrations/002_import_json.php (one-time JSON → DB importer) - Add _incl/switch_tenant.php POST endpoint for tenant/centro switching - Update tools.auth.php: DB-backed login, cookie auth, session reload, init_active_centro() - Update all sysadmin pages (users, centros, aularios, invitations, reset_password) to use DB - Update aulatek/index.php, aulario.php, supercafe.php, supercafe_edit.php to use DB - Update aulatek/comedor.php and api/comedor.php to use DB - Update aulatek/paneldiario.php: aulario config + comedor data from DB - Update aulatek/proyectos.php: aulario config + sharing metadata from DB - Update club/cal.php, index.php, edit_data.php, upload/upload.php to use DB - Update account/index.php: rich profile, tenant list, aula list, session info, permissions - Update pre-body.php account dropdown: shows active org + inline tenant switcher - Update DATA_STRUCTURE.md to document DB approach and migration system Co-authored-by: naielv <109038805+naielv@users.noreply.github.com>
This commit is contained in:
@@ -1,4 +1,65 @@
|
|||||||
# Example Data Structure for Axia4
|
# Data Architecture for Axia4
|
||||||
|
|
||||||
|
Axia4 uses a **SQLite database** (`/DATA/axia4.sqlite`) for all structured data, with the
|
||||||
|
filesystem reserved for binary assets (photos, uploaded files, project documents).
|
||||||
|
|
||||||
|
## Database (`/DATA/axia4.sqlite`)
|
||||||
|
|
||||||
|
The schema is defined in `public_html/_incl/migrations/001_initial_schema.sql` and
|
||||||
|
applied automatically on first boot via `db.php`.
|
||||||
|
|
||||||
|
| Table | Replaces |
|
||||||
|
|----------------------|---------------------------------------------------|
|
||||||
|
| `config` | `/DATA/AuthConfig.json` |
|
||||||
|
| `users` | `/DATA/Usuarios/*.json` |
|
||||||
|
| `invitations` | `/DATA/Invitaciones_de_usuarios.json` |
|
||||||
|
| `centros` | Directory existence at `.../Centros/{id}/` |
|
||||||
|
| `user_centros` | `entreaulas.centro` + `entreaulas.aulas` in users |
|
||||||
|
| `aularios` | `.../Aularios/{id}.json` |
|
||||||
|
| `supercafe_menu` | `.../SuperCafe/Menu.json` |
|
||||||
|
| `supercafe_orders` | `.../SuperCafe/Comandas/*.json` |
|
||||||
|
| `comedor_menu_types` | `.../Comedor-MenuTypes.json` |
|
||||||
|
| `comedor_entries` | `.../Comedor/{ym}/{day}/_datos.json` |
|
||||||
|
| `club_events` | `/DATA/club/IMG/{date}/data.json` |
|
||||||
|
| `club_config` | `/DATA/club/config.json` |
|
||||||
|
|
||||||
|
## Migrations
|
||||||
|
|
||||||
|
Migrations live in `public_html/_incl/migrations/`:
|
||||||
|
|
||||||
|
- `001_initial_schema.sql` — DDL for all tables.
|
||||||
|
- `002_import_json.php` — One-time importer: reads existing JSON files and
|
||||||
|
inserts them into the database. Run automatically on first boot if JSON files
|
||||||
|
exist and the DB is empty.
|
||||||
|
|
||||||
|
## Filesystem (binary / large assets)
|
||||||
|
|
||||||
|
```
|
||||||
|
DATA/
|
||||||
|
└── entreaulas/
|
||||||
|
└── Centros/
|
||||||
|
└── {centro_id}/
|
||||||
|
├── Aularios/
|
||||||
|
│ └── {aulario_id}/
|
||||||
|
│ ├── Alumnos/ # Student photo directories
|
||||||
|
│ │ └── {alumno}/photo.jpg
|
||||||
|
│ ├── Comedor/{ym}/{day}/ # Comedor pictogram images
|
||||||
|
│ └── Proyectos/ # Project binary files
|
||||||
|
└── Panel/
|
||||||
|
└── Actividades/{name}/photo.jpg # Activity photos
|
||||||
|
└── club/
|
||||||
|
└── IMG/{date}/ # Club event photos (still on filesystem)
|
||||||
|
```
|
||||||
|
|
||||||
|
## Multi-Tenant Support
|
||||||
|
|
||||||
|
A user can belong to **multiple centros** (organizations). The active centro
|
||||||
|
is stored in `$_SESSION['active_centro']` and can be switched at any time via
|
||||||
|
`POST /_incl/switch_tenant.php`.
|
||||||
|
|
||||||
|
The account page (`/account/`) shows all assigned organizations and lets the
|
||||||
|
user switch between them.
|
||||||
|
|
||||||
|
|
||||||
This directory contains example data files that demonstrate the structure needed for the Axia4 application.
|
This directory contains example data files that demonstrate the structure needed for the Axia4 application.
|
||||||
|
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ FROM dunglas/frankenphp
|
|||||||
# && rm -rf /var/lib/apt/lists/*
|
# && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Configure PHP extensions
|
# Configure PHP extensions
|
||||||
RUN install-php-extensions gd opcache
|
RUN install-php-extensions gd opcache pdo pdo_sqlite
|
||||||
|
|
||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ FROM dunglas/frankenphp
|
|||||||
# && rm -rf /var/lib/apt/lists/*
|
# && rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
# Configure PHP extensions
|
# Configure PHP extensions
|
||||||
RUN install-php-extensions gd opcache
|
RUN install-php-extensions gd opcache pdo pdo_sqlite
|
||||||
|
|
||||||
# Set working directory
|
# Set working directory
|
||||||
WORKDIR /var/www/html
|
WORKDIR /var/www/html
|
||||||
|
|||||||
589
public_html/_incl/db.php
Normal file
589
public_html/_incl/db.php
Normal file
@@ -0,0 +1,589 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Axia4 Database Layer
|
||||||
|
*
|
||||||
|
* Provides a PDO SQLite connection and a lightweight migration runner.
|
||||||
|
* All application data previously stored as JSON files under /DATA is now
|
||||||
|
* persisted in /DATA/axia4.db.
|
||||||
|
*
|
||||||
|
* Usage: db() → returns the shared PDO instance (auto-migrates on first call).
|
||||||
|
*/
|
||||||
|
|
||||||
|
define('DB_PATH', '/DATA/axia4.db');
|
||||||
|
define('MIGRATIONS_DIR', __DIR__ . '/migrations');
|
||||||
|
|
||||||
|
// ── Connection ────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db(): PDO
|
||||||
|
{
|
||||||
|
static $pdo = null;
|
||||||
|
if ($pdo !== null) {
|
||||||
|
return $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_dir('/DATA')) {
|
||||||
|
mkdir('/DATA', 0755, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo = new PDO('sqlite:' . DB_PATH);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
|
||||||
|
$pdo->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
|
||||||
|
$pdo->exec('PRAGMA journal_mode = WAL');
|
||||||
|
$pdo->exec('PRAGMA foreign_keys = ON');
|
||||||
|
$pdo->exec('PRAGMA synchronous = NORMAL');
|
||||||
|
|
||||||
|
db_migrate($pdo);
|
||||||
|
|
||||||
|
return $pdo;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Migration runner ──────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_migrate(PDO $pdo): void
|
||||||
|
{
|
||||||
|
$pdo->exec(
|
||||||
|
'CREATE TABLE IF NOT EXISTS schema_migrations (
|
||||||
|
version INTEGER PRIMARY KEY,
|
||||||
|
applied_at TEXT NOT NULL DEFAULT (datetime(\'now\'))
|
||||||
|
)'
|
||||||
|
);
|
||||||
|
|
||||||
|
$applied = $pdo->query('SELECT version FROM schema_migrations ORDER BY version')
|
||||||
|
->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
|
||||||
|
$files = glob(MIGRATIONS_DIR . '/*.{sql,php}', GLOB_BRACE) ?: [];
|
||||||
|
sort($files);
|
||||||
|
|
||||||
|
foreach ($files as $file) {
|
||||||
|
if (!preg_match('/^(\d+)/', basename($file), $m)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$version = (int) $m[1];
|
||||||
|
if (in_array($version, $applied, true)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (str_ends_with($file, '.sql')) {
|
||||||
|
$pdo->exec((string) file_get_contents($file));
|
||||||
|
} elseif (str_ends_with($file, '.php')) {
|
||||||
|
// PHP migration receives the connection as $db
|
||||||
|
$db = $pdo;
|
||||||
|
require $file;
|
||||||
|
}
|
||||||
|
|
||||||
|
$pdo->prepare('INSERT INTO schema_migrations (version) VALUES (?)')->execute([$version]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Config helpers ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_get_config(string $key, $default = null)
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare('SELECT value FROM config WHERE key = ?');
|
||||||
|
$stmt->execute([$key]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
$decoded = json_decode($row['value'], true);
|
||||||
|
return $decoded !== null ? $decoded : $row['value'];
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_set_config(string $key, $value): void
|
||||||
|
{
|
||||||
|
db()->prepare('INSERT OR REPLACE INTO config (key, value) VALUES (?, ?)')
|
||||||
|
->execute([$key, is_string($value) ? $value : json_encode($value)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_get_all_config(): array
|
||||||
|
{
|
||||||
|
$rows = db()->query('SELECT key, value FROM config')->fetchAll();
|
||||||
|
$result = [];
|
||||||
|
foreach ($rows as $row) {
|
||||||
|
$decoded = json_decode($row['value'], true);
|
||||||
|
$result[$row['key']] = ($decoded !== null) ? $decoded : $row['value'];
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── User helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Find a user by username (always lower-cased). Returns DB row or null. */
|
||||||
|
function db_get_user(string $username): ?array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare('SELECT * FROM users WHERE username = ?');
|
||||||
|
$stmt->execute([strtolower($username)]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
return $row !== false ? $row : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return all user rows ordered by username. */
|
||||||
|
function db_get_all_users(): array
|
||||||
|
{
|
||||||
|
return db()->query('SELECT * FROM users ORDER BY username')->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Build the auth_data session array from a DB user row.
|
||||||
|
* Preserves the same format existing code expects:
|
||||||
|
* auth_data.permissions, auth_data.entreaulas.centro, .role, .aulas, .centros
|
||||||
|
*/
|
||||||
|
function db_build_auth_data(array $row): array
|
||||||
|
{
|
||||||
|
$permissions = json_decode($row['permissions'] ?? '[]', true) ?: [];
|
||||||
|
$meta = json_decode($row['meta'] ?? '{}', true) ?: [];
|
||||||
|
|
||||||
|
// Fetch all centro assignments for this user
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT centro_id, role, aulas
|
||||||
|
FROM user_centros
|
||||||
|
WHERE user_id = ?
|
||||||
|
ORDER BY centro_id'
|
||||||
|
);
|
||||||
|
$stmt->execute([$row['id']]);
|
||||||
|
$centro_rows = $stmt->fetchAll();
|
||||||
|
|
||||||
|
$ea = ['centro' => '', 'centros' => [], 'role' => '', 'aulas' => []];
|
||||||
|
if (!empty($centro_rows)) {
|
||||||
|
$first = $centro_rows[0];
|
||||||
|
$ea['centro'] = $first['centro_id']; // legacy compat
|
||||||
|
$ea['role'] = $first['role'];
|
||||||
|
$ea['aulas'] = json_decode($first['aulas'] ?? '[]', true) ?: [];
|
||||||
|
$ea['centros'] = array_column($centro_rows, 'centro_id');
|
||||||
|
$ea['centros_data'] = $centro_rows;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_merge($meta, [
|
||||||
|
'display_name' => $row['display_name'],
|
||||||
|
'email' => $row['email'],
|
||||||
|
'password_hash' => $row['password_hash'],
|
||||||
|
'permissions' => $permissions,
|
||||||
|
'entreaulas' => $ea,
|
||||||
|
'google_auth' => (bool) $row['google_auth'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create or update a user.
|
||||||
|
* $data keys: username, display_name, email, password_hash, permissions[],
|
||||||
|
* google_auth, entreaulas{centro,centros[],role,aulas[]}, + any extra meta.
|
||||||
|
* Returns the user ID.
|
||||||
|
*/
|
||||||
|
function db_upsert_user(array $data): int
|
||||||
|
{
|
||||||
|
$pdo = db();
|
||||||
|
$username = strtolower((string) ($data['username'] ?? ''));
|
||||||
|
|
||||||
|
$existing = $pdo->prepare('SELECT id FROM users WHERE username = ?');
|
||||||
|
$existing->execute([$username]);
|
||||||
|
$existing_row = $existing->fetch();
|
||||||
|
|
||||||
|
$permissions = json_encode($data['permissions'] ?? []);
|
||||||
|
$meta_skip = ['username', 'display_name', 'email', 'password_hash',
|
||||||
|
'permissions', 'entreaulas', 'google_auth'];
|
||||||
|
$meta = [];
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
if (!in_array($k, $meta_skip, true)) {
|
||||||
|
$meta[$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($existing_row) {
|
||||||
|
$user_id = (int) $existing_row['id'];
|
||||||
|
$upd = $pdo->prepare(
|
||||||
|
"UPDATE users SET
|
||||||
|
display_name = ?,
|
||||||
|
email = ?,
|
||||||
|
permissions = ?,
|
||||||
|
google_auth = ?,
|
||||||
|
meta = ?,
|
||||||
|
updated_at = datetime('now')
|
||||||
|
WHERE id = ?"
|
||||||
|
);
|
||||||
|
$upd->execute([
|
||||||
|
$data['display_name'] ?? '',
|
||||||
|
$data['email'] ?? '',
|
||||||
|
$permissions,
|
||||||
|
(int) ($data['google_auth'] ?? 0),
|
||||||
|
json_encode($meta),
|
||||||
|
$user_id,
|
||||||
|
]);
|
||||||
|
if (!empty($data['password_hash'])) {
|
||||||
|
$pdo->prepare('UPDATE users SET password_hash = ? WHERE id = ?')
|
||||||
|
->execute([$data['password_hash'], $user_id]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$pdo->prepare(
|
||||||
|
'INSERT INTO users (username, display_name, email, password_hash, permissions, google_auth, meta)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)'
|
||||||
|
)->execute([
|
||||||
|
$username,
|
||||||
|
$data['display_name'] ?? '',
|
||||||
|
$data['email'] ?? '',
|
||||||
|
$data['password_hash'] ?? '',
|
||||||
|
$permissions,
|
||||||
|
(int) ($data['google_auth'] ?? 0),
|
||||||
|
json_encode($meta),
|
||||||
|
]);
|
||||||
|
$user_id = (int) $pdo->lastInsertId();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update centro assignments when entreaulas data is provided
|
||||||
|
if (array_key_exists('entreaulas', $data)) {
|
||||||
|
$ea = $data['entreaulas'] ?? [];
|
||||||
|
$pdo->prepare('DELETE FROM user_centros WHERE user_id = ?')->execute([$user_id]);
|
||||||
|
|
||||||
|
// Support both legacy single centro and new multi-centro
|
||||||
|
$centros = [];
|
||||||
|
if (!empty($ea['centros']) && is_array($ea['centros'])) {
|
||||||
|
$centros = $ea['centros'];
|
||||||
|
} elseif (!empty($ea['centro'])) {
|
||||||
|
$centros = [$ea['centro']];
|
||||||
|
}
|
||||||
|
$role = $ea['role'] ?? '';
|
||||||
|
$aulas = json_encode($ea['aulas'] ?? []);
|
||||||
|
|
||||||
|
$ins_centro = $pdo->prepare('INSERT OR IGNORE INTO centros (centro_id) VALUES (?)');
|
||||||
|
$ins_uc = $pdo->prepare(
|
||||||
|
'INSERT OR REPLACE INTO user_centros (user_id, centro_id, role, aulas) VALUES (?, ?, ?, ?)'
|
||||||
|
);
|
||||||
|
foreach ($centros as $cid) {
|
||||||
|
if ($cid === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ins_centro->execute([$cid]);
|
||||||
|
$ins_uc->execute([$user_id, $cid, $role, $aulas]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $user_id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Delete a user and their centro assignments. */
|
||||||
|
function db_delete_user(string $username): void
|
||||||
|
{
|
||||||
|
db()->prepare('DELETE FROM users WHERE username = ?')->execute([strtolower($username)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Centro helpers ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_get_centros(): array
|
||||||
|
{
|
||||||
|
return db()->query('SELECT centro_id, name FROM centros ORDER BY centro_id')->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_get_centro_ids(): array
|
||||||
|
{
|
||||||
|
return db()->query('SELECT centro_id FROM centros ORDER BY centro_id')->fetchAll(PDO::FETCH_COLUMN);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Aulario helpers ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Get a single aulario config. Returns merged array (name, icon, + extra fields) or null. */
|
||||||
|
function db_get_aulario(string $centro_id, string $aulario_id): ?array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT name, icon, extra FROM aularios WHERE centro_id = ? AND aulario_id = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id, $aulario_id]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
$extra = json_decode($row['extra'] ?? '{}', true) ?: [];
|
||||||
|
return array_merge($extra, ['name' => $row['name'], 'icon' => $row['icon']]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Get all aularios for a centro as aulario_id → config array. */
|
||||||
|
function db_get_aularios(string $centro_id): array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT aulario_id, name, icon, extra FROM aularios WHERE centro_id = ? ORDER BY aulario_id'
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id]);
|
||||||
|
$result = [];
|
||||||
|
foreach ($stmt->fetchAll() as $row) {
|
||||||
|
$extra = json_decode($row['extra'] ?? '{}', true) ?: [];
|
||||||
|
$result[$row['aulario_id']] = array_merge($extra, [
|
||||||
|
'name' => $row['name'],
|
||||||
|
'icon' => $row['icon'],
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── SuperCafe helpers ─────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_get_supercafe_menu(string $centro_id): array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare('SELECT data FROM supercafe_menu WHERE centro_id = ?');
|
||||||
|
$stmt->execute([$centro_id]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return json_decode($row['data'], true) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_set_supercafe_menu(string $centro_id, array $menu): void
|
||||||
|
{
|
||||||
|
db()->prepare('INSERT OR REPLACE INTO supercafe_menu (centro_id, data, updated_at) VALUES (?, ?, datetime(\'now\'))')
|
||||||
|
->execute([$centro_id, json_encode($menu, JSON_UNESCAPED_UNICODE)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return all SC orders for a centro as an array of rows. */
|
||||||
|
function db_get_supercafe_orders(string $centro_id): array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT * FROM supercafe_orders WHERE centro_id = ? ORDER BY created_at DESC'
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id]);
|
||||||
|
return $stmt->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Return a single SC order by ref, or null. */
|
||||||
|
function db_get_supercafe_order(string $centro_id, string $order_ref): ?array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT * FROM supercafe_orders WHERE centro_id = ? AND order_ref = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id, $order_ref]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
return $row !== false ? $row : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Create or update an SC order. */
|
||||||
|
function db_upsert_supercafe_order(
|
||||||
|
string $centro_id,
|
||||||
|
string $order_ref,
|
||||||
|
string $fecha,
|
||||||
|
string $persona,
|
||||||
|
string $comanda,
|
||||||
|
string $notas,
|
||||||
|
string $estado
|
||||||
|
): void {
|
||||||
|
db()->prepare(
|
||||||
|
'INSERT INTO supercafe_orders (centro_id, order_ref, fecha, persona, comanda, notas, estado)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
||||||
|
ON CONFLICT(centro_id, order_ref) DO UPDATE SET
|
||||||
|
fecha = excluded.fecha,
|
||||||
|
persona = excluded.persona,
|
||||||
|
comanda = excluded.comanda,
|
||||||
|
notas = excluded.notas,
|
||||||
|
estado = excluded.estado'
|
||||||
|
)->execute([$centro_id, $order_ref, $fecha, $persona, $comanda, $notas, $estado]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Generate the next order_ref for a centro (sc001, sc002, …). */
|
||||||
|
function db_next_supercafe_ref(string $centro_id): string
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
"SELECT order_ref FROM supercafe_orders WHERE centro_id = ? ORDER BY id DESC LIMIT 1"
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id]);
|
||||||
|
$last = $stmt->fetchColumn();
|
||||||
|
$n = 0;
|
||||||
|
if ($last && preg_match('/^sc(\d+)$/', $last, $m)) {
|
||||||
|
$n = (int) $m[1];
|
||||||
|
}
|
||||||
|
return 'sc' . str_pad($n + 1, 3, '0', STR_PAD_LEFT);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Count 'Deuda' orders for a persona in a centro. */
|
||||||
|
function db_supercafe_count_debts(string $centro_id, string $persona_key): int
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
"SELECT COUNT(*) FROM supercafe_orders WHERE centro_id = ? AND persona = ? AND estado = 'Deuda'"
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id, $persona_key]);
|
||||||
|
return (int) $stmt->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Comedor helpers ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_get_comedor_menu_types(string $centro_id, string $aulario_id): array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT data FROM comedor_menu_types WHERE centro_id = ? AND aulario_id = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id, $aulario_id]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return json_decode($row['data'], true) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_set_comedor_menu_types(string $centro_id, string $aulario_id, array $types): void
|
||||||
|
{
|
||||||
|
db()->prepare(
|
||||||
|
'INSERT OR REPLACE INTO comedor_menu_types (centro_id, aulario_id, data) VALUES (?, ?, ?)'
|
||||||
|
)->execute([$centro_id, $aulario_id, json_encode($types, JSON_UNESCAPED_UNICODE)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_get_comedor_entry(string $centro_id, string $aulario_id, string $ym, string $day): array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT data FROM comedor_entries WHERE centro_id = ? AND aulario_id = ? AND year_month = ? AND day = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id, $aulario_id, $ym, $day]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return json_decode($row['data'], true) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_set_comedor_entry(string $centro_id, string $aulario_id, string $ym, string $day, array $data): void
|
||||||
|
{
|
||||||
|
db()->prepare(
|
||||||
|
'INSERT OR REPLACE INTO comedor_entries (centro_id, aulario_id, year_month, day, data) VALUES (?, ?, ?, ?, ?)'
|
||||||
|
)->execute([$centro_id, $aulario_id, $ym, $day, json_encode($data, JSON_UNESCAPED_UNICODE)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Diario helpers ────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_get_diario_entry(string $centro_id, string $aulario_id, string $entry_date): array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT data FROM diario_entries WHERE centro_id = ? AND aulario_id = ? AND entry_date = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id, $aulario_id, $entry_date]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return json_decode($row['data'], true) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_set_diario_entry(string $centro_id, string $aulario_id, string $entry_date, array $data): void
|
||||||
|
{
|
||||||
|
db()->prepare(
|
||||||
|
'INSERT OR REPLACE INTO diario_entries (centro_id, aulario_id, entry_date, data) VALUES (?, ?, ?, ?)'
|
||||||
|
)->execute([$centro_id, $aulario_id, $entry_date, json_encode($data, JSON_UNESCAPED_UNICODE)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Panel alumno helpers ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_get_panel_alumno(string $centro_id, string $aulario_id, string $alumno): array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare(
|
||||||
|
'SELECT data FROM panel_alumno WHERE centro_id = ? AND aulario_id = ? AND alumno = ?'
|
||||||
|
);
|
||||||
|
$stmt->execute([$centro_id, $aulario_id, $alumno]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return json_decode($row['data'], true) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_set_panel_alumno(string $centro_id, string $aulario_id, string $alumno, array $data): void
|
||||||
|
{
|
||||||
|
db()->prepare(
|
||||||
|
'INSERT OR REPLACE INTO panel_alumno (centro_id, aulario_id, alumno, data) VALUES (?, ?, ?, ?)'
|
||||||
|
)->execute([$centro_id, $aulario_id, $alumno, json_encode($data, JSON_UNESCAPED_UNICODE)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Invitation helpers ────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_get_all_invitations(): array
|
||||||
|
{
|
||||||
|
return db()->query('SELECT * FROM invitations ORDER BY code')->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_get_invitation(string $code): ?array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare('SELECT * FROM invitations WHERE code = ?');
|
||||||
|
$stmt->execute([strtoupper($code)]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
return $row !== false ? $row : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_upsert_invitation(string $code, bool $active, bool $single_use): void
|
||||||
|
{
|
||||||
|
db()->prepare(
|
||||||
|
'INSERT OR REPLACE INTO invitations (code, active, single_use) VALUES (?, ?, ?)'
|
||||||
|
)->execute([strtoupper($code), (int) $active, (int) $single_use]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_deactivate_invitation(string $code): void
|
||||||
|
{
|
||||||
|
db()->prepare('UPDATE invitations SET active = 0 WHERE code = ?')->execute([strtoupper($code)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_delete_invitation(string $code): void
|
||||||
|
{
|
||||||
|
db()->prepare('DELETE FROM invitations WHERE code = ?')->execute([strtoupper($code)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Club helpers ──────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function db_get_club_config(): array
|
||||||
|
{
|
||||||
|
$stmt = db()->query('SELECT data FROM club_config WHERE id = 1');
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return json_decode($row['data'], true) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_set_club_config(array $config): void
|
||||||
|
{
|
||||||
|
db()->prepare('INSERT OR REPLACE INTO club_config (id, data) VALUES (1, ?)')
|
||||||
|
->execute([json_encode($config, JSON_UNESCAPED_UNICODE)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_get_all_club_events(): array
|
||||||
|
{
|
||||||
|
return db()->query('SELECT date_ref, data FROM club_events ORDER BY date_ref DESC')->fetchAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_get_club_event(string $date_ref): array
|
||||||
|
{
|
||||||
|
$stmt = db()->prepare('SELECT data FROM club_events WHERE date_ref = ?');
|
||||||
|
$stmt->execute([$date_ref]);
|
||||||
|
$row = $stmt->fetch();
|
||||||
|
if ($row === false) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return json_decode($row['data'], true) ?: [];
|
||||||
|
}
|
||||||
|
|
||||||
|
function db_set_club_event(string $date_ref, array $data): void
|
||||||
|
{
|
||||||
|
db()->prepare('INSERT OR REPLACE INTO club_events (date_ref, data) VALUES (?, ?)')
|
||||||
|
->execute([$date_ref, json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Multi-tenant helpers ──────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/** Return all centro IDs the authenticated user belongs to. */
|
||||||
|
function get_user_centros(?array $auth_data = null): array
|
||||||
|
{
|
||||||
|
$data = $auth_data ?? $_SESSION['auth_data'] ?? [];
|
||||||
|
$ea = $data['entreaulas'] ?? [];
|
||||||
|
|
||||||
|
if (!empty($ea['centros']) && is_array($ea['centros'])) {
|
||||||
|
return array_values($ea['centros']);
|
||||||
|
}
|
||||||
|
if (!empty($ea['centro'])) {
|
||||||
|
return [$ea['centro']];
|
||||||
|
}
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Ensure $_SESSION['active_centro'] is set to a valid centro. */
|
||||||
|
function init_active_centro(?array $auth_data = null): void
|
||||||
|
{
|
||||||
|
$centros = get_user_centros($auth_data);
|
||||||
|
if (empty($centros)) {
|
||||||
|
$_SESSION['active_centro'] = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!empty($_SESSION['active_centro']) && in_array($_SESSION['active_centro'], $centros, true)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$_SESSION['active_centro'] = $centros[0];
|
||||||
|
}
|
||||||
144
public_html/_incl/migrations/001_initial_schema.sql
Normal file
144
public_html/_incl/migrations/001_initial_schema.sql
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
-- Axia4 Migration 001: Initial Schema
|
||||||
|
-- Converts all JSON file-based storage to a proper relational schema.
|
||||||
|
|
||||||
|
PRAGMA journal_mode = WAL;
|
||||||
|
PRAGMA foreign_keys = ON;
|
||||||
|
|
||||||
|
-- ── Application configuration (replaces /DATA/AuthConfig.json) ─────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS config (
|
||||||
|
key TEXT PRIMARY KEY,
|
||||||
|
value TEXT NOT NULL DEFAULT ''
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── System installation flag (replaces /DATA/SISTEMA_INSTALADO.txt) ────────
|
||||||
|
-- Stored as a config row: key='installed', value='1'
|
||||||
|
|
||||||
|
-- ── Users (replaces /DATA/Usuarios/*.json) ─────────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS users (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
username TEXT UNIQUE NOT NULL,
|
||||||
|
display_name TEXT NOT NULL DEFAULT '',
|
||||||
|
email TEXT NOT NULL DEFAULT '',
|
||||||
|
password_hash TEXT NOT NULL DEFAULT '',
|
||||||
|
permissions TEXT NOT NULL DEFAULT '[]', -- JSON array
|
||||||
|
google_auth INTEGER NOT NULL DEFAULT 0,
|
||||||
|
meta TEXT NOT NULL DEFAULT '{}', -- JSON for extra fields
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
|
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Invitations (replaces /DATA/Invitaciones_de_usuarios.json) ─────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS invitations (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
code TEXT UNIQUE NOT NULL,
|
||||||
|
active INTEGER NOT NULL DEFAULT 1,
|
||||||
|
single_use INTEGER NOT NULL DEFAULT 1,
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Centros/Organizations ───────────────────────────────────────────────────
|
||||||
|
-- Replaces directory existence at /DATA/entreaulas/Centros/{centro_id}/
|
||||||
|
CREATE TABLE IF NOT EXISTS centros (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
centro_id TEXT UNIQUE NOT NULL,
|
||||||
|
name TEXT NOT NULL DEFAULT '',
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now'))
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── User ↔ Centro assignments (many-to-many) ───────────────────────────────
|
||||||
|
-- Replaces entreaulas.centro + entreaulas.aulas fields in user JSON.
|
||||||
|
-- A single user can belong to multiple centros (multi-tenant).
|
||||||
|
CREATE TABLE IF NOT EXISTS user_centros (
|
||||||
|
user_id INTEGER NOT NULL REFERENCES users(id) ON DELETE CASCADE,
|
||||||
|
centro_id TEXT NOT NULL REFERENCES centros(centro_id) ON DELETE CASCADE,
|
||||||
|
role TEXT NOT NULL DEFAULT '',
|
||||||
|
aulas TEXT NOT NULL DEFAULT '[]', -- JSON array of aulario_ids
|
||||||
|
PRIMARY KEY (user_id, centro_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Aularios (replaces /DATA/entreaulas/Centros/{}/Aularios/{id}.json) ──────
|
||||||
|
CREATE TABLE IF NOT EXISTS aularios (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
centro_id TEXT NOT NULL REFERENCES centros(centro_id) ON DELETE CASCADE,
|
||||||
|
aulario_id TEXT NOT NULL,
|
||||||
|
name TEXT NOT NULL DEFAULT '',
|
||||||
|
icon TEXT NOT NULL DEFAULT '',
|
||||||
|
extra TEXT NOT NULL DEFAULT '{}', -- JSON for extra config
|
||||||
|
UNIQUE (centro_id, aulario_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── SuperCafe menu (replaces .../SuperCafe/Menu.json) ──────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS supercafe_menu (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
centro_id TEXT NOT NULL REFERENCES centros(centro_id) ON DELETE CASCADE,
|
||||||
|
data TEXT NOT NULL DEFAULT '{}', -- JSON matching existing format
|
||||||
|
updated_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
|
UNIQUE (centro_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── SuperCafe orders (replaces .../SuperCafe/Comandas/*.json) ───────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS supercafe_orders (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
centro_id TEXT NOT NULL REFERENCES centros(centro_id) ON DELETE CASCADE,
|
||||||
|
order_ref TEXT NOT NULL,
|
||||||
|
fecha TEXT NOT NULL,
|
||||||
|
persona TEXT NOT NULL,
|
||||||
|
comanda TEXT NOT NULL DEFAULT '',
|
||||||
|
notas TEXT NOT NULL DEFAULT '',
|
||||||
|
estado TEXT NOT NULL DEFAULT 'Pedido',
|
||||||
|
created_at TEXT NOT NULL DEFAULT (datetime('now')),
|
||||||
|
UNIQUE (centro_id, order_ref)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Comedor menu types (replaces .../Comedor-MenuTypes.json) ────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS comedor_menu_types (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
centro_id TEXT NOT NULL REFERENCES centros(centro_id) ON DELETE CASCADE,
|
||||||
|
aulario_id TEXT NOT NULL,
|
||||||
|
data TEXT NOT NULL DEFAULT '[]', -- JSON array of menu type objs
|
||||||
|
UNIQUE (centro_id, aulario_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Comedor daily entries (replaces .../Comedor/{ym}/{day}/_datos.json) ─────
|
||||||
|
CREATE TABLE IF NOT EXISTS comedor_entries (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
centro_id TEXT NOT NULL REFERENCES centros(centro_id) ON DELETE CASCADE,
|
||||||
|
aulario_id TEXT NOT NULL,
|
||||||
|
year_month TEXT NOT NULL, -- "2024-01"
|
||||||
|
day TEXT NOT NULL, -- "15"
|
||||||
|
data TEXT NOT NULL DEFAULT '{}',
|
||||||
|
UNIQUE (centro_id, aulario_id, year_month, day)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Diary entries (replaces .../Diario/*.json) ──────────────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS diario_entries (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
centro_id TEXT NOT NULL REFERENCES centros(centro_id) ON DELETE CASCADE,
|
||||||
|
aulario_id TEXT NOT NULL,
|
||||||
|
entry_date TEXT NOT NULL,
|
||||||
|
data TEXT NOT NULL DEFAULT '{}',
|
||||||
|
UNIQUE (centro_id, aulario_id, entry_date)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Panel diario per-student data (replaces .../Alumnos/*/Panel.json) ───────
|
||||||
|
CREATE TABLE IF NOT EXISTS panel_alumno (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
centro_id TEXT NOT NULL REFERENCES centros(centro_id) ON DELETE CASCADE,
|
||||||
|
aulario_id TEXT NOT NULL,
|
||||||
|
alumno TEXT NOT NULL,
|
||||||
|
data TEXT NOT NULL DEFAULT '{}',
|
||||||
|
UNIQUE (centro_id, aulario_id, alumno)
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Club event metadata (replaces /DATA/club/IMG/{date}/data.json) ──────────
|
||||||
|
CREATE TABLE IF NOT EXISTS club_events (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
date_ref TEXT UNIQUE NOT NULL,
|
||||||
|
data TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
|
|
||||||
|
-- ── Club configuration (replaces /DATA/club/config.json) ────────────────────
|
||||||
|
CREATE TABLE IF NOT EXISTS club_config (
|
||||||
|
id INTEGER PRIMARY KEY CHECK (id = 1),
|
||||||
|
data TEXT NOT NULL DEFAULT '{}'
|
||||||
|
);
|
||||||
255
public_html/_incl/migrations/002_import_json.php
Normal file
255
public_html/_incl/migrations/002_import_json.php
Normal file
@@ -0,0 +1,255 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Migration 002: Import existing JSON data from the filesystem into the DB.
|
||||||
|
* This runs once on first boot after the schema is created.
|
||||||
|
* It is safe to run even if /DATA doesn't have all files – missing files are skipped.
|
||||||
|
*
|
||||||
|
* $db (PDO) is provided by the migration runner in db.php.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// ── AuthConfig → config table ────────────────────────────────────────────────
|
||||||
|
$auth_config_file = '/DATA/AuthConfig.json';
|
||||||
|
if (file_exists($auth_config_file)) {
|
||||||
|
$auth_config = json_decode(file_get_contents($auth_config_file), true) ?? [];
|
||||||
|
$ins = $db->prepare("INSERT OR IGNORE INTO config (key, value) VALUES (?, ?)");
|
||||||
|
foreach ($auth_config as $k => $v) {
|
||||||
|
$ins->execute([$k, is_string($v) ? $v : json_encode($v)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── SISTEMA_INSTALADO marker ─────────────────────────────────────────────────
|
||||||
|
if (file_exists('/DATA/SISTEMA_INSTALADO.txt')) {
|
||||||
|
$db->prepare("INSERT OR IGNORE INTO config (key, value) VALUES ('installed', '1')")->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Users (/DATA/Usuarios/*.json) ────────────────────────────────────────────
|
||||||
|
$users_dir = '/DATA/Usuarios';
|
||||||
|
if (is_dir($users_dir)) {
|
||||||
|
$ins_user = $db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO users
|
||||||
|
(username, display_name, email, password_hash, permissions, google_auth, meta)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
$ins_uc = $db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO user_centros (user_id, centro_id, role, aulas)
|
||||||
|
VALUES (?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
$ins_centro = $db->prepare("INSERT OR IGNORE INTO centros (centro_id) VALUES (?)");
|
||||||
|
|
||||||
|
foreach (glob("$users_dir/*.json") ?: [] as $user_file) {
|
||||||
|
$username = basename($user_file, '.json');
|
||||||
|
$data = json_decode(file_get_contents($user_file), true);
|
||||||
|
if (!is_array($data)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$permissions = isset($data['permissions']) ? json_encode($data['permissions']) : '[]';
|
||||||
|
// Store remaining non-standard keys in meta
|
||||||
|
$meta_keys = ['display_name', 'email', 'password_hash', 'permissions', 'entreaulas', 'google_auth'];
|
||||||
|
$meta = [];
|
||||||
|
foreach ($data as $k => $v) {
|
||||||
|
if (!in_array($k, $meta_keys, true)) {
|
||||||
|
$meta[$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$ins_user->execute([
|
||||||
|
$username,
|
||||||
|
$data['display_name'] ?? '',
|
||||||
|
$data['email'] ?? '',
|
||||||
|
$data['password_hash'] ?? '',
|
||||||
|
$permissions,
|
||||||
|
(int) ($data['google_auth'] ?? 0),
|
||||||
|
json_encode($meta),
|
||||||
|
]);
|
||||||
|
$user_id = (int) $db->lastInsertId();
|
||||||
|
if ($user_id === 0) {
|
||||||
|
// Already existed – look it up
|
||||||
|
$row = $db->prepare("SELECT id FROM users WHERE username = ?")->execute([$username]);
|
||||||
|
$user_id = (int) $db->query("SELECT id FROM users WHERE username = " . $db->quote($username))->fetchColumn();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entreaulas centro assignment
|
||||||
|
$ea = $data['entreaulas'] ?? [];
|
||||||
|
// Support both old single "centro" and new "centros" array
|
||||||
|
$centros = [];
|
||||||
|
if (!empty($ea['centros']) && is_array($ea['centros'])) {
|
||||||
|
$centros = $ea['centros'];
|
||||||
|
} elseif (!empty($ea['centro'])) {
|
||||||
|
$centros = [$ea['centro']];
|
||||||
|
}
|
||||||
|
$role = $ea['role'] ?? '';
|
||||||
|
$aulas = json_encode($ea['aulas'] ?? []);
|
||||||
|
foreach ($centros as $cid) {
|
||||||
|
if ($cid === '') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ins_centro->execute([$cid]);
|
||||||
|
$ins_uc->execute([$user_id, $cid, $role, $aulas]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Invitations (/DATA/Invitaciones_de_usuarios.json) ────────────────────────
|
||||||
|
$inv_file = '/DATA/Invitaciones_de_usuarios.json';
|
||||||
|
if (file_exists($inv_file)) {
|
||||||
|
$invs = json_decode(file_get_contents($inv_file), true) ?? [];
|
||||||
|
$ins = $db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO invitations (code, active, single_use) VALUES (?, ?, ?)"
|
||||||
|
);
|
||||||
|
foreach ($invs as $code => $inv) {
|
||||||
|
$ins->execute([
|
||||||
|
strtoupper($code),
|
||||||
|
(int) ($inv['active'] ?? 1),
|
||||||
|
(int) ($inv['single_use'] ?? 1),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Centros & Aularios (directory structure) ──────────────────────────────────
|
||||||
|
$centros_base = '/DATA/entreaulas/Centros';
|
||||||
|
if (is_dir($centros_base)) {
|
||||||
|
$ins_centro = $db->prepare("INSERT OR IGNORE INTO centros (centro_id) VALUES (?)");
|
||||||
|
$ins_aulario = $db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO aularios (centro_id, aulario_id, name, icon, extra) VALUES (?, ?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
foreach (glob("$centros_base/*", GLOB_ONLYDIR) ?: [] as $centro_dir) {
|
||||||
|
$centro_id = basename($centro_dir);
|
||||||
|
$ins_centro->execute([$centro_id]);
|
||||||
|
|
||||||
|
$aularios_dir = "$centro_dir/Aularios";
|
||||||
|
foreach (glob("$aularios_dir/*.json") ?: [] as $aulario_file) {
|
||||||
|
$aulario_id = basename($aulario_file, '.json');
|
||||||
|
$adata = json_decode(file_get_contents($aulario_file), true);
|
||||||
|
if (!is_array($adata)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$name = $adata['name'] ?? $aulario_id;
|
||||||
|
$icon = $adata['icon'] ?? '';
|
||||||
|
$extra_keys = ['name', 'icon'];
|
||||||
|
$extra = [];
|
||||||
|
foreach ($adata as $k => $v) {
|
||||||
|
if (!in_array($k, $extra_keys, true)) {
|
||||||
|
$extra[$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$ins_aulario->execute([$centro_id, $aulario_id, $name, $icon, json_encode($extra)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuperCafe menu
|
||||||
|
$menu_file = "$centro_dir/SuperCafe/Menu.json";
|
||||||
|
if (file_exists($menu_file)) {
|
||||||
|
$menu_data = file_get_contents($menu_file);
|
||||||
|
$db->prepare("INSERT OR IGNORE INTO supercafe_menu (centro_id, data) VALUES (?, ?)")
|
||||||
|
->execute([$centro_id, $menu_data]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// SuperCafe orders
|
||||||
|
$comandas_dir = "$centro_dir/SuperCafe/Comandas";
|
||||||
|
if (is_dir($comandas_dir)) {
|
||||||
|
$ins_order = $db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO supercafe_orders
|
||||||
|
(centro_id, order_ref, fecha, persona, comanda, notas, estado)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
foreach (glob("$comandas_dir/*.json") ?: [] as $order_file) {
|
||||||
|
$order_ref = basename($order_file, '.json');
|
||||||
|
$odata = json_decode(file_get_contents($order_file), true);
|
||||||
|
if (!is_array($odata)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
$ins_order->execute([
|
||||||
|
$centro_id,
|
||||||
|
$order_ref,
|
||||||
|
$odata['Fecha'] ?? '',
|
||||||
|
$odata['Persona'] ?? '',
|
||||||
|
$odata['Comanda'] ?? '',
|
||||||
|
$odata['Notas'] ?? '',
|
||||||
|
$odata['Estado'] ?? 'Pedido',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comedor menu types & daily entries per aulario
|
||||||
|
foreach (glob("$aularios_dir/*.json") ?: [] as $aulario_file) {
|
||||||
|
$aulario_id = basename($aulario_file, '.json');
|
||||||
|
|
||||||
|
$menu_types_file = "$aularios_dir/$aulario_id/Comedor-MenuTypes.json";
|
||||||
|
if (file_exists($menu_types_file)) {
|
||||||
|
$db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO comedor_menu_types (centro_id, aulario_id, data) VALUES (?, ?, ?)"
|
||||||
|
)->execute([$centro_id, $aulario_id, file_get_contents($menu_types_file)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
$comedor_base = "$aularios_dir/$aulario_id/Comedor";
|
||||||
|
if (is_dir($comedor_base)) {
|
||||||
|
$ins_centry = $db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO comedor_entries (centro_id, aulario_id, year_month, day, data) VALUES (?, ?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
foreach (glob("$comedor_base/*", GLOB_ONLYDIR) ?: [] as $ym_dir) {
|
||||||
|
$ym = basename($ym_dir);
|
||||||
|
foreach (glob("$ym_dir/*", GLOB_ONLYDIR) ?: [] as $day_dir) {
|
||||||
|
$day = basename($day_dir);
|
||||||
|
$data_file = "$day_dir/_datos.json";
|
||||||
|
if (file_exists($data_file)) {
|
||||||
|
$ins_centry->execute([
|
||||||
|
$centro_id, $aulario_id, $ym, $day,
|
||||||
|
file_get_contents($data_file),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Diario entries
|
||||||
|
$diario_base = "$aularios_dir/$aulario_id/Diario";
|
||||||
|
if (is_dir($diario_base)) {
|
||||||
|
$ins_d = $db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO diario_entries (centro_id, aulario_id, entry_date, data) VALUES (?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
foreach (glob("$diario_base/*.json") ?: [] as $diario_file) {
|
||||||
|
$entry_date = basename($diario_file, '.json');
|
||||||
|
$ins_d->execute([$centro_id, $aulario_id, $entry_date, file_get_contents($diario_file)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panel alumno data
|
||||||
|
$alumnos_base = "$aularios_dir/$aulario_id/Alumnos";
|
||||||
|
if (is_dir($alumnos_base)) {
|
||||||
|
$ins_pa = $db->prepare(
|
||||||
|
"INSERT OR IGNORE INTO panel_alumno (centro_id, aulario_id, alumno, data) VALUES (?, ?, ?, ?)"
|
||||||
|
);
|
||||||
|
foreach (glob("$alumnos_base/*/", GLOB_ONLYDIR) ?: [] as $alumno_dir) {
|
||||||
|
$alumno = basename($alumno_dir);
|
||||||
|
// Look for Panel.json (used by paneldiario)
|
||||||
|
$panel_files = glob("$alumno_dir/Panel*.json") ?: [];
|
||||||
|
foreach ($panel_files as $pf) {
|
||||||
|
$ins_pa->execute([
|
||||||
|
$centro_id, $aulario_id, $alumno,
|
||||||
|
file_get_contents($pf),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Club config (/DATA/club/config.json) ──────────────────────────────────────
|
||||||
|
$club_config_file = '/DATA/club/config.json';
|
||||||
|
if (file_exists($club_config_file)) {
|
||||||
|
$db->prepare("INSERT OR IGNORE INTO club_config (id, data) VALUES (1, ?)")
|
||||||
|
->execute([file_get_contents($club_config_file)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Club events (/DATA/club/IMG/{date}/data.json) ─────────────────────────────
|
||||||
|
$club_img_dir = '/DATA/club/IMG';
|
||||||
|
if (is_dir($club_img_dir)) {
|
||||||
|
$ins_ev = $db->prepare("INSERT OR IGNORE INTO club_events (date_ref, data) VALUES (?, ?)");
|
||||||
|
foreach (glob("$club_img_dir/*/", GLOB_ONLYDIR) ?: [] as $event_dir) {
|
||||||
|
$date_ref = basename($event_dir);
|
||||||
|
$event_data_file = "$event_dir/data.json";
|
||||||
|
$ins_ev->execute([
|
||||||
|
$date_ref,
|
||||||
|
file_exists($event_data_file) ? file_get_contents($event_data_file) : '{}',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -27,7 +27,11 @@ if (!empty($displayName)) {
|
|||||||
$initials = mb_strtoupper($first . $last);
|
$initials = mb_strtoupper($first . $last);
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
// Tenant (centro) management
|
||||||
|
$userCentros = get_user_centros($_SESSION["auth_data"] ?? []);
|
||||||
|
$activeCentro = $_SESSION['active_centro'] ?? ($_SESSION["auth_data"]["entreaulas"]["centro"] ?? '');
|
||||||
|
|
||||||
|
|
||||||
<!DOCTYPE html>
|
<!DOCTYPE html>
|
||||||
<html lang="es">
|
<html lang="es">
|
||||||
|
|
||||||
@@ -526,6 +530,28 @@ if (!empty($displayName)) {
|
|||||||
<div class="account-name"><?php echo htmlspecialchars($displayName); ?></div>
|
<div class="account-name"><?php echo htmlspecialchars($displayName); ?></div>
|
||||||
<div class="account-email"><?php echo htmlspecialchars($email); ?></div>
|
<div class="account-email"><?php echo htmlspecialchars($email); ?></div>
|
||||||
</div>
|
</div>
|
||||||
|
<?php if (!empty($userCentros) && $_SESSION["auth_ok"]): ?>
|
||||||
|
<div style="padding: 8px 16px; border-top: 1px solid #e0e0e0;">
|
||||||
|
<div style="font-size:.75rem;font-weight:600;color:#5f6368;text-transform:uppercase;letter-spacing:.05em;margin-bottom:6px;">
|
||||||
|
Organización activa
|
||||||
|
</div>
|
||||||
|
<div style="font-size:.9rem;font-weight:600;color:#1a73e8;margin-bottom:<?= count($userCentros) > 1 ? '8px' : '0' ?>;">
|
||||||
|
<?= htmlspecialchars($activeCentro ?: '–') ?>
|
||||||
|
</div>
|
||||||
|
<?php if (count($userCentros) > 1): ?>
|
||||||
|
<div style="font-size:.75rem;color:#5f6368;margin-bottom:4px;">Cambiar organización:</div>
|
||||||
|
<?php foreach ($userCentros as $cid): if ($cid === $activeCentro) continue; ?>
|
||||||
|
<form method="post" action="/_incl/switch_tenant.php" style="margin:0 0 4px;">
|
||||||
|
<input type="hidden" name="redir" value="<?= htmlspecialchars($_SERVER['REQUEST_URI'] ?? '/') ?>">
|
||||||
|
<button type="submit" name="centro" value="<?= htmlspecialchars($cid) ?>"
|
||||||
|
style="display:block;width:100%;text-align:left;padding:5px 8px;border:1px solid #e0e0e0;border-radius:6px;background:#f8f9fa;font-size:.85rem;cursor:pointer;">
|
||||||
|
<?= htmlspecialchars($cid) ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
<div class="account-actions">
|
<div class="account-actions">
|
||||||
<?php if ($_SESSION["auth_ok"]) { ?>
|
<?php if ($_SESSION["auth_ok"]) { ?>
|
||||||
<a href="/account/" class="btn btn-outline-secondary w-100">Gestionar cuenta</a>
|
<a href="/account/" class="btn btn-outline-secondary w-100">Gestionar cuenta</a>
|
||||||
|
|||||||
28
public_html/_incl/switch_tenant.php
Normal file
28
public_html/_incl/switch_tenant.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* switch_tenant.php
|
||||||
|
* POST endpoint to switch the active tenant/centro for the current user session.
|
||||||
|
* Validates the requested centro against the user's allowed centros before applying.
|
||||||
|
*/
|
||||||
|
require_once "tools.session.php";
|
||||||
|
require_once "tools.security.php";
|
||||||
|
require_once "db.php";
|
||||||
|
|
||||||
|
if (!isset($_SESSION["auth_ok"]) || $_SESSION["auth_ok"] !== true) {
|
||||||
|
header("HTTP/1.1 401 Unauthorized");
|
||||||
|
die("No autenticado.");
|
||||||
|
}
|
||||||
|
|
||||||
|
$requested = Sf($_POST['centro'] ?? '');
|
||||||
|
$redir = safe_redir($_POST['redir'] ?? '/');
|
||||||
|
|
||||||
|
$centros = get_user_centros($_SESSION['auth_data'] ?? []);
|
||||||
|
|
||||||
|
if ($requested !== '' && in_array($requested, $centros, true)) {
|
||||||
|
$_SESSION['active_centro'] = $requested;
|
||||||
|
// Also update session auth_data so it reflects immediately
|
||||||
|
$_SESSION['auth_data']['entreaulas']['centro'] = $requested;
|
||||||
|
}
|
||||||
|
|
||||||
|
header("Location: $redir");
|
||||||
|
exit;
|
||||||
@@ -1,124 +1,80 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "tools.session.php";
|
require_once "tools.session.php";
|
||||||
require_once "tools.security.php";
|
require_once "tools.security.php";
|
||||||
|
require_once __DIR__ . "/db.php";
|
||||||
|
|
||||||
|
// Load auth config from DB (replaces /DATA/AuthConfig.json)
|
||||||
if (!isset($AuthConfig)) {
|
if (!isset($AuthConfig)) {
|
||||||
$AuthConfig = json_decode(file_get_contents("/DATA/AuthConfig.json"), true);
|
$AuthConfig = db_get_all_config();
|
||||||
}
|
}
|
||||||
$ua = $_SERVER['HTTP_USER_AGENT'];
|
|
||||||
|
// ── Header-based auth (Axia4Auth/{user}/{pass}) ───────────────────────────────
|
||||||
|
$ua = $_SERVER['HTTP_USER_AGENT'] ?? '';
|
||||||
if (str_starts_with($ua, "Axia4Auth/")) {
|
if (str_starts_with($ua, "Axia4Auth/")) {
|
||||||
$username = explode("/", $ua)[1];
|
$parts = explode("/", $ua);
|
||||||
$userpass = explode("/", $ua)[2];
|
$username = $parts[1] ?? '';
|
||||||
$user_filename = safe_username_to_filename($username);
|
$userpass = $parts[2] ?? '';
|
||||||
if ($user_filename === "") {
|
$row = db_get_user($username);
|
||||||
header("HTTP/1.1 403 Forbidden");
|
if (!$row || !password_verify($userpass, $row['password_hash'])) {
|
||||||
die();
|
|
||||||
}
|
|
||||||
$userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true);
|
|
||||||
if (!$userdata) {
|
|
||||||
header("HTTP/1.1 403 Forbidden");
|
|
||||||
die();
|
|
||||||
}
|
|
||||||
if (!password_verify($userpass, $userdata["password_hash"])) {
|
|
||||||
header("HTTP/1.1 403 Forbidden");
|
header("HTTP/1.1 403 Forbidden");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
$_SESSION["auth_user"] = $username;
|
$_SESSION["auth_user"] = $username;
|
||||||
$_SESSION["auth_data"] = $userdata;
|
$_SESSION["auth_data"] = db_build_auth_data($row);
|
||||||
$_SESSION["auth_ok"] = true;
|
$_SESSION["auth_ok"] = true;
|
||||||
$_COOKIE["auth_user"] = $username;
|
$_COOKIE["auth_user"] = $username;
|
||||||
$_COOKIE["auth_pass_b64"] = base64_encode($userpass);
|
$_COOKIE["auth_pass_b64"] = base64_encode($userpass);
|
||||||
$_SESSION["auth_external_lock"] = "header"; // Cannot logout because auth is done via header
|
$_SESSION["auth_external_lock"] = "header";
|
||||||
|
init_active_centro($_SESSION["auth_data"]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If $_SESSION is empty, check for cookies "auth_user" and "auth_pass_b64"
|
// ── Cookie-based auto-login ───────────────────────────────────────────────────
|
||||||
if ($_SESSION["auth_ok"] != true && isset($_COOKIE["auth_user"]) && isset($_COOKIE["auth_pass_b64"])) {
|
if (($_SESSION["auth_ok"] ?? false) != true
|
||||||
|
&& isset($_COOKIE["auth_user"], $_COOKIE["auth_pass_b64"])
|
||||||
|
) {
|
||||||
$username = $_COOKIE["auth_user"];
|
$username = $_COOKIE["auth_user"];
|
||||||
$userpass_b64 = $_COOKIE["auth_pass_b64"];
|
$userpass = base64_decode($_COOKIE["auth_pass_b64"]);
|
||||||
$userpass = base64_decode($userpass_b64);
|
$row = db_get_user($username);
|
||||||
$user_filename = safe_username_to_filename($username);
|
if ($row && password_verify($userpass, $row['password_hash'])) {
|
||||||
if ($user_filename !== "") {
|
|
||||||
$userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true);
|
|
||||||
if ($userdata && password_verify($userpass, $userdata["password_hash"])) {
|
|
||||||
$_SESSION["auth_user"] = $username;
|
$_SESSION["auth_user"] = $username;
|
||||||
$_SESSION["auth_data"] = $userdata;
|
$_SESSION["auth_data"] = db_build_auth_data($row);
|
||||||
$_SESSION["auth_ok"] = true;
|
$_SESSION["auth_ok"] = true;
|
||||||
}
|
init_active_centro($_SESSION["auth_data"]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If session is older than 5min, reload user data
|
// ── Periodic session reload from DB ──────────────────────────────────────────
|
||||||
if (isset($_SESSION["auth_ok"]) && $_SESSION["auth_ok"] && isset($_SESSION["auth_user"])) {
|
if (!empty($_SESSION["auth_ok"]) && !empty($_SESSION["auth_user"])) {
|
||||||
if (isset($AuthConfig["session_load_mode"]) && $AuthConfig["session_load_mode"] === "force") {
|
$load_mode = $AuthConfig["session_load_mode"] ?? '';
|
||||||
$username = $_SESSION["auth_user"];
|
if ($load_mode === "force") {
|
||||||
$user_filename = safe_username_to_filename($username);
|
$row = db_get_user($_SESSION["auth_user"]);
|
||||||
if ($user_filename !== "") {
|
if ($row) {
|
||||||
$userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true);
|
$_SESSION["auth_data"] = db_build_auth_data($row);
|
||||||
$_SESSION["auth_data"] = $userdata;
|
init_active_centro($_SESSION["auth_data"]);
|
||||||
}
|
}
|
||||||
$_SESSION["last_reload_time"] = time();
|
$_SESSION["last_reload_time"] = time();
|
||||||
} elseif (isset($AuthConfig["session_load_mode"]) && $AuthConfig["session_load_mode"] === "never") {
|
} elseif ($load_mode !== "never") {
|
||||||
// Do nothing, never reload session data
|
$last = $_SESSION["last_reload_time"] ?? 0;
|
||||||
} else {
|
if (time() - $last > 300) {
|
||||||
if (isset($_SESSION["last_reload_time"])) {
|
$row = db_get_user($_SESSION["auth_user"]);
|
||||||
$last_reload = $_SESSION["last_reload_time"];
|
if ($row) {
|
||||||
if (time() - $last_reload > 300) {
|
$_SESSION["auth_data"] = db_build_auth_data($row);
|
||||||
$username = $_SESSION["auth_user"];
|
init_active_centro($_SESSION["auth_data"]);
|
||||||
$user_filename = safe_username_to_filename($username);
|
|
||||||
if ($user_filename !== "") {
|
|
||||||
$userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true);
|
|
||||||
$_SESSION["auth_data"] = $userdata;
|
|
||||||
}
|
}
|
||||||
$_SESSION["last_reload_time"] = time();
|
$_SESSION["last_reload_time"] = time();
|
||||||
}
|
}
|
||||||
} else {
|
if (!isset($_SESSION["last_reload_time"])) {
|
||||||
$_SESSION["last_reload_time"] = time();
|
$_SESSION["last_reload_time"] = time();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function user_is_authenticated(): bool
|
||||||
function user_is_authenticated()
|
|
||||||
{
|
{
|
||||||
return isset($_SESSION["auth_ok"]) && $_SESSION["auth_ok"] === true;
|
return isset($_SESSION["auth_ok"]) && $_SESSION["auth_ok"] === true;
|
||||||
}
|
}
|
||||||
function user_has_permission($perm)
|
|
||||||
{
|
|
||||||
return in_array($perm, $_SESSION["auth_data"]["permissions"] ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
function user_has_permission(string $perm): bool
|
||||||
* Returns all centro/tenant IDs the authenticated user belongs to.
|
|
||||||
* Supports both the legacy single-centro format (entreaulas.centro = "string")
|
|
||||||
* and the new multi-tenant format (entreaulas.centros = ["a", "b"]).
|
|
||||||
*/
|
|
||||||
function get_user_centros($auth_data = null)
|
|
||||||
{
|
{
|
||||||
$data = $auth_data ?? $_SESSION["auth_data"] ?? [];
|
return in_array($perm, $_SESSION["auth_data"]["permissions"] ?? [], true);
|
||||||
$ea = $data["entreaulas"] ?? [];
|
|
||||||
|
|
||||||
if (!empty($ea["centros"]) && is_array($ea["centros"])) {
|
|
||||||
return array_values($ea["centros"]);
|
|
||||||
}
|
|
||||||
if (!empty($ea["centro"])) {
|
|
||||||
return [$ea["centro"]];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensures $_SESSION['active_centro'] is set to a valid centro for the user.
|
|
||||||
* Call after user data is loaded/reloaded.
|
|
||||||
*/
|
|
||||||
function init_active_centro($auth_data = null)
|
|
||||||
{
|
|
||||||
$centros = get_user_centros($auth_data);
|
|
||||||
if (empty($centros)) {
|
|
||||||
$_SESSION['active_centro'] = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Keep existing selection only if it is still valid
|
|
||||||
if (!empty($_SESSION['active_centro']) && in_array($_SESSION['active_centro'], $centros, true)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
$_SESSION['active_centro'] = $centros[0];
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
if (file_exists("/DATA/SISTEMA_INSTALADO.txt")) {
|
require_once "_incl/db.php";
|
||||||
|
if (db_get_config('installed') === '1') {
|
||||||
header("Location: /");
|
header("Location: /");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
@@ -11,18 +12,14 @@ switch ($_GET['form'] ?? '') {
|
|||||||
if (empty($admin_user) || empty($admin_password)) {
|
if (empty($admin_user) || empty($admin_password)) {
|
||||||
die("El nombre de usuario y la contraseña son obligatorios.");
|
die("El nombre de usuario y la contraseña son obligatorios.");
|
||||||
}
|
}
|
||||||
$password_hash = password_hash($admin_password, PASSWORD_DEFAULT);
|
db_upsert_user([
|
||||||
$admin_userdata = [
|
'username' => $admin_user,
|
||||||
'display_name' => 'Administrador',
|
'display_name' => 'Administrador',
|
||||||
'email' => "$admin_user@nomail.arpa",
|
'email' => "$admin_user@nomail.arpa",
|
||||||
'permissions' => ['*', 'sysadmin:access', 'entreaulas:access'],
|
'permissions' => ['*', 'sysadmin:access', 'entreaulas:access'],
|
||||||
'password_hash' => $password_hash
|
'password_hash' => password_hash($admin_password, PASSWORD_DEFAULT),
|
||||||
];
|
]);
|
||||||
if (!is_dir("/DATA/Usuarios")) {
|
db_set_config('installed', '1');
|
||||||
mkdir("/DATA/Usuarios", 0777, true);
|
|
||||||
}
|
|
||||||
file_put_contents("/DATA/Usuarios/$admin_user.json", json_encode($admin_userdata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
||||||
file_put_contents("/DATA/SISTEMA_INSTALADO.txt", "Sistema instalado el ".date("Y-m-d H:i:s")."\n");
|
|
||||||
header("Location: /_login.php");
|
header("Location: /_login.php");
|
||||||
exit;
|
exit;
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/tools.session.php";
|
require_once "_incl/tools.session.php";
|
||||||
require_once "_incl/tools.security.php";
|
require_once "_incl/tools.security.php";
|
||||||
|
require_once "_incl/db.php";
|
||||||
if (!isset($AuthConfig)) {
|
if (!isset($AuthConfig)) {
|
||||||
$AuthConfig = json_decode(file_get_contents("/DATA/AuthConfig.json"), true);
|
$AuthConfig = db_get_all_config();
|
||||||
}
|
}
|
||||||
$DOMAIN = $_SERVER["HTTP_X_FORWARDED_HOST"] ?? $_SERVER["HTTP_HOST"];
|
$DOMAIN = $_SERVER["HTTP_X_FORWARDED_HOST"] ?? $_SERVER["HTTP_HOST"];
|
||||||
|
|
||||||
@@ -21,13 +22,13 @@ function safe_redir($url) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($_GET["reload_user"] == "1") {
|
if ($_GET["reload_user"] == "1") {
|
||||||
$user_filename = safe_username_to_filename($_SESSION["auth_user"] ?? "");
|
$row = db_get_user($_SESSION["auth_user"] ?? "");
|
||||||
if ($user_filename === "") {
|
if (!$row) {
|
||||||
header("Location: /");
|
header("Location: /");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
$userdata = json_decode(file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true);
|
$_SESSION['auth_data'] = db_build_auth_data($row);
|
||||||
$_SESSION['auth_data'] = $userdata;
|
init_active_centro($_SESSION['auth_data']);
|
||||||
$redir = safe_redir($_GET["redir"] ?? "/");
|
$redir = safe_redir($_GET["redir"] ?? "/");
|
||||||
header("Location: $redir");
|
header("Location: $redir");
|
||||||
die();
|
die();
|
||||||
@@ -84,32 +85,34 @@ if ($_GET["google_callback"] == "1") {
|
|||||||
|
|
||||||
$email = $user_info["email"];
|
$email = $user_info["email"];
|
||||||
$name = $user_info["name"] ?? explode("@", $email)[0];
|
$name = $user_info["name"] ?? explode("@", $email)[0];
|
||||||
$user_filename = safe_username_to_filename($email);
|
$username = strtolower($email);
|
||||||
if ($user_filename === "") {
|
if ($username === "") {
|
||||||
die("Error: Dirección de correo inválida.");
|
die("Error: Dirección de correo inválida.");
|
||||||
}
|
}
|
||||||
$userfile = "/DATA/Usuarios/" . $user_filename . ".json";
|
$password = bin2hex(random_bytes(16));
|
||||||
$password = bin2hex(random_bytes(16)); // Generar una contraseña aleatoria para el usuario, aunque no se usará para iniciar sesión
|
$existing = db_get_user($username);
|
||||||
if (file_exists($userfile)) {
|
if ($existing) {
|
||||||
$userdata = json_decode(file_get_contents($userfile), true);
|
$user_row = $existing;
|
||||||
} else {
|
} else {
|
||||||
$userdata = [
|
db_upsert_user([
|
||||||
"display_name" => $name,
|
'username' => $username,
|
||||||
"email" => $email,
|
'display_name' => $name,
|
||||||
"permissions" => ["public"],
|
'email' => $email,
|
||||||
"password_hash" => password_hash($password, PASSWORD_DEFAULT),
|
'permissions' => ['public'],
|
||||||
"google_auth" => true,
|
'password_hash' => password_hash($password, PASSWORD_DEFAULT),
|
||||||
"#" => "Este usuario fue creado automáticamente al iniciar sesión con Google por primera vez.",
|
'google_auth' => true,
|
||||||
];
|
'#' => 'Este usuario fue creado automáticamente al iniciar sesión con Google por primera vez.',
|
||||||
file_put_contents($userfile, json_encode($userdata));
|
]);
|
||||||
|
$user_row = db_get_user($username);
|
||||||
}
|
}
|
||||||
|
|
||||||
session_regenerate_id(true);
|
session_regenerate_id(true);
|
||||||
$_SESSION['auth_user'] = $email;
|
$_SESSION['auth_user'] = $username;
|
||||||
$_SESSION['auth_data'] = $userdata;
|
$_SESSION['auth_data'] = db_build_auth_data($user_row);
|
||||||
$_SESSION['auth_ok'] = true;
|
$_SESSION['auth_ok'] = true;
|
||||||
|
init_active_centro($_SESSION['auth_data']);
|
||||||
$cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"];
|
$cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"];
|
||||||
setcookie("auth_user", $email, $cookie_options);
|
setcookie("auth_user", $username, $cookie_options);
|
||||||
setcookie("auth_pass_b64", base64_encode($password), $cookie_options);
|
setcookie("auth_pass_b64", base64_encode($password), $cookie_options);
|
||||||
|
|
||||||
$redir = safe_redir($state["redir"] ?? "/");
|
$redir = safe_redir($state["redir"] ?? "/");
|
||||||
@@ -161,18 +164,17 @@ if ($_GET["clear_session"] == "1") {
|
|||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
if (isset($_POST["user"])) {
|
if (isset($_POST["user"])) {
|
||||||
$valid = "";
|
|
||||||
$user = trim(strtolower($_POST["user"]));
|
$user = trim(strtolower($_POST["user"]));
|
||||||
$password = $_POST["password"];
|
$password = $_POST["password"];
|
||||||
$user_filename = safe_username_to_filename($user);
|
$row = db_get_user($user);
|
||||||
$userdata = ($user_filename !== "") ? json_decode(@file_get_contents("/DATA/Usuarios/" . $user_filename . ".json"), true) : null;
|
if (!$row || !isset($row["password_hash"])) {
|
||||||
if (!is_array($userdata) || !isset($userdata["password_hash"])) {
|
|
||||||
$_GET["_result"] = "El usuario no existe.";
|
$_GET["_result"] = "El usuario no existe.";
|
||||||
} elseif (password_verify($password, $userdata["password_hash"])) {
|
} elseif (password_verify($password, $row["password_hash"])) {
|
||||||
session_regenerate_id(true);
|
session_regenerate_id(true);
|
||||||
$_SESSION['auth_user'] = $user;
|
$_SESSION['auth_user'] = $user;
|
||||||
$_SESSION['auth_data'] = $userdata;
|
$_SESSION['auth_data'] = db_build_auth_data($row);
|
||||||
$_SESSION['auth_ok'] = true;
|
$_SESSION['auth_ok'] = true;
|
||||||
|
init_active_centro($_SESSION['auth_data']);
|
||||||
$cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"];
|
$cookie_options = ["expires" => time() + (86400 * 30), "path" => "/", "httponly" => true, "secure" => true, "samesite" => "Lax"];
|
||||||
setcookie("auth_user", $user, $cookie_options);
|
setcookie("auth_user", $user, $cookie_options);
|
||||||
setcookie("auth_pass_b64", base64_encode($password), $cookie_options);
|
setcookie("auth_pass_b64", base64_encode($password), $cookie_options);
|
||||||
@@ -182,9 +184,8 @@ if (isset($_POST["user"])) {
|
|||||||
} else {
|
} else {
|
||||||
$_GET["_result"] = "La contraseña no es correcta.";
|
$_GET["_result"] = "La contraseña no es correcta.";
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
if (!file_exists("/DATA/SISTEMA_INSTALADO.txt")) {
|
if (db_get_config('installed') !== '1') {
|
||||||
header("Location: /_install.php");
|
header("Location: /_install.php");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,44 +1,131 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
?>
|
|
||||||
<div id="grid">
|
|
||||||
<div class="card pad grid-item" style="text-align: center;">
|
|
||||||
<h2>¡Hola, <?php echo htmlspecialchars($_SESSION["auth_data"]["display_name"]); ?>!</h2>
|
|
||||||
<span><b>Tu Email:</b> <?php echo htmlspecialchars($_SESSION["auth_data"]["email"]); ?></span>
|
|
||||||
<span><b>Tu Nombre de Usuario:</b> <?php echo htmlspecialchars($_SESSION["auth_user"]); ?></span>
|
|
||||||
</div>
|
|
||||||
<div class="card pad grid-item" style="text-align: center;">
|
|
||||||
<b>Código QR</b>
|
|
||||||
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=<?php echo urlencode($_SESSION["auth_user"]); ?>" alt="QR Code de Nombre de Usuario" style="margin: 0 auto;">
|
|
||||||
<small>Escanea este código para iniciar sesión. Es como tu contraseña, pero más fácil.</small>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<style>
|
|
||||||
.grid-item {
|
|
||||||
margin-bottom: 10px !important;
|
|
||||||
padding: 15px;
|
|
||||||
width: 300px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grid-item img {
|
$authData = $_SESSION["auth_data"] ?? [];
|
||||||
margin: 0 auto;
|
$username = $_SESSION["auth_user"] ?? '';
|
||||||
height: 150px;
|
$displayName = $authData["display_name"] ?? 'Invitado';
|
||||||
|
$email = $authData["email"] ?? '';
|
||||||
|
$permissions = $authData["permissions"] ?? [];
|
||||||
|
|
||||||
|
// Tenant / centro management
|
||||||
|
$userCentros = get_user_centros($authData);
|
||||||
|
$activeCentro = $_SESSION['active_centro'] ?? ($authData['entreaulas']['centro'] ?? '');
|
||||||
|
$aularios = ($activeCentro !== '') ? db_get_aularios($activeCentro) : [];
|
||||||
|
$userAulas = $authData['entreaulas']['aulas'] ?? [];
|
||||||
|
$role = $authData['entreaulas']['role'] ?? '';
|
||||||
|
|
||||||
|
// Initials for avatar
|
||||||
|
$parts = preg_split('/\s+/', trim($displayName));
|
||||||
|
$initials = mb_strtoupper(mb_substr($parts[0] ?? '', 0, 1) . mb_substr($parts[1] ?? '', 0, 1));
|
||||||
|
if ($initials === '') {
|
||||||
|
$initials = '?';
|
||||||
}
|
}
|
||||||
|
?>
|
||||||
|
<style>
|
||||||
|
.account-grid { display: flex; flex-wrap: wrap; gap: 16px; padding: 16px; }
|
||||||
|
.account-card { background: #fff; border: 1px solid #e0e0e0; border-radius: 12px; padding: 24px; min-width: 280px; flex: 1 1 280px; }
|
||||||
|
.account-card h2 { font-size: 1rem; font-weight: 600; color: var(--gw-text-secondary, #5f6368); text-transform: uppercase; letter-spacing: .05em; margin: 0 0 16px; }
|
||||||
|
.avatar-lg { width: 80px; height: 80px; border-radius: 50%; background: var(--gw-blue, #1a73e8); color: #fff; display: flex; align-items: center; justify-content: center; font-size: 2rem; font-weight: 700; margin: 0 auto 12px; }
|
||||||
|
.info-row { display: flex; justify-content: space-between; padding: 6px 0; border-bottom: 1px solid #f1f3f4; font-size: .9rem; }
|
||||||
|
.info-row:last-child { border-bottom: none; }
|
||||||
|
.info-row .label { color: var(--gw-text-secondary, #5f6368); }
|
||||||
|
.badge-pill { display: inline-block; padding: 2px 10px; border-radius: 99px; font-size: .78rem; font-weight: 600; margin: 2px; }
|
||||||
|
.badge-active { background: #e6f4ea; color: #137333; }
|
||||||
|
.badge-perm { background: #e8f0fe; color: #1a73e8; }
|
||||||
|
.tenant-btn { display: block; width: 100%; text-align: left; padding: 8px 12px; border: 1px solid #e0e0e0; border-radius: 8px; margin-bottom: 8px; background: #f8f9fa; cursor: pointer; font-size: .9rem; transition: background .15s; }
|
||||||
|
.tenant-btn:hover { background: #e8f0fe; }
|
||||||
|
.tenant-btn.active-tenant { border-color: var(--gw-blue, #1a73e8); background: #e8f0fe; font-weight: 600; }
|
||||||
</style>
|
</style>
|
||||||
<script>
|
|
||||||
var msnry = new Masonry('#grid', {
|
<div class="account-grid">
|
||||||
"columnWidth": 300,
|
|
||||||
"itemSelector": ".grid-item",
|
<!-- Profile Card -->
|
||||||
"gutter": 10,
|
<div class="account-card" style="text-align:center;">
|
||||||
"transitionDuration": 0
|
<h2>Mi Perfil</h2>
|
||||||
});
|
<div class="avatar-lg"><?= htmlspecialchars($initials) ?></div>
|
||||||
setTimeout(() => {
|
<div style="font-size:1.2rem; font-weight:700; margin-bottom:4px;"><?= htmlspecialchars($displayName) ?></div>
|
||||||
msnry.layout()
|
<div style="color:var(--gw-text-secondary,#5f6368); margin-bottom:16px;"><?= htmlspecialchars($email ?: 'Sin correo') ?></div>
|
||||||
}, 250)
|
<div class="info-row"><span class="label">Usuario</span><span><?= htmlspecialchars($username) ?></span></div>
|
||||||
setInterval(() => {
|
<?php if ($role): ?>
|
||||||
msnry.layout()
|
<div class="info-row"><span class="label">Rol</span><span><?= htmlspecialchars($role) ?></span></div>
|
||||||
}, 1000);
|
<?php endif; ?>
|
||||||
</script>
|
<div style="margin-top:16px;">
|
||||||
|
<a href="/account/change_password.php" class="btn btn-secondary btn-sm">Cambiar contraseña</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- QR Card -->
|
||||||
|
<div class="account-card" style="text-align:center;">
|
||||||
|
<h2>Código QR de Acceso</h2>
|
||||||
|
<img src="https://api.qrserver.com/v1/create-qr-code/?size=150x150&data=<?= urlencode($username) ?>"
|
||||||
|
alt="QR Code" style="margin:0 auto 12px; display:block; width:150px; height:150px;">
|
||||||
|
<small style="color:var(--gw-text-secondary,#5f6368);">Escanea este código para iniciar sesión rápidamente.</small>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tenant / Centro Card -->
|
||||||
|
<?php if (!empty($userCentros)): ?>
|
||||||
|
<div class="account-card">
|
||||||
|
<h2>Organizaciones</h2>
|
||||||
|
<?php foreach ($userCentros as $cid): ?>
|
||||||
|
<form method="post" action="/_incl/switch_tenant.php" style="margin:0;">
|
||||||
|
<input type="hidden" name="redir" value="/account/">
|
||||||
|
<button type="submit" name="centro" value="<?= htmlspecialchars($cid) ?>"
|
||||||
|
class="tenant-btn <?= ($activeCentro === $cid) ? 'active-tenant' : '' ?>">
|
||||||
|
<?php if ($activeCentro === $cid): ?>
|
||||||
|
<span style="color:var(--gw-blue,#1a73e8);">✓ </span>
|
||||||
|
<?php endif; ?>
|
||||||
|
<?= htmlspecialchars($cid) ?>
|
||||||
|
<?php if ($activeCentro === $cid): ?>
|
||||||
|
<span class="badge-pill badge-active" style="float:right;">Activo</span>
|
||||||
|
<?php endif; ?>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Aulas Card -->
|
||||||
|
<?php if (!empty($userAulas)): ?>
|
||||||
|
<div class="account-card">
|
||||||
|
<h2>Mis Aulas (<?= htmlspecialchars($activeCentro) ?>)</h2>
|
||||||
|
<?php foreach ($userAulas as $aula_id): ?>
|
||||||
|
<?php $aula = $aularios[$aula_id] ?? null; ?>
|
||||||
|
<div class="info-row">
|
||||||
|
<?php if ($aula && !empty($aula['icon'])): ?>
|
||||||
|
<img src="<?= htmlspecialchars($aula['icon']) ?>" style="height:20px;vertical-align:middle;margin-right:6px;">
|
||||||
|
<?php endif; ?>
|
||||||
|
<span><?= htmlspecialchars($aula['name'] ?? $aula_id) ?></span>
|
||||||
|
<span class="badge-pill badge-active">Asignada</span>
|
||||||
|
</div>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Permissions Card -->
|
||||||
|
<?php if (!empty($permissions)): ?>
|
||||||
|
<div class="account-card">
|
||||||
|
<h2>Permisos</h2>
|
||||||
|
<div>
|
||||||
|
<?php foreach ($permissions as $p): ?>
|
||||||
|
<span class="badge-pill badge-perm"><?= htmlspecialchars($p) ?></span>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<?php endif; ?>
|
||||||
|
|
||||||
|
<!-- Session Info Card -->
|
||||||
|
<div class="account-card">
|
||||||
|
<h2>Sesión Activa</h2>
|
||||||
|
<div class="info-row"><span class="label">ID Sesión</span><span style="font-family:monospace;font-size:.75rem;"><?= htmlspecialchars(substr(session_id(), 0, 12)) ?>…</span></div>
|
||||||
|
<div class="info-row"><span class="label">Org. activa</span><span><?= htmlspecialchars($activeCentro ?: '–') ?></span></div>
|
||||||
|
<div class="info-row"><span class="label">Autenticación</span><span><?= empty($authData['google_auth']) ? 'Contraseña' : 'Google' ?></span></div>
|
||||||
|
<div style="margin-top:16px;">
|
||||||
|
<a href="/_incl/logout.php" class="btn btn-danger btn-sm">Cerrar sesión</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
<?php require_once "_incl/post-body.php"; ?>
|
<?php require_once "_incl/post-body.php"; ?>
|
||||||
@@ -1,36 +1,27 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
||||||
// Handle form submission
|
$invi_code = strtoupper(trim($_POST['invitation_code'] ?? ''));
|
||||||
$invitations = json_decode(file_get_contents("/DATA/Invitaciones_de_usuarios.json"), true);
|
$invitation = db_get_invitation($invi_code);
|
||||||
$invi_code = strtoupper($_POST['invitation_code'] ?? '');
|
if (!$invitation || !$invitation['active']) {
|
||||||
if (!isset($invitations[$invi_code])) {
|
|
||||||
header("Location: /?_resultcolor=red&_result=" . urlencode("Código de invitación no válido."));
|
header("Location: /?_resultcolor=red&_result=" . urlencode("Código de invitación no válido."));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
$userdata = [
|
$username = strtolower(trim($_POST['username'] ?? ''));
|
||||||
'display_name' => $_POST['display_name'],
|
if (db_get_user($username)) {
|
||||||
'email' => $_POST['email'],
|
|
||||||
'password_hash' => password_hash($_POST['password'], PASSWORD_DEFAULT),
|
|
||||||
'_meta_signup' => [
|
|
||||||
'invitation_code' => $invi_code
|
|
||||||
],
|
|
||||||
'permissions' => []
|
|
||||||
];
|
|
||||||
if ($invitations[$invi_code]["active"] != true) {
|
|
||||||
header("Location: /?_resultcolor=red&_result=" . urlencode("Código de invitación no válido."));
|
|
||||||
exit;
|
|
||||||
}
|
|
||||||
$username = $_POST['username'];
|
|
||||||
if (file_exists("/DATA/Usuarios/$username.json")) {
|
|
||||||
header("Location: /?_resultcolor=red&_result=" . urlencode("El nombre de usuario ya existe. Por favor, elige otro."));
|
header("Location: /?_resultcolor=red&_result=" . urlencode("El nombre de usuario ya existe. Por favor, elige otro."));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
file_put_contents("/DATA/Usuarios/$username.json", json_encode($userdata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
db_upsert_user([
|
||||||
// Deactivate invitation code if it's single-use
|
'username' => $username,
|
||||||
if ($invitations[$invi_code]["single_use"] === true) {
|
'display_name' => $_POST['display_name'] ?? '',
|
||||||
$invitations[$invi_code]["active"] = false;
|
'email' => $_POST['email'] ?? '',
|
||||||
file_put_contents("/DATA/Invitaciones_de_usuarios.json", json_encode($invitations, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
'password_hash' => password_hash($_POST['password'], PASSWORD_DEFAULT),
|
||||||
|
'permissions' => [],
|
||||||
|
'_meta_signup' => ['invitation_code' => $invi_code],
|
||||||
|
]);
|
||||||
|
if ($invitation['single_use']) {
|
||||||
|
db_deactivate_invitation($invi_code);
|
||||||
}
|
}
|
||||||
header("Location: /?_result=" . urlencode("Cuenta creada correctamente. Ya puedes iniciar sesión."));
|
header("Location: /?_result=" . urlencode("Cuenta creada correctamente. Ya puedes iniciar sesión."));
|
||||||
exit;
|
exit;
|
||||||
|
|||||||
@@ -2,24 +2,7 @@
|
|||||||
header("Content-Type: application/json; charset=utf-8");
|
header("Content-Type: application/json; charset=utf-8");
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../../_incl/db.php";
|
||||||
function menu_types_path($centro_id, $aulario_id) {
|
|
||||||
$centro = safe_centro_id($centro_id);
|
|
||||||
$aulario = safe_id_segment($aulario_id);
|
|
||||||
if ($centro === '' || $aulario === '') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario/Comedor-MenuTypes.json";
|
|
||||||
}
|
|
||||||
|
|
||||||
function comedor_day_base_dir($centro_id, $aulario_id, $ym, $day) {
|
|
||||||
$centro = safe_centro_id($centro_id);
|
|
||||||
$aulario = safe_id_segment($aulario_id);
|
|
||||||
if ($centro === '' || $aulario === '') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return "/DATA/entreaulas/Centros/$centro/Aularios/$aulario/Comedor/$ym/$day";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check permissions
|
// Check permissions
|
||||||
if (!in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? [])) {
|
if (!in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? [])) {
|
||||||
@@ -36,61 +19,45 @@ if ($centro_id === "") {
|
|||||||
$action = $_GET["action"] ?? ($_POST["action"] ?? "");
|
$action = $_GET["action"] ?? ($_POST["action"] ?? "");
|
||||||
$aulario_id = safe_id_segment($_GET["aulario"] ?? $_POST["aulario"] ?? "");
|
$aulario_id = safe_id_segment($_GET["aulario"] ?? $_POST["aulario"] ?? "");
|
||||||
|
|
||||||
// Validate aulario_id
|
|
||||||
if ($aulario_id === "") {
|
if ($aulario_id === "") {
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
die(json_encode(["error" => "aulario parameter is required", "code" => "MISSING_PARAM"]));
|
die(json_encode(["error" => "aulario parameter is required", "code" => "MISSING_PARAM"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify that the user has access to this aulario
|
$userAulas = array_values(array_filter(array_map('safe_id_segment', $_SESSION["auth_data"]["entreaulas"]["aulas"] ?? [])));
|
||||||
$userAulas = $_SESSION["auth_data"]["entreaulas"]["aulas"] ?? [];
|
|
||||||
$userAulas = array_values(array_filter(array_map('safe_id_segment', $userAulas)));
|
|
||||||
if (!in_array($aulario_id, $userAulas, true)) {
|
if (!in_array($aulario_id, $userAulas, true)) {
|
||||||
http_response_code(403);
|
http_response_code(403);
|
||||||
die(json_encode(["error" => "Access denied to this aulario", "code" => "FORBIDDEN"]));
|
die(json_encode(["error" => "Access denied to this aulario", "code" => "FORBIDDEN"]));
|
||||||
}
|
}
|
||||||
|
|
||||||
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
|
$aulario = db_get_aulario($centro_id, $aulario_id);
|
||||||
$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null;
|
|
||||||
|
|
||||||
// Handle shared comedor data
|
|
||||||
$source_aulario_id = $aulario_id;
|
$source_aulario_id = $aulario_id;
|
||||||
$is_shared = false;
|
$is_shared = false;
|
||||||
if ($aulario && !empty($aulario["shared_comedor_from"])) {
|
if ($aulario && !empty($aulario["shared_comedor_from"])) {
|
||||||
$shared_from = safe_id_segment($aulario["shared_comedor_from"]);
|
$shared_from = safe_id_segment($aulario["shared_comedor_from"]);
|
||||||
$shared_aulario_path = safe_aulario_config_path($centro_id, $shared_from);
|
if (db_get_aulario($centro_id, $shared_from)) {
|
||||||
if ($shared_aulario_path && file_exists($shared_aulario_path)) {
|
|
||||||
$source_aulario_id = $shared_from;
|
$source_aulario_id = $shared_from;
|
||||||
$is_shared = true;
|
$is_shared = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check edit permissions (must be sysadmin and not shared)
|
|
||||||
$canEdit = in_array("sysadmin:access", $_SESSION["auth_data"]["permissions"] ?? []) && !$is_shared;
|
$canEdit = in_array("sysadmin:access", $_SESSION["auth_data"]["permissions"] ?? []) && !$is_shared;
|
||||||
|
|
||||||
// Helper functions
|
|
||||||
function get_menu_types($centro_id, $source_aulario_id) {
|
|
||||||
$menuTypesPath = menu_types_path($centro_id, $source_aulario_id);
|
|
||||||
$defaultMenuTypes = [
|
$defaultMenuTypes = [
|
||||||
["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"],
|
["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"],
|
||||||
["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"],
|
["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"],
|
||||||
["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"],
|
["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"],
|
||||||
];
|
];
|
||||||
|
|
||||||
if ($menuTypesPath === null) {
|
function get_menu_types($centro_id, $source_aulario_id) {
|
||||||
|
global $defaultMenuTypes;
|
||||||
|
$types = db_get_comedor_menu_types($centro_id, $source_aulario_id);
|
||||||
|
if (empty($types)) {
|
||||||
|
db_set_comedor_menu_types($centro_id, $source_aulario_id, $defaultMenuTypes);
|
||||||
return $defaultMenuTypes;
|
return $defaultMenuTypes;
|
||||||
}
|
}
|
||||||
|
return $types;
|
||||||
if (!file_exists($menuTypesPath)) {
|
|
||||||
if (!is_dir(dirname($menuTypesPath))) {
|
|
||||||
mkdir(dirname($menuTypesPath), 0777, true);
|
|
||||||
}
|
|
||||||
file_put_contents($menuTypesPath, json_encode($defaultMenuTypes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
||||||
return $defaultMenuTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
$menuTypes = json_decode(@file_get_contents($menuTypesPath), true);
|
|
||||||
return (is_array($menuTypes) && count($menuTypes) > 0) ? $menuTypes : $defaultMenuTypes;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function blank_menu() {
|
function blank_menu() {
|
||||||
@@ -113,43 +80,25 @@ switch ($action) {
|
|||||||
case "get_menu_types":
|
case "get_menu_types":
|
||||||
handle_get_menu_types();
|
handle_get_menu_types();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "get_menu":
|
case "get_menu":
|
||||||
handle_get_menu();
|
handle_get_menu();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "save_menu":
|
case "save_menu":
|
||||||
if (!$canEdit) {
|
if (!$canEdit) { http_response_code(403); die(json_encode(["error" => "Insufficient permissions to edit", "code" => "FORBIDDEN"])); }
|
||||||
http_response_code(403);
|
|
||||||
die(json_encode(["error" => "Insufficient permissions to edit", "code" => "FORBIDDEN"]));
|
|
||||||
}
|
|
||||||
handle_save_menu();
|
handle_save_menu();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "add_menu_type":
|
case "add_menu_type":
|
||||||
if (!$canEdit) {
|
if (!$canEdit) { http_response_code(403); die(json_encode(["error" => "Insufficient permissions to edit", "code" => "FORBIDDEN"])); }
|
||||||
http_response_code(403);
|
|
||||||
die(json_encode(["error" => "Insufficient permissions to edit", "code" => "FORBIDDEN"]));
|
|
||||||
}
|
|
||||||
handle_add_menu_type();
|
handle_add_menu_type();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "delete_menu_type":
|
case "delete_menu_type":
|
||||||
if (!$canEdit) {
|
if (!$canEdit) { http_response_code(403); die(json_encode(["error" => "Insufficient permissions to edit", "code" => "FORBIDDEN"])); }
|
||||||
http_response_code(403);
|
|
||||||
die(json_encode(["error" => "Insufficient permissions to edit", "code" => "FORBIDDEN"]));
|
|
||||||
}
|
|
||||||
handle_delete_menu_type();
|
handle_delete_menu_type();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case "rename_menu_type":
|
case "rename_menu_type":
|
||||||
if (!$canEdit) {
|
if (!$canEdit) { http_response_code(403); die(json_encode(["error" => "Insufficient permissions to edit", "code" => "FORBIDDEN"])); }
|
||||||
http_response_code(403);
|
|
||||||
die(json_encode(["error" => "Insufficient permissions to edit", "code" => "FORBIDDEN"]));
|
|
||||||
}
|
|
||||||
handle_rename_menu_type();
|
handle_rename_menu_type();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
http_response_code(400);
|
http_response_code(400);
|
||||||
die(json_encode(["error" => "Invalid action", "code" => "INVALID_ACTION"]));
|
die(json_encode(["error" => "Invalid action", "code" => "INVALID_ACTION"]));
|
||||||
@@ -157,298 +106,102 @@ switch ($action) {
|
|||||||
|
|
||||||
function handle_get_menu_types() {
|
function handle_get_menu_types() {
|
||||||
global $centro_id, $source_aulario_id;
|
global $centro_id, $source_aulario_id;
|
||||||
|
echo json_encode(["success" => true, "menu_types" => get_menu_types($centro_id, $source_aulario_id)]);
|
||||||
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
|
||||||
echo json_encode([
|
|
||||||
"success" => true,
|
|
||||||
"menu_types" => $menuTypes
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_get_menu() {
|
function handle_get_menu() {
|
||||||
global $centro_id, $source_aulario_id;
|
global $centro_id, $source_aulario_id;
|
||||||
|
|
||||||
$date = $_GET["date"] ?? date("Y-m-d");
|
$date = $_GET["date"] ?? date("Y-m-d");
|
||||||
$menuTypeId = safe_id_segment($_GET["menu"] ?? "");
|
$menuTypeId = safe_id_segment($_GET["menu"] ?? "");
|
||||||
|
|
||||||
// Validate date
|
|
||||||
$dateObj = DateTime::createFromFormat("Y-m-d", $date);
|
$dateObj = DateTime::createFromFormat("Y-m-d", $date);
|
||||||
if (!$dateObj) {
|
if (!$dateObj) { http_response_code(400); die(json_encode(["error" => "Invalid date format", "code" => "INVALID_FORMAT"])); }
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "Invalid date format", "code" => "INVALID_FORMAT"]));
|
|
||||||
}
|
|
||||||
$date = $dateObj->format("Y-m-d");
|
$date = $dateObj->format("Y-m-d");
|
||||||
|
|
||||||
// Get menu types
|
|
||||||
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
||||||
$menuTypeIds = [];
|
$menuTypeIds = array_column($menuTypes, "id");
|
||||||
foreach ($menuTypes as $t) {
|
if ($menuTypeId === "" || !in_array($menuTypeId, $menuTypeIds)) { $menuTypeId = $menuTypeIds[0] ?? "basal"; }
|
||||||
if (!empty($t["id"])) {
|
|
||||||
$menuTypeIds[] = $t["id"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($menuTypeId === "" || !in_array($menuTypeId, $menuTypeIds)) {
|
|
||||||
$menuTypeId = $menuTypeIds[0] ?? "basal";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get menu data
|
|
||||||
$ym = $dateObj->format("Y-m");
|
$ym = $dateObj->format("Y-m");
|
||||||
$day = $dateObj->format("d");
|
$day = $dateObj->format("d");
|
||||||
$baseDir = comedor_day_base_dir($centro_id, $source_aulario_id, $ym, $day);
|
$menuData = ["date" => $date, "menus" => []];
|
||||||
if ($baseDir === null) {
|
$existing = db_get_comedor_entry($centro_id, $source_aulario_id, $ym, $day);
|
||||||
http_response_code(400);
|
if (!empty($existing)) { $menuData = array_merge($menuData, $existing); }
|
||||||
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
|
if (!isset($menuData["menus"][$menuTypeId])) { $menuData["menus"][$menuTypeId] = blank_menu(); }
|
||||||
}
|
echo json_encode(["success" => true, "date" => $date, "menu_type" => $menuTypeId, "menu_types" => $menuTypes, "menu" => $menuData["menus"][$menuTypeId]]);
|
||||||
$dataPath = "$baseDir/_datos.json";
|
|
||||||
|
|
||||||
$menuData = [
|
|
||||||
"date" => $date,
|
|
||||||
"menus" => []
|
|
||||||
];
|
|
||||||
|
|
||||||
if (file_exists($dataPath)) {
|
|
||||||
$existing = json_decode(file_get_contents($dataPath), true);
|
|
||||||
if (is_array($existing)) {
|
|
||||||
$menuData = array_merge($menuData, $existing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($menuData["menus"][$menuTypeId])) {
|
|
||||||
$menuData["menus"][$menuTypeId] = blank_menu();
|
|
||||||
}
|
|
||||||
|
|
||||||
$menuForType = $menuData["menus"][$menuTypeId];
|
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
"success" => true,
|
|
||||||
"date" => $date,
|
|
||||||
"menu_type" => $menuTypeId,
|
|
||||||
"menu_types" => $menuTypes,
|
|
||||||
"menu" => $menuForType
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_save_menu() {
|
function handle_save_menu() {
|
||||||
global $centro_id, $source_aulario_id;
|
global $centro_id, $source_aulario_id;
|
||||||
|
$input = json_decode(file_get_contents("php://input"), true) ?: $_POST;
|
||||||
// Parse JSON body
|
|
||||||
$input = json_decode(file_get_contents("php://input"), true);
|
|
||||||
if (!$input) {
|
|
||||||
$input = $_POST;
|
|
||||||
}
|
|
||||||
|
|
||||||
$date = $input["date"] ?? date("Y-m-d");
|
$date = $input["date"] ?? date("Y-m-d");
|
||||||
$menuTypeId = safe_id_segment($input["menu_type"] ?? "");
|
$menuTypeId = safe_id_segment($input["menu_type"] ?? "");
|
||||||
$plates = $input["plates"] ?? [];
|
$plates = $input["plates"] ?? [];
|
||||||
|
|
||||||
// Validate date
|
|
||||||
$dateObj = DateTime::createFromFormat("Y-m-d", $date);
|
$dateObj = DateTime::createFromFormat("Y-m-d", $date);
|
||||||
if (!$dateObj) {
|
if (!$dateObj) { http_response_code(400); die(json_encode(["error" => "Invalid date format", "code" => "INVALID_FORMAT"])); }
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "Invalid date format", "code" => "INVALID_FORMAT"]));
|
|
||||||
}
|
|
||||||
$date = $dateObj->format("Y-m-d");
|
$date = $dateObj->format("Y-m-d");
|
||||||
|
|
||||||
// Validate menu type
|
|
||||||
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
||||||
$validMenuTypeIds = [];
|
$validMenuTypeIds = array_column($menuTypes, "id");
|
||||||
foreach ($menuTypes as $t) {
|
if (!in_array($menuTypeId, $validMenuTypeIds)) { http_response_code(400); die(json_encode(["error" => "Invalid menu type", "code" => "INVALID_MENU_TYPE"])); }
|
||||||
if (!empty($t["id"])) {
|
|
||||||
$validMenuTypeIds[] = $t["id"];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!in_array($menuTypeId, $validMenuTypeIds)) {
|
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "Invalid menu type", "code" => "INVALID_MENU_TYPE"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get existing menu data
|
|
||||||
$ym = $dateObj->format("Y-m");
|
$ym = $dateObj->format("Y-m");
|
||||||
$day = $dateObj->format("d");
|
$day = $dateObj->format("d");
|
||||||
$baseDir = comedor_day_base_dir($centro_id, $source_aulario_id, $ym, $day);
|
$menuData = ["date" => $date, "menus" => []];
|
||||||
if ($baseDir === null) {
|
$existing = db_get_comedor_entry($centro_id, $source_aulario_id, $ym, $day);
|
||||||
http_response_code(400);
|
if (!empty($existing)) { $menuData = array_merge($menuData, $existing); }
|
||||||
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
|
if (!isset($menuData["menus"][$menuTypeId])) { $menuData["menus"][$menuTypeId] = blank_menu(); }
|
||||||
}
|
foreach (["primero", "segundo", "postre"] as $plateKey) {
|
||||||
$dataPath = "$baseDir/_datos.json";
|
|
||||||
|
|
||||||
$menuData = [
|
|
||||||
"date" => $date,
|
|
||||||
"menus" => []
|
|
||||||
];
|
|
||||||
|
|
||||||
if (file_exists($dataPath)) {
|
|
||||||
$existing = json_decode(file_get_contents($dataPath), true);
|
|
||||||
if (is_array($existing)) {
|
|
||||||
$menuData = array_merge($menuData, $existing);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!isset($menuData["menus"][$menuTypeId])) {
|
|
||||||
$menuData["menus"][$menuTypeId] = blank_menu();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update plates
|
|
||||||
$validPlates = ["primero", "segundo", "postre"];
|
|
||||||
foreach ($validPlates as $plateKey) {
|
|
||||||
if (isset($plates[$plateKey])) {
|
|
||||||
if (isset($plates[$plateKey]["name"])) {
|
if (isset($plates[$plateKey]["name"])) {
|
||||||
$menuData["menus"][$menuTypeId]["plates"][$plateKey]["name"] = trim($plates[$plateKey]["name"]);
|
$menuData["menus"][$menuTypeId]["plates"][$plateKey]["name"] = trim($plates[$plateKey]["name"]);
|
||||||
}
|
}
|
||||||
// Note: pictogram upload not supported via JSON API - use form-data instead
|
|
||||||
}
|
}
|
||||||
}
|
db_set_comedor_entry($centro_id, $source_aulario_id, $ym, $day, $menuData);
|
||||||
|
echo json_encode(["success" => true, "date" => $date, "menu_type" => $menuTypeId, "menu" => $menuData["menus"][$menuTypeId]]);
|
||||||
// Save menu
|
|
||||||
if (!is_dir($baseDir)) {
|
|
||||||
mkdir($baseDir, 0777, true);
|
|
||||||
}
|
|
||||||
file_put_contents($dataPath, json_encode($menuData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
"success" => true,
|
|
||||||
"date" => $date,
|
|
||||||
"menu_type" => $menuTypeId,
|
|
||||||
"menu" => $menuData["menus"][$menuTypeId]
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_add_menu_type() {
|
function handle_add_menu_type() {
|
||||||
global $centro_id, $source_aulario_id;
|
global $centro_id, $source_aulario_id;
|
||||||
|
$input = json_decode(file_get_contents("php://input"), true) ?: $_POST;
|
||||||
$input = json_decode(file_get_contents("php://input"), true);
|
|
||||||
if (!$input) {
|
|
||||||
$input = $_POST;
|
|
||||||
}
|
|
||||||
|
|
||||||
$newId = safe_id_segment(strtolower(trim($input["id"] ?? "")));
|
$newId = safe_id_segment(strtolower(trim($input["id"] ?? "")));
|
||||||
$newLabel = trim($input["label"] ?? "");
|
$newLabel = trim($input["label"] ?? "");
|
||||||
$newColor = trim($input["color"] ?? "#0d6efd");
|
$newColor = trim($input["color"] ?? "#0d6efd");
|
||||||
|
if ($newId === "" || $newLabel === "") { http_response_code(400); die(json_encode(["error" => "id and label are required", "code" => "MISSING_PARAM"])); }
|
||||||
if ($newId === "" || $newLabel === "") {
|
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "id and label are required", "code" => "MISSING_PARAM"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
$menuTypesPath = menu_types_path($centro_id, $source_aulario_id);
|
|
||||||
if ($menuTypesPath === null) {
|
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
|
|
||||||
}
|
|
||||||
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
||||||
|
|
||||||
// Check if already exists
|
|
||||||
foreach ($menuTypes as $t) {
|
foreach ($menuTypes as $t) {
|
||||||
if (($t["id"] ?? "") === $newId) {
|
if (($t["id"] ?? "") === $newId) { http_response_code(400); die(json_encode(["error" => "Menu type already exists", "code" => "DUPLICATE"])); }
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "Menu type already exists", "code" => "DUPLICATE"]));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
$menuTypes[] = ["id" => $newId, "label" => $newLabel, "color" => $newColor];
|
$menuTypes[] = ["id" => $newId, "label" => $newLabel, "color" => $newColor];
|
||||||
file_put_contents($menuTypesPath, json_encode($menuTypes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
db_set_comedor_menu_types($centro_id, $source_aulario_id, $menuTypes);
|
||||||
|
echo json_encode(["success" => true, "menu_type" => ["id" => $newId, "label" => $newLabel, "color" => $newColor], "message" => "Menu type added successfully"]);
|
||||||
echo json_encode([
|
|
||||||
"success" => true,
|
|
||||||
"menu_type" => ["id" => $newId, "label" => $newLabel, "color" => $newColor],
|
|
||||||
"message" => "Menu type added successfully"
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_delete_menu_type() {
|
function handle_delete_menu_type() {
|
||||||
global $centro_id, $source_aulario_id;
|
global $centro_id, $source_aulario_id;
|
||||||
|
$input = json_decode(file_get_contents("php://input"), true) ?: $_POST;
|
||||||
$input = json_decode(file_get_contents("php://input"), true);
|
|
||||||
if (!$input) {
|
|
||||||
$input = $_POST;
|
|
||||||
}
|
|
||||||
|
|
||||||
$deleteId = safe_id_segment(trim($input["id"] ?? ""));
|
$deleteId = safe_id_segment(trim($input["id"] ?? ""));
|
||||||
|
if ($deleteId === "") { http_response_code(400); die(json_encode(["error" => "id is required", "code" => "MISSING_PARAM"])); }
|
||||||
if ($deleteId === "") {
|
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "id is required", "code" => "MISSING_PARAM"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
$menuTypesPath = menu_types_path($centro_id, $source_aulario_id);
|
|
||||||
if ($menuTypesPath === null) {
|
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
|
|
||||||
}
|
|
||||||
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
||||||
|
$newMenuTypes = array_values(array_filter($menuTypes, fn($t) => ($t["id"] ?? "") !== $deleteId));
|
||||||
$deleted = false;
|
if (count($newMenuTypes) === count($menuTypes)) { http_response_code(404); die(json_encode(["error" => "Menu type not found", "code" => "NOT_FOUND"])); }
|
||||||
$newMenuTypes = [];
|
db_set_comedor_menu_types($centro_id, $source_aulario_id, $newMenuTypes);
|
||||||
foreach ($menuTypes as $t) {
|
echo json_encode(["success" => true, "message" => "Menu type deleted successfully"]);
|
||||||
if (($t["id"] ?? "") === $deleteId) {
|
|
||||||
$deleted = true;
|
|
||||||
} else {
|
|
||||||
$newMenuTypes[] = $t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$deleted) {
|
|
||||||
http_response_code(404);
|
|
||||||
die(json_encode(["error" => "Menu type not found", "code" => "NOT_FOUND"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
file_put_contents($menuTypesPath, json_encode($newMenuTypes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
"success" => true,
|
|
||||||
"message" => "Menu type deleted successfully"
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function handle_rename_menu_type() {
|
function handle_rename_menu_type() {
|
||||||
global $centro_id, $source_aulario_id;
|
global $centro_id, $source_aulario_id;
|
||||||
|
$input = json_decode(file_get_contents("php://input"), true) ?: $_POST;
|
||||||
$input = json_decode(file_get_contents("php://input"), true);
|
|
||||||
if (!$input) {
|
|
||||||
$input = $_POST;
|
|
||||||
}
|
|
||||||
|
|
||||||
$renameId = safe_id_segment(trim($input["id"] ?? ""));
|
$renameId = safe_id_segment(trim($input["id"] ?? ""));
|
||||||
$newLabel = trim($input["label"] ?? "");
|
$newLabel = trim($input["label"] ?? "");
|
||||||
$newColor = trim($input["color"] ?? "");
|
$newColor = trim($input["color"] ?? "");
|
||||||
|
if ($renameId === "" || $newLabel === "") { http_response_code(400); die(json_encode(["error" => "id and label are required", "code" => "MISSING_PARAM"])); }
|
||||||
if ($renameId === "" || $newLabel === "") {
|
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "id and label are required", "code" => "MISSING_PARAM"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
$menuTypesPath = menu_types_path($centro_id, $source_aulario_id);
|
|
||||||
if ($menuTypesPath === null) {
|
|
||||||
http_response_code(400);
|
|
||||||
die(json_encode(["error" => "Invalid path parameters", "code" => "INVALID_PATH"]));
|
|
||||||
}
|
|
||||||
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
$menuTypes = get_menu_types($centro_id, $source_aulario_id);
|
||||||
|
|
||||||
$found = false;
|
$found = false;
|
||||||
foreach ($menuTypes as &$t) {
|
foreach ($menuTypes as &$t) {
|
||||||
if (($t["id"] ?? "") === $renameId) {
|
if (($t["id"] ?? "") === $renameId) {
|
||||||
$t["label"] = $newLabel;
|
$t["label"] = $newLabel;
|
||||||
if ($newColor !== "") {
|
if ($newColor !== "") { $t["color"] = $newColor; }
|
||||||
$t["color"] = $newColor;
|
|
||||||
}
|
|
||||||
$found = true;
|
$found = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
unset($t);
|
unset($t);
|
||||||
|
if (!$found) { http_response_code(404); die(json_encode(["error" => "Menu type not found", "code" => "NOT_FOUND"])); }
|
||||||
if (!$found) {
|
db_set_comedor_menu_types($centro_id, $source_aulario_id, $menuTypes);
|
||||||
http_response_code(404);
|
echo json_encode(["success" => true, "message" => "Menu type renamed successfully"]);
|
||||||
die(json_encode(["error" => "Menu type not found", "code" => "NOT_FOUND"]));
|
|
||||||
}
|
|
||||||
|
|
||||||
file_put_contents($menuTypesPath, json_encode($menuTypes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
||||||
|
|
||||||
echo json_encode([
|
|
||||||
"success" => true,
|
|
||||||
"menu_type" => ["id" => $renameId, "label" => $newLabel, "color" => $newColor],
|
|
||||||
"message" => "Menu type renamed successfully"
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
$aulario_id = safe_id_segment($_GET["id"] ?? "");
|
$aulario_id = safe_id_segment($_GET["id"] ?? "");
|
||||||
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
|
$centro_id = safe_centro_id($_SESSION["auth_data"]["entreaulas"]["centro"] ?? "");
|
||||||
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
|
$aulario = db_get_aulario($centro_id, $aulario_id);
|
||||||
$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null;
|
|
||||||
|
|
||||||
if (!$aulario || !is_array($aulario)) {
|
if (!$aulario || !is_array($aulario)) {
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
if (in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? []) === false) {
|
if (in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? []) === false) {
|
||||||
header("HTTP/1.1 403 Forbidden");
|
header("HTTP/1.1 403 Forbidden");
|
||||||
@@ -22,38 +23,31 @@ if ($aulario_id === "" || $centro_id === "") {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
|
$aulario = db_get_aulario($centro_id, $aulario_id);
|
||||||
$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null;
|
|
||||||
|
|
||||||
// Check if this aulario shares comedor data from another aulario
|
// Check if this aulario shares comedor data from another aulario
|
||||||
$source_aulario_id = $aulario_id; // Default to current aulario
|
$source_aulario_id = $aulario_id;
|
||||||
$is_shared = false;
|
$is_shared = false;
|
||||||
if ($aulario && !empty($aulario["shared_comedor_from"])) {
|
if ($aulario && !empty($aulario["shared_comedor_from"])) {
|
||||||
$shared_from = safe_id_segment($aulario["shared_comedor_from"]);
|
$shared_from = safe_id_segment($aulario["shared_comedor_from"]);
|
||||||
$shared_aulario_path = safe_aulario_config_path($centro_id, $shared_from);
|
$shared_aulario = db_get_aulario($centro_id, $shared_from);
|
||||||
if ($shared_aulario_path && file_exists($shared_aulario_path)) {
|
if ($shared_aulario) {
|
||||||
$source_aulario_id = $shared_from;
|
$source_aulario_id = $shared_from;
|
||||||
$source_aulario_name = json_decode(file_get_contents($shared_aulario_path), true)["name"] ?? $shared_from;
|
$source_aulario_name = $shared_aulario["name"] ?? $shared_from;
|
||||||
$is_shared = true;
|
$is_shared = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$menuTypesPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json";
|
|
||||||
$defaultMenuTypes = [
|
$defaultMenuTypes = [
|
||||||
["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"],
|
["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"],
|
||||||
["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"],
|
["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"],
|
||||||
["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"],
|
["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"],
|
||||||
];
|
];
|
||||||
if (!file_exists($menuTypesPath)) {
|
|
||||||
if (!is_dir(dirname($menuTypesPath))) {
|
|
||||||
mkdir(dirname($menuTypesPath), 0777, true);
|
|
||||||
}
|
|
||||||
file_put_contents($menuTypesPath, json_encode($defaultMenuTypes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
||||||
}
|
|
||||||
|
|
||||||
$menuTypes = json_decode(@file_get_contents($menuTypesPath), true);
|
$menuTypes = db_get_comedor_menu_types($centro_id, $source_aulario_id);
|
||||||
if (!is_array($menuTypes) || count($menuTypes) === 0) {
|
if (!is_array($menuTypes) || count($menuTypes) === 0) {
|
||||||
$menuTypes = $defaultMenuTypes;
|
$menuTypes = $defaultMenuTypes;
|
||||||
|
db_set_comedor_menu_types($centro_id, $source_aulario_id, $menuTypes);
|
||||||
}
|
}
|
||||||
|
|
||||||
$menuTypeIds = [];
|
$menuTypeIds = [];
|
||||||
@@ -73,8 +67,7 @@ if (!in_array($menuTypeId, $menuTypeIds, true)) {
|
|||||||
|
|
||||||
$ym = $dateObj->format("Y-m");
|
$ym = $dateObj->format("Y-m");
|
||||||
$day = $dateObj->format("d");
|
$day = $dateObj->format("d");
|
||||||
$baseDir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day";
|
|
||||||
$dataPath = "$baseDir/_datos.json";
|
|
||||||
|
|
||||||
function blank_menu()
|
function blank_menu()
|
||||||
{
|
{
|
||||||
@@ -91,12 +84,10 @@ $menuData = [
|
|||||||
"date" => $date,
|
"date" => $date,
|
||||||
"menus" => []
|
"menus" => []
|
||||||
];
|
];
|
||||||
if (file_exists($dataPath)) {
|
$existing = db_get_comedor_entry($centro_id, $source_aulario_id, $ym, $day);
|
||||||
$existing = json_decode(file_get_contents($dataPath), true);
|
if (is_array($existing) && !empty($existing)) {
|
||||||
if (is_array($existing)) {
|
|
||||||
$menuData = array_merge($menuData, $existing);
|
$menuData = array_merge($menuData, $existing);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if (!isset($menuData["menus"][$menuTypeId])) {
|
if (!isset($menuData["menus"][$menuTypeId])) {
|
||||||
$menuData["menus"][$menuTypeId] = blank_menu();
|
$menuData["menus"][$menuTypeId] = blank_menu();
|
||||||
}
|
}
|
||||||
@@ -251,14 +242,11 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && $canEdit) {
|
|||||||
if ($newId !== "" && $newLabel !== "") {
|
if ($newId !== "" && $newLabel !== "") {
|
||||||
$exists = false;
|
$exists = false;
|
||||||
foreach ($menuTypes as $t) {
|
foreach ($menuTypes as $t) {
|
||||||
if (($t["id"] ?? "") === $newId) {
|
if (($t["id"] ?? "") === $newId) { $exists = true; break; }
|
||||||
$exists = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (!$exists) {
|
if (!$exists) {
|
||||||
$menuTypes[] = ["id" => $newId, "label" => $newLabel, "color" => $newColor];
|
$menuTypes[] = ["id" => $newId, "label" => $newLabel, "color" => $newColor];
|
||||||
file_put_contents($menuTypesPath, json_encode($menuTypes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
db_set_comedor_menu_types($centro_id, $source_aulario_id, $menuTypes);
|
||||||
header("Location: /entreaulas/comedor.php?aulario=" . urlencode($aulario_id) . "&date=" . urlencode($date) . "&menu=" . urlencode($newId));
|
header("Location: /entreaulas/comedor.php?aulario=" . urlencode($aulario_id) . "&date=" . urlencode($date) . "&menu=" . urlencode($newId));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@@ -268,19 +256,10 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && $canEdit) {
|
|||||||
if ($action === "delete_type") {
|
if ($action === "delete_type") {
|
||||||
$deleteId = safe_id_segment(trim($_POST["delete_type_id"] ?? ""));
|
$deleteId = safe_id_segment(trim($_POST["delete_type_id"] ?? ""));
|
||||||
if ($deleteId !== "") {
|
if ($deleteId !== "") {
|
||||||
$deleted = false;
|
$newMenuTypes = array_values(array_filter($menuTypes, fn($t) => ($t["id"] ?? "") !== $deleteId));
|
||||||
$newMenuTypes = [];
|
if (count($newMenuTypes) !== count($menuTypes)) {
|
||||||
foreach ($menuTypes as $t) {
|
|
||||||
if (($t["id"] ?? "") === $deleteId) {
|
|
||||||
$deleted = true;
|
|
||||||
} else {
|
|
||||||
$newMenuTypes[] = $t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ($deleted) {
|
|
||||||
$menuTypes = $newMenuTypes;
|
$menuTypes = $newMenuTypes;
|
||||||
file_put_contents($menuTypesPath, json_encode($menuTypes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
db_set_comedor_menu_types($centro_id, $source_aulario_id, $menuTypes);
|
||||||
// Redirect to the first available menu type or default
|
|
||||||
$redirectMenuId = !empty($menuTypes) ? $menuTypes[0]["id"] : "basal";
|
$redirectMenuId = !empty($menuTypes) ? $menuTypes[0]["id"] : "basal";
|
||||||
header("Location: /entreaulas/comedor.php?aulario=" . urlencode($aulario_id) . "&date=" . urlencode($date) . "&menu=" . urlencode($redirectMenuId));
|
header("Location: /entreaulas/comedor.php?aulario=" . urlencode($aulario_id) . "&date=" . urlencode($date) . "&menu=" . urlencode($redirectMenuId));
|
||||||
exit;
|
exit;
|
||||||
@@ -296,15 +275,12 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && $canEdit) {
|
|||||||
foreach ($menuTypes as &$t) {
|
foreach ($menuTypes as &$t) {
|
||||||
if (($t["id"] ?? "") === $renameId) {
|
if (($t["id"] ?? "") === $renameId) {
|
||||||
$t["label"] = $newLabel;
|
$t["label"] = $newLabel;
|
||||||
if ($newColor !== "") {
|
if ($newColor !== "") { $t["color"] = $newColor; }
|
||||||
$t["color"] = $newColor;
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Clean up the reference to avoid accidental usage after the loop
|
|
||||||
unset($t);
|
unset($t);
|
||||||
file_put_contents($menuTypesPath, json_encode($menuTypes, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
db_set_comedor_menu_types($centro_id, $source_aulario_id, $menuTypes);
|
||||||
header("Location: /entreaulas/comedor.php?aulario=" . urlencode($aulario_id) . "&date=" . urlencode($date) . "&menu=" . urlencode($renameId));
|
header("Location: /entreaulas/comedor.php?aulario=" . urlencode($aulario_id) . "&date=" . urlencode($date) . "&menu=" . urlencode($renameId));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@@ -316,23 +292,19 @@ if ($_SERVER["REQUEST_METHOD"] === "POST" && $canEdit) {
|
|||||||
$menuData["menus"][$menuTypeId] = blank_menu();
|
$menuData["menus"][$menuTypeId] = blank_menu();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Pictogram images still stored on filesystem in Comedor dir
|
||||||
|
$baseDir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day";
|
||||||
$plates = ["primero", "segundo", "postre"];
|
$plates = ["primero", "segundo", "postre"];
|
||||||
foreach ($plates as $plate) {
|
foreach ($plates as $plate) {
|
||||||
$name = trim($_POST["name_" . $plate] ?? "");
|
$name = trim($_POST["name_" . $plate] ?? "");
|
||||||
$menuData["menus"][$menuTypeId]["plates"][$plate]["name"] = $name;
|
$menuData["menus"][$menuTypeId]["plates"][$plate]["name"] = $name;
|
||||||
|
|
||||||
$pictUpload = handle_image_upload("pictogram_file_" . $plate, $menuTypeId . "_" . $plate . "_pict", $baseDir, $uploadErrors);
|
$pictUpload = handle_image_upload("pictogram_file_" . $plate, $menuTypeId . "_" . $plate . "_pict", $baseDir, $uploadErrors);
|
||||||
|
|
||||||
if ($pictUpload !== null) {
|
if ($pictUpload !== null) {
|
||||||
$menuData["menus"][$menuTypeId]["plates"][$plate]["pictogram"] = $pictUpload;
|
$menuData["menus"][$menuTypeId]["plates"][$plate]["pictogram"] = $pictUpload;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!is_dir($baseDir)) {
|
db_set_comedor_entry($centro_id, $source_aulario_id, $ym, $day, $menuData);
|
||||||
mkdir($baseDir, 0777, true);
|
|
||||||
}
|
|
||||||
file_put_contents($dataPath, json_encode($menuData, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
||||||
$saveNotice = "Menú guardado correctamente.";
|
$saveNotice = "Menú guardado correctamente.";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -359,11 +331,10 @@ foreach ($userAulas as $aulaId) {
|
|||||||
if ($aulaIdSafe === "") {
|
if ($aulaIdSafe === "") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$aulaPath = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulaIdSafe.json";
|
$aulaData = db_get_aulario($centro_id, $aulaIdSafe);
|
||||||
$aulaData = file_exists($aulaPath) ? json_decode(file_get_contents($aulaPath), true) : null;
|
|
||||||
$aulaOptions[] = [
|
$aulaOptions[] = [
|
||||||
"id" => $aulaIdSafe,
|
"id" => $aulaIdSafe,
|
||||||
"name" => $aulaData["name"] ?? $aulaIdSafe
|
"name" => $aulaData["name"] ?? $aulaIdSafe,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
|
|||||||
25
public_html/aulatek/index.php
Executable file → Normal file
25
public_html/aulatek/index.php
Executable file → Normal file
@@ -2,30 +2,29 @@
|
|||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">¡Hola, <?php echo $_SESSION["auth_data"]["display_name"];?>!</h1>
|
<h1 class="card-title">¡Hola, <?php echo htmlspecialchars($_SESSION["auth_data"]["display_name"]); ?>!</h1>
|
||||||
<span>
|
<span>
|
||||||
Bienvenidx a la plataforma de gestión de aularios conectados. Desde aquí podrás administrar los aularios asociados a tu cuenta.
|
Bienvenidx a la plataforma de gestión de aularios conectados. Desde aquí podrás administrar los aularios asociados a tu cuenta.
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="grid">
|
<div id="grid">
|
||||||
<?php $user_data = $_SESSION["auth_data"];
|
<?php
|
||||||
|
$user_data = $_SESSION["auth_data"];
|
||||||
$centro_id = safe_centro_id($user_data["entreaulas"]["centro"] ?? "");
|
$centro_id = safe_centro_id($user_data["entreaulas"]["centro"] ?? "");
|
||||||
foreach ($user_data["entreaulas"]["aulas"] as $aulario_id) {
|
$user_aulas = $user_data["entreaulas"]["aulas"] ?? [];
|
||||||
|
foreach ($user_aulas as $aulario_id) {
|
||||||
$aulario_id = safe_id_segment($aulario_id);
|
$aulario_id = safe_id_segment($aulario_id);
|
||||||
if ($aulario_id === "") {
|
if ($aulario_id === "") {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
|
$aulario = db_get_aulario($centro_id, $aulario_id);
|
||||||
if (!$aulario_path || !file_exists($aulario_path)) {
|
if (!$aulario) {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$aulario = json_decode(file_get_contents($aulario_path), true);
|
|
||||||
if (!is_array($aulario)) {
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$aulario_name = $aulario["name"] ?? $aulario_id;
|
$aulario_name = $aulario["name"] ?? $aulario_id;
|
||||||
@@ -61,7 +60,6 @@ require_once "../_incl/tools.security.php";
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
var msnry = new Masonry('#grid', {
|
var msnry = new Masonry('#grid', {
|
||||||
"columnWidth": 250,
|
"columnWidth": 250,
|
||||||
@@ -69,10 +67,7 @@ require_once "../_incl/tools.security.php";
|
|||||||
"gutter": 10,
|
"gutter": 10,
|
||||||
"transitionDuration": 0
|
"transitionDuration": 0
|
||||||
});
|
});
|
||||||
setInterval(() => {
|
setTimeout(() => { msnry.layout() }, 250);
|
||||||
msnry.layout()
|
setInterval(() => { msnry.layout() }, 1000);
|
||||||
}, 1000);
|
|
||||||
msnry.layout()
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<?php require_once "_incl/post-body.php"; ?>
|
<?php require_once "_incl/post-body.php"; ?>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
ini_set("display_errors", "0");
|
ini_set("display_errors", "0");
|
||||||
|
|
||||||
// Funciones auxiliares para el diario
|
// Funciones auxiliares para el diario
|
||||||
@@ -784,12 +785,10 @@ switch ($view_action) {
|
|||||||
$source_aulario_id = $aulario_id;
|
$source_aulario_id = $aulario_id;
|
||||||
$is_shared = false;
|
$is_shared = false;
|
||||||
if ($aulario_id !== "" && $centro_id !== "") {
|
if ($aulario_id !== "" && $centro_id !== "") {
|
||||||
$aulario_path = safe_aulario_config_path($centro_id, $aulario_id);
|
$aulario = db_get_aulario($centro_id, $aulario_id);
|
||||||
$aulario = ($aulario_path && file_exists($aulario_path)) ? json_decode(file_get_contents($aulario_path), true) : null;
|
|
||||||
if ($aulario && !empty($aulario["shared_comedor_from"])) {
|
if ($aulario && !empty($aulario["shared_comedor_from"])) {
|
||||||
$shared_from = safe_id_segment($aulario["shared_comedor_from"]);
|
$shared_from = safe_id_segment($aulario["shared_comedor_from"]);
|
||||||
$shared_aulario_path = safe_aulario_config_path($centro_id, $shared_from);
|
if (db_get_aulario($centro_id, $shared_from)) {
|
||||||
if ($shared_aulario_path && file_exists($shared_aulario_path)) {
|
|
||||||
$source_aulario_id = $shared_from;
|
$source_aulario_id = $shared_from;
|
||||||
$is_shared = true;
|
$is_shared = true;
|
||||||
}
|
}
|
||||||
@@ -800,13 +799,12 @@ switch ($view_action) {
|
|||||||
$dateObj = DateTime::createFromFormat("Y-m-d", $dateParam) ?: new DateTime();
|
$dateObj = DateTime::createFromFormat("Y-m-d", $dateParam) ?: new DateTime();
|
||||||
$date = $dateObj->format("Y-m-d");
|
$date = $dateObj->format("Y-m-d");
|
||||||
|
|
||||||
$menuTypesPath = ($centro_id !== '' && $source_aulario_id !== '') ? "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor-MenuTypes.json" : "";
|
|
||||||
$defaultMenuTypes = [
|
$defaultMenuTypes = [
|
||||||
["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"],
|
["id" => "basal", "label" => "Menú basal", "color" => "#0d6efd"],
|
||||||
["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"],
|
["id" => "vegetariano", "label" => "Menú vegetariano", "color" => "#198754"],
|
||||||
["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"],
|
["id" => "alergias", "label" => "Menú alergias", "color" => "#dc3545"],
|
||||||
];
|
];
|
||||||
$menuTypes = ($menuTypesPath !== '' && file_exists($menuTypesPath)) ? json_decode(@file_get_contents($menuTypesPath), true) : null;
|
$menuTypes = ($centro_id !== '' && $source_aulario_id !== '') ? db_get_comedor_menu_types($centro_id, $source_aulario_id) : [];
|
||||||
if (!is_array($menuTypes) || count($menuTypes) === 0) {
|
if (!is_array($menuTypes) || count($menuTypes) === 0) {
|
||||||
$menuTypes = $defaultMenuTypes;
|
$menuTypes = $defaultMenuTypes;
|
||||||
}
|
}
|
||||||
@@ -824,15 +822,11 @@ switch ($view_action) {
|
|||||||
|
|
||||||
$ym = $dateObj->format("Y-m");
|
$ym = $dateObj->format("Y-m");
|
||||||
$day = $dateObj->format("d");
|
$day = $dateObj->format("d");
|
||||||
$dataPath = ($centro_id !== '' && $source_aulario_id !== '') ? "/DATA/entreaulas/Centros/$centro_id/Aularios/$source_aulario_id/Comedor/$ym/$day/_datos.json" : "";
|
|
||||||
|
|
||||||
$menuData = [
|
$menuData = ["date" => $date, "menus" => []];
|
||||||
"date" => $date,
|
if ($centro_id !== '' && $source_aulario_id !== '') {
|
||||||
"menus" => []
|
$existing = db_get_comedor_entry($centro_id, $source_aulario_id, $ym, $day);
|
||||||
];
|
if (!empty($existing)) {
|
||||||
if ($dataPath !== '' && file_exists($dataPath)) {
|
|
||||||
$existing = json_decode(file_get_contents($dataPath), true);
|
|
||||||
if (is_array($existing)) {
|
|
||||||
$menuData = array_merge($menuData, $existing);
|
$menuData = array_merge($menuData, $existing);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
if (in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? []) === false) {
|
if (in_array("entreaulas:docente", $_SESSION["auth_data"]["permissions"] ?? []) === false) {
|
||||||
header("HTTP/1.1 403 Forbidden");
|
header("HTTP/1.1 403 Forbidden");
|
||||||
@@ -23,8 +24,7 @@ if ($aulario_id === "" || $centro_id === "") {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$aulario_path = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
|
$aulario = db_get_aulario($centro_id, $aulario_id);
|
||||||
$aulario = file_exists($aulario_path) ? json_decode(file_get_contents($aulario_path), true) : null;
|
|
||||||
|
|
||||||
$proyectos_dir = "/DATA/entreaulas/Centros/$centro_id/Proyectos";
|
$proyectos_dir = "/DATA/entreaulas/Centros/$centro_id/Proyectos";
|
||||||
if (!is_dir($proyectos_dir)) {
|
if (!is_dir($proyectos_dir)) {
|
||||||
@@ -71,16 +71,6 @@ function safe_join_file($base_dir, $filename)
|
|||||||
return rtrim($base_dir, '/') . '/' . $safe_name;
|
return rtrim($base_dir, '/') . '/' . $safe_name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function safe_aulario_config_path($centro_id, $aulario_id)
|
|
||||||
{
|
|
||||||
$safe_centro = safe_path_segment($centro_id);
|
|
||||||
$safe_aulario = safe_path_segment($aulario_id);
|
|
||||||
if ($safe_centro === '' || $safe_aulario === '') {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return "/DATA/entreaulas/Centros/$safe_centro/Aularios/$safe_aulario.json";
|
|
||||||
}
|
|
||||||
|
|
||||||
function sanitize_html($html)
|
function sanitize_html($html)
|
||||||
{
|
{
|
||||||
$html = trim($html ?? "");
|
$html = trim($html ?? "");
|
||||||
@@ -463,19 +453,14 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
$target_aulario = safe_path_segment($_POST["target_aulario"] ?? "");
|
$target_aulario = safe_path_segment($_POST["target_aulario"] ?? "");
|
||||||
|
|
||||||
if ($project_id !== "" && $target_aulario !== "" && $target_aulario !== $aulario_id) {
|
if ($project_id !== "" && $target_aulario !== "" && $target_aulario !== $aulario_id) {
|
||||||
// Only allow sharing local projects
|
|
||||||
$is_local_project = (load_project($proyectos_dir, $project_id) !== null);
|
$is_local_project = (load_project($proyectos_dir, $project_id) !== null);
|
||||||
if (!$is_local_project) {
|
if (!$is_local_project) {
|
||||||
$error = "No se puede compartir un proyecto ajeno.";
|
$error = "No se puede compartir un proyecto ajeno.";
|
||||||
} else {
|
} else {
|
||||||
$target_config_path = safe_aulario_config_path($centro_id, $target_aulario);
|
$target_config = db_get_aulario($centro_id, $target_aulario);
|
||||||
if ($target_config_path === null || !file_exists($target_config_path)) {
|
if ($target_config === null) {
|
||||||
$error = "Aulario de destino no encontrado.";
|
$error = "Aulario de destino no encontrado.";
|
||||||
} else {
|
} else {
|
||||||
$target_config = json_decode(file_get_contents($target_config_path), true);
|
|
||||||
if (!is_array($target_config)) {
|
|
||||||
$target_config = [];
|
|
||||||
}
|
|
||||||
if (!isset($target_config["linked_projects"]) || !is_array($target_config["linked_projects"])) {
|
if (!isset($target_config["linked_projects"]) || !is_array($target_config["linked_projects"])) {
|
||||||
$target_config["linked_projects"] = [];
|
$target_config["linked_projects"] = [];
|
||||||
}
|
}
|
||||||
@@ -495,12 +480,22 @@ if ($_SERVER["REQUEST_METHOD"] === "POST") {
|
|||||||
$target_config["linked_projects"][] = [
|
$target_config["linked_projects"][] = [
|
||||||
"source_aulario" => $aulario_id,
|
"source_aulario" => $aulario_id,
|
||||||
"project_id" => $project_id,
|
"project_id" => $project_id,
|
||||||
"permission" => "request_edit"
|
"permission" => "request_edit",
|
||||||
];
|
];
|
||||||
$message = "Proyecto compartido correctamente.";
|
$message = "Proyecto compartido correctamente.";
|
||||||
}
|
}
|
||||||
|
|
||||||
file_put_contents($target_config_path, json_encode($target_config, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
// Save back: build extra JSON excluding standard fields
|
||||||
|
$extra_skip = ['name', 'icon'];
|
||||||
|
$extra = [];
|
||||||
|
foreach ($target_config as $k => $v) {
|
||||||
|
if (!in_array($k, $extra_skip, true)) {
|
||||||
|
$extra[$k] = $v;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
db()->prepare(
|
||||||
|
"UPDATE aularios SET extra = ? WHERE centro_id = ? AND aulario_id = ?"
|
||||||
|
)->execute([json_encode($extra, JSON_UNESCAPED_UNICODE), $centro_id, $target_aulario]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1570,10 +1565,9 @@ $view = $current_project ? "project" : "list";
|
|||||||
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19A2.92,2.92 0 0,0 18,16.08Z" />
|
<path d="M18,16.08C17.24,16.08 16.56,16.38 16.04,16.85L8.91,12.7C8.96,12.47 9,12.24 9,12C9,11.76 8.96,11.53 8.91,11.3L15.96,7.19C16.5,7.69 17.21,8 18,8A3,3 0 0,0 21,5A3,3 0 0,0 18,2A3,3 0 0,0 15,5C15,5.24 15.04,5.47 15.09,5.7L8.04,9.81C7.5,9.31 6.79,9 6,9A3,3 0 0,0 3,12A3,3 0 0,0 6,15C6.79,15 7.5,14.69 8.04,14.19L15.16,18.34C15.11,18.55 15.08,18.77 15.08,19C15.08,20.61 16.39,21.91 18,21.91C19.61,21.91 20.92,20.61 20.92,19A2.92,2.92 0 0,0 18,16.08Z" />
|
||||||
</svg>
|
</svg>
|
||||||
<?php
|
<?php
|
||||||
$source_aulario_path = safe_aulario_config_path($centro_id, $source_aulario_for_project);
|
|
||||||
$source_aulario_name = "";
|
$source_aulario_name = "";
|
||||||
if ($source_aulario_path && file_exists($source_aulario_path)) {
|
$source_aulario_data = db_get_aulario($centro_id, $source_aulario_for_project);
|
||||||
$source_aulario_data = json_decode(file_get_contents($source_aulario_path), true);
|
if ($source_aulario_data) {
|
||||||
$source_aulario_name = $source_aulario_data["name"] ?? "";
|
$source_aulario_name = $source_aulario_data["name"] ?? "";
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
@@ -1636,28 +1630,15 @@ $view = $current_project ? "project" : "list";
|
|||||||
<?php
|
<?php
|
||||||
function list_aularios($centro_id)
|
function list_aularios($centro_id)
|
||||||
{
|
{
|
||||||
$aularios_dir = "/DATA/entreaulas/Centros/$centro_id/Aularios";
|
$aularios_db = db_get_aularios($centro_id);
|
||||||
$aularios = [];
|
$aularios = [];
|
||||||
if (is_dir($aularios_dir)) {
|
foreach ($aularios_db as $aid => $adata) {
|
||||||
$entries = scandir($aularios_dir);
|
|
||||||
foreach ($entries as $entry) {
|
|
||||||
if ($entry === "." || $entry === "..") {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
$aulario_path = "$aularios_dir/$entry";
|
|
||||||
if (is_dir($aulario_path)) {
|
|
||||||
$config_file = "$aulario_path.json";
|
|
||||||
if (file_exists($config_file)) {
|
|
||||||
$config = json_decode(file_get_contents($config_file), true);
|
|
||||||
$aularios[] = [
|
$aularios[] = [
|
||||||
"id" => $entry,
|
"id" => $aid,
|
||||||
"name" => $config["name"] ?? "Aulario Desconocido",
|
"name" => $adata["name"] ?? "Aulario Desconocido",
|
||||||
"linked_projects" => $config["linked_projects"] ?? []
|
"linked_projects" => $adata["linked_projects"] ?? [],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $aularios;
|
return $aularios;
|
||||||
}
|
}
|
||||||
$aularios = list_aularios($centro_id);
|
$aularios = list_aularios($centro_id);
|
||||||
@@ -1918,10 +1899,8 @@ $view = $current_project ? "project" : "list";
|
|||||||
<div id="grid">
|
<div id="grid">
|
||||||
<?php foreach ($pending_changes as $change):
|
<?php foreach ($pending_changes as $change):
|
||||||
$requesting_aulario = $change["requested_by_aulario"] ?? "Desconocido";
|
$requesting_aulario = $change["requested_by_aulario"] ?? "Desconocido";
|
||||||
// Get requesting aulario name
|
|
||||||
$requesting_aulario = safe_path_segment($requesting_aulario);
|
$requesting_aulario = safe_path_segment($requesting_aulario);
|
||||||
$req_aul_path = safe_aulario_config_path($centro_id, $requesting_aulario);
|
$req_aul_data = db_get_aulario($centro_id, $requesting_aulario);
|
||||||
$req_aul_data = ($req_aul_path && file_exists($req_aul_path)) ? json_decode(file_get_contents($req_aul_path), true) : null;
|
|
||||||
$req_aul_name = $req_aul_data["name"] ?? $requesting_aulario;
|
$req_aul_name = $req_aul_data["name"] ?? $requesting_aulario;
|
||||||
$req_persona_name = $change["requested_by_persona_name"] ?? "Desconocido";
|
$req_persona_name = $change["requested_by_persona_name"] ?? "Desconocido";
|
||||||
?>
|
?>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
if (!in_array('supercafe:access', $_SESSION['auth_data']['permissions'] ?? [])) {
|
if (!in_array('supercafe:access', $_SESSION['auth_data']['permissions'] ?? [])) {
|
||||||
header('HTTP/1.1 403 Forbidden');
|
header('HTTP/1.1 403 Forbidden');
|
||||||
@@ -8,22 +9,17 @@ if (!in_array('supercafe:access', $_SESSION['auth_data']['permissions'] ?? []))
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load personas from the existing Alumnos system.
|
* Load personas from the Alumnos filesystem (photos still on disk).
|
||||||
* Returns array keyed by "{aulario_id}:{alumno_name}" with
|
* Returns array keyed by "{aulario_id}:{alumno_name}".
|
||||||
* ['Nombre', 'Region' (aulario display name), 'AularioID', 'HasPhoto'] entries.
|
|
||||||
*/
|
*/
|
||||||
function sc_load_personas_from_alumnos($centro_id)
|
function sc_load_personas_from_alumnos($centro_id)
|
||||||
{
|
{
|
||||||
$aularios_path = "/DATA/entreaulas/Centros/$centro_id/Aularios";
|
$aularios = db_get_aularios($centro_id);
|
||||||
$personas = [];
|
$personas = [];
|
||||||
if (!is_dir($aularios_path)) {
|
$aularios_dir = "/DATA/entreaulas/Centros/$centro_id/Aularios";
|
||||||
return $personas;
|
foreach ($aularios as $aulario_id => $aulario_data) {
|
||||||
}
|
|
||||||
foreach (glob("$aularios_path/*.json") ?: [] as $aulario_file) {
|
|
||||||
$aulario_id = basename($aulario_file, '.json');
|
|
||||||
$aulario_data = json_decode(file_get_contents($aulario_file), true);
|
|
||||||
$aulario_name = $aulario_data['name'] ?? $aulario_id;
|
$aulario_name = $aulario_data['name'] ?? $aulario_id;
|
||||||
$alumnos_path = "$aularios_path/$aulario_id/Alumnos";
|
$alumnos_path = "$aularios_dir/$aulario_id/Alumnos";
|
||||||
if (!is_dir($alumnos_path)) {
|
if (!is_dir($alumnos_path)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -62,7 +58,6 @@ if ($centro_id === '') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
define('SC_DATA_DIR', "/DATA/entreaulas/Centros/$centro_id/SuperCafe/Comandas");
|
|
||||||
define('SC_MAX_DEBTS', 3);
|
define('SC_MAX_DEBTS', 3);
|
||||||
|
|
||||||
$estados_colores = [
|
$estados_colores = [
|
||||||
@@ -84,19 +79,14 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $can_edit) {
|
|||||||
$order_id = safe_id($_POST['order_id'] ?? '');
|
$order_id = safe_id($_POST['order_id'] ?? '');
|
||||||
$new_status = $_POST['status'] ?? '';
|
$new_status = $_POST['status'] ?? '';
|
||||||
if ($order_id !== '' && array_key_exists($new_status, $estados_colores)) {
|
if ($order_id !== '' && array_key_exists($new_status, $estados_colores)) {
|
||||||
$order_file = SC_DATA_DIR . '/' . $order_id . '.json';
|
$row = db_get_supercafe_order($centro_id, $order_id);
|
||||||
if (is_readable($order_file)) {
|
if ($row) {
|
||||||
$data = json_decode(file_get_contents($order_file), true);
|
db_upsert_supercafe_order(
|
||||||
if (is_array($data)) {
|
$centro_id, $order_id,
|
||||||
$data['Estado'] = $new_status;
|
$row['fecha'], $row['persona'], $row['comanda'], $row['notas'], $new_status
|
||||||
file_put_contents(
|
|
||||||
$order_file,
|
|
||||||
json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
|
|
||||||
LOCK_EX
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
header('Location: /entreaulas/supercafe.php');
|
header('Location: /entreaulas/supercafe.php');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
@@ -104,34 +94,30 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST' && $can_edit) {
|
|||||||
if ($action === 'delete') {
|
if ($action === 'delete') {
|
||||||
$order_id = safe_id($_POST['order_id'] ?? '');
|
$order_id = safe_id($_POST['order_id'] ?? '');
|
||||||
if ($order_id !== '') {
|
if ($order_id !== '') {
|
||||||
$order_file = SC_DATA_DIR . '/' . $order_id . '.json';
|
db()->prepare('DELETE FROM supercafe_orders WHERE centro_id = ? AND order_ref = ?')
|
||||||
if (is_file($order_file)) {
|
->execute([$centro_id, $order_id]);
|
||||||
unlink($order_file);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
header('Location: /entreaulas/supercafe.php');
|
header('Location: /entreaulas/supercafe.php');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load all orders
|
// Load all orders from DB
|
||||||
|
$db_orders = db_get_supercafe_orders($centro_id);
|
||||||
$orders = [];
|
$orders = [];
|
||||||
if (is_dir(SC_DATA_DIR)) {
|
foreach ($db_orders as $row) {
|
||||||
$files = glob(SC_DATA_DIR . '/*.json') ?: [];
|
$orders[] = [
|
||||||
foreach ($files as $file) {
|
'_id' => $row['order_ref'],
|
||||||
$data = json_decode(file_get_contents($file), true);
|
'Fecha' => $row['fecha'],
|
||||||
if (!is_array($data)) {
|
'Persona'=> $row['persona'],
|
||||||
continue;
|
'Comanda'=> $row['comanda'],
|
||||||
}
|
'Notas' => $row['notas'],
|
||||||
$data['_id'] = basename($file, '.json');
|
'Estado' => $row['estado'],
|
||||||
$orders[] = $data;
|
];
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort newest first (by Fecha desc)
|
// Sort newest first (by Fecha desc)
|
||||||
usort($orders, function ($a, $b) {
|
usort($orders, fn($a, $b) => strcmp($b['Fecha'] ?? '', $a['Fecha'] ?? ''));
|
||||||
return strcmp($b['Fecha'] ?? '', $a['Fecha'] ?? '');
|
|
||||||
});
|
|
||||||
|
|
||||||
$orders_active = array_filter($orders, fn($o) => ($o['Estado'] ?? '') !== 'Deuda');
|
$orders_active = array_filter($orders, fn($o) => ($o['Estado'] ?? '') !== 'Deuda');
|
||||||
$orders_deuda = array_filter($orders, fn($o) => ($o['Estado'] ?? '') === 'Deuda');
|
$orders_deuda = array_filter($orders, fn($o) => ($o['Estado'] ?? '') === 'Deuda');
|
||||||
@@ -139,6 +125,7 @@ $orders_deuda = array_filter($orders, fn($o) => ($o['Estado'] ?? '') === 'Deuda
|
|||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
?>
|
?>
|
||||||
|
|
||||||
|
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px;">
|
<div style="display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px;">
|
||||||
<h1 style="margin: 0;">SuperCafe – Cafetería</h1>
|
<h1 style="margin: 0;">SuperCafe – Cafetería</h1>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
if (!in_array('supercafe:edit', $_SESSION['auth_data']['permissions'] ?? [])) {
|
if (!in_array('supercafe:edit', $_SESSION['auth_data']['permissions'] ?? [])) {
|
||||||
header('HTTP/1.1 403 Forbidden');
|
header('HTTP/1.1 403 Forbidden');
|
||||||
@@ -15,41 +16,29 @@ if ($centro_id === '') {
|
|||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
|
|
||||||
$sc_base = "/DATA/entreaulas/Centros/$centro_id/SuperCafe";
|
|
||||||
define('SC_DATA_DIR', "$sc_base/Comandas");
|
|
||||||
define('SC_MAX_DEBTS', 3);
|
define('SC_MAX_DEBTS', 3);
|
||||||
|
|
||||||
$valid_statuses = ['Pedido', 'En preparación', 'Listo', 'Entregado', 'Deuda'];
|
$valid_statuses = ['Pedido', 'En preparación', 'Listo', 'Entregado', 'Deuda'];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Load personas from the existing Alumnos system (alumnos.php).
|
* Load personas from the Alumnos filesystem (photos still on disk).
|
||||||
* Returns array keyed by "{aulario_id}:{alumno_name}" with
|
* Returns array keyed by "{aulario_id}:{alumno_name}".
|
||||||
* ['Nombre', 'Region' (aulario display name), 'AularioID'] entries.
|
|
||||||
* Groups are sorted by aulario name, alumnos sorted alphabetically.
|
|
||||||
*/
|
*/
|
||||||
function sc_load_personas_from_alumnos($centro_id)
|
function sc_load_personas_from_alumnos($centro_id)
|
||||||
{
|
{
|
||||||
$aularios_path = "/DATA/entreaulas/Centros/$centro_id/Aularios";
|
$aularios = db_get_aularios($centro_id);
|
||||||
$personas = [];
|
$personas = [];
|
||||||
if (!is_dir($aularios_path)) {
|
$aularios_dir = "/DATA/entreaulas/Centros/$centro_id/Aularios";
|
||||||
return $personas;
|
foreach ($aularios as $aulario_id => $aulario_data) {
|
||||||
}
|
|
||||||
$aulario_files = glob("$aularios_path/*.json") ?: [];
|
|
||||||
foreach ($aulario_files as $aulario_file) {
|
|
||||||
$aulario_id = basename($aulario_file, '.json');
|
|
||||||
$aulario_data = json_decode(file_get_contents($aulario_file), true);
|
|
||||||
$aulario_name = $aulario_data['name'] ?? $aulario_id;
|
$aulario_name = $aulario_data['name'] ?? $aulario_id;
|
||||||
$alumnos_path = "$aularios_path/$aulario_id/Alumnos";
|
$alumnos_path = "$aularios_dir/$aulario_id/Alumnos";
|
||||||
if (!is_dir($alumnos_path)) {
|
if (!is_dir($alumnos_path)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$alumno_dirs = glob("$alumnos_path/*/", GLOB_ONLYDIR) ?: [];
|
$alumno_dirs = glob("$alumnos_path/*/", GLOB_ONLYDIR) ?: [];
|
||||||
usort($alumno_dirs, function ($a, $b) {
|
usort($alumno_dirs, fn($a, $b) => strcasecmp(basename($a), basename($b)));
|
||||||
return strcasecmp(basename($a), basename($b));
|
|
||||||
});
|
|
||||||
foreach ($alumno_dirs as $alumno_dir) {
|
foreach ($alumno_dirs as $alumno_dir) {
|
||||||
$alumno_name = basename($alumno_dir);
|
$alumno_name = basename($alumno_dir);
|
||||||
// Key uses ':' as separator; safe_id_segment chars [A-Za-z0-9_-] exclude ':'
|
|
||||||
$key = $aulario_id . ':' . $alumno_name;
|
$key = $aulario_id . ':' . $alumno_name;
|
||||||
$personas[$key] = [
|
$personas[$key] = [
|
||||||
'Nombre' => $alumno_name,
|
'Nombre' => $alumno_name,
|
||||||
@@ -62,43 +51,14 @@ function sc_load_personas_from_alumnos($centro_id)
|
|||||||
return $personas;
|
return $personas;
|
||||||
}
|
}
|
||||||
|
|
||||||
function sc_load_menu($sc_base)
|
|
||||||
{
|
|
||||||
$path = "$sc_base/Menu.json";
|
|
||||||
if (!file_exists($path)) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
$data = json_decode(file_get_contents($path), true);
|
|
||||||
return is_array($data) ? $data : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
function sc_count_debts($persona_key)
|
|
||||||
{
|
|
||||||
if (!is_dir(SC_DATA_DIR)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
$count = 0;
|
|
||||||
foreach (glob(SC_DATA_DIR . '/*.json') ?: [] as $file) {
|
|
||||||
$data = json_decode(file_get_contents($file), true);
|
|
||||||
if (is_array($data)
|
|
||||||
&& ($data['Persona'] ?? '') === $persona_key
|
|
||||||
&& ($data['Estado'] ?? '') === 'Deuda') {
|
|
||||||
$count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return $count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Determine if creating or editing
|
// Determine if creating or editing
|
||||||
$order_id = safe_id($_GET['id'] ?? '');
|
$order_id = safe_id($_GET['id'] ?? '');
|
||||||
$is_new = $order_id === '';
|
$is_new = $order_id === '';
|
||||||
if ($is_new) {
|
if ($is_new) {
|
||||||
$raw_id = uniqid('sc', true);
|
$order_id = db_next_supercafe_ref($centro_id);
|
||||||
$order_id = preg_replace('/[^a-zA-Z0-9_-]/', '', $raw_id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$order_file = SC_DATA_DIR . '/' . $order_id . '.json';
|
// Load existing order from DB (or defaults)
|
||||||
|
|
||||||
$order_data = [
|
$order_data = [
|
||||||
'Fecha' => date('Y-m-d'),
|
'Fecha' => date('Y-m-d'),
|
||||||
'Persona' => '',
|
'Persona' => '',
|
||||||
@@ -106,15 +66,21 @@ $order_data = [
|
|||||||
'Notas' => '',
|
'Notas' => '',
|
||||||
'Estado' => 'Pedido',
|
'Estado' => 'Pedido',
|
||||||
];
|
];
|
||||||
if (!$is_new && is_readable($order_file)) {
|
if (!$is_new) {
|
||||||
$existing = json_decode(file_get_contents($order_file), true);
|
$existing = db_get_supercafe_order($centro_id, $order_id);
|
||||||
if (is_array($existing)) {
|
if ($existing) {
|
||||||
$order_data = array_merge($order_data, $existing);
|
$order_data = [
|
||||||
|
'Fecha' => $existing['fecha'],
|
||||||
|
'Persona' => $existing['persona'],
|
||||||
|
'Comanda' => $existing['comanda'],
|
||||||
|
'Notas' => $existing['notas'],
|
||||||
|
'Estado' => $existing['estado'],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$personas = sc_load_personas_from_alumnos($centro_id);
|
$personas = sc_load_personas_from_alumnos($centro_id);
|
||||||
$menu = sc_load_menu($sc_base);
|
$menu = db_get_supercafe_menu($centro_id);
|
||||||
|
|
||||||
// Group personas by aulario for the optgroup picker
|
// Group personas by aulario for the optgroup picker
|
||||||
$personas_by_aulario = [];
|
$personas_by_aulario = [];
|
||||||
@@ -133,11 +99,9 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
$estado = 'Pedido';
|
$estado = 'Pedido';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Validar persona
|
|
||||||
if ($persona_key === '' || (!empty($personas) && !array_key_exists($persona_key, $personas))) {
|
if ($persona_key === '' || (!empty($personas) && !array_key_exists($persona_key, $personas))) {
|
||||||
$error = '¡Hay que elegir una persona válida!';
|
$error = '¡Hay que elegir una persona válida!';
|
||||||
} else {
|
} else {
|
||||||
// Construir comanda desde los campos de categoría visual
|
|
||||||
$comanda_parts = [];
|
$comanda_parts = [];
|
||||||
if (!empty($menu)) {
|
if (!empty($menu)) {
|
||||||
foreach ($menu as $category => $items) {
|
foreach ($menu as $category => $items) {
|
||||||
@@ -153,47 +117,27 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
$comanda_str = implode(', ', $comanda_parts);
|
$comanda_str = implode(', ', $comanda_parts);
|
||||||
|
$prev_persona = $order_data['Persona'];
|
||||||
// Comprobar deudas
|
|
||||||
$prev_persona = $order_data['Persona'] ?? '';
|
|
||||||
if ($is_new || $prev_persona !== $persona_key) {
|
if ($is_new || $prev_persona !== $persona_key) {
|
||||||
$debt_count = sc_count_debts($persona_key);
|
$debt_count = db_supercafe_count_debts($centro_id, $persona_key);
|
||||||
if ($debt_count >= SC_MAX_DEBTS) {
|
if ($debt_count >= SC_MAX_DEBTS) {
|
||||||
$error = 'Esta persona tiene ' . $debt_count . ' comandas en deuda. No se puede realizar el pedido.';
|
$error = 'Esta persona tiene ' . $debt_count . ' comandas en deuda. No se puede realizar el pedido.';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($error === '') {
|
if ($error === '') {
|
||||||
$new_data = [
|
db_upsert_supercafe_order(
|
||||||
'Fecha' => date('Y-m-d'),
|
$centro_id, $order_id,
|
||||||
'Persona' => $persona_key,
|
date('Y-m-d'), $persona_key, $comanda_str, $notas,
|
||||||
'Comanda' => $comanda_str,
|
$is_new ? 'Pedido' : $estado
|
||||||
'Notas' => $notas,
|
|
||||||
'Estado' => $is_new ? 'Pedido' : $estado,
|
|
||||||
];
|
|
||||||
|
|
||||||
if (!is_dir(SC_DATA_DIR)) {
|
|
||||||
mkdir(SC_DATA_DIR, 0755, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
$tmp = SC_DATA_DIR . '/.' . $order_id . '.tmp';
|
|
||||||
$bytes = file_put_contents(
|
|
||||||
$tmp,
|
|
||||||
json_encode($new_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE),
|
|
||||||
LOCK_EX
|
|
||||||
);
|
);
|
||||||
if ($bytes === false || !rename($tmp, $order_file)) {
|
|
||||||
@unlink($tmp);
|
|
||||||
$error = 'Error al guardar la comanda.';
|
|
||||||
} else {
|
|
||||||
header('Location: /entreaulas/supercafe.php');
|
header('Location: /entreaulas/supercafe.php');
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<h1>Comanda <code><?= htmlspecialchars($order_id) ?></code></h1>
|
<h1>Comanda <code><?= htmlspecialchars($order_id) ?></code></h1>
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "../_incl/tools.session.php";
|
require_once "../_incl/tools.session.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
ini_set("display_errors", 0);
|
ini_set("display_errors", 0);
|
||||||
$file = Sf($_GET["f"]);
|
$file = Sf($_GET["f"]);
|
||||||
$date = implode("/", array_reverse(explode("-", $file)));
|
$date = implode("/", array_reverse(explode("-", $file)));
|
||||||
$val = json_decode(file_get_contents("/DATA/club/IMG/$file/data.json"), true);
|
$val = db_get_club_event($file);
|
||||||
|
|
||||||
$fotos = glob("/DATA/club/IMG/$file/*/");
|
$fotos = glob("/DATA/club/IMG/$file/*/");
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +1,18 @@
|
|||||||
<?php
|
<?php
|
||||||
ini_set("display_errors", 0);
|
ini_set("display_errors", 0);
|
||||||
|
require_once "../_incl/db.php";
|
||||||
$file = Sf($_GET["f"]);
|
$file = Sf($_GET["f"]);
|
||||||
$date = implode("/", array_reverse(explode("-", $file)));
|
$date = implode("/", array_reverse(explode("-", $file)));
|
||||||
$val = json_decode(file_get_contents("/DATA/club/IMG/$file/data.json"), true);
|
$val = db_get_club_event($file);
|
||||||
$config = json_decode(file_get_contents("/DATA/club/config.json"), true);
|
$adminpw = db_get_config('club_adminpw', '');
|
||||||
if(strtoupper($_POST["adminpw"]) == strtoupper($config["adminpw"] ?? "")) {
|
if (strtoupper($_POST["adminpw"] ?? '') === strtoupper($adminpw) && !empty($adminpw)) {
|
||||||
$data = [
|
$data = [
|
||||||
"title" => $_POST["title"],
|
"title" => $_POST["title"],
|
||||||
"note" => $_POST["note"],
|
"note" => $_POST["note"],
|
||||||
"mapa" => [
|
"mapa" => ["url" => $_POST["mapa_url"]],
|
||||||
"url" => $_POST["mapa_url"]
|
|
||||||
]
|
|
||||||
];
|
];
|
||||||
$file = $_POST["date"];
|
$file = $_POST["date"];
|
||||||
$val = file_put_contents("/DATA/club/IMG/$file/data.json", json_encode($data, JSON_UNESCAPED_SLASHES));
|
db_set_club_event($file, $data);
|
||||||
header("Location: /club/");
|
header("Location: /club/");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?php
|
<?php
|
||||||
ini_set("display_errors", 0);
|
ini_set("display_errors", 0);
|
||||||
|
require_once "../_incl/db.php";
|
||||||
$files = glob("/DATA/club/IMG/*/");
|
$files = glob("/DATA/club/IMG/*/");
|
||||||
sort($files);
|
sort($files);
|
||||||
$files = array_reverse($files);
|
$files = array_reverse($files);
|
||||||
@@ -17,7 +18,7 @@ require_once "../_incl/pre-body.php"; ?>
|
|||||||
<?php foreach ($files as $file) {
|
<?php foreach ($files as $file) {
|
||||||
$filenam = str_replace("/", "", str_replace("/DATA/club/IMG/", "", $file));
|
$filenam = str_replace("/", "", str_replace("/DATA/club/IMG/", "", $file));
|
||||||
$date = implode("/", array_reverse(explode("-", $filenam)));
|
$date = implode("/", array_reverse(explode("-", $filenam)));
|
||||||
$val = json_decode(file_get_contents($file . "data.json"), true)
|
$val = db_get_club_event($filenam);
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<li><a class="btn btn-secondary" href="cal.php?f=<?php echo $filenam; ?>"><b><?php echo $date; ?></b></a> -
|
<li><a class="btn btn-secondary" href="cal.php?f=<?php echo $filenam; ?>"><b><?php echo $date; ?></b></a> -
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
<?php
|
<?php
|
||||||
ini_set("display_errors", 1);
|
ini_set("display_errors", 1);
|
||||||
$config = json_decode(file_get_contents("/DATA/club/config.json"), true);
|
require_once "../../_incl/db.php";
|
||||||
if (strtoupper($_GET["pw"]) != $config["uploadpw"]) {
|
$uploadpw = db_get_config('club_uploadpw', '');
|
||||||
|
if ($uploadpw === '' || strtoupper($_GET["pw"] ?? '') !== strtoupper($uploadpw)) {
|
||||||
header("HTTP/1.1 401 Unauthorized");
|
header("HTTP/1.1 401 Unauthorized");
|
||||||
die();
|
die();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
function safe_path_segment($value)
|
function safe_path_segment($value)
|
||||||
{
|
{
|
||||||
@@ -19,416 +20,213 @@ switch ($form_action) {
|
|||||||
case "delete":
|
case "delete":
|
||||||
$aulario_id = safe_path_segment(Sf($_POST["aulario_id"] ?? ""));
|
$aulario_id = safe_path_segment(Sf($_POST["aulario_id"] ?? ""));
|
||||||
$centro_id = safe_path_segment(Sf($_POST["centro_id"] ?? ""));
|
$centro_id = safe_path_segment(Sf($_POST["centro_id"] ?? ""));
|
||||||
$aulario_file = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
|
if ($aulario_id === "" || $centro_id === "") {
|
||||||
if (!file_exists($aulario_file)) {
|
die("Parámetros inválidos.");
|
||||||
die("Aulario no encontrado.");
|
|
||||||
}
|
}
|
||||||
// Remove aulario directory and contents
|
// Remove from DB
|
||||||
|
db()->prepare("DELETE FROM aularios WHERE centro_id = ? AND aulario_id = ?")
|
||||||
|
->execute([$centro_id, $aulario_id]);
|
||||||
|
// Remove comedor, diario, panel data
|
||||||
|
db()->prepare("DELETE FROM comedor_menu_types WHERE centro_id = ? AND aulario_id = ?")
|
||||||
|
->execute([$centro_id, $aulario_id]);
|
||||||
|
db()->prepare("DELETE FROM comedor_entries WHERE centro_id = ? AND aulario_id = ?")
|
||||||
|
->execute([$centro_id, $aulario_id]);
|
||||||
|
db()->prepare("DELETE FROM diario_entries WHERE centro_id = ? AND aulario_id = ?")
|
||||||
|
->execute([$centro_id, $aulario_id]);
|
||||||
|
db()->prepare("DELETE FROM panel_alumno WHERE centro_id = ? AND aulario_id = ?")
|
||||||
|
->execute([$centro_id, $aulario_id]);
|
||||||
|
// Remove filesystem directory with student photos
|
||||||
$aulario_dir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id";
|
$aulario_dir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id";
|
||||||
function rrmdir($dir) {
|
function rrmdir($dir)
|
||||||
|
{
|
||||||
if (is_dir($dir)) {
|
if (is_dir($dir)) {
|
||||||
$objects = scandir($dir);
|
foreach (scandir($dir) as $object) {
|
||||||
foreach ($objects as $object) {
|
if ($object !== "." && $object !== "..") {
|
||||||
if ($object != "." && $object != "..") {
|
$p = "$dir/$object";
|
||||||
$obj_path = $dir . "/" . $object;
|
is_dir($p) ? rrmdir($p) : unlink($p);
|
||||||
if (is_dir($obj_path)) {
|
|
||||||
rrmdir($obj_path);
|
|
||||||
} else {
|
|
||||||
unlink($obj_path);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rmdir($dir);
|
rmdir($dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rrmdir($aulario_dir);
|
rrmdir($aulario_dir);
|
||||||
// Remove aulario config file
|
|
||||||
unlink($aulario_file);
|
|
||||||
header("Location: ?action=index");
|
header("Location: ?action=index");
|
||||||
exit();
|
exit();
|
||||||
break;
|
break;
|
||||||
case "create":
|
case "create":
|
||||||
$user_data = $_SESSION["auth_data"];
|
|
||||||
$centro_id = safe_path_segment(Sf($_POST["centro"] ?? ""));
|
$centro_id = safe_path_segment(Sf($_POST["centro"] ?? ""));
|
||||||
if (empty($centro_id) || !is_dir("/DATA/entreaulas/Centros/$centro_id")) {
|
$aulario_id = strtolower(preg_replace("/[^a-zA-Z0-9_-]/", "_", Sf($_POST["name"] ?? "")));
|
||||||
|
if (empty($centro_id) || empty($aulario_id)) {
|
||||||
|
die("Datos incompletos.");
|
||||||
|
}
|
||||||
|
// Ensure centro exists in DB
|
||||||
|
$stmt = db()->prepare("SELECT id FROM centros WHERE centro_id = ?");
|
||||||
|
$stmt->execute([$centro_id]);
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
die("Centro no válido.");
|
die("Centro no válido.");
|
||||||
}
|
}
|
||||||
$aulario_id = strtolower(preg_replace("/[^a-zA-Z0-9_-]/", "_", Sf($_POST["name"] ?? "")));
|
db()->prepare(
|
||||||
$aulario_data = [
|
"INSERT OR IGNORE INTO aularios (centro_id, aulario_id, name, icon) VALUES (?, ?, ?, ?)"
|
||||||
"name" => Sf($_POST["name"] ?? ""),
|
)->execute([
|
||||||
"icon" => Sf($_POST["icon"] ?? "/static/logo-entreaulas.png")
|
$centro_id, $aulario_id,
|
||||||
];
|
Sf($_POST["name"] ?? ""),
|
||||||
// Make path recursive (mkdir -p equivalent)
|
Sf($_POST["icon"] ?? "/static/logo-entreaulas.png"),
|
||||||
@mkdir("/DATA/entreaulas/Centros/$centro_id/Aularios/", 0777, true);
|
]);
|
||||||
@mkdir("/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id/Proyectos/", 0777, true);
|
// Create Alumnos directory for photo-based features
|
||||||
file_put_contents("/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json", json_encode($aulario_data));
|
@mkdir("/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id/Proyectos/", 0755, true);
|
||||||
// Update user data
|
|
||||||
$_SESSION["auth_data"]["entreaulas"]["aulas"][] = $aulario_id;
|
|
||||||
header("Location: ?action=index");
|
header("Location: ?action=index");
|
||||||
exit();
|
exit();
|
||||||
break;
|
break;
|
||||||
case "save_edit":
|
case "save_edit":
|
||||||
$aulario_id = safe_path_segment(Sf($_POST["aulario_id"] ?? ""));
|
$aulario_id = safe_path_segment(Sf($_POST["aulario_id"] ?? ""));
|
||||||
$centro_id = safe_path_segment(Sf($_POST["centro_id"] ?? ""));
|
$centro_id = safe_path_segment(Sf($_POST["centro_id"] ?? ""));
|
||||||
$aulario_file = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
|
if ($aulario_id === "" || $centro_id === "") {
|
||||||
if (!file_exists($aulario_file)) {
|
die("Parámetros inválidos.");
|
||||||
|
}
|
||||||
|
// Fetch existing extra data
|
||||||
|
$existing = db_get_aulario($centro_id, $aulario_id);
|
||||||
|
if ($existing === null) {
|
||||||
die("Aulario no encontrado.");
|
die("Aulario no encontrado.");
|
||||||
}
|
}
|
||||||
$aulario_data = json_decode(file_get_contents($aulario_file), true);
|
// Build extra JSON preserving any existing extra fields
|
||||||
$aulario_data["name"] = Sf($_POST["name"] ?? "");
|
$extra_skip = ['name', 'icon'];
|
||||||
$aulario_data["icon"] = Sf($_POST["icon"] ?? "/static/logo-entreaulas.png");
|
$extra = [];
|
||||||
|
foreach ($existing as $k => $v) {
|
||||||
// Handle shared comedor configuration
|
if (!in_array($k, $extra_skip, true)) {
|
||||||
$share_comedor_from = safe_path_segment(Sf($_POST["share_comedor_from"] ?? ""));
|
$extra[$k] = $v;
|
||||||
|
|
||||||
if (!empty($share_comedor_from) && $share_comedor_from !== "none") {
|
|
||||||
$aulario_data["shared_comedor_from"] = $share_comedor_from;
|
|
||||||
} else {
|
|
||||||
unset($aulario_data["shared_comedor_from"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle linked projects configuration
|
|
||||||
$linked_projects = [];
|
|
||||||
$linked_aularios = $_POST["linked_aulario"] ?? [];
|
|
||||||
$linked_project_ids = $_POST["linked_project_id"] ?? [];
|
|
||||||
$linked_permissions = $_POST["linked_permission"] ?? [];
|
|
||||||
|
|
||||||
for ($i = 0; $i < count($linked_aularios); $i++) {
|
|
||||||
$src_aul = safe_path_segment($linked_aularios[$i] ?? "");
|
|
||||||
$proj_id = safe_path_segment($linked_project_ids[$i] ?? "");
|
|
||||||
$perm = in_array(($linked_permissions[$i] ?? "read_only"), ["read_only", "request_edit", "full_edit"], true)
|
|
||||||
? ($linked_permissions[$i] ?? "read_only")
|
|
||||||
: "read_only";
|
|
||||||
if (!empty($src_aul) && !empty($proj_id)) {
|
|
||||||
$linked_projects[] = [
|
|
||||||
"source_aulario" => $src_aul,
|
|
||||||
"project_id" => $proj_id,
|
|
||||||
"permission" => $perm
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Update shared_comedor_from if posted
|
||||||
if (count($linked_projects) > 0) {
|
if (isset($_POST['shared_comedor_from'])) {
|
||||||
$aulario_data["linked_projects"] = $linked_projects;
|
$extra['shared_comedor_from'] = Sf($_POST['shared_comedor_from']);
|
||||||
} else {
|
|
||||||
unset($aulario_data["linked_projects"]);
|
|
||||||
}
|
}
|
||||||
@mkdir("/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id/Proyectos/", 0777, true);
|
db()->prepare(
|
||||||
file_put_contents($aulario_file, json_encode($aulario_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
"UPDATE aularios SET name = ?, icon = ?, extra = ? WHERE centro_id = ? AND aulario_id = ?"
|
||||||
header("Location: ?action=edit&aulario=" . urlencode($aulario_id) . "¢ro=" . urlencode($centro_id) . "&saved=1");
|
)->execute([
|
||||||
|
Sf($_POST["name"] ?? ""),
|
||||||
|
Sf($_POST["icon"] ?? "/static/logo-entreaulas.png"),
|
||||||
|
json_encode($extra),
|
||||||
|
$centro_id,
|
||||||
|
$aulario_id,
|
||||||
|
]);
|
||||||
|
header("Location: ?action=edit&aulario=" . urlencode($aulario_id) . "¢ro=" . urlencode($centro_id) . "&_result=" . urlencode("Cambios guardados."));
|
||||||
exit();
|
exit();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once "_incl/pre-body.php";
|
|
||||||
$view_action = $_GET["action"] ?? "index";
|
$view_action = $_GET["action"] ?? "index";
|
||||||
switch ($view_action) {
|
switch ($view_action) {
|
||||||
case "new":
|
case "new":
|
||||||
|
require_once "_incl/pre-body.php";
|
||||||
|
$centro_id = safe_path_segment(Sf($_GET["centro"] ?? ""));
|
||||||
|
$all_centros = db_get_centro_ids();
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">Nuevo Aulario</h1>
|
<h1>Nuevo Aulario</h1>
|
||||||
<span>
|
|
||||||
Aquí puedes crear un nuevo aulario para el centro que administras.
|
|
||||||
</span>
|
|
||||||
<form method="post" action="?form=create">
|
<form method="post" action="?form=create">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="centro" class="form-label"><b>Centro:</b></label>
|
<label for="centro" class="form-label">Centro:</label>
|
||||||
<select required id="centro" name="centro" class="form-select">
|
<select id="centro" name="centro" class="form-select" required>
|
||||||
<option value="">-- Selecciona un centro --</option>
|
<option value="">-- Selecciona un centro --</option>
|
||||||
<?php
|
<?php foreach ($all_centros as $cid): ?>
|
||||||
foreach (glob("/DATA/entreaulas/Centros/*", GLOB_ONLYDIR) as $centro_folder) {
|
<option value="<?= htmlspecialchars($cid) ?>" <?= $cid === $centro_id ? 'selected' : '' ?>><?= htmlspecialchars($cid) ?></option>
|
||||||
$centro_id = basename($centro_folder);
|
<?php endforeach; ?>
|
||||||
$selected = ($centro_id == $_SESSION["auth_data"]["entreaulas"]["centro"]) ? "selected" : "";
|
|
||||||
echo '<option value="' . htmlspecialchars($centro_id) . '" ' . $selected . '>' . htmlspecialchars($centro_id) . '</option>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="name" class="form-label">Nombre del Aulario:</label>
|
<label for="name" class="form-label">Nombre:</label>
|
||||||
<input required type="text" id="name" name="name" class="form-control" placeholder="Ej: Aulario Principal">
|
<input required type="text" id="name" name="name" class="form-control" placeholder="Ej: Aula 1">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="icon" class="form-label">Icono del Aulario (URL):</label>
|
<label for="icon" class="form-label">URL del icono:</label>
|
||||||
<input type="text" id="icon" name="icon" class="form-control" placeholder="Ej: https://example.com/icon.png" value="/static/logo-entreaulas.png">
|
<input type="url" id="icon" name="icon" class="form-control" value="/static/logo-entreaulas.png">
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Crear Aulario</button>
|
<button type="submit" class="btn btn-primary">Crear Aulario</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
|
require_once "_incl/post-body.php";
|
||||||
break;
|
break;
|
||||||
case "edit":
|
case "edit":
|
||||||
|
require_once "_incl/pre-body.php";
|
||||||
$aulario_id = safe_path_segment(Sf($_GET["aulario"] ?? ""));
|
$aulario_id = safe_path_segment(Sf($_GET["aulario"] ?? ""));
|
||||||
$centro_id = safe_path_segment(Sf($_GET["centro"] ?? ""));
|
$centro_id = safe_path_segment(Sf($_GET["centro"] ?? ""));
|
||||||
$aulario_file = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aulario_id.json";
|
$aulario = db_get_aulario($centro_id, $aulario_id);
|
||||||
if (!file_exists($aulario_file)) {
|
if (!$aulario) {
|
||||||
die("Aulario no encontrado.");
|
die("Aulario no encontrado.");
|
||||||
}
|
}
|
||||||
$aulario_data = json_decode(file_get_contents($aulario_file), true);
|
$other_aularios = db_get_aularios($centro_id);
|
||||||
|
|
||||||
// Get all aularios from the same centro for sharing options
|
|
||||||
$available_aularios = [];
|
|
||||||
$aularios_files = glob("/DATA/entreaulas/Centros/$centro_id/Aularios/*.json");
|
|
||||||
foreach ($aularios_files as $aul_file) {
|
|
||||||
$aul_id = basename($aul_file, ".json");
|
|
||||||
if ($aul_id !== $aulario_id) { // Don't allow sharing from itself
|
|
||||||
$aul_data = json_decode(file_get_contents($aul_file), true);
|
|
||||||
$available_aularios[$aul_id] = $aul_data['name'] ?? $aul_id;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get available projects from other aularios
|
|
||||||
$available_projects_by_aulario = [];
|
|
||||||
foreach ($available_aularios as $aul_id => $aul_name) {
|
|
||||||
$proj_dir = "/DATA/entreaulas/Centros/$centro_id/Aularios/$aul_id/Proyectos";
|
|
||||||
if (is_dir($proj_dir)) {
|
|
||||||
$projects = [];
|
|
||||||
$files = glob("$proj_dir/*.json");
|
|
||||||
foreach ($files as $file) {
|
|
||||||
$proj_data = json_decode(file_get_contents($file), true);
|
|
||||||
// Only include root projects (no parent)
|
|
||||||
if ($proj_data && ($proj_data["parent_id"] ?? null) === null) {
|
|
||||||
$projects[] = [
|
|
||||||
"id" => $proj_data["id"] ?? basename($file, ".json"),
|
|
||||||
"name" => $proj_data["name"] ?? "Sin nombre"
|
|
||||||
];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count($projects) > 0) {
|
|
||||||
$available_projects_by_aulario[$aul_id] = $projects;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
?>
|
?>
|
||||||
<?php if (isset($_GET['saved'])): ?>
|
|
||||||
<div class="alert alert-success">Cambios guardados correctamente.</div>
|
|
||||||
<?php endif; ?>
|
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">Editar Aulario: <?php echo htmlspecialchars($aulario_data['name'] ?? 'Sin Nombre'); ?></h1>
|
<h1>Aulario: <?= htmlspecialchars($aulario['name'] ?? $aulario_id) ?></h1>
|
||||||
<form method="post" action="?form=save_edit">
|
<form method="post" action="?form=save_edit">
|
||||||
|
<input type="hidden" name="aulario_id" value="<?= htmlspecialchars($aulario_id) ?>">
|
||||||
|
<input type="hidden" name="centro_id" value="<?= htmlspecialchars($centro_id) ?>">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="name" class="form-label">Nombre del Aulario:</label>
|
<label for="name" class="form-label">Nombre:</label>
|
||||||
<input required type="text" id="name" name="name" class="form-control" value="<?php echo htmlspecialchars($aulario_data['name'] ?? ''); ?>">
|
<input required type="text" id="name" name="name" class="form-control" value="<?= htmlspecialchars($aulario['name'] ?? '') ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="icon" class="form-label">Icono del Aulario (URL):</label>
|
<label for="icon" class="form-label">URL del icono:</label>
|
||||||
<input type="text" id="icon" name="icon" class="form-control" value="<?php echo htmlspecialchars($aulario_data['icon'] ?? '/static/iconexperience/blackboard.png'); ?>">
|
<input type="text" id="icon" name="icon" class="form-control" value="<?= htmlspecialchars($aulario['icon'] ?? '') ?>">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
|
||||||
<h3>Compartir Menú Comedor</h3>
|
|
||||||
<p class="text-muted">Configura desde qué aulario compartir los datos del menú comedor. Si se selecciona un aulario origen, este aulario mostrará los menús del aulario seleccionado en lugar de los propios.</p>
|
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="share_comedor_from" class="form-label">Menú Comedor - Compartir desde:</label>
|
<label for="shared_comedor_from" class="form-label">Compartir comedor de:</label>
|
||||||
<select id="share_comedor_from" name="share_comedor_from" class="form-select">
|
<select id="shared_comedor_from" name="shared_comedor_from" class="form-select">
|
||||||
<option value="none">No compartir (usar datos propios)</option>
|
<option value="">-- Sin compartir --</option>
|
||||||
<?php foreach ($available_aularios as $aul_id => $aul_name): ?>
|
<?php foreach ($other_aularios as $aid => $adata): if ($aid === $aulario_id) continue; ?>
|
||||||
<option value="<?php echo htmlspecialchars($aul_id); ?>"
|
<option value="<?= htmlspecialchars($aid) ?>" <?= ($aulario['shared_comedor_from'] ?? '') === $aid ? 'selected' : '' ?>><?= htmlspecialchars($adata['name'] ?? $aid) ?></option>
|
||||||
<?php echo ($aulario_data['shared_comedor_from'] ?? '') === $aul_id ? 'selected' : ''; ?>>
|
|
||||||
<?php echo htmlspecialchars($aul_name); ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
<?php endforeach; ?>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<hr>
|
|
||||||
<h3>Proyectos Enlazados</h3>
|
|
||||||
<p class="text-muted">Selecciona proyectos raíz específicos de otros aularios para mostrarlos en este aulario. Puedes configurar el nivel de permisos: Solo lectura, Solicitar permiso para cambiar, o Cambiar sin solicitar.</p>
|
|
||||||
|
|
||||||
<div id="linked-projects-container">
|
|
||||||
<?php
|
|
||||||
$existing_links = $aulario_data['linked_projects'] ?? [];
|
|
||||||
if (count($existing_links) === 0) {
|
|
||||||
// Show one empty row
|
|
||||||
$existing_links = [["source_aulario" => "", "project_id" => "", "permission" => "read_only"]];
|
|
||||||
}
|
|
||||||
foreach ($existing_links as $idx => $link):
|
|
||||||
$source_aul = $link['source_aulario'] ?? '';
|
|
||||||
$proj_id = $link['project_id'] ?? '';
|
|
||||||
$permission = $link['permission'] ?? 'read_only';
|
|
||||||
?>
|
|
||||||
<div class="row mb-2 linked-project-row">
|
|
||||||
<div class="col-md-4">
|
|
||||||
<select name="linked_aulario[]" class="form-select linked-aulario-select" data-row="<?php echo $idx; ?>">
|
|
||||||
<option value="">-- Seleccionar aulario origen --</option>
|
|
||||||
<?php foreach ($available_aularios as $aul_id => $aul_name): ?>
|
|
||||||
<option value="<?php echo htmlspecialchars($aul_id); ?>"
|
|
||||||
<?php echo $source_aul === $aul_id ? 'selected' : ''; ?>>
|
|
||||||
<?php echo htmlspecialchars($aul_name); ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<select name="linked_project_id[]" class="form-select linked-project-select" data-row="<?php echo $idx; ?>">
|
|
||||||
<option value="">-- Seleccionar proyecto --</option>
|
|
||||||
<?php if (!empty($source_aul) && isset($available_projects_by_aulario[$source_aul])): ?>
|
|
||||||
<?php foreach ($available_projects_by_aulario[$source_aul] as $proj): ?>
|
|
||||||
<option value="<?php echo htmlspecialchars($proj['id']); ?>"
|
|
||||||
<?php echo $proj_id === $proj['id'] ? 'selected' : ''; ?>>
|
|
||||||
<?php echo htmlspecialchars($proj['name']); ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
<?php endif; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<select name="linked_permission[]" class="form-select">
|
|
||||||
<option value="read_only" <?php echo $permission === 'read_only' ? 'selected' : ''; ?>>Solo lectura</option>
|
|
||||||
<option value="request_edit" <?php echo $permission === 'request_edit' ? 'selected' : ''; ?>>Solicitar permiso para cambiar</option>
|
|
||||||
<option value="full_edit" <?php echo $permission === 'full_edit' ? 'selected' : ''; ?>>Cambiar sin solicitar</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-2">
|
|
||||||
<button type="button" class="btn btn-danger remove-link-btn" onclick="removeLinkedProject(this)">Eliminar</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<button type="button" class="btn btn-secondary mb-3" onclick="addLinkedProject()">+ Añadir Proyecto Enlazado</button>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// Store available projects data
|
|
||||||
const availableProjects = <?php echo json_encode($available_projects_by_aulario); ?>;
|
|
||||||
let rowCounter = <?php echo count($existing_links); ?>;
|
|
||||||
|
|
||||||
function addLinkedProject() {
|
|
||||||
const container = document.getElementById('linked-projects-container');
|
|
||||||
const newRow = document.createElement('div');
|
|
||||||
newRow.className = 'row mb-2 linked-project-row';
|
|
||||||
newRow.innerHTML = `
|
|
||||||
<div class="col-md-4">
|
|
||||||
<select name="linked_aulario[]" class="form-select linked-aulario-select" data-row="${rowCounter}">
|
|
||||||
<option value="">-- Seleccionar aulario origen --</option>
|
|
||||||
<?php foreach ($available_aularios as $aul_id => $aul_name): ?>
|
|
||||||
<option value="<?php echo htmlspecialchars($aul_id); ?>">
|
|
||||||
<?php echo htmlspecialchars($aul_name); ?>
|
|
||||||
</option>
|
|
||||||
<?php endforeach; ?>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<select name="linked_project_id[]" class="form-select linked-project-select" data-row="${rowCounter}">
|
|
||||||
<option value="">-- Seleccionar proyecto --</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-3">
|
|
||||||
<select name="linked_permission[]" class="form-select">
|
|
||||||
<option value="read_only">Solo lectura</option>
|
|
||||||
<option value="request_edit">Solicitar permiso para cambiar</option>
|
|
||||||
<option value="full_edit">Cambiar sin solicitar</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-2">
|
|
||||||
<button type="button" class="btn btn-danger remove-link-btn" onclick="removeLinkedProject(this)">Eliminar</button>
|
|
||||||
</div>
|
|
||||||
`;
|
|
||||||
container.appendChild(newRow);
|
|
||||||
|
|
||||||
// Attach change event to new aulario select
|
|
||||||
const newAularioSelect = newRow.querySelector('.linked-aulario-select');
|
|
||||||
newAularioSelect.addEventListener('change', updateProjectOptions);
|
|
||||||
|
|
||||||
rowCounter++;
|
|
||||||
}
|
|
||||||
|
|
||||||
function removeLinkedProject(btn) {
|
|
||||||
btn.closest('.linked-project-row').remove();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateProjectOptions(event) {
|
|
||||||
const aularioSelect = event.target;
|
|
||||||
const rowId = aularioSelect.dataset.row;
|
|
||||||
const projectSelect = document.querySelector(`.linked-project-select[data-row="${rowId}"]`);
|
|
||||||
const selectedAulario = aularioSelect.value;
|
|
||||||
|
|
||||||
// Clear project options
|
|
||||||
projectSelect.innerHTML = '<option value="">-- Seleccionar proyecto --</option>';
|
|
||||||
|
|
||||||
// Add new options
|
|
||||||
if (selectedAulario && availableProjects[selectedAulario]) {
|
|
||||||
availableProjects[selectedAulario].forEach(proj => {
|
|
||||||
const option = document.createElement('option');
|
|
||||||
option.value = proj.id;
|
|
||||||
option.textContent = proj.name;
|
|
||||||
projectSelect.appendChild(option);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Attach event listeners to existing selects
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
|
||||||
document.querySelectorAll('.linked-aulario-select').forEach(select => {
|
|
||||||
select.addEventListener('change', updateProjectOptions);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<input type="hidden" name="aulario_id" value="<?php echo htmlspecialchars($aulario_id); ?>">
|
|
||||||
<input type="hidden" name="centro_id" value="<?php echo htmlspecialchars($centro_id); ?>">
|
|
||||||
<button type="submit" class="btn btn-primary">Guardar Cambios</button>
|
<button type="submit" class="btn btn-primary">Guardar Cambios</button>
|
||||||
</form>
|
</form>
|
||||||
<form method="post" action="?form=delete" style="display: inline;">
|
<hr>
|
||||||
<input type="hidden" name="aulario_id" value="<?php echo htmlspecialchars($aulario_id); ?>">
|
<form method="post" action="?form=delete" onsubmit="return confirm('¿Eliminar este aulario? Se borrarán todos sus datos.')">
|
||||||
<input type="hidden" name="centro_id" value="<?php echo htmlspecialchars($centro_id); ?>">
|
<input type="hidden" name="aulario_id" value="<?= htmlspecialchars($aulario_id) ?>">
|
||||||
<button type="submit" class="btn btn-danger" onclick="return confirm('¿Estás seguro de que deseas eliminar este aulario? Esta acción no se puede deshacer.')">Eliminar Aulario</button>
|
<input type="hidden" name="centro_id" value="<?= htmlspecialchars($centro_id) ?>">
|
||||||
|
<button type="submit" class="btn btn-danger">Eliminar Aulario</button>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
|
require_once "_incl/post-body.php";
|
||||||
break;
|
break;
|
||||||
case "index":
|
case "index":
|
||||||
default:
|
default:
|
||||||
|
require_once "_incl/pre-body.php";
|
||||||
|
$all_centros = db_get_centros();
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">Gestión de Aularios</h1>
|
<h1>Gestión de Aularios</h1>
|
||||||
<span>
|
<?php foreach ($all_centros as $c): ?>
|
||||||
Desde esta sección puedes administrar los aularios asociados al centro que estás administrando.
|
<?php $aularios = db_get_aularios($c['centro_id']); ?>
|
||||||
</span>
|
<h2><?= htmlspecialchars($c['centro_id']) ?></h2>
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead class="table-dark">
|
<thead class="table-dark">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Icono</th>
|
<th>Icono</th><th>Nombre</th>
|
||||||
<th>Nombre</th>
|
<th><a href="?action=new¢ro=<?= urlencode($c['centro_id']) ?>" class="btn btn-success">+ Nuevo</a></th>
|
||||||
<th>
|
|
||||||
<a href="?action=new" class="btn btn-success">+ Nuevo</a>
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php foreach ($aularios as $aid => $adata): ?>
|
||||||
$user_data = $_SESSION["auth_data"];
|
<tr>
|
||||||
$centro_filter = safe_path_segment(Sf($_GET['centro'] ?? ""));
|
<td><img src="<?= htmlspecialchars($adata['icon'] ?: '/static/logo-entreaulas.png') ?>" style="height: 50px;"></td>
|
||||||
if ($centro_filter !== "") {
|
<td><?= htmlspecialchars($adata['name'] ?: $aid) ?></td>
|
||||||
$aulas_filelist = glob("/DATA/entreaulas/Centros/$centro_filter/Aularios/*.json") ?: [];
|
<td><a href="?action=edit&aulario=<?= urlencode($aid) ?>¢ro=<?= urlencode($c['centro_id']) ?>" class="btn btn-primary">Editar</a></td>
|
||||||
} else {
|
</tr>
|
||||||
$aulas_filelist = glob("/DATA/entreaulas/Centros/*/Aularios/*.json") ?: [];
|
<?php endforeach; ?>
|
||||||
}
|
|
||||||
foreach ($aulas_filelist as $aula_file) {
|
|
||||||
$aula_data = json_decode(file_get_contents($aula_file), true);
|
|
||||||
$centro_id = basename(dirname(dirname($aula_file)));
|
|
||||||
echo '<tr>';
|
|
||||||
echo '<td><img src="' . htmlspecialchars($aula_data['icon'] ?? '/static/logo-entreaulas.png') . '" alt="Icono" style="height: 50px;"></td>';
|
|
||||||
echo '<td>' . htmlspecialchars($aula_data['name'] ?? 'Sin Nombre') . '<br><small>' . $centro_id . '</small></td>';
|
|
||||||
echo '<td><a href="?action=edit&aulario=' . urlencode(basename($aula_file, ".json")) . '¢ro=' . urlencode($centro_id) . '" class="btn btn-primary">Gestionar</a></td>';
|
|
||||||
echo '</tr>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
<?php endforeach; ?>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<?php
|
<?php
|
||||||
|
require_once "_incl/post-body.php";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
require_once "_incl/post-body.php"; ?>
|
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
function safe_path_segment($value)
|
function safe_path_segment($value)
|
||||||
{
|
{
|
||||||
@@ -21,22 +22,31 @@ switch ($form_action) {
|
|||||||
if (empty($centro_id)) {
|
if (empty($centro_id)) {
|
||||||
die("Nombre del centro no proporcionado.");
|
die("Nombre del centro no proporcionado.");
|
||||||
}
|
}
|
||||||
$centro_path = "/DATA/entreaulas/Centros/$centro_id";
|
// Check uniqueness in DB
|
||||||
if (is_dir($centro_path)) {
|
$existing = db()->prepare("SELECT id FROM centros WHERE centro_id = ?");
|
||||||
|
$existing->execute([$centro_id]);
|
||||||
|
if ($existing->fetch()) {
|
||||||
die("El centro ya existe.");
|
die("El centro ya existe.");
|
||||||
}
|
}
|
||||||
mkdir($centro_path, 0777, true);
|
// Create DB record
|
||||||
|
db()->prepare("INSERT INTO centros (centro_id) VALUES (?)")->execute([$centro_id]);
|
||||||
|
// Keep filesystem directory for activity photos (Panel/Actividades)
|
||||||
|
$centro_path = "/DATA/entreaulas/Centros/$centro_id";
|
||||||
|
if (!is_dir($centro_path)) {
|
||||||
|
mkdir($centro_path, 0755, true);
|
||||||
|
}
|
||||||
header("Location: ?action=index");
|
header("Location: ?action=index");
|
||||||
exit();
|
exit();
|
||||||
break;
|
break;
|
||||||
case "create_activity":
|
case "create_activity":
|
||||||
ini_set('memory_limit', '512M');
|
ini_set('memory_limit', '512M');
|
||||||
ini_set("display_errors", 1);
|
|
||||||
ini_set('upload_max_filesize', '256M');
|
ini_set('upload_max_filesize', '256M');
|
||||||
ini_set('post_max_size', '256M');
|
ini_set('post_max_size', '256M');
|
||||||
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
|
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
|
||||||
$centro_path = "/DATA/entreaulas/Centros/$centro_id";
|
// Validate centro exists in DB
|
||||||
if (!is_dir($centro_path)) {
|
$stmt = db()->prepare("SELECT id FROM centros WHERE centro_id = ?");
|
||||||
|
$stmt->execute([$centro_id]);
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
die("Centro no válido.");
|
die("Centro no válido.");
|
||||||
}
|
}
|
||||||
$activity_name = safe_path_segment(Sf($_POST["name"] ?? ''));
|
$activity_name = safe_path_segment(Sf($_POST["name"] ?? ''));
|
||||||
@@ -47,19 +57,17 @@ switch ($form_action) {
|
|||||||
if ($activity_photo === null || $activity_photo["error"] !== UPLOAD_ERR_OK) {
|
if ($activity_photo === null || $activity_photo["error"] !== UPLOAD_ERR_OK) {
|
||||||
die("Error al subir la foto.");
|
die("Error al subir la foto.");
|
||||||
}
|
}
|
||||||
$activity_path = "$centro_path/Panel/Actividades/$activity_name";
|
$activity_path = "/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/$activity_name";
|
||||||
if (is_dir($activity_path)) {
|
if (is_dir($activity_path)) {
|
||||||
die("La actividad ya existe.");
|
die("La actividad ya existe.");
|
||||||
}
|
}
|
||||||
mkdir($activity_path, 0777, true);
|
mkdir($activity_path, 0755, true);
|
||||||
$photo_path = "$activity_path/photo.jpg";
|
move_uploaded_file($activity_photo["tmp_name"], "$activity_path/photo.jpg");
|
||||||
move_uploaded_file($activity_photo["tmp_name"], $photo_path);
|
|
||||||
header("Location: ?action=edit¢ro=" . urlencode($centro_id));
|
header("Location: ?action=edit¢ro=" . urlencode($centro_id));
|
||||||
exit();
|
exit();
|
||||||
break;
|
break;
|
||||||
case "edit_activity":
|
case "edit_activity":
|
||||||
ini_set('memory_limit', '512M');
|
ini_set('memory_limit', '512M');
|
||||||
ini_set("display_errors", 1);
|
|
||||||
ini_set('upload_max_filesize', '256M');
|
ini_set('upload_max_filesize', '256M');
|
||||||
ini_set('post_max_size', '256M');
|
ini_set('post_max_size', '256M');
|
||||||
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
|
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
|
||||||
@@ -70,18 +78,17 @@ switch ($form_action) {
|
|||||||
}
|
}
|
||||||
$activity_photo = $_FILES["file"] ?? null;
|
$activity_photo = $_FILES["file"] ?? null;
|
||||||
if ($activity_photo !== null && $activity_photo["error"] === UPLOAD_ERR_OK) {
|
if ($activity_photo !== null && $activity_photo["error"] === UPLOAD_ERR_OK) {
|
||||||
$photo_path = "$activity_path/photo.jpg";
|
move_uploaded_file($activity_photo["tmp_name"], "$activity_path/photo.jpg");
|
||||||
move_uploaded_file($activity_photo["tmp_name"], $photo_path);
|
|
||||||
}
|
}
|
||||||
if (safe_path_segment(Sf($_POST['nombre'] ?? '')) != $activity_name) {
|
$new_name = safe_path_segment(Sf($_POST['nombre'] ?? ''));
|
||||||
$new_activity_name = safe_path_segment(Sf($_POST['nombre'] ?? ''));
|
if ($new_name !== $activity_name && $new_name !== '') {
|
||||||
$new_activity_path = "/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/$new_activity_name";
|
$new_path = "/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/$new_name";
|
||||||
if (is_dir($new_activity_path)) {
|
if (is_dir($new_path)) {
|
||||||
die("Ya existe una actividad con ese nombre.");
|
die("Ya existe una actividad con ese nombre.");
|
||||||
}
|
}
|
||||||
rename($activity_path, $new_activity_path);
|
rename($activity_path, $new_path);
|
||||||
}
|
}
|
||||||
header("Location: ?action=edit¢ro=" . urlencode($centro_id));;
|
header("Location: ?action=edit¢ro=" . urlencode($centro_id));
|
||||||
exit();
|
exit();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -99,22 +106,20 @@ switch ($view_action) {
|
|||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">Gestión de la Actividad: <?php echo htmlspecialchars($activity_name); ?></h1>
|
<h1 class="card-title">Gestión de la Actividad: <?= htmlspecialchars($activity_name) ?></h1>
|
||||||
<span>
|
<form method="post" action="?form=edit_activity¢ro=<?= urlencode($centro_id) ?>&activity=<?= urlencode($activity_name) ?>" enctype="multipart/form-data">
|
||||||
Desde esta sección puedes administrar la actividad seleccionada del panel del centro <?php echo htmlspecialchars($centro_id); ?>.
|
|
||||||
</span>
|
|
||||||
<form method="post" action="?form=edit_activity¢ro=<?php echo urlencode($centro_id); ?>&activity=<?php echo urlencode($activity_name); ?>" enctype="multipart/form-data">
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="nombre" class="form-label">Nombre de la actividad:</label>
|
<label for="nombre" class="form-label">Nombre de la actividad:</label>
|
||||||
<input required type="text" id="nombre" name="nombre" class="form-control" value="<?php echo htmlspecialchars($activity_name); ?>">
|
<input required type="text" id="nombre" name="nombre" class="form-control" value="<?= htmlspecialchars($activity_name) ?>">
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Foto (pulsa para cambiarla):</label><br>
|
<label class="form-label">Foto (pulsa para cambiarla):</label><br>
|
||||||
<div style="width: 200px;">
|
<div style="width: 200px;">
|
||||||
<label class="dropimage" style="background-image: url('<?php
|
<label class="dropimage" style="background-image: url('<?php
|
||||||
$image_path = "$activity_path/photo.jpg";
|
$img = file_exists("$activity_path/photo.jpg")
|
||||||
$image_fetchpath = file_exists($image_path) ? "/entreaulas/_filefetch.php?type=panel_actividades¢ro=" . urlencode($centro_id) . "&activity=" . urlencode($activity_name) : '/static/logo-entreaulas.png';
|
? "/entreaulas/_filefetch.php?type=panel_actividades¢ro=" . urlencode($centro_id) . "&activity=" . urlencode($activity_name)
|
||||||
echo htmlspecialchars($image_fetchpath);
|
: '/static/logo-entreaulas.png';
|
||||||
|
echo htmlspecialchars($img);
|
||||||
?>');">
|
?>');">
|
||||||
<input title="Drop image or click me" type="file" name="file" accept="image/*">
|
<input title="Drop image or click me" type="file" name="file" accept="image/*">
|
||||||
</label>
|
</label>
|
||||||
@@ -128,18 +133,16 @@ switch ($view_action) {
|
|||||||
break;
|
break;
|
||||||
case "new_activity":
|
case "new_activity":
|
||||||
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
|
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
|
||||||
$centro_path = "/DATA/entreaulas/Centros/$centro_id";
|
$stmt = db()->prepare("SELECT id FROM centros WHERE centro_id = ?");
|
||||||
if (!is_dir($centro_path)) {
|
$stmt->execute([$centro_id]);
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
die("Centro no válido.");
|
die("Centro no válido.");
|
||||||
}
|
}
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">Nueva Actividad del Panel</h1>
|
<h1 class="card-title">Nueva Actividad del Panel</h1>
|
||||||
<span>
|
<form method="post" action="?form=create_activity¢ro=<?= urlencode($centro_id) ?>" enctype="multipart/form-data">
|
||||||
Aquí puedes crear una nueva actividad para el panel del centro <?php echo htmlspecialchars($centro_id); ?>.
|
|
||||||
</span>
|
|
||||||
<form method="post" action="?form=create_activity¢ro=<?php echo urlencode($centro_id); ?>" enctype="multipart/form-data">
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="name" class="form-label">Nombre de la actividad:</label>
|
<label for="name" class="form-label">Nombre de la actividad:</label>
|
||||||
<input required type="text" id="name" name="name" class="form-control" placeholder="Ej: Biblioteca">
|
<input required type="text" id="name" name="name" class="form-control" placeholder="Ej: Biblioteca">
|
||||||
@@ -159,9 +162,6 @@ switch ($view_action) {
|
|||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">Nuevo Centro</h1>
|
<h1 class="card-title">Nuevo Centro</h1>
|
||||||
<span>
|
|
||||||
Aquí puedes crear un nuevo centro para el sistema.
|
|
||||||
</span>
|
|
||||||
<form method="post" action="?form=create">
|
<form method="post" action="?form=create">
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="name" class="form-label">ID del centro:</label>
|
<label for="name" class="form-label">ID del centro:</label>
|
||||||
@@ -171,22 +171,21 @@ switch ($view_action) {
|
|||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
case "edit":
|
case "edit":
|
||||||
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
|
$centro_id = safe_path_segment(Sf($_GET['centro'] ?? ''));
|
||||||
$centro_path = "/DATA/entreaulas/Centros/$centro_id";
|
$stmt = db()->prepare("SELECT id FROM centros WHERE centro_id = ?");
|
||||||
if (!is_dir($centro_path)) {
|
$stmt->execute([$centro_id]);
|
||||||
|
if (!$stmt->fetch()) {
|
||||||
die("Centro no válido.");
|
die("Centro no válido.");
|
||||||
}
|
}
|
||||||
|
$aularios = db_get_aularios($centro_id);
|
||||||
|
$activities = glob("/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/*", GLOB_ONLYDIR) ?: [];
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">Gestión del Centro: <?php echo htmlspecialchars($centro_id); ?></h1>
|
<h1 class="card-title">Gestión del Centro: <?= htmlspecialchars($centro_id) ?></h1>
|
||||||
<span>
|
|
||||||
Desde esta sección puedes administrar el centro seleccionado.
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
@@ -195,25 +194,18 @@ switch ($view_action) {
|
|||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead class="table-dark">
|
<thead class="table-dark">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Icono</th>
|
<th>Icono</th><th>Nombre</th>
|
||||||
<th>Nombre</th>
|
<th><a href="/sysadmin/aularios.php?action=new¢ro=<?= urlencode($centro_id) ?>" class="btn btn-success">+ Nuevo</a></th>
|
||||||
<th>
|
|
||||||
<a href="/sysadmin/aularios.php?action=new¢ro=<?php echo urlencode($centro_id); ?>" class="btn btn-success">+ Nuevo</a>
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php foreach ($aularios as $aula_id => $aula): ?>
|
||||||
$aulas_filelist = glob("/DATA/entreaulas/Centros/$centro_id/Aularios/*.json");
|
<tr>
|
||||||
foreach ($aulas_filelist as $aula_file) {
|
<td><img src="<?= htmlspecialchars($aula['icon'] ?: '/static/logo-entreaulas.png') ?>" alt="Icono" style="height: 50px;"></td>
|
||||||
$aula_data = json_decode(file_get_contents($aula_file), true);
|
<td><?= htmlspecialchars($aula['name'] ?: $aula_id) ?></td>
|
||||||
echo '<tr>';
|
<td><a href="/sysadmin/aularios.php?action=edit&aulario=<?= urlencode($aula_id) ?>¢ro=<?= urlencode($centro_id) ?>" class="btn btn-primary">Gestionar</a></td>
|
||||||
echo '<td><img src="' . htmlspecialchars($aula_data['icon'] ?? '/static/logo-entreaulas.png') . '" alt="Icono" style="height: 50px;"></td>';
|
</tr>
|
||||||
echo '<td>' . htmlspecialchars($aula_data['name'] ?? 'Sin Nombre') . '</td>';
|
<?php endforeach; ?>
|
||||||
echo '<td><a href="/sysadmin/aularios.php?action=edit&aulario=' . urlencode(basename($aula_file, ".json")) . '¢ro=' . urlencode($centro_id) . '" class="btn btn-primary">Gestionar</a></td>';
|
|
||||||
echo '</tr>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -224,64 +216,46 @@ switch ($view_action) {
|
|||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead class="table-dark">
|
<thead class="table-dark">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Foto</th>
|
<th>Foto</th><th>Nombre</th>
|
||||||
<th>Nombre</th>
|
<th><a href="?action=new_activity¢ro=<?= urlencode($centro_id) ?>" class="btn btn-success">+ Nuevo</a></th>
|
||||||
<th>
|
|
||||||
<a href="?action=new_activity¢ro=<?php echo urlencode($centro_id); ?>" class="btn btn-success">+ Nuevo</a>
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php foreach ($activities as $ap): ?>
|
||||||
$activities = glob("/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/*", GLOB_ONLYDIR);
|
<?php $an = basename($ap); $img_path = "$ap/photo.jpg"; ?>
|
||||||
foreach ($activities as $activity_path) {
|
<tr>
|
||||||
$activity_name = basename($activity_path);
|
<td><img src="<?= file_exists($img_path) ? htmlspecialchars("/entreaulas/_filefetch.php?type=panel_actividades¢ro=" . urlencode($centro_id) . "&activity=" . urlencode($an)) : '/static/logo-entreaulas.png' ?>" style="height: 50px;"></td>
|
||||||
$image_path = "/DATA/entreaulas/Centros/$centro_id/Panel/Actividades/" . basename($activity_name) . "/photo.jpg";
|
<td><?= htmlspecialchars($an) ?></td>
|
||||||
$image_fetchpath = file_exists($image_path) ? "/entreaulas/_filefetch.php?type=panel_actividades¢ro=" . urlencode($centro_id) . "&activity=" . urlencode($activity_name) : '/static/logo-entreaulas.png';
|
<td><a href="?action=edit_activity¢ro=<?= urlencode($centro_id) ?>&activity=<?= urlencode($an) ?>" class="btn btn-primary">Gestionar</a></td>
|
||||||
echo '<tr>';
|
</tr>
|
||||||
echo '<td><img src="' . htmlspecialchars($image_fetchpath) . '" alt="Foto" style="height: 50px;"></td>';
|
<?php endforeach; ?>
|
||||||
echo '<td>' . htmlspecialchars($activity_name) . '</td>';
|
|
||||||
echo '<td><a href="?action=edit_activity¢ro=' . urlencode($centro_id) . '&activity=' . urlencode($activity_name) . '" class="btn btn-primary">Gestionar</a></td>';
|
|
||||||
echo '</tr>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
break;
|
break;
|
||||||
case "index":
|
case "index":
|
||||||
default:
|
default:
|
||||||
|
$all_centros = db_get_centros();
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1 class="card-title">Gestión de Centros</h1>
|
<h1 class="card-title">Gestión de Centros</h1>
|
||||||
<span>
|
|
||||||
Desde esta sección puedes administrar los centros asociados al sistema.
|
|
||||||
</span>
|
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead class="table-dark">
|
<thead class="table-dark">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Nombre</th>
|
<th>Centro</th>
|
||||||
<th>
|
<th><a href="?action=new" class="btn btn-success">+ Nuevo</a></th>
|
||||||
<a href="?action=new" class="btn btn-success">+ Nuevo</a>
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php foreach ($all_centros as $c): ?>
|
||||||
$user_data = $_SESSION["auth_data"];
|
<tr>
|
||||||
$centros_filelist = glob("/DATA/entreaulas/Centros/*");
|
<td><?= htmlspecialchars($c['centro_id']) ?></td>
|
||||||
foreach ($centros_filelist as $centro_folder) {
|
<td><a href="?action=edit¢ro=<?= urlencode($c['centro_id']) ?>" class="btn btn-primary">Gestionar</a></td>
|
||||||
$centro_id = basename($centro_folder);
|
</tr>
|
||||||
echo '<tr>';
|
<?php endforeach; ?>
|
||||||
echo '<td>' . htmlspecialchars($centro_id) . '</td>';
|
|
||||||
echo '<td><a href="?action=edit¢ro=' . urlencode($centro_id) . '" class="btn btn-primary">Gestionar</a></td>';
|
|
||||||
echo '</tr>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -290,4 +264,4 @@ switch ($view_action) {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once "_incl/post-body.php"; ?>
|
require_once "_incl/post-body.php";
|
||||||
|
|||||||
@@ -1,39 +1,33 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
switch ($_GET['form']) {
|
switch ($_GET['form'] ?? '') {
|
||||||
case "create":
|
case "create":
|
||||||
// Handle creation logic here
|
$code = strtoupper(trim($_POST['invitation_code'] ?? ''));
|
||||||
$invitations = json_decode(file_get_contents("/DATA/Invitaciones_de_usuarios.json"), true) ?? [];
|
$single_use = isset($_POST['single_use']);
|
||||||
$invitation_code = strtoupper($_POST['invitation_code'] ?? '');
|
if (empty($code)) {
|
||||||
$single_use = isset($_POST['single_use']) ? true : false;
|
header("Location: /sysadmin/invitations.php?action=new&_resultcolor=red&_result=" . urlencode("Código de invitación vacío."));
|
||||||
if (isset($invitations[$invitation_code])) {
|
exit;
|
||||||
|
}
|
||||||
|
if (db_get_invitation($code)) {
|
||||||
header("Location: /sysadmin/invitations.php?action=new&_resultcolor=red&_result=" . urlencode("El código de invitación ya existe."));
|
header("Location: /sysadmin/invitations.php?action=new&_resultcolor=red&_result=" . urlencode("El código de invitación ya existe."));
|
||||||
exit;
|
exit;
|
||||||
}
|
}
|
||||||
$invitations[$invitation_code] = [
|
db_upsert_invitation($code, true, $single_use);
|
||||||
"active" => true,
|
header("Location: /sysadmin/invitations.php?_result=" . urlencode("Código $code creado correctamente."));
|
||||||
"single_use" => $single_use
|
|
||||||
];
|
|
||||||
file_put_contents("/DATA/Invitaciones_de_usuarios.json", json_encode($invitations, JSON_PRETTY_PRINT));
|
|
||||||
header("Location: /sysadmin/invitations.php?_result=" . urlencode("Código $invitation_code creado correctamente."));
|
|
||||||
exit;
|
exit;
|
||||||
break;
|
break;
|
||||||
case "delete":
|
case "delete":
|
||||||
// Handle deletion logic here
|
$code = strtoupper(trim($_POST['invitation_code'] ?? ''));
|
||||||
$invitations = json_decode(file_get_contents("/DATA/Invitaciones_de_usuarios.json"), true) ?? [];
|
db_delete_invitation($code);
|
||||||
$invitation_code = strtoupper($_POST['invitation_code'] ?? '');
|
header("Location: /sysadmin/invitations.php?_result=" . urlencode("Código $code borrado."));
|
||||||
if (isset($invitations[$invitation_code])) {
|
|
||||||
unset($invitations[$invitation_code]);
|
|
||||||
file_put_contents("/DATA/Invitaciones_de_usuarios.json", json_encode($invitations, JSON_PRETTY_PRINT));
|
|
||||||
}
|
|
||||||
header("Location: /sysadmin/invitations.php?_result=" . urlencode("Codigo $invitation_code borrado"));
|
|
||||||
exit;
|
exit;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
switch ($_GET['action']) {
|
switch ($_GET['action'] ?? 'index') {
|
||||||
case "new":
|
case "new":
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
@@ -49,9 +43,7 @@ switch ($_GET['action']) {
|
|||||||
</div>
|
</div>
|
||||||
<div class="form-check mb-3">
|
<div class="form-check mb-3">
|
||||||
<input class="form-check-input" type="checkbox" name="single_use" id="single_use">
|
<input class="form-check-input" type="checkbox" name="single_use" id="single_use">
|
||||||
<label class="form-check-label" for="single_use">
|
<label class="form-check-label" for="single_use">Uso único</label>
|
||||||
Uso único
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<button type="submit" class="btn btn-primary">Crear invitación</button>
|
<button type="submit" class="btn btn-primary">Crear invitación</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -63,33 +55,32 @@ switch ($_GET['action']) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
case "index":
|
case "index":
|
||||||
|
$invitations = db_get_all_invitations();
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1>Invitaciones de usuarios</h1>
|
<h1>Invitaciones de usuarios</h1>
|
||||||
<span>Desde aquí puedes gestionar las invitaciones de usuarios.</span>
|
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead class="table-dark">
|
<thead class="table-dark">
|
||||||
<th>Codigo de invitación</th>
|
<th>Código</th>
|
||||||
<th>
|
<th>Activo</th>
|
||||||
<a href="?action=new" class="btn btn-success">+ Nuevo</a>
|
<th>Uso único</th>
|
||||||
</th>
|
<th><a href="?action=new" class="btn btn-success">+ Nuevo</a></th>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php foreach ($invitations as $inv): ?>
|
||||||
$invitations = json_decode(file_get_contents("/DATA/Invitaciones_de_usuarios.json"), true);
|
<tr>
|
||||||
foreach ($invitations as $inv_key => $inv_data) {
|
<td><?= htmlspecialchars($inv['code']) ?></td>
|
||||||
echo "<tr>";
|
<td><?= $inv['active'] ? 'Sí' : 'No' ?></td>
|
||||||
echo "<td>" . htmlspecialchars($inv_key) . "</td>";
|
<td><?= $inv['single_use'] ? 'Sí' : 'No' ?></td>
|
||||||
echo "<td>";
|
<td>
|
||||||
echo '<form method="post" action="?form=delete" style="display:inline;">';
|
<form method="post" action="?form=delete" style="display:inline">
|
||||||
echo '<input type="hidden" name="invitation_code" value="' . htmlspecialchars($inv_key) . '"/>';
|
<input type="hidden" name="invitation_code" value="<?= htmlspecialchars($inv['code']) ?>">
|
||||||
echo '<button type="submit" class="btn btn-danger" onclick="return confirm(\'¿Estás seguro de que deseas eliminar esta invitación?\');">Eliminar</button>';
|
<button type="submit" class="btn btn-danger btn-sm">Borrar</button>
|
||||||
echo '</form>';
|
</form>
|
||||||
echo "</td>";
|
</td>
|
||||||
echo "</tr>";
|
</tr>
|
||||||
}
|
<?php endforeach; ?>
|
||||||
?>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
function safe_username($value)
|
function safe_username($value)
|
||||||
{
|
{
|
||||||
$value = basename((string)$value);
|
$value = strtolower(basename((string) $value));
|
||||||
$value = preg_replace('/[^a-zA-Z0-9._-]/', '', $value);
|
$value = preg_replace('/[^a-zA-Z0-9._@-]/', '', $value);
|
||||||
if (strpos($value, '..') !== false) {
|
if (strpos($value, '..') !== false) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
@@ -21,28 +21,21 @@ switch ($_GET['form'] ?? '') {
|
|||||||
if (empty($username)) {
|
if (empty($username)) {
|
||||||
die("Nombre de usuario no proporcionado.");
|
die("Nombre de usuario no proporcionado.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (empty($new_password)) {
|
if (empty($new_password)) {
|
||||||
die("La contraseña no puede estar vacía.");
|
die("La contraseña no puede estar vacía.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($new_password !== $confirm_password) {
|
if ($new_password !== $confirm_password) {
|
||||||
die("Las contraseñas no coinciden.");
|
die("Las contraseñas no coinciden.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strlen($new_password) < 6) {
|
if (strlen($new_password) < 6) {
|
||||||
die("La contraseña debe tener al menos 6 caracteres.");
|
die("La contraseña debe tener al menos 6 caracteres.");
|
||||||
}
|
}
|
||||||
|
$row = db_get_user($username);
|
||||||
$userfile = "/DATA/Usuarios/$username.json";
|
if (!$row) {
|
||||||
if (!file_exists($userfile)) {
|
|
||||||
die("Usuario no encontrado.");
|
die("Usuario no encontrado.");
|
||||||
}
|
}
|
||||||
|
db()->prepare("UPDATE users SET password_hash = ?, updated_at = datetime('now') WHERE id = ?")
|
||||||
$userdata = json_decode(file_get_contents($userfile), true);
|
->execute([password_hash($new_password, PASSWORD_DEFAULT), $row['id']]);
|
||||||
$userdata['password_hash'] = password_hash($new_password, PASSWORD_DEFAULT);
|
|
||||||
|
|
||||||
file_put_contents($userfile, json_encode($userdata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
|
|
||||||
|
|
||||||
header("Location: users.php?action=edit&user=" . urlencode($username) . "&_result=" . urlencode("Contraseña restablecida correctamente a las " . date("H:i:s") . " (hora servidor)."));
|
header("Location: users.php?action=edit&user=" . urlencode($username) . "&_result=" . urlencode("Contraseña restablecida correctamente a las " . date("H:i:s") . " (hora servidor)."));
|
||||||
exit;
|
exit;
|
||||||
@@ -50,44 +43,33 @@ switch ($_GET['form'] ?? '') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
|
|
||||||
$username = safe_username($_GET['user'] ?? '');
|
$username = safe_username($_GET['user'] ?? '');
|
||||||
if (empty($username)) {
|
if (empty($username)) {
|
||||||
die("Usuario no especificado.");
|
die("Usuario no especificado.");
|
||||||
}
|
}
|
||||||
|
$row = db_get_user($username);
|
||||||
$userfile = "/DATA/Usuarios/$username.json";
|
if (!$row) {
|
||||||
if (!file_exists($userfile)) {
|
|
||||||
die("Usuario no encontrado.");
|
die("Usuario no encontrado.");
|
||||||
}
|
}
|
||||||
|
|
||||||
$userdata = json_decode(file_get_contents($userfile), true);
|
|
||||||
?>
|
?>
|
||||||
|
|
||||||
<form method="post" action="?form=save_password">
|
<form method="post" action="?form=save_password">
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1>Restablecer Contraseña: <?php echo htmlspecialchars($username); ?></h1>
|
<h1>Restablecer Contraseña: <?= htmlspecialchars($username) ?></h1>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="new_password" class="form-label">Nueva Contraseña:</label>
|
<label for="new_password" class="form-label">Nueva Contraseña:</label>
|
||||||
<input type="password" id="new_password" name="new_password" class="form-control" required minlength="6">
|
<input type="password" id="new_password" name="new_password" class="form-control" required minlength="6">
|
||||||
<small class="form-text text-muted">Mínimo 6 caracteres</small>
|
<small class="form-text text-muted">Mínimo 6 caracteres</small>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="confirm_password" class="form-label">Confirmar Contraseña:</label>
|
<label for="confirm_password" class="form-label">Confirmar Contraseña:</label>
|
||||||
<input type="password" id="confirm_password" name="confirm_password" class="form-control" required minlength="6">
|
<input type="password" id="confirm_password" name="confirm_password" class="form-control" required minlength="6">
|
||||||
</div>
|
</div>
|
||||||
|
<input type="hidden" name="username" value="<?= htmlspecialchars($username) ?>">
|
||||||
<input type="hidden" name="username" value="<?php echo htmlspecialchars($username); ?>">
|
|
||||||
|
|
||||||
<button type="submit" class="btn btn-primary">Restablecer Contraseña</button>
|
<button type="submit" class="btn btn-primary">Restablecer Contraseña</button>
|
||||||
<a href="users.php?action=edit&user=<?php echo urlencode($username); ?>" class="btn btn-secondary">Cancelar</a>
|
<a href="users.php?action=edit&user=<?= urlencode($username) ?>" class="btn btn-secondary">Cancelar</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
<?php
|
<?php
|
||||||
require_once "_incl/post-body.php";
|
require_once "_incl/post-body.php";
|
||||||
?>
|
|
||||||
|
|||||||
@@ -1,81 +1,45 @@
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/auth_redir.php";
|
require_once "_incl/auth_redir.php";
|
||||||
require_once "../_incl/tools.security.php";
|
require_once "../_incl/tools.security.php";
|
||||||
|
require_once "../_incl/db.php";
|
||||||
|
|
||||||
function safe_username($value)
|
function safe_username($value)
|
||||||
{
|
{
|
||||||
$value = basename((string)$value);
|
$value = strtolower(basename((string) $value));
|
||||||
$value = preg_replace('/[^a-zA-Z0-9._-]/', '', $value);
|
$value = preg_replace('/[^a-zA-Z0-9._@-]/', '', $value);
|
||||||
if (strpos($value, '..') !== false) {
|
if (strpos($value, '..') !== false) {
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
return $value;
|
return $value;
|
||||||
}
|
}
|
||||||
|
|
||||||
define('USERS_DIR', '/DATA/Usuarios/');
|
|
||||||
|
|
||||||
switch ($_GET['form'] ?? '') {
|
switch ($_GET['form'] ?? '') {
|
||||||
case 'save_edit':
|
case 'save_edit':
|
||||||
$username = safe_username($_POST['username'] ?? '');
|
$username = safe_username($_POST['username'] ?? '');
|
||||||
if (empty($username)) {
|
if (empty($username)) {
|
||||||
die("Nombre de usuario no proporcionado.");
|
die("Nombre de usuario no proporcionado.");
|
||||||
}
|
}
|
||||||
$user_file = get_user_file_path($username);
|
|
||||||
$userdata_old = [];
|
|
||||||
if (is_readable($user_file)) {
|
|
||||||
$file_contents = file_get_contents($user_file);
|
|
||||||
if ($file_contents !== false) {
|
|
||||||
$decoded = json_decode($file_contents, true);
|
|
||||||
if (is_array($decoded)) {
|
|
||||||
$userdata_old = $decoded;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
$permissions = $_POST['permissions'] ?? [];
|
$permissions = $_POST['permissions'] ?? [];
|
||||||
if (!is_array($permissions)) {
|
if (!is_array($permissions)) {
|
||||||
$permissions = [];
|
$permissions = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
$aulas = $_POST['aulas'] ?? [];
|
$aulas = $_POST['aulas'] ?? [];
|
||||||
if (!is_array($aulas)) {
|
if (!is_array($aulas)) {
|
||||||
$aulas = [];
|
$aulas = [];
|
||||||
}
|
}
|
||||||
$aulas = array_values(array_filter(array_map('safe_aulario_id', $aulas)));
|
$aulas = array_values(array_filter(array_map('safe_aulario_id', $aulas)));
|
||||||
|
|
||||||
$userdata_new = [
|
db_upsert_user([
|
||||||
|
'username' => $username,
|
||||||
'display_name' => $_POST['display_name'] ?? '',
|
'display_name' => $_POST['display_name'] ?? '',
|
||||||
'email' => $_POST['email'] ?? '',
|
'email' => $_POST['email'] ?? '',
|
||||||
'permissions' => $permissions,
|
'permissions' => $permissions,
|
||||||
'entreaulas' => [
|
'entreaulas' => [
|
||||||
'centro' => safe_centro_id($_POST['centro'] ?? ''),
|
'centro' => safe_centro_id($_POST['centro'] ?? ''),
|
||||||
'role' => $_POST['role'] ?? '',
|
'role' => $_POST['role'] ?? '',
|
||||||
'aulas' => $aulas
|
'aulas' => $aulas,
|
||||||
]
|
],
|
||||||
];
|
]);
|
||||||
// Merge old and new data to preserve any other fields, like password hashes or custom metadata.
|
|
||||||
$userdata = array_merge($userdata_old, $userdata_new);
|
|
||||||
$user_dir = rtrim(USERS_DIR, '/');
|
|
||||||
$user_file = get_user_file_path($username);
|
|
||||||
if (!is_dir($user_dir) || !is_writable($user_dir)) {
|
|
||||||
die("No se puede guardar el usuario: directorio de datos no disponible.");
|
|
||||||
}
|
|
||||||
$json_data = json_encode($userdata, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);
|
|
||||||
if ($json_data === false) {
|
|
||||||
die("No se puede guardar el usuario: error al codificar los datos.");
|
|
||||||
}
|
|
||||||
$tmp_file = tempnam($user_dir, 'user_');
|
|
||||||
if ($tmp_file === false) {
|
|
||||||
die("No se puede guardar el usuario: no se pudo crear un archivo temporal.");
|
|
||||||
}
|
|
||||||
$bytes_written = file_put_contents($tmp_file, $json_data, LOCK_EX);
|
|
||||||
if ($bytes_written === false) {
|
|
||||||
@unlink($tmp_file);
|
|
||||||
die("No se puede guardar el usuario: error al escribir en el disco.");
|
|
||||||
}
|
|
||||||
if (!rename($tmp_file, $user_file)) {
|
|
||||||
@unlink($tmp_file);
|
|
||||||
die("No se puede guardar el usuario: no se pudo finalizar la grabación del archivo.");
|
|
||||||
}
|
|
||||||
header("Location: ?action=edit&user=" . urlencode($username) . "&_result=" . urlencode("Cambios guardados correctamente a las " . date("H:i:s") . " (hora servidor)."));
|
header("Location: ?action=edit&user=" . urlencode($username) . "&_result=" . urlencode("Cambios guardados correctamente a las " . date("H:i:s") . " (hora servidor)."));
|
||||||
exit;
|
exit;
|
||||||
break;
|
break;
|
||||||
@@ -84,6 +48,7 @@ switch ($_GET['form'] ?? '') {
|
|||||||
switch ($_GET['action'] ?? '') {
|
switch ($_GET['action'] ?? '') {
|
||||||
case 'add':
|
case 'add':
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
|
$all_centros = db_get_centro_ids();
|
||||||
?>
|
?>
|
||||||
<form method="post" action="?form=save_edit">
|
<form method="post" action="?form=save_edit">
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
@@ -104,70 +69,52 @@ switch ($_GET['action'] ?? '') {
|
|||||||
<b>Permisos:</b>
|
<b>Permisos:</b>
|
||||||
<div class="accordion mt-3" id="permissionsAccordion">
|
<div class="accordion mt-3" id="permissionsAccordion">
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingSysadmin">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSysadmin" aria-expanded="true" aria-controls="collapseSysadmin">
|
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSysadmin">Administración del sistema</button>
|
||||||
Administración del sistema
|
|
||||||
</button>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseSysadmin" class="accordion-collapse collapse show" aria-labelledby="headingSysadmin" data-bs-parent="#permissionsAccordion">
|
<div id="collapseSysadmin" class="accordion-collapse collapse show" data-bs-parent="#permissionsAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="sysadmin:access" id="sysadmin-access">
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="sysadmin:access" id="sysadmin-access">
|
||||||
<label class="form-check-label" for="sysadmin-access">
|
<label class="form-check-label" for="sysadmin-access">Acceso</label>
|
||||||
Acceso
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingEntreaulas">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseEntreaulas" aria-expanded="false" aria-controls="collapseEntreaulas">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseEntreaulas">EntreAulas</button>
|
||||||
EntreAulas
|
|
||||||
</button>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseEntreaulas" class="accordion-collapse collapse" aria-labelledby="headingEntreaulas" data-bs-parent="#permissionsAccordion">
|
<div id="collapseEntreaulas" class="accordion-collapse collapse" data-bs-parent="#permissionsAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:access" id="entreaulas-access">
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:access" id="entreaulas-access">
|
||||||
<label class="form-check-label" for="entreaulas-access">
|
<label class="form-check-label" for="entreaulas-access">Acceso</label>
|
||||||
Acceso
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:docente" id="entreaulas-docente">
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:docente" id="entreaulas-docente">
|
||||||
<label class="form-check-label" for="entreaulas-docente">
|
<label class="form-check-label" for="entreaulas-docente">Docente</label>
|
||||||
Docente
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:proyectos:delete" id="entreaulas-proyectos-delete">
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:proyectos:delete" id="entreaulas-proyectos-delete">
|
||||||
<label class="form-check-label" for="entreaulas-proyectos-delete">
|
<label class="form-check-label" for="entreaulas-proyectos-delete">Eliminar Proyectos</label>
|
||||||
Eliminar Proyectos
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingSupercafe">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSupercafe" aria-expanded="false" aria-controls="collapseSupercafe">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSupercafe">SuperCafe</button>
|
||||||
SuperCafe
|
|
||||||
</button>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseSupercafe" class="accordion-collapse collapse" aria-labelledby="headingSupercafe" data-bs-parent="#permissionsAccordion">
|
<div id="collapseSupercafe" class="accordion-collapse collapse" data-bs-parent="#permissionsAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:access" id="supercafe-access">
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:access" id="supercafe-access">
|
||||||
<label class="form-check-label" for="supercafe-access">
|
<label class="form-check-label" for="supercafe-access">Acceso</label>
|
||||||
Acceso
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:edit" id="supercafe-edit">
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:edit" id="supercafe-edit">
|
||||||
<label class="form-check-label" for="supercafe-edit">
|
<label class="form-check-label" for="supercafe-edit">Editar comandas</label>
|
||||||
Editar comandas
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -183,13 +130,9 @@ switch ($_GET['action'] ?? '') {
|
|||||||
<label for="centro" class="form-label">Centro asociado:</label>
|
<label for="centro" class="form-label">Centro asociado:</label>
|
||||||
<select id="centro" name="centro" class="form-select">
|
<select id="centro" name="centro" class="form-select">
|
||||||
<option value="">-- Selecciona un centro --</option>
|
<option value="">-- Selecciona un centro --</option>
|
||||||
<?php
|
<?php foreach ($all_centros as $cid): ?>
|
||||||
$centros_folders_add = glob("/DATA/entreaulas/Centros/*", GLOB_ONLYDIR) ?: [];
|
<option value="<?= htmlspecialchars($cid) ?>"><?= htmlspecialchars($cid) ?></option>
|
||||||
foreach ($centros_folders_add as $centro_folder) {
|
<?php endforeach; ?>
|
||||||
$centro_id_opt = basename($centro_folder);
|
|
||||||
echo '<option value="' . htmlspecialchars($centro_id_opt) . '">' . htmlspecialchars($centro_id_opt) . '</option>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
@@ -207,110 +150,89 @@ switch ($_GET['action'] ?? '') {
|
|||||||
<?php
|
<?php
|
||||||
require_once "_incl/post-body.php";
|
require_once "_incl/post-body.php";
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'edit':
|
case 'edit':
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
$username = safe_username($_GET['user'] ?? '');
|
$username = safe_username($_GET['user'] ?? '');
|
||||||
if (empty($username)) {
|
if (empty($username)) {
|
||||||
die("Nombre de usuario inválido.");
|
die("Nombre de usuario inválido.");
|
||||||
}
|
}
|
||||||
$user_file = get_user_file_path($username);
|
$row = db_get_user($username);
|
||||||
if (!file_exists($user_file) || !is_readable($user_file)) {
|
if (!$row) {
|
||||||
die("Usuario no encontrado o datos no disponibles.");
|
die("Usuario no encontrado.");
|
||||||
}
|
|
||||||
$jsonContent = file_get_contents($user_file);
|
|
||||||
if ($jsonContent === false) {
|
|
||||||
die("Error al leer los datos del usuario.");
|
|
||||||
}
|
|
||||||
$userdata = json_decode($jsonContent, true);
|
|
||||||
if (!is_array($userdata) || json_last_error() !== JSON_ERROR_NONE) {
|
|
||||||
die("Datos de usuario corruptos o con formato inválido.");
|
|
||||||
}
|
}
|
||||||
|
$userdata = db_build_auth_data($row);
|
||||||
|
$all_centros = db_get_centro_ids();
|
||||||
|
$user_centro = safe_centro_id($userdata['entreaulas']['centro'] ?? '');
|
||||||
|
$aularios = $user_centro !== '' ? db_get_aularios($user_centro) : [];
|
||||||
?>
|
?>
|
||||||
<form method="post" action="?form=save_edit">
|
<form method="post" action="?form=save_edit">
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1>Editar Usuario: <?php echo htmlspecialchars($username); ?></h1>
|
<h1>Editar Usuario: <?= htmlspecialchars($username) ?></h1>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="display_name" class="form-label">Nombre para mostrar:</label>
|
<label for="display_name" class="form-label">Nombre para mostrar:</label>
|
||||||
<input type="text" id="display_name" name="display_name" value="<?php echo htmlspecialchars($userdata['display_name'] ?? ''); ?>" class="form-control" required>
|
<input type="text" id="display_name" name="display_name" value="<?= htmlspecialchars($userdata['display_name'] ?? '') ?>" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="email" class="form-label">Correo electrónico:</label>
|
<label for="email" class="form-label">Correo electrónico:</label>
|
||||||
<input type="email" id="email" name="email" value="<?php echo htmlspecialchars($userdata['email'] ?? ''); ?>" class="form-control" required>
|
<input type="email" id="email" name="email" value="<?= htmlspecialchars($userdata['email'] ?? '') ?>" class="form-control" required>
|
||||||
</div>
|
</div>
|
||||||
<b>Permisos:</b>
|
<b>Permisos:</b>
|
||||||
<div class="accordion mt-3" id="permissionsAccordion">
|
<div class="accordion mt-3" id="permissionsAccordion">
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingSysadmin">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSysadmin" aria-expanded="true" aria-controls="collapseSysadmin">
|
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSysadmin">Administración del sistema</button>
|
||||||
Administración del sistema
|
|
||||||
</button>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseSysadmin" class="accordion-collapse collapse show" aria-labelledby="headingSysadmin" data-bs-parent="#permissionsAccordion">
|
<div id="collapseSysadmin" class="accordion-collapse collapse show" data-bs-parent="#permissionsAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="sysadmin:access" id="sysadmin-access" <?php if (in_array('sysadmin:access', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="sysadmin:access" id="sysadmin-access" <?= in_array('sysadmin:access', $userdata['permissions'] ?? []) ? 'checked' : '' ?>>
|
||||||
<label class="form-check-label" for="sysadmin-access">
|
<label class="form-check-label" for="sysadmin-access">Acceso</label>
|
||||||
Acceso
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingEntreaulas">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseEntreaulas" aria-expanded="false" aria-controls="collapseEntreaulas">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseEntreaulas">EntreAulas</button>
|
||||||
EntreAulas
|
|
||||||
</button>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseEntreaulas" class="accordion-collapse collapse" aria-labelledby="headingEntreaulas" data-bs-parent="#permissionsAccordion">
|
<div id="collapseEntreaulas" class="accordion-collapse collapse" data-bs-parent="#permissionsAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:access" id="entreaulas-access" <?php if (in_array('entreaulas:access', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:access" id="entreaulas-access" <?= in_array('entreaulas:access', $userdata['permissions'] ?? []) ? 'checked' : '' ?>>
|
||||||
<label class="form-check-label" for="entreaulas-access">
|
<label class="form-check-label" for="entreaulas-access">Acceso</label>
|
||||||
Acceso
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:docente" id="entreaulas-docente" <?php if (in_array('entreaulas:docente', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:docente" id="entreaulas-docente" <?= in_array('entreaulas:docente', $userdata['permissions'] ?? []) ? 'checked' : '' ?>>
|
||||||
<label class="form-check-label" for="entreaulas-docente">
|
<label class="form-check-label" for="entreaulas-docente">Docente</label>
|
||||||
Docente
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:proyectos:delete" id="entreaulas-proyectos-delete" <?php if (in_array('entreaulas:proyectos:delete', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="entreaulas:proyectos:delete" id="entreaulas-proyectos-delete" <?= in_array('entreaulas:proyectos:delete', $userdata['permissions'] ?? []) ? 'checked' : '' ?>>
|
||||||
<label class="form-check-label" for="entreaulas-proyectos-delete">
|
<label class="form-check-label" for="entreaulas-proyectos-delete">Eliminar Proyectos</label>
|
||||||
Eliminar Proyectos
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="accordion-item">
|
<div class="accordion-item">
|
||||||
<h2 class="accordion-header" id="headingSupercafe">
|
<h2 class="accordion-header">
|
||||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSupercafe" aria-expanded="false" aria-controls="collapseSupercafe">
|
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseSupercafe">SuperCafe</button>
|
||||||
SuperCafe
|
|
||||||
</button>
|
|
||||||
</h2>
|
</h2>
|
||||||
<div id="collapseSupercafe" class="accordion-collapse collapse" aria-labelledby="headingSupercafe" data-bs-parent="#permissionsAccordion">
|
<div id="collapseSupercafe" class="accordion-collapse collapse" data-bs-parent="#permissionsAccordion">
|
||||||
<div class="accordion-body">
|
<div class="accordion-body">
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:access" id="supercafe-access" <?php if (in_array('supercafe:access', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:access" id="supercafe-access" <?= in_array('supercafe:access', $userdata['permissions'] ?? []) ? 'checked' : '' ?>>
|
||||||
<label class="form-check-label" for="supercafe-access">
|
<label class="form-check-label" for="supercafe-access">Acceso</label>
|
||||||
Acceso
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="form-check">
|
<div class="form-check">
|
||||||
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:edit" id="supercafe-edit" <?php if (in_array('supercafe:edit', $userdata['permissions'] ?? [])) echo 'checked'; ?>>
|
<input class="form-check-input" type="checkbox" name="permissions[]" value="supercafe:edit" id="supercafe-edit" <?= in_array('supercafe:edit', $userdata['permissions'] ?? []) ? 'checked' : '' ?>>
|
||||||
<label class="form-check-label" for="supercafe-edit">
|
<label class="form-check-label" for="supercafe-edit">Editar comandas</label>
|
||||||
Editar comandas
|
|
||||||
</label>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<input type="hidden" name="username" value="<?php echo htmlspecialchars($username); ?>">
|
<input type="hidden" name="username" value="<?= htmlspecialchars($username) ?>">
|
||||||
<button type="submit" class="btn btn-primary mt-3">Guardar Cambios</button>
|
<button type="submit" class="btn btn-primary mt-3">Guardar Cambios</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -320,98 +242,76 @@ switch ($_GET['action'] ?? '') {
|
|||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="centro" class="form-label">Centro asociado:</label>
|
<label for="centro" class="form-label">Centro asociado:</label>
|
||||||
<select id="centro" name="centro" class="form-select" required>
|
<select id="centro" name="centro" class="form-select" required>
|
||||||
<option value="" <?php if (empty($userdata["entreaulas"]['centro'] ?? '')) echo 'selected'; ?>>-- Selecciona un centro --</option>
|
<option value="" <?= empty($user_centro) ? 'selected' : '' ?>>-- Selecciona un centro --</option>
|
||||||
<?php
|
<?php foreach ($all_centros as $cid): ?>
|
||||||
$centros_folders = glob("/DATA/entreaulas/Centros/*", GLOB_ONLYDIR);
|
<option value="<?= htmlspecialchars($cid) ?>" <?= $user_centro === $cid ? 'selected' : '' ?>><?= htmlspecialchars($cid) ?></option>
|
||||||
foreach ($centros_folders as $centro_folder) {
|
<?php endforeach; ?>
|
||||||
$centro_id = basename($centro_folder);
|
|
||||||
echo '<option value="' . htmlspecialchars($centro_id) . '"';
|
|
||||||
if (($userdata["entreaulas"]['centro'] ?? '') === $centro_id) {
|
|
||||||
echo ' selected';
|
|
||||||
}
|
|
||||||
echo '>' . htmlspecialchars($centro_id) . '</option>';
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label for="role" class="form-label">Rol en EntreAulas:</label>
|
<label for="role" class="form-label">Rol en EntreAulas:</label>
|
||||||
<select id="role" name="role" class="form-select" required>
|
<select id="role" name="role" class="form-select" required>
|
||||||
<option value="" <?php if (empty($userdata["entreaulas"]['role'] ?? '')) echo 'selected'; ?>>-- Selecciona un rol --</option>
|
<option value="" <?= empty($userdata['entreaulas']['role'] ?? '') ? 'selected' : '' ?>>-- Selecciona un rol --</option>
|
||||||
<option value="teacher" <?php if (($userdata["entreaulas"]['role'] ?? '') === 'teacher') echo 'selected'; ?>>Profesor</option>
|
<option value="teacher" <?= ($userdata['entreaulas']['role'] ?? '') === 'teacher' ? 'selected' : '' ?>>Profesor</option>
|
||||||
<option value="student" <?php if (($userdata["entreaulas"]['role'] ?? '') === 'student') echo 'selected'; ?>>Estudiante</option>
|
<option value="student" <?= ($userdata['entreaulas']['role'] ?? '') === 'student' ? 'selected' : '' ?>>Estudiante</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
<div class="mb-3">
|
<div class="mb-3">
|
||||||
<label class="form-label">Aulas asignadas: <small>(Guarda primero para actualizar la lista)</small></label><br>
|
<label class="form-label">Aulas asignadas: <small>(Guarda primero para actualizar la lista)</small></label><br>
|
||||||
<?php
|
<?php foreach ($aularios as $aula_id => $aula_data): ?>
|
||||||
$user_centro = safe_centro_id($userdata["entreaulas"]['centro'] ?? '');
|
<div class="form-check form-check-inline">
|
||||||
$aulas_filelist = $user_centro !== '' ? (glob("/DATA/entreaulas/Centros/" . $user_centro . "/Aularios/*.json") ?: []) : [];
|
<input class="form-check-input" type="checkbox" name="aulas[]"
|
||||||
foreach ($aulas_filelist as $aula_file) {
|
value="<?= htmlspecialchars($aula_id) ?>"
|
||||||
$aula_data = json_decode(file_get_contents($aula_file), true);
|
id="aula-<?= htmlspecialchars($aula_id) ?>"
|
||||||
$aula_id = safe_aulario_id(basename($aula_file, ".json"));
|
<?= in_array($aula_id, $userdata['entreaulas']['aulas'] ?? []) ? 'checked' : '' ?>>
|
||||||
$is_assigned = in_array($aula_id, $userdata["entreaulas"]['aulas'] ?? []);
|
<label class="form-check-label" for="aula-<?= htmlspecialchars($aula_id) ?>">
|
||||||
echo '<div class="form-check form-check-inline">';
|
<?= htmlspecialchars($aula_data['name'] ?? $aula_id) ?>
|
||||||
echo '<input class="form-check-input" type="checkbox" name="aulas[]" value="' . htmlspecialchars($aula_id) . '" id="aula-' . htmlspecialchars($aula_id) . '" ' . ($is_assigned ? 'checked' : '') . '>';
|
</label>
|
||||||
echo '<label class="form-check-label" for="aula-' . htmlspecialchars($aula_id) . '">' . htmlspecialchars($aula_data['name'] ?? $aula_id) . '</label>';
|
</div>
|
||||||
echo '</div>';
|
<?php endforeach; ?>
|
||||||
}
|
|
||||||
?>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h2>Cambiar contraseña</h2>
|
<h2>Cambiar contraseña</h2>
|
||||||
<p>Para cambiar la contraseña de este usuario, utiliza la herramienta de restablecimiento de contraseñas disponible en el siguiente enlace:</p>
|
<a href="/sysadmin/reset_password.php?user=<?= urlencode($username) ?>" class="btn btn-secondary">Restablecer Contraseña</a>
|
||||||
<a href="/sysadmin/reset_password.php?user=<?php echo urlencode($username); ?>" class="btn btn-secondary">Restablecer Contraseña</a>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
<?php
|
<?php
|
||||||
require_once "_incl/post-body.php";
|
require_once "_incl/post-body.php";
|
||||||
break;
|
break;
|
||||||
case "index":
|
|
||||||
|
case 'index':
|
||||||
default:
|
default:
|
||||||
require_once "_incl/pre-body.php";
|
require_once "_incl/pre-body.php";
|
||||||
|
$all_users = db_get_all_users();
|
||||||
?>
|
?>
|
||||||
<div class="card pad">
|
<div class="card pad">
|
||||||
<div>
|
<div>
|
||||||
<h1>Gestión de Usuarios</h1>
|
<h1>Gestión de Usuarios</h1>
|
||||||
<p>Desde esta sección puedes gestionar los usuarios del sistema. Puedes agregar, editar o eliminar usuarios según sea necesario.</p>
|
<p>Desde esta sección puedes gestionar los usuarios del sistema.</p>
|
||||||
<table class="table table-striped table-hover">
|
<table class="table table-striped table-hover">
|
||||||
<thead class="table-dark">
|
<thead class="table-dark">
|
||||||
<tr>
|
<tr>
|
||||||
<th>Usuario</th>
|
<th>Usuario</th>
|
||||||
<th>Nombre</th>
|
<th>Nombre</th>
|
||||||
<th>Correo</th>
|
<th>Correo</th>
|
||||||
<th>
|
<th><a href="?action=add" class="btn btn-success">+ Nuevo</a></th>
|
||||||
<a href="?action=add" class="btn btn-success">+ Nuevo</a>
|
|
||||||
</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<?php
|
<?php foreach ($all_users as $u): ?>
|
||||||
$users_filelist = glob(USERS_DIR . '*.json') ?: [];
|
<tr>
|
||||||
foreach ($users_filelist as $user_file) {
|
<td><?= htmlspecialchars($u['username']) ?></td>
|
||||||
$userdata = json_decode(file_get_contents($user_file), true);
|
<td><?= htmlspecialchars($u['display_name'] ?: 'N/A') ?></td>
|
||||||
if (!is_array($userdata)) {
|
<td><?= htmlspecialchars($u['email'] ?: 'N/A') ?></td>
|
||||||
error_log("users.php: corrupted or unreadable user file: $user_file");
|
<td>
|
||||||
$userdata = [];
|
<a href="?action=edit&user=<?= urlencode($u['username']) ?>" class="btn btn-primary">Editar</a>
|
||||||
}
|
</td>
|
||||||
// Username is the filename without path and extension
|
</tr>
|
||||||
$username = basename($user_file, ".json");
|
<?php endforeach; ?>
|
||||||
echo "<tr>";
|
|
||||||
echo "<td>" . htmlspecialchars($username) . "</td>";
|
|
||||||
echo "<td>" . htmlspecialchars($userdata['display_name'] ?? 'N/A') . "</td>";
|
|
||||||
echo "<td>" . htmlspecialchars($userdata['email'] ?? 'N/A') . "</td>";
|
|
||||||
echo "<td>";
|
|
||||||
echo '<a href="?action=edit&user=' . urlencode($username) . '" class="btn btn-primary">Editar</a> ';
|
|
||||||
echo '<a href="?action=delete&user=' . urlencode($username) . '" class="btn btn-danger">Eliminar</a>';
|
|
||||||
echo "</td>";
|
|
||||||
echo "</tr>";
|
|
||||||
}
|
|
||||||
?>
|
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
@@ -420,4 +320,3 @@ switch ($_GET['action'] ?? '') {
|
|||||||
require_once "_incl/post-body.php";
|
require_once "_incl/post-body.php";
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
?>
|
|
||||||
Reference in New Issue
Block a user