Add the FactBox to Any Page
This page provides a ready-to-use template for adding the SharePoint Connector FactBox to any page in Business Central. The template uses background tasks so that SharePoint calls do not block the UI.
Based on the guide at hougaard.com.
Prerequisites
- A table mapping must exist for the source table of the page you are extending.
- Your extension must depend on the SharePoint Connector (see App Dependency).
App Dependency
In your extension's app.json, add a dependency on the SharePoint Connector:
{
"dependencies": [
{
"id": "23ebd065-b289-4a68-85e3-b8410e360157",
"name": "SharePoint Connector",
"publisher": "hougaard.com",
"version": "6.0.0.0"
}
]
}
Page Extension Template
Copy the template below and replace the three placeholders:
| Placeholder | Replace with |
|---|---|
<Object Number> |
Your page extension object number (e.g., 50100) |
<Give it a Name> |
A descriptive name (e.g., "Job Card SharePoint") |
<PageNameToBeExtended> |
The target page name (e.g., "Job Card") |
pageextension <Object Number> "<Give it a Name>" extends <PageNameToBeExtended>
{
layout
{
addfirst(factboxes)
{
part(SharePointPart_EFQ; "SharePoint Factbox EFQ")
{
ApplicationArea = All;
Visible = SharePointActivated;
}
}
}
trigger OnAfterGetCurrRecord()
var
Mapping: Record "Table Mapping EFQ";
SP: Codeunit "SharePoint EFQ";
Ref: RecordRef;
Parms: Dictionary of [Text, Text];
EmptyData: Dictionary of [Text, Text];
begin
if SharePointActivated then
if GuiAllowed then begin
Ref.GetTable(Rec);
SP.GetTableMapping(Mapping, Ref);
Clear(Parms);
Parms.Add('sitename', Mapping."Site Name");
Parms.Add('basefolder', Mapping."Base Folder");
Parms.Add('folder', CurrPage.SharePointPart_EFQ.Page.PrepareFill(Ref));
if not CurrPage.EnqueueBackgroundTask(
PageTaskId,
Codeunit::"Factbox Background Task EFQ",
Parms,
30000,
PageBackgroundTaskErrorLevel::Warning)
then begin
EmptyData.Add('data',
'{"error":{"code":"-2147024894, System.IO.FileNotFoundException",' +
'"message":{"lang":"en-US","value":"File Not Found."}}}');
CurrPage.SharePointPart_EFQ.Page.FinishFill(EmptyData);
end;
end;
end;
trigger OnPageBackgroundTaskCompleted(TaskId: Integer; Results: Dictionary of [Text, Text])
begin
if SharePointActivated then
if TaskId = PageTaskId then
CurrPage.SharePointPart_EFQ.Page.FinishFill(Results);
end;
trigger OnPageBackgroundTaskError(TaskId: Integer; ErrorCode: Text; ErrorText: Text;
ErrorCallStack: Text; var IsHandled: Boolean)
begin
if SharePointActivated then
if TaskId = PageTaskId then
CurrPage.SharePointPart_EFQ.Page.FillError(ErrorText);
end;
trigger OnOpenPage()
var
TableMapping: Record "Table Mapping EFQ";
Ref: RecordRef;
begin
Ref.GetTable(Rec);
SharePointActivated := TableMapping.NeedsFactBox(Ref.Number());
end;
var
SharePointActivated: Boolean;
PageTaskId: Integer;
}
How It Works
OnOpenPage
Checks whether a table mapping exists for the page's source table. If no mapping is configured, the FactBox stays hidden and no SharePoint calls are made.
OnAfterGetCurrRecord
Each time the user selects a record, the trigger:
- Gets a
RecordReffor the current record. - Looks up the table mapping to find the SharePoint site and base folder.
- Calls
PrepareFillon the FactBox to calculate the record's folder path. - Enqueues a
PageBackgroundTaskwith a 30-second timeout so the SharePoint call runs without blocking the UI. - If the background task cannot be enqueued (e.g., maximum tasks reached), it sends an empty result to the FactBox to avoid a spinner that never completes.
OnPageBackgroundTaskCompleted
When the background task finishes, the results (file list from SharePoint) are passed to the FactBox via FinishFill.
OnPageBackgroundTaskError
If the background task fails, the error is displayed in the FactBox via FillError instead of raising a runtime error.
Example: Adding FactBox to the Job Card
pageextension 50100 "Job Card SharePoint" extends "Job Card"
{
layout
{
addfirst(factboxes)
{
part(SharePointPart_EFQ; "SharePoint Factbox EFQ")
{
ApplicationArea = All;
Visible = SharePointActivated;
}
}
}
trigger OnAfterGetCurrRecord()
var
Mapping: Record "Table Mapping EFQ";
SP: Codeunit "SharePoint EFQ";
Ref: RecordRef;
Parms: Dictionary of [Text, Text];
EmptyData: Dictionary of [Text, Text];
begin
if SharePointActivated then
if GuiAllowed then begin
Ref.GetTable(Rec);
SP.GetTableMapping(Mapping, Ref);
Clear(Parms);
Parms.Add('sitename', Mapping."Site Name");
Parms.Add('basefolder', Mapping."Base Folder");
Parms.Add('folder', CurrPage.SharePointPart_EFQ.Page.PrepareFill(Ref));
if not CurrPage.EnqueueBackgroundTask(
PageTaskId,
Codeunit::"Factbox Background Task EFQ",
Parms,
30000,
PageBackgroundTaskErrorLevel::Warning)
then begin
EmptyData.Add('data',
'{"error":{"code":"-2147024894, System.IO.FileNotFoundException",' +
'"message":{"lang":"en-US","value":"File Not Found."}}}');
CurrPage.SharePointPart_EFQ.Page.FinishFill(EmptyData);
end;
end;
end;
trigger OnPageBackgroundTaskCompleted(TaskId: Integer; Results: Dictionary of [Text, Text])
begin
if SharePointActivated then
if TaskId = PageTaskId then
CurrPage.SharePointPart_EFQ.Page.FinishFill(Results);
end;
trigger OnPageBackgroundTaskError(TaskId: Integer; ErrorCode: Text; ErrorText: Text;
ErrorCallStack: Text; var IsHandled: Boolean)
begin
if SharePointActivated then
if TaskId = PageTaskId then
CurrPage.SharePointPart_EFQ.Page.FillError(ErrorText);
end;
trigger OnOpenPage()
var
TableMapping: Record "Table Mapping EFQ";
Ref: RecordRef;
begin
Ref.GetTable(Rec);
SharePointActivated := TableMapping.NeedsFactBox(Ref.Number());
end;
var
SharePointActivated: Boolean;
PageTaskId: Integer;
}
Disabling the FactBox from Another Extension
Before the shared FactBox activates for a page, it raises the OnBeforeActivatingAnything event with the table number of the page's source table. Set FactboxDisabled to true to keep the FactBox hidden and skip all SharePoint calls for that table, without modifying the page extensions.
codeunit 50101 "Hide SP Factbox For Items"
{
[EventSubscriber(ObjectType::Page, Page::"SharePoint Factbox EFQ", 'OnBeforeActivatingAnything', '', false, false)]
local procedure HideForItems(GlobalTableNo: Integer; var FactboxDisabled: Boolean)
begin
if GlobalTableNo = Database::Item then
FactboxDisabled := true;
end;
}
Typical uses are hiding the FactBox for tables that are mapped but should not expose documents to a group of users, or switching it off during data migration.
Checklist
Before deploying your extension:
- Table mapping exists for the target page's source table
- App dependency added to
app.json - Object number and name are unique
- FactBox appears on the page
- Files load when a record is selected
- Upload and download work correctly
- FactBox hides when no mapping is configured