Knowledge base WordPress plugins Data and documents
Configuring SharePoint Search and Document Display
This plugin is being retired. How to rebuild each shortcode in the Documents component, exactly which attributes have no equivalent — and, for reference, the original setup: base plugin, licence key, access token, the seven shortcodes and what each failure message means.
Published 12 August 2026
This covers Office 365 Sharepoint List, Documents and Search Display (v3.7), the plugin that adds SharePoint search, document lists and item previews to WordPress through shortcodes. Everything below is drawn from the plugin’s own source, including the exact strings it prints when something is wrong.
It is an add-on, not a standalone plugin. It will not activate on its own, and each setup step below unlocks the next — do them in this order.
This plugin is being retired
Everything below still describes how it works today. Most anonymous configurations it can render are rebuildable in the Documents component in the WPI dashboard, which reads the same SharePoint libraries and lists through Microsoft Graph and needs no plugin licence key or access token on your site. The list of what is not rebuildable is further down this section, in full, so you can check before you start rather than after.
[o365_sp_search_list], and[o365_sp_view]with alistid— choose SharePoint list as the source. Thesiteidandlistidfrom your shortcode paste straight into the builder’s Enter a site or list by hand panel; the component then reads the list’s own columns so you can pick which to publish, as a table, a stacked list or a card grid.[o365_sp_view]with adriveid, and[o365_sp_search]— choose SharePoint or OneDrive as the source for a file browser.userbinding="yes"andpasswordare not covered: both gate on a logged-in WordPress user. Worth knowing before you migrate thatuserbindingnever trimmed SharePoint permissions — every visitor, signed in or not, received identical content, because every Graph call here uses one tenant-wide token.
What has no equivalent yet
These attributes work today and cannot be rebuilt. None of them is a rendering detail; each changes what a visitor can do.
| Attribute | What you lose |
|---|---|
single_page_template | The click-through. A masonry_tpl.php card is a link to a generated page for that one item; the new card is not a link, and there is no per-item page to link to. |
datafilter | The cascading refiner (“Document Type” → “Product Family” → “Product”). There is no facet UI on any component. |
exclude_folders | Excluding folders from that refiner. It only ever affected datafilter. |
autocomplete (on a list) | The type-ahead suggestion list. A library or folder embed now has one — switch on Suggest file names as a visitor types in the builder’s Features zone. A list does not: this plugin’s list suggestions are built from the rows it had already downloaded, and the new component does not download the whole list. |
searchparam and autoloadresult were on this list until August 2026 and are not any
more:
searchparam— the builder’s Features zone has Start with this search. The box opens with your term in it and the page loads already searched. On a library embed that also reproduces what the attribute really does in this plugin: it turns the first read into a search rather than a listing of the top folder. If you have pinned a root folder in the Source zone, that search covers the folder and everything under it; with no root folder set, it covers the whole library.autoloadresult="no"— the same zone has Load rows when the page opens. Switch it off and the embed shows only the search box until a visitor searches, with no request to Microsoft on a page view at all. That is the reason to reach for it: it is the one setting that stops a busy page reading SharePoint every time it is opened.
Three differences of DEGREE rather than of kind, which matter most on a long list or a large library:
-
The list search box works one of two ways, and the page tells a visitor which. On a list short enough to arrive in one read — which is most of them — the box behaves exactly as it did in the retired plugin: it matches anywhere inside a value, in every column on screen, including Choice, person, date and number columns, and it costs no request at all. The page says Searched the whole list for ”…” anywhere in ….
On a list too long for one read, the search moves to Microsoft, who runs it over the list itself rather than over the rows already downloaded — which reaches rows no page ever loaded, and is the case the retired plugin handled by downloading every row into the browser first. That path matches differently, and it is the part worth knowing before you migrate: it matches the start of a value in the text columns you published, so Inv finds Invoice 4102 and does not find 2024-Invoice-A, and Choice, person, date and number columns cannot be searched at all. Microsoft offers no mid-word search for list rows. The page says …starting with ”…” on that path, and names the columns it searched, under the box.
One read walks a bounded number of pages, so on a very long list it can stop before the end; when it does, the page says so under the box, and Show Previous / Next carries a visitor the rest of the way. Turning Show Previous / Next on always puts the search on the Microsoft path, because the browser then holds one page rather than the list. Sortable headings are the part that still covers only the page a visitor is looking at; turn Show Previous / Next on in the Layout zone so every row stays reachable, and use Sort in the Source zone (for example
fields/Amount desc) to order the whole list.Two things can make that search refuse outright on a list that otherwise works, and both have a fix:
- Past 5,000 items, SharePoint will not run a “starts with” over a column that is not indexed, and answers This list can’t be searched. Index the column you search on in the list’s own settings (List settings → Indexed columns), or narrow the embed with Filter in the builder’s Source zone so it reads under the threshold. This is a SharePoint list-view limit, not a limit of the component — the retired plugin avoided it by downloading every row into the browser first, which is the cost the new component removes.
- A multi-line column cannot be filtered by SharePoint at all, and because a search asks about every published text column at once, one such column left marked Text takes every search on the list down with it. Read your columns again in the Columns zone: a multi-line column is corrected to Text (multi-line) and is simply not searched, leaving the rest of the search working. If a column still disagrees after that, its own row in that zone offers the change as one click — Microsoft describes this column as Text (multi-line) … Use Text (multi-line). Do not reach the other way and set a multi-line column to Text: that is the setting that takes the search down.
-
In a library or folder view a search covers the whole library or the whole folder you scoped the embed to — Microsoft runs it, not the browser — and only the RESULTS arrive a page at a time. (That is the one place the new search is wider than the list one described above, and the line here previously said the opposite.) Sorting does not belong on this list at all, and saying it did was our mistake: this plugin never ordered a library view by the
sort=you typed — it sends the value as a parameter Microsoft ignores, and on the table view it does not readsort=at all (that view readsorderby=, which it sends in the same ignored form). Either way its table starts unsorted until someone clicks a heading. The new embed asks Microsoft to order the whole folder; where the library answers, the sort covers all of it, and where it declines, the page itself says so under Previous / Next. Folders always group above files within the page a visitor is on. (sort=on a masonry or list view is real, and it rebuilds as the Sort field in the builder’s Source zone.) -
A person or lookup column you created yourself renders blank, because Microsoft answers with a row reference rather than a name. Created by and Modified by — the two most migrations actually use — do come through.
Ask support for the full attribute-by-attribute map if you want to check a specific shortcode before rebuilding it.
1. The base plugin has to be there first
On activation the plugin checks for o365/o365.php. If it is not active, this plugin
deactivates itself and stops with:
This plugin requires o365 base plugin.
It also reads the option o365_base_plugin_is_verify. If that is 0, activation stops
with a pointer to the base plugin’s settings:
ERROR: This plugin needs to be configured on the settings page >> General tab, before proceeding.
The General tab is where the o365_settings option is populated. Three of its keys are
used by every feature here: client_id, client_secret and tenant_name.
On a multisite install, if o365_settings['shared_settings'] is "yes", the plugin
reads and writes network-wide options (get_site_option) instead of per-site ones.
Otherwise every site keeps its own settings and its own token.
2. Enter the licence key
Until the key is entered, every shortcode renders a sentence instead of content:
Please enter the Office365 SharePoint Display Plugin activation key.
[o365_sp_search_list] prints the same sentence with a stray 1 on the end
(...activation key1.), which is a quick way to tell which shortcode on a busy page is
the one complaining.
The field lives on the base plugin’s licence screen, admin.php?page=o365_license,
in the row labelled Office 365 Sharepoint Search Display. There is also a permanent
admin nag pointing at it. Activating stores o365_sp_search_lcode and sets
o365_sp_search_verify_auth to yes; the shortcodes check that second option, so a
“Delete License” wipes both and the front end reverts to the message above.
3. Get an access token
While no token is stored you get an admin notice on every page:
Office 365 Sharepoint List, Documents and Search Display Plugin doesn’t have an access token.
Use the Get Access Token button on the base plugin’s token screen
(admin.php?page=o365_settings). Once a token exists that button is replaced by
Revoke Token.
What the code does with it:
- Requests a token from
https://login.microsoftonline.com/<tenant-id>/oauth2/v2.0/tokenwithgrant_type=client_credentialsandscope=https://graph.microsoft.com/.default, using theclient_idandclient_secretfrom the base plugin’s settings. - Returns to
admin.php?page=o365_settingswith eithero365_token_message=Successfully get Sharepoint List access tokenoro365_token_message=Error while getting access token. The failure case does not surface the error body Azure sent, so a bad secret and a bad tenant name look identical here. - Stores
search_access_token,search_token_expires_inandsearch_token_expires_timestamp. That last value is set totime() + 3600— the plugin assumes a one-hour lifetime rather than readingexpires_in. - Renews on
initonce the stored timestamp is within 300 seconds of now.
What to consent to in Azure
This is a client-credentials (app-only) token, and the only scope the plugin ever
requests is .default — meaning “everything already consented on this app
registration”. The plugin therefore names no individual permission anywhere, and adding
one to the registration is an Azure-side job that needs tenant admin consent. The
consent round-trip is recognised by the plugin when Azure sends the browser back with
state=o365_search_identifier and an admin_consent parameter.
To know what your registration needs to be able to do, the Graph endpoints the plugin actually calls are:
| Purpose | Endpoint |
|---|---|
| Search a document library | /v1.0/sites/{site}/drives/{drive}/root/search(q='…') |
| Browse a library or folder | /v1.0/sites/{site}/drives/{drive}/root/children, /items/{folder}/children |
| List items and their fields | /v1.0/sites/{site}/lists/{list}/items?expand=fields |
| List column definitions | /v1.0/sites/{site}/lists/{list}/columns |
| Embed preview | /v1.0/sites/{site}/drives/{drive}/items/{item}/preview |
| Directory lookups (form fields) | /v1.0/users, /v1.0/groups/{id}/members |
Because the token is app-only, there is no visitor identity in any of these calls.
Results are not trimmed to the browsing user’s SharePoint permissions. The
userbinding attribute is not a substitute for that — see below.
Deactivating the plugin deletes the token. The deactivation hook removes
search_access_token, search_token_expires_in and search_token_expires_timestamp
at both site and network scope, so a deactivate/reactivate cycle always ends with
step 3 again.
4. The shortcodes
Seven are registered, one per file in the plugin’s shortcode/ directory:
| Shortcode | What it renders |
|---|---|
[o365_sp_search] | Keyword search across one or more document libraries (drives) |
[o365_sp_search_list] | Keyword search across SharePoint lists |
[o365_sp_view] | Library/folder browser — or a SharePoint list, when listid is set — optionally with a search box and filters |
[o365_sp_view_user_context] | Site/drive view rendered in a user context template |
[o365_sp_embed] | A single item: thumbnail link, or an iframe preview |
[mgt_files_list] | File list built on the Microsoft Graph Toolkit templates |
[mgt_sp_view] | Graph Toolkit list view driven by a query |
The mgt_ pair works differently
[mgt_files_list] and [mgt_sp_view] do not use the stored app-only token for
rendering. Their templates load the Microsoft Graph Toolkit from
https://unpkg.com/@microsoft/mgt/dist/bundle/mgt-loader.js and put an
<mgt-msal2-provider> on the page, configured with the base plugin’s client_id,
login-type="redirect" and authority="https://login.microsoftonline.com/common",
with the current page URL as the redirect URI. An <mgt-get resource="…"> element then
fetches whatever the query attribute names.
Two consequences. First, the visitor signs in to Microsoft 365 in their own browser, so these views are permission-trimmed the way the app-only shortcodes are not. Second, the page URL has to be registered as a redirect URI on the app registration, and the registration needs delegated permissions rather than only application ones.
Both take query, proxy, template, proxybinding="yes",
singledisplaytype="single" and single_page_template. [mgt_files_list] adds
siteid, driveid and itemid; [mgt_sp_view] adds driveid, listid and
postquery. Setting proxy swaps in an <mgt-proxy-provider> pointed at that URL
instead of the MSAL provider.
[o365_sp_search] attributes and defaults
url="" siteid="" driveid="" itemid=""
searchparam="" autoloadresult="yes" includefile="" template=""
maxresults="10" orderby="" userbinding="no" id="o365"
searchform="no" columns="title,author" source_id=""
siteurl="" filetype=""
single_page_template="o365-sp-display-single-page"
[o365_sp_search_list] takes the same set with listid in place of driveid, plus
table_col="" and pagination="false".
[o365_sp_view] takes the largest set: url, siteid, driveid, listid,
listname, template, id="o365", maxresults="10", pagination="no", sort,
columns, userbinding="no", password, lookups, searchparam, includefile,
duplicates="false", folderurl, folderid, autoloadresult="yes", orderby,
searchform, siteurl, source_id, datafilter, filter, exclude_folders,
autocomplete="yes", dynamiccolumns="no", single_page_template.
[o365_sp_embed] takes siteid, itemid, driveid, id="o365", width, height,
view, userbinding. With view="iframe" it POSTs to the item’s /preview endpoint
and renders an iframe at width/height; otherwise it requests
?$expand=thumbnails and renders the thumbnail as a link to the item’s
@microsoft.graph.downloadUrl. With no thumbnail it falls back to a bundled
spreadsheet icon for .xlsx items and to no-preview.png for everything else. A
transport failure prints cURL Error #: followed by the cURL message, inline.
How the attributes actually behave
searchparam / autoloadresult. The keyword is taken from the query string
?search_keyword_sp= if present, otherwise from searchparam — but only when
autoloadresult="yes". The search handlers return nothing at all when there is no
keyword, so a search shortcode with neither set renders an empty results area rather
than “everything”.
driveid and listid accept a comma-separated list. Each one is queried
separately and the results are concatenated. In the browse handler, maxresults is
divided by the number of drives, so maxresults="10" across two drives asks each for
five.
folderid switches the browse query from /root/children to
/items/{folderid}/children, and internally raises the page size to 4999 — folder
drill-down is not paginated the way the top level is.
columns is passed straight through as the Graph $select and defaults to
title,author. The results table then builds its own header from what came back:
Title (from name), Author (from createdBy.user.displayName), Created Date, Last
Modified Date, Size and Share appear when those fields are present; a Preview column
appears only if columns contains thumbnails or preview, and a Download column
only if it contains @microsoft.graph.downloadUrl. A missing column is usually a
missing entry in columns, not a template problem.
orderby / sort are appended to the Graph request as &sort='…'.
filetype and includefile are different things. filetype is a comma-separated
list of MIME types, applied after the response comes back: an item is dropped if it
is a file whose file.mimeType is not in the list. Folders are never dropped.
includefile is a comma-separated list of extensions that is compiled into a
SharePoint refinement string (fileExtension:equals(pdf), or
fileExtension:or(pdf,docx) for more than one) — the source marks this attribute
//not tested, so prefer filetype unless you have verified includefile on your own
tenant.
userbinding="yes" does two things: it refuses to render for logged-out visitors
(“First need to login user in wordpress for see the SP search result.”, and the
equivalent wording in [o365_sp_embed] and [o365_sp_view_user_context]), and it adds
author:equals("<WordPress display name>") to the refinement string. It matches the
WordPress display name against the SharePoint author field. It is not SharePoint
permission trimming and does not depend on who the visitor is in Microsoft 365.
template — and the one that will catch you out
template takes a filename with its .php extension. It is resolved in two
places, theme first:
wp-content/themes/<your-theme>/o365-display/sp-search/views/<file>- the plugin’s own
templates/views/<file>
Templates shipped in templates/views/ include tabular_tpl.php, listv1_tpl.php,
listv2_tpl.php, masonry_tpl.php, tabular_list_tpl.php, mgt_tabular_tpl.php,
mgt_list_tpl.php, mgt_files_tpl.php, mgt_media_grid_tpl.php,
mgt_spforms_tpl.php, mgt_dynamicforms_tpl.php, sharepoint_account.php and
datafilters.php.
Always pass template. The defaults the code falls back to are not files that
exist in templates/views/ in this version — [o365_sp_search] and
[o365_sp_search_list] fall back to sp_bootstrap_template.php (which is present only
under templates/views/TemplatesBackup/), [o365_sp_view] falls back to
list_tpl.php, and [o365_sp_view_user_context] to accounts_template_tpl.php.
Neither of the last two exists at all. The include is wrapped in a file_exists()
check with no else, so the result is an empty <div class="o365_search"> and no
error, no notice and nothing in the log. A shortcode that renders a blank space is
almost always this.
[mgt_files_list] and [mgt_sp_view] are the exceptions — their fallbacks
(mgt_tabular_tpl.php, mgt_list_tpl.php) do ship.
5. The generated single-item pages
On every admin request the plugin ensures two pages exist, creating them if they are
missing: “Sp Item” (o365_wp_sp_display_single_page_id) and “mgt single page”
(o365_wp_sp_display_mgt_single_page_id). Delete them and they come back on the next
wp-admin page load — the check is on admin_init.
They back these permalinks:
/sp-item/<title>/<id>/<template>— the last two segments are optional/mgt-single-page/<site>/<list>/<item>
The page templates are templates/pages/o365-sp-display-single-page.php and
templates/pages/mgt-sp-display-single.php, both overridable from your theme at
o365-display/sp-search/pages/. The single_page_template shortcode attribute names
the file without the .php extension and defaults to
o365-sp-display-single-page.
The rewrite rules are registered on init, and the plugin never calls
flush_rewrite_rules(). If /sp-item/... 404s after install, re-save
Settings > Permalinks once.
6. Optional integrations
- Contact Form 7: when CF7 is active, the plugin registers an
o365splist_fieldform tag and adds an SP List field button to the tag generator. - Gravity Forms: when Gravity Forms is active, it registers two add-ons — a SharePoint list-column field and an Azure AD user field.
- REST:
GET /wp-json/wp/v2/splisttokenreturns the stored Graph access token to any logged-in WordPress user (permission_callbackisis_user_logged_in()). If no token or tenant name is configured it returns{"message":"SharePoint token is not ready for get data from sharepoint."}. Treat that route as sensitive: it hands out an app-only Graph token to every authenticated account on the site. - Updates are checked against
https://api.wpintegrate.com/plugins_update_api_conn.php, and only when you visit Plugins, at most once a day.
Troubleshooting
“Tenant name or Sharepoint token data not defined.Please O365 check base plugin
setting tab.” — printed in a <p class='error'> by every shortcode. It means one of
two options is empty: o365_settings['tenant_name'], or search_access_token. Redo
step 3. If you recently deactivated and reactivated the plugin, the token was deleted
on deactivation.
A shortcode renders a blank area, silently. The template file was not found. See
the template section above — this is the most likely single cause, and it produces no
diagnostics of any kind.
The results table says “No results found!” — the Graph call returned zero rows.
Check that the keyword is reaching the handler (?search_keyword_sp= or searchparam
with autoloadresult="yes"), and that siteid/driveid are the composite Graph IDs
rather than SharePoint URLs.
A browser alert saying “search error” comes from the autocomplete request in
tabular_tpl.php when the admin-ajax.php call fails. Open the network tab: the
response body is the real error.
Search works when you are logged in and not when you are logged out. Only some AJAX
actions are registered for anonymous visitors. Registered for both: the record and
next-record loaders for lists, the search form submit for drives and for lists, file
download, the drive autocomplete, and the filter-options loader. Not registered for
anonymous visitors: o365_sp_search_next_records, o365_sp_view_records and
o365_sp_search_list_records_auto_complete — those return 0 from admin-ajax.php
for logged-out users, so “load more” on a search template and list autocomplete can
work for an editor and silently do nothing for the public.
Results ignore SharePoint permissions. Expected: the token is app-only. Anything
the app registration can read, any visitor to the page can see. Scope the shortcode
with siteid/driveid/folderid rather than relying on tenant permissions, and use
WordPress-side page restrictions if the content should not be public.
An intranet with strict egress or a CSP blocks the layout. The bundled templates
load jQuery, jQuery UI, Bootstrap and DataTables from public CDNs
(cdnjs.cloudflare.com, code.jquery.com, maxcdn.bootstrapcdn.com,
cdn.datatables.net, cdn.jsdelivr.net) in addition to the plugin’s own assets. Copy
the template into your theme and swap those tags for local handles if that is a
problem.
Everything works, then stops after an hour. The token refresh runs on init when
the stored timestamp is within five minutes of expiry. That timestamp is written as
time() + 3600 at issue rather than from Azure’s expires_in, so if your tenant
issues shorter-lived tokens the plugin will keep using an expired one until its own
clock catches up.
What gets removed
Uninstalling deletes search_tenant_id, search_access_token, search_refresh_token,
search_token_expires, o365_sp_search_verify_auth and o365_sp_search_lcode. The
two generated pages and the o365_settings option (which belongs to the base plugin)
are left in place.
Read next
-
WordPress plugins · Data and documents
Embedding OneDrive files in WordPress with OneDrive Display
The five shortcodes the Office 365 OneDrive Display plugin registers, the order it wants you to connect things in, and what each failure message on the page actually means.
-
WordPress plugins · Getting started
Setting up the Office 365 Microsoft Booking plugin
Base plugin, Azure token, licence, then a shortcode — the order the plugin actually enforces, with every [msb_booking] attribute and its default.
-
WordPress plugins · Getting started
SharePoint Calendar Display: setup, shortcodes and room booking
Installing Office 365 SharePoint Calendar Display, the order the plugin requires, every attribute of [o365_sp_events] and [o365_sp_booking], and what each failure message means.