A complete GDPR compliance toolkit for your LaraClassifier / JobClass site. Display a customizable cookie consent banner, log visitor consent decisions for audit compliance, and manage cookie categories from the admin panel.
Cookie Consent Banner
A polished, responsive banner with Accept All, Reject All, and Customize options. Seven position variants including modal mode.
Granular Categories
Four cookie categories (Essential, Analytics, Marketing, Preferences) with per-category toggle controls for visitors.
Consent Logging
Full audit trail of every consent decision: IP address, user agent, action taken, and categories selected. GDPR Article 7 compliant.
Auto-injection
The banner is automatically injected into all front-end pages via middleware. No template changes needed. Skips admin panel and AJAX requests.
Use Cases
EU/EEA Website Compliance
Your site serves visitors in the European Union and must comply with the GDPR and ePrivacy Directive.
- Enable the cookie consent banner to inform visitors about cookie usage.
- Enable consent logging to maintain an auditable record of all consent decisions.
- Link your privacy policy page so visitors can review your data practices.
- Configure category toggles so visitors can opt in/out of analytics and marketing cookies.
Analytics-Heavy Site
You use Google Analytics, Hotjar, or similar tools and need user consent before loading tracking scripts.
- Enable the Analytics category toggle.
- Read the consent cookie value (
gdpr_consent) in your JavaScript before loading analytics scripts. - Only load analytics when
categories.analytics === true.
Marketing & Ad Retargeting
You run Facebook Pixel, Google Ads, or email marketing tools that set cookies.
- Enable the Marketing category toggle.
- Gate all marketing/advertising scripts behind the
marketingconsent flag. - Visitors can revoke consent at any time via the withdraw endpoint.
Requirements
- LaraClassifier / JobClass v19.0.0 or later
- PHP 8.3+
- MySQL 8.0+
Installation
Step 1: Upload the Add-on
In the admin panel, go to Admin → Extensions → Add-ons and click the Upload Add-on button. Select the add-on’s ZIP file: the system extracts it automatically and the add-on appears in the installed add-ons list.
Step 2: Activate the Add-on
Find GDPR & Cookie Consent in the list and click Activate. Its migrations, seeders (if any) and permissions are set up automatically.
Step 3: Configure
Navigate to Admin → GDPR → Settings to configure the banner position, cookie lifetime, consent logging, and cookie categories. See Configuration.
Purchase Code (License Key)
GDPR & Cookie Consent is sold as a separate product, so it has its own purchase code (license key) — distinct from the purchase code of the main application and from the one of every other add-on. You are asked for it when you activate GDPR & Cookie Consent in Admin panel → Add-ons.
Our products are sold on three platforms. The way you receive a purchase code depends on where you bought the product.
| Platform / Marketplace | How you get the purchase code | Where to find it again |
|---|---|---|
| bedigit.com Store In-site purchase (Shop) |
Generated automatically when the order is paid, then sent by email — either in its own license email, or inside the order confirmation email. | My Account → My Licenses on bedigit.com |
| Gumroad | Created as soon as Gumroad notifies us of the sale, then sent in a separate email, in addition to the Gumroad receipt. | The license email, your Gumroad Library, and My Account → My Licenses on bedigit.com |
| Envato Market CodeCanyon |
Issued by Envato, not by us, and never sent by email: you download it yourself from your Envato account. | Envato account → Downloads → License certificate & purchase code |
1. bedigit.com Store (in-site purchase)
- As soon as the order’s payment status becomes Paid, a license key is generated automatically for every licensed item in the order (one key per purchased unit — buying 3 units gives 3 distinct keys).
- It is emailed to the address used on the order, either in a dedicated license email or inside the order confirmation email. Check your inbox and your spam / junk folder.
- The key stays available in your account under My Account → My Licenses. Keys are masked in the list; open the license detail page to reveal and copy the full key, see the domains it is activated on, and deactivate a domain to free an activation slot.
- The matching invoice is under My Account → My Orders.
2. Gumroad
- A Gumroad purchase produces two separate emails: the Gumroad receipt (sent by Gumroad, giving access to the files) and a license key email (sent by bedigit.com) that contains your purchase code.
- The license key email is generated as soon as Gumroad notifies us of the sale, so it normally arrives within seconds of the payment. Here too, check your inbox and your spam / junk folder.
- When the Gumroad product uses Gumroad’s own license-key feature, the same key also appears in your Gumroad receipt and under Library → your purchase on gumroad.com.
- Use the same email address on bedigit.com as on Gumroad: your keys are then linked to your account automatically and listed under My Account → My Licenses, even if you register after the purchase. You can also add a Gumroad key manually from My Account → My Gumroad Licenses.
3. Envato Market (CodeCanyon)
- Envato purchase codes are issued and delivered by Envato Market, never emailed by us — so there is nothing to look for in your spam folder: you retrieve the code from your Envato account.
- Log in to your Envato / CodeCanyon account, open the Downloads page, find the item, and choose License certificate & purchase code from the Download dropdown. The code is written in that certificate.
- An Envato purchase code looks like
12345678-90ab-cdef-1234-567890abcdef(8-4-4-4-12 characters). It never changes, and renewing item support does not issue a new one. - Official Envato article: Where Is My Purchase Code?
Configuration
All settings are managed in Admin → GDPR → Settings
(stored in the settings table, group gdpr).
Default values come from config/gdpr.php.
| Setting | Description | Default |
|---|---|---|
gdpr_enabled |
Enable or disable the cookie consent banner globally. | true |
gdpr_cookie_lifetime |
How many days the consent preference cookie is remembered (1–3650). | 365 |
gdpr_log_consents |
Record each visitor’s consent decision to the database for compliance auditing. | true |
gdpr_banner_position |
Where the banner appears on the page (7 positions available). | bottom-center |
gdpr_privacy_policy_url |
URL to your privacy policy page, displayed as a link in the banner. | (empty) |
gdpr_category_analytics |
Show the Analytics cookie category toggle in the preferences panel. | false |
gdpr_category_marketing |
Show the Marketing cookie category toggle in the preferences panel. | false |
gdpr_category_preferences |
Show the Preferences cookie category toggle in the preferences panel. | false |
gdpr_log_retention_days |
Number of days before consent logs and archive files are permanently deleted (1–7300). | 730 (2 years) |
gdpr_archive_enabled |
When enabled, previous-month logs are gzipped and moved to private storage; only the current month remains in the database. | false |
gdpr_dedup_window_minutes |
Skip writing a new consent log if an identical one (same user/IP, action, categories) was recorded within this window. Set to 0 to disable. |
5 |
gdpr_stats_cache_ttl |
How long (in seconds) cached dashboard statistics stay fresh. Set to 0 to disable the cache entirely. |
900 (15 minutes) |
Config File Defaults
The config/gdpr.php file provides default values that are used when no database setting exists.
It also exposes archive_disk (default local) and archive_path (default gdpr)
to control where monthly archive files are stored.
Admin: Settings
The settings page (GDPR → Settings) is organized into four main areas:
General Settings
The left column (7/12 width) contains the core configuration options:
- Enable Cookie Banner: toggle switch to enable/disable the banner globally. When disabled, no banner is shown and no middleware injection occurs.
- Cookie Lifetime (Days): numeric input (1–3650 days) controlling how long the consent cookie persists in the visitor’s browser.
- Banner Position: visual position picker with 7 positions displayed on a browser mockup. Click a position to select it; the label updates in real time.
- Log Consent Actions: toggle to enable/disable consent logging to the database. When enabled, every Accept/Reject/Customize/Withdraw action is recorded.
- Privacy Policy URL: text input for the URL to your privacy policy page. If provided, a “Privacy Policy” link appears in the banner.
Banner Position Picker
The position picker is an interactive visual element showing a miniature browser window. Seven clickable dots represent the available positions:
| Position | Layout | Description |
|---|---|---|
top-left |
Compact card | Floating card in the top-left corner (max 420px wide) |
top-center |
Full-width bar | Horizontal bar across the top of the page |
top-right |
Compact card | Floating card in the top-right corner (max 420px wide) |
middle-center |
Modal dialog | Centered modal with a semi-transparent backdrop overlay |
bottom-left |
Compact card | Floating card in the bottom-left corner (max 420px wide) |
bottom-center |
Full-width bar | Horizontal bar across the bottom of the page (default) |
bottom-right |
Compact card | Floating card in the bottom-right corner (max 420px wide) |
Cookie Categories
The right column (5/12 width) displays the four cookie categories with toggle switches:
- Essential: always on, cannot be disabled (displayed with an “Always On” badge). These are cookies required for the website to function (sessions, CSRF tokens, etc.).
- Analytics: toggleable. When enabled, visitors can opt in/out of analytics cookies (Google Analytics, Hotjar, etc.).
- Marketing: toggleable. When enabled, visitors can opt in/out of marketing cookies (Facebook Pixel, ad retargeting, etc.).
- Preferences: toggleable. When enabled, visitors can opt in/out of preference cookies (language, theme, personalization, etc.).
Logs & Retention
A third full-width card below the two existing columns controls data retention and performance:
- Monthly Log Archive: toggle switch. When enabled, only the current month’s logs stay in the database; previous months are gzip-compressed and moved to private storage (see Admin: Archives). A shortcut to the Archives page appears next to the toggle.
- Log Retention (Days): integer (1–7300). Both database rows and archive files older than this
are permanently deleted by the daily
gdpr:purge-logstask. The GDPR typically recommends 2–6 years for consent evidence. - Deduplication Window: integer minutes (0–1440). If an identical consent entry exists for the
same user/IP within this window, the new write is skipped and the existing record is reused. Set to
0to disable. - Stats Cache TTL: integer seconds (0–86400). How long cached dashboard statistics stay fresh.
The cache is automatically invalidated whenever a consent log is created or deleted. Set to
0to disable caching. - Clear Stats Cache Now: button that purges the cached statistics on demand (useful after bulk deletes or imports).
Consent Overview Stats
At the top of the settings page, five stat cards show consent metrics for the last 30 days:
- Total Consents: total number of consent actions recorded
- Accepted All: visitors who clicked “Accept All”
- Rejected All: visitors who clicked “Reject All”
- Customized: visitors who saved custom preferences
- Acceptance Rate: percentage of “Accepted All” vs total consents
These stats are cached for gdpr_stats_cache_ttl seconds (default 15 minutes) to keep the page fast on
databases with millions of rows.
Admin: Consent Logs
The Consent Logs page (GDPR → Consent Logs) provides a full audit trail of every visitor consent decision, as required by GDPR Article 7. The page is fully AJAX-driven: filtering, pagination, inline delete and bulk actions all refresh the table in place without a full reload.
Statistics Card
A dedicated card at the top of the page shows four mini stat cards for the last 30 days (independent of the table filters below):
- Total Consents (neutral color)
- Accepted All (success/green color)
- Rejected All (danger/red color)
- Acceptance Rate (accent color, shown as percentage)
These figures are cached for gdpr_stats_cache_ttl seconds and automatically refresh when any log is
created or deleted.
Page Actions
The page header exposes three permission-gated action buttons:
- Archives (shown only when archive mode is enabled): opens the monthly archive browser
(see below). Requires
gdpr.archives.view. - Export CSV: downloads a CSV file of the currently filtered rows, with a UTF-8 BOM for Excel
compatibility. Columns:
id,created_at,user_id,user_name,user_email,ip_address,user_agent,action,categories(JSON). Requiresgdpr.consent_logs.export. - Purge Now: queues the
PurgeConsentLogsJobto immediately delete database rows and archive files beyond the retention period (asks for confirmation). Requiresgdpr.consent_logs.delete.
Filters
All filters sit inline on a single row in the card header and trigger an AJAX refresh:
- Search (debounced 300 ms): filter by IP address, user name, or user email.
- Action: dropdown with
accepted_all,rejected_all,customized,withdrew. - From date and To date: native
<input type="date">pickers that apply awhereDate(created_at, …)range.
The Export CSV button respects the currently applied filters.
Log Table
A paginated table (25 records per page by default) with the following columns:
| Column | Description |
|---|---|
| Select | Row checkbox + header “select all”. Shown only when the user has gdpr.consent_logs.delete. |
| Visitor | User avatar + name/email (if authenticated) or “Guest” label (if anonymous). |
| IP Address | Visitor’s IP address, displayed in monospace/code formatting. |
| Action | Color-coded badge: Accepted All (green), Rejected All (red), Customized (yellow), Withdrew (gray). |
| Categories | Inline badges for each category showing enabled (filled) or disabled (outline) state. |
| Date | Human-readable relative time (e.g. “2 hours ago”) with full date in a tooltip. |
| Actions | Per-row trash icon that deletes the entry via AJAX after a confirmation prompt. |
Bulk Delete
Ticking one or more row checkboxes surfaces a floating bulk-action bar at the top of the list. Click Delete selected and confirm the SweetAlert prompt to remove all selected rows in a single AJAX request. The selection is reset and the table refreshes on success.
Deduplication
Before inserting a new row, GdprService::logConsent() looks for an identical recent log (same
user_id or IP, same action, same categories) created within the
gdpr_dedup_window_minutes window. If one is found, the existing record is returned and no new row is
written. This prevents the table from filling up when a visitor reloads a page or quickly toggles their consent.
Empty State
When no logs match the current filters, a friendly empty state is displayed with a large icon and a message explaining that records will appear once visitors interact with the banner.
Admin: Archives
The Archives page (GDPR → Archives) is visible in the sidebar only when
gdpr_archive_enabled is on. It lists all gzipped monthly archive files found under
storage/app/private/gdpr/{year}/{month}/consent-logs-{year}-{month}.csv.gz.
How Archives Are Created
Every 1st of the month at 00:05, the scheduled gdpr:archive-monthly command runs (if the archive
setting is enabled). It performs the following for each month older than the current one that does not yet have
an archive file:
- Streams every log of that month into a CSV buffer (1000 rows at a time).
- Gzips the CSV and writes it to the private disk at the path above.
- Deletes the archived rows from
gdpr_consent_logsin chunks, inside a DB transaction per chunk. - Finally prunes any archive files older than
gdpr_log_retention_days.
Browsing Archives
The admin page groups files by year in a collapsible list. Each row shows the month, filename, file size and
archive timestamp, plus a Download button that streams the gzip file back to the browser. Access
to both browsing and downloading is permission-gated (gdpr.archives.view and
gdpr.archives.download).
year / month /
filename URL parameters against strict regex patterns and aborts with 404 for anything outside the
gdpr/ storage directory.
CLI & Scheduling
The add-on registers two Artisan commands and their matching scheduled entries. Both are wired up automatically
inside GdprServiceProvider::boot()—no changes to
routes/console.php are required.
| Command | Schedule | Purpose |
|---|---|---|
gdpr:purge-logs |
Daily at 00:00 | Dispatches PurgeConsentLogsJob: deletes DB rows older than gdpr_log_retention_days,
prunes expired archive files, and clears the stats cache. |
gdpr:archive-monthly [--force] |
1st of each month at 00:05 | Dispatches ArchiveMonthlyConsentLogsJob: archives every completed past month, then prunes
expired archives. Skips entirely when gdpr_archive_enabled is off unless
--force is passed. |
Both jobs implement ShouldQueue, so they run on whatever queue connection you configure. With the
default sync driver they execute in-process; on Redis/DB they run in a worker. Confirm they are
active with php artisan schedule:list.
Front-end: Cookie Banner
The cookie consent banner is a self-contained HTML/CSS/JS component that is automatically injected
into every front-end page via the InjectCookieBanner middleware.
Main Banner View
The main view displays:
- Cookie icon: an inline SVG cookie illustration
- Title: “Cookie Preferences”
- Message: a brief explanation of cookie usage
- Privacy Policy link: shown only if a URL is configured in settings
- Three action buttons:
- Accept All (primary dark button): accepts all cookie categories
- Reject All (secondary gray button): rejects all non-essential cookies
- Customize (ghost/text button): opens the preferences panel
Banner Positions
The banner adapts its layout based on the configured position:
- Full-width positions (
top-center,bottom-center) use a horizontal flex layout with content and buttons side by side. Max width 860px, centered. - Corner positions (
top-left,top-right,bottom-left,bottom-right) use a compact card layout (max 420px) with stacked content. - Modal position (
middle-center) centers the banner with a semi-transparent backdrop overlay, functioning as a modal dialog (max 520px).
Preferences Panel
Clicking “Customize” transitions from the main view to the preferences panel (no page reload). The panel shows:
- Back button with chevron icon: returns to the main banner view
- Category list (scrollable, max height 280px) with one row per enabled category:
- Category name and description
- Required categories: display an “Always On” badge (no toggle)
- Optional categories: display a custom CSS toggle switch
- Save Preferences button: saves the selected categories and dismisses the banner
Animations
The banner uses smooth CSS animations:
- Slide in from bottom for bottom positions
- Slide in from top for top positions
- Fade + scale for the center modal position
- Reverse animations when the banner is dismissed
Responsive Design
On screens below 768px:
- Full-width bars switch to vertical (stacked) layout
- Corner cards expand to full width
- Action buttons stack vertically and fill the width
Translations
Translation files are provided for English (en) and French (fr).
All strings use the gdpr:: namespace prefix.
Translation Keys
| Section | Key Pattern | Description |
|---|---|---|
| Categories | gdpr::messages.categories.* |
Category names and descriptions (necessary, analytics, marketing, preferences) |
| Actions | gdpr::messages.actions.* |
Consent action labels (accepted_all, rejected_all, customized, withdrew) |
| Positions | gdpr::messages.positions.* |
Banner position labels for the admin picker |
| Front-end | gdpr::messages.front.* |
Banner UI strings (title, message, button labels, privacy link) |
| Admin Settings | gdpr::messages.admin.settings.* |
Settings page labels, hints, and section headings |
| Admin Consent Logs | gdpr::messages.admin.consent_logs.* |
Consent logs page labels, column headers, empty state |
| Admin Stats | gdpr::messages.admin.stats.* |
Stat card labels (total_consents, accepted_all, etc.) |
| Admin Archives | gdpr::messages.admin.archives.* |
Archive browser labels (title, subtitle, month, filename, size, archived_at, download, empty state) |
| Messages | gdpr::messages.admin.messages.* |
Flash messages (settings_updated, stats_cache_cleared) |
Updating
There are two ways to update this add-on: via the admin panel (recommended) or manually replacing files.
Method 1: Admin Panel Upload (Recommended)
- Download the latest
.zipfile of this add-on. - Go to Admin panel → Add-ons and click the Upload button.
- Select or drag the
.zipfile into the upload area. - A confirmation prompt will show the current and new version numbers. Click Replace to proceed.
- Go to Admin panel → System Update (
/admin/update) to apply any pending database migrations.
Method 2: Manual File Replacement
Step 1: Replace Files
Replace the add-on directory with the new version.
Step 2: Run Migrations
php artisan migrate
Pending migrations run once, so the command is safe to repeat.
Step 3: Clear Caches
php artisan config:clear
php artisan route:clear
php artisan view:clear
Step 4: Verify
Visit GDPR → Settings in the admin panel to confirm all settings are intact and the banner is working correctly on the front-end.
Uninstallation
Switching an add-on off without losing anything is a deactivation: go to Admin panel → Add-ons, find GDPR & Cookie Consent and click Deactivate.
- Its routes, views, admin menu entries and permissions stop being registered, and its front-end pages stop answering.
- Its database tables and all the data they hold are kept, and its files stay under
extensions/addons/gdpr/. Nothing is deleted. - The purchase code recorded at activation is kept too, so activating the add-on again does not ask for it.
- Deactivation is refused while another active add-on depends on this one: deactivate that add-on first.
Click Activate on the same card to switch it back on. Pending migrations are re-run, assets are republished, and the add-on picks up exactly where it left off.
Removing
Removing is permanent and destroys the add-on's data. The Remove button only appears on a deactivated add-on, so removal is always two steps:
- Deactivate GDPR & Cookie Consent (see Uninstallation).
- Click Remove on its card and confirm the prompt.
The admin panel then, in one pass:
- runs the add-on's uninstall hook, if it ships one, while its code is still on disk;
- revokes the permissions declared in its
addon.json; - rolls back its migrations — this drops its database tables and every row they hold — and purges its entries from the
migrationstable, so a later reinstall migrates from scratch; - deletes its published assets:
public/addons/gdpr/,public/vendor/gdpr/andstorage/app/public/addons/gdpr/; - deletes the add-on directory
extensions/addons/gdpr/; - deletes its row in the
addonstable (the recorded purchase code goes with it) and clears the application cache.
Removal is refused, with an explanatory message and before anything is destroyed, when the add-on is still active, when another active add-on depends on it, or when the web server (PHP) user cannot delete extensions/addons/gdpr/. In that last case, give that user write permission on the directory and on its parent, then try again.
Deleting the folder over FTP or SSH is not equivalent: the add-on's tables, its entries in the migrations table and its addons row are all left behind, and its card stays in the list. Use Remove in the admin panel instead.
Troubleshooting
Banner not appearing on the front-end
Check the following:
- The add-on is activated in Admin → Add-ons.
- The
gdpr_enabledsetting is set to1(enabled) in settings. - You don’t already have a
gdpr_consentcookie set. Clear cookies or use an incognito window to test. - The page is a standard HTML response with a
</body>tag (the middleware looks for this tag to inject the banner). - You are viewing a front-end page, not an admin page (admin routes are excluded).
Banner appears on admin pages
This should not happen: the middleware explicitly skips admin routes via isAdminPanelRoute().
If it does occur, check that:
- The
isAdminPanelRoute()helper function is defined and working in your LaraClassifier / JobClass core installation. - Your admin route prefix matches the configured value in
config('larapen.core.admin_prefix').
Consent cookie is encrypted / unreadable by JavaScript
The add-on explicitly excludes the gdpr_consent cookie from Laravel’s EncryptCookies middleware.
If the cookie is still encrypted:
- Ensure the
GdprServiceProvideris properly registered (add-on is active). - Check that no custom middleware or package is re-encrypting the cookie.
- Clear your browser cookies and consent again: old encrypted cookies from a previous configuration may persist.
Consent logs not being recorded
- Check that
gdpr_log_consentsis set to1(enabled) in the admin settings. - Verify the
gdpr_consent_logstable exists (runphp artisan migrate). - Check your server logs for database errors (e.g. permission issues).
Banner position not changing
- After changing the position in admin settings, make sure to clear the
gdpr_consentcookie (or use an incognito window): the banner only appears for visitors without a consent cookie. - Clear the view cache:
php artisan view:clear.
Categories not showing in preferences panel
Only categories that are enabled in the admin settings appear in the preferences panel. The “Essential” category always appears. For optional categories:
- Check that
gdpr_category_analytics,gdpr_category_marketing, and/orgdpr_category_preferencesare set to1in settings. - Disabled categories are filtered out by the
InjectCookieBannermiddleware before rendering the view.
Log retention / purging
Retention is fully automated. The gdpr:purge-logs scheduled command runs every day at 00:00 and
deletes both database rows and archive files older than gdpr_log_retention_days
(default: 730 days / 2 years, configurable from the Settings page). You can also:
- Trigger it on demand from the Consent Logs page via the Purge Now button
(requires
gdpr.consent_logs.delete). - Run it manually:
php artisan gdpr:purge-logs. - Verify scheduling with
php artisan schedule:list— bothgdpr:purge-logs(daily) andgdpr:archive-monthly(monthly on the 1st, 00:05) should appear.
Monthly archive job does not run
- Make sure
gdpr_archive_enabledis set to1in Settings; otherwise the job exits early. Pass--forcetophp artisan gdpr:archive-monthlyto bypass this check. - Confirm that Laravel’s scheduler is wired up (
* * * * * cd /path && php artisan schedule:runin your crontab). Without it, no scheduled task ever fires. - Check that the
localdisk is writable and thatstorage/app/private/gdpr/exists (it is created on the fly by the job).
Archives page does not appear in the sidebar
- The Archives menu item is hidden unless both the
gdpr_archive_enabledsetting is on and the current admin user has thegdpr.archives.viewpermission. - If you just toggled the setting, reload the admin page: the sidebar is re-rendered on each request and will pick it up.
Dashboard statistics look stale
- Stats are cached for
gdpr_stats_cache_ttlseconds (default 15 minutes). The cache is automatically cleared on any log create/delete. - After a bulk import or a manual database cleanup, click Clear Stats Cache Now on the Settings page to force a recompute.
- Set
gdpr_stats_cache_ttlto0to disable caching entirely (not recommended on large datasets).
CSRF token mismatch on consent endpoints
All consent endpoints (/gdpr/accept-all, etc.) require a valid CSRF token.
The banner JavaScript includes the token from the rendered Blade view. If you see 419 errors:
- Ensure your Laravel session is configured correctly.
- Check that the
webmiddleware group (which includesVerifyCsrfToken) is applied to the consent routes. - If using a custom banner view, make sure it passes the CSRF token in the
X-CSRF-TOKENheader of fetch requests.
GDPR & Cookie Consent v1.0.0: A LaraClassifier / JobClass add-on.
© BeDigit. All rights reserved.