Commit cde5a63a authored by Patrick Barroca's avatar Patrick Barroca 🐧

activitypub : community server harvesting federation

parent e0782270
......@@ -176,79 +176,56 @@ class Activitypub_ReviewController extends ZendAfi_Controller_Action {
if (Class_WebService_ActivityPub::MIME_TYPE != $this->_request->getHeader('Accept'))
return $this->_response->setHttpResponseCode(500);
$params = ['module' => 'activitypub',
'controller' => 'review',
'action' => 'outbox'];
$collection_url = $this->_absoluteUrl($params);
if ((!$auth = $this->_request->getHeader('Authorization'))
|| (!$bearer = trim(str_replace('Bearer ', '', $auth))))
return $this->_response->setHttpResponseCode(403);
if ($key = $this->_getParam('key'))
return $this->_receiveRecordQuery($key);
return $this->_receiveRecordQueryFrom($key, $bearer);
$total = Class_AvisNotice::count();
$items_by_page = 15;
$paginator = Zend_Paginator::factory($total)->setItemCountPerPage($items_by_page);
if ($page = $this->_getParam('page')) {
$models = Class_AvisNotice::findAllBy([]);
$items = [];
foreach($models as $model)
$items[] = ['type' => 'Create',
'actor' => $this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'user',
'id' => $model->getIdUser()]),
'object' => $this->_absoluteUrl(['module' => 'opac',
'controller' => 'blog',
'action' => 'viewavis',
'id' => $model->getId()])];
$response = ['@context' => 'https://www.w3.org/ns/activitystreams',
'type' => 'CollectionPage',
'id' => $this->_absoluteUrl(array_merge($params, ['page' => $page])),
'partOf' => $collection_url,
'items' => $items];
return $this->_helper->json($response);
}
if (Class_AdminVar::get('FEDERATION_COMMUNITY_SERVER') != $bearer)
return $this->_response->setHttpResponseCode(403);
$response = ['@context' => 'https://www.w3.org/ns/activitystreams',
'type' => 'Collection',
'id' => $collection_url,
'totalItems' => $total,
'first' => $this->_absoluteUrl(array_merge($params, ['page' => '1']))];
$filters = ['abon_ou_bib' => Class_AvisNotice::TYPE_LIBRARIAN];
if (Class_AdminVar::isLibrarianReviewsModerated())
$filters['statut'] = Class_AvisNotice::STATUS_VALIDATED;
if (1 < ($page_count = $paginator->count()))
$response['last'] = $this->_absoluteUrl(array_merge($params, ['page' => $page_count]));
if ($from = $this->_getParam('from')) {
if (false === $from_date = DateTime::createFromFormat('Y-m-d', $from))
return $this->_response->setHttpResponseCode(400);
$this->_helper->json($response);
$filters['where'] = 'date_avis >= "' . $from_date->format('Y-m-d') . '"';
}
$this->_reviewPageFilteredBy($filters);
}
protected function _receiveRecordQuery($key) {
if ((!$auth = $this->_request->getHeader('Authorization'))
|| (!$bearer = trim(str_replace('Bearer ', '', $auth)))
|| (!$member = Class_Federation_GroupMembership::findFirstBy(['actor_id' => $bearer,
'group_name' => 'REVIEW_DISPLAY'])))
protected function _receiveRecordQueryFrom($key, $bearer) {
if (!Class_Federation_GroupMembership::findFirstBy(['actor_id' => $bearer,
'group_name' => 'REVIEW_DISPLAY']))
return $this->_response->setHttpResponseCode(403);
$total = Class_AvisNotice::countBy(['clef_oeuvre' => $key]);
$items_by_page = 15;
$page = $this->_getParam('page', '1');
$filters = ['clef_oeuvre' => $key, 'source_actor_id not' => null];
$this->_reviewPageFilteredBy($filters, ['key' => $key]);
}
$models = Class_AvisNotice::findAllBy(['clef_oeuvre' => $key,
'limitPage' => [$page, $items_by_page]]);
$items = [];
foreach($models as $model)
$items[] = ['date' => $model->getDateAvis(),
'title' => $model->getEntete(),
'content' => $model->getAvis(),
'rating' => $model->getNote()];
protected function _reviewPageFilteredBy($filters, $url_params=[]) {
$items_by_page = 15;
$total = Class_AvisNotice::countBy($filters);
$page = $this->_getParam('page', 1);
$models = Class_AvisNotice::findAllBy(array_merge($filters,
['limitPage' => [$page, $items_by_page]]));
$items = array_map([$this, '_filterAttributes'], $models);
$activity = (new CollectionPage)
->id($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review',
'action' => 'outbox',
'key' => $key,
'page' => $page]))
->id($this->_absoluteUrl(array_merge(['module' => 'activitypub',
'controller' => 'review',
'action' => 'outbox',
'page' => $page],
$url_params)))
->totalItems($total)
->items($items);
......@@ -256,6 +233,17 @@ class Activitypub_ReviewController extends ZendAfi_Controller_Action {
}
protected function _filterAttributes($review) {
return ['id' => $review->getId(),
'clef_oeuvre' => $review->getClefOeuvre(),
'date_avis' => $review->getDateAvis(),
'date_mod' => $review->getDateMod(),
'note' => $review->getNote(),
'entete' => $review->getEntete(),
'avis' => $review->getAvis()];
}
protected function _absoluteUrl($params) {
return Class_Url::absolute($params, null, true, false);
}
......@@ -270,8 +258,7 @@ class Activitypub_ReviewController extends ZendAfi_Controller_Action {
protected function _activityResponseWith($activity) {
(new Class_WebService_ActivityPubServer($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review'],
null, true)))
'controller' => 'review'])))
->setLogger($this->_log)
->respondTo($this->_request, $this->_response, $activity);
}
......
......@@ -77,3 +77,16 @@ try {
. ') engine=MyISAM default charset=utf8'
);
} catch(Exception $e) {}
try {
$adapter->query(
'ALTER TABLE `notices_avis` '
. 'ADD COLUMN `source_actor_id` varchar(255) null default null,'
. 'ADD COLUMN `source_author` varchar(255) null default null,'
. 'ADD COLUMN `source_primary` int(11) null default null,'
. 'ADD KEY `source_actor_id` (`source_actor_id`),'
. 'ADD KEY `source_author` (`source_author`),'
. 'ADD KEY `source_primary` (`source_primary`)'
);
} catch(Exception $e) {}
......@@ -471,7 +471,8 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
protected function _getFederationVars() {
return ['FEDERATION_PUBKEY' => Class_AdminVar_Meta::newCryptKey($this->_('Clé publique permettant la vérification de signature des messages envoyés par ce Bokeh à la fédération')),
'FEDERATION_PRIVKEY' => Class_AdminVar_Meta::newCryptKey($this->_('Clé privée permettant de générer les signatures des messages envoyés par ce Bokeh à la fédération')),
'FEDERATION_COMMUNITY_SERVER' => Class_AdminVar_Meta::newDefault($this->_('URL du serveur communautaire de la fédération'))];
'FEDERATION_COMMUNITY_SERVER' => Class_AdminVar_Meta::newDefault($this->_('URL du serveur communautaire de la fédération')),
'FEDERATION_IS_COMMUNITY_SERVER' => Class_AdminVar_Meta::newOnOff($this->_('Ce Bokeh est un serveur communautaire'))];
}
......@@ -1020,6 +1021,16 @@ class Class_AdminVarLoader extends Storm_Model_Loader {
public function shouldKeepLastSigbRecord() {
return Class_AdminVar::isModuleEnabled('KEEP_LAST_SIGB_RECORD');
}
public function isFederationCommunityServer() {
return Class_AdminVar::isModuleEnabled('FEDERATION_IS_COMMUNITY_SERVER');
}
public function isLibrarianReviewsModerated() {
return Class_AdminVar::isModuleEnabled('MODO_AVIS_BIBLIO');
}
}
......
......@@ -239,9 +239,11 @@ class AvisNoticeLoader extends Storm_Model_Loader {
class Class_AvisNotice extends Storm_Model_Abstract {
use Trait_Avis, Trait_Translator;
const NO_FLAG=0;
const ORPHAN_FLAG=1;
const ARCHIVED_FLAG=2;
const NO_FLAG = 0;
const ORPHAN_FLAG = 1;
const ARCHIVED_FLAG = 2;
const STATUS_VALIDATED = 1;
const TYPE_LIBRARIAN = 1;
protected $_loader_class = 'AvisNoticeLoader';
protected $_table_name = 'notices_avis';
......
......@@ -41,7 +41,9 @@ class Class_BatchLoader extends Storm_Model_Loader {
Class_Batch_BuildSiteMap::TYPE => new Class_Batch_BuildSiteMap(),
Class_Batch_PremierChapitre::TYPE => new Class_Batch_PremierChapitre(),
Class_Batch_NoveltyFacet::TYPE => new Class_Batch_NoveltyFacet(),
Class_Batch_ExternalAgenda::TYPE => new Class_Batch_ExternalAgenda]);
Class_Batch_ExternalAgenda::TYPE => new Class_Batch_ExternalAgenda(),
Class_Batch_FederationReviewHarvest::TYPE => new Class_Batch_FederationReviewHarvest(),
]);
}
......
<?php
/**
* Copyright (c) 2012-2019, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Class_Batch_FederationReviewHarvest extends Class_Batch_Abstract {
const TYPE = 'FEDERATION_REVIEW_HARVEST';
public function getLabel() {
return $this->_('Moissonner les avis des Bokehs ayant activé le partage d\'avis');
}
public function isEnabled() {
return Class_AdminVar::isFederationCommunityServer();
}
public function run() {
$members = Class_Federation_GroupMembership::findAllBy(['group_name' => 'REVIEW_SHARE']);
foreach ($members as $member)
$this->_harvestMember($member);
}
protected function _harvestMember($member) {
$service = new Class_WebService_ActivityPub($member->getActorId());
if (!$service->isValid())
return;
$journal_type = 'AP_REVIEW_HARVEST_' . $member->getId();
$from = ($last = Class_Journal::lastOf($journal_type))
? $last->getCreatedAt()
: '';
$reviews = $service->harvestReviews($from);
if (!$reviews && $service->getLastMessage()) {
// todo log message
return;
}
foreach($reviews as $review)
$this->_harvestReviewOf($review, $member);
Class_Journal::newInstance(['type' => $journal_type]);
}
protected function _harvestReviewOf($review, $member) {
$source_primary = $review['id'];
unset($review['id']);
$key_params = ['source_primary' => $source_primary,
'source_actor_id' => $member->getActorId()];
if (!$model = Class_AvisNotice::findFirstBy($key_params))
$model = Class_AvisNotice::newInstance($key_params);
$model->updateAttributes($review)->save();
}
}
......@@ -136,16 +136,7 @@ class Class_FederationReview {
if (!$collection_page = $service->reviews($record, $page))
return Class_Notice_ReviewsSet::emptyInstance();
$reviews = [];
foreach($collection_page->items() as $item) {
$reviews[] = (new Class_AvisNotice())
->setDateAvis($item['date'])
->setEntete($item['title'])
->setAvis($item['content'])
->setNote(($item['rating'] ? $item['rating'] : 0))
->setNotice($record)
->setUser(null);
}
$reviews = array_map([$this, '_receiveReview'], $collection_page->items());
return new Class_Notice_ReviewsSet($this->_('Avis communautaires'),
$reviews,
......@@ -155,6 +146,12 @@ class Class_FederationReview {
}
protected function _receiveReview($review) {
unset($review['id']);
return (new Class_AvisNotice())->updateAttributes($review);
}
protected function _getCommunityService() {
if (!$community_server = $this->_getCommunityServer())
return;
......
......@@ -285,12 +285,32 @@ class Class_WebService_ActivityPub {
public function reviews($notice, $page) {
return $this->_getReviewsPage(['key' => $notice->getClefOeuvre(),
'page' => $page]);
}
public function harvestReviews($from) {
$reviews = new Storm_Collection();
$page = 1;
while (($collection_page = $this->_getReviewsPage(['from' => $from,
'page' => $page]))
&& ($items = $collection_page->items())) {
$reviews->addAll($items);
$page++;
}
return $reviews;
}
protected function _getReviewsPage($query_params) {
if (!$outbox = $this->_outboxUrl())
return;
$outbox .= '?' . http_build_query(['key' => $notice->getClefOeuvre(),
'page' => $page]);
$outbox .= '?' . http_build_query($query_params);
$request_target = 'get ' . Zend_Uri::factory($outbox)->getPath();
$response = $this->_activityGet($outbox, $request_target);
if (!$body = $this->_validateResponseAndBody($response, $request_target))
......
......@@ -2548,6 +2548,9 @@ class UpgradeDB_369_Test extends UpgradeDBTestCase {
$this->dropTable('federation_follow');
$this->dropTable('federation_follower');
$this->dropTable('federation_group_membership');
$this->dropIndexedFieldFrom('notices_avis', 'source_actor_id');
$this->dropIndexedFieldFrom('notices_avis', 'source_author');
$this->dropIndexedFieldFrom('notices_avis', 'source_primary');
}
......@@ -2695,4 +2698,20 @@ class UpgradeDB_369_Test extends UpgradeDBTestCase {
$this->assertFieldType('federation_group_membership', $field, $type);
}
public function noticesAvisNewFields() {
return [['source_actor_id', 'varchar(255)'],
['source_author', 'varchar(255)'],
['source_primary', 'int(11)']];
}
/**
* @test
* @dataProvider noticesAvisNewFields
*/
public function noticesAvisNewFieldShouldExistsAndBeIndexed($field, $type) {
$this->assertFieldType('notices_avis', $field, $type);
$this->assertIndex('notices_avis', $field);
}
}
<?php
/**
* Copyright (c) 2012-2019, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class ActivitypubAdminBatchControllerTest extends Admin_AbstractControllerTestCase {
protected $_storm_default_to_volatile = true;
/** @test */
public function withServerDisabledFederationBatchShouldNotBeAvailable() {
Class_AdminVar::set('FEDERATION_IS_COMMUNITY_SERVER', '0');
$this->dispatch('/admin/batch');
$this->assertNotXPath('//a[contains(@href, "FEDERATION_REVIEW_HARVEST")]');
}
/** @test */
public function withServerEnabledFederationBatchShouldBeAvailable() {
Class_AdminVar::set('FEDERATION_IS_COMMUNITY_SERVER', '1');
$this->dispatch('/admin/batch');
$this->assertXPath('//a[contains(@href, "FEDERATION_REVIEW_HARVEST")]');
}
}
class ActivitypubFederationReviewBatchTest extends ModelTestCase {
protected
$_actor_id,
$_get_response_called = false,
$_web_client;
public function setUp() {
parent::setUp();
$this->_actor_id = 'https://review-share.member.com/activitypub/review';
$this->fixture('Class_Federation_GroupMembership',
['id' => 1,
'group_name' => 'REVIEW_SHARE',
'actor_id' => $this->_actor_id,
'accepted_at' => '2019-05-03 14:15:54']);
$this->_web_client = $this
->mock()
->whenCalled('open_url')
->with($this->_actor_id, ['headers' => ['Accept' => Class_WebService_ActivityPub::MIME_TYPE]])
->answers(str_replace('{actor}',
$this->_actor_id,
'{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Service",
"id": "{actor}",
"inbox": "{actor}/inbox",
"outbox": "{actor}/outbox",
"publicKey": "{actor}/pubkey"
}'))
->whenCalled('open_url')
->with($this->_actor_id . '/pubkey',
['headers' => ['Accept' => Class_WebService_ActivityPub::MIME_TYPE]])
->answers(str_replace('{actor}',
$this->_actor_id,
'{
"@context": "https://www.w3.org/ns/activitystreams",
"type": "Key",
"id": "{actor}/pubkey",
"owner": "{actor}",
"publicKeyPem": "TheirKey"
}'))
->whenCalled('getResponse')
->willDo(function()
{
if ($this->_get_response_called)
return $this->_responseWithItems([]);
$this->_get_response_called = true;
return $this->_responseWithItems([['id' => '3773',
'clef_oeuvre' => 'UNAMOURDELAPIN44--DAVISJ-',
'date_avis' => '2019-04-19 17:46:27',
'date_mod' => null,
'note' => '3',
'entete' => 'Au top',
'avis' => 'Comme d\'habitude, trop bien !',
'source_author' => 'SuperBibliothécaire']]);
})
;
$signer = Storm_Test_ObjectWrapper::on(new Class_HttpSignature('keyId="'. $this->_actor_id .'/pubkey", algorithm="rsa-sha256", headers="(request-target) date digest", signature="TheirSignature"'))
->whenCalled('sign')->answers('MySignature')
->whenCalled('verify')->answers(true);
Class_WebService_ActivityPub::setSigner($signer);
Class_WebService_ActivityPub::setTimeSource(new TimeSourceForTest('2019-05-03 16:10:35'));
Class_WebService_ActivityPub::setWebClient($this->_web_client);
Class_WebService_ActivityPub::setThrowErrors(true);
(new Class_Batch_FederationReviewHarvest)->run();
}
protected function _responseWithItems($items) {
return $this->mock()
->whenCalled('isError')->answers(false)
->whenCalled('getHeader')->with('Signature')->answers('TheirSignature')
->whenCalled('getHeaders')->answers([])
->whenCalled('getBody')
->answers(json_encode(['@context' => 'https://www.w3.org/ns/activitystreams',
'type' => 'CollectionPage',
'totalItems' => count($items),
'items' => $items]));
}
/** @test */
public function shouldHaveAReviewFromMember() {
$this->assertNotNull(Class_AvisNotice::findFirstBy(['source_actor_id' => $this->_actor_id]));
}
}
......@@ -39,11 +39,6 @@ abstract class ActivityPubReviewTestCase extends AbstractControllerTestCase {
public function setUp() {
parent::setUp();
$this->fixture('Class_AvisNotice',
['id' => 42,
'entete' => 'Not so bad',
'id_user' => 55]);
Class_AdminVar::set('FEDERATION_PUBKEY',
'-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA3/jVue8oWhJvFkQ98C8y1g3+HJUa2LXqbsRT4iNH9JySVC0B2Fg/
......@@ -138,22 +133,21 @@ class ActivityPubReviewPubkeyTest extends ActivityPubReviewTestCase {
class ActivityPubReviewOutboxTest extends ActivityPubReviewTestCase {
public function setUp() {
parent::setUp();
$this->dispatch('/activitypub/review/outbox', true,
['Accept' => Class_WebService_ActivityPub::MIME_TYPE]);
$this->_json = json_decode($this->_response->getBody(), true);
}
$client_endpoint = 'https://my.communityserver.la/activitypub/review';
Class_AdminVar::set('FEDERATION_COMMUNITY_SERVER', $client_endpoint);
/** @test */
public function typeShouldBeCollection() {
$this->assertEquals('Collection', $this->_json['type']);
$this->dispatch('/activitypub/review/outbox',
true,
['Accept' => Class_WebService_ActivityPub::MIME_TYPE,
'Authorization' => 'Bearer ' . $client_endpoint]);
$this->_json = json_decode($this->_response->getBody(), true);
}
/** @test */
public function firstPageShouldBePresent() {
$this->assertContains('first', array_keys($this->_json));
$this->assertContains('/activitypub/review/outbox/page/1', $this->_json['first']);
public function responseTypeShouldBeCollectionPage() {
$this->assertEquals('CollectionPage', $this->_json['type']);
}
}
......@@ -162,8 +156,20 @@ class ActivityPubReviewOutboxTest extends ActivityPubReviewTestCase {
class ActivityPubReviewOutboxPageOneTest extends ActivityPubReviewTestCase {
public function setUp() {
parent::setUp();
$this->dispatch('/activitypub/review/outbox/page/1', true,
['Accept' => Class_WebService_ActivityPub::MIME_TYPE]);
$this->fixture('Class_AvisNotice',
['id' => 42,
'entete' => 'Not so bad',
'user' => Class_Users::getIdentity(),
'date_mod' => null]);
$client_endpoint = 'https://my.communityserver.la/activitypub/review';
Class_AdminVar::set('FEDERATION_COMMUNITY_SERVER', $client_endpoint);
$this->dispatch('/activitypub/review/outbox/page/1',
true,
['Accept' => Class_WebService_ActivityPub::MIME_TYPE,
'Authorization' => 'Bearer ' . $client_endpoint]);
$this->_json = json_decode($this->_response->getBody(), true);
}
......@@ -175,27 +181,60 @@ class ActivityPubReviewOutboxPageOneTest extends ActivityPubReviewTestCase {
/** @test */
public function pageShouldBePartOfCollection() {
$this->assertContains('partOf', array_keys($this->_json));
$this->assertEquals('/activitypub/review/outbox', substr($this->_json['partOf'], -26));
public function firstReviewShouldHaveEnteteNotSoBad() {
$this->assertEquals('Not so bad', $this->_json['items'][0]['entete']);
}
}
class ActivityPubReviewOutboxBadFromTest extends ActivityPubReviewTestCase {
public function setUp() {
parent::setUp();
$client_endpoint = 'https://my.communityserver.la/activitypub/review';
Class_AdminVar::set('FEDERATION_COMMUNITY_SERVER', $client_endpoint);
$this->dispatch('/activitypub/review/outbox?from=no%20valid',
true,
['Accept' => Class_WebService_ActivityPub::MIME_TYPE,
'Authorization' => 'Bearer ' . $client_endpoint]);
$this->_json = json_decode($this->_response->getBody(), true);
}
/** @test */
public function firstActivityShouldBeCreation() {
$this->assertEquals('Create', $this->_json['items'][0]['type']);
public function responseShouldBeError400() {
$this->assertResponseCode(400);
}
}
class ActivityPubReviewOutboxValidFromTest extends ActivityPubReviewTestCase {
public function setUp() {
parent::setUp();
$client_endpoint = 'https://my.communityserver.la/activitypub/review';
Class_AdminVar::set('FEDERATION_COMMUNITY_SERVER', $client_endpoint);
$this->dispatch('/activitypub/review/outbox?from=2019-05-17',
true,
['Accept' => Class_WebService_ActivityPub::MIME_TYPE,
'Authorization' => 'Bearer ' . $client_endpoint]);
$this->_json = json_decode($this->_response->getBody(), true);
}
/** @test */
public function firstActivityActorShouldBeUser55() {
$this->assertContains('/activitypub/user/index/id/55', $this->_json['items'][0]['actor']);
public function responseShouldBeSuccess200() {
$this->assertResponseCode(200);
}
/** @test */
public function firstActivityObjectShouldBeReview42() {
$this->assertContains('/blog/viewavis/id/42', $this->_json['items'][0]['object']);
public function typeShouldBeCollectionPage() {
$this->assertEquals('CollectionPage', $this->_json['type']);
}
}
......@@ -601,7 +640,7 @@ class ActivityPubReviewOutboxForRecordTest extends ActivityPubReviewTestCase {
$this->fixture('Class_AvisNotice',
['id' => 55,
'ID_USER' => '1',
'user' => Class_Users::getIdentity(),
'CLEF_OEUVRE' => 'UNAMOURDELAPIN44--DAVISJ-',
'ID_NOTICE' => '113132',
'DATE_AVIS' => '2019-04-19 17:46:27',
......@@ -610,10 +649,13 @@ class ActivityPubReviewOutboxForRecordTest extends ActivityPubReviewTestCase {
'ENTETE' => 'Au top',
'AVIS' => 'comme d\'habitude trop bien !',
'STATUT' => '0',
'ABON_OU_BIB' => '1',
'abon_ou_bib' => '1',
'USER_KEY' => '0--0--sysadm',
'flags' => '0',
'type_doc' => '1',
'source_actor_id' => 'https://other.bokeh.es/activitypub/review',
'source_author' => null,
'source_primary' => 4839
]);
$client_endpoint = 'https://my.super_bokeh.nl/activitypub/review';
......@@ -640,6 +682,112 @@ class ActivityPubReviewOutboxForRecordTest extends ActivityPubReviewTestCase {
/** @test */
public function firstReviewTitleShouldBeAuTop() {
$this->assertEquals('Au top', $this->_json['items'][0]['title']);
$this->assertEquals('Au top', $this->_json['items'][0]['entete']);
}
}
class ActivityPubReviewOutboxHarvestingTest extends ActivityPubReviewTestCase {
protected $_review;