Privacy - v1.0.0
Privacy Changelog
[Unreleased]
[1.0.0] - 2026-06-20
Added
privacy:process-requestsArtisan 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:reportArtisan command (#43). Generates consent / request / breach compliance reports in JSON or CSV for a period (--period=day|week|month|quarter|year|custom, with--start/--endfor custom windows), optionally writes to--output=pathand/or emails to--email=address.privacy:installnow publishes views, runs migrations with confirmation (auto-runs in--no-interaction), seeds default consent categories fromcookie_categoriesconfig (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 withip-api, MaxMind GeoIP2, and CloudflareCF-IPCountryproviders; configurable cache duration, fallback region, and an opt-in MaxMind database path.getLocation(),getCountryCode(),getRegionCode(), andresolveRegion()surface the lookup for the regulation resolver. Newprivacy.geolocatemiddleware (GeolocateUser) stamps the resolved region onto the request asprivacy_regionsoBaseRegulation::applies()matches against authoritative data.Gdprregulation 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.Ccparegulation class (#27). California-resident applicability;know,delete,correct,opt_out_of_sale,limit_use_of_sensitive_information,non_discrimination,data_portabilityrights; 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-resolvesgdprandccpato the dedicated classes instead of the generic fallback.Admin\ConsentManagerLivewire 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 byGET /api/privacy/admin/consentsfor the React/Vue surfaces. Authorization enforced through the configuredmanage-privacygate.Admin\DataRequestManagerLivewire 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 byGET /api/privacy/admin/data-requests,GET /api/privacy/admin/data-requests/{id}, andPOST /api/privacy/admin/data-requests/{id}/actions. Authorization enforced through the configuredmanage-privacygate.- Event/listener system (#6). Six new events —
DataAccessRequested,DataDeletionRequested,DataExportRequested,DataRequestCompleted,DataBreach,PrivacyPolicyUpdated— plus five default listeners (LogConsentActivity,ProcessDataAccessRequest,ProcessDataExportRequest,NotifyAdminOfRequest,NotifyDataBreach) wired throughPrivacyServiceProvider. 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 newDataRequestServiceandAnonymizationService. CookieBannerLivewire component (#8). Accept all / reject all / customise actions, inline preferences panel with required-category lock, dispatchesprivacy:consent-updatedandprivacy:banner-closed, supportsheader/description/footerslots and position/style props.ConsentPreferencesLivewire 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;SyncConsentOnLoginlistener does it automatically on the frameworkLoginevent. NewresolveGuestIdentifier()supports fingerprint (default), session, and IP strategies. - JSON API.
GET /api/privacy/consent,POST /api/privacy/consent(single or bulk),GET /api/privacy/categories.UpdateConsentRequestperforms cross-field validation. Default middleware iswebso session and cookies remain available for stateful applications. - Vanilla JS client (
@artisanpack-ui/privacy).PrivacyClientwithload/getState/hasConsent/setConsent/setConsents/onChange, plus awindow.ArtisanPackPrivacysingleton install (synchronous; installed alongsidewindow.PrivacyConsentfrom./window). - React subpath (
@artisanpack-ui/privacy/react).useConsenthook,CookieBannerandConsentPreferencescomponents backed by the JSON API. - Vue subpath (
@artisanpack-ui/privacy/vue).useConsentcomposable,CookieBannerandConsentPreferencessingle-file components backed by the JSON API. package.jsonandtsconfig.jsondeclaring.,./react,./vuesubpath exports with React/Vue as optional peer dependencies.window.PrivacyConsentglobal JS API (#11). SynchronoushasConsent/getConsents, asyncsetConsent/setConsents/load,openPreferencesbrowser-event dispatcher,whenConsented(category)promise helper,loadScript()for deferred third-party tags, andactivatePlaceholderScripts()for rewriting<script type="text/plain" data-privacy-category="…">placeholders. Exposed at@artisanpack-ui/privacy/window.- Blade directives
@hasConsent('category')/@endhasConsentand@consentRequired('category')/@else/@endconsentRequired(#12). Compile to plainifstatements viaArtisanPackUI\Privacy\View\PrivacyDirectives, so cached views never need a container resolve. - Consent middleware (#13).
privacy.consent:category[,category…](alias forEnsureConsentGiven) blocks routes until consent is granted — configurable to abort with a status or redirect viaartisanpack.privacy.middleware.ensure_consent.privacy.context(CheckCookieConsent) shares the consent map with views as$privacyConsentand stashes it on$request->attributes->get('privacy_consent'). - Consent expiration / re-consent (#14). New
privacy:purge-expiredArtisan command with--pruneand--dry-runflags, registered by default in Laravel's scheduler at 03:00 daily (override viaartisanpack.privacy.scheduling.purge_expired).CookieBannernow re-prompts when an authenticated visitor has any expired consent rows and pre-seeds the inline preferences panel with their previous selections. DataRequestFormrequest 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 newPOST /api/privacy/data-requestsJSON endpoint andDataRequestService::createRectificationRequest().DataRectificationRequestedevent +NotifyAdminOfRequest::handleRectificationlistener so rectification submissions notify admins in the same way as the three other request types.artisanpack.privacy.data_requests.allowed_typesconfig — 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::scopeExpiredquery scope.
Changed
livewire/livewire ^3.5is now a required dependency (the package ships Livewire components).CookieBanner::hasExpiredConsentno longer filters onwithdrawn_at, so the re-consent banner keeps re-prompting after the scheduledprivacy:purge-expiredcommand soft-withdraws expired rows. Uses awhereNotExistssubquery so re-consent suppresses the prompt once the visitor has actually granted fresh consent.PurgeExpiredConsentsnow dispatchesConsentWithdrawnper row so audit/log listeners stay in sync with expiry-driven withdrawals, and--pruneskips the redundant soft-update before delete.CookieBanner::previousSelectionsfalls back toConsentService::getConsentswhen the cookie is empty (storage = 'database'deployments), and parses cookie values throughFILTER_VALIDATE_BOOLEANso a partner CMP encoding"false"cannot silently pre-check an opt-in category.CheckCookieConsentmiddleware no longer writes toView::share(which leaked across requests on Octane) and no longer fires Nexists()queries per request — it attaches the consent map to the request attributes and a global view composer registered by the service provider exposes$privacyConsentto every view per-request.EnsureConsentGivenguardsroute()withRoute::has()so a typo'dredirect_routeconfig falls back toredirect_urlinstead of throwing aRouteNotFoundException. The missing-categories session flash key changed from the dottedprivacy.consent_required(unreadable viasession()/Arr::get) toprivacyConsentRequired.window.PrivacyConsentinstall is synchronous again —privacy.tsnow statically importsinstallWindowPrivacyConsent, so inline scripts can read the global immediately and theprivacy:consent-updatedplaceholder-activation listener is attached before any Livewire-side event can fire.window.PrivacyConsent.whenConsentedandloadScriptaccept{signal, timeoutMs}so callers can bound the wait. Both helpers unsubscribe the consent listener and remove the scriptload/errorlisteners on every resolution path, fixing the forever-pending-Promise memory leak.WindowPrivacyConsent.activatePlaceholderScriptsforwardsnonce,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-defernow correctly setsasync=falseso dynamically-inserted scripts honour the defer ordering contract instead of silently running async.DataRequestForm::submitandPrivacyDirectives::hasConsentno longer silently swallow exceptions — both nowreport($e)so operators can see when the service layer breaks.- Laravel 13 support. The
illuminate/supportconstraint now allows^13.0alongside the existing^10.0|^11.0|^12.0range. Laravel 13 requires PHP 8.3+; the PHP floor for users staying on older Laravel versions is unchanged.