Журнал изменений
Replace direct counter/backup.log file read with getSchedulerState('dbbackup') so the last backup timestamp comes from the same source as the scheduler UI.
Core changes:
- getLastBackupRunLabel (admin/modules/monitor.php):
- Remove backup.log file read
- Use getSchedulerState('dbbackup')['last_success'] instead
- Keep BACKUP_DIR mtime fallback unchanged
Benefits:
- Single source of truth for backup timestamps
- No dependency on a separate counter file
Technical notes:
- backup.log is no longer read by monitor; file can be removed from disk
Remove the separate $ext map and hardcode .log extension throughout, since all log files now share the same format. Add dump_skip path sanitization on save. Restore sess_d, sess_b, log_b and log_d fields to the conf form and confsave handler.
Core changes:
- Log file listing (admin/modules/security.php):
- Remove $ext map; all log files are .log
- Replace whitelist skip array with preg_match on .log extension
- Remove database, dump_map and monitor entries from $labels
- dump_skip sanitization (admin/modules/security.php):
- Normalize line endings, strip leading ./, collapse slashes
- Reject lines containing .. (path traversal)
- Ensure each entry ends with /; deduplicate
- Conf form + confsave (admin/modules/security.php):
- Add sess_d, sess_b, log_b to conf form output
- Add log_d field and restore _SEC_LOG_D row
- Save sess_d, sess_b, log_b, log_d in confsave handler
Benefits:
- No more silent loss of sess_d/sess_b/log_b/log_d on settings save
- dump_skip entries are normalized before storage
- Log list does not depend on a manually maintained extension map
Technical notes:
- $ext variable removed; fileview/down/del now always use .log
- Copyright header encoding fixed (© instead of ©)
Consolidate getSchedulerNextTime and getSchedulerPlannedTime into a single function to eliminate the redundant two-step call chain. Simplify the boot sequence in system.php by replacing inline conditional blocks with a direct require_once of the common includes.
Core changes:
- Scheduler time resolution (core/system.php):
Remove getSchedulerConfig, getSchedulerDir, getSchedulerFile, getSchedulerBeat, getSchedulerBase, getSchedulerJob, getSchedulerSettings (moved to dedicated scheduler core)
Merge getSchedulerNextTime into getSchedulerPlannedTime
- Now computes next run directly from last_run state
- Removes unused $from parameter
- Boot sequence (core/system.php):
- Replace scattered require logic with require_once for security, user/admin and template
Benefits:
- Fewer function calls per scheduler dispatch cycle
- Simpler call site in scheduler.php (one function instead of two)
- Boot sequence is now linear and readable
Technical notes:
- getSchedulerPlannedTime signature unchanged (array $job, array $state)
- Removed functions were unused outside core; no external API break
Rename all SCHEDULER* constants exceeding 18-char limit to comply with the updated constants.md naming rule (max 18 chars for lang constants). Add two new warning constants with a direct link to Security settings.
Core changes:
- _SCHEDULER_NEXT_RUN → _SCHEDULER_NEXTRUN
- _SCHEDULER_LAST_RUN → _SCHEDULER_LASTRUN
- _SCHEDULER_DURATION → _SCHEDULER_RUNTIME
- _SCHEDULER_SCHEDULE → _SCHEDULER_SCHED
- _SCHEDULER_PRIORITY → _SCHEDULER_PRIO
- _SCHEDULER_PRIORITY_INFO → _SCHEDULER_PRIOTIP
- _SCHEDULER_PRIORITY_DUP → _SCHEDULER_PRIODUP
- _SCHEDULER_UNLOCKED → _SCHEDULER_UNLOCKD
- _SCHEDULER_URL_INFO → _SCHEDULER_URLINFO
- _SCHEDULER_SYSTEM_INFO → _SCHEDULER_SYSINFO
- _SCHEDULER_SCHEDULE_INFO → _SCHEDULER_CRONFMT
- _SCHEDULER_WARN_DMAP → _SCHEDULER_WARNLOG
- Add _SCHEDULER_WARN_DB, _SCHEDULER_WARNLOG, _SCHEDULER_WARN_GO (all 6 locales)
- scheduler.php: show config warnings when log_b/log_d disabled, link to security settings
Benefits:
- All SCHEDULER* constants now ≤18 chars (rule compliant)
- Warnings inform admin where to enable the feature
Technical notes:
- Updated in all 6 locales simultaneously (de, en, fr, pl, ru, uk)
- Warning text uses correct semantic mapping: log_b=DB backup, log_d=file scan
File is runtime state and already covered by .gitignore (/storage/counter/*). Was tracked only because it existed in the index before the ignore rule.
Replace string-based function_exists() dispatch and four wrapper functions with a typed match dispatcher. System jobs are now identified by a fixed 'system' key in config instead of a callable handler string. Sitemap admin trigger is routed through the scheduler flow.
Core changes:
- Dispatcher (core/system.php):
Add addSchedulerSystemJob() with match on 'backup'/'filescan'/'sitemap'/'newsletter'
- Replaces dynamic function_exists($handler) call
- Unknown system key returns failed status with explicit message
Remove addSchedulerBackup(), addSchedulerFilescan(), addSitemapTask() wrappers
- Renamed doSitemap() to addSitemapTask() for naming consistency
- Update addSchedulerRun() dispatch to call addSchedulerSystemJob()
- Update getSchedulerJob() to normalize 'system' field instead of 'handler'
- Update getSchedulerNextJob() validity checks to use type + system
- Config (config/scheduler.php):
Replace 'handler' field with 'system' in all 4 system jobs
- dbbackup -> system: backup
- filescan -> system: filescan
- newsletter -> system: newsletter
- sitemap -> system: sitemap
- Admin UI (admin/modules/scheduler.php):
- Show 'system' value (readonly) instead of handler string
- save() persists 'system' field instead of 'handler'
- Remove 'handler' key from default new custom job
- Sitemap admin (modules/sitemap/admin/index.php):
Replace direct doSitemap() call with addSchedulerRun('sitemap', 'manual')
- Uses lock mechanism, prevents race conditions
Benefits:
- Eliminates dynamic function dispatch via string from config (security improvement)
- Single dispatch point for all system jobs
- Consistent naming: addBackupTask, addFilescanTask, addSitemapTask
Technical notes:
- BREAKING CHANGE: 'handler' field is no longer read at runtime
- Existing configs without 'system' field will treat jobs as invalid
- doSitemap() renamed to addSitemapTask(); all call sites updated
- Verified: dbbackup, filescan, sitemap manual run successful; error logs clean
Harden authentication, SQL queries, and input handling across admin and modules; migrate Bootstrap 5 and HTMX to plugins/ with proper structure.
Core changes:
- Authentication fixes (core/security.php, core/system.php, admin/index.php):
- isAdmin(): remove substr() truncation on bcrypt hash (was 72 chars, now full)
- is_user(): replace loose == with hash_equals() for timing-safe comparison
- check_admin(), add_admin(): header('Location:') replaced with setRedirect()
- logout(): raw SQL concatenation replaced with prepared statement + setRedirect()
- changeeditor(): raw SQL replaced with prepared statement; $_POST → getVar()
- login(): raw $_POST['aname'], $_POST['aemail'] → getVar()
- SQL hardening — LIKE prepared statements (8 modules):
modules/news, media, files, links, faq, pages, shop: $let interpolation replaced with :let placeholder + ['let' => $let.'%'] params
- modules/help: $let and $uid both replaced with named placeholders
- news: removed redundant addslashes() on $let
- Security headers (core/system.php):
- Added X-Content-Type-Options: nosniff
- Added X-Frame-Options: SAMEORIGIN
- Added Referrer-Policy: strict-origin-when-cross-origin
- Frontend plugin structure (plugins/, config/global.php):
- Bootstrap 5 (CSS + JS bundle + Icons) moved to plugins/bootstrap/
- HTMX moved from templates/admin/js/ to plugins/htmx/
- bootstrap-icons.css, fonts/ removed from templates/admin/
- script_f and css_f updated to reflect new paths
- Scheduler module (admin/modules/scheduler.php, config/scheduler.php):
- Full scheduler module implementation with cron-based job execution
- Newsletter module (admin/modules/newsletter.php, config/newsletter.php):
- Newsletter configuration and admin module updates
Benefits:
- Timing-safe password comparison prevents brute-force timing attacks
- Prepared statements on LIKE queries eliminate SQL injection vectors
- Security headers protect against MIME sniffing, clickjacking, referrer leakage
- Centralised plugin paths simplify future library updates
Technical notes:
- bcrypt hashes are 60 chars; old 40-char substr caused login failure after migration
- setRedirect() calls exit internally; explicit exit after header() no longer needed
- params array passed as 10th arg to setArticleNumbers() — already supported
Small maintenance changes to newsletter module and sitemap configuration to align with scheduler-based job dispatch and updated admin panel.
Core changes:
- Newsletter (admin/modules/newsletter.php, admin/info/newsletter/*.html):
- Minor adjustments following scheduler integration refactor
- Sitemap (config/sitemap.php, modules/sitemap/admin/index.php, sitemap.xml):
- Sitemap config and admin panel aligned with current module structure
- sitemap.xml regenerated
Benefits:
- Consistent state after scheduler refactor
- Info pages reflect current system behavior
Expands the security statistics panel to cover all files in storage/logs/, replacing the .log-only filter with a label-based allowlist approach. Removes hardcoded .log extension throughout; .json files (dump_map, monitor) are now handled via an $ext exception map.
Core changes:
- Security module (admin/modules/security.php):
$labels array extended: database, dump, dump_log, dump_map, error_file, error_php, error_site, error_sql, hack, log, log_admin, log_user, monitor, warn
- $ext map: ['dump_map' => 'json', 'monitor' => 'json']
- security(): replaced preg_match('.log') with skip-list + isset($labels)
- fileview(), down(), del(): use $ext[$file] ?? 'log' for path and filename
- confsave(): persists sess_d, sess_b, log_b, log_d; dump_skip field added
- Info pages (admin/info/security/*.html — all 6):
- Reference to hardcoded interval setting replaced with Scheduler module link
- Config (config/security.php):
- dump_skip default removed (now managed via Scheduler/confsave)
- log_d default adjusted
Benefits:
- .json log files (dump_map, monitor) now visible and downloadable in UI
- No unknown files shown: only keys present in $labels are rendered
- .htaccess and index.html automatically skipped
Technical notes:
- filterVar strips dots, so file extensions cannot be passed via URL
- Extension is derived server-side from $ext map with 'log' fallback
Introduces a configurable task scheduler with cron-format schedules, per-job state tracking, lock/timeout protection, and a HTMX-powered live status panel. Replaces hardcoded filereport/backup/sitemap/newsletter triggers in index.php with a unified scheduler dispatch.
Core changes:
- Scheduler module (admin/modules/scheduler.php):
- Live status table with HTMX auto-refresh per job
- Add/edit/delete custom jobs, unlock stuck jobs, manual run trigger
- Cron-format schedule field with format hint
- Scheduler engine (core/system.php):
- getSchedulerConfig(), getSchedulerDir(), getSchedulerFile()
- addSchedulerRun(): dispatches filereport, backup, sitemap, newsletter
- checkSchedulerAccess(): validates cron/manual token access
- dump.json renamed to dump_map.json to avoid key conflict with dump.log
- Dispatcher (index.php):
- New case 'scheduler' in go==3 branch: validates access, runs job, returns JSON
- Config (config/scheduler.php, config/modules.php):
- Default scheduler config with system jobs (filescan, backup, sitemap, newsletter)
- scheduler module entry added to modules.php
- Lang (admin/lang/*.php — all 6 languages):
SCHEDULER* constants: status, last_run, last_ok, next_run, trigger, duration, fails, schedule, handler, priority, lock, unlock, run, jobkey, url, saved, deleted, unlocked, running, idle, addjob, editjob, url_info, system_info, saveerr, manual, batch, schedule_info
_SEC_STAT_DB, _SEC_STAT_DMAP, _SEC_STAT_MON: security log labels for database.log, dump_map.json, monitor.json
Benefits:
- Decoupled scheduling from HTTP request cycle
- Per-job state files with lock/timeout prevent concurrent execution
- Extensible: custom jobs configurable without code changes
Technical notes:
- Job state stored in storage/logs/scheduler/<job>.json
- Cron schedule validated server-side; manual runs bypass schedule check
- dump_map.json replaces dump.json (breaking: rename existing file)