Skip to content

Portal Request Catalogue

Ed Mozley edited this page Jul 23, 2026 · 2 revisions

Portal Request Catalogue

Where: the self-service portal β†’ Request Something Publish with: Forms β†’ the 🌐 globe icon on a form's row Starts empty on purpose β€” no form is offered to customers until you say so

Customers pick what they need from a list of forms β€” a new starter, a laptop, an access request β€” fill it in, and submit. The answers arrive in Forms β†’ Submissions for your team to action.

Offering a form

Nothing appears in the catalogue until you publish it. In Forms, each row has a globe action:

  • Outline globe β€” internal only. This is the default for every form, old and new.
  • Filled globe + "In catalogue" pill β€” customers can request it.

πŸ’‘ Why this isn't just the Active switch. Active is your own on/off β€” whether the form can be used at all. Being in the catalogue is a different decision: your New Starter Request is active and used daily by managers, but has no business appearing on every customer's menu. They're separate switches because they answer separate questions.

Editing a published form keeps it published. (Editing creates a new version behind the scenes, and it would be a nasty surprise if pressing Save quietly withdrew your form from customers.)

What a customer sees

Only forms that are in the catalogue, active, and the current version. Everything else is invisible β€” and a form that isn't offered can't be reached even if someone knows its web address, so there's no "hidden but guessable" state.

The form itself shows only its title, description and questions. Who wrote it, how many times it's been revised, and every other piece of internal bookkeeping stays on your side.

What happens when they submit

The answers are recorded as a submission against that form, exactly like one of your own staff filling it in. In Forms β†’ Submissions you'll see:

  • Every answer
  • Who submitted it β€” including customers coming from the portal, marked as such
  • When

By default a submission does not automatically become a ticket. You decide which requests need work, rather than having every enquiry open a ticket.

⚠️ Keep an eye on Forms β†’ Submissions β€” an ordinary catalogue request won't appear in your ticket inbox on its own.

Unless you require approval. A catalogue item can be set to need sign-off first β€” see Catalogue Request Approvals. When it's approved, the ticket is raised automatically (and the requester sees the status on their own dashboard).

What customers can be asked for

Every question type is a plain input:

Type Notes
Text, Textarea Free text
Email Checked as a real address
Number Checked as numeric
Dropdown, Radio One of the options you configured β€” and only one of them
Checkbox A single yes/no
Checkboxes Any of the options you configured

There is no field type that reaches into your system β€” no picker of assets, tickets or configuration items β€” so nothing on a form can expose your internal records. There's also no file upload on forms; if you need one, point the customer at raising a ticket, which does support attachments.

Required fields, email format and number format are enforced on the server, not just in the browser. So are the options: an answer to a dropdown must be one of that dropdown's own choices. (That last check was added with this feature β€” before it, a submitted value was stored as-is, so a "Department" offering four choices could be saved with a fifth.)

Worked example

You have three forms:

Form Active In catalogue
New Starter Request βœ… βœ…
Equipment Return βœ… ❌
Old Equipment Return (v1) βœ… βœ… (an older version)

Sarah opens Request Something and sees one entry: New Starter Request.

  • Equipment Return isn't in the catalogue.
  • Old Equipment Return (v1) is in the catalogue but is a previous version, so it's not offered β€” only the current one ever is.

She fills it in, choosing IT Support from the Department dropdown, and submits. In Forms β†’ Submissions the request appears attributed to Sarah, flagged as coming from the portal. An analyst reads it and raises a ticket for the work.

Had she tampered with the page to submit "Board of Directors" β€” not one of the configured options β€” the server would have refused it.

Limitations worth knowing

  • The catalogue is the same for every company. Forms is not yet a multi-company module (no company column on any forms table), so on an MSP install every client sees the same list. If you need per-client catalogues, that's the Forms multi-company work.
  • No file uploads on forms (as above).
  • Approvals are a single designated approver, per item. A catalogue item can require sign-off before a ticket is raised β€” see Catalogue Request Approvals. Routing to the requester's manager, or a multi-approver board, isn't built yet; for richer routing the Workflows module is still the place.

Under the hood

🎨 File Purpose
πŸ–₯️ self-service/catalogue.php The portal page β€” list, form renderer, submit
πŸ”Œ api/self-service/get_catalogue.php The offered forms (visible + active + current version)
πŸ”Œ api/self-service/get_catalogue_form.php One form and its fields, minus all analyst bookkeeping
πŸ”Œ api/self-service/submit_catalogue_form.php The submit adapter β€” passes the requester as a separate argument
βš™οΈ includes/services/forms.php FormsService::submitForm() β€” validation, the visibility re-check, both INSERTs
πŸ–₯️ forms/index.php The globe toggle and the "In catalogue" pill
πŸ”Œ api/forms/get_forms.php Returns is_portal_visible for the list
πŸ”Œ api/forms/get_submissions.php Resolves both submitter kinds (analyst and requester)
πŸ—„οΈ forms.is_portal_visible The switch. Defaults to 0
πŸ—„οΈ form_submissions.submitted_by_user_id The requester who submitted it
πŸ—„οΈ form_submissions.ticket_id The ticket raised from it β€” NULL until actioned; written when an approval is approved (see Catalogue Request Approvals)

Two details that matter if you're reading the code:

  1. The visibility check lives in the service, not only the endpoint. Submitting to an unpublished form is refused even though the adapter never looked β€” so a future caller can't accidentally bypass it.
  2. The requester is stored in its own column. form_submissions.submitted_by is an analyst id with no foreign key, and every reader joins it to the analysts table β€” so writing a requester's id there would have silently credited the request to whichever analyst happened to share that number. Exactly one of the two columns is ever set.

See also

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally