Overview
The Client Workspace Portal gives your clients a dedicated page on the frontend of their WordPress site where they can submit requests and track their status. Instead of sending emails or using external ticket systems, clients interact with a self-service interface that feeds directly into your AdminLocks workflow.
The portal runs entirely on the frontend using a WordPress shortcode. Clients never need to enter wp-admin. Each request is stored in a dedicated database table, logged to the audit log, and can be managed via the REST API or the AdminLocks Cloud dashboard.
The portal shortcode is available in both Lite and Cloud editions. Cloud adds centralized management, cross-site request views, and the ability to link portal requests to Approval records.
Setting Up the Portal
Setting up the portal takes two steps: create a WordPress page and add the shortcode.
Step 1: Create a page
Create a new WordPress page (e.g., "Client Portal" or "Workspace"). You can use any page template. The portal renders its own UI inside the content area.
Step 2: Add the shortcode
Add the following shortcode to the page content:
[adminlocks_portal]
That is the entire setup. The shortcode handles authentication detection, asset loading, request display, and form rendering.
How it works behind the scenes
- AdminLocks registers the
adminlocks_portalshortcode oninit. - On
wp_enqueue_scripts, it checks if the current post contains the shortcode. If it does, it enqueuesportal.cssandportal.js. This means zero asset overhead on pages without the portal. - When the shortcode renders, it first checks
is_user_logged_in(). Unauthenticated visitors see a login prompt with a link towp_login_url()that redirects back to the portal page after login. - For authenticated users, it queries the last 50 requests from the
adminlocks_portal_requeststable and renders the portal template.
The portal requires WordPress authentication. It is not suitable for anonymous/public ticket submission. Clients must have a WordPress user account (even a Subscriber role is sufficient).
Request Types
The portal supports six request types, each representing a common category of client need. The type is selected from a dropdown when submitting a request.
| Type | Slug | Use Case |
|---|---|---|
| General Request | general |
Catch-all for questions, feedback, or requests that do not fit other categories |
| Plugin Install | plugin_install |
Client wants a new plugin installed or an existing one updated |
| Setting Change | setting_change |
Request to modify a WordPress or plugin setting |
| Content Edit | content_edit |
Changes to existing pages, posts, or media that the client cannot make themselves |
| Theme Change | theme_change |
Request for visual or layout changes, theme customization, or theme switch |
| Other | other |
Anything else that does not fit the categories above |
If an invalid type is submitted, it silently defaults to general. The type list is validated server-side against a hardcoded allowlist.
Submitting Requests
Requests are submitted via AJAX to the wp_ajax_adminlocks_portal_submit action. The portal JavaScript handles form submission, nonce verification, and response display.
Request fields
| Field | Required | Description |
|---|---|---|
request_type |
No (defaults to general) | One of the 6 request type slugs |
request_title |
Yes | Short summary of the request |
request_description |
No | Detailed description of what the client needs |
Security
Every submission is protected by three layers of validation:
- Nonce verification —
check_ajax_referer('adminlocks_portal', 'nonce')prevents CSRF attacks. The nonce is generated viawp_create_nonce()and passed to the frontend in theadminlocksPortallocalized object. - Authentication check —
is_user_logged_in()is verified server-side. Unauthenticated requests receive a 403 error. - Input sanitization — the title is sanitized with
sanitize_text_field(), the description withsanitize_textarea_field(), and the type is validated against the allowlist.
// The AJAX submission flow (simplified)
jQuery.ajax({
url: adminlocksPortal.ajaxUrl,
method: 'POST',
data: {
action: 'adminlocks_portal_submit',
nonce: adminlocksPortal.nonce,
request_type: 'plugin_install',
request_title: 'Install WooCommerce',
request_description: 'We need e-commerce for our new product line.'
},
success: function(response) {
// response.data.message: "Request submitted successfully."
// response.data.id: 42
}
});
What happens after submission
- A new row is inserted into
adminlocks_portal_requestswithstatus = 'pending'. - The request is logged to the audit log as a
portal_request_createdevent with severityinfo. - A success message is shown to the client and the request appears in their request list.
Managing Requests (Admin)
Administrators manage portal requests through the REST API. Two endpoints are available, both requiring the manage_options capability.
| Method | Endpoint | Description |
|---|---|---|
GET |
/adminlocks/v1/portal-requests |
List all pending portal requests |
POST |
/adminlocks/v1/portal-requests/{id}/review |
Update a request's status with an optional note |
Request lifecycle
pending ──┬──> approved ──> completed
└──> denied
The review endpoint accepts a status and optional note parameter:
// POST /wp-json/adminlocks/v1/portal-requests/42/review
{
"status": "approved",
"note": "WooCommerce installed and configured. Please review."
}
// Or deny with a reason
{
"status": "denied",
"note": "This plugin conflicts with your current theme."
}
// Or mark as completed after the work is done
{
"status": "completed",
"note": "Changes are live. Please check the site."
}
The allowed statuses are: pending, approved, denied, and completed. Each status update records the handler's user ID and note in the database row.
List response format
// GET /wp-json/adminlocks/v1/portal-requests
[
{
"id": 42,
"type": "plugin_install",
"title": "Install WooCommerce",
"description": "We need e-commerce for our new product line.",
"requested_by": 5,
"requested_email": "client@example.com",
"status": "pending",
"handler_id": null,
"handler_note": null,
"created_at": "2026-03-05 14:22:10"
}
]
Cloud Portal
When connected to AdminLocks Cloud, the portal experience is enhanced with several features beyond the base WordPress integration.
Cloud portal route
Cloud provides a dedicated portal route at /portal that renders a streamlined interface with a reduced sidebar. This route is designed for client users who have been granted access to the Cloud dashboard with limited permissions.
Site membership scoping
In the Cloud portal, clients only see requests associated with the sites they are members of. This is enforced through the Cloud's site membership system, ensuring that agency clients cannot see requests from other clients even if they share the same Cloud organization.
Linked approvals
Portal requests in the Cloud are linked to Approval records through the ClientRequest model. When a portal request requires an action that is also gated by the Approval Gates system (e.g., a plugin install request), the Cloud can automatically create an associated Approval record. This means approving the portal request can also pre-approve the corresponding WordPress capability, creating a seamless workflow.
The Cloud portal is designed to replace the traditional agency support workflow. Instead of email chains and spreadsheets, clients submit structured requests, you review them in a centralized dashboard, and the entire history is preserved in both the portal table and the audit log.