File Script Action
File Script actions combine HTML templates with embedded AL code, offering the best of both File and Script action types.
Overview
A File Script action:
- Uses HTML/template syntax for structure
- Embeds AL code blocks for logic
- Compiles to executable form
- Provides dynamic content generation
When to Use File Script Actions
Use File Script actions for:
- Pages with complex logic embedded in HTML
- Dynamic content that needs AL processing
- Templates requiring calculations
- Mixed static and dynamic content
Creating a File Script Action
Step-by-Step
- Open the Advanced Portal Designer menu page (search for it in Tell Me) and click Actions
- Click New
- Enter the Web Action code
- Set Type to "File Script"
- Click the "Edit Script" link
- Write your HTML with embedded AL
- Save and compile
Syntax
Basic Structure
<?bc
// AL code block - declarations and setup
var
Customer: Record Customer;
CustomerName: Text;
?>
<!DOCTYPE html>
<html>
<head>
<title>Customer Page</title>
</head>
<body>
<?bc
Customer.FindFirst();
CustomerName := Customer.Name;
?>
<h1>Welcome, <?bc output(CustomerName); ?></h1>
</body>
</html>
The <?bc ?> Tag
AL code is wrapped in <?bc ?> tags:
- First block: Must start with
<?bc- sets up the script context - Subsequent blocks: Contain AL code to execute
- Output: Use
output()to insert content
Script Functions Reference
File Script actions have access to the same additional AL commands as Script actions. These can be used inside <?bc ?> blocks:
| Function | Description |
|---|---|
output(<text>) |
Send text/HTML content into the page at the current position |
response(<html/redirect>,<ContentType>,<RedirectLink>) |
Set the response type - use to return specific content types or redirect the user |
parm(<parameter>) |
Get a parameter value from the query string or POST data |
action(<name>,[Record Variable]) |
Navigate to another action, optionally passing a record variable |
currentuserid() |
Get the current APD user ID |
setlanguage(<language no>) |
Set the language for the response |
updatesession(<Type>,<Number>,'',<Security Profiles Filter>) |
Update the current session. Type: 1=Customer, 2=Vendor, 3=Contact, 4=Salesperson |
stripestatus(<Stripe Session>,<SettingToGet>) |
Get the status of a Stripe payment session |
For detailed descriptions and examples of each function, see the Script Action documentation.
Code Blocks
Declaration Block
The first <?bc ?> block typically contains variable declarations:
<?bc
var
Customer: Record Customer;
Items: Record Item;
Total: Decimal;
Html: TextBuilder;
?>
Logic Blocks
Subsequent blocks contain executable code:
<?bc
Customer.Get('10000');
Total := Customer."Balance (LCY)";
?>
Inline Output
Use output() within HTML context:
<p>Customer: <?bc output(Customer.Name); ?></p>
<p>Balance: <?bc output(format(Total, 0, '<Precision,2:2>')); ?></p>
Complete Example
<?bc
var
SalesHeader: Record "Sales Header";
SalesLine: Record "Sales Line";
OrderTotal: Decimal;
?>
<!DOCTYPE html>
<html>
<head>
<title>My Orders</title>
<style>
table { border-collapse: collapse; width: 100%; }
th, td { border: 1px solid #ddd; padding: 8px; }
</style>
</head>
<body>
<h1>Your Orders</h1>
<table>
<tr>
<th>Order No.</th>
<th>Date</th>
<th>Amount</th>
</tr>
<?bc
SalesHeader.SetRange("Document Type", SalesHeader."Document Type"::Order);
SalesHeader.SetRange("Sell-to Customer No.", Rec."Navision number");
if SalesHeader.FindSet() then
repeat
SalesHeader.CalcFields("Amount Including VAT");
?>
<tr>
<td><?bc output(SalesHeader."No."); ?></td>
<td><?bc output(format(SalesHeader."Order Date")); ?></td>
<td><?bc output(format(SalesHeader."Amount Including VAT", 0, '<Precision,2:2>')); ?></td>
</tr>
<?bc
OrderTotal += SalesHeader."Amount Including VAT";
until SalesHeader.Next() = 0;
?>
</table>
<p><strong>Total: <?bc output(format(OrderTotal, 0, '<Precision,2:2>')); ?></strong></p>
</body>
</html>
Conditional Content
If Statements
<?bc
if Rec."User Authenticated" then
?>
<p>Welcome back, <?bc output(Rec.UserID); ?>!</p>
<?bc
else
?>
<p>Please <a href="?action=LOGIN">log in</a>.</p>
<?bc
end;
?>
Shorter Form
<?bc if Customer."Balance (LCY)" > 0 then ?>
<p class="positive"><?bc output(format(Customer."Balance (LCY)")); ?></p>
<?bc else ?>
<p class="zero">No balance</p>
<?bc end; ?>
Loops
Repeat Loop
<ul>
<?bc
if Item.FindSet() then
repeat ?>
<li><?bc output(Item.Description); ?></li>
<?bc until Item.Next() = 0;
?>
</ul>
Building Tables
<table>
<?bc
Customer.FindSet();
repeat ?>
<tr>
<td><?bc output(Customer."No."); ?></td>
<td><?bc output(Customer.Name); ?></td>
</tr>
<?bc until Customer.Next() = 0; ?>
</table>
Accessing Parameters
Session Data
<?bc
// Access session record via Rec
var
CustomerNo: Code[20];
?>
<?bc
CustomerNo := Rec."Navision number";
?>
<p>Your customer number: <?bc output(CustomerNo); ?></p>
URL Parameters
<?bc
var
OrderNo: Code[20];
?>
<?bc
OrderNo := parm('orderno');
?>
<h1>Order: <?bc output(OrderNo); ?></h1>
HTML Escaping
When outputting user data, consider HTML escaping to prevent XSS:
<?bc
// Use a helper function to escape HTML
var
UserInput: Text;
SafeOutput: Text;
?>
<?bc
UserInput := parm('search');
SafeOutput := UserInput.Replace('<', '<').Replace('>', '>');
?>
<p>Search term: <?bc output(SafeOutput); ?></p>
Comparison with Other Types
| Feature | File | Script | File Script |
|---|---|---|---|
| HTML structure | Yes | Manual | Yes |
| AL code | Tags only | Full | Embedded |
| Complex logic | Limited | Yes | Yes |
| Template reuse | Yes | No | Limited |
| Best for | Simple pages | APIs | Dynamic pages |
Best Practices
Separate Concerns
Keep logic minimal in the template:
<?bc
// Do complex logic in one block
var
Data: Text;
?>
<?bc
// Prepare all data first
Data := PrepareComplexData();
?>
<!-- Then just output in HTML -->
<p><?bc output(Data); ?></p>
Error Handling
<?bc
var
HasError: Boolean;
ErrorMsg: Text;
?>
<?bc
if not TryLoadData() then begin
HasError := true;
ErrorMsg := GetLastErrorText();
end;
?>
<?bc if HasError then ?>
<div class="error"><?bc output(ErrorMsg); ?></div>
<?bc else ?>
<!-- Normal content -->
<?bc end; ?>
Performance
- Minimize database calls within loops
- Prepare data before output
- Use appropriate filters
Compilation
Requirements
File Script actions require compilation:
- Click the compile action
- Review any errors
- Test the page
Common Errors
| Error | Cause | Solution |
|---|---|---|
| Unclosed | Missing ?> | Add closing ?> |
| Unexpected code | AL outside | Wrap in |
| Missing begin | First block issue | Check structure |
Debugging
Tips for Debugging
- Start with simple content
- Add logic incrementally
- Use output() for debug values
- Check compilation errors carefully
Debug Output
<?bc
// Temporary debug output
output('DEBUG: Customer count = ' + format(Customer.Count));
?>