create-flowmo 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,144 @@
1
+ # OutSystems Built-in Functions Reference
2
+
3
+ Complete reference for all built-in functions available in OutSystems expressions.
4
+
5
+ ## Data Conversion
6
+
7
+ | Function | Description | Example |
8
+ |----------|-------------|---------|
9
+ | `BooleanToInteger(b)` | Boolean → Integer (True=1, False=0) | `BooleanToInteger(IsActive)` → `1` |
10
+ | `BooleanToText(b)` | Boolean → "True" / "False" | `BooleanToText(True)` → `"True"` |
11
+ | `DateTimeToDate(dt)` | DateTime → Date (strips time) | `DateTimeToDate(CurrDateTime())` |
12
+ | `DateTimeToText(dt)` | DateTime → Text (default format) | `DateTimeToText(CurrDateTime())` |
13
+ | `DateTimeToTime(dt)` | DateTime → Time (strips date) | `DateTimeToTime(CurrDateTime())` |
14
+ | `DateToDateTime(d)` | Date → DateTime (time = 00:00:00) | `DateToDateTime(CurrDate())` |
15
+ | `DateToText(d)` | Date → Text | `DateToText(CurrDate())` |
16
+ | `DecimalToBoolean(d)` | Decimal → Boolean (0=False, else True) | `DecimalToBoolean(1.5)` → `True` |
17
+ | `DecimalToInteger(d)` | Decimal → Integer (truncates) | `DecimalToInteger(3.7)` → `3` |
18
+ | `DecimalToLongInteger(d)` | Decimal → Long Integer | `DecimalToLongInteger(3.7)` → `3` |
19
+ | `DecimalToText(d)` | Decimal → Text | `DecimalToText(3.14)` → `"3.14"` |
20
+ | `IntegerToBoolean(i)` | Integer → Boolean (0=False) | `IntegerToBoolean(1)` → `True` |
21
+ | `IntegerToDecimal(i)` | Integer → Decimal | `IntegerToDecimal(5)` → `5.0` |
22
+ | `IntegerToText(i)` | Integer → Text | `IntegerToText(42)` → `"42"` |
23
+ | `LongIntegerToInteger(l)` | Long Integer → Integer (may overflow) | `LongIntegerToInteger(100)` |
24
+ | `LongIntegerToText(l)` | Long Integer → Text | `LongIntegerToText(100)` → `"100"` |
25
+ | `NullDate()` | Returns null date (#1900-01-01#) | `If(Date = NullDate(), ...)` |
26
+ | `NullIdentifier()` | Returns null identifier for any Id type | `CustomerId = NullIdentifier()` |
27
+ | `NullObject()` | Returns null object reference | `NullObject()` |
28
+ | `NullTextIdentifier()` | Returns null text identifier ("") | `NullTextIdentifier()` |
29
+ | `TextToDate(t)` | Text → Date | `TextToDate("2024-01-15")` |
30
+ | `TextToDateTime(t)` | Text → DateTime | `TextToDateTime("2024-01-15 14:30:00")` |
31
+ | `TextToDecimal(t)` | Text → Decimal | `TextToDecimal("3.14")` → `3.14` |
32
+ | `TextToIdentifier(t)` | Text → Identifier | `TextToIdentifier("abc-123")` |
33
+ | `TextToInteger(t)` | Text → Integer | `TextToInteger("42")` → `42` |
34
+ | `TextToLongInteger(t)` | Text → Long Integer | `TextToLongInteger("1000000")` |
35
+ | `TextToTime(t)` | Text → Time | `TextToTime("14:30:00")` |
36
+ | `TimeToText(t)` | Time → Text | `TimeToText(CurrTime())` |
37
+ | `ToObject(x)` | Any → Object | `ToObject(MyRecord)` |
38
+
39
+ ## Date and Time
40
+
41
+ | Function | Description | Example |
42
+ |----------|-------------|---------|
43
+ | `CurrDate()` | Current date (server) | `CurrDate()` |
44
+ | `CurrDateTime()` | Current date+time (server) | `CurrDateTime()` |
45
+ | `CurrTime()` | Current time (server) | `CurrTime()` |
46
+ | `Year(d)` | Extract year | `Year(CurrDate())` → `2024` |
47
+ | `Month(d)` | Extract month (1-12) | `Month(CurrDate())` → `6` |
48
+ | `Day(d)` | Extract day (1-31) | `Day(CurrDate())` → `15` |
49
+ | `Hour(t)` | Extract hour (0-23) | `Hour(CurrTime())` → `14` |
50
+ | `Minute(t)` | Extract minute (0-59) | `Minute(CurrTime())` → `30` |
51
+ | `Second(t)` | Extract second (0-59) | `Second(CurrTime())` → `45` |
52
+ | `AddDays(d, n)` | Add n days to date | `AddDays(CurrDate(), 7)` |
53
+ | `AddHours(dt, n)` | Add n hours to datetime | `AddHours(CurrDateTime(), 2)` |
54
+ | `AddMinutes(dt, n)` | Add n minutes to datetime | `AddMinutes(CurrDateTime(), 30)` |
55
+ | `AddMonths(d, n)` | Add n months | `AddMonths(CurrDate(), 3)` |
56
+ | `AddSeconds(dt, n)` | Add n seconds | `AddSeconds(CurrDateTime(), 60)` |
57
+ | `AddYears(d, n)` | Add n years | `AddYears(CurrDate(), 1)` |
58
+ | `DiffDays(d1, d2)` | Difference in days | `DiffDays(StartDate, EndDate)` |
59
+ | `DiffHours(dt1, dt2)` | Difference in hours | `DiffHours(Start, End)` |
60
+ | `DiffMinutes(dt1, dt2)` | Difference in minutes | `DiffMinutes(Start, End)` |
61
+ | `DiffMonths(d1, d2)` | Difference in months | `DiffMonths(Start, End)` |
62
+ | `DiffSeconds(dt1, dt2)` | Difference in seconds | `DiffSeconds(Start, End)` |
63
+ | `DiffYears(d1, d2)` | Difference in years | `DiffYears(Start, End)` |
64
+ | `BuildDateTime(d, t)` | Combine Date + Time → DateTime | `BuildDateTime(CurrDate(), CurrTime())` |
65
+ | `NewDate(y, m, d)` | Construct a Date | `NewDate(2024, 12, 25)` |
66
+ | `NewDateTime(y, m, d, h, mi, s)` | Construct a DateTime | `NewDateTime(2024, 12, 25, 0, 0, 0)` |
67
+ | `NewTime(h, m, s)` | Construct a Time | `NewTime(14, 30, 0)` |
68
+ | `DayOfWeek(d)` | Day of week (0=Sun, 6=Sat) | `DayOfWeek(CurrDate())` |
69
+
70
+ ## Math
71
+
72
+ | Function | Description | Example |
73
+ |----------|-------------|---------|
74
+ | `Abs(n)` | Absolute value | `Abs(-5)` → `5` |
75
+ | `Ceiling(d)` | Round up | `Ceiling(3.2)` → `4` |
76
+ | `Floor(d)` | Round down | `Floor(3.8)` → `3` |
77
+ | `Round(d, decimals)` | Round to N decimals | `Round(3.456, 2)` → `3.46` |
78
+ | `Trunc(d)` | Truncate decimal | `Trunc(3.9)` → `3` |
79
+ | `Max(a, b)` | Larger value | `Max(10, 20)` → `20` |
80
+ | `Min(a, b)` | Smaller value | `Min(10, 20)` → `10` |
81
+ | `Power(base, exp)` | Exponent | `Power(2, 3)` → `8` |
82
+ | `Sqrt(n)` | Square root | `Sqrt(16)` → `4` |
83
+ | `Log(n)` | Natural log | `Log(2.718)` → `1.0` |
84
+ | `Mod(a, b)` | Modulo/remainder | `Mod(10, 3)` → `1` |
85
+
86
+ ## Text
87
+
88
+ | Function | Description | Example |
89
+ |----------|-------------|---------|
90
+ | `Chr(n)` | ASCII code → character | `Chr(65)` → `"A"` |
91
+ | `Concat(t1, t2)` | Concatenate two texts | `Concat("Hello", " World")` |
92
+ | `Index(t, search, start)` | Find position (0-based) | `Index("Hello", "ll", 0)` → `2` |
93
+ | `Length(t)` | String length | `Length("Hello")` → `5` |
94
+ | `NewLine()` | Line break character | `"Line1" + NewLine() + "Line2"` |
95
+ | `Replace(text, search, replace)` | Replace occurrences | `Replace("Hello", "l", "r")` → `"Herro"` |
96
+ | `Substr(t, start, length)` | Substring (0-based start) | `Substr("Hello", 0, 3)` → `"Hel"` |
97
+ | `ToLower(t)` | Lowercase | `ToLower("HELLO")` → `"hello"` |
98
+ | `ToUpper(t)` | Uppercase | `ToUpper("hello")` → `"HELLO"` |
99
+ | `Trim(t)` | Remove leading/trailing whitespace | `Trim(" hi ")` → `"hi"` |
100
+ | `TrimStart(t)` | Remove leading whitespace | `TrimStart(" hi")` → `"hi"` |
101
+ | `TrimEnd(t)` | Remove trailing whitespace | `TrimEnd("hi ")` → `"hi"` |
102
+
103
+ ## Format
104
+
105
+ | Function | Description | Example |
106
+ |----------|-------------|---------|
107
+ | `FormatCurrency(value, symbol, decimals, decSep, thousSep)` | Format as currency | `FormatCurrency(1234.5, "$", 2, ".", ",")` → `"$1,234.50"` |
108
+ | `FormatDateTime(dt, format)` | Format datetime | `FormatDateTime(CurrDateTime(), "yyyy-MM-dd")` |
109
+ | `FormatDecimal(d, decimals, decSep, thousSep)` | Format decimal | `FormatDecimal(1234.5, 2, ".", ",")` → `"1,234.50"` |
110
+ | `FormatPercent(d, decimals)` | Format as percentage | `FormatPercent(0.85, 1)` → `"85.0%"` |
111
+ | `FormatPhoneNumber(phone, locale)` | Format phone number | `FormatPhoneNumber("+1234567890", "en-US")` |
112
+ | `FormatText(t, format)` | Apply text format | `FormatText("hello", "uppercase")` |
113
+
114
+ ## Email Validation
115
+
116
+ | Function | Description |
117
+ |----------|-------------|
118
+ | `EmailAddressValidate(email)` | Returns True if valid email format |
119
+
120
+ ## URL
121
+
122
+ | Function | Description | Example |
123
+ |----------|-------------|---------|
124
+ | `EncodeURL(t)` | URL-encode text | `EncodeURL("hello world")` → `"hello%20world"` |
125
+ | `DecodeURL(t)` | URL-decode text | `DecodeURL("hello%20world")` → `"hello world"` |
126
+ | `GetBookmarkableURL()` | Get current screen URL | `GetBookmarkableURL()` |
127
+
128
+ ## Organization
129
+
130
+ | Function | Description |
131
+ |----------|-------------|
132
+ | `GetCurrentLocale()` | Returns the current user's locale (e.g., "en-US") |
133
+ | `GetAppName()` | Returns the current application name |
134
+ | `GetOwnerURLPath()` | Returns the base URL path of the application |
135
+ | `GetExceptionURL()` | Returns the URL of the exception handler page |
136
+
137
+ ## Roles
138
+
139
+ | Function | Description | Example |
140
+ |----------|-------------|---------|
141
+ | `CheckRole(RoleName)` | Check if current user has role | `CheckRole(Entities.Role.Admin)` |
142
+ | `Check<RoleName>Role()` | Auto-generated per role | `CheckAdminRole()` |
143
+ | `GetUserId()` | Current logged-in user's ID | `GetUserId()` |
144
+ | `GetUserName()` | Current user's username | Return type: Text |
@@ -0,0 +1,169 @@
1
+ # OutSystems Libraries Reference
2
+
3
+ System libraries that provide additional utility functions. Import these modules to use their functions in Server Actions and expressions.
4
+
5
+ ## BinaryData
6
+
7
+ Functions for working with binary/blob data.
8
+
9
+ | Function | Description |
10
+ |----------|-------------|
11
+ | `BinaryDataToText(data, encoding)` | Convert binary to text with specified encoding (UTF-8, ASCII, etc.) |
12
+ | `TextToBinaryData(text, encoding)` | Convert text to binary data |
13
+ | `BinaryDataSize(data)` | Returns the size in bytes |
14
+ | `BinaryToBase64(data)` | Encode binary as Base64 string |
15
+ | `Base64ToBinary(text)` | Decode Base64 string to binary |
16
+ | `Compare(data1, data2)` | Compare two binary values (returns 0 if equal) |
17
+ | `ConvertEncoding(data, srcEncoding, dstEncoding)` | Convert between text encodings |
18
+
19
+ ## DateTime
20
+
21
+ Extended date/time manipulation beyond built-in functions.
22
+
23
+ | Function | Description |
24
+ |----------|-------------|
25
+ | `DiffWeeks(d1, d2)` | Difference in weeks |
26
+ | `IsLeapYear(year)` | Check if year is a leap year |
27
+ | `DaysInMonth(year, month)` | Number of days in a month |
28
+ | `FirstDayOfWeek(d)` | Get the first day (Monday) of the week containing date |
29
+ | `LastDayOfMonth(d)` | Get the last day of the month |
30
+ | `StartOfDay(dt)` | DateTime with time set to 00:00:00 |
31
+ | `EndOfDay(dt)` | DateTime with time set to 23:59:59 |
32
+
33
+ ## HTTP
34
+
35
+ HTTP request and response handling for REST and web interactions.
36
+
37
+ | Function | Description |
38
+ |----------|-------------|
39
+ | `GetRequestHeader(name)` | Get value of an HTTP request header |
40
+ | `SetRequestHeader(name, value)` | Set an HTTP request header |
41
+ | `GetResponseHeader(name)` | Get value of an HTTP response header |
42
+ | `GetRequestContent()` | Get the raw request body |
43
+ | `GetRawURL()` | Get the full request URL |
44
+ | `GetFormValue(name)` | Get a form field value from POST data |
45
+ | `GetURLMethod()` | Get the HTTP method (GET, POST, etc.) |
46
+ | `SetStatusCode(code)` | Set the HTTP response status code |
47
+ | `SetCookie(name, value, expires)` | Set an HTTP cookie |
48
+ | `GetCookie(name)` | Get a cookie value |
49
+ | `URLEncode(text)` | URL-encode a text value |
50
+ | `URLDecode(text)` | URL-decode a text value |
51
+
52
+ ## Sanitization
53
+
54
+ Security-related functions for preventing XSS, SQL injection, and other attacks.
55
+
56
+ | Function | Description |
57
+ |----------|-------------|
58
+ | `SanitizeHtml(html)` | Remove dangerous HTML tags/attributes, keep safe formatting |
59
+ | `VerifyJavascriptLiteral(text)` | Escape text for safe use in JavaScript strings |
60
+ | `BuildSafe_InClauseIntegerList(idList)` | Safely build SQL IN clause from integer list |
61
+ | `BuildSafe_InClauseTextList(textList)` | Safely build SQL IN clause from text list |
62
+
63
+ ## Security
64
+
65
+ Cryptographic and security utility functions.
66
+
67
+ | Function | Description |
68
+ |----------|-------------|
69
+ | `GenerateSecurePassword()` | Generate a cryptographically secure random password |
70
+ | `ComputeMAC(data, key, algorithm)` | Compute Message Authentication Code (HMAC) |
71
+ | `CryptoEncrypt(plainText, key)` | Encrypt text using AES |
72
+ | `CryptoDecrypt(cipherText, key)` | Decrypt AES-encrypted text |
73
+ | `ComputeHash(data, algorithm)` | Compute hash (MD5, SHA-256, SHA-512) |
74
+ | `JWT_CreateToken(header, payload, key)` | Create a JSON Web Token (ODC) |
75
+ | `JWT_VerifyToken(token, key)` | Verify and decode a JWT (ODC) |
76
+
77
+ ## Text
78
+
79
+ Extended text manipulation functions.
80
+
81
+ | Function | Description |
82
+ |----------|-------------|
83
+ | `Format(text, args)` | String formatting with placeholders |
84
+ | `Join(list, separator)` | Join list elements into a single text |
85
+ | `Split(text, separator)` | Split text into a list |
86
+ | `PadLeft(text, totalWidth, paddingChar)` | Pad text on the left |
87
+ | `PadRight(text, totalWidth, paddingChar)` | Pad text on the right |
88
+ | `Regex_Replace(text, pattern, replacement)` | Replace using regex |
89
+ | `Regex_Search(text, pattern)` | Search for regex match |
90
+ | `Regex_IsMatch(text, pattern)` | Check if regex matches |
91
+ | `StringBuilder_Create()` | Create a string builder for efficient concatenation |
92
+ | `StringBuilder_Append(sb, text)` | Append text to string builder |
93
+ | `StringBuilder_ToString(sb)` | Get final text from string builder |
94
+ | `String_LastIndexOf(text, search)` | Find last occurrence position |
95
+ | `String_StartsWith(text, prefix)` | Check if text starts with prefix |
96
+ | `String_EndsWith(text, suffix)` | Check if text ends with suffix |
97
+ | `String_Contains(text, search)` | Check if text contains substring |
98
+
99
+ ## TextDictionary
100
+
101
+ Key-value pair storage (dictionary/map data structure).
102
+
103
+ | Function | Description |
104
+ |----------|-------------|
105
+ | `TextDictionary_Create()` | Create a new dictionary |
106
+ | `TextDictionary_Set(dict, key, value)` | Set a key-value pair |
107
+ | `TextDictionary_Get(dict, key)` | Get value by key |
108
+ | `TextDictionary_Remove(dict, key)` | Remove a key |
109
+ | `TextDictionary_ContainsKey(dict, key)` | Check if key exists |
110
+ | `TextDictionary_Count(dict)` | Number of entries |
111
+ | `TextDictionary_Keys(dict)` | Get all keys as a list |
112
+ | `TextDictionary_Values(dict)` | Get all values as a list |
113
+
114
+ ## URL
115
+
116
+ URL parsing and construction.
117
+
118
+ | Function | Description |
119
+ |----------|-------------|
120
+ | `GetRelativeURL(absoluteURL)` | Extract the relative path from an absolute URL |
121
+ | `GetAbsoluteURL(relativeURL)` | Construct absolute URL from relative path |
122
+ | `GetURLHost(url)` | Extract the host from a URL |
123
+ | `GetURLProtocol(url)` | Extract the protocol (http/https) |
124
+ | `GetURLPath(url)` | Extract the path component |
125
+ | `GetURLQueryString(url)` | Extract the query string |
126
+ | `AddURLParameter(url, name, value)` | Append a query parameter |
127
+
128
+ ## XML
129
+
130
+ XML parsing and generation.
131
+
132
+ | Function | Description |
133
+ |----------|-------------|
134
+ | `XmlDocument_Load(xml)` | Parse XML string into document |
135
+ | `XmlDocument_GetRootNode(doc)` | Get the root element |
136
+ | `XmlNode_GetChildNodes(node)` | Get child nodes |
137
+ | `XmlNode_GetAttribute(node, name)` | Get attribute value |
138
+ | `XmlNode_GetInnerText(node)` | Get text content |
139
+ | `XmlNode_SelectNodes(node, xpath)` | Query nodes by XPath |
140
+ | `XmlDocument_Save(doc)` | Convert document back to XML string |
141
+ | `Xsl_Transform(xml, xslt)` | Apply XSLT transformation |
142
+
143
+ ## Zip
144
+
145
+ File compression and decompression.
146
+
147
+ | Function | Description |
148
+ |----------|-------------|
149
+ | `Zip_Create()` | Create a new zip archive |
150
+ | `Zip_AddFile(zip, fileName, content)` | Add a file to the archive |
151
+ | `Zip_GetBinary(zip)` | Get the zip as binary data |
152
+ | `Zip_Open(binaryData)` | Open a zip from binary data |
153
+ | `Zip_GetFileList(zip)` | List files in the archive |
154
+ | `Zip_GetFile(zip, fileName)` | Extract a specific file |
155
+
156
+ ## Math (Extended)
157
+
158
+ Additional math functions beyond the built-in set.
159
+
160
+ | Function | Description |
161
+ |----------|-------------|
162
+ | `Random(min, max)` | Generate random integer in range |
163
+ | `PI()` | Returns π (3.14159...) |
164
+ | `Sin(angle)` | Sine |
165
+ | `Cos(angle)` | Cosine |
166
+ | `Tan(angle)` | Tangent |
167
+ | `Asin(value)` | Arc sine |
168
+ | `Acos(value)` | Arc cosine |
169
+ | `Atan(value)` | Arc tangent |
@@ -0,0 +1,221 @@
1
+ # OutSystems System Actions Reference
2
+
3
+ System-provided server actions for authentication, user management, and session handling.
4
+
5
+ ## Authentication Actions
6
+
7
+ ### User_Login
8
+ Log in a user with username and password.
9
+
10
+ ```
11
+ User_Login(Username, Password, PersistLogin)
12
+ Input: Username (Text) — The user's username or email
13
+ Input: Password (Text) — The user's password
14
+ Input: PersistLogin (Boolean) — If True, creates a persistent cookie
15
+ Output: (none — sets session automatically)
16
+ Throws: Security Exception if credentials are invalid
17
+ ```
18
+
19
+ **Usage pattern:**
20
+ ```
21
+ ServerAction: DoLogin
22
+ Input: Username (Text), Password (Text), RememberMe (Boolean)
23
+
24
+ Try
25
+ User_Login(Username, Password, RememberMe)
26
+ // Redirect to home screen
27
+ Catch SecurityException
28
+ FeedbackMessage("Invalid username or password", "error")
29
+ ```
30
+
31
+ ### User_Logout
32
+ Log out the current user and clear session.
33
+
34
+ ```
35
+ User_Logout()
36
+ Input: (none)
37
+ Output: (none — clears session)
38
+ ```
39
+
40
+ ### User_GetUnifiedLoginUrl
41
+ Get the URL of the central login page (when using an external IdP or unified login).
42
+
43
+ ```
44
+ User_GetUnifiedLoginUrl(OriginalUrl)
45
+ Input: OriginalUrl (Text) — URL to redirect back to after login
46
+ Output: URL (Text) — The login page URL
47
+ ```
48
+
49
+ ## User Management Actions
50
+
51
+ ### User_CreateOrUpdate
52
+ Create a new user or update an existing one.
53
+
54
+ ```
55
+ User_CreateOrUpdate(User)
56
+ Input: User (User Record)
57
+ - Username (Text) — Required, unique
58
+ - Name (Text) — Display name
59
+ - Email (Text) — Email address
60
+ - MobilePhone (Phone Number) — Optional
61
+ - IsActive (Boolean) — Whether the user can login
62
+ Output: UserId (User Identifier) — The created/updated user's ID
63
+ ```
64
+
65
+ ### User_SetPassword
66
+ Change a user's password.
67
+
68
+ ```
69
+ User_SetPassword(UserId, Password)
70
+ Input: UserId (User Identifier) — Target user
71
+ Input: Password (Text) — New password (must meet complexity requirements)
72
+ Output: (none)
73
+ ```
74
+
75
+ ### User_ChangePassword
76
+ Change the current user's own password (requires current password).
77
+
78
+ ```
79
+ User_ChangePassword(UserId, OldPassword, NewPassword)
80
+ Input: UserId (User Identifier)
81
+ Input: OldPassword (Text) — Current password for verification
82
+ Input: NewPassword (Text) — New password
83
+ Output: (none)
84
+ Throws: Security Exception if old password is wrong
85
+ ```
86
+
87
+ ### User_Delete
88
+ Deactivate or delete a user account.
89
+
90
+ ```
91
+ User_Delete(UserId)
92
+ Input: UserId (User Identifier)
93
+ Output: (none)
94
+ ```
95
+
96
+ **Note**: In most OutSystems configurations, this deactivates rather than hard-deletes the user.
97
+
98
+ ## Session & Identity
99
+
100
+ ### GetUserId
101
+ Get the current logged-in user's identifier.
102
+
103
+ ```
104
+ GetUserId()
105
+ Output: UserId (User Identifier)
106
+ Note: Returns NullIdentifier() if no user is logged in
107
+ ```
108
+
109
+ ### GetUserName
110
+ Get the current user's username.
111
+
112
+ ```
113
+ GetUserName()
114
+ Output: Username (Text)
115
+ ```
116
+
117
+ ### GetExternalLoginURL
118
+ Get the URL for external authentication providers (SAML, OAuth, etc.).
119
+
120
+ ```
121
+ GetExternalLoginURL(ProviderName)
122
+ Input: ProviderName (Text) — e.g., "AzureAD", "Okta"
123
+ Output: URL (Text)
124
+ ```
125
+
126
+ ## Role Management
127
+
128
+ ### GrantRole
129
+ Assign a role to a user.
130
+
131
+ ```
132
+ GrantRole(UserId, RoleId)
133
+ Input: UserId (User Identifier)
134
+ Input: RoleId (Role Identifier) — e.g., Entities.Role.Admin
135
+ Output: (none)
136
+ ```
137
+
138
+ ### RevokeRole
139
+ Remove a role from a user.
140
+
141
+ ```
142
+ RevokeRole(UserId, RoleId)
143
+ Input: UserId (User Identifier)
144
+ Input: RoleId (Role Identifier)
145
+ Output: (none)
146
+ ```
147
+
148
+ ### CheckRole
149
+ Check if a user has a specific role.
150
+
151
+ ```
152
+ CheckRole(UserId, RoleId)
153
+ Input: UserId (User Identifier)
154
+ Input: RoleId (Role Identifier)
155
+ Output: HasRole (Boolean)
156
+ ```
157
+
158
+ **Shortcut**: Auto-generated `Check<RoleName>Role()` functions check the current user.
159
+
160
+ ## Common Patterns
161
+
162
+ ### Registration Flow
163
+ ```
164
+ ServerAction: Register
165
+ Input: Username, Password, Name, Email
166
+
167
+ // 1. Create user
168
+ NewUser = CreateUser(Username, Name, Email, IsActive: True)
169
+ UserId = User_CreateOrUpdate(NewUser)
170
+
171
+ // 2. Set password
172
+ User_SetPassword(UserId, Password)
173
+
174
+ // 3. Assign default role
175
+ GrantRole(UserId, Entities.Role.RegisteredUser)
176
+
177
+ // 4. Auto-login
178
+ User_Login(Username, Password, PersistLogin: False)
179
+ ```
180
+
181
+ ### Password Reset Flow
182
+ ```
183
+ ServerAction: ResetPassword
184
+ Input: UserId, NewPassword
185
+
186
+ // 1. Validate token (custom logic — not built-in)
187
+ If not ValidateResetToken(UserId, Token)
188
+ Raise User Exception "Invalid or expired reset link"
189
+ End If
190
+
191
+ // 2. Set new password
192
+ User_SetPassword(UserId, NewPassword)
193
+
194
+ // 3. Invalidate token
195
+ InvalidateResetToken(UserId, Token)
196
+ ```
197
+
198
+ ### Authorization Check Pattern
199
+ ```
200
+ ServerAction: EnsureAuthorized
201
+ Input: RequiredRole (Role Identifier)
202
+
203
+ If GetUserId() = NullIdentifier()
204
+ Raise Security Exception "Not authenticated"
205
+ End If
206
+
207
+ If not CheckRole(GetUserId(), RequiredRole)
208
+ Raise Security Exception "Insufficient permissions"
209
+ End If
210
+ ```
211
+
212
+ ## Platform Differences
213
+
214
+ | Feature | O11 | ODC |
215
+ |---------|-----|-----|
216
+ | User entity | `System.User` | Built-in User entity |
217
+ | Roles | Defined per module, granted at runtime | Defined in ODC Portal, managed via API |
218
+ | External auth | Users module + IdP config | Built-in OIDC/SAML in ODC Portal |
219
+ | Session storage | Server-side session | Stateless JWT tokens |
220
+ | User_Login | Creates server session + cookie | Returns JWT token |
221
+ | Multi-tenant | `TenantId` on Site entities | Separate ODC organizations |