Commit 89526f39 authored by Patrick Barroca's avatar Patrick Barroca 😁
Browse files

dev #93553 : federated reviews

parent eff96ddf
Pipeline #8173 passed with stage
in 36 minutes and 46 seconds
......@@ -16,3 +16,9 @@
[submodule "library/matomo-php-tracker"]
path = library/matomo-php-tracker
url = http://git.afi-sa.fr/afi/matomo-php-tracker.git
[submodule "library/phpseclib"]
path = library/phpseclib
url = https://git.afi-sa.net/afi/phpseclib.git
[submodule "library/activitystreams"]
path = library/activitystreams
url = https://git.afi-sa.net/afi/activitystreams.git
- ticket #93553 : Avis : Ajout d'une fédération des avis
\ No newline at end of file
<?php
/**
* Copyright (c) 2012-2017, 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
*/
require_once __DIR__ . '/../../../../library/activitystreams/autoload.php';
use Patbator\ActivityStreams\Model\Join;
use Patbator\ActivityStreams\Model\Leave;
use Patbator\ActivityStreams\Model\Accept;
use Patbator\ActivityStreams\Model\Reject;
use Patbator\ActivityStreams\Model\CollectionPage;
use Patbator\ActivityStreams\Stream;
class Activitypub_ReviewController extends ZendAfi_Controller_Action {
use Trait_TimeSource;
public function preDispatch() {
parent::preDispatch();
$this->getHelper('ViewRenderer')->setNoRender();
$this->_log = null;//new Zend_Log(new Zend_Log_Writer_Stream(PATH_TEMP . 'activitypub_server.log'));
}
public function unavailableAction() {
$this->_response->setHttpResponseCode(503);
}
public function indexAction() {
if (Class_WebService_ActivityPub::MIME_TYPE != $this->_request->getHeader('Accept'))
return $this->_response->setHttpResponseCode(500);
$service = $this->_service()->asActor()
->inbox($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review',
'action' => 'inbox']))
->outbox($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review',
'action' => 'outbox']))
->publicKey($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review',
'action' => 'pubkey']))
;
$this->_activityResponseWith($service);
}
public function pubkeyAction() {
if (Class_WebService_ActivityPub::MIME_TYPE != $this->_request->getHeader('Accept'))
return $this->_response->setHttpResponseCode(500);
$key = (new Class_ActivityPub_PublicKey)
->id($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review',
'action' => 'pubkey']))
->owner($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review']))
->publicKeyPem((new Class_Federation())->getPublicKey());
$this->_activityResponseWith($key);
}
public function inboxAction() {
if (!$this->_request->isPost()
|| Class_WebService_ActivityPub::MIME_TYPE != $this->_request->getHeader('Content-Type'))
return $this->_response->setHttpResponseCode(500);
if (!$signature = $this->_request->getHeader('Signature'))
return $this->_response->setHttpResponseCode(400);
$rawBody = $this->_request->getRawBody();
if ((!$activity = Stream::fromJson($rawBody)->getRoot())
|| (!$actor = $activity->actor()))
return $this->_response->setHttpResponseCode(400);
$service = (new Class_WebService_ActivityPub($actor->id()))
->setLogger($this->_log);
if (!$service->validateRequest($this->_request))
return $this->_response->setHttpResponseCode(400);
if ($handler = Activitypub_ReviewController_GroupHandler::handlerFor($activity)) {
$response = $handler->handle()
->actor($this->_service());
return $this->_activityResponseWith($response);
}
$this->_response->setHttpResponseCode(400);
}
public function outboxAction() {
if (Class_WebService_ActivityPub::MIME_TYPE != $this->_request->getHeader('Accept'))
return $this->_response->setHttpResponseCode(500);
if ((!$auth = $this->_request->getHeader('Authorization'))
|| (!$bearer = trim(str_replace('Bearer ', '', $auth))))
return $this->_response->setHttpResponseCode(403);
if ($key = $this->_getParam('key'))
return $this->_receiveRecordQueryFrom($key, $bearer);
if (Class_AdminVar::get('FEDERATION_COMMUNITY_SERVER') != $bearer)
return $this->_response->setHttpResponseCode(403);
$filters = ['abon_ou_bib' => Class_AvisNotice::TYPE_LIBRARIAN];
if (Class_AdminVar::isLibrarianReviewsModerated())
$filters['statut'] = Class_AvisNotice::STATUS_VALIDATED;
if ($from = $this->_getParam('from')) {
if (false === $from_date = DateTime::createFromFormat('Y-m-d', $from))
return $this->_response->setHttpResponseCode(400);
$filters['where'] = 'date_avis >= "' . $from_date->format('Y-m-d') . '"';
}
$this->_reviewPageFilteredBy($filters);
}
protected function _receiveRecordQueryFrom($key, $bearer) {
if (!Class_Federation_GroupMembership::findFirstBy(['actor_id' => $bearer,
'group_name' => 'REVIEW_DISPLAY']))
return $this->_response->setHttpResponseCode(403);
$filters = ['clef_oeuvre' => $key, 'source_actor_id not' => null];
$this->_reviewPageFilteredBy($filters, ['key' => $key]);
}
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(array_merge(['module' => 'activitypub',
'controller' => 'review',
'action' => 'outbox',
'page' => $page],
$url_params)))
->totalItems($total)
->items($items);
$this->_activityResponseWith($activity);
}
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(),
'abon_ou_bib' => $review->getAbonOuBib(),
'source_author' => $review->getSourceAuthor()];
}
protected function _absoluteUrl($params) {
return Class_Url::absolute($params, null, true, false);
}
protected function _service() {
return (new Class_ActivityPub_Service())
->id($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review']))
->name((new Class_Federation())->getActorName());
}
protected function _activityResponseWith($activity) {
(new Class_WebService_ActivityPubServer($this->_absoluteUrl(['module' => 'activitypub',
'controller' => 'review'])))
->setLogger($this->_log)
->respondTo($this->_request, $this->_response, $activity);
}
}
abstract class Activitypub_ReviewController_GroupHandler {
protected $_group_name, $_actor_id, $_activity_id;
public static function handlerFor($activity) {
if (!$activity->object()
|| (!$group_name = $activity->object()->name()))
return;
if ($activity instanceof Join)
return new Activitypub_ReviewController_JoinHandler($activity);
if ($activity instanceof Leave)
return new Activitypub_ReviewController_LeaveHandler($activity);
}
public function __construct($activity) {
$this->_group_name = $activity->object()->name();
$this->_actor_id = $activity->actor()->id();
$this->_activity_id = $activity->id();
}
protected function _findMembership() {
return Class_Federation_GroupMembership::findFirstBy($this->_membershipParams());
}
protected function _newMembership() {
return Class_Federation_GroupMembership::newInstance($this->_membershipParams());
}
protected function _membershipParams() {
return ['actor_id' => $this->_actor_id,
'group_name' => $this->_group_name];
}
protected function _reject($message) {
return (new Reject)->summary($message);
}
protected function _accept($member) {
return (new Accept)->id(Class_Url::absolute(['module' => 'activitypub',
'controller' => 'review',
'action' => 'group-membership',
'id' => $member->getId()],
null, true, false));
}
public function handle() {
return Class_AdminVar::isFederationCommunityServer()
? $this->_handle()
: $this->_reject('Service unavailable');
}
abstract protected function _handle();
}
class Activitypub_ReviewController_JoinHandler extends Activitypub_ReviewController_GroupHandler {
protected function _handle() {
if (!$member = $this->_findMembership())
$member = $this->_newMembership();
$response = ($member->isNew() && !$member->save())
? $this->_reject(implode(', ', $member->getErrors()))
: $this->_accept($member);
return $response->object((new Join)->id($this->_activity_id));
}
}
class Activitypub_ReviewController_LeaveHandler extends Activitypub_ReviewController_GroupHandler {
protected function _handle() {
if ($member = $this->_findMembership())
$member->delete();
$response = (!$member)
? $this->_reject('Cannot leave a group you did not join')
: $this->_accept($member);
return $response->object((new Leave)->id($this->_activity_id));
}
}
\ No newline at end of file
<?php
/**
* Copyright (c) 2012-2017, 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 Admin_FederationReviewsController extends ZendAfi_Controller_Action {
public function indexAction() {
$this->view->titre = $this->_('Avis communautaires');
}
public function enableDisplayAction() {
$federation = Class_FederationReview::getInstance();
$message = $federation->enableDisplay()
? $this->_('Affichage des avis communautaires activé')
: $this->_('Activation impossible : %s',
$federation->getLastMessage());
$this->_helper->notify($message);
$this->_redirectToIndex();
}
public function disableDisplayAction() {
Class_FederationReview::getInstance()->disableDisplay();
$this->_helper->notify($this->_('Affichage des avis communautaires désactivé'));
$this->_redirectToIndex();
}
public function enableShareAction() {
$federation = Class_FederationReview::getInstance();
$message = $federation->enableShare()
? $this->_('Partage des avis à la communauté activé')
: $this->_('Activation impossible : %s',
$federation->getLastMessage());
$this->_helper->notify($message);
$this->_redirectToIndex();
}
public function disableShareAction() {
Class_FederationReview::getInstance()->disableShare();
$this->_helper->notify($this->_('Partage des avis à la communauté désactivé'));
$this->_redirectToIndex();
}
}
\ No newline at end of file
<?php
/**
* Copyright (c) 2012-2017, 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 Admin_JournalController extends ZendAfi_Controller_Action {
public function getPlugins() {
return ['ZendAfi_Controller_Plugin_ResourceDefinition_Journal'];
}
}
\ No newline at end of file
<?php
echo $this->tag('p',
$this->_('Bokeh peut se connecter à un serveur communautaire de partage d\'avis.'));
$this->disable_onchange = true;
$federation_review = Class_FederationReview::getInstance();
echo $federation_review->isDisplayEnabled()
? $this->Button_Cancel((new Class_Entity())
->setText($this->_('Désactiver l\'affichage des avis communautaires'))
->setUrl($this->url(['action' => 'disable-display'])))
: $this->Button_New((new Class_Entity())
->setText($this->_('Activer l\'affichage des avis communautaires'))
->setUrl($this->url(['action' => 'enable-display'])));
echo $federation_review->isShareEnabled()
? $this->Button_Cancel((new Class_Entity())
->setText($this->_('Désactiver l\'envoi des avis de ce portail à la communauté'))
->setUrl($this->url(['action' => 'disable-share'])))
: $this->Button_New((new Class_Entity())
->setText($this->_('Activer l\'envoi des avis de ce portail à la communauté'))
->setUrl($this->url(['action' => 'enable-share'])));
......@@ -177,11 +177,15 @@ class AbonneController extends ZendAfi_Controller_Action {
->setTypeDoc($notice->getTypeDoc())
->setStatut(0);
$journal_type = $avis->isNew() ? 'REVIEW_CREATE' : 'REVIEW_UPDATE';
  • Pourquoi le faire de façon inconditionnelle ? J'avais cru lire que c'était uniquement pour les bibliothécaires.

  • j'ai supprimé, les entrées de journal pour les avis ne sont finalement pas utilisées par cette story

Please register or sign in to reply
if ($avis->save()) {
$this->_user
->setPseudo($this->_request->getParam('avisSignature'))
->save();
$this->_helper->journal($journal_type, $avis);
$this->_helper->notify($this->_('Votre avis à bien été enregistré'));
return $this->_redirectClose($this->_getReferer());
}
......
<?php
//re run 1.2
(new Class_Migration_DigitalResource_ToutApprendre())->run();
\ No newline at end of file
(new Class_Migration_DigitalResource_ToutApprendre())->run();
  • Les trois prochaines modifications sont inutiles et ne correspondent pas au développement.

  • arf, j'ai viré les retours chariot en trop, ça m'arrive parfois lorsqu'un patch conflict :(

Please register or sign in to reply
<?php
(new Class_Migration_DigitalResource_1DTouch())->run();
\ No newline at end of file
(new Class_Migration_DigitalResource_1DTouch())->run();
......@@ -9,4 +9,4 @@ $try = function ($query) use($adapter) {
$try('ALTER TABLE notices add column type int not null default 1, add KEY type (type)');
$try('ALTER TABLE exemplaires add column type int not null default 1, add KEY type (type)');
$try("UPDATE `variables` SET liste='0:notices\r\n1:abonnés\r\n2:prêts\r\n3:reservations\r\n4:paniers\r\n5:autorités' WHERE clef='type_fichier'");
\ No newline at end of file
$try("UPDATE `variables` SET liste='0:notices\r\n1:abonnés\r\n2:prêts\r\n3:reservations\r\n4:paniers\r\n5:autorités' WHERE clef='type_fichier'");
<?php
$adapter = Zend_Db_Table_Abstract::getDefaultAdapter();
try {
$adapter->query(
'CREATE TABLE `journal` ('
. '`id` int(11) unsigned not null auto_increment,'
. '`type` varchar(255) not null,'
. '`created_at` datetime not null,'
. 'primary key (id),'
. 'key `type` (`type`),'
. 'key `created_at` (`created_at`)'
. ') engine=MyISAM default charset=utf8'
);
} catch(Exception $e) {}
try {
$adapter->query(
'CREATE TABLE `journal_detail` ('
. '`id` int(11) unsigned not null auto_increment,'
. '`journal_id` int(11) unsigned not null,'
. '`type` varchar(255) not null,'
. '`value` text not null,'
. 'primary key (id),'
. 'key `journal_id` (`journal_id`),'
. 'key `type` (`type`)'
. ') engine=MyISAM default charset=utf8'
);
} catch(Exception $e) {}
try {
$adapter->query(
'CREATE TABLE `federation_group_membership` ('
. '`id` int(11) unsigned not null auto_increment,'
. '`group_name` varchar(255) not null,'
. '`actor_id` varchar(255) not null,'
. '`accepted_at` datetime not null,'
. 'primary key (id),'
. 'key `accepted_at` (`accepted_at`),'
. 'key `group_name` (`group_name`),'
. 'key `actor_id` (`actor_id`)'
. ') 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) {}
......@@ -45,5 +45,5 @@
| icon slideshow by Javier Cabezas | CCBY | | editeur d'articles | | https://thenounproject.com/term/slideshow/6517/ |
| PHP-Parser | BSD-3-Clauses | - | validation de fichiers php (formulaires de recherche) | | https://github.com/nikic/PHP-Parser |
| Jquery Notification | MIT ? | | barre bleue de notification | oui (barre en bas) | n'existe plus |
| activitystreams | MIT | | Avis communautaires | | https://gitlab.com/patbator/activitystreams |
| phpseclib | MIT | | Avis communautaires | X ajout d'un autoload.php | https://github.com/phpseclib/phpseclib |
<?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
*/
require_once __DIR__ . '/../../activitystreams/autoload.php';
use \Patbator\ActivityStreams\Model\Base;
class Class_ActivityPub_PublicKey extends Base {
protected $_attribs = ['id' => null,
'type' => 'Key',
'owner' => null,
'publicKeyPem' => null];
}