* Add --workers flag to mmctl import process to control concurrency
The bulk import worker count was hardcoded to runtime.NumCPU(), causing
high database load on the master during imports on live systems. This is
particularly impactful for incremental Slack imports where all users are
re-imported each time, generating 8-15 DB operations per user against
the master (due to LockToMaster).
The new --workers flag allows administrators to reduce concurrency
(e.g., --workers 1) to minimize impact on live users at the cost of
longer import duration. Defaults to 0 which preserves the existing
runtime.NumCPU() behavior.
* Add max workers limit, capped at CPU Count * 4
* Validate that RefreshedToken differs from original invite token in remote cluster confirmation
* Add unit test for MM-67098
---------
Co-authored-by: JG Heithcock <jgheithcock@gmail.com>
* Add single-channel guests filter and channel count column to System Console Users
- Add guest_filter query parameter to Reports API with store-level
filtering by guest channel membership count (all, single_channel,
multi_channel)
- Add channel_count field to user report responses and CSV exports
- Add grouped guest role filter options in the filter popover
- Add toggleable Channel count column to the users table
- Add GuestFilter and SearchTerm to Go client GetUsersForReporting
- Add tests: API parsing, API integration, app job dedup, webapp utils,
E2E column data rendering
Made-with: Cursor
* Fix gofmt alignment and isolate guest store tests
- Align GuestFilter constants to satisfy gofmt
- Move guest user/channel setup into a nested sub-test to avoid
breaking existing ordering and role filter assertions
Made-with: Cursor
* Exclude archived channels from guest filter queries and ChannelCount
The ChannelMembers subqueries for guest_filter (single/multi channel)
and the ChannelCount column did not join with Channels to check
DeleteAt = 0. Since channel archival soft-deletes (sets DeleteAt) but
leaves ChannelMembers rows intact, archived channel memberships were
incorrectly counted, potentially misclassifying guests between
single-channel and multi-channel filters and inflating ChannelCount.
- Join ChannelMembers with Channels (DeleteAt = 0) in all three
subqueries in applyUserReportFilter and GetUserReport
- Add store test covering archived channel exclusion
- Tighten existing guest filter test assertions with found-flags
and exact count checks
Made-with: Cursor
* Exclude DM/GM from guest channel counts, validate GuestFilter, fix dropdown divider
- Scope ChannelCount and guest filter subqueries to Open/Private channel
types only (exclude DM and GM), so a guest with one team channel plus
a DM is correctly classified as single-channel
- Add GuestFilter validation in UserReportOptions.IsValid with
AllowedGuestFilters whitelist
- Add API test for invalid guest_filter rejection (400)
- Add store regression test for DM/GM exclusion
- Fix role filter dropdown: hide the divider above the first group
heading via CSS rule on DropDown__group:first-child
- Update E2E test label to match "Guests in a single channel" wording
Made-with: Cursor
* Add store test coverage for private and GM channel types
Private channels (type P) should be counted in ChannelCount and guest
filters, while GM channels (type G) should not. Add a test that creates
a guest with memberships in an open channel, a private channel, and a
GM, then asserts ChannelCount = 2, multi-channel filter includes the
guest, and single-channel filter excludes them.
Made-with: Cursor
* Add server i18n translation for invalid_guest_filter error
The new error ID model.user_report_options.is_valid.invalid_guest_filter
was missing from server/i18n/en.json, causing CI to fail.
Made-with: Cursor
* Make filter dropdown dividers full width
Remove the horizontal inset from grouped dropdown separators so the
system user role filter dividers span edge to edge across the menu.
Leave the unrelated webapp/package-lock.json change uncommitted.
Made-with: Cursor
* Optimize guest channel report filters.
Use per-user channel count subqueries for the single- and multi-channel guest filters so the report avoids aggregating all channel memberships before filtering guests.
* COmposing messages with redacted URLs
* Handled non member channels
* Some refinements
* Optimizations
* lint fixes
* cleaned up hasObfuscatedSlug test
* Fixed a test
* Added system console setting
* WIP
* fixed channel seelection double selection bug
* LInt fixes
* i18n fixes
* fixed test
* CI
* renamed setting
* lint fixes
* lint fixes
* WIP
* Combined TeamSignupDisplayNamePage and TeamUrl component into a single CreateTeamForm component
* Converted CreateTeamForm to functional component
* Refactored to mnake code cleaner
* Handle team creation with setting enabled
* Skipped team URL step if secure URL feature is enabled
* Managed button text and steps in team creation flow
* lint fixes
* Don't register team URL path when using secure URL
* Display team display name instead of name in system console top nav bar
* Fixed tests
* Fixed coderabbit issues
* Fixed type errors
* Optimization
* improvements
* Handled API errors during team creation when using secure URL setting
* Some refinements
* Added test
* Updaetd tests, and trimming when reading instead of writing
* Added tests for new components
* Added BackstageNavbar tests
* Restored package lock
* lint fix
* Updaetd plugin API
* Updated team creation tests
* Added tests for ChannelNameFormField
* Added plugin API tests
* Updated API terst
* Review fixes
* Added test for ConvertGmToChannelModal component
* Added EA license check for secure urls feature
* restored package lock
* Fixed GM conversion test
* Fixed team creation tests
* remove message composition changes
* remove message composition changes
* remove message composition changes
* restored a file
* lint fix
* renamed feature
* used model.SafeDereference
* Added E2E tests
* add secure URL Playwright coverage
Expand the secure URLs Playwright coverage to validate creation, routing, rename flows, system console configuration, and search/navigation behavior while adding the page objects needed to keep the tests maintainable.
Made-with: Cursor
* rename secure URLs copy to anonymous URLs
Align the admin console and Playwright coverage with the Anonymous URLs feature name while preserving the existing UseAnonymousURLs config behavior and validating the renamed test surfaces.
Made-with: Cursor
* Update team creation CTA for anonymous URLs
Show Create in the single-step anonymous URL flow while preserving Next and Finish in the standard team creation flow. Update unit and Playwright coverage to match the revised create-team UX.
Made-with: Cursor
---------
Co-authored-by: maria.nunez <maria.nunez@mattermost.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Channel sharing operations (invite, uninvite, list shared channel remotes)
now require ManageSharedChannels instead of ManageSecureConnections, allowing
customers to delegate channel sharing without granting full connection management access.
Endpoints serving both roles (getRemoteClusters, getSharedChannelRemotesByRemoteCluster) accept either permission.
Also adds RequirePermission helpers on Context to reduce boilerplate across all remote cluster and shared channel handlers, and fixes a bug where invite/uninvite checked ManageSecureConnections but reported ManageSharedChannels in the error.
* Add single-channel guest tracking and reporting
- Add AnalyticsGetSingleChannelGuestCount store method to count guests in exactly one channel
- Exclude single-channel guests from active user seat count in GetServerLimits
- Add single-channel guest count to standard analytics response
- Add Single-channel Guests card to System Statistics page with overage warning
- Add Single-channel guests row to Edition and License page with overage styling
- Add dismissible admin-only banner when single-channel guest limit is exceeded
- Gate feature behind non-Entry SKU and guest accounts enabled checks
- Re-fetch server limits on config changes for reactive UI updates
- Fix label alignment in license details panel
Made-with: Cursor
* Refine single-channel guest tracking
- Remove license GuestAccounts feature check from shouldTrackSingleChannelGuests (only config matters)
- Re-add getServerLimits calls on page mount for fresh data
- Remove config-change reactivity code (componentDidUpdate, useEffect)
- Add server i18n translations for error strings
- Sync webapp i18n via extract
- Add inline comments for business logic
- Restore struct field comments in ServerLimits model
- Add Playwright E2E tests for single-channel guest feature
- Fix label alignment in license details panel
Made-with: Cursor
* Guests over limit fixes and PR feedback
* Fix linter issues and code quality improvements
- Use max() builtin to clamp adjusted user count instead of if-statement (modernize linter)
- Change banner type from ADVISOR to CRITICAL for proper red color styling
Made-with: Cursor
* Fix overage warnings incorrectly counting single-channel guests
Single-channel guests are free and should not trigger license seat
overage warnings. Update all overage checks to use
serverLimits.activeUserCount (seat-adjusted, excluding SCG) instead
of the raw total_users_count or TOTAL_USERS analytics stat.
- UserSeatAlertBanner on License page: use serverLimits.activeUserCount
- UserSeatAlertBanner on Site Statistics page: use serverLimits.activeUserCount
- ActivatedUserCard display and overage check: use serverLimits.activeUserCount
- OverageUsersBanner: use serverLimits.activeUserCount
Made-with: Cursor
* Use license.Users as fallback for singleChannelGuestLimit before limits load
This prevents the SingleChannelGuestsCard from showing a false overage
state before serverLimits has been fetched, while still rendering the
card immediately on page load.
Made-with: Cursor
* Fix invite modal overage banner incorrectly counting single-channel guests
Made-with: Cursor
* Fix invitation modal tests missing limits entity in mock state
Made-with: Cursor
* Fix tests
* Add E2E test for single-channel guest exceeded limit scenario
Made-with: Cursor
* Fix TypeScript errors in single channel guests E2E test
Made-with: Cursor
* Fix channel name validation error caused by unawaited async getRandomId()
Made-with: Cursor
* Add contextual tooltips to stat cards when guest accounts are enabled
Made-with: Cursor
* Code review feedback: query builder, readability, tooltips, and alignment fixes
Made-with: Cursor
* Fix license page tooltip alignment, width, and SaveLicense SCG exclusion
Made-with: Cursor
* Fix banner dismiss, license spacing, and add dismiss test
Made-with: Cursor
* Exclude DM/GM channels from single-channel guest count and fix E2E tests
Filter the AnalyticsGetSingleChannelGuestCount query to only count
memberships in public/private channels, excluding DMs and GMs. Update
store tests with DM-only, GM-only, and mixed membership cases. Fix E2E
overage test to mock the server limits API instead of skipping, and
correct banner locator to use data-testid.
Made-with: Cursor
* Add operation tracking fields to bridge client CompletionRequest calls
Populate UserID, Operation, and OperationSubType on CompletionRequest
for recaps (SummarizePosts) and message rewrite (RewriteMessage) so
token usage logs show correct values instead of defaults.
Also bumps mattermost-plugin-ai v1.8.1 → v1.12.0 which adds the
Operation/OperationSubType fields to the bridgeclient struct.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Address PR feedback: normalize rewrite action and use session-derived userID
- post.go: Add normalizeRewriteAction() that validates action against a
whitelist of known RewriteAction values, mapping unknown values to
"unknown" before assigning to OperationSubType.
- summarization.go: Use sessionUserID (derived from rctx.Session().UserId)
instead of the userID parameter for tracking, ensuring operation
tracking always uses the authenticated session user.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
When TeamSettings.RestrictDirectMessage is set to "team", the system bot could not create DM channels with users on different teams (or no shared team). This broke SendTestMessage, CheckPostReminders, and other background jobs that use an empty session context.
The existing bypass in GetOrCreateDirectChannel only covered bots owned by the current session user or a plugin. The system bot is owned by a system admin, so it failed the ownership check and hit the common-team guard.
Changes:
- Rename IsBotOwnedByCurrentUserOrPlugin to IsBotExemptFromDMRestrictions to better reflect its purpose
- Add an explicit system bot exemption (bot.Username == BotSystemBotUsername) as the first check in the function
- Add tests covering the system bot exemption with both empty and user sessions
* UpdateByQuery methods for channel_type; rewrite reindexChannelPosts
log pre-fetch error in channel Update and upgrade reindexChannelPosts to error level
* add backfill orchestration, config listener, webapp toggle changes
fix misleading backfill complete log when SaveOrUpdate fails
* add integration & unit tests for public channel search and backfill
* fix nil pointer dereference on UpdateByQuery response and log partial failures
* add tests for compliance mode override and P channel post leakage
* update system console snapshots
* add instructions to error message
* improve compliance-mode test
* getAllChannels doesn't filter by O/S, need to do ourselves
* in search, load channel info for channels we're not a member of
* blank commit -- something is wrong with github, maybe this will help
* improve channelType passing; error msg; simplify settings naming
* debug logging for timing backfill and channel change; TO BE REVERTED
* fix getMissingChannelsFromFiles in search as well
* blank commit
* Improve job progress estimation with no data
For the Elasticsearch indexing job, we compute the job progress
executing analytics queries to get the total number of posts, channels,
users and files in the database.
Until now, if that call failed, we instead used an estimate. That
estimate is a hardcoded number that is in no way related to the server
data. If that estimate was smaller than the number of already processed
entities, the job progress would show up as larger than 100%.
This commit changes that behaviour by caching the result of the
analytics query:
1. If the analytics query succeeds, we store that value in the job data.
2. If the analytics query fails, we pick a value for the total as
follows:
- Use the value previously stored in the job data if available.
- If not, use the hardcoded estimate.
- If the hardcoded estimate is smaller than the current count of
processed entities, use that count instead.
* Add defensive code against division by zero
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* Rename Content Flagging to Data Spillage Handling
Update all user-facing text to use "Data Spillage Handling" and
"Quarantine for Review" terminology. Rename i18n keys that referenced
content flagging. Auto-patch bot display name on pre-existing servers.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fixed searchable stringgs
* Revert unintended package-lock.json changes
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix i18n extract check: correct typo and key ordering
Fix "posed" -> "posted" typo in keep/remove quarantine modal
defaultMessages. Move admin.contentFlagging.title to correct
alphabetical position in en.json.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix webapp tests for Data Spillage Handling rename
Update test assertions to match renamed i18n strings:
notification settings, content reviewers, and additional
settings tests now expect the new quarantine terminology.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Use translatable i18n strings for notification messages
Replace hardcoded "flagged for review" notification templates with
i18n.T() calls using "quarantined for review" terminology. Add six
new server i18n keys for author, reporter, and reviewer notifications.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix server i18n key mismatches and update test assertions
Rename remaining app.content_flagging.* keys to app.data_spillage.*
in server/i18n/en.json to match Go code references. Fix the
quarantine_post_confirmation key name. Update test assertions to
match new "quarantined for review" terminology.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Fix gofmt formatting in content_flagging.go
Co-authored-by: Cursor <cursoragent@cursor.com>
* Prevent nil bot on PatchBot failure in getContentReviewBot
Use a separate variable for PatchBot result so the original bot
is preserved if the display name update fails.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Reorder server i18n keys after extract
Run mmgotool i18n extract to sort entries into correct
alphabetical order.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Replace i18n.T() with fmt.Sprintf for notification messages and fix test assertions
Use direct string formatting for bot notification messages instead of
i18n translation keys, which were being removed by mmgotool i18n extract
due to indirect key references. Also update test expectations for renamed
error keys (content_flagging -> data_spillage).
Co-authored-by: Cursor <cursoragent@cursor.com>
* Update default quarantine reasons to DISC-aligned terminology
Replace generic content moderation reasons with defense/intelligence
sector terminology: Classification mismatch, Need-to-know violation,
PII exposure, OPSEC concern, CUI violation, Unauthorized disclosure,
and Other. Updated across model, API tests, webapp tests, and e2e tests.
Co-authored-by: Cursor <cursoragent@cursor.com>
* Adding a string missing from bad merge
* Update remaining flagged terminology and icon for data spillage rename
- Change post menu icon from flag-outline to alert-outline
- Update reviewer notification: "quarantined" -> "submitted" a message
- Update action notifications: "flagged message" -> "quarantined message"
- Update modal errors: "flagging" -> "quarantining" this message
- Update report title: "flagged" -> "submitted" a message for review
- Update e2e page object locator for renamed menu item
Made-with: Cursor
* Fix tests
* Fix quarantine icon alignment in post dot menu
Use AlertOutlineIcon React component with size={18} instead of raw
<i> tag to match the sizing of all other menu item icons.
Made-with: Cursor
* Fixed E2E tests
* Missing test fix
* Fix E2E tests
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Mattermost Build <build@mattermost.com>
* Add shared_channel_manager and secure_connection_manager built-in roles
Introduce two new delegated admin roles for granular Shared Channels
permission management, allowing admins to assign shared channel and
secure connection management to specific non-admin users without
granting full System Admin or System Manager access.
- shared_channel_manager: grants manage_shared_channels permission
- secure_connection_manager: grants manage_secure_connections permission
Includes server role definitions, app migrations, permissions migrations,
System Console UI support, and API permission tests.
#### Summary
Use the atomic `ConsumeOnce` pattern for guest magic link token consumption, consistent with how SSO code exchange tokens are already handled.
#### Ticket Link
https://mattermost.atlassian.net/browse/MM-67791
#### Release Note
```release-note
Improved token handling in the guest magic link authentication flow.
```
Change the /api/v4/users/{user_id}/auth endpoint to use
APISessionRequired instead of APISessionRequiredTrustRequester.
This endpoint is a JSON API called via XHR and does not need
the TrustRequester flag which is intended for directly-requested
resources like images and file downloads.
Made-with: Cursor
* Add structured outputs, response sanitization, and session context for recaps
- Wrap BridgeClient to strip markdown code fencing from LLM JSON responses,
using explicit delegation to prevent unsanitized methods from leaking
- Add JSONOutputFormat schema to SummarizePosts for structured LLM output
- Pass user session in recap worker context for session-dependent code paths
- Pre-parse min plugin version semver at package level to avoid repeated parsing
- Hoist static JSON schema to package-level var to avoid per-call allocation
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix stripMarkdownCodeFencing to handle single-line fenced payloads
Address CodeRabbit feedback: the function previously returned the original
string when fenced JSON had no newline (e.g. ```json {"a":1}```), which
would break downstream JSON parsing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Handle case/spacing variants for single-line fenced language tags
Address CodeRabbit feedback: use case-insensitive comparison for the
"json" language tag and check for whitespace separator, so inputs like
```JSON {"a":1}``` are handled correctly.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Revert BridgeClient wrapper and keep only structured output changes
Remove the BridgeClient wrapper, stripMarkdownCodeFencing, and semver
pre-parse from agents.go. The scope of this PR is limited to adding
JSONOutputFormat structured outputs for recaps and the worker session
context fix.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* Fix lint: use any instead of interface{} and fix gofmt formatting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* MM-45293 Index all Slack attachment content fields in Elasticsearch
Previously only the "text" field from Slack attachments was indexed.
This adds title, pretext, fallback, and field titles/values, making
posts from integrations (JIRA, GitHub, CI bots) fully searchable.
* fix: only match root-level JSONL files when importing a zip
When importing a Mattermost export zip, the code iterated over all files
to find the first .jsonl by extension. Exported attachments under data/
could themselves be .jsonl files, causing the import to pick an
attachment as the manifest instead of the actual root-level JSONL file.
Extract an IsRootJsonlFile helper in the imports package and use it in
the import process worker, mmctl validator, and bulk import test to
restrict the search to files with no directory component.
* test: add integration test for import with nested JSONL decoy file
Verify that BulkImportWithPath correctly picks the root-level JSONL
manifest and ignores a decoy .jsonl inside a subdirectory, covering
the fix from ad7f230f06.
* Includes deleted remote cluster infos to correctly show shared user information
* Addressing review comments
---------
Co-authored-by: Miguel de la Cruz <miguel@ctrlz.es>
Co-authored-by: Mattermost Build <build@mattermost.com>
* [MM-66789] Add directory conflict checks for plugin and import uploads
Prevent security issues where plugin uploads could write into the import
directory (or subdirectories) and vice versa by adding validation checks
at the REST API level when uploading plugins or creating import uploads.
* improved handling of root directories
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
* Adds e2e tests to the plugin list command
Adds end-to-end tests to the pluginListCmdF function.
These tests verify scenarios where appropriate permissions
are not available, plugins are disabled, and success cases.
* Updates plugin list command E2E tests
Removes the `pluginArg` from `pluginListCmdF` calls within end-to-end tests.
Ensures test cases accurately evaluate the `pluginListCmdF` behavior when no specific plugin name is provided as an argument, improving test coverage for default listing scenarios.
Relates to MM-29974
* Simplifies plugin test defer cleanup
Refactors defer statements in plugin E2E tests to directly call `s.th.App.UpdateConfig`.
Removes redundant anonymous function wrappers, making the test cleanup code cleaner and more concise.
Relates to MM-29974
---------
Co-authored-by: Mattermost Build <build@mattermost.com>
IntuneSettings fields had access:"mobile_intune" tags, but no matching
permission exists in AllPermissions. Changed to "environment_mobile_security"
to match the existing permissions defined in permission.go.
Also added TestConfigAccessTagsMapToValidPermissions to validate all config
access tags resolve to real permissions.
Jira https://mattermost.atlassian.net/browse/MM-67693
Co-authored-by: Cursor <cursoragent@cursor.com>
* Add container name to Docker logs
This will allow for querying Loki by container's name:
{job="docker",container_name="mattermost-postgres"}
* Configue Loki to prepare for OTLP ingestion
- Add a volume to Loki container to get the config
- Configure Loki with the expected labels so that we can query by job,
app, container.name...
* Add OpenTelemetry collector configuration
There are three pipelines:
1. logs/mattermost scrapes the logs from mattermost.log, parsing the
timestamp and severity, and pushes them to Loki.
2. logs/docker scrapes the Docker logs from *-json.log, parsing the
timestamp, the log itself and the container name, and pushes them to Loki.
3. metrics/docker scrapes the Docker socket to retrieve the containers'
uptime values and pushes them to Prometheus.
* Replace Promtail with OpenTelemetry collector
* Update build tooling for OpenTelemetry collector
1. Make sure that the logs directory is created
2. Swap Promtail with OpenTelemetry collector
* Scrape collector to get Docker stats
Prometheus needs to scrape the OpenTelemetry collector in the exposed
port to get the Docker stats, so that we can query the uptime with
metric container_uptime_seconds, which has a container_name label to
filter by container.
* Update Grafana dashboard for Docker health checks
1. Use Prometheus as the datasource in all queries
2. Simplify the mappings to either 0 (offline, red) or 1 (online,
green).
3. Unify all queries on container_uptime_seconds, filtering by
container_name, and making sure that the latest value we got is at most
15 seconds old, so that it does not show stale data.
4. Add Redis health check, that was missing
5. Update the dashboard title to Docker containers
* Tune Loki and OTel collector configs for local dev
- Switch filelog receivers to start_at: beginning so existing logs are
ingested on collector startup, not just new entries.
- Fix Docker log timestamp layout to use 9s (variable-length nanos)
instead of 0s (fixed-width), matching actual Docker JSON log format.
- Add ingester max_chunk_age to keep chunks open longer in the
single-instance dev setup, so that we can ingest older logs (the
window is max_chunk_age/2).
- Relax Loki limits for local development: allow unordered writes,
disable old-sample rejection, and raise ingestion rate/burst to 64 MB
to avoid throttling during bulk ingest.
Add LIKE-based CJK (Chinese, Japanese, Korean) search support for PostgreSQL, gated behind a `CJKSearch` feature flag.
PostgreSQL's built-in full-text search (`to_tsvector`/`to_tsquery`) does not support CJK languages because it relies on whitespace-based tokenization, which doesn't work for logographic/syllabic scripts that don't use spaces between words. This PR adds a `LIKE`-based fallback for search terms containing CJK characters.
**How it works:** When the `CJKSearch` feature flag is enabled and a search term contains CJK characters (Han, Hiragana, Katakana, or Hangul), the query builder generates `LIKE '%term%'` clauses instead of `to_tsvector @@ to_tsquery` expressions. Case-sensitive `LIKE` is used rather than `ILIKE` since CJK scripts have no letter casing, which also allows potential use of standard B-tree indexes.
* Handled null column for scheduled post type in database
* [skip ci]
* CI
* CI
* Minor improvement
---------
Co-authored-by: Mattermost Build <build@mattermost.com>