This issue tracks a set of changes to make the module compatible with
Drupal 11 and PHP 8.4, fix several bugs that caused incorrect API
behaviour, and address a number of security concerns.

## Compatibility

- core_version_requirement updated to ^9 || ^10 || ^11
- ScribitProFormatter: replaced Psr\ContainerInterface with
Symfony\ContainerInterface (Drupal standard)
- ApiService: constructor type hints changed from concrete classes
(AccountProxy, KeyValueFactory) to their interfaces
- ConfigForm: replaced constructor override with property injection via
parent::create(), fixing an ArgumentCountError crash on Drupal 10.2+/11

## Bug fixes

- API payload: moved the language field out of the nested voice object
to the top level. The API rejected the previous structure with
422 Unprocessable Entity.
- YouTube short URLs: fixed the regex in extractYouTubeIdFromUrl() to
stop at '?', so share URLs like youtu.be/ID?si=... no longer include
the tracking parameter in the extracted ID.
- Duplicate submissions: added a static guard in massageFormValues();
Drupal's Form API calls this method multiple times per save, previously
causing the same video to be submitted up to five times.
- Widget re-initialisation: wrapped the embed script loader in once()
and added the core/once dependency, preventing the widget from being
injected on every Drupal.behaviors attach.
- Undefined array keys: remarks and urgent are now read safely with
?? '' / !empty() to prevent notices when the API omits optional fields.

## Security

- Replaced !isset($token) with empty($token) in authenticatedGetRequest()
— isset(FALSE) evaluates to TRUE, causing requests to proceed with an
empty Bearer token. Added the same guard to authenticatedPostRequest().
- Set CURLOPT_TIMEOUT to 30 seconds (was 0 / unlimited).
- API errors are now logged to watchdog only; the user sees a generic
message instead of a raw var_export() of the response body.
- Callback route changed from _permission: 'access content' to
_access: 'TRUE'. The previous setting blocked Scribit Pro's server
(no Drupal session) while allowing any authenticated user to trigger
the endpoint.
- Added Html::escape() in createSystemReportRow() for plain string
values from the API response.

## Dead code removal

- Removed unused EXPIRATION_OFFSET constant (token-refresh was never
implemented).
- Removed dead $video_details variable from getMediaLinks().
- Removed unused $video_id parameter from postProServicesRequest().

Command icon Show commands

Start within a Git clone of the project using the version control instructions.

Or, if you do not have SSH keys set up on git.drupalcode.org:

Comments

bond708 created an issue.