Privacy - v1.0.0

Privacy Changelog

[Unreleased]

[1.0.0] - 2026-06-20

Added

  • privacy:process-requests Artisan command (#42). Sweeps pending verified access + export requests on a schedule, runs the export, marks the request completed, writes an audit log, and notifies the subject. Deletion requests are intentionally skipped (manual review). Options: --type=access|export, --dry-run, --limit=N.
  • privacy:report Artisan command (#43). Generates consent / request / breach compliance reports in JSON or CSV for a period (--period=day|week|month|quarter|year|custom, with --start/--end for custom windows), optionally writes to --output=path and/or emails to --email=address.
  • privacy:install now publishes views, runs migrations with confirmation (auto-runs in --no-interaction), seeds default consent categories from cookie_categories config (idempotent), clears caches, and prints next-step guidance (#41). New flags: --skip-views, --skip-seed, --skip-migrate.
  • Comprehensive documentation (#44). README rewrite, full INSTALLATION / CONFIGURATION / UPGRADING guides, and a docs/ tree with six feature guides (cookie consent, data subject rights, admin dashboard, multi-regulation, view customization, React/Vue integration) and six API reference pages (services, models, events, helpers, Blade directives, JavaScript API).
  • Coverage badge + dedicated coverage CI job (#45).
  • GeoLocationService (#28). IP-based geolocation with ip-api, MaxMind GeoIP2, and Cloudflare CF-IPCountry providers; configurable cache duration, fallback region, and an opt-in MaxMind database path. getLocation(), getCountryCode(), getRegionCode(), and resolveRegion() surface the lookup for the regulation resolver. New privacy.geolocate middleware (GeolocateUser) stamps the resolved region onto the request as privacy_region so BaseRegulation::applies() matches against authoritative data.
  • Gdpr regulation class (#26). Explicit, granular, withdrawable consent requirements; the eight GDPR data subject rights (access, rectification, erasure, portability, restriction, objection, automated decision-making, be-informed); 72-hour breach window; 30-day response deadline; configurable consent expiry; EU member state / EEA / UK applicability via country-code translation; DPO contact metadata; six legal bases for processing.
  • Ccpa regulation class (#27). California-resident applicability; know, delete, correct, opt_out_of_sale, limit_use_of_sensitive_information, non_discrimination, data_portability rights; 45-day response deadline (extendable); financial-incentive disclosure metadata; "Do Not Sell" link requirement (configurable); CCPA business-threshold evaluator (revenue, consumer count, sale-share). The configured registry now auto-resolves gdpr and ccpa to the dedicated classes instead of the generic fallback.
  • Admin\ConsentManager Livewire component (#29) + React (AdminConsentManager) and Vue (AdminConsentManager) counterparts. Paginated, filterable consent admin table with category / status / date-range filters, search, bulk selection, and CSV / JSON export. Backed by GET /api/privacy/admin/consents for the React/Vue surfaces. Authorization enforced through the configured manage-privacy gate.
  • Admin\DataRequestManager Livewire component (#30) + React (AdminDataRequestManager) and Vue (AdminDataRequestManager) counterparts. Filterable, deadline-aware queue with overdue highlighting; per-request detail panel for manual verification, approval, rejection, and completion; audit log on every action. Backed by GET /api/privacy/admin/data-requests, GET /api/privacy/admin/data-requests/{id}, and POST /api/privacy/admin/data-requests/{id}/actions. Authorization enforced through the configured manage-privacy gate.
  • Event/listener system (#6). Six new events — DataAccessRequested, DataDeletionRequested, DataExportRequested, DataRequestCompleted, DataBreach, PrivacyPolicyUpdated — plus five default listeners (LogConsentActivity, ProcessDataAccessRequest, ProcessDataExportRequest, NotifyAdminOfRequest, NotifyDataBreach) wired through PrivacyServiceProvider. Listener bindings are configurable by registering your own handlers in the application service provider.
  • Global helper functions (#7). privacyHasConsent(), privacyGetRegulation(), privacyRequestDataExport(), privacyRequestDataDeletion(), privacyAnonymize(), privacyCanDelete() — autoloaded via Composer and delegating to the new DataRequestService and AnonymizationService.
  • CookieBanner Livewire component (#8). Accept all / reject all / customise actions, inline preferences panel with required-category lock, dispatches privacy:consent-updated and privacy:banner-closed, supports header/description/footer slots and position/style props.
  • ConsentPreferences Livewire component (#9). Per-category toggles with dirty/saved tracking, optional description and cookie-list visibility, accessible toggle controls, save action.
  • Database + cookie consent storage (#10). ConsentService::syncCookieToDatabase() materialises guest cookie consents into database rows; SyncConsentOnLogin listener does it automatically on the framework Login event. New resolveGuestIdentifier() supports fingerprint (default), session, and IP strategies.
  • JSON API. GET /api/privacy/consent, POST /api/privacy/consent (single or bulk), GET /api/privacy/categories. UpdateConsentRequest performs cross-field validation. Default middleware is web so session and cookies remain available for stateful applications.
  • Vanilla JS client (@artisanpack-ui/privacy). PrivacyClient with load/getState/hasConsent/setConsent/setConsents/onChange, plus a window.ArtisanPackPrivacy singleton install (synchronous; installed alongside window.PrivacyConsent from ./window).
  • React subpath (@artisanpack-ui/privacy/react). useConsent hook, CookieBanner and ConsentPreferences components backed by the JSON API.
  • Vue subpath (@artisanpack-ui/privacy/vue). useConsent composable, CookieBanner and ConsentPreferences single-file components backed by the JSON API.
  • package.json and tsconfig.json declaring ., ./react, ./vue subpath exports with React/Vue as optional peer dependencies.
  • window.PrivacyConsent global JS API (#11). Synchronous hasConsent/getConsents, async setConsent/setConsents/load, openPreferences browser-event dispatcher, whenConsented(category) promise helper, loadScript() for deferred third-party tags, and activatePlaceholderScripts() for rewriting <script type="text/plain" data-privacy-category="…"> placeholders. Exposed at @artisanpack-ui/privacy/window.
  • Blade directives @hasConsent('category') / @endhasConsent and @consentRequired('category') / @else / @endconsentRequired (#12). Compile to plain if statements via ArtisanPackUI\Privacy\View\PrivacyDirectives, so cached views never need a container resolve.
  • Consent middleware (#13). privacy.consent:category[,category…] (alias for EnsureConsentGiven) blocks routes until consent is granted — configurable to abort with a status or redirect via artisanpack.privacy.middleware.ensure_consent. privacy.context (CheckCookieConsent) shares the consent map with views as $privacyConsent and stashes it on $request->attributes->get('privacy_consent').
  • Consent expiration / re-consent (#14). New privacy:purge-expired Artisan command with --prune and --dry-run flags, registered by default in Laravel's scheduler at 03:00 daily (override via artisanpack.privacy.scheduling.purge_expired). CookieBanner now re-prompts when an authenticated visitor has any expired consent rows and pre-seeds the inline preferences panel with their previous selections.
  • DataRequestForm request UI (#15). Livewire component (<livewire:privacy-data-request-form />), React component (<DataRequestForm />), and Vue component (<DataRequestForm />) for filing access / export / deletion / rectification requests. Backed by a new POST /api/privacy/data-requests JSON endpoint and DataRequestService::createRectificationRequest().
  • DataRectificationRequested event + NotifyAdminOfRequest::handleRectification listener so rectification submissions notify admins in the same way as the three other request types.
  • artisanpack.privacy.data_requests.allowed_types config — operators set the allowlist once and both the JSON validator and Livewire form honour it, so a UI-hidden type can't be bypassed via curl.
  • Consent::scopeExpired query scope.

Changed

  • livewire/livewire ^3.5 is now a required dependency (the package ships Livewire components).
  • CookieBanner::hasExpiredConsent no longer filters on withdrawn_at, so the re-consent banner keeps re-prompting after the scheduled privacy:purge-expired command soft-withdraws expired rows. Uses a whereNotExists subquery so re-consent suppresses the prompt once the visitor has actually granted fresh consent.
  • PurgeExpiredConsents now dispatches ConsentWithdrawn per row so audit/log listeners stay in sync with expiry-driven withdrawals, and --prune skips the redundant soft-update before delete.
  • CookieBanner::previousSelections falls back to ConsentService::getConsents when the cookie is empty (storage = 'database' deployments), and parses cookie values through FILTER_VALIDATE_BOOLEAN so a partner CMP encoding "false" cannot silently pre-check an opt-in category.
  • CheckCookieConsent middleware no longer writes to View::share (which leaked across requests on Octane) and no longer fires N exists() queries per request — it attaches the consent map to the request attributes and a global view composer registered by the service provider exposes $privacyConsent to every view per-request.
  • EnsureConsentGiven guards route() with Route::has() so a typo'd redirect_route config falls back to redirect_url instead of throwing a RouteNotFoundException. The missing-categories session flash key changed from the dotted privacy.consent_required (unreadable via session()/Arr::get) to privacyConsentRequired.
  • window.PrivacyConsent install is synchronous again — privacy.ts now statically imports installWindowPrivacyConsent, so inline scripts can read the global immediately and the privacy:consent-updated placeholder-activation listener is attached before any Livewire-side event can fire.
  • window.PrivacyConsent.whenConsented and loadScript accept {signal, timeoutMs} so callers can bound the wait. Both helpers unsubscribe the consent listener and remove the script load/error listeners on every resolution path, fixing the forever-pending-Promise memory leak.
  • WindowPrivacyConsent.activatePlaceholderScripts forwards nonce, integrity, crossorigin, referrerpolicy, id, class, and any other attribute the developer set on the placeholder, so the activated script keeps working under strict CSP. data-defer now correctly sets async=false so dynamically-inserted scripts honour the defer ordering contract instead of silently running async.
  • DataRequestForm::submit and PrivacyDirectives::hasConsent no longer silently swallow exceptions — both now report($e) so operators can see when the service layer breaks.
  • Laravel 13 support. The illuminate/support constraint now allows ^13.0 alongside the existing ^10.0|^11.0|^12.0 range. Laravel 13 requires PHP 8.3+; the PHP floor for users staying on older Laravel versions is unchanged.