| Name | Modified | Size | Downloads / Week |
|---|---|---|---|
| Parent folder | |||
| README.md | 2026-10-04 | 9.5 kB | |
| v0.6.0 -- Arbeitspakete, Pro-Teaser, Side-Panel _ Updater source code.tar.gz | 2026-10-04 | 1.2 MB | |
| v0.6.0 -- Arbeitspakete, Pro-Teaser, Side-Panel _ Updater source code.zip | 2026-10-04 | 1.3 MB | |
| Totals: 3 Items | 2.5 MB | 0 | |
Added
- Side-panel now also renders HTML banners as sandboxed
<iframe>s (in addition to image/SVG), so creatives can be fluid (adapt to the slot) and carry their own clickable links. A feed item may have animageor aniframeURL;iframeURLs are validated against the same first-party allow-list, and the CSP gainsframe-src/child-srcfor the banner host(s) (derived frombanners.allowed_hosts, so HTML banners load without further config). - Arbeitspaket (Workpackage) layer between projects and tasks (fixes [#65]). Hierarchy becomes Kunde → Projekt → Arbeitspaket → Aufgabe, matching Pro so CSV exports/imports stay portable between editions.
- New
work_packagestable (SQLite + MySQL) + nullablework_package_idFKs ontasksandtime_entries(denormalized for the same reason asproject_id/client_id). - Idempotent migration (
schema/migrations/2026-10-04-01-work- packages.php): CREATE TABLE IF NOT EXISTS, column-exists guards around ALTER TABLE for upgrading installs, and a transactional backfill that seeds a default "Allgemein" work package per project and remaps every existing task + time entry to it. Zero-touch for live installations. - New admin nav entry Arbeitspakete between Projekte and
Aufgaben; CRUD view at
?r=workpackagesunderstructure.manage. - Task form grows an "Arbeitspaket" select; the entries form option-groups tasks by Kunde / Projekt / Arbeitspaket so the hierarchy is visible when picking.
- CSV export adds
work_package_codeandwork_package_namecolumns. CSV import still accepts files without these columns — tasks land in the project's default "Allgemein" WP (backwards compat for existing exports and third-party CSVs). - Pro-feature teaser pages. Four new nav entries — Rechnungen,
Angebote, Budget, Rollen Pro — open dimmed read-only mockups of the
features that only exist in Timeminator Pro. Each page carries a
sticky upsell banner on the right edge linking to
pro_url. No data is written, nothing is sent; the mockups are seeded from the user's real clients / projects where available so the preview looks plausible. All inline positioning (budget progress bars) is emitted into nonce-authorized<style>blocks so the strict CSP stays strict. LoginThrottle::retryAfterSeconds($username, $ip)— precise upper bound in seconds until whichever locked bucket clears first; used to fill theRetry-Afterheader.- Dockerfile + docker-compose.yml for zero-infrastructure local
evaluation and self-hosting. Image is
php:8.3-apachewith PDO (MySQL and SQLite), zip for the in-app updater and mod_rewrite enabled — the same shape as a typical PHP shared host. Two Compose profiles:sqlite(single service) andmysql(app + MariaDB).data/is persisted on a named volume. The project remains Composer-free at install time:composer.jsonandvendor/are excluded from the build context via.dockerignore. README gains a "Run it in Docker" section (fixes [#19]). - Update banner. Admins see a small "new version available" panel
under the top bar when the release manifest reports a newer version.
Three actions: Ignorieren (dismiss for this session — comes back
next login), Ueberspringen (skip this specific version until a
newer one appears; persisted globally), Update (jump to
Admin → System to apply). All buttons are forms with CSRF tokens; no
inline JS. The banner respects
Updater::isDisabled()and stays hidden for non-admins. - Opportunistic update check. The layout partial triggers
Updater::opportunisticCheck()once every 24 h per install (throttled by the cache timestamp). Silent on failure — a page never breaks because the release manifest is unreachable. .phpdata migrations.Updater::runMigrations()now picks up both.sqland.phpfiles fromschema/migrations/<driver>/. PHP migrations arerequired in an isolated closure and receive access toDBandSettings— for backfills that cannot be expressed as one SQL statement. Applied migrations are tracked by basename inSettings['applied_migrations']as before, so an existing install is unaffected.docs/schema-migrations.md. Documents the migration naming convention (YYYY-MM-DD-NN-slug.<ext>), the additive-by-default policy, the.phpmigration flow and the pre-1.0 compatibility contract.referrer_or_default()helper. Same-origin-only redirect helper for the new dismiss/skip flow — keeps the admin on whatever page they were on instead of jumping to the dashboard.- Community banner carousel with server-side feed loading, offline fallback and opt-outable telemetry (fixes [#22]). A slim, collapsible banner column on the right of the main layout rotates through FelixSchallerCOM services, tools and news:
- Creatives are loaded server-side from a configurable subdomain
(
banners.endpoint, defaulthttps://banners.felixschaller.com/feed.json) through a first-party route (index.php?r=banners), so the strict default CSP (connect-src 'self') needs no exception. The fetch is async and never blocks page rendering, and results are cached forbanners.cache_ttl. - On any network failure the app falls back to the bundled default set in
assets/banners/default.json, so the slot is never empty or broken on an isolated server. - Every creative is sanitized: links are accepted only as
httpsfelixschaller.comURLs and open withrel="noopener noreferrer"; images only as same-origin assets orhttpsfelixschaller.comURLs. - A small anonymized usage signal rides along with each feed refresh (app
version, a random installation id stored in
data/settings.json, a salted hash of it, and a truncated request IP). No time-tracking data is ever sent. Each field is switchable underbanners.telemetryinconfig.php. - Admin → System → Community-Banner & Telemetrie adds runtime toggles:
show/hide the column (
banner_visible, default on), a master telemetry opt-out (banner_telemetry), and an endpoint override. - Set
banners['enabled'] => falseinconfig.phpto remove the whole feature and its telemetry (the Pro edition ships it off). - Documented in
docs/BANNERS.md. - Creatives are portrait "skyscraper" (300x600) SVGs with the real brand logos embedded. Bundled set covers the FelixSchallerCOM brand portfolio: AI Transformation ("AI Maturity Made Right"), Fractional & Interim Deep-Tech, the XIXUM AI-maturity assessment (xixum.ai), AF-AX ("the wing that never stalls", af-ax.com, logo + eVTOL-photo variant), and Timeminator Pro.
- The feed link/image allow-list is configurable via
banners.allowed_hosts(defaultfelixschaller.com,xixum.ai,af-ax.com; subdomains included); foreign links/images are stripped. Images may be a same-origin asset, anhttpsfirst-party URL, or an inlinedata:image/*URI, so remote creatives render under the strict default CSP without changes. - Default feed endpoint is
https://assets.felixschaller.com/feed.json.
Changed
- Banner slot renamed to a side-panel (ad-blocker-neutral: no "banner"
anywhere in element classes, ids, or the fetch URL). Element ids /
classes are now
#sidepanel/.sp-*, the first-party fetch route is?r=sidepanel, and the live feed subdomain migrated frombanner.felixschaller.comtoassets.felixschaller.comso content- blocking filter lists stop disappearing the whole column. Config keys (banners.*) and the admin UI label ("Anzeige") are unchanged. - PromoFeed system retired. The provisional
src/PromoFeed.php+views/partials/promo_sidebar.php+assets/promo-fallback.jsonwiring (branch-only) is superseded by theBanners+#sidepanelcarousel which supports bothimagecreatives and sandboxed HTMLiframebanners. - LoginThrottle now escalates the lockout window for repeat offenders in a 24 h horizon: 15 min → 1 h → 4 h → 24 h. The first lockout stays at the baseline 15 min; every subsequent lockout inside the horizon climbs one rung. Message copy reflects the actual wait ("… in 1 Stunde erneut versuchen") (fixes [#38]).
ctrl_loginnow returns HTTP 429 Too Many Requests with aRetry-After: <seconds>header whenLoginThrottlerefuses a login. The German error string in the page body stays unchanged, so the human path is identical; the change is for monitoring probes, health checks and retry scripts (fixes [#37]).asset()now appends a cache-busting?v=<filemtime>query string to everyassets/*URL (CSS/JS/images) so browsers pick up a changed file immediately after a deploy — including a one-off hotfix viascp, not just a version bump. Falls back toapp_version()when the file is not found on disk (#78)..gitignorenow excludes runtime artifacts underdata/(settings.json,*.json,*.log) so per-install state and logs stay out of the repo.docs/ISSUES.mdand the README privacy section describe the banner carousel and its telemetry.
Config
- New
bannersblock inconfig.sample.php(enabled,endpoint,cache_ttl,telemetry.*,hash_salt). Pre-1.0: this is an additive, optional block; existing installs keep working with the Community defaults.