MINISTRYPLATFORM CUSTOM WIDGETS
Getting Started — Installation Guide
Every custom widget on this site installs the same way. This page is the path all of them follow: what to switch on in MinistryPlatform, how to find your host prefix, what the widget tag means, and how to test what you've built. The individual widget guides assume you have read this one and describe only where they diverge from it.
What a custom widget is
A standard MinistryPlatform widget is built for you. You choose it in the Widget Configurator, set a few parameters, and paste the code — that process is covered on Adding Widgets.
A custom widget is a shell. You supply the data, usually as a stored procedure you install in your own database, and you supply the presentation as a Liquid template. The toolkit handles the parts in between: calling the API, passing the signed-in user through, binding query-string values, caching, and rendering the template into the page.
The trade is control for effort. A custom widget can present anything in MinistryPlatform in any layout you want. It also means you own a stored procedure and a template that no one at ACST will patch for you.
Custom widgets are included with MinistryPlatform at no additional cost.
What you need before starting
Access to add pages and HTML blocks on your website; and, for most widgets, SQL Server Management Studio access to the MinistryPlatform database. A widget that reads only published events needs no database work at all — the Event Calendar is one of these — but most of the interesting ones ship a stored procedure.
You will also need to be signed in to MinistryPlatform as an administrator to check the API Clients page.
If you do not have a dedicated hosting plan or the required SQL Server skills, reach out to the professional services team for assistance. Normal hourly rates will apply.
Step 1 Confirm the platform side is ready
Three things must be true before any widget will return data. They are all one-time setup for the instance, not per widget.
Custom Widgets are enabled for your site. Inquire with support if you're not sure. There is no additional cost.
The MP.SKY API client is configured. In MinistryPlatform, go to the API Clients page and confirm it exists. This is the client the Custom Widget API authenticates as, and every stored procedure you install must be linked to a security role that this client holds.
Your website's URLs are permitted. Contact support and ask them to whitelist your site with MinistryPlatform — both the design environment and the published production URLs. Widgets that require a login will fail silently from a URL that is not on the list, which is a confusing way to spend an afternoon.
Step 2 Find your domain prefix
Almost every setting you will touch depends on this one value, and it is the single most common thing to get wrong.
Launch the Add/Edit Family tool from within MinistryPlatform and look at the URL. Your domain prefix is what appears before .cloudapps.ministryplatform.cloud
.
In https://
catholicdemo
.cloudapps.ministryplatform.cloud/tools...
the prefix is catholicdemo
.
Confirm the API is reachable before going any further:
https://<prefix>.cloudapps.ministryplatform.cloud/sky/swagger/index.html
Two hostnames, and they are not interchangeable.
The Custom Widget API lives at <prefix>.cloudapps.ministryplatform.cloud
. Files, images and the shared widget script live at <prefix>.ministryplatform.net
. The cloudapps host does not serve files and returns an error for those URLs; the .net host does not answer API calls. Widgets that show images or require a login touch both.
Step 3 Install the stored procedure
Skip this step for widgets that read published events only.
Each widget guide links the SQL script it needs. Open it in SSMS, confirm the database dropdown shows the MinistryPlatform database and not
master
, and execute it. Every script in this library does the same three things, and all three are required:
- Creates the procedure, named
api_custom_<something>. Theapi_custom_prefix is not decoration — the API will not call a procedure without it. - Registers it in
dp_API_Procedures. - Links it to a security role in
dp_Role_API_Procedures.
Step 3 is the one most often missed, and nothing works without it.
The API checks role links, not SQL permissions. A procedure you can execute happily in SSMS will still be refused by the API if the role link is absent. If your widget API client uses a role other than Administrators
, change @RoleName
in the script — it usually appears twice, once in the registration block and again in the verification block, and both need changing.
The scripts end with verification queries. Read them rather than the printed messages: on some MinistryPlatform versions an insert can roll back while the PRINT
that follows it still runs, so the script will cheerfully claim success it did not have.
Why the scripts look old-fashioned.
Plenty of MinistryPlatform databases still run at a SQL Server 2008 compatibility level even on a modern server, which quietly removes functions you would otherwise reach for. These scripts avoid TRY_CONVERT
, STRING_SPLIT
, CREATE OR ALTER
, DROP … IF EXISTS
, IIF
and CONCAT
, and they inspect the metadata tables before inserting, because key columns are named differently across versions. Do not raise the database compatibility level to work around an error — that is a decision for whoever owns the instance, not a side effect of installing a widget.
The @UserName parameter
The Custom Widget API passes @UserName
automatically whenever the visitor is signed in. Every procedure in this library accepts it, and any procedure you write yourself should too.
Give it a default of NULL
if the widget does not need it. A parameter declared NOT NULL
without a default will cause the procedure to fail for anonymous visitors — the API simply will not supply a value it does not have.
Verify the API can reach it
In a browser, with <prefix>
and the procedure name substituted:
https://<prefix>.cloudapps.ministryplatform.cloud/sky/api/CustomWidget?storedProcedure=api_custom_YourProcedure
You should see JSON. If the response complains that a parameter was not supplied, append &@DomainID=1
and try again. If it says the procedure does not exist or user does not have access to it
, that is almost always the missed role link from step 3 — see Troubleshooting.
If the script succeeded but the API still doesn't see it
This is the single most common way a correct installation appears to fail, and it is worth recognising before you start editing SQL that was already right.
The MinistryPlatform API caches its list of registered procedures. A procedure you have just created and registered may not be visible to the API until that cache is refreshed. The symptom is precise: all three verification queries in the deploy script pass, the procedure runs perfectly in SSMS, and the API insists it does not exist. Nothing is wrong with your script.
You have two ways forward.
Wait for the scheduled recycle. IIS recycles application pools on a timer — commonly overnight, sometimes every 29 hours on a default configuration. If you are not in a hurry, come back tomorrow and test again before changing anything.
Recycle the application pool. This clears the cache immediately. How you do it depends on where your MinistryPlatform is hosted.
If you are hosted on MinistryPlatform Cloud , you do not have server access and cannot do this yourself. Raise a ticket with support asking them to recycle the MinistryPlatform API application pool, and say why — that you have installed a new custom widget stored procedure and the API is not yet seeing it. That phrasing gets it handled quickly, because it is a request they recognise.
If MinistryPlatform runs on a server you control , someone with administrative access to that server can do it in under a minute:
- Remote onto the MinistryPlatform web server.
- Open Internet Information Services (IIS) Manager.
- Expand the server node and select Application Pools.
- Find the pool serving the MinistryPlatform API. It is usually named for the API site — confirm which by selecting Sites, finding the API site, and reading its application pool in the Basic Settings.
- Right-click the pool and choose Recycle.
Or, from an elevated PowerShell prompt on the same server:
Restart-WebAppPool -Name "YourApiAppPoolName"
Recycling is not free, so do it deliberately. A recycle drops in-flight requests and clears in-memory state, so anyone mid-action in MinistryPlatform or on a widget may see an error and have to retry. It completes in seconds, but pick a quiet moment rather than Sunday morning.
Recycle once, then re-test properly.
Two other caches sit in front of you and will happily serve you the old answer: the widget's own response cache, and your browser's. After the recycle, test the API URL directly in a private browsing window before you judge whether the recycle worked. If the direct URL now returns JSON and your page still does not, the problem has moved to the page — set data-cache="false"
and hard-refresh.
If the recycle does not fix it, the cache was not the cause. Go back to the three registration checks in Troubleshooting; something in dp_API_Procedures
or dp_Role_API_Procedures
does not match.
Step 4 Understand the widget tag
The widget itself is one empty <div>
. The attributes on it are the whole configuration:
<div id="MyCustomWidget"
data-component="CustomWidget"
data-sp="api_custom_GroupWidget"
data-template="/Widgets/WidgetTemplate/Template/template.html"
data-requireUser="false"
data-cache="true"
data-host="mpi"></div>
| Attribute | What it does |
|---|---|
id
|
Any unique value. Required, and must be unique if you put two widgets on one page. |
data-component
|
Always CustomWidget
. This is what the toolkit looks for. |
data-sp
|
The stored procedure to call. Omit it for widgets that read published events instead. |
data-template
|
The Liquid template that renders the result — see below. |
data-requireUser
|
true
forces a sign-in before the widget renders and makes @UserName
available to the procedure. false
for anything public. |
data-cache
|
Caches the response for about five minutes. See the warning below. |
data-host
|
Your domain prefix from Step 2. |
data-params
|
Optional. Extra values for the procedure or the events feed. Square brackets bind a query-string value from the current URL — @CongregationID=[id]
reads ?id=
off the page's own address. |
data-debug
|
true
logs the widget's request and response to the browser console. Set it to false
in production. |
Do not set data-cache="true"
on a widget that shows one person their own data.
Caching is right for a parish directory or an events calendar, where every visitor sees the same thing. It is wrong for anything behind a login — at best a member sees stale information, and it is not a risk worth taking to save one API call. Public widget: cache. Personal widget: do not.
Where the template lives
data-template
is a URL to an HTML file containing the Liquid markup. In the repository examples it is a site-relative path, which assumes you can host arbitrary .html
files on your web server. Many website builders cannot.
The deployments on this site point at the file in the GitHub repository instead, which works and needs nothing hosted. Prefer the jsDelivr mirror over a raw GitHub URL:
https://cdn.jsdelivr.net/gh/MinistryPlatform-Community/MPCustomWidgets@main/Widgets/<Widget>/Template/<file>.html
jsDelivr is a CDN with sensible caching and no rate limit for ordinary traffic. raw.githubusercontent.com
is neither, and is throttled under load.
The trade-off is that pointing at @main
means your page follows the community repository. If a template changes upstream, your page changes with it. For a production site, copy the template to your own hosting or pin a specific commit rather than @main
.
Step 5 Add the page and paste the code
Create a page on your website and add a single HTML block to it. Into that block goes the widget <div>
, followed by the toolkit script:
<script type="text/javascript"
src="https://cdn.jsdelivr.net/gh/MinistryPlatform-Community/MPCustomWidgets@main/dist/js/customWidgetV1.js"></script>
The script must come after
the widget div. It initialises on DOMContentLoaded
; anything that configures the tag has to be in place before that fires, or the widget runs with no host and returns nothing.
Some widgets need more — a charting library, the FullCalendar library, the modal helper, a stylesheet from the widget's Assets
folder. Each widget guide lists what else to load and where it goes.
If the widget requires a login
Add MinistryPlatform's shared widget script. On Refresh sites this goes in More → Settings → Head HTML → Body End HTML, so it loads on every page and you do not have to repeat it:
<script id="MPWidgets" src="https://<prefix>.ministryplatform.net/widgets/dist/MPWidgets.js"></script>
Note the .ministryplatform.net
host, not cloudapps. This script supplies the <mpp-user-login></mpp-user-login>
element, which gives visitors somewhere to sign in and gives the toolkit the identity it passes to your procedure as @UserName
.
A note on HTML comments
Several website builders — Duda among them — strip the <!--
and -->
markers from an HTML embed and render what was between them as ordinary page text. If you want to leave notes for the next administrator, put them in a JavaScript or CSS comment instead. If you see blocks of explanatory text appearing on your live page, this is why.
Step 6 Test
- Open the page. The widget should render within a second or two.
- Open the browser console (F12). With
data-debug="true"you will see the request the widget made and the response it got — this tells you immediately whether the problem is the API or the template. - For a widget requiring a login, sign out and reload. You should get the sign-in prompt, not an error and not a blank space.
- Check it on a phone-width browser.
- Set
data-debugtofalseand publish.
Troubleshooting
"Procedure … does not exist or user 'apiuser' does not have access to it."
This message comes from the MinistryPlatform API, not SQL Server, and almost always means a missed registration step. Check in order:
- The procedure exists in the right database and in the
dboschema. - A row exists in
dp_API_Procedureswith the exact name and no trailing spaces. - A row links it to a role in
dp_Role_API_Procedures.
The deploy script's verification queries check all three. If all three pass and the API still refuses, the procedure is almost certainly fine and the API has simply not noticed it yet — see If the script succeeded but the API still doesn't see it in Step 3.
The widget renders nothing at all, with no error.
Almost always data-host
. Confirm the prefix, then open the Step 3 verification URL directly in a browser. If that returns JSON and the page does not, the problem is on the page — check the console for a script error and confirm the toolkit script comes after the div.
The widget returns data but the page is blank or malformed.
The template did not render. Look for a Liquid error in the console, and confirm data-template
resolves — paste the URL into a browser and check you get HTML rather than a 404 or a GitHub error page.
Everything is centred, the fonts are wrong, or form controls look broken.
Your site theme is winning the specificity contest against the widget's stylesheet. The fix is to scope the widget's CSS under its own container ID, which beats any selector that does not itself contain an ID. If a theme still breaks through, it is using !important
or its own ID selector.
A signed-in widget behaves as though nobody is signed in.
Either the shared widget script is missing, or the page's URL is not on the permitted list from Step 1, or data-requireUser
is false
. All three produce the same symptom: @UserName
arrives as NULL
and the procedure returns nothing.
Changes don't take effect.
Three caches sit between you and the page: the API's registered procedure list, the widget's own five-minute response cache, and your browser's. Work through them in that order — a hard refresh and data-cache="false"
clear the second and third in seconds, and only if the direct API URL is also stale do you need the recycle described in Step 3.
Where the widgets come from
There is no single catalogue. Custom widgets reach you from several places, and it is worth knowing which you are looking at, because it determines where the files live and who you ask when something breaks.
The community repository
— github.com/MinistryPlatform-Community/MPCustomWidgets
Each widget has its own folder under /Widgets
containing a demo.html
with the tag to copy, a Template
folder with the Liquid file, a StoredProc
folder where one is needed, and an Assets
folder for any CSS or JavaScript the widget requires. WidgetTemplate
is the empty shell to clone when writing your own. The repository is a community project; the code is offered as-is, and contributions are welcome.
A sample of what's in there
The list below is a snapshot taken at the time of writing, offered to show the kinds of things people build rather than as a catalogue. It is a community repository, so widgets are added and changed without notice — always browse the repository itself for what is there today. Select any widget for a short description.
Widget Template
no database
The empty shell to copy when you build your own. The clearest place to see what each part of the widget tag does.
Any Custom Form
public no database
Hosts every one of your MinistryPlatform custom forms on a single page, choosing which to show from a value in the link. Saves building a page per form.
Add to Calendar
public no database
Gives visitors a one-click button to save an event to Google, Apple or Outlook, or to download a calendar file.
Event Calendar
public no database
A full parish calendar in month, week, day or list view, built from the events you already approve for the web. This is the widget behind the Event Calendar guide.
Group Finder
public sql script
A searchable group directory with as many filter dropdowns as you need, and full control over how each group card looks.
Staff Directory
public sql script
A staff and clergy listing with photos, roles and contact details. Note that this one stores its data in a custom table rather than a standard MinistryPlatform page.
Campaign Progress
public sql script
A fundraising thermometer showing pledge progress against a campaign, broken down by parish.
Charts and Dashboards
public sql script
Charts and sortable tables for presenting summary figures. More a worked pattern to copy than a finished page.
Family Sacraments
sign-in sql script
A read-only record of the sacraments received by everyone in a household. This is the widget behind the Family Sacraments guide.
My Week
sign-in sql script
The coming week's events for everyone in the signed-in person's household, on one page. Ships with a light and a dark theme.
My Family Events
sign-in sql script
The events a household has registered for, so a parent can see everything the family is signed up to.
My Mission Trips
sign-in sql script
A participant's mission trip history and upcoming trips, recreating the My Mission Trips page from the old MinistryPlatform Portal.
Group Manager
sign-in sql script
Lets a group leader see and work with their own group's roster from the website, without giving them access to the Platform.
Milestone Gamification
sign-in sql script
Turns milestone progress into a visual, game-like display. A different treatment of the same data as the Milestone Tracker.
Sign-in Gate
sign-in
A starting point for any widget that must sit behind a login. Not a finished page — copy it when you need the sign-in behaviour.
Publications
public sql script
An archive of newsletters and published messages, with a list view and a page for reading a single message.
View in Browser
public sql script
The web version of an emailed message — the page behind a "view this message in your browser" link.
Sermon Player
sign-in sql script
A video player that appears inside MinistryPlatform on a record rather than on your website. The odd one out in the repository, and worth knowing about so you don't deploy it to a web page by mistake.
Read the demo, not the README.
Several folders carry a README copied from a different widget — at the time of writing, four describe the Group Finder regardless of what they actually do, one describes a charting example, and one still reads "Fill in Widget Details". The demo.html
, the stored procedure name and the Liquid template are reliable; the README often is not. This is a community repository and nobody's job is to tidy it.
Widgets hosted on this site
Several of the widgets demonstrated here do not exist in the community repository. They were built for this site, and their scripts, templates and embed code ship with their own installation guide rather than from GitHub. Where that is the case, the guide says so and lists the files by name. Take them from the guide, not from a repository search that will not find them.
Other sources
Partners, consultants, other MinistryPlatform churches, and anything you or your developer writes. The framework is open — a custom widget is just a registered stored procedure and a Liquid template, so anyone can produce one.
Vetting a widget before you install it
A custom widget is not a plugin running in a sandbox. Installing one means running SQL you did not write against your MinistryPlatform database, and pointing a page at a template hosted somewhere you may not control. Both deserve a look before you proceed, whatever the source.
- Read the SQL before you run it.
You are looking for what it selects and what it writes. Most of these procedures are read-only; one that issues an
INSERTorUPDATEshould say so in its guide, and you should understand why. - Check what data it exposes. A procedure that returns more columns than the template displays is publishing them anyway — anyone can read the API response directly. This matters most for widgets touching contacts, giving or sacramental records.
- Check whether it requires a login, and whether it should.
A widget reading personal data with
data-requireUser="false"is a problem regardless of how good it looks. - Know where the template is served from.
If it points at a repository's
@mainbranch, your page follows whatever that repository does next. Fine while evaluating, worth pinning or copying before you go live. - Test on a draft page first. An unpublished page with real data tells you more than any amount of reading, and costs nothing if it goes wrong.