Files
6083cc2282 MM-68196 Adding Global Classification configuration and banners (#36231)
* Add Classification Markings admin console page

Adds a new admin console page under Site Configuration for managing
classification markings. This allows system administrators to define
classification levels (e.g., UNCLASSIFIED, SECRET, TOP SECRET) with
associated colors and rank ordering, which will be used for system-wide
and per-channel classification banners.

The page includes:
- Enable/disable toggle backed by the property field system (field
  existence = enabled)
- Country preset dropdown (US DoD, NATO, UK GSCP, Canada, Australia
  PSPF) that auto-fills standard classification levels
- Editable classification levels table with drag-and-drop reorder,
  inline text editing, color picker, and delete
- Auto-switch to "Custom" preset when levels are manually modified
- Confirmation dialog when switching presets would overwrite custom data

Also adds:
- ClassificationMarkings feature flag (default off)
- Generic property field client methods (get/create/patch/delete) for
  the /api/v4/properties/ endpoints
- Enterprise license + feature flag gating on the admin page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix classification markings: add validation, error handling, and system object type

- Add "system" as a valid property field object type so the
  classification markings API calls succeed
- Surface load errors instead of silently swallowing them (only
  suppress 404 for unconfigured state)
- Validate before save: require at least one level, non-empty names,
  and no duplicates
- Default to custom preset with empty levels on first open
- Add section strings to searchableStrings for admin console search

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Move classification field to CPA group targeting users

Store the classification markings property field in the
custom_profile_attributes group with object_type 'user' instead of the
attributes group with object_type 'system'. Clear target_id for PSAv2
system target compliance and mark the field as admin-managed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Stabilize preset option IDs and add danger warning on preset switch

Hardcode deterministic IDs for all preset classification levels so
switching away and back preserves option IDs, preventing orphaned
property values. Compare only level data (not preset label) for change
detection so cosmetic preset switches don't trigger false save states.

Show a danger modal with red confirm button when changing presets on an
existing field, warning about system-wide impact on classified resources.
The warning appears once per session then allows frictionless switching.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove system object type from property fields

Not needed yet — will be added when system/channel banners are implemented.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix ESLint errors in classification markings admin page

Fix import ordering and remove unused generateId import.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Address CodeRabbit review feedback for classification markings

- Register property field API endpoints when ClassificationMarkings flag
  is enabled (not just IntegratedBoards) to prevent 404s
- Preserve preset option IDs when creating a new classification field
  instead of blanking them with empty strings
- Add sysconsole read/write permission constants for classification
  markings across server and webapp, and wire up resource-level
  permission checks in the admin definition

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add rank attribute to classification marking options

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add classification markings permissions migration and read-only support

Add a permissions migration to grant classification markings sysconsole
permissions to existing roles on upgrade. Wire up the disabled prop so
read-only users can view but not edit classification settings. Register
the permission in the Delegated Granular Administration UI.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Paginate loadField to find classification field beyond first page

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix lint errors and warnings in classification markings

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Remove classification markings sysconsole permissions; gate on sysadmin instead

Classification markings admin page no longer uses feature-specific
read/write permissions. Visibility is gated on license + feature flag,
editing is gated on system admin role. This avoids coupling
feature-specific permissions to the generic property service.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Set sysadmin-level permissions on classification markings field creation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Use stable IDs instead of array indices for classification level operations

Switch updateLevel/deleteLevel to identify levels by ID rather than
index, sort levels by rank on load, and extract i18n strings.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Refactor classification markings into extracted helper functions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Add tests for classification markings admin console feature

Add unit and component tests covering:
- Pure function tests for detectPreset, optionsToLevels, levelsToOptions,
  processClassificationField, and fetchClassificationField pagination logic
- React component tests for rendering states, validation, and user interactions
- Client4 property field method tests for URL construction and HTTP verbs
- Server routing test verifying routes register with ClassificationMarkings flag
- Feature flag default and serialization test

Export pure functions from classification_markings.tsx to enable direct testing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix lint errors in classification markings tests

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fix test compilation error

* Fix color input auto-filling after 3 hex characters in classification markings

Buffer ColorInput onChange in a LevelColorCell wrapper so the table
doesn't re-render mid-typing, preventing the input from losing its
focus-guarded local state.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* Fixing style issues with color picker z-index

* Added fix to prevent immediate dismissal when clicking inside color picker

* Adding E2E test suite for configuration

* Removing duplicates

* Fixing unrelated linter error

* Fixing test linting issues

* Updating tests to skip appropriately

* Matching configuration to UX specs

* Fixing style lint

* Added informational banner for presentational nature of markings

* Enabling the markings flag on playwright server

* Added missing feature flag to e2e test environment in ci

* Reverting changes to color_input

- Not needed as we're using a custom component

* Added and polished global banner configuration

* Refactoring webapp for readability

- Separating components
- Adding unit tests
- Isolating helper methods into utilities

* Fixing linter errors

* linter fix

* Manually fixing linter issues

* Separating global classification component

* Added persistence of classification marking configuration

* Changing LevelID with LevelName

* Making changes for PR reviews

* Changing property object of classification field to template

* syncing i18n file

* Removing inaccurate note from comments

* PR fixes for UX review

* Cleaning up unused value

* Added GlobalClassificationBanner component

- Made sure it syncs on change by using normal configuration values on it
- Works with "top" and "top_and_bottom"
- Renders on both root and admin_console

* Adding E2E test cases for global classification

* Linter fixes, i18n extract

* PR Fixes

* Linter fix

* Matching default messages

* Fixing type errors

* Fixing pipeline and runtime errors

* Fixing announcementbar rendering on top of global classifications

* Increasing banner & font sizes

* Fixing font size to 12px instead of 16px

- I read it wrong

* Replacing config values with property

* Test linter fixes

* Fixing type errors and go format error

* Making changes needed to align with specs

- Ensuring system_classification is a separate linked property that differs from the template
- Saving the global classification banner values as a propertyvalue

* Added missing arguments in e2e tests

* Added missing conditions for useEffect

- Also fixing E2E error in pipeline

* Fixing issues with V1 and V2 group mismatch

* Fixes for linter errors and coderabbit review

* Addressing more issues found by coderabbit

* Fixing issues found by coderabbit

* Migrating to use system properties

* Ran all linters and prettier

- Resolving coding style drift that happened from not running prettier on the webapp (even though CI doesn't check for this)

* Undoing the prettier changes in webapp

* Cleaning up unwanted autoformatted changes

* Reverting prettier changes to clean diff

* Fixing E2E test

* Import fixes in test

* Applying changes for PR feedback

* Fixing issues with failing e2e tests

* Changing key of selection from name to id

* Replacing field setup in E2E tests to use levelId instead of levelName

---------

Co-authored-by: David Krauser <david@krauser.org>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: David Krauser <david@kruser.org>
Co-authored-by: Mattermost Build <build@mattermost.com>
2026-05-11 21:35:38 +03:00
..
2023-04-14 15:17:44 -04:00

This folder contains a number of packages intended to be built and shipped separately on NPM as well as a few legacy packages for internal use only (reselect and mattermost-redux). The following documentation only applies to the newer packages and not to the legacy ones.

Importing a subpackage

Subpackages should be imported using their full name, both inside the web app and when installing them using npm. They should not be imported using a relative path, and the src folder shouldn't be necessary to include.

// Correct
import {Client4} from '@mattermost/client';
import {UserProfile} from '@mattermost/types/users';

// Incorrect
import Client4 from 'packages/client/src/client4.ts';
import {UserProfile} from '../../types/src/users';

Some tools have difficulty doing this on their own, but they often support import path aliases so that we can keep them consistent acrosss the code base. More details on how to do this will be provided in packages where this is necessary such as types.

Importing one subpackage into another

When building packages that depend on each other, be careful to:

  1. Avoid import loops. While JavaScript lets us get away with these in most cases within a project, we cannot have two packages that depend directly with each other.
  2. Not compile one subpackage into another. We don't want the published libraries to include code from one subpackage into another. They should be set up so that they're peer dependencies in the package.json, and if a project wants to use multiple packages, they can install them each separately.

As above, some tooling may need additional configuration to have one subpackage use code from another. For example, in packages compiled with the TypeScript compiler (tsc), you'll need to have the tsconfig.json from the dependent pacakge reference its dependency using the references field.

Versioning subpackages

At this time, we'll have the version of each package match the version of the web app. Versions can be incremented for each affected package by using npm version, and then npm install should be run to propagate those changes into the shared package-lock.json.

# Set a version of a single package
npm version 6.7.8 --workspace=packages/apple

# Increment the version of each package to the next minor version
npm version minor --workspaces

## Increment the version of a package to a pre-release version of the next minor version
npm version preminor --workspace=packages/apple

When a subpackage imports another, it should be set to depend on the * version of the other subpackage.

Adding a new subpackage

To set up a new package:

  1. Add a package.json and README.md for that package.
  2. Ensure all source files are located in src and all compiled files are built to lib.
  3. Add an entry to the workspaces section of the root package.json so that NPM is aware of your package.
  4. Set up import aliases so that the package is visible from the web app to the following tools:
    1. TypeScript - In the root tsconfig.json, add an entry to the compilerOptions.paths section pointing to the src folder and an entry to the references section pointing to the root of your package which should contain its own tsconfig.json.

      Note that the compilerOptions.paths entry will differ based on if your package exports just a single module (ie a single index.js file) or if it exports multiple submodules.

      {
          "compilerOptions": {
              "paths": {
                  "@mattermost/apple": ["packages/apple/lib"], // import * as Apple from '@mattermost/apple';
                  "@mattermost/banana/*": ["packages/banana/lib/*"], // import Yellow from    '@mattermost/banana/yellow';
              }
          },
          "references": [
              {"path": "./packages/apple"},
              {"path": "./packages/banana"},
          ]
      }
      
    2. Jest - Add an entry to the jest.moduleNameMapper section of the root jest.config.js for your package. Since that setting supports regexes, you can add these to the existing patterns used by the client and types packages.

      Similar to TypeScript, this will differ based on if the package exports a single module or multiple modules.

      {
          "jest": {
              "moduleNameMapper": {
                  "^@mattermost/(apple|client)$": "<rootDir>/packages/$1/src",
                  "^@mattermost/(banana|types)/(.*)$": "<rootDir>/packages/$1/src/$2",
              }
          }
      }
      
  5. Add the compiled code to the CircleCI dependency cache. This is done by modifying the paths used by the save_cache step in .circleci/config.yml
    aliases:
      - &save_cache
        save_cache:
          paths:
            - ~/mattermost/mattermost-webapp/packages/apple/lib
            - ~/mattermost/mattermost-webapp/packages/banana/lib
    

Publishing a subpackage

The following is the rough process for releasing these packages. They'll require someone with write access on our NPM organization to run them, and they'll likely change over time as we improve this process.

For full releases accompanying new versions of Mattermost:

  1. Clean the repo.

    make clean
    
  2. Update the version of the desired packages to match the server/web app as described above.

  3. Download an up to date copy of the dependencies and update package-lock.json.

    make node_modules
    
  4. Check in the changes to the package-lock.json.

  5. Build the desired packages.

    npm run build --workspace=packages/apple --workspace=packages/banana
    
  6. Test everything in the web app. This will be needed until the packages get their own standalone tests.

    make check-style check-types test
    
  7. Assuming those pass, you can now publish those packages to npm. You can also do a dry run first or use npm pack to see exactly which files will be pushed.

    # Run a dry run which will list all the files to be included in the published package.
    npm publish --dry-run --workspace=packages/apple
    
    # Generate the tar file that will be uploaded to NPM for inspection.
    npm pack --workspace=packages/apple
    
    # Actually publish these packages. You can also use --workspaces to publish everything.
    npm publish --access=public --workspace=packages/apple --workspace=packages/banana
    

    The packages have now been published! There's still a few remaining cleanup tasks to do though.

  8. Tag the commit for each package that has been updated. The tag name should be of the form @mattermost/package-name@x.y.z.

  9. Push that commit and the corresponding tags up to GitHub

    git push release-x.y
    git push origin @mattermost/apple@x.y.z @mattermost/banana@x.y.z
    

Publishing a pre-release version

Similarly, you can publish a pre-release version of the package. This can be done either to use changes from master while developing another product/plugin or to generate a release candidate.

This process is the same as above, except the version will have a suffix like -1, -2, etc. As explained above, this can be automatically done by using npm version preminor for minor releases, npm version premajor for major releases, and npm version prerelease for patch releases. These versions won't be automatically installed when people add them using npm add without a version, but they can be installed by specifying the version number manually.

Caveats

  1. Currently, all packages are treated by CI as if they're part of the web app. This means that, for example, their style checking and tests are ran as part of the web app. In turn, that means that regardless of what tooling we use to build each package, they'll be compiled into the web app using webpack directly from source, and that it's possible for them to behave slightly differently in development compared to after release.

    Eventually, we hope to get these building in parallel (so instead of having webpack watch the whole repo for changes during development, we'll have multiple watchers for the web app and each package) which should solve this issue, but that requires much larger changes that we're not ready to do yet.

  2. For packages that export multiple submodules (such as types), we've chosen to expose these using Node's subpath exports feature. Some tools like Webpack support this natively, but others like TypeScript and Jest don't support it yet. We've provided steps on how to support this in the README.md for the types package, but this may vary depending on the project's setup.