Bentley ProjectWise-specific CEL environment

PW CEL

This technical handbook starts with a quick explanation of what PW CEL allows, what it no longer permits, and which limits matter. It then covers contexts, objects, lookups, Bentley extensions, Library Expressions, testing, and real attribute situations. It is a working PW configuration reference, not a lesson-based course.

Core CEL and PW CEL are different layers.

This reference targets ProjectWise Administrator 2026 and keeps portable core CEL separate from ProjectWise contexts, objects, lookups, and Bentley extensions.

01 · ProjectWise scope

What PW CEL is

ProjectWise CEL is Bentley’s host-specific CEL environment for Document Attributes, WorkArea properties, and Workflow Rules Engine. It is not a general ProjectWise data API and cannot query arbitrary database tables, internal IDs, or bulk data.

PW CEL in three points

What it is for

An expression calculates one result from the current ProjectWise context, CEL operations, and registered lookups.

What CEL does not allow

There is no arbitrary SELECT, table, view, join, stored procedure, or unrestricted access to the ProjectWise database.

What changes in practice

Start with a supported object or lookup instead of a database table. Complex SQL logic may require precomputed data or an integration redesign.

Read the complete plain-language explanationOpen the full comparison of the original ProjectWise model and PW CEL, including lookup behavior, supported datasets, limits, examples, and migration decisions.

Configuration areas

Document AttributesDefault · Update · Value list
WorkArea PropertiesProperty expressions
Workflow Rules EngineCHANGE_VERSION
Source: official Bentley ProjectWise CEL documentation

02 · Runtime model

Contexts and objects

The active context decides which names and objects an expression can access. A valid expression in one context can fail in another.

Document Attributes

Available names
thisDocumentthisFolderthisFormthisUserutcTime
Used for
Default value, Update value, and Value list expressions
Important boundary
Folder and WorkArea hierarchy functions are available only here.

WorkArea Properties

Available names
thisFormthisUserutcTime
Used for
Property value expressions, mainly depending on other form fields
Important boundary
It does not expose the current folder name or parent hierarchy.

Workflow Rules Version

Available names
currentVersionutcTime
Used for
CHANGE_VERSION actions in Workflow Rules Engine
Important boundary
The expression result becomes the new document version value.

ProjectWise objects

Open an object to see its fields, types, methods, null behavior, and operational boundaries. These names are the actual ProjectWise context contract.

DocumentthisDocumentThe current document and its ProjectWise metadata.
FieldTypeMeaning
.guidGUIDDocument identifier
.namestringDocument name
.descriptionstringDocument description
.fileFileAssociated file object
.versionstringVersion label
.versionSeqintegerSystem-maintained version sequence
.workflowWorkflowAssigned workflow
.stateStateCurrent workflow state
.createdByUserUser who created the document
.createdOntimestampUTC creation time
.updatedByUserUser who last updated the document
.updatedOntimestampUTC time of the last update
.applicationApplicationAssociated application

Methods

getPWLink()string
getPWLink(PWLinkType)string
getPWLink(PWLinkType, bool)string
getHttpLink()string
getHttpLink(AppToOpen)string
Source: official Bentley ProjectWise CEL documentation
Attribute FormthisFormA map of current, including unsaved, form values addressed by internal field name.
Source: official Bentley ProjectWise CEL documentation
FolderthisFolderThe folder containing the current document and its position in the hierarchy.
FieldTypeMeaning
.guidGUIDFolder identifier
.namestringFolder name
.descriptionstringFolder description
.workflowWorkflowAssigned workflow
.environmentEnvironmentAssigned Environment
.updatedByUserUser who last changed the folder
.updatedOntimestampUTC time of the last change
.createdByUserUser who created the folder
.createdOntimestampUTC creation time
.isWorkAreaboolWhether the folder is a WorkArea

Methods

getWorkArea()WorkArea | null
getOwnerWorkArea()WorkArea | null
getOwnerWorkArea(string)WorkArea | null
getTopFolder()Folder
getTopWorkArea()WorkArea | null
getRelativeFolder(int)Folder | null
getFolderPath(Folder)list<Folder>
Source: official Bentley ProjectWise CEL documentation
WorkAreafolder hierarchy resultA WorkArea reached from a Folder or another WorkArea.
FieldTypeMeaning
.folderFolderFolder represented by this WorkArea
.typestringWorkArea type
.propertiesmap<string,string>WorkArea Type property values

Methods

getWorkArea()WorkArea
getOwnerWorkArea()WorkArea | null
getOwnerWorkArea(string)WorkArea | null
getTopFolder()Folder
getTopWorkArea()WorkArea | null
getRelativeFolder(int)Folder | null
getFolderPath(Folder)list<Folder>
Source: official Bentley ProjectWise CEL documentation
UserthisUser / document user fieldThe active user or a user referenced by another ProjectWise object.
FieldTypeMeaning
.namestringLogin name
.descriptionstringUser description or full name
.emailstringEmail address
Source: official Bentley ProjectWise CEL documentation
FilethisDocument.fileThe physical file associated with a document.
FieldTypeMeaning
.namestringFile name
.sizeintFile size in bytes
.updatedByUserUser who last updated the file
.updatedOntimestampUTC time of the last file update
Source: official Bentley ProjectWise CEL documentation
Workflowdocument.workflow / folder.workflowThe workflow assigned to a document or folder.
FieldTypeMeaning
.namestringWorkflow name
.descriptionstringWorkflow description
Source: official Bentley ProjectWise CEL documentation
StatethisDocument.stateThe current workflow state of a document.
FieldTypeMeaning
.namestringState name
.descriptionstringState description
Source: official Bentley ProjectWise CEL documentation
ApplicationthisDocument.applicationThe ProjectWise application associated with a document.
FieldTypeMeaning
.namestringApplication name
Source: official Bentley ProjectWise CEL documentation
EnvironmentthisFolder.environmentThe Environment assigned to a folder.
FieldTypeMeaning
.namestringEnvironment name
Source: official Bentley ProjectWise CEL documentation

Documents, folders, and WorkAreas in practice

Open a situation to connect the object model to a concrete document, file, folder hierarchy, or WorkArea property and its expected result.

Documentdocument-metadata-summaryRead a document summary
Situation in ProjectWise

An attribute needs a compact snapshot of the current document name, version, and workflow state.

CEL expression
{'name': thisDocument.name,
 'version': thisDocument.version,
 'state': thisDocument.state.name}
Illustrative object statethisDocument.name = 'BS-A1-DR-001.dgn'thisDocument.version = 'P03'thisDocument.state.name = 'Approved'
Result{name: 'BS-A1-DR-001.dgn', version: 'P03', state: 'Approved'}map<string,string>
What happens, step by step
  1. Read scalar metadata from the current Document object.
  2. Follow the state property to the nested State object.
  3. Return the selected values in one CEL map.
thisDocumentDocument.nameDocument.versionState.name
Source: official Bentley ProjectWise CEL documentation
Document → Filedocument-file-nameHandle a document with or without a file
Situation in ProjectWise

A display value should show the physical file name, but new or placeholder documents may not have a file.

CEL expression
thisDocument.file.name == '' ? '(no file)' : thisDocument.file.name
Illustrative object statethisDocument.file.name = 'BS-A1-DR-001.dgn'
Result'BS-A1-DR-001.dgn'string
What happens, step by step
  1. Read the nested File object from thisDocument.
  2. Check its name before using it.
  3. Return either the real file name or an explicit fallback.
thisDocument.fileFile.nameconditionalempty object
Source: official Bentley ProjectWise CEL documentation
Folder → Environmentfolder-environmentShow the folder and its Environment
Situation in ProjectWise

A calculated value should identify both the containing folder and the Environment assigned to it.

CEL expression
thisFolder.name + ' · ' + thisFolder.environment.name
Illustrative object statethisFolder.name = 'Drawings'thisFolder.environment.name = 'BS Document Attributes'
Result'Drawings · BS Document Attributes'string
What happens, step by step
  1. Read the current containing Folder.
  2. Follow environment to the nested Environment object.
  3. Join both names into a readable label.
thisFolderFolder.nameFolder.environmentEnvironment.name
Source: official Bentley ProjectWise CEL documentation
Folder hierarchyfolder-parentRead the parent folder name
Situation in ProjectWise

The current document is in Drawings, while an attribute needs the discipline folder one level above it.

CEL expression
[thisFolder.getRelativeFolder(1)].map(parent, parent == null ? '' : parent.name)[0]
Illustrative object statefolder path = Project > Building A > Architecture > DrawingsthisFolder.name = 'Drawings'
Result'Architecture'string
What happens, step by step
  1. Start at the document folder.
  2. Move one level toward the top of the hierarchy.
  3. Return the parent name, or an empty string when no parent is available.
thisFoldergetRelativeFolderFolder.namenull guard
Source: official Bentley ProjectWise CEL documentation
Folder hierarchy → Documentdocument-full-pathBuild the complete document path
Situation in ProjectWise

A legacy FULLNAME-style value must be assembled from the real folder hierarchy and document name.

CEL expression
thisFolder.getTopFolder().getFolderPath(thisFolder).map(f, f.name).join('\') + '\' + thisDocument.name
Illustrative object statefolder names = ['Project', 'Building A', 'Drawings']thisDocument.name = 'A-101.dgn'
Result'Project\Building A\Drawings\A-101.dgn'string
What happens, step by step
  1. Find the top folder and request the path back to thisFolder.
  2. Replace Folder objects with their names and join them with backslashes.
  3. Append the current document name.
thisFolderthisDocument.namegetTopFoldergetFolderPathmapjoin
Source: official Bentley ProjectWise CEL documentation
Folder → WorkAreaowner-workarea-propertyRead a property from the owning Project WorkArea
Situation in ProjectWise

A document attribute needs the project code stored on the nearest owning WorkArea of type Project.

CEL expression
[thisFolder.getOwnerWorkArea('Project')].map(wa, wa == null ? '' : wa.properties.ProjectCode)[0]
Illustrative object stateowning WorkArea.type = 'Project'owning WorkArea.properties.ProjectCode = 'BS-RAIL-001'
Result'BS-RAIL-001'string
What happens, step by step
  1. Search upward for the nearest owning WorkArea whose type is Project.
  2. Guard the object because the search can return null.
  3. Read ProjectCode from its string properties map.
thisFoldergetOwnerWorkAreaWorkArea.typeWorkArea.propertiesnull guard
Source: official Bentley ProjectWise CEL documentation
Document creationnew-document-null-guardGuard thisDocument during document creation
Situation in ProjectWise

A default expression can run before the new Document object is available.

CEL expression
thisDocument == null ? '(new document)' : thisDocument.name
Illustrative object statethisDocument = null
Result'(new document)'string
What happens, step by step
  1. Check the context object before reading any Document property.
  2. Return a safe creation-time value when it is null.
  3. Read the real name only after the Document exists.
thisDocumentnullconditionalDocument.name
Source: official Bentley ProjectWise CEL documentation
Source: official Bentley ProjectWise CEL documentation

03 · Data selection

Lookups and supported datasets

PW CEL does not expose supported SQL tables. It exposes registered lookups and the exact built-in DMS.* datasets below.

Three lookup sources

Document Environment lookup

Uses attributes of documents in a selected Environment as lookup rows.

Documented condition or limitDatasource-wide; document permissions are not applied. Do not store sensitive values.

Standard lookup

Loads external ERP, CRM, or other data through PowerShell or the SDK.

Documented condition or limitUp to 10 columns; every value is a string with at most 255 characters.

Built-in lookup

Exposes selected ProjectWise system data through DMS.* lookup names.

Documented condition or limitOnly the documented datasets and columns below are available.

How registered lookups are defined and populated

A lookup definition and its data source are separate concerns. Environment lookups read live document attributes; Standard lookups receive uploaded rows; built-ins are supplied by ProjectWise.

Environment lookupNew-PWLookupA live view over selected attributes of documents in one Environment.

The definition does not copy Environment rows. New, changed, and deleted document attribute values are reflected by subsequent lookup evaluations. A new attribute column must be added to the lookup definition before CEL can select it.

Definition fieldExampleWhat it controls
Lookup nameLKP.StatusUnique in the datasource; DMS. is reserved
Environment nameLookup Table - General AttributesEnvironment used as the live row source
ColumnsPW_CODE, PW_DESCRIPTIONEnvironment attributes exposed to CEL
Default value columnPW_CODEUsed as value by select()
Default description columnPW_DESCRIPTIONOptional description used by select()
Primary filterPW_FILTER = StatusOptional fixed, case-insensitive equals filters
Sorting columnsPW_SORTORDER = ascLogical order; the sort column need not be selectable
Source: official Bentley ProjectWise CEL documentation
Standard lookupNew-PWLookup · Set-PWLookupData · SDKA ProjectWise-managed dataset loaded from an external system.

The definition describes the columns and defaults; data is uploaded separately. Keeping it synchronized with SQL, ERP, CRM, CSV, or another source is the integration’s responsibility.

Definition fieldExampleWhat it controls
Lookup namemy.townsUnique in the datasource; DMS. is reserved
Columnscountry, region, nameAt most 10 string columns, 255 characters each
Default value columnnameUsed as value by select()
Default description columnregionOptional description used by select()
Sorting columnssort_order = ascOptional logical sorting
Source: official Bentley ProjectWise CEL documentation

Built-in lookup datasets

Lookup nameSelectable columnsRequired hidden filterContents
DMS.UsersnamedescriptionemailAll datasource users
DMS.UserListsnamedescriptionAll user lists
DMS.UsersInListnamedescriptionemaillistUsers in one list; filtering by list is required
DMS.UserGroupsnamedescriptionDatasource user groups
DMS.UsersInGroupnamedescriptionemailgroupUsers in one group; filtering by group is required
DMS.WorkflowsnamedescriptionDatasource workflows
DMS.StatesnamedescriptionDatasource states
DMS.StatesInWorkflownamedescriptionworkflowStates in one workflow; filtering by workflow is required

Lookup query pipeline

Get a lookup, apply the most selective lookup filters, then call select() for rows or selectOne() for one string.

getLookup('DMS.UsersInGroup')
  .filterEquals('group', 'Design')
  .select('name', 'description')

Documented lookup functions

Lookup filters produce another Lookup object. Data is materialized only by select() or selectOne(), so narrow large datasets before selecting.

getLookup

Lookup
getLookup(name)

Gets a registered or built-in lookup; names are case-insensitive.

getLookup('DMS.Users')
Source: official Bentley ProjectWise CEL documentation

listLookups

list<string>
listLookups()

Lists registered and built-in lookup names in the datasource.

listLookups()
Source: official Bentley ProjectWise CEL documentation

filterEquals

Lookup
<Lookup>.filterEquals(column, value)

Adds an equality filter; chained lookup filters are applied together.

.filterEquals('country', 'US')
Source: official Bentley ProjectWise CEL documentation

filterNotEquals

Lookup
<Lookup>.filterNotEquals(column, value)

Excludes rows whose column equals the supplied value.

.filterNotEquals('status', 'inactive')
Source: official Bentley ProjectWise CEL documentation

filterContains

Lookup
<Lookup>.filterContains(column, substring)

Adds a case-insensitive substring filter.

.filterContains('state', 'new')
Source: official Bentley ProjectWise CEL documentation

filterNotContains

Lookup
<Lookup>.filterNotContains(column, substring)

Excludes rows containing the substring, case-insensitively.

.filterNotContains('name', 'test')
Source: official Bentley ProjectWise CEL documentation

filterIn

Lookup
<Lookup>.filterIn(column, values)

Keeps rows whose column value occurs in the supplied list.

.filterIn('status', ['active', 'pending'])
Source: official Bentley ProjectWise CEL documentation

filterNotIn

Lookup
<Lookup>.filterNotIn(column, values)

Excludes rows whose column value occurs in the supplied list.

.filterNotIn('status', ['inactive', 'closed'])
Source: official Bentley ProjectWise CEL documentation

select

list<{value: string, description: string}>
<Lookup>.select([valueColumn], [descriptionColumn])

Returns matching value-list rows. With no arguments it uses the configured default columns.

.select('name', 'description')
Source: official Bentley ProjectWise CEL documentation

selectOne

string
<Lookup>.selectOne(column[, fallback])

Returns one column value. Without fallback, zero or multiple rows fail; with fallback, both cases return it.

.selectOne('description', '')
Source: official Bentley ProjectWise CEL documentation
Source: official Bentley ProjectWise CEL documentation

04 · Host additions

Bentley / ProjectWise extensions

These names are supplied by ProjectWise. Do not present them as portable core CEL syntax.

Folder and WorkArea access

getWorkAreagetOwnerWorkAreagetTopFoldergetTopWorkAreagetRelativeFoldergetFolderPath

Document Attributes context only; object-returning methods may return null.

String extensions

charAtformatincrementAlphaVersionindexOflastIndexOfparseparseRegexreplacereversesplitsubstringtoLowertoUppertrimtakeLefttakeRightdropLeftdropRight

ProjectWise additions include .NET-style format, parsers, and slicing helpers.

List extensions

takeLefttakeRightdropLeftdropRight

Return a selected edge or the remainder of a list.

Time extensions

toLocalTimetoUTCsecondsOffset

Use TZDB/IANA names and a ProjectWise local-time compound object.

Document links

getPWLinkgetHttpLinkPWLinkTypeAppToOpen

Creates PW URL/URN links or web links targeting WEB, WEBVIEW, or PWE.

Signatures, return types, behavior, and examples

Folder and WorkArea hierarchyDocument Attributes context onlyNavigate from the document folder toward the datasource root and back.

getWorkArea

WorkArea | null
Folder.getWorkArea() / WorkArea.getWorkArea()

Converts a WorkArea folder to a WorkArea object; a normal folder returns null.

thisFolder.getWorkArea().type
Source: official Bentley ProjectWise CEL documentation

getOwnerWorkArea

WorkArea | null
<Folder|WorkArea>.getOwnerWorkArea([workAreaType])

Finds the nearest owning WorkArea, optionally restricted by case-insensitive type.

thisFolder.getOwnerWorkArea('Project').properties.Code
Source: official Bentley ProjectWise CEL documentation

getTopFolder

Folder
<Folder|WorkArea>.getTopFolder()

Returns the top-most folder relative to the current document path.

thisFolder.getTopFolder().name
Source: official Bentley ProjectWise CEL documentation

getTopWorkArea

WorkArea | null
<Folder|WorkArea>.getTopWorkArea()

Returns the top-most WorkArea in the current path, or null when none exists.

thisFolder.getTopWorkArea().type
Source: official Bentley ProjectWise CEL documentation

getRelativeFolder

Folder | null
<Folder|WorkArea>.getRelativeFolder(level)

0 keeps the origin, positive moves toward the top, negative moves toward the document.

thisFolder.getTopFolder().getRelativeFolder(-3).description
Source: official Bentley ProjectWise CEL documentation

getFolderPath

list<Folder>
<Folder|WorkArea>.getFolderPath(endFolder)

Returns the ordered folder path between the receiver and the end folder.

thisFolder.getTopFolder().getFolderPath(thisFolder).map(f, f.name).join('\')
Source: official Bentley ProjectWise CEL documentation
String functions18 ProjectWise entriesFormatting, parsing, searching, splitting, and controlled slicing.

charAt

string
<string>.charAt(position)

Character at a zero-based position

'abc'.charAt(1)
Source: official Bentley ProjectWise CEL documentation

format

string
<format>.format(value[, locale])

.NET-compatible value, number, and date formatting

'{0:yyyy-MM-dd}'.format(utcTime)
Source: official Bentley ProjectWise CEL documentation

incrementAlphaVersion

string
<string>.incrementAlphaVersion([alphabet])

Increments the last alphabetic version position

'Z'.incrementAlphaVersion()
Source: official Bentley ProjectWise CEL documentation

indexOf

int
<string>.indexOf(fragment[, offset])

First zero-based occurrence, optionally from an offset

'abc'.indexOf('b')
Source: official Bentley ProjectWise CEL documentation

lastIndexOf

int
<string>.lastIndexOf(fragment[, maxIndex])

Last zero-based occurrence, optionally bounded

'abcba'.lastIndexOf('b')
Source: official Bentley ProjectWise CEL documentation

parse

list<map>
<string>.parse(patterns)

Parses named A, N, and wildcard fragments; returns a singleton result list

'P01.02'.parse(['{pre:A}{maj:N}.{min:N}'])
Source: official Bentley ProjectWise CEL documentation

parseRegex

list<map>
<string>.parseRegex(patterns)

Tests regex patterns and returns match index plus capture groups

'P01.02'.parseRegex([R'(P\d+)([.])(\d+)'])
Source: official Bentley ProjectWise CEL documentation

replace

string
<string>.replace(what, replacement[, limit])

Replaces all or a limited number of occurrences

'abccc'.replace('c', 'x', 2)
Source: official Bentley ProjectWise CEL documentation

reverse

string
<string>.reverse()

Reverses the characters

'abc'.reverse()
Source: official Bentley ProjectWise CEL documentation

split

list<string>
<string>.split(separator[, limit])

Splits text, optionally limiting the result count

'ab-cd-ef'.split('-', 2)
Source: official Bentley ProjectWise CEL documentation

substring

string
<string>.substring(position[, count])

Returns text from a position; the second argument is count, not end index

'abc'.substring(1, 2)
Source: official Bentley ProjectWise CEL documentation

toLower

string
<string>.toLower()

Converts text to lowercase

'ABC'.toLower()
Source: official Bentley ProjectWise CEL documentation

toUpper

string
<string>.toUpper()

Converts text to uppercase

'abc'.toUpper()
Source: official Bentley ProjectWise CEL documentation

trim

string
<string>.trim()

Removes leading and trailing whitespace

' ab '.trim()
Source: official Bentley ProjectWise CEL documentation

takeLeft

string
<string>.takeLeft(count)

Returns the first count characters

'abcdef'.takeLeft(3)
Source: official Bentley ProjectWise CEL documentation

takeRight

string
<string>.takeRight(count)

Returns the last count characters

'abcdef'.takeRight(3)
Source: official Bentley ProjectWise CEL documentation

dropLeft

string
<string>.dropLeft(count)

Drops the first count characters

'abcdef'.dropLeft(2)
Source: official Bentley ProjectWise CEL documentation

dropRight

string
<string>.dropRight(count)

Drops the last count characters

'abcdef'.dropRight(2)
Source: official Bentley ProjectWise CEL documentation
List functionstake / dropSelect or remove elements from either edge of a list.

takeLeft

list
<list>.takeLeft(count)

Keeps the first count elements

[1,2,3,4].takeLeft(2)
Source: official Bentley ProjectWise CEL documentation

takeRight

list
<list>.takeRight(count)

Keeps the last count elements

[1,2,3,4].takeRight(2)
Source: official Bentley ProjectWise CEL documentation

dropLeft

list
<list>.dropLeft(count)

Drops the first count elements

[1,2,3,4].dropLeft(2)
Source: official Bentley ProjectWise CEL documentation

dropRight

list
<list>.dropRight(count)

Drops the last count elements

[1,2,3,4].dropRight(2)
Source: official Bentley ProjectWise CEL documentation
Time functions and objectstimestamp · duration · PWLocalTimeUTC evaluation time, IANA zones, local formatting, and conversion.

timestamp and duration

timestamp / duration
timestamp(...) ± duration(...)

Core CEL timestamp arithmetic and comparisons remain available.

timestamp('2020-12-10T00:00:00Z') + duration('1h')
Source: official Bentley ProjectWise CEL documentation

toLocalTime

PWLocalTime
<timestamp>.toLocalTime(timeZone)

Converts UTC to a ProjectWise local-time object using a TZDB/IANA name.

utcTime.toLocalTime('Europe/London')
Source: official Bentley ProjectWise CEL documentation

secondsOffset

int
<PWLocalTime>.secondsOffset

Exposes the active time-zone offset in seconds.

utcTime.toLocalTime('Europe/Berlin').secondsOffset
Source: official Bentley ProjectWise CEL documentation

toUTC

timestamp
<PWLocalTime>.toUTC()

Converts a local-time object back to its UTC timestamp.

utcTime.toLocalTime('Europe/London').toUTC()
Source: official Bentley ProjectWise CEL documentation

format local time

string
<format>.format(PWLocalTime[, locale])

Formats after applying the local-time offset.

'{0:yyyy-MM-dd}'.format(utcTime.toLocalTime('Europe/London'))
Source: official Bentley ProjectWise CEL documentation
Source: official Bentley ProjectWise CEL documentation

05 · Help in practice

Official ProjectWise CEL situations

Bentley’s help examples show how context objects and extensions combine into complete attribute expressions. Each pattern below explains the input, transformation, result, and host boundary.

dependent-title-block-date

Dependent title-block date

Set a date only when the author field carries a real value.

thisForm.TB_DRAWN_BY != '---' ? '{0:yyyy-MM-dd}'.format(utcTime) : '---'
How it works

thisForm reads the current unsaved author value. The conditional either formats the evaluation time or preserves the title-block placeholder.

Illustrative ProjectWise valuesthisForm.TB_DRAWN_BY = 'P.Novák'utcTime = timestamp('2026-08-17T09:15:00Z')
Result'2026-08-17'string
thisFormutcTimeformatconditional
Source: official Bentley ProjectWise CEL documentation
file-extension

File extension

Read the final segment of an associated file name.

[thisDocument.file.name.split('.')].map(r, r[r.size()-1])
How it works

The singleton list creates a scoped value, split produces file-name segments, and the final index selects the extension.

Illustrative ProjectWise valuesthisDocument.file.name = 'BS-A1-DR-001.dgn'
Result'dgn'string
thisDocument.filesplitmapsize
Source: official Bentley ProjectWise CEL documentation
fixed-file-name

Fixed-format file name

Extract a named numeric fragment and provide a non-match fallback.

thisDocument.file.name.parse(['{a:N}P{b:N}.{c:N}.DWG']).map(r, r.match == 0 ? r.b : -1)
How it works

parse returns one result map. match identifies the successful pattern and b contains the requested fragment.

Illustrative ProjectWise valuesthisDocument.file.name = '100P20.30.DWG'
Result'20'string
thisDocument.fileparsemapconditional
Source: official Bentley ProjectWise CEL documentation
user-initials

Current-user initials

Build initials from the current user description.

thisUser.description.split(' ').map(r, r.substring(0,1)).join()
How it works

The description is split into words, every word contributes its first character, and join produces one string.

Illustrative ProjectWise valuesthisUser.description = 'Peter Novák'
Result'PN'string
thisUsersplitmapsubstringjoin
Source: official Bentley ProjectWise CEL documentation
document-date

Formatted document date

Format the document update timestamp for an attribute value.

'{0:yyyy-MM-dd}'.format(thisDocument.updatedOn)
How it works

updatedOn is a UTC timestamp; format returns the requested text representation.

Illustrative ProjectWise valuesthisDocument.updatedOn = timestamp('2026-08-17T14:20:00Z')
Result'2026-08-17'string
thisDocument.updatedOnformat
Source: official Bentley ProjectWise CEL documentation
document-path

Document path

Assemble a readable folder path and document name without a legacy FULLNAME token.

thisFolder.getTopFolder().getFolderPath(thisFolder).map(f, f.name).join('\\') + '\\' + thisDocument.name
How it works

The hierarchy function returns Folder objects, map keeps their names, join inserts separators, and the document name is appended.

Illustrative ProjectWise valuesfolder names = ['Project', 'Building A', 'Drawings']thisDocument.name = 'A-101.dgn'
Result'Project\Building A\Drawings\A-101.dgn'string
thisFolderthisDocumentgetTopFoldergetFolderPathmapjoin
Source: official Bentley ProjectWise CEL documentation
Source: official Bentley ProjectWise CEL documentation

06 · BS attributes in practice

Real BS attribute samples

These expressions come from the supplied BS ProjectWise configuration. Each sample connects the actual attribute code to example form values or lookup rows, the expected output, and a step-by-step explanation.

PW_FULLNAMEbs-pw-fullnameResolve a full name from the selected ProjectWise user
Situation in ProjectWise

The form stores a ProjectWise login in PW_USERNAME, but the title block needs the readable user description.

Attribute expression
getLookup('DMS.Users')
  .filterEquals('name', thisForm.PW_USERNAME)
  .selectOne('description', '')
Illustrative datathisForm.PW_USERNAME = 'pnovak'DMS.Users row = {name: 'pnovak', description: 'Peter Novák'}
Result'Peter Novák'string
What happens, step by step
  1. Open the built-in DMS.Users dataset.
  2. Keep the row whose name equals the form login.
  3. Return its description, or an empty string when exactly one row is not available.
thisForm.PW_USERNAMEDMS.Usersnamedescription
Source: supplied BS ProjectWise configuration
PW_USERINITIALSbs-pw-userinitialsBuild initials from the resolved full name
Situation in ProjectWise

PW_FULLNAME already contains a readable name and the next attribute must derive compact initials.

Attribute expression
thisForm.PW_FULLNAME.trim()
  .split(' ')
  .map(p, p.charAt(0))
  .join()
  .toUpper()
Illustrative datathisForm.PW_FULLNAME = 'Peter Novák'
Result'PN'string
What happens, step by step
  1. Remove whitespace around the full name.
  2. Split the name into words and keep the first character of each word.
  3. Join the characters and normalize them to uppercase.
thisForm.PW_FULLNAMEtrimsplitmapcharAtjointoUpper
Source: supplied BS ProjectWise configuration
TB_DRAWN_BYbs-tb-drawn-byPopulate the title-block author only when triggered
Situation in ProjectWise

A trigger decides whether the current user’s configured title-block name is written or a placeholder is kept.

Attribute expression
thisForm.TRIG_DRAWN == '1'
  ? getLookup('Lkp.UserSupl')
      .filterEquals('PW_USERNAME', thisUser.name)
      .selectOne('PW_TBNAME', '')
  : '---'
Illustrative datathisForm.TRIG_DRAWN = '1'thisUser.name = 'pnovak'Lkp.UserSupl row = {PW_USERNAME: 'pnovak', PW_TBNAME: 'P.Novák'}
Result'P.Novák'string
What happens, step by step
  1. Check the trigger first.
  2. Find the current login in the supplemental user lookup.
  3. Return the configured title-block name.
thisForm.TRIG_DRAWNthisUser.nameLkp.UserSuplPW_TBNAME
Source: supplied BS ProjectWise configuration
FI_ROLE_CODEbs-fi-role-codeDerive a role code from the folder hierarchy
Situation in ProjectWise

The role is encoded in the description of a folder at a fixed position below the top folder.

Attribute expression
thisFolder.getTopFolder().getRelativeFolder(-3).description
Illustrative datafolder path = Project > Building A > Architecture > DocumentsDocuments.description = 'ARC'
Result'ARC'string
What happens, step by step
  1. Move from the document folder to the top folder.
  2. Move three levels back toward the document.
  3. Read the selected folder description.
thisFoldergetTopFoldergetRelativeFolderFolder.description
Source: supplied BS ProjectWise configuration
RV_DATE_1bs-rv-date-1Stamp a local approval date
Situation in ProjectWise

The revision date stays as a placeholder until an approver is present; then it uses the London calendar date.

Attribute expression
thisForm.TB_APPROVED_BY == '---'
  ? '---'
  : '{0:yyyy-MM-dd}'.format(utcTime.toLocalTime('Europe/London'))
Illustrative datathisForm.TB_APPROVED_BY = 'A.Smith'utcTime = timestamp('2026-08-17T22:30:00Z')
Result'2026-08-17'string
What happens, step by step
  1. Check whether a real approver exists.
  2. Convert the UTC evaluation time to Europe/London.
  3. Format only the local calendar date.
thisForm.TB_APPROVED_BYutcTimetoLocalTimeformat
Source: supplied BS ProjectWise configuration
PW_USERNAMEbs-pw-usernameOffer only users missing from the supplemental lookup
Situation in ProjectWise

An administrator is adding supplemental user records and should not see logins that are already registered.

Attribute expression
getLookup('DMS.Users')
  .select('name')
  .map(dmsuser,
    !(dmsuser in getLookup('Lkp.UserSupl').select('PW_USERNAME')),
    dmsuser)
Illustrative dataDMS.Users names = ['pnovak', 'asmith', 'bnovak']Lkp.UserSupl PW_USERNAME values = ['pnovak', 'asmith']
Resultonly bnovak remains selectablevalue-list rows
What happens, step by step
  1. Load all datasource user names.
  2. Load user names already present in Lkp.UserSupl.
  3. Keep only names that are not in the supplemental set.
DMS.UsersLkp.UserSuplselectmapin
Source: supplied BS ProjectWise configuration

07 · Reuse and proof

Library Expressions

Database-stored CEL expressions are registered below lib. A library expression accepts one map argument, reads it through arg, and can return any CEL-supported type.

lib.calculate({'x': 1, 'y': 2})

// inside the library expression
arg.x + arg.y

PowerShell management

Get-PWCelLibraryExpressionNew-PWCelLibraryExpressionUpdate-PWCelLibraryExpressionRemove-PWCelLibraryExpression

Testing with pwps

Invoke-PWCelExpression supports contextless, version, and Document Attributes executions with the corresponding ProjectWise context values.

ContextlessInvoke-PWCelExpression -Expression "1 + 1"
Version contextInvoke-PWCelExpression -Expression "currentVersion.incrementAlphaVersion()" -CurrentVersion "A"
Document Attributes contextInvoke-PWCelExpression -Expression "thisDocument.name" -DocumentGUID "<guid>" -FormFields @{ Field = "value" }
Built-in WRE version expressions

ProjectWise documents these 20 DMS.* version algorithms. Library Expressions can expose built-ins through the lib.DMS.* namespace.

DMS.INC_ALPHADMS.INC_ALPHANUM_MAJDMS.INC_ALPHANUM_MAJ_P1DMS.INC_ALPHANUM_MINDMS.INC_ALPHANUM_MIN_P1DMS.INC_MAJDMS.INC_MAJ_P1_P1DMS.INC_MAJ_P1_P2DMS.INC_MAJ_P2_P1DMS.INC_MINDMS.INC_MIN_P1_P1DMS.INC_MIN_P1_P2DMS.INC_MIN_P2_P1DMS.INC_NUMBERDMS.INC_NUMBER_P1DMS.PUBDMS.PUB_P1DMS.REMOVE_ALPHANUM_MINDMS.REMOVE_MINDMS.REMOVE_MIN_P1
Source: official Bentley ProjectWise CEL documentation

08 · Legacy → PW CEL

Migration from legacy configurations

This is one part of PW CEL. Cases distinguish ProjectWise system variables from actual SQL functions and identify whether the change is direct, semantic, or a redesign.

What the Bentley migration appendix covers

ProjectWise system variables

Context values such as user, form, folder, WorkArea, and UTC time. Internal numeric IDs, table names, and several legacy variables have no direct CEL access.

$USER.NAME$$EDIT#XXX$VAULT.NAME$$DATE$

Built-in SQL functions

Documented replacements include conversions, conditions, null fallback, strings, dates, and collection operations. Several SQL functions have no direct replacement.

IIFISNULLCHARINDEXDATEADDSUBSTRING

ISO19650 stored procedures

Bentley documents CEL replacement patterns for these six legacy procedures.

dms_ISO19650_attr_lkpdms_ISO19650_attr_lkp_statedms_ISO19650_user_name_calcdms_ISO19650_action_namesdms_ISO19650_datedms_ISO19650_root_folder_attribute
Source: official Bentley ProjectWise CEL documentation

Primary Bentley sources

ProjectWise Administrator 2026 · PW CEL HelpProjectWise CEL LookupsList of Built-in LookupsLibrary ExpressionsProjectWise Administrator 2026