diff --git a/communication/provider/matrix/classes/communication_feature.php b/communication/provider/matrix/classes/communication_feature.php index e955aa1f5d1..a7070f38aed 100644 --- a/communication/provider/matrix/classes/communication_feature.php +++ b/communication/provider/matrix/classes/communication_feature.php @@ -24,6 +24,7 @@ use communication_matrix\local\spec\features\matrix\{ update_room_name_v3 as update_room_name_feature, update_room_topic_v3 as update_room_topic_feature, upload_content_v3 as upload_content_feature, + media_create_v1 as media_create_feature, }; use communication_matrix\local\spec\features\synapse\{ create_user_v2 as create_user_feature, @@ -409,7 +410,7 @@ class communication_feature implements * Update the room avatar when an instance image is added or updated. */ public function update_room_avatar(): void { - // Either of the following features of the remote API are required. + // Both of the following features of the remote API are required. $this->matrixapi->require_features([ upload_content_feature::class, update_room_avatar_feature::class, @@ -423,20 +424,37 @@ class communication_feature implements $instanceimage = $this->processor->get_avatar(); $contenturi = null; - // If avatar is set for the instance, upload to Matrix. Otherwise, leave null for unsetting. - if (!empty($instanceimage)) { - // First upload the content. - $response = $this->matrixapi->upload_content($instanceimage); - $body = self::get_body($response); - $contenturi = $body->content_uri; + if ($this->matrixapi->implements_feature(media_create_feature::class)) { + // From version 1.7 we can fetch a mxc URI and use it before uploading the content. + if ($instanceimage) { + $response = $this->matrixapi->media_create(); + $contenturi = self::get_body($response)->content_uri; + + // Now update the room avatar. + $response = $this->matrixapi->update_room_avatar($this->get_room_id(), $contenturi); + + // And finally upload the content. + $this->matrixapi->upload_content($instanceimage); + } else { + $response = $this->matrixapi->update_room_avatar($this->get_room_id(), null); + } + } else { + // Prior to v1.7 the only way to upload content was to upload the content, which returns a mxc URI to use. + + if ($instanceimage) { + // First upload the content. + $response = $this->matrixapi->upload_content($instanceimage); + $body = self::get_body($response); + $contenturi = $body->content_uri; + } // Now update the room avatar. $response = $this->matrixapi->update_room_avatar($this->get_room_id(), $contenturi); + } - // Indicate the avatar has been synced if it was successfully set with Matrix. - if ($response->getReasonPhrase() === 'OK') { - $this->processor->set_avatar_synced_flag(true); - } + // Indicate the avatar has been synced if it was successfully set with Matrix. + if ($response->getReasonPhrase() === 'OK') { + $this->processor->set_avatar_synced_flag(true); } } diff --git a/communication/provider/matrix/classes/local/spec/features/matrix/media_create_v1.php b/communication/provider/matrix/classes/local/spec/features/matrix/media_create_v1.php new file mode 100644 index 00000000000..c47f0142112 --- /dev/null +++ b/communication/provider/matrix/classes/local/spec/features/matrix/media_create_v1.php @@ -0,0 +1,47 @@ +. + +namespace communication_matrix\local\spec\features\matrix; + +use communication_matrix\local\command; +use GuzzleHttp\Psr7\Response; + +/** + * Matrix API feature to create an mxc Media URI. + * + * https://spec.matrix.org/v1.1/client-server-api/#post_matrixmediav3upload + * + * @package communication_matrix + * @copyright 2023 Andrew Lyons + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + * @codeCoverageIgnore + * This code does not warrant being tested. Testing offers no discernible benefit given its usage is tested. + */ +trait media_create_v1 { + + /** + * Create a media URI. + * + * @return Response + */ + public function media_create(): Response { + return $this->execute(new command( + $this, + method: 'POST', + endpoint: '_matrix/media/v1/create', + )); + } +} diff --git a/communication/provider/matrix/classes/local/spec/features/matrix/upload_content_v3.php b/communication/provider/matrix/classes/local/spec/features/matrix/upload_content_v3.php index 962553e9592..0e53bc04328 100644 --- a/communication/provider/matrix/classes/local/spec/features/matrix/upload_content_v3.php +++ b/communication/provider/matrix/classes/local/spec/features/matrix/upload_content_v3.php @@ -37,23 +37,41 @@ trait upload_content_v3 { * Upload the content in the matrix/synapse server. * * @param null|\stored_file $content The content to be uploaded + * @param null|string $mediaid The mediaid to associate a file with. Supported for v1.7 API an above only. * @return Response */ public function upload_content( ?\stored_file $content, + ?string $mediaid = null, ): Response { $query = []; if ($content) { $query['filename'] = $content->get_filename(); } - $command = new command( - $this, - method: 'POST', - endpoint: '_matrix/media/v3/upload', - sendasjson: false, - query: $query, - ); + if ($mediaid !== null) { + // Specification of the mediaid requires version 1.7 or above of the upload API. + // See https://spec.matrix.org/v1.7/client-server-api/#put_matrixmediav3uploadservernamemediaid. + $this->requires_version('1.7'); + $command = new command( + $this, + method: 'PUT', + endpoint: '_matrix/media/v3/upload/:mediaid', + sendasjson: false, + query: $query, + params: [ + 'mediaid' => $mediaid, + ], + ); + } else { + $command = new command( + $this, + method: 'POST', + endpoint: '_matrix/media/v3/upload', + sendasjson: false, + query: $query, + ); + } if ($content) { // Add the content-type, and header. diff --git a/communication/provider/matrix/classes/local/spec/v1p7.php b/communication/provider/matrix/classes/local/spec/v1p7.php index 3e2012500b3..0f2acf82c81 100644 --- a/communication/provider/matrix/classes/local/spec/v1p7.php +++ b/communication/provider/matrix/classes/local/spec/v1p7.php @@ -27,7 +27,9 @@ namespace communication_matrix\local\spec; * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class v1p7 extends v1p6 { - // Note: A new Content Upload API was introduced, but it doesn't benefit us in any way. + // Note: A new Content Upload API was introduced. // See details in the spec: // https://github.com/matrix-org/matrix-spec-proposals/pull/2246. + + use features\matrix\media_create_v1; } diff --git a/communication/provider/matrix/tests/matrix_client_test.php b/communication/provider/matrix/tests/matrix_client_test.php index 272bea4e327..f7b68261e08 100644 --- a/communication/provider/matrix/tests/matrix_client_test.php +++ b/communication/provider/matrix/tests/matrix_client_test.php @@ -253,6 +253,16 @@ class matrix_client_test extends \advanced_testcase { */ public function implements_feature_provider(): array { return [ + 'Basic supported feature' => [ + 'v1.7', + features\matrix\media_create_v1::class, + true, + ], + 'Basic unsupported feature' => [ + 'v1.6', + features\matrix\media_create_v1::class, + false, + ], '[supported] as array' => [ 'v1.6', [features\matrix\create_room_v3::class], @@ -266,6 +276,21 @@ class matrix_client_test extends \advanced_testcase { ], true, ], + '[unsupported] as array' => [ + 'v1.6', + [ + features\matrix\media_create_v1::class, + ], + false, + ], + '[unsupported, supported] as array' => [ + 'v1.6', + [ + features\matrix\media_create_v1::class, + features\matrix\update_room_avatar_v3::class, + ], + true, + ], ]; } @@ -290,6 +315,27 @@ class matrix_client_test extends \advanced_testcase { true, ]; + $testcases['Require many including an unsupported feature'] = [ + 'v1.6', + [ + features\matrix\create_room_v3::class, + features\matrix\media_create_v1::class, + ], + false, + ]; + + $testcases['Require many including an unsupported feature which has an alternate'] = [ + 'v1.6', + [ + features\matrix\create_room_v3::class, + [ + features\matrix\media_create_v1::class, + features\matrix\update_room_avatar_v3::class, + ], + ], + true, + ]; + return $testcases; }