Author SHA1 Message Date
dc e995626ff4 docs(options): update the EDIT and VIEW size limits
DC_MAXOBS_WEBEDIT 100 -> 250, DC_MAXOBS_WEBVIEW 500 -> 2000, and a new
DC_MAXCELLS_WEBEDIT (200000) which is applied together with the EDIT
observation limit - whichever is reached first.
2026-10-03 11:57:36 +00:00
allan c800145ead Merge pull request 'feat(theme): dark mode by default, with a light/dark toggle' (#13) from docs/dark-mode-toggle into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m17s
Reviewed-on: #13
2026-09-28 23:44:54 +00:00
dc 504a3136b7 feat(theme): dark mode by default, with a light/dark toggle
The docs site only offered Material's light scheme. This adds the dark
scheme and makes it the default.

- palette: two entries, slate first, each with a toggle. Material takes the
  first entry as the default, so the site opens dark and a sun icon in the
  header switches to light (and a moon switches back).
- dc-brand.css: the dark scheme's greys are derived from a hue variable, so
  it is pointed at the brand slate (206deg) rather than Material's neutral
  225deg, which gives the whole scheme the same cast as the header.
- dc-brand.css: the brand slate is unreadable on a dark background, so the
  dark scheme uses a light tint of it (#9fb5c6) for body links. Green stays
  the accent, which keeps link hover visible in both schemes.

Contrast, over the dark background rgb(30, 36, 41): body text 8.8:1, body
links 7.4:1, inline code 7.2:1, accent green 5.6:1, header text 10.2:1. The
light scheme is unchanged.
2026-09-28 23:15:54 +00:00
allan 2e269341df Merge pull request 'style(theme): apply the Data Controller brand palette and logo' (#12) from docs/onbrand-styling into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m16s
Reviewed-on: #12
2026-09-28 23:03:26 +00:00
dc 6d09a8e1d8 style(theme): apply the Data Controller brand palette and logo
The docs site rendered as stock Material: indigo header, indigo links and
Open Sans. Neither colour nor type matched the marketing site
(datacontroller.io) or the application itself.

- Palette: the slate (#314351) and green (#79a843) already used by the
  marketing navbar, hero and logo accent, wired up via a `custom` palette
  and defined in the new docs/dc-brand.css.
- Type: Montserrat for text, matching the marketing site's headings.
- Logo: the application's own dc-logo.svg mark in the header.

The previous `palette:` mapping form (a dict of primary/accent) is no
longer honoured by the Material version this repo builds against, so the
theme fell through to Material's default indigo - the site was not in fact
rendering the White/Amber it asked for. The list form used here is the one
Material currently reads.
2026-09-28 22:55:05 +00:00
allan 19e50fdf6a Merge pull request 'docs(troubleshooting): note the platform's own URL parameters' (#11) from docs/platform-params into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m13s
Reviewed-on: #11
2026-09-28 00:17:27 +00:00
dc 6f4166dad3 docs(troubleshooting): note the platform's own URL parameters
On SAS 9 and Viya the app is reached through the platform's job URL, so
work.browser_url_vars carries the platform's parameters (_FILE, _program,
_debug, _webout) alongside DC's. Say so, so a hook author does not read
_FILE as a Data Controller parameter.
2026-09-27 22:42:51 +00:00
allan aff6806bb9 Merge pull request 'docs(troubleshooting): document the browser_info / browser_url_vars support diagnostics' (#9) from docs/support-diagnostics into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m21s
Reviewed-on: #9
2026-09-27 20:51:20 +00:00
dc 13042876b8 docs(troubleshooting): a worked URL-parameter example for the hook read 2026-09-27 20:47:53 +00:00
dc 7f2a76986a docs(troubleshooting): correct the browser_url_vars collision, add hook read examples 2026-09-27 20:20:07 +00:00
allan 90b374a52e Merge pull request 'docs(downloads): new page for release assets and download verification' (#10) from docs/release-download-verification into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m17s
Reviewed-on: #10
2026-09-25 19:35:08 +00:00
dc 4316f519c4 docs(downloads): appLoc is a metadata folder, not a disk location
The integrity section claimed SAS writes nothing to disk outside the
appLoc. The appLoc is the SAS Folder the stored processes or jobs are
deployed into (metadata), so it is not a disk location at all - the only
physical location the deployment programs write to is the Data Controller
location (dcLoc) configured at first launch.
2026-09-25 19:33:42 +00:00
dc be8d92f439 docs(troubleshooting): note the 128 debug value and the loadfile upload limitation
The adapter sends _debug=128 (not 131) on the Viya web JES path when
runAsTask is enabled, which is what the frontend uses there. Document
that value alongside 131, and note that editors/loadfile is reached
through the multipart file upload, which carries no input tables.
2026-09-25 07:55:49 +00:00
dc 7770737963 docs(troubleshooting): scope the diagnostics tables to their services, add browser_url_vars
The support diagnostics are now sent only where they can be used: the
startup service and the services that execute customer-provided code
(hook scripts, dynamic cell dropdown programs) - not with every service
call. The page's URL parameters arrive as a new browser_url_vars table,
one row per parameter, so a SAS developer reads a parameter by name
rather than parsing the url string.
2026-09-25 00:21:56 +00:00
dc fdb39a78a5 docs(downloads): new page for release assets and download verification
Adds /downloads to the Installation section, listing what each release
asset is for and how to verify a download against the SHA256SUMS file
the release pipeline publishes (sha256sum -c, plus certutil for
Windows). The SAS 9 deployment page now links to it wherever it sends
the reader to the releases page.

mkdocs build passes; nav, sitemap and cross-links verified in the
rendered output.
2026-09-24 22:30:38 +00:00
dc 2a65664a51 docs(troubleshooting): document the browser_info support diagnostics
Every service call carries a single-row browser_info input table, readable
by any service or hook script as work.browser_info and dumped to the job log
when debug is on. Document its columns and how to see it, so a support ticket
can be answered from the log without follow-up questions.
2026-09-24 20:29:30 +00:00
allan c21f413f06 Merge pull request 'docs(validations): document how each rule treats a special missing value' (#8) from docs/special-missing-rule-behaviour into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m20s
Reviewed-on: #8
Reviewed-by: Allan <allan@4gl.io>
2026-09-23 22:30:41 +00:00
dc 10448de15b docs(validations): point the range rules at the special missing section, and tidy
MINVAL and MAXVAL now say what a special missing does to them and that the bound
itself may be a special missing, pointing at the Special Missing Values section,
since that is the least obvious thing about them.

Also in that section: the framing sentence covers the range rules' ordering, the
summary sentence is rewritten (it read badly), and the arrow in the ROUND row is
plain ASCII.
2026-09-23 22:00:18 +00:00
dc 83641a204f docs(validations): a range rule compares in SAS's own order
A range rule keys both the cell value and its own value into the order SAS uses
for a numeric variable: every missing below every non-missing value, and the
missing values ordered ._ then the regular missing then .A through .Z. So a range
can be written in special missings and mean what SAS means by it - MINVAL .A with
MAXVAL .C accepts .B and rejects .D - and the numeric cases follow from the same
order: a missing fails a numeric MINVAL and passes a numeric MAXVAL, and a number
sits above every missing.

Replaces the "a range rule ignores a missing value" wording, which described the
previous take's behaviour rather than SAS's.
2026-09-23 21:27:57 +00:00
dc 66c48a4cc7 docs(validations): a range rule ignores a missing value
Three corrections to the special missing section:

- the period is optional: a special missing is typed as `a` or `.a`, `_` or `._`
- MINVAL and MAXVAL both accept a missing value, and both still reject a real
  number that is out of range. A minimum constrains a number, and a missing is
  not a number - NOTNULL is the rule for a column that must be populated
- only NOTNULL and the formulas actually reject a special missing, so the
  "not supported" summary is replaced with what does reject it and what does not
2026-09-23 21:01:11 +00:00
dc 1a5cea9ebe docs(validations): a date formatted column takes no special missing 2026-09-23 15:38:37 +00:00
dc abf01d6375 docs(validations): the dropdowns support special missings
Both SOFTSELECT and HARDSELECT support them, and the dropdown lists them
alongside the ordinary values (no NaN entries for a numeric column).
2026-09-23 08:37:12 +00:00
dc e7fdb3ab52 docs(validations): primary keys are NOT NULL; HARDSELECT can match a special missing
- a primary key column rejects a blank and a special missing whether or not
  the table carries a constraint or MPE_VALIDATIONS a NOTNULL rule
- HARDSELECT is now checked like any other value: it passes when the
  dropdown list contains the special missing
- HARDSELECT drops out of the 'not supported' list
2026-09-23 08:19:40 +00:00
dc 4d53ced89c docs(validations): document how each rule treats a special missing value
New Special Missing Values section covering NOTNULL, MINVAL/MAXVAL, CASE,
ROUND, the regex rules, the dropdowns and the formula rules, with the
'not supported' set called out.  Behaviour verified against a real SAS
estate and the deployed frontend validator.
2026-09-23 07:50:06 +00:00
allan 7d0e349247 Merge pull request 'docs(viewer): correct the full table search row count claim' (#6) from docs/full-table-search-row-cap into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m12s
Reviewed-on: #6
2026-09-22 16:38:19 +00:00
dc 99b2a7454e docs(viewer): state the row count rule for the unsearched view too
Follow-up to the previous commit on this branch, both wording fixes.

- The sentence this branch deleted was the page's only description of the row
  count when no search is active - and that is the case where the count really
  can exceed the rows displayed (viewdata reports count(*) for the filtered
  view while the grid stops at DC_MAXOBS_WEBVIEW). State it, instead of leaving
  the page silent on the path readers use most.
- "returns the first 500" claims an ordering the search does not apply: the
  search is a WHERE clause with no ORDER BY, so which 500 come back is whatever
  the engine hands over first. Say "returns 500 of them".

Checked against viewdata.sas and mp_searchdata.sas on main: the search path
passes outobs=DC_MAXOBS_WEBVIEW to mp_searchdata, which caps the result dataset
in the same data step that builds it, and viewdata takes its row count from that
dataset - so for a search the count and the rows are capped together.
2026-09-22 16:34:14 +00:00
allan 046f24d0b7 Merge pull request 'docs(sas9): unzip the frontend archive into its own folder' (#7) from docs/frontend-zip-flat-layout into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m21s
Reviewed-on: #7
Reviewed-by: Allan <allan@4gl.io>
2026-09-22 16:31:29 +00:00
dc bd30a059ed docs(sas9): unzip the frontend archive into its own folder
frontend.zip now holds the frontend files at their root (dc/dc #147, released in
v7.14.2), so unzipping no longer produces a client/dist folder to navigate into.

Step 2 now says to create the app folder and unzip the archive into it, naming
the file that should end up there. Step 3's URL example matches the folder the
reader created, and the intro line drops "to the root of" - it contradicted the
subfolder instruction directly below it.
2026-09-22 16:28:10 +00:00
dc 206d88c6a9 docs(viewer): correct the full table search row count claim
The row count next to the table name is the capped count, not the total
number of matches. A search that matches more rows than DC_MAXOBS_WEBVIEW
returns the first 500 and reports 500, so the count does not rise above the
cap even when many more rows match.
2026-09-17 11:43:23 +00:00
allan cd47d0e442 Merge pull request 'docs(viewer): document full table search semantics with a screenshot' (#5) from docs/full-table-search into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m18s
Reviewed-on: #5
Reviewed-by: Allan <allan@4gl.io>
2026-09-17 09:49:11 +00:00
dc 85f200c724 docs(viewer): correct the character search example to match the code
The example claimed `smith` finds `Smithson`, which only holds for a case
insensitive match. The search is a case sensitive CONTAINS match - DC passes
the search value straight to %mp_searchdata, which uses the SAS `?` operator -
so `smith` finds `Goldsmith` (a lowercase substring) and not `Smithson`.
2026-09-16 23:42:37 +00:00
dc 4a53499055 docs(viewer): show the matching column in the full table search screenshot
The screenshot showed the siphonophore search returning 3 rows, but the
NOTES column - the only column the value appears in - was clipped off the
right edge, so the image did not show why those rows matched.

Re-captured at 1920x900 (all nine columns on screen, no clipping) from the
mocked instance, with the capture gated on an assertion that the matched
cell is inside the grid viewport.
2026-09-16 23:38:14 +00:00
dc d95f440c4f docs(viewer): document full table search semantics with a screenshot
The "Full Table Search" section said only that a search box exists. It now
states the behaviour that users actually hit:

- the search covers every column at once, character columns by case sensitive
  CONTAINS and numeric columns by exact match
- the Numeric toggle switches to exact numeric matching
- an applied filter scopes the search
- results are capped by DC_MAXOBS_WEBVIEW (linked to the options page), and the
  row count next to the table name reports the match count
- a search with no matches shows the "No data found with given conditions"
  panel

Adds a screenshot of the search in action (docs/img/full-table-search.png),
captured from a running instance via the Cypress suite in the dc repo.
2026-09-16 00:52:12 +00:00
hermes 94b27628aa Merge pull request 'docs: remove customer reference and conversion-function warning from timezone FAQ' (#4) from fix/timezone-docs-redact into main
Publish to docs.datacontroller.io / Deploy docs (push) Successful in 1m12s
2026-09-04 11:34:05 +00:00
dc-bot 5236b74fee docs: remove customer reference and conversion-function warning from timezone FAQ 2026-09-04 11:33:49 +00:00
hermes 06df7e6e23 Merge pull request 'docs: remove customer-identifying details from timezone troubleshooting section' (#3) from fix/timezone-docs-redact into main
Publish to docs.datacontroller.io / Deploy docs (push) Failing after 13m6s
2026-09-04 10:21:03 +00:00
dc-bot 7e49033aae docs: remove customer-identifying details from timezone troubleshooting section 2026-09-04 10:20:08 +00:00
10 changed files with 289 additions and 23 deletions

No files matched your search

+45
View File
@@ -0,0 +1,45 @@
/*
* Data Controller brand overrides for the Material theme.
*
* The values below mirror the marketing site (https://datacontroller.io): the
* slate used for its navbar and hero, and the green used for its logo accent.
* They are wired up through the `custom` palette in mkdocs.yml, which leaves
* these CSS variables for us to define.
*/
/* Primary - the brand slate, used for the header, the mobile drawer title and body links. */
:root,
[data-md-color-primary='custom'] {
--md-primary-fg-color: #314351;
--md-primary-fg-color--light: #3d5468;
--md-primary-fg-color--dark: #263542;
--md-primary-bg-color: #fff;
--md-primary-bg-color--light: #ffffffb3;
--md-typeset-a-color: #314351;
}
/* Accent - the brand green, used for link hover, focus rings and the active search hit. */
[data-md-color-accent='custom'] {
--md-accent-fg-color: #79a843;
--md-accent-fg-color--transparent: #79a8431a;
--md-accent-bg-color: #fff;
--md-accent-bg-color--light: #ffffffb3;
}
/*
* Dark scheme. Material derives its dark greys from a hue variable, so pointing
* that at the brand slate (206deg) gives the whole scheme the same cast as the
* header instead of Material's neutral default.
*/
[data-md-color-scheme='slate'] {
--md-hue: 206deg;
}
/*
* The brand slate is only readable on a light background, so the dark scheme
* uses a light tint of it for body links. Green stays the accent, which keeps
* link hover visible in both schemes.
*/
[data-md-color-scheme='slate'][data-md-color-primary='custom'] {
--md-typeset-a-color: #9fb5c6;
}
+9 -2
View File
@@ -20,16 +20,23 @@ Currently used scopes include:
### DC_EMAIL_ALERTS
Set to YES or NO to enable email alerts. This requires email options to be preconfigured (mail server etc).
### DC_MAXCELLS_WEBEDIT
By default, a maximum of 200,000 cells (observations multiplied by variables) can be edited in the browser at one time. This limit is applied together with [DC_MAXOBS_WEBEDIT](#dc_maxobs_webedit) - whichever is reached first.
The cell count matters because the cost of an EDIT screen load tracks the number of cells, not the number of rows. A 10 column table is comfortable at 250 rows, but 250 rows of a 1000 column table is 250,000 cells.
### DC_MAXOBS_WEBEDIT
By default, a maximum of 100 observations can be edited in the browser at one time. This number can be increased, but note that the following factors will impact performance:
By default, a maximum of 250 observations can be edited in the browser at one time. This number can be increased, but note that the following factors will impact performance:
* Number of configured [Validations](/dcc-validations)
* Browser type and version (works best in Chrome)
* Number (and size) of columns
* Speed of client machine (laptop/desktop)
A wide table can reach the [DC_MAXCELLS_WEBEDIT](#dc_maxcells_webedit) limit well before this one, so increase both together.
### DC_MAXOBS_WEBVIEW
By default, a maximum of 500 observations can be viewed in the browser at one time. Please see previous section for items to consider if increasing this value.
By default, a maximum of 2000 observations can be viewed in the browser at one time. Please see previous section for items to consider if increasing this value.
### DC_REQUEST_LOGS
On SASjs Server and SAS9 Server types, at the end of each DC SAS request, a record is added to the [MPE_REQUESTS](/tables/mpe_requests) table. In some situations this can cause table locks. To prevent this issue from occuring, the `DC_REQUEST_LOGS` option can be set to `NO` (Default is `YES`).
+33 -5
View File
@@ -23,17 +23,17 @@ It is possible to configure a number of other rules by updating the MPE_VALIDATI
## Configurable Checks
Check back frequently as we keep growing this list of checks.
Check back frequently as we keep growing this list of checks. For how each rule treats a special missing value (`.A`-`.Z`, `._`), see [Special Missing Values](#special-missing-values).
|Rule Type|Example Value |Description|
|---|---|---|
|CASE|UPCASE|Will enforce the case of cell values. Valid values: UPCASE, LOWCASE, PROPCASE|
|NOTNULL|(defaultval)|Will prevent submission if null values are present. Optional - provide a default value.|
|MINVAL|1|Defines a minimum value for a numeric cell|
|MAXVAL|1000000|Defines a maximum value for a numeric cell|
|NOTNULL|(defaultval)|Will prevent submission if null values are present - a special missing counts as null. Optional - provide a default value.|
|MINVAL|1|Defines a minimum value for a numeric cell. A special missing value sorts below every number, so it fails a numeric floor, and the floor itself may be a special missing - see [Special Missing Values](#special-missing-values).|
|MAXVAL|1000000|Defines a maximum value for a numeric cell. A special missing value sorts below every number, so it passes a numeric ceiling, and the ceiling itself may be a special missing - see [Special Missing Values](#special-missing-values).|
|READONLY|(defaultval) |Renders the column read-only in the editor. The defaultval is used when rows are added. |
|HIDDEN|(defaultval) |Hides the column from the editor grid while still submitting its data. The defaultval is used when rows are added. |
|ROUND|2 |Rounds numeric input on paste/edit. Positive digits round to number of decimal places (eg 2 rounds to 0.01) Negative digits round to the nearest ten/hundred etc. Half-away-from-zero rounding is applied so `-0.5 → -1` and `2.5 → 3`. |
|ROUND|2 |Rounds numeric input on paste/edit. Positive digits round to number of decimal places (eg 2 rounds to 0.01) Negative digits round to the nearest ten/hundred etc. Half-away-from-zero rounding is applied so `-0.5 -> -1` and `2.5 -> 3`. |
|NUMBER_FORMAT|`{"style":"currency","currency":"GBP"}` |Display-only [`Intl.NumberFormat` renderer](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat). RULE_VALUE is the JSON options object passed straight to `Intl.NumberFormat`. Does not change the stored value. |
|HARDFORMULA|`= PRICE * VOLUME`|The cell displays a value computed from a formula, using other columns in the same row. The column is read-only - the user cannot override the result. See [Formula Rules](#formula-rules) below.|
|SOFTFORMULA|`= if( DC.ROW_STATUS != 'U', DC.USER_NAME, DC.ORIG_VALUE )`|Like HARDFORMULA, but the user can override the computed value and type their own. See [Formula Rules](#formula-rules) below.|
@@ -45,6 +45,34 @@ Check back frequently as we keep growing this list of checks.
|[SOFTSELECT_HOOK](/dynamic-cell-dropdown)|/physical/path/program.sas|A SAS service (STP or Viya Job) or a path to a SAS program on the filesystem. User-provided values may (or may not) be in this list. Cannot be used alongside a HARDSELECT_HOOK.|
## Special Missing Values
A SAS numeric variable has 28 missing values: the regular `.` and 27 special ones (`.A`-`.Z` and `._`). Data Controller accepts them in a numeric cell as a letter - `a` to `z` - or an underscore, with or without a leading period (`a`, `.a`, `_`, `._`), and stores the matching special missing in SAS. The period is optional, and a plain `.` stores the regular missing.
A numeric column that carries a date, datetime or time format is the exception. Those cells are edited through a date picker rather than the numeric editor, and the picker accepts only a date - so a special missing cannot be typed into one of them.
The rules treat a special missing as a value for some checks and as a missing value for others - and the range rules compare it in the order SAS gives it, below every number but ordered among the other missing values:
|Rule|What happens to a special missing|
|---|---|
|NOTNULL|Fails. A special missing is a missing value, so a NOT NULL (or primary key) constraint rejects it, and a real SAS NOT NULL constraint on the target table rejects it too.|
|MINVAL|Fails if the minimum is a number - a missing sorts below every number, so it is below any numeric floor. A minimum that is itself a special missing is compared in the order the missing values have among themselves, and can accept one.|
|MAXVAL|Passes - a missing sorts below every number, so it is below any numeric ceiling. A ceiling that is itself a special missing is compared in the missing values' own order, and can reject one.|
|CASE|Not applicable - it is a character rule.|
|ROUND|Ignored - ROUND only rounds values that are numbers, so a special missing is left as typed.|
|HARDREGEX / SOFTREGEX|Checked against the pattern like any other value. Unlike blanks and the plain `.`, special missings are **not** exempt. See [Regex Rules](#regex-rules).|
|SOFTSELECT|Supports special missings - a soft dropdown never blocks a value.|
|HARDSELECT|Supports special missings - the value is matched against the dropdown list like any other value.|
|HARDFORMULA / SOFTFORMULA|The formula returns `#VALUE!` for that row instead of a number, and that is what gets submitted.|
In short, a special missing is rejected by NOTNULL, by a primary key column, and by a MINVAL whose floor is a number; a formula returns `#VALUE!` for it. It is fine in a column that carries a MAXVAL, a regex, a dropdown, or ROUND - and it can satisfy a MINVAL when the floor is expressed as a special missing.
A range rule compares in the order SAS itself uses, so a range written in special missings means what SAS would mean by it. That order puts every missing below every non-missing value, and orders the missing values as `._`, then the regular missing, then `.A` through `.Z`. So `MINVAL .A` with `MAXVAL .C` accepts `.B` and rejects `.D`, and a blank - the regular missing - fails a floor of `.A` because it sorts below it. A number sorts above every missing, so it passes a floor of `.A` and fails a ceiling of `.C`.
A primary key column is treated as NOT NULL whether or not the target table carries a physical constraint, and whether or not MPE_VALIDATIONS has a NOTNULL rule for it - a key identifies the row, so a blank and a special missing are both rejected there.
A range rule's own value is read as a number when it looks like one and as a missing value when it is a special missing. A value that is neither - a typo such as `..` or `AB` - satisfies nothing, so every cell in the column fails until the rule is corrected.
## Formula Rules
HARDFORMULA and SOFTFORMULA let you configure a column so that its value is automatically calculated from other columns in the same row - just like a spreadsheet formula. When a user opens the editor, the formula is evaluated live and the result is shown in each cell.
+8 -6
View File
@@ -14,6 +14,8 @@ There are two ways to deploy Data Controller on SAS 9:
* Full Deployment (preferred)
* Streaming (for quick demos)
Both fetch a deployment program from the [releases page](https://git.datacontroller.io/dc/dc/releases) - see the [downloads page](/downloads) for what each asset contains and how to verify it.
### Full Deployment
#### 1 - Deploy Stored Processes
@@ -27,16 +29,16 @@ filename dc url "https://git.datacontroller.io/dc/dc/releases/download/latest/sa
%inc dc;
```
If you don't have internet access from SAS, download `sas9.sas` from [here](https://git.datacontroller.io/dc/dc/releases), and change the initial `compiled_apploc` and `compiled_serverName` macro variable assignments as necessary.
If you don't have internet access from SAS, download `sas9.sas` from [here](https://git.datacontroller.io/dc/dc/releases), verify it against the release `SHA256SUMS` file ([downloads page](/downloads/#verifying-a-download)), and change the initial `compiled_apploc` and `compiled_serverName` macro variable assignments as necessary.
#### 2 - Deploy the Frontend
The Data Controller frontend comes pre-built, and ready to deploy to the root of the SAS Web Server (mid-tier).
The Data Controller frontend comes pre-built, and ready to deploy to the SAS Web Server (mid-tier).
Deploy as follows:
1. Download the `frontend.zip` file from: [https://git.datacontroller.io/dc/dc/releases](https://git.datacontroller.io/dc/dc/releases)
2. Unzip and place in the [htdocs folder of your SAS Web Server](https://sasjs.io/frontend-deployment/#sas9-deploy) - typically a subdirectory of: `!SASCONFIG/LevX/Web/WebServer/htdocs`.
1. Download the `frontend.zip` file from: [https://git.datacontroller.io/dc/dc/releases](https://git.datacontroller.io/dc/dc/releases) (verify it against the release `SHA256SUMS` file - see the [downloads page](/downloads/#verifying-a-download))
2. Create a folder for Data Controller in the [htdocs folder of your SAS Web Server](https://sasjs.io/frontend-deployment/#sas9-deploy) - typically a subdirectory of: `!SASCONFIG/LevX/Web/WebServer/htdocs` - and unzip the archive **into** that folder. The archive holds the frontend files at its root, so `index.html` sits directly inside your folder (alongside the `images` folder and the compiled asset files) - for example `htdocs/dc/index.html`.
3. Open the `index.html` file and update the values in the `<sasjs>` tag as follows:
* `appLoc` - same as per SAS code in the section above
@@ -47,7 +49,7 @@ Deploy as follows:
The remaining properties are not relevant for a SAS 9 deployment and can be **safely ignored**.
You can now open the app at `https://YOURWEBSERVER/unzippedfoldername` (step 2 above) and follow the configuration steps (DC Physical Location and Admin Group) to complete deployment.
You can now open the app at `https://YOURWEBSERVER/<yourfoldername>` (the folder created in step 2 above) and follow the configuration steps (DC Physical Location and Admin Group) to complete deployment.
#### 3 - Run the Configurator
@@ -93,7 +95,7 @@ filename dc url "https://git.datacontroller.io/dc/dc/releases/download/vX.X.X/de
%inc dc;
```
If you don't have internet access from your SAS environment, just download `demostream_sas9.sas` from [https://git.datacontroller.io/dc/dc/releases](https://git.datacontroller.io/dc/dc/releases) and modify the `appLoc` on line 2, as follows:
If you don't have internet access from SAS, download `demostream_sas9.sas` from [https://git.datacontroller.io/dc/dc/releases](https://git.datacontroller.io/dc/dc/releases) (verify it against the release `SHA256SUMS` file - see the [downloads page](/downloads/#verifying-a-download)) and modify the `appLoc` on line 2, as follows:
![](img/sas9_apploc.png)
+97 -5
View File
@@ -112,7 +112,7 @@ The error may also be thrown due to an encoding issue - changing to a UTF-8 serv
Data Controller records timestamps (such as the SUBMITTED column on the Submitted screen, and the audit history) using the SAS session clock, via the `datetime()` function. That function returns the time of the operating system, adjusted by the [TIMEZONE= system option](https://documentation.sas.com/doc/en/pgmsascdc/default/lesysoptsref/p15siqs0s00e50n1wuuvygzkr14r.htm) if it is set. The value is then displayed in the frontend exactly as stored, without conversion.
On Viya, the SAS compute sessions that run Data Controller jobs are started inside Kubernetes containers whose clock is UTC by default, and which inherit no timezone from the host machine. If the TIMEZONE= option is not set for the compute context used by Data Controller, every timestamp DC records and displays is UTC. A customer in Germany (CEST, UTC+2 in summer) will therefore see submission times exactly 2 hours behind the wall clock. Other SAS products can appear unaffected because clients such as SAS Studio create their own compute sessions and pass the browser timezone / locale, whereas Data Controller submits jobs to its own shared compute context, which gets the raw container clock.
On Viya, the SAS compute sessions that run Data Controller jobs are started inside Kubernetes containers whose clock is UTC by default, and which inherit no timezone from the host machine. If the TIMEZONE= option is not set for the compute context used by Data Controller, every timestamp DC records and displays is UTC. Other SAS products can appear unaffected because clients such as SAS Studio create their own compute sessions and pass the browser timezone / locale, whereas Data Controller submits jobs to its own shared compute context, which gets the raw container clock.
To confirm the current state, run the following in a SAS session under the Data Controller compute context (or check the DC job log, where `_DEBUG` output shows the same values):
@@ -136,11 +136,103 @@ Then recycle any existing compute sessions - hot sessions keep the old setting u
!!! note
Changing the timezone affects new timestamps only. Previously recorded submissions keep the UTC values that were stored when they were created.
!!! warning
If you have downstream code that already converts DC timestamps from UTC to local time (for example a post-approve hook using `tzoneu2s(processed_dttm, 'Europe/Amsterdam')`), remove that conversion after applying the fix - otherwise the offset is applied twice and the times are wrong again.
## Determining Application Version
The app version is bundled into the frontend during the release, and is visible by clicking your username in the top right.
You can also determine the app version (and SASjs Version, and build time) by opening browser Development Tools and running `appinfo()` in the console.
You can also determine the app version (and SASjs Version, and build time) by opening browser Development Tools and running `appinfo()` in the console.
## Support Diagnostics
The frontend sends two input tables with the startup service and with the services that execute customer-provided code - the [hook scripts](macros.md) (pre/post edit and approve hooks) and the dynamic cell dropdown programs. They are available in those services as `work.browser_info` and `work.browser_url_vars`, so a support ticket can be diagnosed from the job log without asking the user follow-up questions, and a hook can adapt to its context. Other services do not receive them.
The services that receive the tables are: `public/startupservice`, `editors/getdata`, `editors/getdynamiccolvals`, `editors/stagedata`, `editors/loadfile`, `editors/restore` and `auditors/postdata`.
`editors/loadfile` is the exception in practice: the app reaches it through the adapter's file-upload call (multipart), which carries no input tables, so the service - and the post edit hook it runs - does not receive them in normal use. It is on the list for completeness, and would receive them if the service were ever invoked through the ordinary request path.
### browser_info
A single-row table with these columns:
| Column | Description |
|---|---|
| `url` | The URL of the Data Controller page itself (the iframe), not the document embedding it. When the editor is embedded in a report, the embedding URL is visible here. |
| `referrer` | The embedding document, from `document.referrer`. For an embedded report this is the report URL, so the embedding report can be told apart from the editor URL. |
| `timezone` | The browser's IANA timezone name, e.g. `Europe/Berlin`. |
| `tz_offset` | The browser's UTC offset in minutes, as `Date.getTimezoneOffset()` returns it (positive west of UTC, so `Europe/Berlin` in summer is -120). |
| `locale` | The browser locale, e.g. `en-GB`. |
| `dc_version` | The Data Controller build, as shown by `appinfo()`. |
| `adapter_version` | The `@sasjs/adapter` version the client was built with. |
| `browser` | The browser family, parsed from the user agent. |
| `browser_version` | The browser version. |
| `platform` | The operating system, parsed from the user agent. |
| `user_agent` | The raw `navigator.userAgent` string. |
### browser_url_vars
The page URL parameters as one row per parameter, so a SAS program can read a parameter by name rather than parse the `url` string:
| Column | Description |
|---|---|
| `name` | The URL parameter name. |
| `value` | The URL parameter value. |
Parameters from both the search string and the hash query string (Angular routes carry them after the `#`) are included; on a name collision the search string value wins. The table is sent only when the page URL has at least one parameter.
On SAS 9 and Viya the app is reached through the platform's own job URL, so the platform's parameters (`_FILE`, `_program`, `_debug`, `_webout` and friends) are in the table alongside DC's. Read a parameter by name rather than assuming every row belongs to Data Controller - `embed` and `labels` are DC's, `_FILE` and `_program` are the platform's.
The values in both tables are supplied by the client, so treat them as diagnostics hints rather than a security boundary.
To see the tables, run any of the services above with debug on. The debug value depends on the path: `&_debug=131` on the Compute API and SAS 9, or `&_debug=128` on the Viya web (JES) path with `runAsTask` enabled - which is what the frontend sends there, so turning debug on in the app is enough. Either value makes the session initialisation write the tables to the job log:
```
NOTE: MPEINIT: work.browser_url_vars:
name=embed value=va
NOTE: MPEINIT: work.browser_info:
url=... referrer=... timezone=Europe/Berlin tz_offset=-120 locale=en-GB
dc_version=7.15.0 adapter_version=4.19.0 browser=Chrome browser_version=120.0
platform=Linux user_agent=...
```
A service called directly (for instance from a URL, or by a script), or by an older frontend, has no `work.browser_info` - the log then records that fact instead.
### Reading the tables from a hook
A [hook script](macros.md) is `%include`d into the running service, so it shares the service's WORK library and can read the tables directly. Take an embedded editor opened at:
```
https://datacontroller.io/#/editor/SASHELP.CLASS?labels=true&embed=va
```
`browser_url_vars` then holds a row per parameter - `labels=true` and `embed=va` - so a hook can pick up either by name:
```sas
%let labels=N;
%let embed=N;
%if %sysfunc(exist(work.browser_url_vars)) %then %do;
proc sql noprint;
select value into :labels
from work.browser_url_vars
where name = 'labels';
select value into :embed
from work.browser_url_vars
where name = 'embed';
quit;
%end;
```
To branch on what the client reports:
```sas
%if %sysfunc(exist(work.browser_info)) %then %do;
data _null_;
set work.browser_info;
call symputx('dc_timezone', timezone);
call symputx('dc_locale', locale);
run;
%end;
```
!!! warning
Guard with `%sysfunc(exist())` before reading. A service called directly, or by a frontend older than this feature, has no `work.browser_info` at all, so an unguarded `set` or `select` fails the job.
+12
View File
@@ -16,6 +16,18 @@ Row and Column level security can also be applied in VIEW mode, as can additiona
A single search box can be used to make a full table search on any character or numeric value, using this [macro](https://core.sasjs.io/mp__searchdata_8sas.html).
The search covers **every** column of the table at once, so you do not need to know which column holds the value you are looking for:
- **Character search** (the default) is a case sensitive CONTAINS match against every character column, so `Smith` finds `Smithson` and `smith` finds `Goldsmith`, but `smith` will not find `Smithson`.
- **Numeric search** (tick the Numeric box) is an exact match against every numeric column, so `42` will not find `420` or `4210`.
- If a filter is applied, the search runs within the filtered rows rather than the whole table.
- The grid is capped by the [`DC_MAXOBS_WEBVIEW`](/dcc-options/#dc_maxobs_webview) option (500 rows by default).
- For a search, the cap applies to the row count as well: a search that matches more rows than the cap returns 500 of them, and the count shown next to the table name is the number of rows returned, not the total number of matches.
- Without a search, the count shown next to the table name is the full number of rows in the filtered view, so it can be higher than the number of rows displayed - a table of 1,200 rows reports `1,200 rows` above a grid holding the first 500.
- A search that matches nothing shows a "No data found with given conditions" panel rather than an empty grid.
![Full table search](img/full-table-search.png)
<iframe width="560" height="315" src="https://www.youtube.com/embed/i27w-xq85WQ" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>
## Options
+52
View File
@@ -0,0 +1,52 @@
---
layout: article
title: DC Downloads
description: Data Controller release assets and how to verify them
---
# Downloads
Data Controller releases are published on the [releases page](https://git.datacontroller.io/dc/dc/releases) of the source repository. Every release carries the same set of assets:
| Asset | Use |
|---|---|
| `frontend.zip` | The pre-built web frontend, for deployments that serve the frontend from a web server (SAS 9 full deploy, Viya separated deploy) |
| `sas9.sas` | SAS 9 full deployment program (stored processes + frontend registration) |
| `demostream_sas9.sas` | SAS 9 streaming deployment program (demos and evaluations) |
| `viya.sas` | Viya streaming deployment program (backend + frontend) |
| `viya_noweb.sas` | Viya separated deployment program (backend only) |
| `viya_noweb.json` | The Viya backend services as a SASjs Drive JSON deployment bundle |
| `sasjs_server.json.zip` | SASjs Server deployment bundle (all services, for [SASjs Server](https://server.sasjs.io) deployments) |
| `SHA256SUMS` | SHA-256 checksums for every asset above |
Which asset you need depends on the deployment route - see [SAS 9 Deployment](/dci-deploysas9) or [SAS Viya Deployment](/deploy-viya).
## Verifying a download
Every asset is covered by the `SHA256SUMS` file, which the release pipeline generates over the exact files it uploads. To verify a download:
1. Download the asset (for example `frontend.zip`) from the [releases page](https://git.datacontroller.io/dc/dc/releases).
2. Download `SHA256SUMS` from the same release into the same folder.
3. Run the check:
```bash
sha256sum --check SHA256SUMS --ignore-missing
```
Each asset that matches reports `OK`. The `--ignore-missing` flag means you only need the files you actually downloaded - the check skips assets you did not fetch.
On Windows, `certutil` can compute the hash of a single file for comparison against the value listed in `SHA256SUMS`:
```
certutil -hashfile frontend.zip SHA256
```
### What the checksum proves
A matching checksum confirms the asset is byte-for-byte the file the release pipeline uploaded. It catches corrupted and tampered downloads - interrupted transfers, proxies or mirrors that altered the file in transit.
It is not a cryptographic signature. The hashes travel in the same release as the files they cover, so they protect the download path, not the release itself: a compromise of the forge would allow both the asset and its checksum to be altered together. For download integrity - the typical concern when fetching deployment artifacts over the internet - it is the standard check.
## Integrity inside SAS
The SAS deployment programs embed the frontend and services as generated SAS code. Once `%inc`'d, their integrity is established by the check above at download time. They deploy the stored processes or jobs into the SAS Folder you nominate (`appLoc`) - a metadata location, not physical disk - and the only physical location they write to is the Data Controller location (`dcLoc`) you configure. The programs can be [reviewed in full](https://code.datacontroller.io) (or in the [source repository](https://git.datacontroller.io/dc/dc)) before you run them.
+15
View File
@@ -0,0 +1,15 @@
<svg width="70" height="70" viewBox="0 0 70 70" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_101_8)">
<path d="M37.5993 16.6212C38.5916 16.7415 39.5538 16.8317 40.5461 16.9519C42.9216 17.2827 45.237 17.7337 47.402 18.8463C48.0334 19.1771 48.6348 19.5379 49.0859 20.1092C49.8075 20.9512 49.7774 21.8533 49.0558 22.6952C48.304 23.5371 47.3418 24.0183 46.3195 24.4092C43.944 25.3413 41.4482 25.7623 38.9224 25.9728C34.8029 26.3035 30.7435 26.1532 26.7142 25.191C25.3911 24.8903 24.0981 24.4693 22.9254 23.8078C22.4744 23.5672 22.0534 23.2665 21.6625 22.9057C20.58 21.8533 20.58 20.7407 21.6926 19.7484C22.8954 18.6659 24.3688 18.1547 25.8722 17.7337C27.9771 17.1324 30.112 16.8317 32.277 16.6813C32.4575 16.6813 32.6679 16.7415 32.8183 16.5911C34.3819 16.6212 36.0056 16.6212 37.5993 16.6212Z" fill="#E0E0E0"/>
<path d="M20.8507 41.3082C22.7751 43.7738 25.5115 44.6759 28.338 45.2773C33.9309 46.4801 39.5238 46.3297 44.9663 44.4053C46.6502 43.8039 48.2138 42.992 49.3264 41.4886C49.5068 41.248 49.597 41.3683 49.6872 41.5487C49.8677 41.9396 49.9579 42.3305 49.9579 42.7515C49.9579 44.2249 49.9579 45.7284 49.9579 47.2018C49.9579 48.164 49.5068 48.9157 48.8453 49.5772C47.4621 50.9604 45.7181 51.7122 43.8838 52.2835C40.1853 53.4261 36.3966 53.6667 32.5777 53.3961C29.7212 53.1856 26.9548 52.6443 24.3387 51.4716C23.2261 50.9604 22.2038 50.3591 21.3919 49.4269C20.7605 48.7052 20.4297 47.8934 20.4297 46.9311C20.4598 45.578 20.4297 44.2249 20.4297 42.8718C20.4297 42.3305 20.5199 41.8193 20.8507 41.3082Z" fill="#8EC63F"/>
<path d="M20.8807 32.0167C22.4744 34.1516 24.6996 35.0537 27.075 35.6852C32.7582 37.1886 38.4413 37.1586 44.0643 35.4747C45.9286 34.9334 47.6726 34.1216 49.0258 32.6482C49.2062 32.4677 49.3265 32.0468 49.5369 32.1069C49.8376 32.1971 49.8376 32.6181 49.9278 32.9488C50.0481 33.46 49.988 33.9712 49.988 34.4824C49.988 35.5047 49.9579 36.497 49.988 37.5194C50.0481 38.7823 49.5369 39.7746 48.6048 40.5865C47.1915 41.8494 45.4776 42.6011 43.6433 43.0822C37.4791 44.7361 31.3449 44.706 25.3009 42.6011C23.8275 42.09 22.4744 41.3683 21.3919 40.1956C20.7304 39.4739 20.3997 38.632 20.3997 37.6397C20.4297 36.3166 20.3997 34.9635 20.3997 33.6404C20.4297 33.0992 20.5199 32.5579 20.8807 32.0167Z" fill="#E0E0E0"/>
<path d="M20.8207 23.0861C21.2717 24.4091 22.3542 25.0105 23.4969 25.5517C25.5416 26.514 27.7366 26.965 29.9317 27.2357C34.2918 27.7769 38.6519 27.7168 42.9518 26.8147C44.6958 26.4539 46.3797 25.9727 47.9132 25.0105C48.7853 24.4693 49.2363 23.9581 49.537 23.1161C49.8377 23.5371 49.9279 23.9882 49.9279 24.4392C49.9279 25.9427 49.958 27.4161 49.9279 28.9195C49.8978 30.1825 49.1762 31.0845 48.244 31.8363C46.5 33.2195 44.4553 33.9411 42.3203 34.4223C36.6673 35.6551 31.0744 35.5649 25.5717 33.7006C24.1283 33.2195 22.7451 32.5279 21.6326 31.4454C20.7906 30.6335 20.3396 29.7014 20.3696 28.4986C20.3997 27.2056 20.3696 25.9126 20.3696 24.6196C20.4298 24.0483 20.52 23.5672 20.8207 23.0861Z" fill="#8EC63F"/>
<path d="M55.3403 63.4393C60.2417 63.7099 62.8577 57.696 59.3095 54.2981C55.5809 50.7199 49.4467 54.0576 50.409 59.1093C28.2478 73.2419 -0.258034 52.1031 7.86071 26.4238L2.38808 22.7553C-9.91033 55.4408 27.9471 83.0746 55.3403 63.4393ZM53.7767 56.3729C56.062 53.9974 59.7004 57.3051 57.5655 59.8009C55.3103 62.4169 51.3411 58.8988 53.7767 56.3729Z" fill="#E0E0E0"/>
<path d="M10.6272 15.2681C14.1453 18.6358 19.9487 15.8694 19.6179 11.0583C41.749 -3.19461 70.4052 17.9142 62.2865 43.6836L67.7591 47.3521C79.6967 15.5988 43.463 -12.9972 15.4383 6.21715C10.1761 5.13465 6.77827 11.5695 10.6272 15.2681ZM16.1599 13.1933C13.8145 15.6289 10.116 12.0807 12.4614 9.64507C14.8068 7.20944 18.5054 10.7576 16.1599 13.1933Z" fill="#8EC63F"/>
</g>
<defs>
<clipPath id="clip0_101_8">
<rect width="70" height="70" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 KiB

+18 -5
View File
@@ -71,6 +71,7 @@ nav:
- Macros: macros.md
- Installation:
- System Requirements: dci-requirements.md
- Downloads: downloads.md
- SAS Viya: deploy-viya.md
- SAS 9 EBI: dci-deploysas9.md
- SAS 9 STP Hardening: dci-stpinstance.md
@@ -95,7 +96,9 @@ markdown_extensions:
extra:
manifest: manifest.webmanifest
extra_css: ['font-awesome.css']
extra_css:
- font-awesome.css
- dc-brand.css
plugins:
- search:
@@ -111,12 +114,22 @@ repo_url: 'https://git.datacontroller.io/dc/docs.datacontroller.io'
theme:
name: material
logo: 'img/favicon.ico'
logo: 'img/dc-logo.svg'
palette:
primary: 'White'
accent: 'Amber'
- scheme: slate
primary: custom
accent: custom
toggle:
icon: material/weather-sunny
name: Switch to light mode
- scheme: default
primary: custom
accent: custom
toggle:
icon: material/weather-night
name: Switch to dark mode
font:
text: 'Open Sans'
text: 'Montserrat'
code: 'Ubuntu Mono'
favicon: img/favicon.ico
custom_dir: 'theme'