Using WordPress? Every integration here is also available as a plugin. Browse the plugin store →

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

  • sharepoint
  • search
  • documents
  • shortcodes
  • microsoft graph

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 a listid — choose SharePoint list as the source. The siteid and listid from 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 a driveid, and [o365_sp_search] — choose SharePoint or OneDrive as the source for a file browser.
  • userbinding="yes" and password are not covered: both gate on a logged-in WordPress user. Worth knowing before you migrate that userbinding never 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.

AttributeWhat you lose
single_page_templateThe 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.
datafilterThe cascading refiner (“Document Type” → “Product Family” → “Product”). There is no facet UI on any component.
exclude_foldersExcluding 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 read sort= at all (that view reads orderby=, 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/token with grant_type=client_credentials and scope=https://graph.microsoft.com/.default, using the client_id and client_secret from the base plugin’s settings.
  • Returns to admin.php?page=o365_settings with either o365_token_message=Successfully get Sharepoint List access token or o365_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_in and search_token_expires_timestamp. That last value is set to time() + 3600 — the plugin assumes a one-hour lifetime rather than reading expires_in.
  • Renews on init once the stored timestamp is within 300 seconds of now.

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:

PurposeEndpoint
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:

ShortcodeWhat 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:

  1. wp-content/themes/<your-theme>/o365-display/sp-search/views/<file>
  2. 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_field form 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/splisttoken returns the stored Graph access token to any logged-in WordPress user (permission_callback is is_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

Back to the knowledge base