├── CODEOWNERS
├── .github
├── AL-Go-Settings.json
├── Test Current.settings.json
├── Test Next Major.settings.json
├── Test Next Minor.settings.json
├── workflows
│ ├── AddExistingAppOrTestApp.yaml
│ ├── IncrementVersionNumber.yaml
│ ├── CreateTestApp.yaml
│ ├── CreateApp.yaml
│ ├── CreatePerformanceTestApp.yaml
│ ├── UpdateGitHubGoSystemFiles.yaml
│ ├── CreateOnlineDevelopmentEnvironment.yaml
│ ├── Current.yaml
│ ├── NextMajor.yaml
│ ├── NextMinor.yaml
│ ├── PullRequestHandler.yaml
│ ├── PublishToEnvironment.yaml
│ └── CreateRelease.yaml
└── RELEASENOTES.copy.md
├── .AL-Go
├── settings.json
├── cloudDevEnv.ps1
└── localDevEnv.ps1
├── al.code-workspace
├── SystemAppPresentation
├── src
│ └── Examples
│ │ ├── 4_Barcode
│ │ ├── ItemBarcode.docx
│ │ └── ItemBarcode.Report.al
│ │ ├── 20_Email
│ │ ├── EmailScenario.EnumExt.al
│ │ └── Email.Cod.al
│ │ ├── 2_ImageCamera
│ │ ├── PurchaseHeader.TableExt.al
│ │ ├── PurchaseOrderPicture.Pag.al
│ │ ├── PurchaseOrder.PagExt.al
│ │ └── CameraandImage.Cod.al
│ │ ├── 6_RetentionPolicy
│ │ ├── RetenPolInstall.Codeunit.al
│ │ └── MyLog.Table.al
│ │ ├── 5_FilterTokens
│ │ ├── SalesOrderList.PageExt.al
│ │ └── FilterTokens.Codeunit.al
│ │ ├── 1_AzureStorage
│ │ ├── AzureContainers.Page.al
│ │ ├── AzureStorageSetup.Table.al
│ │ ├── ContainerContent.Page.al
│ │ ├── AzureBlobStorage.Codeunit.al
│ │ └── AzureStorageSetup.Page.al
│ │ ├── 13_AzureFunction
│ │ ├── AzureFunctionSetup.Table.al
│ │ ├── AzureFunctionMgt.Codeunit.al
│ │ └── AzureFunctionSetup.Page.al
│ │ ├── 15_Environment
│ │ ├── CompanyInformation.PagExt.al
│ │ └── EnvironmentInfo.Cod.al
│ │ ├── 3_GeoLocation
│ │ ├── FixedAssetCard.PageExt.al
│ │ └── AssetLocationMgt.Codeunit.al
│ │ ├── 19_UserSelection
│ │ ├── Customer.TabExt.al
│ │ └── CustomerCard.PagExt.al
│ │ ├── 8_RecurranceSchedule
│ │ ├── OccurrenceMgt.Codeunit.al
│ │ └── NextOccurrence.Pag.al
│ │ ├── 10_SharePoint
│ │ ├── SharePointLists.Page.al
│ │ ├── SharePointFiles.Page.al
│ │ ├── SharePointSetup.Table.al
│ │ ├── SharePointSetup.Page.al
│ │ └── SharePointMgt.Codeunit.al
│ │ ├── 17_Base64
│ │ ├── Base64.Cod.al
│ │ └── Base64.Pag.al
│ │ ├── 18_Zip
│ │ └── Zip.Cod.al
│ │ ├── 16_XMLWriter
│ │ ├── CustomerList.PagExt.al
│ │ └── XMLExport.Cod.al
│ │ ├── 21_Blob
│ │ ├── PersistentBlob.Cod.al
│ │ └── PersistentBlob.Pag.al
│ │ ├── 7_ChoosingObjects
│ │ ├── ObjectAndFields.Codeunit.al
│ │ └── ChoosingObjects.Pag.al
│ │ ├── 12_RegEx
│ │ └── RegExExamples.Page.al
│ │ ├── 11_Password
│ │ └── PasswordOperations.Page.al
│ │ ├── 14_Checklists
│ │ └── ChecklistInstall.Codeunit.al
│ │ └── 9_Math
│ │ └── MathOperations.Page.al
├── app.json
└── SystemAppExamples.permissionset.al
├── README.md
├── .gitignore
├── SUPPORT.md
└── SECURITY.md
/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @microsoft/dynamics-smb-engineering-systems
2 |
--------------------------------------------------------------------------------
/.github/AL-Go-Settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "PTE",
3 | "templateUrl": "https://github.com/microsoft/AL-Go-PTE@main"
4 | }
5 |
--------------------------------------------------------------------------------
/.AL-Go/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "country": "us",
3 | "appFolders": [],
4 | "testFolders": [],
5 | "bcptTestFolders": []
6 | }
7 |
--------------------------------------------------------------------------------
/.github/Test Current.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "artifact": "////latest",
3 | "cacheImageName": "",
4 | "versioningStrategy": 15
5 | }
6 |
--------------------------------------------------------------------------------
/al.code-workspace:
--------------------------------------------------------------------------------
1 | {
2 | "folders": [
3 | {
4 | "path": ".AL-Go"
5 | }
6 | ],
7 | "settings": {}
8 | }
9 |
--------------------------------------------------------------------------------
/.github/Test Next Major.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "artifact": "////nextmajor/{INSIDERSASTOKEN}",
3 | "cacheImageName": "",
4 | "versioningStrategy": 15
5 | }
6 |
--------------------------------------------------------------------------------
/.github/Test Next Minor.settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "artifact": "////nextminor/{INSIDERSASTOKEN}",
3 | "cacheImageName": "",
4 | "versioningStrategy": 15
5 | }
6 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/4_Barcode/ItemBarcode.docx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mynavblog/SystemAppExamples/HEAD/SystemAppPresentation/src/Examples/4_Barcode/ItemBarcode.docx
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # AL-Go Template
2 | ## Per Tenant Extension Project
3 | This template repository can be used for managing Per Tenant Extensions for Business Central.
4 |
5 | Please consult https://github.com/microsoft/AL-Go/#readme for scenarios on usage
6 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/20_Email/EmailScenario.EnumExt.al:
--------------------------------------------------------------------------------
1 | enumextension 54100 "MNB Email Scenario" extends "Email Scenario"
2 | {
3 | value(54100; "MNB My Email Scenario")
4 | {
5 | Caption = 'My Email Scenario';
6 | }
7 | }
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.app
2 | *.flf
3 | *.bclicense
4 | *.g.xlf
5 | .DS_Store
6 | Thumbs.db
7 | TestResults*.xml
8 | bcptTestResults*.json
9 | BuildOutput.txt
10 | rad.json
11 | .output/
12 | .dependencies/
13 | .buildartifacts/
14 | .alpackages/
15 | .packages/
16 | .alcache/
17 | .altemplates/
18 | .snapshots/
19 | cache_*
20 | ~$*
21 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/2_ImageCamera/PurchaseHeader.TableExt.al:
--------------------------------------------------------------------------------
1 | tableextension 54100 "MNB Purchase Header" extends "Purchase Header"
2 | {
3 | fields
4 | {
5 | field(54100; "MNB Invoice Picture"; Media)
6 | {
7 | Caption = 'MNB Invoice Picture';
8 | DataClassification = SystemMetadata;
9 | }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/SUPPORT.md:
--------------------------------------------------------------------------------
1 | # Support
2 |
3 | ## How to file issues and get help
4 |
5 | This GitHub repo is auto-generated from https://github.com/microsoft/AL-Go
6 | Issues, bug tracking and feature requests should be done there.
7 |
8 | Please follow the documentation [here](https://github.com/microsoft/AL-Go/blob/main/Scenarios/Contribute.md) if you want to contribute to AL-Go for GitHub.
9 |
10 | ## Microsoft Support Policy
11 |
12 | Support for this **PROJECT or PRODUCT** is limited to the resources listed above.
13 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/6_RetentionPolicy/RetenPolInstall.Codeunit.al:
--------------------------------------------------------------------------------
1 | codeunit 54105 "MNB Reten. Pol. Install"
2 | {
3 | ///
4 | /// Shows how to add tables to retention policy allowed tables
5 | ///
6 | ///
7 | Subtype = Install;
8 |
9 | trigger OnInstallAppPerCompany()
10 | var
11 | RetenPolAllowedTables: Codeunit "Reten. Pol. Allowed Tables";
12 | begin
13 | RetenPolAllowedTables.AddAllowedTable(Database::"MNB My Log Table")
14 | end;
15 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/6_RetentionPolicy/MyLog.Table.al:
--------------------------------------------------------------------------------
1 | table 54101 "MNB My Log Table"
2 | {
3 | DataClassification = SystemMetadata;
4 | Caption = 'My Log Table';
5 |
6 | fields
7 | {
8 | field(1; "Entry No."; Integer)
9 | {
10 | DataClassification = SystemMetadata;
11 | Caption = 'Entry No.';
12 | }
13 | field(2; "Description"; Text[100])
14 | {
15 | DataClassification = SystemMetadata;
16 | Caption = 'Description';
17 | }
18 | }
19 |
20 | keys
21 | {
22 | key(Key1; "Entry No.")
23 | {
24 | Clustered = true;
25 | }
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/2_ImageCamera/PurchaseOrderPicture.Pag.al:
--------------------------------------------------------------------------------
1 | page 54100 "MNB Purchase Order Picture"
2 | {
3 | Caption = 'MNB Purchase Order Picture';
4 | PageType = CardPart;
5 | SourceTable = "Purchase Header";
6 | ApplicationArea = All;
7 |
8 | layout
9 | {
10 | area(content)
11 | {
12 | group(General)
13 | {
14 | ShowCaption = false;
15 | field("MNB Invoice Picture"; Rec."MNB Invoice Picture")
16 | {
17 | ApplicationArea = All;
18 | ToolTip = 'Shows the invoice picture for the purchase order';
19 | }
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/5_FilterTokens/SalesOrderList.PageExt.al:
--------------------------------------------------------------------------------
1 | pageextension 54100 "MNB Sales Order List" extends "Sales Order List"
2 | {
3 | views
4 | {
5 | addlast
6 | {
7 | view(MNBNextWeek)
8 | {
9 | Caption = 'Next Week Orders';
10 | Filters = where("Requested Delivery Date" = filter('%nextweek'));
11 | SharedLayout = true;
12 | }
13 | view(MNBLastWeek)
14 | {
15 | Caption = 'Last Week Orders';
16 | Filters = where("Requested Delivery Date" = filter('%lastweek'));
17 | SharedLayout = true;
18 | }
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/1_AzureStorage/AzureContainers.Page.al:
--------------------------------------------------------------------------------
1 | page 54104 "MNB Azure Containers"
2 | {
3 | Caption = 'MNB Azure Containers';
4 | PageType = List;
5 | SourceTable = "ABS Container";
6 | UsageCategory = None;
7 | Editable = false;
8 | InsertAllowed = false;
9 | DeleteAllowed = false;
10 | ModifyAllowed = false;
11 |
12 | layout
13 | {
14 | area(content)
15 | {
16 | repeater(General)
17 | {
18 | field(Name; Rec.Name)
19 | {
20 | ApplicationArea = All;
21 | ToolTip = 'Specifies the value of the Name field.';
22 | }
23 | }
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/SystemAppPresentation/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "id": "cf2b1144-9cff-4f53-b6fe-17581ee30322",
3 | "name": "System App Presentation",
4 | "publisher": "BC4All.com",
5 | "version": "1.0.0.0",
6 | "brief": "",
7 | "description": "This extension shows examples for System App. ",
8 | "privacyStatement": "",
9 | "EULA": "",
10 | "help": "",
11 | "url": "",
12 | "logo": "",
13 | "dependencies": [],
14 | "screenshots": [],
15 | "platform": "1.0.0.0",
16 | "application": "21.0.0.0",
17 | "idRanges": [
18 | {
19 | "from": 54100,
20 | "to": 54149
21 | }
22 | ],
23 | "resourceExposurePolicy": {
24 | "allowDebugging": true,
25 | "allowDownloadingSource": false,
26 | "includeSourceInSymbolFile": false
27 | },
28 | "runtime": "11.0"
29 |
30 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/13_AzureFunction/AzureFunctionSetup.Table.al:
--------------------------------------------------------------------------------
1 | table 54103 "MNB Azure Function Setup"
2 | {
3 | DataClassification = SystemMetadata;
4 | Caption = 'Azure Function Setup';
5 |
6 | fields
7 | {
8 | field(1; "Primary Key"; Code[10])
9 | {
10 | Caption = 'Primary Key';
11 | DataClassification = SystemMetadata;
12 | }
13 | field(2; "Azure Function Url"; Text[250])
14 | {
15 | Caption = 'Azure Function Url';
16 | DataClassification = SystemMetadata;
17 | ExtendedDatatype = Masked;
18 | }
19 |
20 | }
21 |
22 | keys
23 | {
24 | key(PK; "Primary Key")
25 | {
26 | Clustered = true;
27 | }
28 | }
29 |
30 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/15_Environment/CompanyInformation.PagExt.al:
--------------------------------------------------------------------------------
1 | pageextension 54103 "MNB Company Information" extends "Company Information"
2 | {
3 | actions
4 | {
5 | addlast("Application Settings")
6 | {
7 | action(MNBEnvironmentInfo)
8 | {
9 | ApplicationArea = All;
10 | Image = Info;
11 | Caption = 'Environment Info';
12 | Promoted = true;
13 | PromotedCategory = Process;
14 | trigger OnAction()
15 | var
16 | MNBEnvironmentInfo: Codeunit "MNB Environment Info";
17 | begin
18 | MNBEnvironmentInfo.GetInfo();
19 | end;
20 | }
21 |
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/3_GeoLocation/FixedAssetCard.PageExt.al:
--------------------------------------------------------------------------------
1 | pageextension 54102 "MNB Fixed Asset Card" extends "Fixed Asset Card"
2 | {
3 | actions
4 | {
5 | addlast(navigation)
6 | {
7 | action(MNBGetCoordinates)
8 | {
9 | Caption = 'Get Coordinates';
10 | ToolTip = 'Shows GPS Coordinates for the fixed asset';
11 | Promoted = true;
12 | PromotedCategory = Process;
13 | PromotedIsBig = true;
14 | Image = Map;
15 | ApplicationArea = All;
16 | trigger OnAction()
17 | var
18 | AssetLocationMgt: Codeunit "MNB Asset Location Mgt";
19 | begin
20 | AssetLocationMgt.GetGeolocation(Rec);
21 | end;
22 |
23 | }
24 | }
25 | }
26 |
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/3_GeoLocation/AssetLocationMgt.Codeunit.al:
--------------------------------------------------------------------------------
1 | codeunit 54102 "MNB Asset Location Mgt"
2 | {
3 | ///
4 | /// The example how to get GPS coordinates
5 | ///
6 | ///
7 | internal procedure GetGeolocation(FixedAsset: Record "Fixed Asset")
8 | var
9 | Geolocation: Codeunit Geolocation;
10 | Latitude: Decimal;
11 | Longitude: Decimal;
12 | FixedAssetIsLocatedMsg: Label 'Fixed Asset %1 is located at: \ Latitude: %2 \ Longitude: %3', Comment = '%1 = FA description, %2 - Lat. value, %3 - Long. value';
13 | begin
14 | Geolocation.SetHighAccuracy(true);
15 | if Geolocation.RequestGeolocation() then
16 | Geolocation.GetGeolocation(Latitude, Longitude);
17 | Message(FixedAssetIsLocatedMsg, FixedAsset."Description", Latitude, Longitude);
18 | end;
19 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/15_Environment/EnvironmentInfo.Cod.al:
--------------------------------------------------------------------------------
1 | codeunit 54110 "MNB Environment Info"
2 | {
3 | procedure GetInfo()
4 | var
5 | EnvironmentInformation: Codeunit "Environment Information";
6 | TenantInformation: Codeunit "Tenant Information";
7 | InfoAboutEnvTxt: Label 'This is the information about the environment: \\ Is it Sandbox: %1 \\ Is it Production: %2 \\ Is it SaaS: %3 \\ Environment Name: %4';
8 | InfoAboutTenantTxt: Label 'This is the information about the tenant: \\ Tenant ID: %1 \\ Tenant Display Name: %2';
9 | begin
10 | Message(InfoAboutEnvTxt,
11 | EnvironmentInformation.IsSandbox(),
12 | EnvironmentInformation.IsProduction(),
13 | EnvironmentInformation.IsSaaS(),
14 | EnvironmentInformation.GetEnvironmentName());
15 |
16 | Message(InfoAboutTenantTxt,
17 | TenantInformation.GetTenantID(),
18 | TenantInformation.GetTenantDisplayName());
19 | end;
20 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/19_UserSelection/Customer.TabExt.al:
--------------------------------------------------------------------------------
1 | tableextension 54101 "MNB Customer" extends Customer
2 | {
3 | fields
4 | {
5 | field(54100; "MNB Responsible User"; Code[50])
6 | {
7 | DataClassification = EndUserPseudonymousIdentifiers;
8 | Caption = 'Responsible User';
9 |
10 | trigger OnValidate()
11 | var
12 | UserSelection: Codeunit "User Selection";
13 | begin
14 | UserSelection.ValidateUserName(Rec."MNB Responsible User");
15 | end;
16 |
17 | trigger OnLookup()
18 | var
19 | User: Record User;
20 | UserSelection: Codeunit "User Selection";
21 | begin
22 | UserSelection.FilterSystemUserAndGroupUsers(User);
23 | UserSelection.Open(User);
24 | Rec."MNB Responsible User" := User."User Name";
25 | end;
26 | }
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/8_RecurranceSchedule/OccurrenceMgt.Codeunit.al:
--------------------------------------------------------------------------------
1 | ///
2 | /// Shows how to get next occurrence for specific schedule
3 | ///
4 | ///
5 | codeunit 54107 "MNB Occurrence Mgt."
6 | {
7 | procedure OpenRecurrenceScheduleCard(var RecurrenceID: Guid): Text
8 | var
9 | RecurrenceSchedule: Codeunit "Recurrence Schedule";
10 | begin
11 | RecurrenceSchedule.OpenRecurrenceSchedule(RecurrenceID);
12 | exit(RecurrenceSchedule.RecurrenceDisplayText(RecurrenceID));
13 | end;
14 |
15 | procedure ShowNextDate(RecurrenceID: Guid; LastDate: DateTime)
16 | var
17 | RecurrenceSchedule: Codeunit "Recurrence Schedule";
18 | NextOccurrenceTxt: Label 'Next Occurrence: %1', Comment = '%1 - date-time';
19 | begin
20 | Message(NextOccurrenceTxt, RecurrenceSchedule.CalculateNextOccurrence(RecurrenceID, LastDate));
21 | end;
22 |
23 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/10_SharePoint/SharePointLists.Page.al:
--------------------------------------------------------------------------------
1 | page 54108 "MNB SharePoint Folders"
2 | {
3 | PageType = List;
4 | UsageCategory = None;
5 | SourceTable = "SharePoint Folder";
6 | SourceTableTemporary = true;
7 | Editable = false;
8 | ApplicationArea = All;
9 |
10 | layout
11 | {
12 | area(Content)
13 | {
14 | repeater(Folders)
15 | {
16 | field(Name; Rec.Name)
17 | {
18 | ApplicationArea = All;
19 | ToolTip = 'Specifies the value of the Title field.';
20 | }
21 | field("Item Count"; Rec."Item Count")
22 | {
23 | ToolTip = 'Specifies the value of the Item Count field.';
24 | }
25 | }
26 | }
27 |
28 | }
29 | procedure SetData(var SharePointFolder: Record "SharePoint Folder" temporary)
30 | begin
31 | Rec.Copy(SharePointFolder, true);
32 | end;
33 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/17_Base64/Base64.Cod.al:
--------------------------------------------------------------------------------
1 | codeunit 54112 "MNB Base64"
2 | {
3 | procedure DownloadFromBase64(var Base64Txt: Text)
4 | var
5 | Base64Convert: Codeunit "Base64 Convert";
6 | TempBlob: Codeunit "Temp Blob";
7 | FileName: Text;
8 | FileNameTxt: Label 'Picture.jpg';
9 | OutStream: OutStream;
10 | InStream: InStream;
11 | begin
12 | TempBlob.CreateOutStream(OutStream);
13 | Base64Convert.FromBase64(Base64Txt, OutStream);
14 | TempBlob.CreateInStream(InStream);
15 | FileName := FileNameTxt;
16 | DownloadFromStream(InStream, '', '', '', FileName);
17 | end;
18 |
19 | procedure PictureToBase64(): Text
20 | var
21 | Base64Convert: Codeunit "Base64 Convert";
22 | FileName, Base64Text : Text;
23 | InStream: InStream;
24 | begin
25 | UploadIntoStream('', '', '', FileName, InStream);
26 | Base64Text := Base64Convert.ToBase64(InStream);
27 | exit(Base64Text);
28 | end;
29 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/10_SharePoint/SharePointFiles.Page.al:
--------------------------------------------------------------------------------
1 | page 54109 "MNB SharePoint Files"
2 | {
3 | PageType = List;
4 | UsageCategory = None;
5 | SourceTable = "SharePoint File";
6 | SourceTableTemporary = true;
7 | Editable = false;
8 | ApplicationArea = All;
9 |
10 | layout
11 | {
12 | area(Content)
13 | {
14 | repeater(Folders)
15 | {
16 | field(Name; Rec.Name)
17 | {
18 | ApplicationArea = All;
19 | ToolTip = 'Specifies the value of the Title field.';
20 | }
21 | field("Server Relative Url"; Rec."Server Relative Url")
22 | {
23 | ToolTip = 'Specifies the value of the Server Relative Url field.';
24 | }
25 | }
26 |
27 | }
28 | }
29 |
30 | procedure SetData(var SharePointFile: Record "SharePoint File" temporary)
31 | begin
32 | Rec.Copy(SharePointFile, true);
33 | end;
34 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/1_AzureStorage/AzureStorageSetup.Table.al:
--------------------------------------------------------------------------------
1 | table 54100 "MNB Azure Storage Setup"
2 | {
3 | DataClassification = SystemMetadata;
4 | Caption = 'Azure Storage Setup';
5 |
6 | fields
7 | {
8 | field(1; "Primary Key"; Code[10])
9 | {
10 | Caption = 'Primary Key';
11 | DataClassification = SystemMetadata;
12 | }
13 | field(2; "Container Name"; Text[250])
14 | {
15 | Caption = 'Container Name';
16 | DataClassification = SystemMetadata;
17 | }
18 | field(3; "Azure Account Name"; Text[250])
19 | {
20 | Caption = 'Azure Account Name';
21 | DataClassification = SystemMetadata;
22 | }
23 | field(4; "Storage Key"; Text[1024])
24 | {
25 | Caption = 'Storage Key';
26 | DataClassification = SystemMetadata;
27 | ExtendedDatatype = Masked;
28 | }
29 |
30 | }
31 |
32 | keys
33 | {
34 | key(PK; "Primary Key")
35 | {
36 | Clustered = true;
37 | }
38 | }
39 |
40 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/13_AzureFunction/AzureFunctionMgt.Codeunit.al:
--------------------------------------------------------------------------------
1 | codeunit 54109 "MNB Azure Function Mgt"
2 | {
3 | internal procedure RunAzureFunction()
4 | var
5 | MNBAzureFunctionSetup: Record "MNB Azure Function Setup";
6 | AzureFunctionsAuthenticationIn: Interface "Azure Functions Authentication";
7 | AzureFunctionsAuthentication: Codeunit "Azure Functions Authentication";
8 | AzureFunctions: Codeunit "Azure Functions";
9 | AzureFunctionsResponse: Codeunit "Azure Functions Response";
10 | QueryDict: Dictionary of [Text, Text];
11 | ResponseTxt: Text;
12 | ResponseMsg: Label 'Response from Azure Function: %1', Comment = '%1 - response from azure function';
13 | begin
14 | MNBAzureFunctionSetup.Get();
15 | AzureFunctionsAuthenticationIn := AzureFunctionsAuthentication.CreateCodeAuth(MNBAzureFunctionSetup."Azure Function Url", '');
16 |
17 | AzureFunctionsResponse := AzureFunctions.SendGetRequest(AzureFunctionsAuthenticationIn, QueryDict);
18 | AzureFunctionsResponse.GetResultAsText(ResponseTxt);
19 |
20 | Message(ResponseMsg, ResponseTxt);
21 | end;
22 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/10_SharePoint/SharePointSetup.Table.al:
--------------------------------------------------------------------------------
1 | table 54102 "MNB SharePoint Setup"
2 | {
3 | DataClassification = SystemMetadata;
4 | Caption = 'SharePoint Setup';
5 |
6 | fields
7 | {
8 | field(1; "Primary Key"; Code[10])
9 | {
10 | Caption = 'Primary Key';
11 | DataClassification = SystemMetadata;
12 | }
13 | field(2; "Client Id"; Text[250])
14 | {
15 | Caption = 'Client Id';
16 | DataClassification = SystemMetadata;
17 | }
18 | field(3; "Client Secret"; Text[1024])
19 | {
20 | Caption = 'Client Secret';
21 | DataClassification = SystemMetadata;
22 | ExtendedDatatype = Masked;
23 | }
24 | field(4; "Base URL"; Text[250])
25 | {
26 | Caption = 'Base URL';
27 | DataClassification = SystemMetadata;
28 | }
29 | field(5; "Folder Name"; Text[250])
30 | {
31 | Caption = 'Folder Name';
32 | DataClassification = SystemMetadata;
33 | }
34 | }
35 |
36 | keys
37 | {
38 | key(PK; "Primary Key")
39 | {
40 | Clustered = true;
41 | }
42 | }
43 |
44 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/20_Email/Email.Cod.al:
--------------------------------------------------------------------------------
1 | codeunit 54114 "MNB Email"
2 | {
3 | ///
4 | /// The example how to open and send email from the customer card
5 | /// To test scenario you need to setup the email account in the system
6 | ///
7 | ///
8 |
9 | procedure OpenInEditor(var Customer: Record Customer)
10 | var
11 | Email: Codeunit Email;
12 | EmailMessage: Codeunit "Email Message";
13 | EmailSubjectTxt: Label 'Please Pay!';
14 | EmailBodyTxt: Label 'Hi, Please pay your invoices!';
15 | begin
16 | EmailMessage.Create(Customer."E-Mail", EmailSubjectTxt, EmailBodyTxt, true);
17 | Email.OpenInEditor(EmailMessage, Enum::"Email Scenario"::"MNB My Email Scenario");
18 | end;
19 |
20 | procedure SendEmail(var Customer: Record Customer)
21 | var
22 | Email: Codeunit Email;
23 | EmailMessage: Codeunit "Email Message";
24 | EmailSubjectTxt: Label 'Please Pay!';
25 | EmailBodyTxt: Label 'Hi, Please pay your invoices!';
26 | begin
27 | EmailMessage.Create(Customer."E-Mail", EmailSubjectTxt, EmailBodyTxt, true);
28 | Email.Send(EmailMessage, Enum::"Email Scenario"::"MNB My Email Scenario");
29 | end;
30 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/18_Zip/Zip.Cod.al:
--------------------------------------------------------------------------------
1 | codeunit 54113 "MNB Zip"
2 | {
3 |
4 | procedure ExportToZip()
5 | var
6 | MNBXMLExport: Codeunit "MNB XML Export";
7 | MyXMLTxt: BigText;
8 | begin
9 | MNBXMLExport.ExportCustomersToXML(MyXMLTxt);
10 | CreateZipFile(MyXMLTxt);
11 | end;
12 |
13 | local procedure CreateZipFile(var MyXMLTxt: BigText)
14 | var
15 | DataCompression: Codeunit "Data Compression";
16 | TempBlob: Codeunit "Temp Blob";
17 | FileName: Text;
18 | FileNameTxt: Label 'Customers.zip';
19 | InStream, InStreamZip : InStream;
20 | OutStream, OutStreamZip : OutStream;
21 | begin
22 | DataCompression.CreateZipArchive();
23 |
24 | //Add files
25 | TempBlob.CreateOutStream(OutStream);
26 | MyXMLTxt.Write(OutStream);
27 | TempBlob.CreateInStream(InStream);
28 | DataCompression.AddEntry(InStream, 'customers.xml');
29 | DataCompression.AddEntry(InStream, 'customers_2.xml');
30 |
31 | //Save zip file
32 | TempBlob.CreateOutStream(OutStreamZip);
33 | DataCompression.SaveZipArchive(OutStreamZip);
34 | TempBlob.CreateInStream(InStreamZip);
35 | FileName := FileNameTxt;
36 | DownloadFromStream(InStreamZip, '', '', '', FileName);
37 | end;
38 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/1_AzureStorage/ContainerContent.Page.al:
--------------------------------------------------------------------------------
1 | page 54103 "MNB Container Content"
2 | {
3 | Caption = 'MNB Container Content';
4 | PageType = List;
5 | SourceTable = "ABS Container Content";
6 | UsageCategory = None;
7 | Editable = false;
8 | InsertAllowed = false;
9 | DeleteAllowed = false;
10 | ModifyAllowed = false;
11 |
12 | layout
13 | {
14 | area(content)
15 | {
16 | repeater(General)
17 | {
18 | field(Name; Rec.Name)
19 | {
20 | ApplicationArea = All;
21 | ToolTip = 'Specifies the value of the Name field.';
22 | }
23 |
24 | field("Content Type"; Rec."Content Type")
25 | {
26 | ApplicationArea = All;
27 | ToolTip = 'Specifies the value of the Content-Type field.';
28 | }
29 | field("Creation Time"; Rec."Creation Time")
30 | {
31 | ApplicationArea = All;
32 | ToolTip = 'Specifies the value of the Creation Time field.';
33 | }
34 | field("Full Name"; Rec."Full Name")
35 | {
36 | ApplicationArea = All;
37 | ToolTip = 'Specifies the value of the Full Name field.';
38 | }
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/16_XMLWriter/CustomerList.PagExt.al:
--------------------------------------------------------------------------------
1 | pageextension 54104 "MNB Customer List" extends "Customer List"
2 | {
3 | actions
4 | {
5 | addlast("&Customer")
6 | {
7 | action(MNBExportToXML)
8 | {
9 | Caption = 'Export to XML';
10 | ToolTip = 'Export to XML';
11 | Image = Export;
12 | Promoted = true;
13 | PromotedCategory = Process;
14 | PromotedIsBig = true;
15 | PromotedOnly = true;
16 | ApplicationArea = All;
17 | trigger OnAction()
18 | var
19 | XMLExport: Codeunit "MNB XML Export";
20 | begin
21 | XMLExport.ExportToXML();
22 | end;
23 | }
24 | action(MNBExportToZip)
25 | {
26 | Caption = 'Export to Zip';
27 | ToolTip = 'Export to Zip';
28 | Image = Export;
29 | Promoted = true;
30 | PromotedCategory = Process;
31 | PromotedIsBig = true;
32 | PromotedOnly = true;
33 | ApplicationArea = All;
34 | trigger OnAction()
35 | var
36 | MNBZip: Codeunit "MNB Zip";
37 | begin
38 | MNBZip.ExportToZip();
39 | end;
40 | }
41 | }
42 | }
43 |
44 |
45 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/21_Blob/PersistentBlob.Cod.al:
--------------------------------------------------------------------------------
1 | codeunit 54115 "MNB Persistent Blob"
2 | {
3 | procedure SaveBlobToPersistedBlob(): BigInteger
4 | var
5 | PersistentBlob: Codeunit "Persistent Blob";
6 | BlobInteger: BigInteger;
7 | InStream: InStream;
8 | FileName: Text;
9 | begin
10 | UploadIntoStream('', '', '', FileName, InStream);
11 | BlobInteger := PersistentBlob.Create();
12 | PersistentBlob.CopyFromInStream(BlobInteger, InStream);
13 | exit(BlobInteger);
14 | end;
15 |
16 | procedure DownloadBlobFromPersistedBlob(BlobInteger: BigInteger)
17 | var
18 | PersistentBlob: Codeunit "Persistent Blob";
19 | TempBlob: Codeunit "Temp Blob";
20 | InStream: InStream;
21 | BlobDoesNotExistErr: Label 'Blob does not exist';
22 | FileNameTxt: Label 'ExportedFile.txt';
23 | OutStream: OutStream;
24 | FileName: Text;
25 |
26 | begin
27 | if not PersistentBlob.Exists(BlobInteger) then
28 | Error(BlobDoesNotExistErr);
29 | TempBlob.CreateOutStream(OutStream);
30 | PersistentBlob.CopyToOutStream(BlobInteger, OutStream);
31 | TempBlob.CreateInStream(InStream);
32 | FileName := FileNameTxt;
33 | DownloadFromStream(InStream, '', '', '', FileName);
34 | end;
35 |
36 | procedure DeleteBlobFromPersistedBlob(BlobInteger: BigInteger)
37 | var
38 | PersistentBlob: Codeunit "Persistent Blob";
39 | begin
40 | if PersistentBlob.Exists(BlobInteger) then
41 | PersistentBlob.Delete(BlobInteger);
42 | end;
43 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/7_ChoosingObjects/ObjectAndFields.Codeunit.al:
--------------------------------------------------------------------------------
1 | codeunit 54106 "MNB Object and Fields"
2 | {
3 | ///
4 | /// Shows how to choose objects
5 | ///
6 | ///
7 | procedure SelectObject(var Result: Record AllObjWithCaption): Boolean
8 | var
9 | AllObjects: Record AllObjWithCaption;
10 | Objects: Page Objects;
11 | begin
12 | AllObjects.FilterGroup(2);
13 | AllObjects.SetRange("Object Type", AllObjects."Object Type"::Table);
14 | AllObjects.FilterGroup(0);
15 |
16 | Objects.SetRecord(AllObjects);
17 | Objects.SetTableView(AllObjects);
18 | Objects.LookupMode := true;
19 |
20 | if Objects.RunModal = Action::LookupOK then begin
21 | Objects.GetRecord(Result);
22 | exit(true);
23 | end;
24 |
25 | exit(false);
26 | end;
27 |
28 | ///
29 | /// Shows how to choose fields
30 | ///
31 | ///
32 |
33 | procedure GetFieldCaption(var FieldCaption: Text)
34 | var
35 | SelectedField: Record Field;
36 | FieldSelection: Codeunit "Field Selection";
37 | begin
38 | SelectedField.FilterGroup(2);
39 | SelectedField.SetRange(TableNo, 27);
40 | SelectedField.FilterGroup(0);
41 | if FieldSelection.Open(SelectedField) then
42 | FieldCaption := SelectedField."Field Caption";
43 | end;
44 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/16_XMLWriter/XMLExport.Cod.al:
--------------------------------------------------------------------------------
1 | codeunit 54111 "MNB XML Export"
2 | {
3 | procedure ExportToXML()
4 | var
5 | MyXMLTxt: BigText;
6 | begin
7 | ExportCustomersToXML(MyXMLTxt);
8 | DownloadXML(MyXMLTxt);
9 | end;
10 |
11 | local procedure DownloadXML(var MyXMLTxt: BigText)
12 | var
13 | TempBlob: Codeunit "Temp Blob";
14 | FileName: Text;
15 | FileNameTxt: Label 'Customers.xml';
16 | OutStream: OutStream;
17 | InStream: InStream;
18 | begin
19 | TempBlob.CreateOutStream(OutStream);
20 | MyXMLTxt.Write(OutStream);
21 | TempBlob.CreateInStream(InStream);
22 | FileName := FileNameTxt;
23 | DownloadFromStream(InStream, '', '', '', FileName);
24 | end;
25 |
26 | procedure ExportCustomersToXML(var MyXMLTxt: BigText)
27 | var
28 | Customer: Record Customer;
29 | XmlWriter: Codeunit XmlWriter;
30 | begin
31 | XmlWriter.WriteStartDocument();
32 | XmlWriter.WriteStartElement('Customers');
33 | if Customer.FindSet() then
34 | repeat
35 | XmlWriter.WriteStartElement('Customer');
36 | XmlWriter.WriteAttributeString('number', Customer."No.");
37 | XmlWriter.WriteElementString('name', Customer.Name);
38 | XmlWriter.WriteElementString('address', Customer.Address);
39 | XmlWriter.WriteEndElement; // Customer
40 | until Customer.Next() = 0;
41 | XmlWriter.WriteEndElement; // Customers
42 | XmlWriter.WriteEndDocument();
43 | XmlWriter.ToBigText(MyXMLTxt);
44 | end;
45 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/13_AzureFunction/AzureFunctionSetup.Page.al:
--------------------------------------------------------------------------------
1 | page 54112 "MNB Azure Function Setup"
2 | {
3 | Caption = 'Azure Function Setup';
4 | PageType = Card;
5 | SourceTable = "MNB Azure Function Setup";
6 | ApplicationArea = All;
7 | UsageCategory = Administration;
8 | InsertAllowed = false;
9 | DeleteAllowed = false;
10 |
11 | layout
12 | {
13 | area(content)
14 | {
15 | group(General)
16 | {
17 | Caption = 'General';
18 | field("Azure Function Url"; Rec."Azure Function Url")
19 | {
20 | ApplicationArea = All;
21 | ToolTip = 'Specifies the value of the Azure Function URL field.';
22 | }
23 | }
24 | }
25 | }
26 | actions
27 | {
28 | area(Processing)
29 | {
30 | action(RunFunction)
31 | {
32 | Caption = 'Run Function';
33 | Image = Start;
34 | ToolTip = 'Run Azure Function';
35 | Promoted = true;
36 | PromotedCategory = Process;
37 | ApplicationArea = All;
38 | trigger OnAction()
39 | var
40 | MNBAzureFunctionMgt: Codeunit "MNB Azure Function Mgt";
41 | begin
42 | MNBAzureFunctionMgt.RunAzureFunction();
43 | end;
44 | }
45 | }
46 | }
47 |
48 | trigger OnOpenPage()
49 | begin
50 | if not Rec.Get() then begin
51 | Rec.Init();
52 | Rec.Insert();
53 | end;
54 | end;
55 | }
56 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/19_UserSelection/CustomerCard.PagExt.al:
--------------------------------------------------------------------------------
1 | pageextension 54105 "MNB Customer Card" extends "Customer Card"
2 | {
3 | layout
4 | {
5 | addlast(General)
6 | {
7 | field("MNB Responsible User"; Rec."MNB Responsible User")
8 | {
9 | ApplicationArea = All;
10 | ToolTip = 'Specifies the Responsible User for the customer.';
11 | }
12 | }
13 | }
14 | actions
15 | {
16 | addlast("&Customer")
17 | {
18 | action(MNBEmailEditor)
19 | {
20 | ApplicationArea = All;
21 | Caption = 'Email Editor';
22 | Image = Email;
23 | Promoted = true;
24 | PromotedCategory = Process;
25 | PromotedIsBig = true;
26 | ToolTip = 'Open email editor for the customer.';
27 |
28 | trigger OnAction()
29 | var
30 | MNBEmail: Codeunit "MNB Email";
31 | begin
32 | MNBEmail.OpenInEditor(Rec);
33 | end;
34 | }
35 | action(MNBEmailSend)
36 | {
37 | ApplicationArea = All;
38 | Caption = 'Email Send';
39 | Image = Email;
40 | Promoted = true;
41 | PromotedCategory = Process;
42 | PromotedIsBig = true;
43 | ToolTip = 'Send email for the customer.';
44 |
45 | trigger OnAction()
46 | var
47 | MNBEmail: Codeunit "MNB Email";
48 | begin
49 | MNBEmail.SendEmail(Rec);
50 | end;
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/5_FilterTokens/FilterTokens.Codeunit.al:
--------------------------------------------------------------------------------
1 | codeunit 54100 "MNB Filter Tokens"
2 | {
3 | ///
4 | /// The examples in this codeunit shows how to create Filter Tokens
5 | ///
6 | ///
7 |
8 | ///
9 | /// Add new Filter Tokens fro Date filters
10 | ///
11 | [EventSubscriber(ObjectType::Codeunit, Codeunit::"Filter Tokens", 'OnResolveDateFilterToken', '', true, true)]
12 | local procedure RunOnResolveDateFilterToken(DateToken: Text; var FromDate: Date; var ToDate: Date; var Handled: Boolean)
13 | begin
14 | AddNextLastWeekFilterToken(DateToken, FromDate, ToDate, Handled);
15 | end;
16 |
17 | ///
18 | /// Example for Filter Token Text
19 | ///
20 | local procedure FilterTokensTextExamples()
21 | begin
22 |
23 | end;
24 |
25 | local procedure AddNextLastWeekFilterToken(DateToken: Text; var FromDate: Date; var ToDate: Date; var Handled: Boolean)
26 | var
27 | LastWeekLbl: Label 'LASTWEEK', Comment = 'Must be uppercase';
28 | NextWeekLbl: Label 'NEXTWEEK', Comment = 'Must be uppercase';
29 | begin
30 | case DateToken.ToUpper() of
31 | NextWeekLbl:
32 | begin
33 | FromDate := Today;
34 | ToDate := CalcDate('<7D>', Today);
35 | Handled := true;
36 | end;
37 | LastWeekLbl:
38 | begin
39 | FromDate := CalcDate('<-7D>', Today);
40 | ToDate := Today;
41 | Handled := true;
42 | end;
43 | end;
44 | end;
45 |
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/.github/workflows/AddExistingAppOrTestApp.yaml:
--------------------------------------------------------------------------------
1 | name: 'Add existing app or test app'
2 |
3 | run-name: "Add existing app or test app in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | url:
13 | description: Direct Download Url of .app or .zip file
14 | required: true
15 | directCommit:
16 | description: Direct COMMIT (Y/N)
17 | required: false
18 | default: 'N'
19 |
20 | permissions:
21 | contents: write
22 | pull-requests: write
23 |
24 | defaults:
25 | run:
26 | shell: powershell
27 |
28 | env:
29 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
30 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
31 |
32 | jobs:
33 | AddExistingAppOrTestApp:
34 | runs-on: [ windows-latest ]
35 | steps:
36 | - name: Checkout
37 | uses: actions/checkout@v3
38 |
39 | - name: Initialize the workflow
40 | id: init
41 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
42 | with:
43 | shell: powershell
44 | eventId: "DO0090"
45 |
46 | - name: Add existing app
47 | uses: microsoft/AL-Go-Actions/AddExistingApp@v3.1
48 | with:
49 | shell: powershell
50 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
51 | project: ${{ github.event.inputs.project }}
52 | url: ${{ github.event.inputs.url }}
53 | directCommit: ${{ github.event.inputs.directCommit }}
54 |
55 | - name: Finalize the workflow
56 | if: always()
57 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
58 | with:
59 | shell: powershell
60 | eventId: "DO0090"
61 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
62 |
--------------------------------------------------------------------------------
/SystemAppPresentation/SystemAppExamples.permissionset.al:
--------------------------------------------------------------------------------
1 | permissionset 54100 SystemAppExamples
2 | {
3 | Assignable = true;
4 | Permissions = tabledata "MNB Azure Function Setup"=RIMD,
5 | tabledata "MNB Azure Storage Setup"=RIMD,
6 | tabledata "MNB My Log Table"=RIMD,
7 | tabledata "MNB SharePoint Setup"=RIMD,
8 | table "MNB Azure Function Setup"=X,
9 | table "MNB Azure Storage Setup"=X,
10 | table "MNB My Log Table"=X,
11 | table "MNB SharePoint Setup"=X,
12 | report "MNB Item Barcode"=X,
13 | codeunit "MNB Asset Location Mgt"=X,
14 | codeunit "MNB Azure Blob Storage"=X,
15 | codeunit "MNB Azure Function Mgt"=X,
16 | codeunit "MNB Base64"=X,
17 | codeunit "MNB Camera and Image"=X,
18 | codeunit "MNB Checklist Install"=X,
19 | codeunit "MNB Email"=X,
20 | codeunit "MNB Environment Info"=X,
21 | codeunit "MNB Filter Tokens"=X,
22 | codeunit "MNB Object and Fields"=X,
23 | codeunit "MNB Occurrence Mgt."=X,
24 | codeunit "MNB Persistent Blob"=X,
25 | codeunit "MNB Reten. Pol. Install"=X,
26 | codeunit "MNB SharePoint Mgt."=X,
27 | codeunit "MNB XML Export"=X,
28 | codeunit "MNB Zip"=X,
29 | page "MNB Azure Containers"=X,
30 | page "MNB Azure Function Setup"=X,
31 | page "MNB Azure Storage Setup"=X,
32 | page "MNB Base64"=X,
33 | page "MNB Choosing Objects"=X,
34 | page "MNB Container Content"=X,
35 | page "MNB Math Operations"=X,
36 | page "MNB Next Occurrence"=X,
37 | page "MNB Password Operations"=X,
38 | page "MNB Persistent Blob"=X,
39 | page "MNB Purchase Order Picture"=X,
40 | page "MNB RegEx Examples"=X,
41 | page "MNB SharePoint Files"=X,
42 | page "MNB SharePoint Folders"=X,
43 | page "MNB SharePoint Setup"=X;
44 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/12_RegEx/RegExExamples.Page.al:
--------------------------------------------------------------------------------
1 | page 54111 "MNB RegEx Examples"
2 | {
3 | Caption = 'RegEx Examples';
4 | PageType = Card;
5 | ApplicationArea = All;
6 | UsageCategory = Tasks;
7 |
8 | layout
9 | {
10 | area(Content)
11 | {
12 | group(Examples)
13 | {
14 | field(RegExTxtField; RegExTxt)
15 | {
16 | ApplicationArea = All;
17 | Caption = 'Regular Expression';
18 | ToolTip = 'Specifies the value of the Regular Expression field.';
19 | }
20 | field(TextToLookField; TextToLook)
21 | {
22 | Caption = 'Text';
23 | MultiLine = true;
24 | ToolTip = 'Specifies the value of the Text field.';
25 | }
26 | }
27 | }
28 | }
29 |
30 | actions
31 | {
32 | area(Processing)
33 | {
34 | action(Find)
35 | {
36 | ApplicationArea = All;
37 | Caption = 'Find in Text';
38 | ToolTip = 'Executes the ActionName action.';
39 | trigger OnAction()
40 | var
41 | Regex: Codeunit Regex;
42 | NoMatchErr: Label 'No Match for %1', Comment = '%1 - regular expression';
43 | MatchMsg: Label 'There is a match for %1', Comment = '%1 - regular expression';
44 | begin
45 | if not Regex.IsMatch(TextToLook, RegExTxt) then
46 | Error(NoMatchErr, RegExTxt)
47 | else
48 | Message(MatchMsg, RegExTxt);
49 | end;
50 | }
51 | }
52 | }
53 |
54 | var
55 | RegExTxt: Text;
56 | TextToLook: Text;
57 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/7_ChoosingObjects/ChoosingObjects.Pag.al:
--------------------------------------------------------------------------------
1 | page 54105 "MNB Choosing Objects"
2 | {
3 | PageType = Card;
4 | ApplicationArea = All;
5 | UsageCategory = Administration;
6 | Caption = 'MNB Choosing Objects';
7 |
8 | layout
9 | {
10 | area(Content)
11 | {
12 | group(GroupName)
13 | {
14 | Caption = 'Object';
15 | field(ObjectNameField; ObjectName)
16 | {
17 | ApplicationArea = All;
18 | ToolTip = 'Specifies the object name. Choose the object from assist edit.';
19 | Editable = false;
20 | Caption = 'Object Name';
21 | trigger OnAssistEdit()
22 | var
23 | AllObjWithCaption: Record AllObjWithCaption;
24 | ObjectAndFields: Codeunit "MNB Object and Fields";
25 | begin
26 | if ObjectAndFields.SelectObject(AllObjWithCaption) then
27 | ObjectName := AllObjWithCaption."Object Name";
28 | end;
29 | }
30 | field(FieldNameField; FieldName)
31 | {
32 | ApplicationArea = All;
33 | ToolTip = 'Specifies the field name. Choose the field from table Items from assist edit.';
34 | Editable = false;
35 | Caption = 'Field Name';
36 | trigger OnAssistEdit()
37 | var
38 | ObjectAndFields: Codeunit "MNB Object and Fields";
39 | begin
40 | ObjectAndFields.GetFieldCaption(FieldName);
41 | end;
42 | }
43 | }
44 | }
45 | }
46 |
47 | var
48 | FieldName, ObjectName : Text;
49 |
50 |
51 | }
--------------------------------------------------------------------------------
/.github/workflows/IncrementVersionNumber.yaml:
--------------------------------------------------------------------------------
1 | name: ' Increment Version Number'
2 |
3 | run-name: "Increment Version Number in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects (* for all projects)
10 | required: false
11 | default: '*'
12 | versionNumber:
13 | description: Updated Version Number. Use Major.Minor for absolute change, use +Major.Minor for incremental change.
14 | required: true
15 | directCommit:
16 | description: Direct COMMIT (Y/N)
17 | required: false
18 | default: 'N'
19 |
20 | permissions:
21 | contents: write
22 | pull-requests: write
23 |
24 | defaults:
25 | run:
26 | shell: powershell
27 |
28 | env:
29 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
30 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
31 |
32 | jobs:
33 | IncrementVersionNumber:
34 | runs-on: [ windows-latest ]
35 | steps:
36 | - name: Checkout
37 | uses: actions/checkout@v3
38 |
39 | - name: Initialize the workflow
40 | id: init
41 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
42 | with:
43 | shell: powershell
44 | eventId: "DO0096"
45 |
46 | - name: Increment Version Number
47 | uses: microsoft/AL-Go-Actions/IncrementVersionNumber@v3.1
48 | with:
49 | shell: powershell
50 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
51 | project: ${{ github.event.inputs.project }}
52 | versionNumber: ${{ github.event.inputs.versionNumber }}
53 | directCommit: ${{ github.event.inputs.directCommit }}
54 |
55 | - name: Finalize the workflow
56 | if: always()
57 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
58 | with:
59 | shell: powershell
60 | eventId: "DO0096"
61 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
62 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/2_ImageCamera/PurchaseOrder.PagExt.al:
--------------------------------------------------------------------------------
1 | pageextension 54101 "MNB Purchase Order" extends "Purchase Order"
2 | {
3 | layout
4 | {
5 | addfirst(factboxes)
6 | {
7 | part(MNBPicture; "MNB Purchase Order Picture")
8 | {
9 | ApplicationArea = All;
10 | Caption = 'Picture';
11 | SubPageLink = "No." = field("No.");
12 | }
13 | }
14 | }
15 | actions
16 | {
17 | addlast(IncomingDocument)
18 | {
19 | action(MNBTakeInvoicePhoto)
20 | {
21 | Caption = 'Take Invoice Photo';
22 | ToolTip = 'Allows to take the invoice photo before invoice';
23 | Promoted = true;
24 | PromotedCategory = Process;
25 | PromotedIsBig = true;
26 | Image = Camera;
27 | ApplicationArea = All;
28 | Enabled = CameraIsAvailable;
29 | trigger OnAction()
30 | var
31 | MNBCameraImage: Codeunit "MNB Camera and Image";
32 | begin
33 | MNBCameraImage.TakePurchaseInvoicePhoto(Rec);
34 | end;
35 | }
36 | action(MNBTakeInvoicePhotoSize)
37 | {
38 | Caption = 'Invoice Photo (Size)';
39 | ToolTip = 'Allows to check size of the photo.';
40 | Promoted = true;
41 | PromotedCategory = Process;
42 | PromotedIsBig = true;
43 | Image = Camera;
44 | ApplicationArea = All;
45 | Enabled = CameraIsAvailable;
46 | trigger OnAction()
47 | var
48 | MNBCameraImage: Codeunit "MNB Camera and Image";
49 | begin
50 | MNBCameraImage.GetPictureSizeAndTransform(Rec);
51 | end;
52 | }
53 |
54 | }
55 | }
56 | trigger OnOpenPage()
57 | var
58 | Camera: Codeunit Camera;
59 | begin
60 | CameraIsAvailable := Camera.IsAvailable();
61 | end;
62 |
63 | trigger OnAfterGetCurrRecord()
64 | begin
65 |
66 | end;
67 |
68 | var
69 | CameraIsAvailable: Boolean;
70 | }
71 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/1_AzureStorage/AzureBlobStorage.Codeunit.al:
--------------------------------------------------------------------------------
1 | codeunit 54103 "MNB Azure Blob Storage"
2 | {
3 | ///
4 | /// The examples in this codeunit shows how to use Azure Blob Storage to get containers and files
5 | ///
6 | ///
7 |
8 | ///
9 | /// List all containers in the Azure Storage
10 | ///
11 | procedure ListContainers(): Text
12 | var
13 | ABSContainer: Record "ABS Container";
14 | ABSContainerClient: Codeunit "ABS Container Client";
15 | StorageServiceAuthorization: Codeunit "Storage Service Authorization";
16 | Authorization: Interface "Storage Service Authorization";
17 | begin
18 | MNBAzureStorageSetup.Get();
19 | Authorization := StorageServiceAuthorization.CreateSharedKey(MNBAzureStorageSetup."Storage Key");
20 |
21 | ABSContainerClient.Initialize(MNBAzureStorageSetup."Azure Account Name", Authorization);
22 | ABSContainerClient.ListContainers(ABSContainer);
23 | if Page.RunModal(Page::"MNB Azure Containers", ABSContainer) = Action::LookupOK then
24 | exit(ABSContainer.Name);
25 | end;
26 |
27 | ///
28 | /// List all files in the storage and download one
29 | ///
30 | procedure DownloadContainerFile(): Text
31 | var
32 | ABSContainerContent: Record "ABS Container Content";
33 | ABSBlobClient: Codeunit "ABS Blob Client";
34 | StorageServiceAuthorization: Codeunit "Storage Service Authorization";
35 | Authorization: Interface "Storage Service Authorization";
36 | begin
37 | MNBAzureStorageSetup.Get();
38 | Authorization := StorageServiceAuthorization.CreateSharedKey(MNBAzureStorageSetup."Storage Key");
39 | ABSBlobClient.Initialize(MNBAzureStorageSetup."Azure Account Name", MNBAzureStorageSetup."Container Name", Authorization);
40 | ABSBlobClient.ListBlobs(ABSContainerContent);
41 | if Page.RunModal(Page::"MNB Container Content", ABSContainerContent) = Action::LookupOK then
42 | ABSBlobClient.GetBlobAsFile(ABSContainerContent."Name")
43 | end;
44 |
45 | var
46 | MNBAzureStorageSetup: Record "MNB Azure Storage Setup";
47 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/17_Base64/Base64.Pag.al:
--------------------------------------------------------------------------------
1 | page 54113 "MNB Base64"
2 | {
3 | Caption = 'Base64 Example';
4 | PageType = Card;
5 | ApplicationArea = All;
6 | UsageCategory = Administration;
7 |
8 | layout
9 | {
10 | area(Content)
11 | {
12 | group(Example)
13 | {
14 | Caption = 'Example';
15 | field(Base64TextField; FromBase64Text)
16 | {
17 | ApplicationArea = All;
18 | Caption = 'Base64 Text';
19 | MultiLine = true;
20 | ToolTip = 'Base64 Text value.';
21 | }
22 | field(ImportedBase64TextField; ToBase64Text)
23 | {
24 | ApplicationArea = All;
25 | Caption = 'Imported Base64 Text';
26 | MultiLine = true;
27 | ToolTip = 'Imported Base64 Text value.';
28 | }
29 |
30 | }
31 | }
32 | }
33 |
34 | actions
35 | {
36 | area(Processing)
37 | {
38 | action(GetPicture)
39 | {
40 | ApplicationArea = All;
41 | Caption = 'Get Picture';
42 | ToolTip = 'Get Picture from Base64 Text';
43 | Promoted = true;
44 | PromotedCategory = Process;
45 | PromotedOnly = true;
46 | trigger OnAction()
47 | var
48 | MNBBase64: Codeunit "MNB Base64";
49 | begin
50 | MNBBase64.DownloadFromBase64(FromBase64Text);
51 | end;
52 | }
53 | action(UploadPicture)
54 | {
55 | ApplicationArea = All;
56 | Caption = 'Upload Picture';
57 | ToolTip = 'Upload Picture to Base64 Text';
58 | Promoted = true;
59 | PromotedCategory = Process;
60 | PromotedOnly = true;
61 | trigger OnAction()
62 | var
63 | MNBBase64: Codeunit "MNB Base64";
64 | begin
65 | ToBase64Text := MNBBase64.PictureToBase64();
66 | end;
67 | }
68 | }
69 | }
70 | var
71 | FromBase64Text, ToBase64Text : Text;
72 | }
--------------------------------------------------------------------------------
/.github/workflows/CreateTestApp.yaml:
--------------------------------------------------------------------------------
1 | name: 'Create a new test app'
2 |
3 | run-name: "Create a new test app in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | name:
13 | description: Name
14 | required: true
15 | default: '.Test'
16 | publisher:
17 | description: Publisher
18 | required: true
19 | idrange:
20 | description: ID range
21 | required: true
22 | default: '50000..99999'
23 | sampleCode:
24 | description: Include Sample code (Y/N)
25 | required: false
26 | default: 'Y'
27 | directCommit:
28 | description: Direct COMMIT (Y/N)
29 | required: false
30 | default: 'N'
31 |
32 | permissions:
33 | contents: write
34 | pull-requests: write
35 |
36 | defaults:
37 | run:
38 | shell: powershell
39 |
40 | env:
41 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
42 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
43 |
44 | jobs:
45 | CreateTestApp:
46 | runs-on: [ windows-latest ]
47 | steps:
48 | - name: Checkout
49 | uses: actions/checkout@v3
50 |
51 | - name: Initialize the workflow
52 | id: init
53 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
54 | with:
55 | shell: powershell
56 | eventId: "DO0095"
57 |
58 | - name: Creating a new test app
59 | uses: microsoft/AL-Go-Actions/CreateApp@v3.1
60 | with:
61 | shell: powershell
62 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
63 | project: ${{ github.event.inputs.project }}
64 | type: 'Test App'
65 | name: ${{ github.event.inputs.name }}
66 | publisher: ${{ github.event.inputs.publisher }}
67 | idrange: ${{ github.event.inputs.idrange }}
68 | sampleCode: ${{ github.event.inputs.sampleCode }}
69 | directCommit: ${{ github.event.inputs.directCommit }}
70 |
71 | - name: Finalize the workflow
72 | if: always()
73 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
74 | with:
75 | shell: powershell
76 | eventId: "DO0095"
77 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
78 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/8_RecurranceSchedule/NextOccurrence.Pag.al:
--------------------------------------------------------------------------------
1 | page 54106 "MNB Next Occurrence"
2 | {
3 | PageType = Card;
4 | ApplicationArea = All;
5 | UsageCategory = Administration;
6 | Caption = 'Next Occurrence';
7 |
8 | layout
9 | {
10 | area(Content)
11 | {
12 | group(GroupName)
13 | {
14 | Caption = 'Next Occurrence';
15 | field(Name; RecurrenceID)
16 | {
17 | ApplicationArea = All;
18 | ToolTip = 'Specifies set recurrence.Use Assist Edit to set it.';
19 | Editable = false;
20 | trigger OnAssistEdit()
21 | var
22 | OccurrenceMgt: Codeunit "MNB Occurrence Mgt.";
23 | begin
24 | RecurrenceText := OccurrenceMgt.OpenRecurrenceScheduleCard(RecurrenceID);
25 | end;
26 | }
27 | field(RecurrenceTextField; RecurrenceText)
28 | {
29 | Caption = 'Recurrence Description';
30 | ToolTip = 'Specifies the description for the recurrence schedule.';
31 | Editable = false;
32 | }
33 |
34 | field(LastOccurrenceDateField; LastOccurrenceDateTime)
35 | {
36 | Caption = 'Last Occurrence Date-Time';
37 | ToolTip = 'Specifies the last occurrence of the schedule.';
38 | }
39 | }
40 | }
41 | }
42 | actions
43 | {
44 | area(Processing)
45 | {
46 | action(GetNextDate)
47 | {
48 | Caption = 'Get Next Date-Time';
49 | Image = NextRecord;
50 | ToolTip = 'Get next occurrence of the schedule.';
51 | Promoted = true;
52 | PromotedCategory = Process;
53 | trigger OnAction()
54 | var
55 | OccurrenceMgt: Codeunit "MNB Occurrence Mgt.";
56 | begin
57 | OccurrenceMgt.ShowNextDate(RecurrenceID, LastOccurrenceDateTime);
58 | end;
59 | }
60 | }
61 | }
62 |
63 |
64 |
65 | var
66 | LastOccurrenceDateTime: DateTime;
67 | RecurrenceID: Guid;
68 | RecurrenceText: Text;
69 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/21_Blob/PersistentBlob.Pag.al:
--------------------------------------------------------------------------------
1 | page 54114 "MNB Persistent Blob"
2 | {
3 | Caption = 'MNB Persistent Blob';
4 | PageType = Card;
5 | ApplicationArea = All;
6 | UsageCategory = Administration;
7 |
8 | layout
9 | {
10 | area(Content)
11 | {
12 | group(GroupName)
13 | {
14 | Caption = 'Persistent Blob';
15 | field(BlobIntegerField; BlobInteger)
16 | {
17 | ApplicationArea = All;
18 | Caption = 'Blob Integer Field';
19 | }
20 | }
21 | }
22 | }
23 |
24 | actions
25 | {
26 | area(Processing)
27 | {
28 | action(ImportBlob)
29 | {
30 | ApplicationArea = All;
31 | Caption = 'Import Blob';
32 | Promoted = true;
33 | PromotedCategory = Process;
34 | Image = Import;
35 | trigger OnAction()
36 | var
37 | MNBPersistentBlob: Codeunit "MNB Persistent Blob";
38 | begin
39 | BlobInteger := MNBPersistentBlob.SaveBlobToPersistedBlob()
40 | end;
41 | }
42 | action(ExportBlob)
43 | {
44 | ApplicationArea = All;
45 | Caption = 'Export Blob';
46 | Promoted = true;
47 | PromotedCategory = Process;
48 | Image = Export;
49 | trigger OnAction()
50 | var
51 | MNBPersistentBlob: Codeunit "MNB Persistent Blob";
52 | begin
53 | MNBPersistentBlob.DownloadBlobFromPersistedBlob(BlobInteger);
54 | end;
55 | }
56 |
57 | action(DeleteBlob)
58 | {
59 | ApplicationArea = All;
60 | Caption = 'Delete Blob';
61 | Promoted = true;
62 | PromotedCategory = Process;
63 | Image = Delete;
64 | trigger OnAction()
65 | var
66 | MNBPersistentBlob: Codeunit "MNB Persistent Blob";
67 | begin
68 | MNBPersistentBlob.DeleteBlobFromPersistedBlob(BlobInteger);
69 | end;
70 | }
71 |
72 | }
73 | }
74 | var
75 | BlobInteger: BigInteger;
76 | }
--------------------------------------------------------------------------------
/.github/workflows/CreateApp.yaml:
--------------------------------------------------------------------------------
1 | name: 'Create a new app'
2 |
3 | run-name: "Create a new app in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | name:
13 | description: Name
14 | required: true
15 | publisher:
16 | description: Publisher
17 | required: true
18 | idrange:
19 | description: ID range (from..to)
20 | required: true
21 | sampleCode:
22 | description: Include Sample code (Y/N)
23 | required: false
24 | default: 'Y'
25 | directCommit:
26 | description: Direct COMMIT (Y/N)
27 | required: false
28 | default: "N"
29 |
30 | permissions:
31 | contents: write
32 | pull-requests: write
33 |
34 | defaults:
35 | run:
36 | shell: powershell
37 |
38 | env:
39 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
40 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
41 |
42 | jobs:
43 | CreateApp:
44 | runs-on: [ windows-latest ]
45 | steps:
46 | - name: Checkout
47 | uses: actions/checkout@v3
48 |
49 | - name: Initialize the workflow
50 | id: init
51 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
52 | with:
53 | shell: powershell
54 | eventId: "DO0092"
55 |
56 | - name: Read settings
57 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
58 | with:
59 | shell: powershell
60 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
61 | get: type
62 |
63 | - name: Creating a new app
64 | uses: microsoft/AL-Go-Actions/CreateApp@v3.1
65 | with:
66 | shell: powershell
67 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
68 | project: ${{ github.event.inputs.project }}
69 | type: ${{ env.type }}
70 | name: ${{ github.event.inputs.name }}
71 | publisher: ${{ github.event.inputs.publisher }}
72 | idrange: ${{ github.event.inputs.idrange }}
73 | sampleCode: ${{ github.event.inputs.sampleCode }}
74 | directCommit: ${{ github.event.inputs.directCommit }}
75 |
76 | - name: Finalize the workflow
77 | if: always()
78 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
79 | with:
80 | shell: powershell
81 | eventId: "DO0092"
82 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
83 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/4_Barcode/ItemBarcode.Report.al:
--------------------------------------------------------------------------------
1 | report 54100 "MNB Item Barcode"
2 | {
3 | ///
4 | /// Shows how to create barcodes for item
5 | ///
6 | ///
7 | ///
8 | ///
9 |
10 | ApplicationArea = All;
11 | Caption = 'Item Barcode';
12 | UsageCategory = ReportsAndAnalysis;
13 | WordLayout = './src/Examples/4_Barcode/ItemBarcode.docx';
14 | DefaultLayout = Word;
15 | dataset
16 | {
17 | dataitem(Item; Item)
18 | {
19 | RequestFilterFields = "No.";
20 | column(No; "No.")
21 | {
22 | }
23 | column(Description; Description)
24 | {
25 | }
26 | column(Ean13Txt; Ean13Txt) { }
27 | column(Code39Txt; Code39Txt) { }
28 | column(Code128Txt; Code128Txt) { }
29 | column(QRTxt; QRTxt) { }
30 | column(AztecTxt; AztecTxt) { }
31 |
32 | trigger OnAfterGetRecord()
33 | var
34 | BarcodeFontProvider: Interface "Barcode Font Provider";
35 | BarcodeFontProvider2D: Interface "Barcode Font Provider 2D";
36 | begin
37 | BarCodeTxt := '978020137962';
38 | BarcodeFontProvider := Enum::"Barcode Font Provider"::IDAutomation1D;
39 | Ean13Txt := BarcodeFontProvider.EncodeFont(BarCodeTxt, Enum::"Barcode Symbology"::"EAN-13");
40 | Code39Txt := BarcodeFontProvider.EncodeFont(Item."No.", Enum::"Barcode Symbology"::Code39);
41 | Code128Txt := BarcodeFontProvider.EncodeFont(StrSubstNo('%1 %2', Item."No.", Item.Description), Enum::"Barcode Symbology"::Code128);
42 |
43 | BarcodeFontProvider2D := Enum::"Barcode Font Provider 2D"::IDAutomation2D;
44 | BarCodeTxt := 'URL:https://www.mynavblog.com';
45 | QRTxt := BarcodeFontProvider2D.EncodeFont(BarCodeTxt, Enum::"Barcode Symbology 2D"::"QR-Code");
46 | AztecTxt := BarcodeFontProvider2D.EncodeFont(BarCodeTxt, Enum::"Barcode Symbology 2D"::Aztec);
47 | end;
48 | }
49 | }
50 | var
51 | AztecTxt, Code39Txt, Code128Txt, Ean13Txt, QRTxt : Text;
52 | BarCodeTxt: Text;
53 | }
54 |
--------------------------------------------------------------------------------
/.github/workflows/CreatePerformanceTestApp.yaml:
--------------------------------------------------------------------------------
1 | name: 'Create a new performance test app'
2 |
3 | run-name: "Create a new performance test app in [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | project:
9 | description: Project name if the repository is setup for multiple projects
10 | required: false
11 | default: '.'
12 | name:
13 | description: Name
14 | required: true
15 | default: '.PerformanceTest'
16 | publisher:
17 | description: Publisher
18 | required: true
19 | idrange:
20 | description: ID range
21 | required: true
22 | default: '50000..99999'
23 | sampleCode:
24 | description: Include Sample code (Y/N)
25 | required: false
26 | default: 'Y'
27 | sampleSuite:
28 | description: Include Sample BCPT Suite (Y/N)
29 | required: false
30 | default: 'Y'
31 | directCommit:
32 | description: Direct COMMIT (Y/N)
33 | required: false
34 | default: 'N'
35 |
36 | permissions:
37 | contents: write
38 | pull-requests: write
39 |
40 | defaults:
41 | run:
42 | shell: powershell
43 |
44 | env:
45 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
46 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
47 |
48 | jobs:
49 | CreatePerformanceTestApp:
50 | runs-on: [ windows-latest ]
51 | steps:
52 | - name: Checkout
53 | uses: actions/checkout@v3
54 |
55 | - name: Initialize the workflow
56 | id: init
57 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
58 | with:
59 | shell: powershell
60 | eventId: "DO0102"
61 |
62 | - name: Creating a new test app
63 | uses: microsoft/AL-Go-Actions/CreateApp@v3.1
64 | with:
65 | shell: powershell
66 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
67 | project: ${{ github.event.inputs.project }}
68 | type: 'Performance Test App'
69 | name: ${{ github.event.inputs.name }}
70 | publisher: ${{ github.event.inputs.publisher }}
71 | idrange: ${{ github.event.inputs.idrange }}
72 | sampleCode: ${{ github.event.inputs.sampleCode }}
73 | sampleSuite: ${{ github.event.inputs.sampleSuite }}
74 | directCommit: ${{ github.event.inputs.directCommit }}
75 |
76 | - name: Finalize the workflow
77 | if: always()
78 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
79 | with:
80 | shell: powershell
81 | eventId: "DO0102"
82 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
83 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/11_Password/PasswordOperations.Page.al:
--------------------------------------------------------------------------------
1 | page 54110 "MNB Password Operations"
2 | {
3 | ///
4 | /// Shows example of math module
5 | ///
6 | ///
7 |
8 | Caption = 'Password Operations';
9 | PageType = Card;
10 | ApplicationArea = All;
11 | UsageCategory = Tasks;
12 |
13 | layout
14 | {
15 | area(content)
16 | {
17 | group(General)
18 | {
19 | Caption = 'General';
20 | field(PasswordText; Password)
21 | {
22 | Editable = false;
23 | Caption = 'My Password';
24 | }
25 | }
26 | }
27 | }
28 | actions
29 | {
30 | area(Processing)
31 | {
32 |
33 | action(TypePassword)
34 | {
35 | Caption = 'Type Password';
36 | ToolTip = 'Type password for the user.';
37 | Image = Absence;
38 | Promoted = true;
39 | PromotedCategory = Process;
40 | PromotedOnly = true;
41 | ApplicationArea = All;
42 | trigger OnAction()
43 | var
44 | PasswordDialogManagement: Codeunit "Password Dialog Management";
45 | PasswordToCheck: Text;
46 | MyPasswordTxt: Label 'My Top Secret Password does not match.';
47 | begin
48 | PasswordToCheck := PasswordDialogManagement.OpenPasswordDialog();
49 | if PasswordToCheck <> Password then
50 | Error(MyPasswordTxt, Password);
51 | end;
52 | }
53 | action(ChangePassword)
54 | {
55 | Caption = 'Change Password';
56 | ToolTip = 'Set password for the user.';
57 | Image = Absence;
58 | Promoted = true;
59 | PromotedCategory = Process;
60 | PromotedOnly = true;
61 | ApplicationArea = All;
62 | trigger OnAction()
63 | var
64 | NewPassword: Text;
65 | PasswordDialogManagement: Codeunit "Password Dialog Management";
66 | begin
67 | PasswordDialogManagement.OpenChangePasswordDialog(Password, NewPassword);
68 | Password := NewPassword;
69 | end;
70 | }
71 | }
72 | }
73 | var
74 | Password: Text;
75 | }
76 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | ## Security
4 |
5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/).
6 |
7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://docs.microsoft.com/en-us/previous-versions/tn-archive/cc751383(v=technet.10)), please report it to us as described below.
8 |
9 | ## Reporting Security Issues
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues.**
12 |
13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://msrc.microsoft.com/create-report).
14 |
15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://www.microsoft.com/en-us/msrc/pgp-key-msrc).
16 |
17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc).
18 |
19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue:
20 |
21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.)
22 | * Full paths of source file(s) related to the manifestation of the issue
23 | * The location of the affected source code (tag/branch/commit or direct URL)
24 | * Any special configuration required to reproduce the issue
25 | * Step-by-step instructions to reproduce the issue
26 | * Proof-of-concept or exploit code (if possible)
27 | * Impact of the issue, including how an attacker might exploit the issue
28 |
29 | This information will help us triage your report more quickly.
30 |
31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://microsoft.com/msrc/bounty) page for more details about our active programs.
32 |
33 | ## Preferred Languages
34 |
35 | We prefer all communications to be in English.
36 |
37 | ## Policy
38 |
39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://www.microsoft.com/en-us/msrc/cvd).
40 |
41 |
42 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/1_AzureStorage/AzureStorageSetup.Page.al:
--------------------------------------------------------------------------------
1 | page 54102 "MNB Azure Storage Setup"
2 | {
3 | Caption = 'MNB Azure Storage Setup';
4 | PageType = Card;
5 | SourceTable = "MNB Azure Storage Setup";
6 | ApplicationArea = All;
7 | UsageCategory = Administration;
8 | InsertAllowed = false;
9 | DeleteAllowed = false;
10 |
11 | layout
12 | {
13 | area(content)
14 | {
15 | group(General)
16 | {
17 | Caption = 'General';
18 | field("Container Name"; Rec."Container Name")
19 | {
20 | ApplicationArea = All;
21 | ToolTip = 'Specifies the value of the Container Name field.';
22 | Editable = false;
23 | }
24 | field("Azure Account Name"; Rec."Azure Account Name")
25 | {
26 | ToolTip = 'Specifies the value of the Azure Account Name field.';
27 | ApplicationArea = All;
28 | Importance = Additional;
29 | }
30 | field("Storage Key"; Rec."Storage Key")
31 | {
32 | ToolTip = 'Specifies the value of the Storage Key field.';
33 | ApplicationArea = All;
34 | Importance = Additional;
35 | }
36 | }
37 | }
38 | }
39 | actions
40 | {
41 | area(Processing)
42 | {
43 | action(ListContainers)
44 | {
45 | Caption = 'List Containers';
46 | Image = ShowList;
47 | ToolTip = 'Show list of containers';
48 | Promoted = true;
49 | PromotedCategory = Process;
50 | ApplicationArea = All;
51 | trigger OnAction()
52 | var
53 | MNBAzureBlobStorage: Codeunit "MNB Azure Blob Storage";
54 | begin
55 | Rec."Container Name" := MNBAzureBlobStorage.ListContainers();
56 | end;
57 | }
58 | action(ListFiles)
59 | {
60 | Caption = 'List Files';
61 | Image = FileContract;
62 | ToolTip = 'Show list of files in the container.';
63 | Promoted = true;
64 | PromotedCategory = Process;
65 | ApplicationArea = All;
66 | trigger OnAction()
67 | var
68 | MNBAzureBlobStorage: Codeunit "MNB Azure Blob Storage";
69 | begin
70 | MNBAzureBlobStorage.DownloadContainerFile();
71 | end;
72 | }
73 | }
74 | }
75 |
76 | trigger OnOpenPage()
77 | begin
78 | if not Rec.Get() then begin
79 | Rec.Init();
80 | Rec.Insert();
81 | end;
82 | end;
83 | }
84 |
--------------------------------------------------------------------------------
/.github/workflows/UpdateGitHubGoSystemFiles.yaml:
--------------------------------------------------------------------------------
1 | name: ' Update AL-Go System Files'
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | templateUrl:
7 | description: Template Repository URL (current is https://github.com/microsoft/AL-Go-PTE@main)
8 | required: false
9 | default: ''
10 | directCommit:
11 | description: Direct COMMIT (Y/N)
12 | required: false
13 | default: 'N'
14 |
15 | permissions:
16 | contents: read
17 |
18 | defaults:
19 | run:
20 | shell: powershell
21 |
22 | env:
23 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
24 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
25 |
26 | jobs:
27 | UpdateALGoSystemFiles:
28 | runs-on: [ windows-latest ]
29 | steps:
30 | - name: Checkout
31 | uses: actions/checkout@v3
32 |
33 | - name: Initialize the workflow
34 | id: init
35 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
36 | with:
37 | shell: powershell
38 | eventId: "DO0098"
39 |
40 | - name: Read settings
41 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
42 | with:
43 | shell: powershell
44 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
45 | get: keyVaultName,ghTokenWorkflowSecretName,templateUrl
46 |
47 | - name: Read secrets
48 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
49 | env:
50 | secrets: ${{ toJson(secrets) }}
51 | with:
52 | shell: powershell
53 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
54 | settingsJson: ${{ env.Settings }}
55 | secrets: 'ghTokenWorkflow=${{ env.GHTOKENWORKFLOWSECRETNAME }}'
56 |
57 | - name: Override templateUrl
58 | env:
59 | templateUrl: ${{ github.event.inputs.templateUrl }}
60 | run: |
61 | $ErrorActionPreference = "STOP"
62 | Set-StrictMode -version 2.0
63 | $templateUrl = $ENV:templateUrl
64 | if ($templateUrl) {
65 | Write-Host "Using Template Url: $templateUrl"
66 | Add-Content -Path $env:GITHUB_ENV -Value "templateUrl=$templateUrl"
67 | }
68 |
69 | - name: Calculate DirectCommit
70 | env:
71 | directCommit: ${{ github.event.inputs.directCommit }}
72 | eventName: ${{ github.event_name }}
73 | run: |
74 | $ErrorActionPreference = "STOP"
75 | Set-StrictMode -version 2.0
76 | $directCommit = $ENV:directCommit
77 | Write-Host $ENV:eventName
78 | if ($ENV:eventName -eq 'schedule') {
79 | Write-Host "Running Update AL-Go System Files on a schedule. Setting DirectCommit = Y"
80 | $directCommit = 'Y'
81 | }
82 | Add-Content -Path $env:GITHUB_ENV -Value "DirectCommit=$directCommit"
83 |
84 | - name: Update AL-Go system files
85 | uses: microsoft/AL-Go-Actions/CheckForUpdates@v3.1
86 | with:
87 | shell: powershell
88 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
89 | token: ${{ env.ghTokenWorkflow }}
90 | Update: Y
91 | templateUrl: ${{ env.templateUrl }}
92 | directCommit: ${{ env.directCommit }}
93 |
94 | - name: Finalize the workflow
95 | if: always()
96 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
97 | with:
98 | shell: powershell
99 | eventId: "DO0098"
100 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
101 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/10_SharePoint/SharePointSetup.Page.al:
--------------------------------------------------------------------------------
1 | page 54107 "MNB SharePoint Setup"
2 | {
3 | Caption = 'SharePoint Setup';
4 | PageType = Card;
5 | SourceTable = "MNB SharePoint Setup";
6 | ApplicationArea = All;
7 | UsageCategory = Administration;
8 | InsertAllowed = false;
9 | DeleteAllowed = false;
10 |
11 | layout
12 | {
13 | area(content)
14 | {
15 | group(General)
16 | {
17 | Caption = 'General';
18 | field("Client Id"; Rec."Client Id")
19 | {
20 | ApplicationArea = All;
21 | ToolTip = 'Specifies the value of the Client Id field.';
22 | }
23 | field("Client Secret"; Rec."Client Secret")
24 | {
25 | ToolTip = 'Specifies the value of the Client Secret field.';
26 | ApplicationArea = All;
27 |
28 | }
29 | field("Base URL"; Rec."Base URL")
30 | {
31 | ToolTip = 'Specifies the value of the Base URL field.';
32 | ApplicationArea = All;
33 | }
34 | field("Folder Name"; Rec."Folder Name")
35 | {
36 | ToolTip = 'Specifies the value of the Folder Name field.';
37 | ApplicationArea = All;
38 | Editable = false;
39 | }
40 | }
41 | }
42 | }
43 | actions
44 | {
45 | area(Processing)
46 | {
47 | action(UploadFile)
48 | {
49 | Caption = 'Upload File to SharePoint';
50 | Image = ShowList;
51 | ToolTip = 'Upload File to SharePoint';
52 | Promoted = true;
53 | PromotedCategory = Process;
54 | ApplicationArea = All;
55 | trigger OnAction()
56 | var
57 | MNBSharePointMgt: Codeunit "MNB SharePoint Mgt.";
58 | begin
59 | MNBSharePointMgt.UploadFile();
60 | end;
61 | }
62 | action(ListFolders)
63 | {
64 | Caption = 'List Folders';
65 | Image = ShowList;
66 | ToolTip = 'Show list of folders';
67 | Promoted = true;
68 | PromotedCategory = Process;
69 | ApplicationArea = All;
70 | trigger OnAction()
71 | var
72 | MNBSharePointMgt: Codeunit "MNB SharePoint Mgt.";
73 | begin
74 | Rec."Folder Name" := MNBSharePointMgt.ShowFolders();
75 | end;
76 | }
77 | action(ListFiles)
78 | {
79 | Caption = 'List Files';
80 | Image = FileContract;
81 | ToolTip = 'Show list of files in the SharePoint.';
82 | Promoted = true;
83 | PromotedCategory = Process;
84 | ApplicationArea = All;
85 | trigger OnAction()
86 | var
87 | MNBSharePointMgt: Codeunit "MNB SharePoint Mgt.";
88 | begin
89 | MNBSharePointMgt.ListFiles();
90 | end;
91 | }
92 | }
93 | }
94 |
95 | trigger OnOpenPage()
96 | begin
97 | if not Rec.Get() then begin
98 | Rec.Init();
99 | Rec.Insert();
100 | end;
101 | end;
102 | }
103 |
--------------------------------------------------------------------------------
/.AL-Go/cloudDevEnv.ps1:
--------------------------------------------------------------------------------
1 | #
2 | # Script for creating cloud development environment
3 | # Please do not modify this script as it will be auto-updated from the AL-Go Template
4 | # Recommended approach is to use as is or add a script (freddyk-devenv.ps1), which calls this script with the user specific parameters
5 | #
6 | Param(
7 | [string] $environmentName = "",
8 | [bool] $reuseExistingEnvironment,
9 | [switch] $fromVSCode
10 | )
11 |
12 | $ErrorActionPreference = "stop"
13 | Set-StrictMode -Version 2.0
14 |
15 | try {
16 | $webClient = New-Object System.Net.WebClient
17 | $webClient.CachePolicy = New-Object System.Net.Cache.RequestCachePolicy -argumentList ([System.Net.Cache.RequestCacheLevel]::NoCacheNoStore)
18 | $webClient.Encoding = [System.Text.Encoding]::UTF8
19 | Write-Host "Downloading GitHub Helper module"
20 | $GitHubHelperPath = "$([System.IO.Path]::GetTempFileName()).psm1"
21 | $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v3.1/Github-Helper.psm1', $GitHubHelperPath)
22 | Write-Host "Downloading AL-Go Helper script"
23 | $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
24 | $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v3.1/AL-Go-Helper.ps1', $ALGoHelperPath)
25 |
26 | Import-Module $GitHubHelperPath
27 | . $ALGoHelperPath -local
28 |
29 | $baseFolder = GetBaseFolder -folder $PSScriptRoot
30 | $project = GetProject -baseFolder $baseFolder -projectALGoFolder $PSScriptRoot
31 |
32 | Clear-Host
33 | Write-Host
34 | Write-Host -ForegroundColor Yellow @'
35 | _____ _ _ _____ ______
36 | / ____| | | | | __ \ | ____|
37 | | | | | ___ _ _ __| | | | | | _____ __ |__ _ ____ __
38 | | | | |/ _ \| | | |/ _` | | | | |/ _ \ \ / / __| | '_ \ \ / /
39 | | |____| | (_) | |_| | (_| | | |__| | __/\ V /| |____| | | \ V /
40 | \_____|_|\___/ \__,_|\__,_| |_____/ \___| \_/ |______|_| |_|\_/
41 |
42 | '@
43 |
44 | Write-Host @'
45 | This script will create a cloud based development environment (Business Central SaaS Sandbox) for your project.
46 | All apps and test apps will be compiled and published to the environment in the development scope.
47 | The script will also modify launch.json to have a "Cloud Sandbox ()" configuration point to your environment.
48 |
49 | '@
50 |
51 | if (Test-Path (Join-Path $PSScriptRoot "NewBcContainer.ps1")) {
52 | Write-Host -ForegroundColor Red "WARNING: The project has a NewBcContainer override defined. Typically, this means that you cannot run a cloud development environment"
53 | }
54 |
55 | $settings = ReadSettings -baseFolder $baseFolder -project $project -userName $env:USERNAME
56 |
57 | Write-Host
58 |
59 | if (-not $environmentName) {
60 | $environmentName = Enter-Value `
61 | -title "Environment name" `
62 | -question "Please enter the name of the environment to create" `
63 | -default "$($env:USERNAME)-sandbox" `
64 | -trimCharacters @('"',"'",' ')
65 | }
66 |
67 | if ($PSBoundParameters.Keys -notcontains 'reuseExistingEnvironment') {
68 | $reuseExistingEnvironment = (Select-Value `
69 | -title "What if the environment already exists?" `
70 | -options @{ "Yes" = "Reuse existing environment"; "No" = "Recreate environment" } `
71 | -question "Select behavior" `
72 | -default "No") -eq "Yes"
73 | }
74 |
75 | CreateDevEnv `
76 | -kind cloud `
77 | -caller local `
78 | -environmentName $environmentName `
79 | -reuseExistingEnvironment:$reuseExistingEnvironment `
80 | -baseFolder $baseFolder `
81 | -project $project
82 | }
83 | catch {
84 | Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)"
85 | }
86 | finally {
87 | if ($fromVSCode) {
88 | Read-Host "Press ENTER to close this window"
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/2_ImageCamera/CameraandImage.Cod.al:
--------------------------------------------------------------------------------
1 | codeunit 54101 "MNB Camera and Image"
2 | {
3 | ///
4 | /// The example how to take photo from the camera in few lines of code
5 | ///
6 | ///
7 |
8 | internal procedure TakePurchaseInvoicePhoto(var PurchaseHeader: Record "Purchase Header")
9 | var
10 | Camera: Codeunit Camera;
11 | PictureInStream: InStream;
12 | CameraIsNotAvailableErr: Label 'Camera is not available.';
13 | NoPhotoTakenErr: Label 'No Photo Taken';
14 | PictureText: Text;
15 | begin
16 | if not Camera.IsAvailable() then
17 | Error(CameraIsNotAvailableErr);
18 |
19 | if not Camera.GetPicture(PictureInStream, PictureText) then
20 | Error(NoPhotoTakenErr);
21 |
22 | SavePicture(PurchaseHeader, PictureInStream, PictureText);
23 | end;
24 |
25 | internal procedure GetPictureSizeAndTransform(var PurchaseHeader: Record "Purchase Header")
26 | var
27 | Camera: Codeunit Camera;
28 | Image: Codeunit Image;
29 | PictureInStream: InStream;
30 | CameraIsNotAvailableErr: Label 'Camera is not available.';
31 | NoPhotoTakenErr: Label 'No Photo Taken';
32 | PictureSizeMsg: Label 'Picture Size: \ Height: %1\ Width: %2.';
33 | PictureText: Text;
34 | begin
35 | if not Camera.IsAvailable() then
36 | Error(CameraIsNotAvailableErr);
37 |
38 | if not Camera.GetPicture(PictureInStream, PictureText) then
39 | Error(NoPhotoTakenErr);
40 |
41 | TransformImage(Image, PictureInStream);
42 |
43 | GetTransformedFile(Image, PurchaseHeader);
44 |
45 | Message(PictureSizeMsg, Image.GetHeight(), Image.GetWidth());
46 |
47 | end;
48 |
49 | local procedure SavePicture(var PurchaseHeader: Record "Purchase Header"; var PictureInStream: InStream; var PictureText: Text)
50 | var
51 | MimeTypeTok: Label 'image/jpeg', Locked = true;
52 | begin
53 | Clear(PurchaseHeader."MNB Invoice Picture");
54 | PurchaseHeader."MNB Invoice Picture".ImportStream(PictureInStream, PictureText, MimeTypeTok);
55 | PurchaseHeader.Modify(true);
56 | end;
57 |
58 | local procedure GetTransformedFile(var Image: Codeunit Image; var PurchaseHeader: Record "Purchase Header")
59 | var
60 | TempBlob: Codeunit "Temp Blob";
61 | InStream: InStream;
62 | MimeTypeTok: Label 'image/jpeg', Locked = true;
63 | PictureTxt: Label 'picture_name.jpg';
64 | PictureOutStream: OutStream;
65 | begin
66 | TempBlob.CreateOutStream(PictureOutStream);
67 | Image.Save(PictureOutStream);
68 | TempBlob.CreateInStream(InStream);
69 | Clear(PurchaseHeader."MNB Invoice Picture");
70 | PurchaseHeader."MNB Invoice Picture".ImportStream(InStream, PictureTxt, MimeTypeTok);
71 | PurchaseHeader.Modify(true);
72 | end;
73 |
74 | local procedure TransformImage(var Image: Codeunit Image; var PictureInStream: InStream)
75 | begin
76 | Image.FromStream(PictureInStream);
77 | Image.RotateFlip(Enum::"Rotate Flip Type"::Rotate90FlipNone);
78 | Image.Crop(0, 0, Image.GetWidth() - 100, Image.GetHeight() - 100);
79 | end;
80 |
81 | procedure Example()
82 | var
83 | Image: Codeunit Image;
84 | TempBlob: Codeunit "Temp Blob";
85 | InStream: InStream;
86 | OutStream: OutStream;
87 | FileName: Text;
88 | begin
89 | UploadIntoStream('', '', '', FileName, InStream);
90 |
91 | Image.FromStream(InStream);
92 | Image.SetFormat(Enum::"Image Format"::Png);
93 |
94 | TempBlob.CreateOutStream(OutStream);
95 | Image.Save(OutStream);
96 |
97 | TempBlob.CreateInStream(InStream);
98 | FileName := FileName + '.png';
99 | DownloadFromStream(InStream, '', '', '', FileName);
100 | end;
101 |
102 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/14_Checklists/ChecklistInstall.Codeunit.al:
--------------------------------------------------------------------------------
1 | codeunit 54104 "MNB Checklist Install"
2 | {
3 | ///
4 | /// The examples in this codeunit show how to create Assisted Setup and Guided Experience (for example links to blog posts)
5 | ///
6 | ///
7 | Subtype = Install;
8 | trigger OnInstallAppPerCompany()
9 | begin
10 | CreateManualSetups();
11 | CreateVideoLinks();
12 | end;
13 |
14 | ///
15 | /// Create manual setups that can be later use in the checklists and are visible in the manual setups
16 | ///
17 |
18 | local procedure CreateManualSetups()
19 | var
20 | GuidedExperience: Codeunit "Guided Experience";
21 | begin
22 | GuidedExperience.InsertManualSetup(AzureStorageTitleLbl, AzureStorageShortTitleLbl, AzureStorageDescriptionLbl, 2, ObjectType::Page, Page::"MNB Azure Storage Setup", Enum::"Manual Setup Category"::General, 'Demo');
23 | GuidedExperience.InsertManualSetup(ImageAndCameraTitleLbl, ImageAndCameraShortTitleLbl, ImageAndCameraDescriptionLbl, 4, ObjectType::Page, Page::"Purchase Orders", Enum::"Manual Setup Category"::General, 'Demo');
24 | GuidedExperience.InsertManualSetup(GeolocationTitleLbl, GeolocationShortTitleLbl, GeolocationDescriptionLbl, 4, ObjectType::Page, Page::"Fixed Asset List", Enum::"Manual Setup Category"::General, 'Demo');
25 | GuidedExperience.InsertManualSetup(BarcodeTitleLbl, BarcodeShortTitleLbl, BarcodeDescriptionLbl, 4, ObjectType::Report, Report::"MNB Item Barcode", Enum::"Manual Setup Category"::General, 'Demo');
26 | GuidedExperience.InsertManualSetup(FilterTokensTitleLbl, FilterTokensShortTitleLbl, FilterTokensDescriptionLbl, 4, ObjectType::Page, Page::"Sales Order List", Enum::"Manual Setup Category"::General, 'Demo');
27 | GuidedExperience.InsertManualSetup(ObjectsTitleLbl, ObjectsShortTitleLbl, ObjectsDescriptionLbl, 4, ObjectType::Page, Page::"MNB Choosing Objects", Enum::"Manual Setup Category"::General, 'Demo');
28 | GuidedExperience.InsertManualSetup(RecurrenceTitleLbl, RecurrenceShortTitleLbl, RecurrenceDescriptionLbl, 4, ObjectType::Page, Page::"MNB Next Occurrence", Enum::"Manual Setup Category"::General, 'Demo');
29 | end;
30 |
31 | local procedure CreateVideoLinks()
32 | var
33 | GuidedExperience: Codeunit "Guided Experience";
34 | begin
35 | GuidedExperience.InsertVideo('Special Guest', 'Special Guest', '', 1, 'https://www.youtube.com/embed/btPJPFnesV4', Enum::"Video Category"::DoMoreWithBC);
36 | end;
37 |
38 |
39 |
40 | var
41 | AzureStorageDescriptionLbl: Label 'Integration with Azure Blob storage and how to use it to import the files.';
42 | AzureStorageShortTitleLbl: Label 'Azure Storage';
43 | AzureStorageTitleLbl: Label 'Azure Blob Storage Example';
44 | BarcodeDescriptionLbl: Label 'Print 1d and 2d barcode in the reports.';
45 | BarcodeShortTitleLbl: Label 'Barcode Printouts';
46 | BarcodeTitleLbl: Label 'Barcode Printouts Example';
47 | FilterTokensDescriptionLbl: Label 'Filter tokens allow to easier filter the data with predefine tokens.';
48 | FilterTokensShortTitleLbl: Label 'Filter Tokens';
49 | FilterTokensTitleLbl: Label 'Filter Tokens Example';
50 | GeolocationDescriptionLbl: Label 'Get GPS coordinates for the client.';
51 | GeolocationShortTitleLbl: Label 'Geolocation';
52 | GeolocationTitleLbl: Label 'Geolocation Example';
53 | ImageAndCameraDescriptionLbl: Label 'Get pictures and process the images without additional code';
54 | ImageAndCameraShortTitleLbl: Label 'Camera and Image';
55 | ImageAndCameraTitleLbl: Label 'Camera and Image Example';
56 | ObjectsDescriptionLbl: Label 'In SystemApp there are functions allowing to choose fields and objects.';
57 | ObjectsShortTitleLbl: Label 'Choose Objects and Fields';
58 | ObjectsTitleLbl: Label 'Choose Objects and Fields Example';
59 | RecurrenceDescriptionLbl: Label 'Example shows how to get the next occurrence of date for specific schedule.';
60 | RecurrenceShortTitleLbl: Label 'Recurrence Schedule';
61 | RecurrenceTitleLbl: Label 'Recurrence Schedule Example';
62 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/10_SharePoint/SharePointMgt.Codeunit.al:
--------------------------------------------------------------------------------
1 | codeunit 54108 "MNB SharePoint Mgt."
2 | {
3 | internal procedure ShowFolders(): Text;
4 | var
5 | MNBSharePointSetup: Record "MNB SharePoint Setup";
6 | SharePointFolder: Record "SharePoint Folder" temporary;
7 | SharePointClient: Codeunit "SharePoint Client";
8 | MNBSharePointFolders: Page "MNB SharePoint Folders";
9 | AadTenantId, ServerRelativeUrl : Text;
10 | begin
11 | MNBSharePointSetup.Get();
12 | AadTenantId := GetAadTenantNameFromBaseUrl(MNBSharePointSetup."Base URL");
13 | SharePointClient.Initialize(MNBSharePointSetup."Base URL", GetSharePointAuthorization(AadTenantId));
14 | SharePointClient.GetSubFoldersByServerRelativeUrl(ServerRelativeUrl, SharePointFolder);
15 |
16 | MNBSharePointFolders.SetData(SharePointFolder);
17 | MNBSharePointFolders.LookupMode(true);
18 | if MNBSharePointFolders.RunModal() = Action::LookupOK then begin
19 | MNBSharePointFolders.GetRecord(SharePointFolder);
20 | exit(SharePointFolder.Name);
21 | end;
22 | end;
23 |
24 | internal procedure UploadFile()
25 | var
26 | MNBSharePointSetup: Record "MNB SharePoint Setup";
27 | SharePointFile: Record "SharePoint File";
28 | FileManagement: Codeunit "File Management";
29 | SharePointClient: Codeunit "SharePoint Client";
30 | TempBlob: Codeunit "Temp Blob";
31 | InStream: InStream;
32 | HttpDiagnostics: Interface "HTTP Diagnostics";
33 | SaveFailedErr: Label 'Save to SharePoint failed.\ErrorMessage: %1\HttpRetryAfter: %2\HttpStatusCode: %3\ResponseReasonPhrase: %4', Comment = '%1=GetErrorMessage; %2=GetHttpRetryAfter; %3=GetHttpStatusCode; %4=GetResponseReasonPhrase';
34 | AadTenantId: Text;
35 | FileName: Text;
36 | begin
37 | MNBSharePointSetup.Get();
38 | FileName := FileManagement.BLOBImportWithFilter(TempBlob, 'Import File', 'Import File', 'All Files (*.*)|*.*', '*.*');
39 | AadTenantId := GetAadTenantNameFromBaseUrl(MNBSharePointSetup."Base URL");
40 | SharePointClient.Initialize(MNBSharePointSetup."Base URL", GetSharePointAuthorization(AadTenantId));
41 | InStream := TempBlob.CreateInStream();
42 | if not SharePointClient.AddFileToFolder(MNBSharePointSetup."Folder Name", FileName, InStream, SharePointFile) then begin
43 | HttpDiagnostics := SharePointClient.GetDiagnostics();
44 | Error(SaveFailedErr, HttpDiagnostics.GetErrorMessage(), HttpDiagnostics.GetHttpRetryAfter(), HttpDiagnostics.GetHttpStatusCode(), HttpDiagnostics.GetResponseReasonPhrase());
45 | end;
46 | end;
47 |
48 | internal procedure ListFiles()
49 | var
50 | MNBSharePointSetup: Record "MNB SharePoint Setup";
51 | SharePointFile: Record "SharePoint File";
52 | SharePointClient: Codeunit "SharePoint Client";
53 | MNBSharePointFiles: Page "MNB SharePoint Files";
54 | AadTenantId: Text;
55 | begin
56 | MNBSharePointSetup.Get();
57 | AadTenantId := GetAadTenantNameFromBaseUrl(MNBSharePointSetup."Base URL");
58 | SharePointClient.Initialize(MNBSharePointSetup."Base URL", GetSharePointAuthorization(AadTenantId));
59 | SharePointClient.GetFolderFilesByServerRelativeUrl(MNBSharePointSetup."Folder Name", SharePointFile);
60 | MNBSharePointFiles.SetData(SharePointFile);
61 | MNBSharePointFiles.LookupMode(true);
62 | MNBSharePointFiles.RunModal();
63 | end;
64 |
65 | local procedure GetSharePointAuthorization(AadTenantId: Text): Interface "SharePoint Authorization"
66 | var
67 | MNBSharePointSetup: Record "MNB SharePoint Setup";
68 | SharePointAuth: Codeunit "SharePoint Auth.";
69 | Scopes: List of [Text];
70 | begin
71 | MNBSharePointSetup.Get();
72 | Scopes.Add('00000003-0000-0ff1-ce00-000000000000/.default');
73 | exit(SharePointAuth.CreateAuthorizationCode(AadTenantId, MNBSharePointSetup."Client Id", MNBSharePointSetup."Client Secret", Scopes));
74 | end;
75 |
76 | local procedure GetAadTenantNameFromBaseUrl(BaseUrl: Text): Text
77 | var
78 | Uri: Codeunit Uri;
79 | MySiteHostSuffixTxt: Label '-my.sharepoint.com', Locked = true;
80 | OnMicrosoftTxt: Label '.onmicrosoft.com', Locked = true;
81 | SharePointHostSuffixTxt: Label '.sharepoint.com', Locked = true;
82 | UrlInvalidErr: Label 'The Base Url %1 does not seem to be a valid SharePoint Online Url.', Comment = '%1=BaseUrl';
83 | Host: Text;
84 | begin
85 | // SharePoint Online format: https://tenantname.sharepoint.com/SiteName/LibraryName/
86 | // SharePoint My Site format: https://tenantname-my.sharepoint.com/personal/user_name/
87 | Uri.Init(BaseUrl);
88 | Host := Uri.GetHost();
89 | if not Host.EndsWith(SharePointHostSuffixTxt) then
90 | Error(UrlInvalidErr, BaseUrl);
91 | if Host.EndsWith(MySiteHostSuffixTxt) then
92 | exit(CopyStr(Host, 1, StrPos(Host, MySiteHostSuffixTxt) - 1) + OnMicrosoftTxt);
93 | exit(CopyStr(Host, 1, StrPos(Host, SharePointHostSuffixTxt) - 1) + OnMicrosoftTxt);
94 | end;
95 |
96 | }
--------------------------------------------------------------------------------
/SystemAppPresentation/src/Examples/9_Math/MathOperations.Page.al:
--------------------------------------------------------------------------------
1 | page 54101 "MNB Math Operations"
2 | {
3 | ///
4 | /// Shows example of math module
5 | ///
6 | ///
7 |
8 | Caption = 'Math Operations';
9 | PageType = Card;
10 | ApplicationArea = All;
11 | UsageCategory = Tasks;
12 |
13 | layout
14 | {
15 | area(content)
16 | {
17 | group(General)
18 | {
19 | Caption = 'General';
20 | field(xDecimal; xDecimal)
21 | {
22 | Caption = 'Let the X be';
23 | ToolTip = 'Specifies the X.';
24 | ApplicationArea = All;
25 | DecimalPlaces = 2 : 5;
26 | }
27 | field(yDecimal; yDecimal)
28 | {
29 | Caption = 'Let the Y be';
30 | ToolTip = 'Specifies the Y.';
31 | ApplicationArea = All;
32 | DecimalPlaces = 2 : 5;
33 | }
34 | }
35 | }
36 | }
37 | actions
38 | {
39 | area(Processing)
40 | {
41 | action(MaxValue)
42 | {
43 | Caption = 'Get Max';
44 | ToolTip = 'Gets max value of two.';
45 | Image = Calculate;
46 | Promoted = true;
47 | PromotedCategory = Process;
48 | PromotedOnly = true;
49 | ApplicationArea = All;
50 | trigger OnAction()
51 | var
52 | Math: Codeunit Math;
53 | MaxIsMsg: Label 'Max is: %1';
54 | begin
55 | Message(MaxIsMsg, Math.Max(xDecimal, yDecimal));
56 | end;
57 | }
58 | action(MinValue)
59 | {
60 | Caption = 'Get Min';
61 | ToolTip = 'Gets min value of two.';
62 | Image = Calculate;
63 | Promoted = true;
64 | PromotedCategory = Process;
65 | PromotedOnly = true;
66 | ApplicationArea = All;
67 | trigger OnAction()
68 | var
69 | Math: Codeunit Math;
70 | MinIsMsg: Label 'Min is: %1';
71 | begin
72 | Message(MinIsMsg, Math.Min(xDecimal, yDecimal));
73 | end;
74 | }
75 | action(FloorValue)
76 | {
77 | Caption = 'Floor X';
78 | ToolTip = 'Gets Floor value of X.';
79 | Image = Calculate;
80 | Promoted = true;
81 | PromotedCategory = Process;
82 | PromotedOnly = true;
83 | ApplicationArea = All;
84 | trigger OnAction()
85 | var
86 | Math: Codeunit Math;
87 | FloorIsMsg: Label 'Floor is: %1';
88 | begin
89 | Message(FloorIsMsg, Math.Floor(xDecimal));
90 | end;
91 | }
92 | action(CeilingValue)
93 | {
94 | Caption = 'Ceiling X';
95 | ToolTip = 'Gets Ceiling value of X.';
96 | Image = Calculate;
97 | Promoted = true;
98 | PromotedCategory = Process;
99 | PromotedOnly = true;
100 | ApplicationArea = All;
101 | trigger OnAction()
102 | var
103 | Math: Codeunit Math;
104 | CeilingIsMsg: Label 'Ceiling is: %1';
105 | begin
106 | Message(CeilingIsMsg, Math.Ceiling(xDecimal));
107 | end;
108 | }
109 | action(TruncateValue)
110 | {
111 | Caption = 'Truncate X';
112 | ToolTip = 'Gets Truncate value of X.';
113 | Image = Calculate;
114 | Promoted = true;
115 | PromotedCategory = Process;
116 | PromotedOnly = true;
117 | ApplicationArea = All;
118 | trigger OnAction()
119 | var
120 | Math: Codeunit Math;
121 | TruncateIsMsg: Label 'Truncate is: %1';
122 | begin
123 | Message(TruncateIsMsg, Math.Truncate(xDecimal));
124 | end;
125 | }
126 | action(RemainderValue)
127 | {
128 | Caption = 'Remainder';
129 | ToolTip = 'Gets Remainder value of X and Y.';
130 | Image = Calculate;
131 | Promoted = true;
132 | PromotedCategory = Process;
133 | PromotedOnly = true;
134 | ApplicationArea = All;
135 | trigger OnAction()
136 | var
137 | Math: Codeunit Math;
138 | RemainderIsMsg: Label 'Remainder is: %1';
139 | begin
140 | Message(RemainderIsMsg, Math.IEEERemainder(xDecimal, yDecimal));
141 | end;
142 | }
143 | }
144 | }
145 | var
146 | xDecimal, yDecimal : Decimal;
147 | }
148 |
--------------------------------------------------------------------------------
/.github/workflows/CreateOnlineDevelopmentEnvironment.yaml:
--------------------------------------------------------------------------------
1 | name: ' Create Online Dev. Environment'
2 |
3 | run-name: "Create Online Dev. Environment for [${{ github.ref_name }}]"
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | environmentName:
9 | description: Name of the online environment
10 | required: true
11 | reUseExistingEnvironment:
12 | description: Reuse environment if it exists
13 | required: false
14 | default: 'N'
15 | directCommit:
16 | description: Direct COMMIT (Y/N)
17 | required: false
18 | default: 'N'
19 |
20 | permissions:
21 | contents: write
22 | pull-requests: write
23 |
24 | defaults:
25 | run:
26 | shell: powershell
27 |
28 | env:
29 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
30 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
31 |
32 | jobs:
33 | Initialize:
34 | runs-on: [ windows-latest ]
35 | outputs:
36 | deviceCode: ${{ steps.authenticate.outputs.deviceCode }}
37 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
38 | steps:
39 | - name: Checkout
40 | uses: actions/checkout@v3
41 |
42 | - name: Initialize the workflow
43 | id: init
44 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
45 | with:
46 | shell: powershell
47 | eventId: "DO0093"
48 |
49 | - name: Read settings
50 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
51 | with:
52 | shell: powershell
53 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
54 |
55 | - name: Read secrets
56 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
57 | env:
58 | secrets: ${{ toJson(secrets) }}
59 | with:
60 | shell: powershell
61 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
62 | settingsJson: ${{ env.Settings }}
63 | secrets: 'adminCenterApiCredentials'
64 |
65 | - name: Check AdminCenterApiCredentials / Initiate Device Login (open to see code)
66 | id: authenticate
67 | run: |
68 | $ErrorActionPreference = "STOP"
69 | Set-StrictMode -version 2.0
70 | $adminCenterApiCredentials = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($env:adminCenterApiCredentials))
71 | if ($adminCenterApiCredentials) {
72 | Write-Host "AdminCenterApiCredentials provided in secret $($ENV:adminCenterApiCredentialsSecretName)!"
73 | Set-Content -Path $ENV:GITHUB_STEP_SUMMARY -value "Admin Center Api Credentials was provided in a secret called $($ENV:adminCenterApiCredentialsSecretName). Using this information for authentication."
74 | }
75 | else {
76 | Write-Host "AdminCenterApiCredentials not provided, initiating Device Code flow"
77 | $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
78 | $webClient = New-Object System.Net.WebClient
79 | $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v3.1/AL-Go-Helper.ps1', $ALGoHelperPath)
80 | . $ALGoHelperPath
81 | $BcContainerHelperPath = DownloadAndImportBcContainerHelper -baseFolder $ENV:GITHUB_WORKSPACE
82 | $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0))
83 | CleanupAfterBcContainerHelper -bcContainerHelperPath $bcContainerHelperPath
84 | Set-Content -Path $ENV:GITHUB_STEP_SUMMARY -value "AL-Go needs access to the Business Central Admin Center Api and could not locate a secret called $($ENV:adminCenterApiCredentialsSecretName) (https://aka.ms/ALGoSettings#AdminCenterApiCredentialsSecretName)`n`n$($authContext.message)"
85 | Add-Content -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)"
86 | }
87 |
88 | CreateDevelopmentEnvironment:
89 | runs-on: [ windows-latest ]
90 | name: Create Development Environment
91 | needs: [ Initialize ]
92 | env:
93 | deviceCode: ${{ needs.Initialize.outputs.deviceCode }}
94 | steps:
95 | - name: Checkout
96 | uses: actions/checkout@v3
97 |
98 | - name: Read settings
99 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
100 | with:
101 | shell: powershell
102 | parentTelemetryScopeJson: ${{ needs.Initialize.outputs.telemetryScopeJson }}
103 |
104 | - name: Read secrets
105 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
106 | env:
107 | secrets: ${{ toJson(secrets) }}
108 | with:
109 | shell: powershell
110 | parentTelemetryScopeJson: ${{ needs.Initialize.outputs.telemetryScopeJson }}
111 | settingsJson: ${{ env.Settings }}
112 | secrets: 'adminCenterApiCredentials'
113 |
114 | - name: Set AdminCenterApiCredentials
115 | run: |
116 | if ($env:deviceCode) {
117 | $adminCenterApiCredentials = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("{""deviceCode"":""$($env:deviceCode)""}"))
118 | Add-Content -path $env:GITHUB_ENV -value "adminCenterApiCredentials=$adminCenterApiCredentials"
119 | }
120 |
121 | - name: Create Development Environment
122 | uses: microsoft/AL-Go-Actions/CreateDevelopmentEnvironment@v3.1
123 | with:
124 | shell: powershell
125 | parentTelemetryScopeJson: ${{ needs.Initialize.outputs.telemetryScopeJson }}
126 | environmentName: ${{ github.event.inputs.environmentName }}
127 | reUseExistingEnvironment: ${{ github.event.inputs.reUseExistingEnvironment }}
128 | directCommit: ${{ github.event.inputs.directCommit }}
129 | adminCenterApiCredentials: ${{ env.adminCenterApiCredentials }}
130 |
131 | - name: Finalize the workflow
132 | if: always()
133 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
134 | with:
135 | shell: powershell
136 | eventId: "DO0093"
137 | telemetryScopeJson: ${{ needs.Initialize.outputs.telemetryScopeJson }}
138 |
--------------------------------------------------------------------------------
/.AL-Go/localDevEnv.ps1:
--------------------------------------------------------------------------------
1 | #
2 | # Script for creating local development environment
3 | # Please do not modify this script as it will be auto-updated from the AL-Go Template
4 | # Recommended approach is to use as is or add a script (freddyk-devenv.ps1), which calls this script with the user specific parameters
5 | #
6 | Param(
7 | [string] $containerName = "",
8 | [string] $auth = "",
9 | [pscredential] $credential = $null,
10 | [string] $licenseFileUrl = "",
11 | [string] $insiderSasToken = "",
12 | [switch] $fromVSCode
13 | )
14 |
15 | $ErrorActionPreference = "stop"
16 | Set-StrictMode -Version 2.0
17 |
18 | try {
19 | $webClient = New-Object System.Net.WebClient
20 | $webClient.CachePolicy = New-Object System.Net.Cache.RequestCachePolicy -argumentList ([System.Net.Cache.RequestCacheLevel]::NoCacheNoStore)
21 | $webClient.Encoding = [System.Text.Encoding]::UTF8
22 | Write-Host "Downloading GitHub Helper module"
23 | $GitHubHelperPath = "$([System.IO.Path]::GetTempFileName()).psm1"
24 | $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v3.1/Github-Helper.psm1', $GitHubHelperPath)
25 | Write-Host "Downloading AL-Go Helper script"
26 | $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
27 | $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v3.1/AL-Go-Helper.ps1', $ALGoHelperPath)
28 |
29 | Import-Module $GitHubHelperPath
30 | . $ALGoHelperPath -local
31 |
32 | $baseFolder = GetBaseFolder -folder $PSScriptRoot
33 | $project = GetProject -baseFolder $baseFolder -projectALGoFolder $PSScriptRoot
34 |
35 | Clear-Host
36 | Write-Host
37 | Write-Host -ForegroundColor Yellow @'
38 | _ _ _____ ______
39 | | | | | | __ \ | ____|
40 | | | ___ ___ __ _| | | | | | _____ __ |__ _ ____ __
41 | | | / _ \ / __/ _` | | | | | |/ _ \ \ / / __| | '_ \ \ / /
42 | | |____ (_) | (__ (_| | | | |__| | __/\ V /| |____| | | \ V /
43 | |______\___/ \___\__,_|_| |_____/ \___| \_/ |______|_| |_|\_/
44 |
45 | '@
46 |
47 | Write-Host @'
48 | This script will create a docker based local development environment for your project.
49 |
50 | NOTE: You need to have Docker installed, configured and be able to create Business Central containers for this to work.
51 | If this fails, you can setup a cloud based development environment by running cloudDevEnv.ps1
52 |
53 | All apps and test apps will be compiled and published to the environment in the development scope.
54 | The script will also modify launch.json to have a Local Sandbox configuration point to your environment.
55 |
56 | '@
57 |
58 | $settings = ReadSettings -baseFolder $baseFolder -project $project -userName $env:USERNAME
59 |
60 | Write-Host "Checking System Requirements"
61 | $dockerProcess = (Get-Process "dockerd" -ErrorAction Ignore)
62 | if (!($dockerProcess)) {
63 | Write-Host -ForegroundColor Red "Dockerd process not found. Docker might not be started, not installed or not running Windows Containers."
64 | }
65 | if ($settings.keyVaultName) {
66 | if (-not (Get-Module -ListAvailable -Name 'Az.KeyVault')) {
67 | Write-Host -ForegroundColor Red "A keyvault name is defined in Settings, you need to have the Az.KeyVault PowerShell module installed (use Install-Module az) or you can set the keyVaultName to an empty string in the user settings file ($($ENV:UserName).settings.json)."
68 | }
69 | }
70 |
71 | Write-Host
72 |
73 | if (-not $containerName) {
74 | $containerName = Enter-Value `
75 | -title "Container name" `
76 | -question "Please enter the name of the container to create" `
77 | -default "bcserver" `
78 | -trimCharacters @('"',"'",' ')
79 | }
80 |
81 | if (-not $auth) {
82 | $auth = Select-Value `
83 | -title "Authentication mechanism for container" `
84 | -options @{ "Windows" = "Windows Authentication"; "UserPassword" = "Username/Password authentication" } `
85 | -question "Select authentication mechanism for container" `
86 | -default "UserPassword"
87 | }
88 |
89 | if (-not $credential) {
90 | if ($auth -eq "Windows") {
91 | $credential = Get-Credential -Message "Please enter your Windows Credentials" -UserName $env:USERNAME
92 | $CurrentDomain = "LDAP://" + ([ADSI]"").distinguishedName
93 | $domain = New-Object System.DirectoryServices.DirectoryEntry($CurrentDomain,$credential.UserName,$credential.GetNetworkCredential().password)
94 | if ($null -eq $domain.name) {
95 | Write-Host -ForegroundColor Red "Unable to verify your Windows Credentials, you might not be able to authenticate to your container"
96 | }
97 | }
98 | else {
99 | $credential = Get-Credential -Message "Please enter username and password for your container" -UserName "admin"
100 | }
101 | }
102 |
103 | if (-not $licenseFileUrl) {
104 | if ($settings.type -eq "AppSource App") {
105 | $description = "When developing AppSource Apps for Business Central versions prior to 22, your local development environment needs the developer licensefile with permissions to your AppSource app object IDs"
106 | $default = "none"
107 | }
108 | else {
109 | $description = "When developing PTEs, you can optionally specify a developer licensefile with permissions to object IDs of your dependant apps"
110 | $default = "none"
111 | }
112 |
113 | $licenseFileUrl = Enter-Value `
114 | -title "LicenseFileUrl" `
115 | -description $description `
116 | -question "Local path or a secure download URL to license file " `
117 | -default $default `
118 | -doNotConvertToLower `
119 | -trimCharacters @('"',"'",' ')
120 | }
121 |
122 | if ($licenseFileUrl -eq "none") {
123 | $licenseFileUrl = ""
124 | }
125 |
126 | CreateDevEnv `
127 | -kind local `
128 | -caller local `
129 | -containerName $containerName `
130 | -baseFolder $baseFolder `
131 | -project $project `
132 | -auth $auth `
133 | -credential $credential `
134 | -licenseFileUrl $licenseFileUrl `
135 | -insiderSasToken $insiderSasToken
136 | }
137 | catch {
138 | Write-Host -ForegroundColor Red "Error: $($_.Exception.Message)`nStacktrace: $($_.scriptStackTrace)"
139 | }
140 | finally {
141 | if ($fromVSCode) {
142 | Read-Host "Press ENTER to close this window"
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/.github/workflows/Current.yaml:
--------------------------------------------------------------------------------
1 | name: ' Test Current'
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | contents: read
8 |
9 | defaults:
10 | run:
11 | shell: powershell
12 |
13 | env:
14 | workflowDepth: 1
15 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
16 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
17 |
18 | jobs:
19 | Initialization:
20 | runs-on: [ windows-latest ]
21 | outputs:
22 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
23 | settings: ${{ steps.ReadSettings.outputs.SettingsJson }}
24 | githubRunner: ${{ steps.ReadSettings.outputs.GitHubRunnerJson }}
25 | githubRunnerShell: ${{ steps.ReadSettings.outputs.GitHubRunnerShell }}
26 | projects: ${{ steps.determineProjectsToBuild.outputs.ProjectsJson }}
27 | projectDependenciesJson: ${{ steps.determineProjectsToBuild.outputs.ProjectDependenciesJson }}
28 | buildOrderJson: ${{ steps.determineProjectsToBuild.outputs.BuildOrderJson }}
29 | steps:
30 | - name: Checkout
31 | uses: actions/checkout@v3
32 | with:
33 | lfs: true
34 |
35 | - name: Initialize the workflow
36 | id: init
37 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
38 | with:
39 | shell: powershell
40 | eventId: "DO0101"
41 |
42 | - name: Read settings
43 | id: ReadSettings
44 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
45 | with:
46 | shell: powershell
47 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
48 |
49 | - name: Determine Projects To Build
50 | id: determineProjectsToBuild
51 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v3.1
52 | with:
53 | shell: powershell
54 | maxBuildDepth: ${{ env.workflowDepth }}
55 |
56 | Build:
57 | needs: [ Initialization ]
58 | if: (!failure()) && (!cancelled()) && fromJson(needs.Initialization.outputs.buildOrderJson)[0].projectsCount > 0
59 | runs-on: ${{ fromJson(needs.Initialization.outputs.githubRunner) }}
60 | defaults:
61 | run:
62 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
63 | strategy:
64 | matrix:
65 | include: ${{ fromJson(needs.Initialization.outputs.buildOrderJson)[0].buildDimensions }}
66 | fail-fast: false
67 | name: Build ${{ matrix.project }} - ${{ matrix.buildMode }}
68 | outputs:
69 | TestResultsArtifactsName: ${{ steps.calculateArtifactNames.outputs.TestResultsArtifactsName }}
70 | BcptTestResultsArtifactsName: ${{ steps.calculateArtifactNames.outputs.BcptTestResultsArtifactsName }}
71 | BuildOutputArtifactsName: ${{ steps.calculateArtifactNames.outputs.BuildOutputArtifactsName }}
72 | steps:
73 | - name: Checkout
74 | uses: actions/checkout@v3
75 | with:
76 | lfs: true
77 |
78 | - name: Download thisbuild artifacts
79 | if: env.workflowDepth > 1
80 | uses: actions/download-artifact@v3
81 | with:
82 | path: '${{ github.workspace }}\.dependencies'
83 |
84 | - name: Read settings
85 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
86 | with:
87 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
88 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
89 | project: ${{ matrix.project }}
90 |
91 | - name: Read secrets
92 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
93 | env:
94 | secrets: ${{ toJson(secrets) }}
95 | with:
96 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
97 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
98 | settingsJson: ${{ env.Settings }}
99 | secrets: 'licenseFileUrl,insiderSasToken,codeSignCertificateUrl,codeSignCertificatePassword,keyVaultCertificateUrl,keyVaultCertificatePassword,keyVaultClientId,gitHubPackagesContext'
100 |
101 | - name: Determine ArtifactUrl
102 | uses: microsoft/AL-Go-Actions/DetermineArtifactUrl@v3.1
103 | id: determineArtifactUrl
104 | with:
105 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
106 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
107 | project: ${{ matrix.project }}
108 | settingsJson: ${{ env.Settings }}
109 | secretsJson: ${{ env.RepoSecrets }}
110 |
111 | - name: Run pipeline
112 | uses: microsoft/AL-Go-Actions/RunPipeline@v3.1
113 | env:
114 | BuildMode: ${{ matrix.buildMode }}
115 | with:
116 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
117 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
118 | project: ${{ matrix.project }}
119 | projectDependenciesJson: ${{ needs.Initialization.outputs.projectDependenciesJson }}
120 | settingsJson: ${{ env.Settings }}
121 | secretsJson: ${{ env.RepoSecrets }}
122 | buildMode: ${{ matrix.buildMode }}
123 |
124 | - name: Calculate Artifact names
125 | id: calculateArtifactsNames
126 | uses: microsoft/AL-Go-Actions/CalculateArtifactNames@v3.1
127 | if: success() || failure()
128 | with:
129 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
130 | settingsJson: ${{ env.Settings }}
131 | project: ${{ matrix.project }}
132 | buildMode: ${{ matrix.buildMode }}
133 | branchName: ${{ github.ref_name }}
134 | suffix: 'Current'
135 |
136 | - name: Upload thisbuild artifacts - apps
137 | if: env.workflowDepth > 1
138 | uses: actions/upload-artifact@v3
139 | with:
140 | name: ${{ steps.calculateArtifactsNames.outputs.ThisBuildAppsArtifactsName }}
141 | path: '${{ matrix.project }}/.buildartifacts/Apps/'
142 | if-no-files-found: ignore
143 | retention-days: 1
144 |
145 | - name: Upload thisbuild artifacts - test apps
146 | if: env.workflowDepth > 1
147 | uses: actions/upload-artifact@v3
148 | with:
149 | name: ${{ steps.calculateArtifactsNames.outputs.ThisBuildTestAppsArtifactsName }}
150 | path: '${{ matrix.project }}/.buildartifacts/TestApps/'
151 | if-no-files-found: ignore
152 | retention-days: 1
153 |
154 | - name: Publish artifacts - build output
155 | uses: actions/upload-artifact@v3
156 | if: (success() || failure()) && (hashFiles(format('{0}/BuildOutput.txt',matrix.project)) != '')
157 | with:
158 | name: ${{ env.buildOutputArtifactsName }}
159 | path: '${{ matrix.project }}/BuildOutput.txt'
160 | if-no-files-found: ignore
161 |
162 | - name: Publish artifacts - container event log
163 | uses: actions/upload-artifact@v3
164 | if: (failure()) && (hashFiles(format('{0}/ContainerEventLog.evtx',matrix.project)) != '')
165 | with:
166 | name: ${{ env.ContainerEventLogArtifactsName }}
167 | path: '${{ matrix.project }}/ContainerEventLog.evtx'
168 | if-no-files-found: ignore
169 |
170 | - name: Publish artifacts - test results
171 | uses: actions/upload-artifact@v3
172 | if: (success() || failure()) && (hashFiles(format('{0}/TestResults.xml',matrix.project)) != '')
173 | with:
174 | name: ${{ env.TestResultsArtifactsName }}
175 | path: '${{ matrix.project }}/TestResults.xml'
176 | if-no-files-found: ignore
177 |
178 | - name: Publish artifacts - bcpt test results
179 | uses: actions/upload-artifact@v3
180 | if: (success() || failure()) && (hashFiles(format('{0}/bcptTestResults.json',matrix.project)) != '')
181 | with:
182 | name: ${{ env.BcptTestResultsArtifactsName }}
183 | path: '${{ matrix.project }}/bcptTestResults.json'
184 | if-no-files-found: ignore
185 |
186 | - name: Analyze Test Results
187 | id: analyzeTestResults
188 | if: success() || failure()
189 | uses: microsoft/AL-Go-Actions/AnalyzeTests@v3.1
190 | with:
191 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
192 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
193 | Project: ${{ matrix.project }}
194 |
195 | - name: Cleanup
196 | if: always()
197 | uses: microsoft/AL-Go-Actions/PipelineCleanup@v3.1
198 | with:
199 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
200 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
201 | Project: ${{ matrix.project }}
202 |
203 | PostProcess:
204 | if: always()
205 | runs-on: [ windows-latest ]
206 | needs: [ Initialization, Build ]
207 | steps:
208 | - name: Checkout
209 | uses: actions/checkout@v3
210 |
211 | - name: Finalize the workflow
212 | id: PostProcess
213 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
214 | with:
215 | shell: powershell
216 | eventId: "DO0101"
217 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
218 |
--------------------------------------------------------------------------------
/.github/workflows/NextMajor.yaml:
--------------------------------------------------------------------------------
1 | name: ' Test Next Major'
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | contents: read
8 |
9 | defaults:
10 | run:
11 | shell: powershell
12 |
13 | env:
14 | workflowDepth: 1
15 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
16 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
17 |
18 | jobs:
19 | Initialization:
20 | runs-on: [ windows-latest ]
21 | outputs:
22 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
23 | settings: ${{ steps.ReadSettings.outputs.SettingsJson }}
24 | githubRunner: ${{ steps.ReadSettings.outputs.GitHubRunnerJson }}
25 | githubRunnerShell: ${{ steps.ReadSettings.outputs.GitHubRunnerShell }}
26 | projects: ${{ steps.determineProjectsToBuild.outputs.ProjectsJson }}
27 | projectDependenciesJson: ${{ steps.determineProjectsToBuild.outputs.ProjectDependenciesJson }}
28 | buildOrderJson: ${{ steps.determineProjectsToBuild.outputs.BuildOrderJson }}
29 | steps:
30 | - name: Checkout
31 | uses: actions/checkout@v3
32 | with:
33 | lfs: true
34 |
35 | - name: Initialize the workflow
36 | id: init
37 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
38 | with:
39 | shell: powershell
40 | eventId: "DO0099"
41 |
42 | - name: Read settings
43 | id: ReadSettings
44 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
45 | with:
46 | shell: powershell
47 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
48 |
49 | - name: Determine Projects To Build
50 | id: determineProjectsToBuild
51 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v3.1
52 | with:
53 | shell: powershell
54 | maxBuildDepth: ${{ env.workflowDepth }}
55 |
56 | Build:
57 | needs: [ Initialization ]
58 | if: (!failure()) && (!cancelled()) && fromJson(needs.Initialization.outputs.buildOrderJson)[0].projectsCount > 0
59 | runs-on: ${{ fromJson(needs.Initialization.outputs.githubRunner) }}
60 | defaults:
61 | run:
62 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
63 | strategy:
64 | matrix:
65 | include: ${{ fromJson(needs.Initialization.outputs.buildOrderJson)[0].buildDimensions }}
66 | fail-fast: false
67 | name: Build ${{ matrix.project }} - ${{ matrix.buildMode }}
68 | outputs:
69 | TestResultsArtifactsName: ${{ steps.calculateArtifactNames.outputs.TestResultsArtifactsName }}
70 | BcptTestResultsArtifactsName: ${{ steps.calculateArtifactNames.outputs.BcptTestResultsArtifactsName }}
71 | BuildOutputArtifactsName: ${{ steps.calculateArtifactNames.outputs.BuildOutputArtifactsName }}
72 | steps:
73 | - name: Checkout
74 | uses: actions/checkout@v3
75 | with:
76 | lfs: true
77 |
78 | - name: Download thisbuild artifacts
79 | if: env.workflowDepth > 1
80 | uses: actions/download-artifact@v3
81 | with:
82 | path: '${{ github.workspace }}\.dependencies'
83 |
84 | - name: Read settings
85 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
86 | with:
87 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
88 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
89 | project: ${{ matrix.project }}
90 |
91 | - name: Read secrets
92 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
93 | env:
94 | secrets: ${{ toJson(secrets) }}
95 | with:
96 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
97 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
98 | settingsJson: ${{ env.Settings }}
99 | secrets: 'licenseFileUrl,insiderSasToken,codeSignCertificateUrl,codeSignCertificatePassword,keyVaultCertificateUrl,keyVaultCertificatePassword,keyVaultClientId,gitHubPackagesContext'
100 |
101 | - name: Determine ArtifactUrl
102 | uses: microsoft/AL-Go-Actions/DetermineArtifactUrl@v3.1
103 | id: determineArtifactUrl
104 | with:
105 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
106 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
107 | project: ${{ matrix.project }}
108 | settingsJson: ${{ env.Settings }}
109 | secretsJson: ${{ env.RepoSecrets }}
110 |
111 | - name: Run pipeline
112 | uses: microsoft/AL-Go-Actions/RunPipeline@v3.1
113 | env:
114 | BuildMode: ${{ matrix.buildMode }}
115 | with:
116 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
117 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
118 | project: ${{ matrix.project }}
119 | projectDependenciesJson: ${{ needs.Initialization.outputs.projectDependenciesJson }}
120 | settingsJson: ${{ env.Settings }}
121 | secretsJson: ${{ env.RepoSecrets }}
122 | buildMode: ${{ matrix.buildMode }}
123 |
124 | - name: Calculate Artifact names
125 | id: calculateArtifactsNames
126 | uses: microsoft/AL-Go-Actions/CalculateArtifactNames@v3.1
127 | if: success() || failure()
128 | with:
129 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
130 | settingsJson: ${{ env.Settings }}
131 | project: ${{ matrix.project }}
132 | buildMode: ${{ matrix.buildMode }}
133 | branchName: ${{ github.ref_name }}
134 | suffix: 'NextMajor'
135 |
136 | - name: Upload thisbuild artifacts - apps
137 | if: env.workflowDepth > 1
138 | uses: actions/upload-artifact@v3
139 | with:
140 | name: ${{ steps.calculateArtifactsNames.outputs.ThisBuildAppsArtifactsName }}
141 | path: '${{ matrix.project }}/.buildartifacts/Apps/'
142 | if-no-files-found: ignore
143 | retention-days: 1
144 |
145 | - name: Upload thisbuild artifacts - test apps
146 | if: env.workflowDepth > 1
147 | uses: actions/upload-artifact@v3
148 | with:
149 | name: ${{ steps.calculateArtifactsNames.outputs.ThisBuildTestAppsArtifactsName }}
150 | path: '${{ matrix.project }}/.buildartifacts/TestApps/'
151 | if-no-files-found: ignore
152 | retention-days: 1
153 |
154 | - name: Publish artifacts - build output
155 | uses: actions/upload-artifact@v3
156 | if: (success() || failure()) && (hashFiles(format('{0}/BuildOutput.txt',matrix.project)) != '')
157 | with:
158 | name: ${{ env.buildOutputArtifactsName }}
159 | path: '${{ matrix.project }}/BuildOutput.txt'
160 | if-no-files-found: ignore
161 |
162 | - name: Publish artifacts - container event log
163 | uses: actions/upload-artifact@v3
164 | if: (failure()) && (hashFiles(format('{0}/ContainerEventLog.evtx',matrix.project)) != '')
165 | with:
166 | name: ${{ env.ContainerEventLogArtifactsName }}
167 | path: '${{ matrix.project }}/ContainerEventLog.evtx'
168 | if-no-files-found: ignore
169 |
170 | - name: Publish artifacts - test results
171 | uses: actions/upload-artifact@v3
172 | if: (success() || failure()) && (hashFiles(format('{0}/TestResults.xml',matrix.project)) != '')
173 | with:
174 | name: ${{ env.TestResultsArtifactsName }}
175 | path: '${{ matrix.project }}/TestResults.xml'
176 | if-no-files-found: ignore
177 |
178 | - name: Publish artifacts - bcpt test results
179 | uses: actions/upload-artifact@v3
180 | if: (success() || failure()) && (hashFiles(format('{0}/bcptTestResults.json',matrix.project)) != '')
181 | with:
182 | name: ${{ env.BcptTestResultsArtifactsName }}
183 | path: '${{ matrix.project }}/bcptTestResults.json'
184 | if-no-files-found: ignore
185 |
186 | - name: Analyze Test Results
187 | id: analyzeTestResults
188 | if: success() || failure()
189 | uses: microsoft/AL-Go-Actions/AnalyzeTests@v3.1
190 | with:
191 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
192 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
193 | Project: ${{ matrix.project }}
194 |
195 | - name: Cleanup
196 | if: always()
197 | uses: microsoft/AL-Go-Actions/PipelineCleanup@v3.1
198 | with:
199 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
200 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
201 | Project: ${{ matrix.project }}
202 |
203 | PostProcess:
204 | if: always()
205 | runs-on: [ windows-latest ]
206 | needs: [ Initialization, Build ]
207 | steps:
208 | - name: Checkout
209 | uses: actions/checkout@v3
210 |
211 | - name: Finalize the workflow
212 | id: PostProcess
213 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
214 | with:
215 | shell: powershell
216 | eventId: "DO0099"
217 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
218 |
--------------------------------------------------------------------------------
/.github/workflows/NextMinor.yaml:
--------------------------------------------------------------------------------
1 | name: ' Test Next Minor'
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | permissions:
7 | contents: read
8 |
9 | defaults:
10 | run:
11 | shell: powershell
12 |
13 | env:
14 | workflowDepth: 1
15 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
16 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
17 |
18 | jobs:
19 | Initialization:
20 | runs-on: [ windows-latest ]
21 | outputs:
22 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
23 | settings: ${{ steps.ReadSettings.outputs.SettingsJson }}
24 | githubRunner: ${{ steps.ReadSettings.outputs.GitHubRunnerJson }}
25 | githubRunnerShell: ${{ steps.ReadSettings.outputs.GitHubRunnerShell }}
26 | projects: ${{ steps.determineProjectsToBuild.outputs.ProjectsJson }}
27 | projectDependenciesJson: ${{ steps.determineProjectsToBuild.outputs.ProjectDependenciesJson }}
28 | buildOrderJson: ${{ steps.determineProjectsToBuild.outputs.BuildOrderJson }}
29 | steps:
30 | - name: Checkout
31 | uses: actions/checkout@v3
32 | with:
33 | lfs: true
34 |
35 | - name: Initialize the workflow
36 | id: init
37 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
38 | with:
39 | shell: powershell
40 | eventId: "DO0100"
41 |
42 | - name: Read settings
43 | id: ReadSettings
44 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
45 | with:
46 | shell: powershell
47 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
48 |
49 | - name: Determine Projects To Build
50 | id: determineProjectsToBuild
51 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v3.1
52 | with:
53 | shell: powershell
54 | maxBuildDepth: ${{ env.workflowDepth }}
55 |
56 | Build:
57 | needs: [ Initialization ]
58 | if: (!failure()) && (!cancelled()) && fromJson(needs.Initialization.outputs.buildOrderJson)[0].projectsCount > 0
59 | runs-on: ${{ fromJson(needs.Initialization.outputs.githubRunner) }}
60 | defaults:
61 | run:
62 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
63 | strategy:
64 | matrix:
65 | include: ${{ fromJson(needs.Initialization.outputs.buildOrderJson)[0].buildDimensions }}
66 | fail-fast: false
67 | name: Build ${{ matrix.project }} - ${{ matrix.buildMode }}
68 | outputs:
69 | TestResultsArtifactsName: ${{ steps.calculateArtifactNames.outputs.TestResultsArtifactsName }}
70 | BcptTestResultsArtifactsName: ${{ steps.calculateArtifactNames.outputs.BcptTestResultsArtifactsName }}
71 | BuildOutputArtifactsName: ${{ steps.calculateArtifactNames.outputs.BuildOutputArtifactsName }}
72 | steps:
73 | - name: Checkout
74 | uses: actions/checkout@v3
75 | with:
76 | lfs: true
77 |
78 | - name: Download thisbuild artifacts
79 | if: env.workflowDepth > 1
80 | uses: actions/download-artifact@v3
81 | with:
82 | path: '${{ github.workspace }}\.dependencies'
83 |
84 | - name: Read settings
85 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
86 | with:
87 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
88 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
89 | project: ${{ matrix.project }}
90 |
91 | - name: Read secrets
92 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
93 | env:
94 | secrets: ${{ toJson(secrets) }}
95 | with:
96 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
97 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
98 | settingsJson: ${{ env.Settings }}
99 | secrets: 'licenseFileUrl,insiderSasToken,codeSignCertificateUrl,codeSignCertificatePassword,keyVaultCertificateUrl,keyVaultCertificatePassword,keyVaultClientId,gitHubPackagesContext'
100 |
101 | - name: Determine ArtifactUrl
102 | uses: microsoft/AL-Go-Actions/DetermineArtifactUrl@v3.1
103 | id: determineArtifactUrl
104 | with:
105 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
106 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
107 | project: ${{ matrix.project }}
108 | settingsJson: ${{ env.Settings }}
109 | secretsJson: ${{ env.RepoSecrets }}
110 |
111 | - name: Run pipeline
112 | uses: microsoft/AL-Go-Actions/RunPipeline@v3.1
113 | env:
114 | BuildMode: ${{ matrix.buildMode }}
115 | with:
116 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
117 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
118 | project: ${{ matrix.project }}
119 | projectDependenciesJson: ${{ needs.Initialization.outputs.projectDependenciesJson }}
120 | settingsJson: ${{ env.Settings }}
121 | secretsJson: ${{ env.RepoSecrets }}
122 | buildMode: ${{ matrix.buildMode }}
123 |
124 | - name: Calculate Artifact names
125 | id: calculateArtifactsNames
126 | uses: microsoft/AL-Go-Actions/CalculateArtifactNames@v3.1
127 | if: success() || failure()
128 | with:
129 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
130 | settingsJson: ${{ env.Settings }}
131 | project: ${{ matrix.project }}
132 | buildMode: ${{ matrix.buildMode }}
133 | branchName: ${{ github.ref_name }}
134 | suffix: 'NextMinor'
135 |
136 | - name: Upload thisbuild artifacts - apps
137 | if: env.workflowDepth > 1
138 | uses: actions/upload-artifact@v3
139 | with:
140 | name: ${{ steps.calculateArtifactsNames.outputs.ThisBuildAppsArtifactsName }}
141 | path: '${{ matrix.project }}/.buildartifacts/Apps/'
142 | if-no-files-found: ignore
143 | retention-days: 1
144 |
145 | - name: Upload thisbuild artifacts - test apps
146 | if: env.workflowDepth > 1
147 | uses: actions/upload-artifact@v3
148 | with:
149 | name: ${{ steps.calculateArtifactsNames.outputs.ThisBuildTestAppsArtifactsName }}
150 | path: '${{ matrix.project }}/.buildartifacts/TestApps/'
151 | if-no-files-found: ignore
152 | retention-days: 1
153 |
154 | - name: Publish artifacts - build output
155 | uses: actions/upload-artifact@v3
156 | if: (success() || failure()) && (hashFiles(format('{0}/BuildOutput.txt',matrix.project)) != '')
157 | with:
158 | name: ${{ env.buildOutputArtifactsName }}
159 | path: '${{ matrix.project }}/BuildOutput.txt'
160 | if-no-files-found: ignore
161 |
162 | - name: Publish artifacts - container event log
163 | uses: actions/upload-artifact@v3
164 | if: (failure()) && (hashFiles(format('{0}/ContainerEventLog.evtx',matrix.project)) != '')
165 | with:
166 | name: ${{ env.ContainerEventLogArtifactsName }}
167 | path: '${{ matrix.project }}/ContainerEventLog.evtx'
168 | if-no-files-found: ignore
169 |
170 | - name: Publish artifacts - test results
171 | uses: actions/upload-artifact@v3
172 | if: (success() || failure()) && (hashFiles(format('{0}/TestResults.xml',matrix.project)) != '')
173 | with:
174 | name: ${{ env.TestResultsArtifactsName }}
175 | path: '${{ matrix.project }}/TestResults.xml'
176 | if-no-files-found: ignore
177 |
178 | - name: Publish artifacts - bcpt test results
179 | uses: actions/upload-artifact@v3
180 | if: (success() || failure()) && (hashFiles(format('{0}/bcptTestResults.json',matrix.project)) != '')
181 | with:
182 | name: ${{ env.BcptTestResultsArtifactsName }}
183 | path: '${{ matrix.project }}/bcptTestResults.json'
184 | if-no-files-found: ignore
185 |
186 | - name: Analyze Test Results
187 | id: analyzeTestResults
188 | if: success() || failure()
189 | uses: microsoft/AL-Go-Actions/AnalyzeTests@v3.1
190 | with:
191 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
192 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
193 | Project: ${{ matrix.project }}
194 |
195 | - name: Cleanup
196 | if: always()
197 | uses: microsoft/AL-Go-Actions/PipelineCleanup@v3.1
198 | with:
199 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
200 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
201 | Project: ${{ matrix.project }}
202 |
203 | PostProcess:
204 | if: always()
205 | runs-on: [ windows-latest ]
206 | needs: [ Initialization, Build ]
207 | steps:
208 | - name: Checkout
209 | uses: actions/checkout@v3
210 |
211 | - name: Finalize the workflow
212 | id: PostProcess
213 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
214 | with:
215 | shell: powershell
216 | eventId: "DO0100"
217 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
218 |
--------------------------------------------------------------------------------
/.github/workflows/PullRequestHandler.yaml:
--------------------------------------------------------------------------------
1 | name: 'Pull Request Build'
2 |
3 | on:
4 | pull_request_target:
5 | paths-ignore:
6 | - '**.md'
7 | branches: [ 'main' ]
8 |
9 | concurrency:
10 | group: ${{ github.workflow }}-${{ github.event.pull_request.number }}
11 | cancel-in-progress: true
12 |
13 | defaults:
14 | run:
15 | shell: powershell
16 |
17 | permissions:
18 | contents: read
19 | actions: read
20 | pull-requests: read
21 |
22 | env:
23 | workflowDepth: 1
24 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
25 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
26 |
27 | jobs:
28 | PregateCheck:
29 | if: github.event.pull_request.base.repo.full_name != github.event.pull_request.head.repo.full_name
30 | runs-on: [ windows-latest ]
31 | steps:
32 | - uses: actions/checkout@v3
33 | with:
34 | lfs: true
35 | ref: refs/pull/${{ github.event.number }}/merge
36 |
37 | - uses: microsoft/AL-Go-Actions/VerifyPRChanges@v3.1
38 | with:
39 | baseSHA: ${{ github.event.pull_request.base.sha }}
40 | headSHA: ${{ github.event.pull_request.head.sha }}
41 | prbaseRepository: ${{ github.event.pull_request.base.repo.full_name }}
42 |
43 | Initialization:
44 | needs: [ PregateCheck ]
45 | if: (!failure() && !cancelled())
46 | runs-on: [ windows-latest ]
47 | outputs:
48 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
49 | settings: ${{ steps.ReadSettings.outputs.SettingsJson }}
50 | githubRunner: ${{ steps.ReadSettings.outputs.GitHubRunnerJson }}
51 | githubRunnerShell: ${{ steps.ReadSettings.outputs.GitHubRunnerShell }}
52 | projects: ${{ steps.determineProjectsToBuild.outputs.ProjectsJson }}
53 | projectDependenciesJson: ${{ steps.determineProjectsToBuild.outputs.ProjectDependenciesJson }}
54 | buildOrderJson: ${{ steps.determineProjectsToBuild.outputs.BuildOrderJson }}
55 | steps:
56 | - name: Checkout
57 | uses: actions/checkout@v3
58 | with:
59 | lfs: true
60 | ref: refs/pull/${{ github.event.number }}/merge
61 |
62 | - name: Initialize the workflow
63 | id: init
64 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
65 | with:
66 | shell: powershell
67 | eventId: "DO0104"
68 |
69 | - name: Read settings
70 | id: ReadSettings
71 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
72 | with:
73 | shell: powershell
74 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
75 | getEnvironments: '*'
76 |
77 | - name: Determine Projects To Build
78 | id: determineProjectsToBuild
79 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v3.1
80 | with:
81 | shell: powershell
82 | maxBuildDepth: ${{ env.workflowDepth }}
83 |
84 | Build:
85 | needs: [ Initialization ]
86 | if: (!failure()) && (!cancelled()) && fromJson(needs.Initialization.outputs.buildOrderJson)[0].projectsCount > 0
87 | runs-on: ${{ fromJson(needs.Initialization.outputs.githubRunner) }}
88 | defaults:
89 | run:
90 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
91 | strategy:
92 | matrix:
93 | include: ${{ fromJson(needs.Initialization.outputs.buildOrderJson)[0].buildDimensions }}
94 | fail-fast: false
95 | name: Build ${{ matrix.project }} - ${{ matrix.buildMode }}
96 | outputs:
97 | AppsArtifactsName: ${{ steps.calculateArtifactNames.outputs.AppsArtifactsName }}
98 | TestAppsArtifactsName: ${{ steps.calculateArtifactNames.outputs.TestAppsArtifactsName }}
99 | TestResultsArtifactsName: ${{ steps.calculateArtifactNames.outputs.TestResultsArtifactsName }}
100 | BcptTestResultsArtifactsName: ${{ steps.calculateArtifactNames.outputs.BcptTestResultsArtifactsName }}
101 | BuildOutputArtifactsName: ${{ steps.calculateArtifactNames.outputs.BuildOutputArtifactsName }}
102 | steps:
103 | - name: Checkout
104 | uses: actions/checkout@v3
105 | with:
106 | lfs: true
107 | ref: refs/pull/${{ github.event.number }}/merge
108 |
109 | - name: Download thisbuild artifacts
110 | if: env.workflowDepth > 1
111 | uses: actions/download-artifact@v3
112 | with:
113 | path: '.dependencies'
114 |
115 | - name: Read settings
116 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
117 | with:
118 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
119 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
120 | project: ${{ matrix.project }}
121 |
122 | - name: Read secrets
123 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
124 | env:
125 | secrets: ${{ toJson(secrets) }}
126 | with:
127 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
128 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
129 | settingsJson: ${{ env.Settings }}
130 | secrets: 'licenseFileUrl,insiderSasToken,keyVaultCertificateUrl,keyVaultCertificatePassword,keyVaultClientId,gitHubPackagesContext'
131 |
132 | - name: Determine ArtifactUrl
133 | uses: microsoft/AL-Go-Actions/DetermineArtifactUrl@v3.1
134 | id: determineArtifactUrl
135 | with:
136 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
137 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
138 | project: ${{ matrix.project }}
139 | settingsJson: ${{ env.Settings }}
140 | secretsJson: ${{ env.RepoSecrets }}
141 |
142 | - name: Cache Business Central Artifacts
143 | if: env.useCompilerFolder == 'True' && steps.determineArtifactUrl.outputs.ArtifactUrl && !contains(env.artifact,'INSIDERSASTOKEN')
144 | uses: actions/cache@v3
145 | with:
146 | path: .artifactcache
147 | key: ${{ steps.determineArtifactUrl.outputs.ArtifactUrl }}
148 |
149 | - name: Run pipeline
150 | id: RunPipeline
151 | uses: microsoft/AL-Go-Actions/RunPipeline@v3.1
152 | env:
153 | BuildMode: ${{ matrix.buildMode }}
154 | with:
155 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
156 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
157 | project: ${{ matrix.project }}
158 | projectDependenciesJson: ${{ needs.Initialization.outputs.projectDependenciesJson }}
159 | settingsJson: ${{ env.Settings }}
160 | secretsJson: ${{ env.RepoSecrets }}
161 | buildMode: ${{ matrix.buildMode }}
162 |
163 | - name: Calculate Artifact names
164 | id: calculateArtifactsNames
165 | uses: microsoft/AL-Go-Actions/CalculateArtifactNames@v3.1
166 | if: success() || failure()
167 | with:
168 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
169 | settingsJson: ${{ env.Settings }}
170 | project: ${{ matrix.project }}
171 | buildMode: ${{ matrix.buildMode }}
172 | branchName: ${{ github.ref_name }}
173 |
174 | - name: Upload thisbuild artifacts - apps
175 | if: env.workflowDepth > 1
176 | uses: actions/upload-artifact@v3
177 | with:
178 | name: ${{ steps.calculateArtifactsNames.outputs.ThisBuildAppsArtifactsName }}
179 | path: '${{ matrix.project }}/.buildartifacts/Apps/'
180 | if-no-files-found: ignore
181 | retention-days: 1
182 |
183 | - name: Upload thisbuild artifacts - test apps
184 | if: env.workflowDepth > 1
185 | uses: actions/upload-artifact@v3
186 | with:
187 | name: ${{ steps.calculateArtifactsNames.outputs.ThisBuildTestAppsArtifactsName }}
188 | path: '${{ matrix.project }}/.buildartifacts/TestApps/'
189 | if-no-files-found: ignore
190 | retention-days: 1
191 |
192 | - name: Publish artifacts - build output
193 | uses: actions/upload-artifact@v3
194 | if: (success() || failure()) && (hashFiles(format('{0}/BuildOutput.txt',matrix.project)) != '')
195 | with:
196 | name: ${{ env.BuildOutputArtifactsName }}
197 | path: '${{ matrix.project }}/BuildOutput.txt'
198 | if-no-files-found: ignore
199 |
200 | - name: Publish artifacts - container event log
201 | uses: actions/upload-artifact@v3
202 | if: (failure()) && (hashFiles(format('{0}/ContainerEventLog.evtx',matrix.project)) != '')
203 | with:
204 | name: ${{ env.ContainerEventLogArtifactsName }}
205 | path: '${{ matrix.project }}/ContainerEventLog.evtx'
206 | if-no-files-found: ignore
207 |
208 | - name: Publish artifacts - test results
209 | uses: actions/upload-artifact@v3
210 | if: (success() || failure()) && (hashFiles(format('{0}/TestResults.xml',matrix.project)) != '')
211 | with:
212 | name: ${{ env.TestResultsArtifactsName }}
213 | path: '${{ matrix.project }}/TestResults.xml'
214 | if-no-files-found: ignore
215 |
216 | - name: Publish artifacts - bcpt test results
217 | uses: actions/upload-artifact@v3
218 | if: (success() || failure()) && (hashFiles(format('{0}/bcptTestResults.json',matrix.project)) != '')
219 | with:
220 | name: ${{ env.BcptTestResultsArtifactsName }}
221 | path: '${{ matrix.project }}/bcptTestResults.json'
222 | if-no-files-found: ignore
223 |
224 | - name: Analyze Test Results
225 | id: analyzeTestResults
226 | if: success() || failure()
227 | uses: microsoft/AL-Go-Actions/AnalyzeTests@v3.1
228 | with:
229 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
230 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
231 | Project: ${{ matrix.project }}
232 |
233 | - name: Cleanup
234 | if: always()
235 | uses: microsoft/AL-Go-Actions/PipelineCleanup@v3.1
236 | with:
237 | shell: ${{ needs.Initialization.outputs.githubRunnerShell }}
238 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
239 | Project: ${{ matrix.project }}
240 |
241 | PostProcess:
242 | runs-on: [ windows-latest ]
243 | needs: [ Initialization, Build ]
244 | if: (!cancelled())
245 | steps:
246 | - name: Checkout
247 | uses: actions/checkout@v3
248 | with:
249 | lfs: true
250 | ref: refs/pull/${{ github.event.number }}/merge
251 |
252 | - name: Finalize the workflow
253 | id: PostProcess
254 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
255 | with:
256 | shell: powershell
257 | eventId: "DO0104"
258 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
259 |
--------------------------------------------------------------------------------
/.github/workflows/PublishToEnvironment.yaml:
--------------------------------------------------------------------------------
1 | name: ' Publish To Environment'
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | appVersion:
7 | description: App version to deploy to environment(s) (current, prerelease, draft, latest or version number)
8 | required: false
9 | default: 'current'
10 | environmentName:
11 | description: Environment mask to receive the new version (* for all, PROD* for all environments starting with PROD)
12 | required: true
13 |
14 | permissions:
15 | contents: read
16 | actions: read
17 |
18 | defaults:
19 | run:
20 | shell: powershell
21 |
22 | env:
23 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
24 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
25 |
26 | jobs:
27 | Initialization:
28 | runs-on: [ windows-latest ]
29 | outputs:
30 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
31 | settings: ${{ steps.ReadSettings.outputs.SettingsJson }}
32 | environments: ${{ steps.ReadSettings.outputs.EnvironmentsJson }}
33 | environmentCount: ${{ steps.ReadSettings.outputs.EnvironmentCount }}
34 | unknownEnvironment: ${{ steps.ReadSettings.outputs.UnknownEnvironment }}
35 | deviceCode: ${{ steps.Authenticate.outputs.deviceCode }}
36 | steps:
37 | - name: Checkout
38 | uses: actions/checkout@v3
39 |
40 | - name: Initialize the workflow
41 | id: init
42 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
43 | with:
44 | shell: powershell
45 | eventId: "DO0097"
46 |
47 | - name: Read settings
48 | id: ReadSettings
49 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
50 | with:
51 | shell: powershell
52 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
53 | getEnvironments: ${{ github.event.inputs.environmentName }}
54 | includeProduction: 'Y'
55 |
56 | - name: EnvName
57 | id: envName
58 | if: steps.ReadSettings.outputs.UnknownEnvironment == 1
59 | run: |
60 | $ErrorActionPreference = "STOP"
61 | Set-StrictMode -version 2.0
62 | $envName = '${{ fromJson(steps.ReadSettings.outputs.environmentsJson).matrix.include[0].environment }}'.split(' ')[0]
63 | Add-Content -Path $env:GITHUB_OUTPUT -Value "envName=$envName"
64 |
65 | - name: Read secrets
66 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
67 | if: steps.ReadSettings.outputs.UnknownEnvironment == 1
68 | env:
69 | secrets: ${{ toJson(secrets) }}
70 | with:
71 | shell: powershell
72 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
73 | settingsJson: ${{ env.Settings }}
74 | secrets: '${{ steps.envName.outputs.envName }}-AuthContext,${{ steps.envName.outputs.envName }}_AuthContext,AuthContext'
75 |
76 | - name: Authenticate
77 | id: Authenticate
78 | if: steps.ReadSettings.outputs.UnknownEnvironment == 1
79 | run: |
80 | $envName = '${{ steps.envName.outputs.envName }}'
81 | $secretName = ''
82 | $authContext = $null
83 | "$($envName)-AuthContext", "$($envName)_AuthContext", "AuthContext" | ForEach-Object {
84 | if (!($authContext)) {
85 | $authContext = [System.Environment]::GetEnvironmentVariable($_)
86 | if ($authContext) {
87 | Write-Host "Using $_ secret as AuthContext"
88 | $secretName = $_
89 | }
90 | }
91 | }
92 | if ($authContext) {
93 | Write-Host "AuthContext provided in secret $secretName!"
94 | Set-Content -Path $ENV:GITHUB_STEP_SUMMARY -value "AuthContext was provided in a secret called $secretName. Using this information for authentication."
95 | }
96 | else {
97 | Write-Host "No AuthContext provided for $envName, initiating Device Code flow"
98 | $ALGoHelperPath = "$([System.IO.Path]::GetTempFileName()).ps1"
99 | $webClient = New-Object System.Net.WebClient
100 | $webClient.DownloadFile('https://raw.githubusercontent.com/microsoft/AL-Go-Actions/v3.1/AL-Go-Helper.ps1', $ALGoHelperPath)
101 | . $ALGoHelperPath
102 | $BcContainerHelperPath = DownloadAndImportBcContainerHelper -baseFolder $ENV:GITHUB_WORKSPACE
103 | $authContext = New-BcAuthContext -includeDeviceLogin -deviceLoginTimeout ([TimeSpan]::FromSeconds(0))
104 | CleanupAfterBcContainerHelper -bcContainerHelperPath $bcContainerHelperPath
105 | Set-Content -Path $ENV:GITHUB_STEP_SUMMARY -value "AL-Go needs access to the Business Central Environment $('${{ steps.envName.outputs.envName }}'.Split(' ')[0]) and could not locate a secret called ${{ steps.envName.outputs.envName }}_AuthContext`n`n$($authContext.message)"
106 | Add-Content -Path $env:GITHUB_OUTPUT -Value "deviceCode=$($authContext.deviceCode)"
107 | }
108 |
109 | Deploy:
110 | needs: [ Initialization ]
111 | if: needs.Initialization.outputs.environmentCount > 0
112 | strategy: ${{ fromJson(needs.Initialization.outputs.environments) }}
113 | runs-on: ${{ fromJson(matrix.os) }}
114 | name: Deploy to ${{ matrix.environment }}
115 | environment:
116 | name: ${{ matrix.environment }}
117 | env:
118 | deviceCode: ${{ needs.Initialization.outputs.deviceCode }}
119 | steps:
120 | - name: Checkout
121 | uses: actions/checkout@v3
122 |
123 | - name: EnvName
124 | id: envName
125 | run: |
126 | $ErrorActionPreference = "STOP"
127 | Set-StrictMode -version 2.0
128 | $envName = '${{ matrix.environment }}'.split(' ')[0]
129 | Add-Content -Path $env:GITHUB_OUTPUT -Value "envName=$envName"
130 |
131 | - name: Read settings
132 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
133 | with:
134 | shell: powershell
135 |
136 | - name: Read secrets
137 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
138 | env:
139 | secrets: ${{ toJson(secrets) }}
140 | with:
141 | shell: powershell
142 | settingsJson: ${{ env.Settings }}
143 | secrets: '${{ steps.envName.outputs.envName }}-AuthContext,${{ steps.envName.outputs.envName }}_AuthContext,AuthContext,${{ steps.envName.outputs.envName }}-EnvironmentName,${{ steps.envName.outputs.envName }}_EnvironmentName,EnvironmentName,projects'
144 |
145 | - name: AuthContext
146 | id: authContext
147 | run: |
148 | $ErrorActionPreference = "STOP"
149 | Set-StrictMode -version 2.0
150 | $envName = '${{ steps.envName.outputs.envName }}'
151 | $deployToSettingStr = [System.Environment]::GetEnvironmentVariable("DeployTo$envName")
152 | if ($deployToSettingStr) {
153 | $deployToSetting = $deployToSettingStr | ConvertFrom-Json
154 | }
155 | else {
156 | $deployToSetting = [PSCustomObject]@{}
157 | }
158 | $authContext = $null
159 | if ($env:deviceCode) {
160 | $authContext = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes("{""deviceCode"":""$($env:deviceCode)""}"))
161 | }
162 | "$($envName)-AuthContext", "$($envName)_AuthContext", "AuthContext" | ForEach-Object {
163 | if (!($authContext)) {
164 | $authContext = [System.Environment]::GetEnvironmentVariable($_)
165 | if ($authContext) {
166 | Write-Host "Using $_ secret as AuthContext"
167 | }
168 | }
169 | }
170 | if (!($authContext)) {
171 | Write-Host "::Error::No AuthContext provided"
172 | exit 1
173 | }
174 | if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "EnvironmentName") {
175 | $environmentName = $deployToSetting.EnvironmentName
176 | }
177 | else {
178 | $environmentName = $null
179 | "$($envName)-EnvironmentName", "$($envName)_EnvironmentName", "EnvironmentName" | ForEach-Object {
180 | if (!($EnvironmentName)) {
181 | $EnvironmentName = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String([System.Environment]::GetEnvironmentVariable($_)))
182 | if ($EnvironmentName) {
183 | Write-Host "Using $_ secret as EnvironmentName"
184 | Write-Host "Please consider using the DeployTo$_ setting instead, where you can specify EnvironmentName, projects and branches"
185 | }
186 | }
187 | }
188 | }
189 | if (!($environmentName)) {
190 | $environmentName = '${{ steps.envName.outputs.envName }}'
191 | }
192 | $environmentName = [Convert]::ToBase64String([System.Text.Encoding]::UTF8.GetBytes(($environmentName + '${{ matrix.environment }}'.SubString($envName.Length)).ToUpperInvariant()))
193 | if (("$deployToSetting" -ne "") -and $deployToSetting.PSObject.Properties.name -eq "projects") {
194 | $projects = $deployToSetting.projects
195 | }
196 | else {
197 | $projects = [System.Environment]::GetEnvironmentVariable("$($envName)-projects")
198 | if (-not $projects) {
199 | $projects = [System.Environment]::GetEnvironmentVariable("$($envName)_Projects")
200 | if (-not $projects) {
201 | $projects = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String([System.Environment]::GetEnvironmentVariable('projects')))
202 | }
203 | }
204 | }
205 | if ($projects -eq '') {
206 | $projects = '*'
207 | }
208 | elseif ($projects -ne '*') {
209 | $buildProjects = '${{ needs.Initialization.outputs.projects }}' | ConvertFrom-Json
210 | $projects = ($projects.Split(',') | Where-Object { $buildProjects -contains $_ }) -join ','
211 | }
212 | Add-Content -Path $env:GITHUB_OUTPUT -Value "authContext=$authContext"
213 | Add-Content -Path $env:GITHUB_OUTPUT -Value "environmentName=$environmentName"
214 | Write-Host "environmentName=$([System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String($environmentName)))"
215 | Write-Host "environmentName (as Base64)=$environmentName"
216 | Add-Content -Path $env:GITHUB_OUTPUT -Value "projects=$projects"
217 | Write-Host "projects=$projects"
218 |
219 | - name: Deploy
220 | uses: microsoft/AL-Go-Actions/Deploy@v3.1
221 | env:
222 | AuthContext: ${{ steps.authContext.outputs.authContext }}
223 | with:
224 | shell: powershell
225 | parentTelemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
226 | type: 'Publish'
227 | projects: ${{ steps.authContext.outputs.projects }}
228 | environmentName: ${{ steps.authContext.outputs.environmentName }}
229 | artifacts: ${{ github.event.inputs.appVersion }}
230 |
231 | PostProcess:
232 | if: always()
233 | runs-on: [ windows-latest ]
234 | needs: [ Initialization, Deploy ]
235 | steps:
236 | - name: Checkout
237 | uses: actions/checkout@v3
238 |
239 | - name: Finalize the workflow
240 | id: PostProcess
241 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
242 | with:
243 | shell: powershell
244 | eventId: "DO0097"
245 | telemetryScopeJson: ${{ needs.Initialization.outputs.telemetryScopeJson }}
246 |
--------------------------------------------------------------------------------
/.github/workflows/CreateRelease.yaml:
--------------------------------------------------------------------------------
1 | name: ' Create release'
2 |
3 | on:
4 | workflow_dispatch:
5 | inputs:
6 | appVersion:
7 | description: App version to promote to release (default is latest)
8 | required: false
9 | default: 'latest'
10 | name:
11 | description: Name of this release
12 | required: true
13 | default: ''
14 | tag:
15 | description: Tag of this release (needs to be semantic version string https://semver.org, ex. 1.0.0)
16 | required: true
17 | default: ''
18 | prerelease:
19 | description: Prerelease (Y/N)
20 | required: false
21 | default: 'N'
22 | draft:
23 | description: Draft (Y/N)
24 | required: false
25 | default: 'N'
26 | createReleaseBranch:
27 | description: Create Release Branch (Y/N)
28 | required: false
29 | default: 'N'
30 | updateVersionNumber:
31 | description: New Version Number in main branch. Use Major.Minor for absolute change, use +Major.Minor for incremental change.
32 | required: false
33 | default: ''
34 | directCommit:
35 | description: Direct COMMIT (Y/N)
36 | required: false
37 | default: 'N'
38 |
39 | permissions:
40 | contents: write
41 | pull-requests: write
42 | actions: read
43 |
44 | concurrency: release
45 |
46 | defaults:
47 | run:
48 | shell: powershell
49 |
50 | env:
51 | ALGoOrgSettings: ${{ vars.ALGoOrgSettings }}
52 | ALGoRepoSettings: ${{ vars.ALGoRepoSettings }}
53 |
54 | jobs:
55 | CreateRelease:
56 | runs-on: [ windows-latest ]
57 | outputs:
58 | telemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
59 | artifacts: ${{ steps.analyzeartifacts.outputs.artifacts }}
60 | releaseId: ${{ steps.createrelease.outputs.releaseId }}
61 | commitish: ${{ steps.analyzeartifacts.outputs.commitish }}
62 | releaseBranch: ${{ steps.createreleasenotes.outputs.releaseBranch }}
63 | steps:
64 | - name: Checkout
65 | uses: actions/checkout@v3
66 |
67 | - name: Initialize the workflow
68 | id: init
69 | uses: microsoft/AL-Go-Actions/WorkflowInitialize@v3.1
70 | with:
71 | shell: powershell
72 | eventId: "DO0094"
73 |
74 | - name: Read settings
75 | id: ReadSettings
76 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
77 | with:
78 | shell: powershell
79 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
80 | get: templateUrl,repoName
81 |
82 | - name: Determine Projects
83 | id: determineProjects
84 | uses: microsoft/AL-Go-Actions/DetermineProjectsToBuild@v3.1
85 | with:
86 | shell: powershell
87 |
88 | - name: Check for updates to AL-Go system files
89 | uses: microsoft/AL-Go-Actions/CheckForUpdates@v3.1
90 | with:
91 | shell: powershell
92 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
93 | templateUrl: ${{ env.templateUrl }}
94 |
95 | - name: Analyze Artifacts
96 | id: analyzeartifacts
97 | run: |
98 | $ErrorActionPreference = "STOP"
99 | Set-StrictMode -version 2.0
100 | $projects = '${{ steps.determineProjects.outputs.ProjectsJson }}' | ConvertFrom-Json
101 | Write-Host "projects:"
102 | $projects | ForEach-Object { Write-Host "- $_" }
103 | $include = @()
104 | $sha = ''
105 | $allArtifacts = @()
106 | $page = 1
107 | $headers = @{
108 | "Authorization" = "token ${{ github.token }}"
109 | "Accept" = "application/json"
110 | }
111 | do {
112 | $repoArtifacts = Invoke-WebRequest -UseBasicParsing -Headers $headers -Uri "$($ENV:GITHUB_API_URL)/repos/$($ENV:GITHUB_REPOSITORY)/actions/artifacts?per_page=100&page=$page" | ConvertFrom-Json
113 | $allArtifacts += $repoArtifacts.Artifacts
114 | $page++
115 | }
116 | while ($repoArtifacts.Artifacts.Count -gt 0)
117 | Write-Host "Repo Artifacts count: $($repoArtifacts.total_count)"
118 | Write-Host "Downloaded Artifacts count: $($allArtifacts.Count)"
119 | $projects | ForEach-Object {
120 | $thisProject = $_
121 | if ($thisProject -and ($thisProject -ne '.')) {
122 | $project = $thisProject.Replace('\','_').Replace('/','_')
123 | }
124 | else {
125 | $project = $env:repoName
126 | }
127 | $refname = "$ENV:GITHUB_REF_NAME".Replace('/','_')
128 | Write-Host "Analyzing artifacts for project $project"
129 | $appVersion = '${{ github.event.inputs.appVersion }}'
130 | if ($appVersion -eq "latest") {
131 | Write-Host "Grab latest"
132 | $artifact = $allArtifacts | Where-Object { $_.name -like "$project-$refname-Apps-*" } | Select-Object -First 1
133 | }
134 | else {
135 | Write-Host "Search for $project-$refname-Apps-$appVersion"
136 | $artifact = $allArtifacts | Where-Object { $_.name -eq "$project-$refname-Apps-$appVersion" } | Select-Object -First 1
137 | }
138 | if ($artifact) {
139 | $artifactsVersion = $artifact.name.SubString($artifact.name.LastIndexOf('-Apps-')+6)
140 | }
141 | else {
142 | Write-Host "::Error::No artifacts found for this project"
143 | exit 1
144 | }
145 | if ($sha) {
146 | if ($artifact.workflow_run.head_sha -ne $sha) {
147 | Write-Host "::Error::The build selected for release doesn't contain all projects. Please rebuild all projects by manually running the CI/CD workflow and recreate the release."
148 | throw "The build selected for release doesn't contain all projects. Please rebuild all projects by manually running the CI/CD workflow and recreate the release."
149 | }
150 | }
151 | else {
152 | $sha = $artifact.workflow_run.head_sha
153 | }
154 |
155 | $allArtifacts | Where-Object { ($_.name -like "$project-$refname-Apps-$($artifactsVersion)" -or $_.name -like "$project-$refname-TestApps-$($artifactsVersion)" -or $_.name -like "$project-$refname-Dependencies-$($artifactsVersion)") } | ForEach-Object {
156 | $atype = $_.name.SubString(0,$_.name.Length-$artifactsVersion.Length-1)
157 | $atype = $atype.SubString($atype.LastIndexOf('-')+1)
158 | $include += $( [ordered]@{ "name" = $_.name; "url" = $_.archive_download_url; "atype" = $atype; "project" = $thisproject } )
159 | }
160 | if ($include.Count -eq 0) {
161 | Write-Host "::Error::No artifacts found"
162 | exit 1
163 | }
164 | }
165 | $artifacts = @{ "include" = $include }
166 | $artifactsJson = $artifacts | ConvertTo-Json -compress
167 | Add-Content -Path $env:GITHUB_OUTPUT -Value "artifacts=$artifactsJson"
168 | Write-Host "artifacts=$artifactsJson"
169 | Add-Content -Path $env:GITHUB_OUTPUT -Value "commitish=$sha"
170 | Write-Host "commitish=$sha"
171 |
172 | - name: Prepare release notes
173 | id: createreleasenotes
174 | uses: microsoft/AL-Go-Actions/CreateReleaseNotes@v3.1
175 | with:
176 | shell: powershell
177 | parentTelemetryScopeJson: ${{ steps.init.outputs.telemetryScopeJson }}
178 | tag_name: ${{ github.event.inputs.tag }}
179 |
180 | - name: Create release
181 | uses: actions/github-script@v6
182 | id: createrelease
183 | env:
184 | bodyMD: ${{ steps.createreleasenotes.outputs.releaseNotes }}
185 | with:
186 | github-token: ${{ secrets.GITHUB_TOKEN }}
187 | script: |
188 | var bodyMD = process.env.bodyMD
189 | const createReleaseResponse = await github.rest.repos.createRelease({
190 | owner: context.repo.owner,
191 | repo: context.repo.repo,
192 | tag_name: '${{ github.event.inputs.tag }}',
193 | name: '${{ github.event.inputs.name }}',
194 | body: bodyMD.replaceAll('\\n','\n').replaceAll('%0A','\n').replaceAll('%0D','\n').replaceAll('%25','%'),
195 | draft: ${{ github.event.inputs.draft=='Y' }},
196 | prerelease: ${{ github.event.inputs.prerelease=='Y' }},
197 | make_latest: 'legacy',
198 | target_commitish: '${{ steps.analyzeartifacts.outputs.commitish }}'
199 | });
200 | const {
201 | data: { id: releaseId, html_url: htmlUrl, upload_url: uploadUrl }
202 | } = createReleaseResponse;
203 | core.setOutput('releaseId', releaseId);
204 |
205 | UploadArtifacts:
206 | runs-on: [ windows-latest ]
207 | needs: [ CreateRelease ]
208 | strategy:
209 | matrix: ${{ fromJson(needs.CreateRelease.outputs.artifacts) }}
210 | fail-fast: true
211 | steps:
212 | - name: Checkout
213 | uses: actions/checkout@v3
214 |
215 | - name: Read settings
216 | uses: microsoft/AL-Go-Actions/ReadSettings@v3.1
217 | with:
218 | shell: powershell
219 | parentTelemetryScopeJson: ${{ needs.CreateRelease.outputs.telemetryScopeJson }}
220 |
221 | - name: Read secrets
222 | uses: microsoft/AL-Go-Actions/ReadSecrets@v3.1
223 | env:
224 | secrets: ${{ toJson(secrets) }}
225 | with:
226 | shell: powershell
227 | parentTelemetryScopeJson: ${{ needs.CreateRelease.outputs.telemetryScopeJson }}
228 | settingsJson: ${{ env.Settings }}
229 | secrets: 'nuGetContext,storageContext'
230 |
231 | - name: Download artifact
232 | run: |
233 | $ErrorActionPreference = "STOP"
234 | Set-StrictMode -version 2.0
235 | Write-Host "Downloading artifact ${{ matrix.name}}"
236 | $headers = @{
237 | "Authorization" = "token ${{ github.token }}"
238 | "Accept" = "application/vnd.github.v3+json"
239 | }
240 | Invoke-WebRequest -UseBasicParsing -Headers $headers -Uri '${{ matrix.url }}' -OutFile '${{ matrix.name }}.zip'
241 |
242 | - name: Upload release artifacts
243 | uses: actions/github-script@v6
244 | env:
245 | releaseId: ${{ needs.createrelease.outputs.releaseId }}
246 | with:
247 | github-token: ${{ secrets.GITHUB_TOKEN }}
248 | script: |
249 | const releaseId = process.env.releaseId
250 | const assetPath = '${{ matrix.name }}.zip'
251 | const assetName = '${{ matrix.name }}.zip'
252 | const fs = require('fs');
253 | const uploadAssetResponse = await github.rest.repos.uploadReleaseAsset({
254 | owner: context.repo.owner,
255 | repo: context.repo.repo,
256 | release_id: releaseId,
257 | name: assetName,
258 | data: fs.readFileSync(assetPath)
259 | });
260 |
261 | - name: nuGetContext
262 | id: nuGetContext
263 | if: ${{ env.nuGetContext }}
264 | run: |
265 | $ErrorActionPreference = "STOP"
266 | Set-StrictMode -version 2.0
267 | $nuGetContext = ''
268 | if ('${{ matrix.atype }}' -eq 'Apps') {
269 | $nuGetContext = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String([System.Environment]::GetEnvironmentVariable('nuGetContext')))
270 | }
271 | Add-Content -Path $env:GITHUB_OUTPUT -Value "nuGetContext=$nuGetContext"
272 |
273 | - name: Deliver to NuGet
274 | uses: microsoft/AL-Go-Actions/Deliver@v3.1
275 | if: ${{ steps.nuGetContext.outputs.nuGetContext }}
276 | env:
277 | deliveryContext: ${{ steps.nuGetContext.outputs.nuGetContext }}
278 | with:
279 | shell: powershell
280 | type: 'Release'
281 | projects: ${{ matrix.project }}
282 | deliveryTarget: 'NuGet'
283 | artifacts: ${{ github.event.inputs.appVersion }}
284 | atypes: 'Apps,TestApps'
285 |
286 | - name: storageContext
287 | id: storageContext
288 | if: ${{ env.storageContext }}
289 | run: |
290 | $ErrorActionPreference = "STOP"
291 | Set-StrictMode -version 2.0
292 | $storageContext = ''
293 | if ('${{ matrix.atype }}' -eq 'Apps') {
294 | $storageContext = [System.Text.Encoding]::UTF8.GetString([System.Convert]::FromBase64String([System.Environment]::GetEnvironmentVariable('storageContext')))
295 | }
296 | Add-Content -Path $env:GITHUB_OUTPUT -Value "storageContext=$storageContext"
297 |
298 | - name: Deliver to Storage
299 | uses: microsoft/AL-Go-Actions/Deliver@v3.1
300 | if: ${{ steps.storageContext.outputs.storageContext }}
301 | env:
302 | deliveryContext: ${{ steps.storageContext.outputs.storageContext }}
303 | with:
304 | shell: powershell
305 | type: 'Release'
306 | projects: ${{ matrix.project }}
307 | deliveryTarget: 'Storage'
308 | artifacts: ${{ github.event.inputs.appVersion }}
309 | atypes: 'Apps,TestApps,Dependencies'
310 |
311 | CreateReleaseBranch:
312 | if: ${{ github.event.inputs.createReleaseBranch=='Y' }}
313 | runs-on: [ windows-latest ]
314 | needs: [ CreateRelease, UploadArtifacts ]
315 | steps:
316 | - name: Checkout
317 | uses: actions/checkout@v3
318 | with:
319 | ref: '${{ needs.createRelease.outputs.commitish }}'
320 |
321 | - name: Create Release Branch
322 | run: |
323 | $ErrorActionPreference = "STOP"
324 | Set-StrictMode -version 2.0
325 | git checkout -b ${{ needs.CreateRelease.outputs.releaseBranch }}
326 | git config user.name ${{ github.actor}}
327 | git config user.email ${{ github.actor}}@users.noreply.github.com
328 | git commit --allow-empty -m "Release branch ${{ needs.CreateRelease.outputs.releaseBranch }}"
329 | git push origin ${{ needs.CreateRelease.outputs.releaseBranch }}
330 |
331 | UpdateVersionNumber:
332 | if: ${{ github.event.inputs.updateVersionNumber!='' }}
333 | runs-on: [ windows-latest ]
334 | needs: [ CreateRelease, UploadArtifacts ]
335 | steps:
336 | - name: Update Version Number
337 | uses: microsoft/AL-Go-Actions/IncrementVersionNumber@v3.1
338 | with:
339 | shell: powershell
340 | parentTelemetryScopeJson: ${{ needs.CreateRelease.outputs.telemetryScopeJson }}
341 | versionNumber: ${{ github.event.inputs.updateVersionNumber }}
342 | directCommit: ${{ github.event.inputs.directCommit }}
343 |
344 | PostProcess:
345 | if: always()
346 | runs-on: [ windows-latest ]
347 | needs: [ CreateRelease, UploadArtifacts, CreateReleaseBranch, UpdateVersionNumber ]
348 | steps:
349 | - name: Checkout
350 | uses: actions/checkout@v3
351 |
352 | - name: Finalize the workflow
353 | id: PostProcess
354 | uses: microsoft/AL-Go-Actions/WorkflowPostProcess@v3.1
355 | with:
356 | shell: powershell
357 | eventId: "DO0094"
358 | telemetryScopeJson: ${{ needs.CreateRelease.outputs.telemetryScopeJson }}
359 |
--------------------------------------------------------------------------------
/.github/RELEASENOTES.copy.md:
--------------------------------------------------------------------------------
1 | ## v3.1
2 |
3 | ### Issues
4 |
5 | Issue #446 Wrong NewLine character in Release Notes
6 | Issue #453 DeliverToStorage - override fails reading secrets
7 | Issue #434 Use gh auth token to get authentication token instead of gh auth status
8 | Issue #501 The Create New App action will now use 22.0.0.0 as default application reference and include NoImplicitwith feature.
9 |
10 |
11 | ### New behavior
12 |
13 | The following workflows:
14 |
15 | - Create New App
16 | - Create New Test App
17 | - Create New Performance Test App
18 | - Increment Version Number
19 | - Add Existing App
20 | - Create Online Development Environment
21 |
22 | All these actions now uses the selected branch in the **Run workflow** dialog as the target for the Pull Request or Direct COMMIT.
23 |
24 | ### New Settings
25 |
26 | - `UseCompilerFolder`: Setting useCompilerFolder to true causes your pipelines to use containerless compiling. Unless you also set `doNotPublishApps` to true, setting useCompilerFolder to true won't give you any performance advantage, since AL-Go for GitHub will still need to create a container in order to publish and test the apps. In the future, publishing and testing will be split from building and there will be other options for getting an instance of Business Central for publishing and testing.
27 | - `vsixFile`: vsixFile should be a direct download URL to the version of the AL Language extension you want to use for building the project or repo. By default, AL-Go will use the AL Language extension that comes with the Business Central Artifacts.
28 |
29 | ### New Actions
30 |
31 | - **DetermineArtifactUrl** is used to determine which artifacts to use for building a project in CI/CD, PullRequestHandler, Current, NextMinor and NextMajor workflows.
32 |
33 | ### License File
34 |
35 | With the changes to the CRONUS license in Business Central version 22, that license can in most cases be used as a developer license for AppSource Apps and it is no longer mandatory to specify a license file in AppSource App repositories.
36 | Obviously, if you build and test your app for Business Central versions prior to 21, it will fail if you don't specify a licenseFileUrl secret.
37 |
38 | ## v3.0
39 |
40 | ### **NOTE:** When upgrading to this version
41 | When upgrading to this version form earlier versions of AL-Go for GitHub, you will need to run the _Update AL-Go System Files_ workflow twice if you have the `useProjectDependencies` setting set to _true_.
42 |
43 | ### Publish to unknown environment
44 | You can now run the **Publish To Environment** workflow without creating the environment in GitHub or settings up-front, just by specifying the name of a single environment in the Environment Name when running the workflow.
45 | Subsequently, if an AuthContext secret hasn't been created for this environment, the Device Code flow authentication will be initiated from the Publish To Environment workflow and you can publish to the new environment without ever creating a secret.
46 | Open Workflow details to get the device Code for authentication in the job summary for the initialize job.
47 |
48 | ### Create Online Dev. Environment
49 | When running the **Create Online Dev. Environment** workflow without having the _adminCenterApiCredentials_ secret created, the workflow will intiate the deviceCode flow and allow you to authenticate to the Business Central Admin Center.
50 | Open Workflow details to get the device Code for authentication in the job summary for the initialize job.
51 |
52 | ### Issues
53 | - Issue #391 Create release action - CreateReleaseBranch error
54 | - Issue 434 Building local DevEnv, downloading dependencies: Authentication fails when using "gh auth status"
55 |
56 | ### Changes to Pull Request Process
57 | In v2.4 and earlier, the PullRequestHandler would trigger the CI/CD workflow to run the PR build.
58 | Now, the PullRequestHandler will perform the build and the CI/CD workflow is only run on push (or manual dispatch) and will perform a complete build.
59 |
60 | ### Build modes per project
61 | Build modes can now be specified per project
62 |
63 | ### New Actions
64 | - **DetermineProjectsToBuild** is used to determine which projects to build in PullRequestHandler, CI/CD, Current, NextMinor and NextMajor workflows.
65 | - **CalculateArtifactNames** is used to calculate artifact names in PullRequestHandler, CI/CD, Current, NextMinor and NextMajor workflows.
66 | - **VerifyPRChanges** is used to verify whether a PR contains changes, which are not allowed from a fork.
67 |
68 | ## v2.4
69 |
70 | ### Issues
71 | - Issue #171 create a workspace file when creating a project
72 | - Issue #356 Publish to AppSource fails in multi project repo
73 | - Issue #358 Publish To Environment Action stopped working in v2.3
74 | - Issue #362 Support for EnableTaskScheduler
75 | - Issue #360 Creating a release and deploying from a release branch
76 | - Issue #371 'No previous release found' for builds on release branches
77 | - Issue #376 CICD jobs that are triggered by the pull request trigger run directly to an error if title contains quotes
78 |
79 | ### Release Branches
80 | **NOTE:** Release Branches are now only named after major.minor if the patch value is 0 in the release tag (which must be semver compatible)
81 |
82 | This version contains a number of bug fixes to release branches, to ensure that the recommended branching strategy is fully supported. Bugs fixed includes:
83 | - Release branches was named after the full tag (1.0.0), even though subsequent hotfixes released from this branch would be 1.0.x
84 | - Release branches named 1.0 wasn't picked up as a release branch
85 | - Release notes contained the wrong changelog
86 | - The previous release was always set to be the first release from a release branch
87 | - SemVerStr could not have 5 segments after the dash
88 | - Release was created on the right SHA, but the release branch was created on the wrong SHA
89 |
90 | Recommended branching strategy:
91 |
92 | 
93 |
94 | ### New Settings
95 | New Project setting: EnableTaskScheduler in container executing tests and when setting up local development environment
96 |
97 | ### Support for GitHub variables: ALGoOrgSettings and ALGoRepoSettings
98 | Recently, GitHub added support for variables, which you can define on your organization or your repository.
99 | AL-Go now supports that you can define a GitHub variable called ALGoOrgSettings, which will work for all repositories (with access to the variable)
100 | Org Settings will be applied before Repo settings and local repository settings files will override values in the org settings
101 | You can also define a variable called ALGoRepoSettings on the repository, which will be applied after reading the Repo Settings file in the repo
102 | Example for usage could be setup of branching strategies, versioning or an appDependencyProbingPaths to repositories which all repositories share.
103 | appDependencyProbingPaths from settings variables are merged together with appDependencyProbingPaths defined in repositories
104 |
105 | ### Refactoring and tests
106 | ReadSettings has been refactored to allow organization wide settings to be added as well. CI Tests have been added to cover ReadSettings.
107 |
108 | ## v2.3
109 |
110 | ### Issues
111 | - Issue #312 Branching enhancements
112 | - Issue #229 Create Release action tags wrong commit
113 | - Issue #283 Create Release workflow uses deprecated actions
114 | - Issue #319 Support for AssignPremiumPlan
115 | - Issue #328 Allow multiple projects in AppSource App repo
116 | - Issue #344 Deliver To AppSource on finding app.json for the app
117 | - Issue #345 LocalDevEnv.ps1 can't Dowload the file license file
118 |
119 | ### New Settings
120 | New Project setting: AssignPremiumPlan on user in container executing tests and when setting up local development environment
121 | New Repo setting: unusedALGoSystemFiles is an array of AL-Go System Files, which won't be updated during Update AL-Go System Files. They will instead be removed. Use with care, as this can break the AL-Go for GitHub functionality and potentially leave your repo no longer functional.
122 |
123 | ### Build modes support
124 | AL-Go projects can now be built in different modes, by specifying the _buildModes_ setting in AL-Go-Settings.json. Read more about build modes in the [Basic Repository settings](https://github.com/microsoft/AL-Go/blob/main/Scenarios/settings.md#basic-repository-settings).
125 |
126 | ### LocalDevEnv / CloudDevEnv
127 | With the support for PowerShell 7 in BcContainerHelper, the scripts LocalDevEnv and CloudDevEnv (placed in the .AL-Go folder) for creating development environments have been modified to run inside VS Code instead of spawning a new powershell 5.1 session.
128 |
129 | ### Continuous Delivery
130 | Continuous Delivery can now run from other branches than main. By specifying a property called branches, containing an array of branches in the deliveryContext json construct, the artifacts generated from this branch are also delivered. The branch specification can include wildcards (like release/*). Default is main, i.e. no changes to functionality.
131 |
132 | ### Continuous Deployment
133 | Continuous Deployment can now run from other branches than main. By creating a repo setting (.github/AL-Go-Settings.json) called **`-Branches`**, which is an array of branches, which will deploy the generated artifacts to this environment. The branch specification can include wildcards (like release/*), although this probably won't be used a lot in continuous deployment. Default is main, i.e. no changes to functionality.
134 |
135 | ### Create Release
136 | When locating artifacts for the various projects, the SHA used to build the artifact is used for the release tag
137 | If all projects are not available with the same SHA, this error is thrown: **The build selected for release doesn't contain all projects. Please rebuild all projects by manually running the CI/CD workflow and recreate the release.**
138 | There is no longer a hard dependency on the main branch name from Create Release.
139 |
140 | ### AL-Go Tests
141 | Some unit tests have been added and AL-Go unit tests can now be run directly from VS Code.
142 | Another set of end to end tests have also been added and in the documentation on contributing to AL-Go, you can see how to run these in a local fork or from VS Code.
143 |
144 | ### LF, UTF8 and JSON
145 | GitHub natively uses LF as line seperator in source files.
146 | In earlier versions of AL-Go for GitHub, many scripts and actions would use CRLF and convert back and forth. Some files were written with UTF8 BOM (Byte Order Mark), other files without and JSON formatting was done using PowerShell 5.1 (which is different from PowerShell 7).
147 | In the latest version, we always use LF as line seperator, UTF8 without BOM and JSON files are written using PowerShell 7. If you have self-hosted runners, you need to ensure that PS7 is installed to make this work.
148 |
149 | ### Experimental Support
150 | Setting the repo setting "shell" to "pwsh", followed by running Update AL-Go System Files, will cause all PowerShell code to be run using PowerShell 7 instead of PowerShell 5. This functionality is experimental. Please report any issues at https://github.com/microsoft/AL-Go/issues
151 | Setting the repo setting "runs-on" to "Ubuntu-Latest", followed by running Update AL-Go System Files, will cause all non-build jobs to run using Linux. This functionality is experimental. Please report any issues at https://github.com/microsoft/AL-Go/issues
152 |
153 | ## v2.2
154 |
155 | ### Enhancements
156 | - Container Event log is added as a build artifact if builds or tests are failing
157 |
158 | ### Issues
159 | - Issue #280 Overflow error when test result summary was too big
160 | - Issue #282, 292 AL-Go for GitHub causes GitHub to issue warnings
161 | - Issue #273 Potential security issue in Pull Request Handler in Open Source repositories
162 | - Issue #303 PullRequestHandler fails on added files
163 | - Issue #299 Multi-project repositories build all projects on Pull Requests
164 | - Issue #291 Issues with new Pull Request Handler
165 | - Issue #287 AL-Go pipeline fails in ReadSettings step
166 |
167 | ### Changes
168 | - VersioningStrategy 1 is no longer supported. GITHUB_ID has changed behavior (Issue #277)
169 |
170 | ## v2.1
171 |
172 | ### Issues
173 | - Issue #233 AL-Go for GitHub causes GitHub to issue warnings
174 | - Issue #244 Give error if AZURE_CREDENTIALS contains line breaks
175 |
176 | ### Changes
177 | - New workflow: PullRequestHandler to handle all Pull Requests and pass control safely to CI/CD
178 | - Changes to yaml files, PowerShell scripts and codeowners files are not permitted from fork Pull Requests
179 | - Test Results summary (and failed tests) are now displayed directly in the CI/CD workflow and in the Pull Request Check
180 |
181 | ### Continuous Delivery
182 | - Proof Of Concept Delivery to GitHub Packages and Nuget
183 |
184 | ## v2.0
185 |
186 | ### Issues
187 | - Issue #143 Commit Message for **Increment Version Number** workflow
188 | - Issue #160 Create local DevEnv aith appDependencyProbingPaths
189 | - Issue #156 Versioningstrategy 2 doesn't use 24h format
190 | - Issue #155 Initial Add existing app fails with "Cannot find path"
191 | - Issue #152 Error when loading dependencies from releases
192 | - Issue #168 Regression in preview fixed
193 | - Issue #189 Warnings: Resource not accessible by integration
194 | - Issue #190 PublishToEnvironment is not working with AL-Go-PTE@preview
195 | - Issue #186 AL-GO build fails for multi-project repository when there's nothing to build
196 | - When you have GitHub pages enabled, AL-Go for GitHub would try to publish to github_pages environment
197 | - Special characters wasn't supported in parameters to GitHub actions (Create New App etc.)
198 |
199 | ### Continuous Delivery
200 | - Added new GitHub Action "Deliver" to deliver build output to Storage or AppSource
201 | - Refactor CI/CD and Release workflows to use new deliver action
202 | - Custom delivery supported by creating scripts with the naming convention DeliverTo*.ps1 in the .github folder
203 |
204 | ### AppSource Apps
205 | - New workflow: Publish to AppSource
206 | - Continuous Delivery to AppSource validation supported
207 |
208 | ### Settings
209 | - New Repo setting: CICDPushBranches can be specified as an array of branches, which triggers a CI/CD workflow on commit. Default is main', release/\*, feature/\*
210 | - New Repo setting: CICDPullRequestBranches can be specified as an array of branches, which triggers a CI/CD workflow on pull request. Default is main
211 | - New Repo setting: CICDSchedule can specify a CRONTab on when you want to run CI/CD on a schedule. Note that this will disable Push and Pull Request triggers unless specified specifically using CICDPushBranches or CICDPullRequestBranches
212 | - New Repo setting: UpdateGitHubGoSystemFilesSchedule can specify a CRONTab on when you want to Update AL-Go System Files. Note that when running on a schedule, update AL-Go system files will perfom a direct commit and not create a pull request.
213 | - New project Setting: AppSourceContext should be a compressed json structure containing authContext for submitting to AppSource. The BcContainerHelperFunction New-ALGoAppSourceContext will help you create this structure.
214 | - New project Setting: AppSourceContinuousDelivery. Set this to true in enable continuous delivery for this project to AppSource. This requires AppSourceContext and AppSourceProductId to be set as well
215 | - New project Setting: AppSourceProductId should be set to the product Id of this project in AppSource
216 | - New project Setting: AppSourceMainAppFolder. If you have multiple appFolders, this is the folder name of the main app to submit to AppSource.
217 |
218 | ### All workflows
219 | - Support 2 folder levels projects (apps\w1, apps\dk etc.)
220 | - Better error messages for if an error occurs within an action
221 | - Special characters are now supported in secrets
222 | - Initial support for agents running inside containers on a host
223 | - Optimized workflows to have fewer jobs
224 |
225 | ### Update AL-Go System Files Workflow
226 | - workflow now displays the currently used template URL when selecting the Run Workflow action
227 |
228 | ### CI/CD workflow
229 | - Better detection of changed projects
230 | - appDependencyProbingPaths did not support multiple projects in the same repository for latestBuild dependencies
231 | - appDependencyProbingPaths with release=latestBuild only considered the last 30 artifacts
232 | - Use mutex around ReadSecrets to ensure that multiple agents on the same host doesn't clash
233 | - Add lfs when checking out files for CI/CD to support checking in dependencies
234 | - Continue on error with Deploy and Deliver
235 |
236 | ### CI/CD and Publish To New Environment
237 | - Base functionality for selecting a specific GitHub runner for an environment
238 | - Include dependencies artifacts when deploying (if generateDependencyArtifacts is true)
239 |
240 | ### localDevEnv.ps1 and cloudDevEnv.ps1
241 | - Display clear error message if something goes wrong
242 |
243 | ## v1.5
244 |
245 | ### Issues
246 | - Issue #100 - Add more resilience to localDevEnv.ps1 and cloudDevEnv.ps1
247 | - Issue #131 - Special characters are not allowed in secrets
248 |
249 | ### All workflows
250 | - During initialize, all AL-Go settings files are now checked for validity and reported correctly
251 | - During initialize, the version number of AL-Go for GitHub is printed in large letters (incl. preview or dev.)
252 |
253 | ### New workflow: Create new Performance Test App
254 | - Create BCPT Test app and add to bcptTestFolders to run bcpt Tests in workflows (set doNotRunBcptTests in workflow settings for workflows where you do NOT want this)
255 |
256 | ### Update AL-Go System Files Workflow
257 | - Include release notes of new version in the description of the PR (and in the workflow output)
258 |
259 | ### CI/CD workflow
260 | - Apps are not signed when the workflow is running as a Pull Request validation
261 | - if a secret called applicationInsightsConnectionString exists, then the value of that will be used as ApplicationInsightsConnectionString for the app
262 |
263 | ### Increment Version Number Workflow
264 | - Bugfix: increment all apps using f.ex. +0.1 would fail.
265 |
266 | ### Environments
267 | - Add suport for EnvironmentName redirection by adding an Environment Secret under the environment or a repo secret called \_EnvironmentName with the actual environment name.
268 | - No default environment name on Publish To Environment
269 | - For multi-project repositories, you can specify an environment secret called Projects or a repo setting called \_Projects, containing the projects you want to deploy to this environment.
270 |
271 | ### Settings
272 | - New setting: **runs-on** to allow modifying runs-on for all jobs (requires Update AL-Go System files after changing the setting)
273 | - New setting: **DoNotSignApps** - setting this to true causes signing of the app to be skipped
274 | - New setting: **DoNotPublishApps** - setting this to true causes the workflow to skip publishing, upgrading and testing the app to improve performance.
275 | - New setting: **ConditionalSettings** to allow to use different settings for specific branches. Example:
276 | ```
277 | "ConditionalSettings": [
278 | {
279 | "branches": [
280 | "feature/*"
281 | ],
282 | "settings": {
283 | "doNotPublishApps": true,
284 | "doNotSignApps": true
285 | }
286 | }
287 | ]
288 | ```
289 | - Default **BcContainerHelperVersion** is now based on AL-Go version. Preview AL-Go selects preview bcContainerHelper, normal selects latest.
290 | - New Setting: **bcptTestFolders** contains folders with BCPT tests, which will run in all build workflows
291 | - New Setting: set **doNotRunBcptTest** to true (in workflow specific settings file?) to avoid running BCPT tests
292 | - New Setting: set **obsoleteTagMinAllowedMajorMinor** to enable appsource cop to validate your app against future changes (AS0105). This setting will become auto-calculated in Test Current, Test Next Minor and Test Next Major later.
293 |
294 | ## v1.4
295 |
296 | ### All workflows
297 | - Add requested permissions to avoid dependency on user/org defaults being too permissive
298 |
299 | ### Update AL-Go System Files Workflow
300 | - Default host to https://github.com/ (you can enter **myaccount/AL-Go-PTE@main** to change template)
301 | - Support for "just" changing branch (ex. **\@Preview**) to shift to the preview version
302 |
303 | ### CI/CD Workflow
304 | - Support for feature branches (naming **feature/\***) - CI/CD workflow will run, but not generate artifacts nor deploy to QA
305 |
306 | ### Create Release Workflow
307 | - Support for release branches
308 | - Force Semver format on release tags
309 | - Add support for creating release branches on release (naming release/\*)
310 | - Add support for incrementing main branch after release
311 |
312 | ### Increment version number workflow
313 | - Add support for incremental (and absolute) version number change
314 |
315 | ### Environments
316 | - Support environmentName redirection in CI/CD and Publish To Environments workflows
317 | - If the name in Environments or environments settings doesn't match the actual environment name,
318 | - You can add a secret called EnvironmentName under the environment (or \_ENVIRONMENTNAME globally)
319 |
320 |
321 | ## v1.3
322 |
323 | ### Issues
324 | - Issue #90 - Environments did not work. Secrets for environments specified in settings can now be **\_AUTHCONTEXT**
325 |
326 | ### CI/CD Workflow
327 | - Give warning instead of error If no artifacts are found in **appDependencyProbingPaths**
328 |
329 | ## v1.2
330 |
331 | ### Issues
332 | - Issue #90 - Environments did not work. Environments (even if only defined in the settings file) did not work for private repositories if you didn't have a premium subscription.
333 |
334 | ### Local scripts
335 | - **LocalDevEnv.ps1** and ***CloudDevEnv.ps1** will now spawn a new PowerShell window as admin instead of running inside VS Code. Normally people doesn't run VS Code as administrator, and they shouldn't have to. Furthermore, I have seen a some people having problems when running these scripts inside VS Code.
336 |
337 |
338 | ## v1.1
339 |
340 | ### Settings
341 | - New Repo Setting: **GenerateDependencyArtifact** (default **false**). When true, CI/CD pipeline generates an artifact with the external dependencies used for building the apps in this repo.
342 | - New Repo Setting: **UpdateDependencies** (default **false**). When true, the default artifact for building the apps in this repo is not the latest available artifacts for this country, but instead the first compatible version (after calculating application dependencies). It is recommended to run Test Current, Test NextMinor and Test NextMajor in order to test your app against current and future builds.
343 |
344 | ### CI/CD Workflow
345 | - New Artifact: BuildOutput.txt. All compiler warnings and errors are emitted to this file to make it easier to investigate compiler errors and build a better UI for build errors and test results going forward.
346 | - TestResults artifact name to include repo version number and workflow name (for Current, NextMinor and NextMajor)
347 | - Default dependency version in appDependencyProbingPaths setting used is now latest Release instead of LatestBuild
348 |
--------------------------------------------------------------------------------