diff --git a/htdocs/blockedlog/admin/blockedlog.php b/htdocs/blockedlog/admin/blockedlog.php
new file mode 100644
index 00000000000..3c143b70b83
--- /dev/null
+++ b/htdocs/blockedlog/admin/blockedlog.php
@@ -0,0 +1,126 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file htdocs/blockedlog/admin/blockedlog.php
+ * \ingroup system
+ * \brief Page setup for blockedlog module
+ */
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+
+$langs->load("admin");
+$langs->load("other");
+$langs->load("blockedlog");
+
+if (! $user->admin) accessforbidden();
+
+$action = GETPOST('action','alpha');
+
+/*
+ * Actions
+ */
+if (preg_match('/set_(.*)/',$action,$reg))
+{
+ $code=$reg[1];
+ if (dolibarr_set_const($db, $code, GETPOST($code), 'chaine', 0, '', $conf->entity) > 0)
+ {
+ header("Location: ".$_SERVER["PHP_SELF"]);
+ exit;
+ }
+ else
+ {
+ dol_print_error($db);
+ }
+}
+
+if (preg_match('/del_(.*)/',$action,$reg))
+{
+ $code=$reg[1];
+ if (dolibarr_del_const($db, $code, 0) > 0)
+ {
+ Header("Location: ".$_SERVER["PHP_SELF"]);
+ exit;
+ }
+ else
+ {
+ dol_print_error($db);
+ }
+}
+
+
+/*
+ * View
+ */
+
+$block_static = new BlockedLog($db);
+
+$form=new Form($db);
+
+llxHeader('',$langs->trans("BlockedLogSetup"));
+
+$linkback=''.$langs->trans("BackToModuleList").'';
+print load_fiche_titre($langs->trans("ModuleSetup").' BlockedLog',$linkback);
+
+$head=blockedlogadmin_prepare_head();
+
+dol_fiche_head($head, 'blockedlog', '', -1);
+
+print $langs->trans("BlockedLogDesc")."
\n";
+
+print '
';
+
+print '
';
+print '';
+print '| '.$langs->trans("Key").' | ';
+print ''.$langs->trans("Value").' | ';
+print "
\n";
+
+print '';
+print '| ';
+print $langs->trans("EntityKey").' | ';
+
+print $block_static->getSignature();
+
+print ' |
';
+
+
+// Example with a yes / no select
+$var=!$var;
+print '';
+print '| '.$langs->trans("BlockedLogAuthorityUrl").img_info($langs->trans('BlockedLogAuthorityNeededToStoreYouFingerprintsInNonAlterableRemote')).' | ';
+print '';
+print '';
+print ' |
';
+
+
+print '
';
+
+dol_fiche_end();
+
+print '
';
+
+llxFooter();
+$db->close();
diff --git a/htdocs/blockedlog/admin/fingerprints.php b/htdocs/blockedlog/admin/fingerprints.php
new file mode 100644
index 00000000000..29cd2a3f73b
--- /dev/null
+++ b/htdocs/blockedlog/admin/fingerprints.php
@@ -0,0 +1,142 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file htdocs/blockedlog/admin/blockedlog.php
+ * \ingroup system
+ * \brief Page setup for blockedlog module
+ */
+
+require '../../main.inc.php';
+require_once DOL_DOCUMENT_ROOT.'/blockedlog/lib/blockedlog.lib.php';
+require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
+require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/authority.class.php';
+require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+
+$langs->load("admin");
+$langs->load("other");
+$langs->load("blockedlog");
+
+if (! $user->admin) accessforbidden();
+
+$action = GETPOST('action','alpha');
+
+if($action === 'downloadblockchain') {
+
+ $auth = new BlockedLogAuthority($db);
+
+ $bc = $auth->getLocalBlockChain();
+
+ header('Content-Type: application/octet-stream');
+ header("Content-Transfer-Encoding: Binary");
+ header("Content-disposition: attachment; filename=\"" .$auth->signature. ".certif\"");
+
+ echo $bc;
+
+ exit;
+}
+
+
+/*
+ * View
+ */
+
+$block_static = new BlockedLog($db);
+
+$blocks = $block_static->getLog('all', 0, GETPOST('all') ? 0 : 50);
+
+$form=new Form($db);
+
+llxHeader('',$langs->trans("BlockedLogSetup"));
+
+$linkback=''.$langs->trans("BackToModuleList").'';
+print load_fiche_titre($langs->trans("ModuleSetup").' BlockedLog',$linkback);
+
+$head=blockedlogadmin_prepare_head();
+
+dol_fiche_head($head, 'fingerprints', '', -1);
+
+print $langs->trans("FingerprintsDesc")."
\n";
+
+print '
';
+
+echo '';
+
+
+print '';
+print '';
+
+print '| '.$langs->trans('Date').' | ';
+print ''.$langs->trans('Ref').' | ';
+print ''.$langs->trans('Action').' | ';
+print ''.$langs->trans('Element').' | ';
+print ''.$langs->trans('Amount').' | ';
+print ''.$langs->trans('Author').' | ';
+print ''.$langs->trans('Fingerprint').' | ';
+print ' | ';
+
+print '
';
+
+foreach($blocks as &$block) {
+
+ print '';
+ print '| '.dol_print_date($block->tms,'dayhour').' | ';
+ print ''.$block->ref_object.' | ';
+ print ''.$langs->trans('log'.$block->action).' | ';
+ print ''.$block->getObject().' | ';
+ print ''.price($block->amounts).' | ';
+ print ''.$block->getUser().' | ';
+ print ''.$block->signature.' | ';
+ print '';
+
+ print $block->checkSignature() ? img_picto('OkCheckPaymentValidity', 'on') : img_picto($langs->trans('KoCheckPaymentValidity'), 'off');
+ print ' '.($block->certified ? img_picto($langs->trans('AddedByAuthority'), 'info') : img_picto($langs->trans('NotAddedByAuthorityYet'), 'info_black') );
+
+ print ' | ';
+ print '
';
+
+}
+
+print '
';
+
+?>
+
+
+
';
+
+llxFooter();
+$db->close();
diff --git a/htdocs/blockedlog/admin/index.html b/htdocs/blockedlog/admin/index.html
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/htdocs/blockedlog/ajax/authority.php b/htdocs/blockedlog/ajax/authority.php
new file mode 100644
index 00000000000..12584b2bace
--- /dev/null
+++ b/htdocs/blockedlog/ajax/authority.php
@@ -0,0 +1,45 @@
+fetch(1); //TODO conf user authority
+
+ $auth = new BlockedLogAuthority($db);
+
+ $signature = GETPOST('s');
+ $newblock = GETPOST('b');
+ $hash = GETPOST('h');
+
+ if($auth->fetch(0, $signature)<=0) {
+ $auth->signature = $signature;
+ $auth->create($user);
+ }
+
+
+ if(!empty($hash)) {
+
+ echo $auth->checkBlockchain($hash) ? 'hashisok' : 'hashisjunk';
+
+ }
+ elseif(!empty($newblock)){
+ if($auth->checkBlock($newblock)) {
+ $auth->addBlock($newblock);
+ $auth->update($user);
+
+ echo 'blockadded';
+ }
+ else{
+
+ echo 'blockalreadyadded';
+
+ }
+ }
+ else{
+ echo 'idontunderstandwhatihavetodo';
+ }
+
+
diff --git a/htdocs/blockedlog/ajax/check_signature.php b/htdocs/blockedlog/ajax/check_signature.php
new file mode 100644
index 00000000000..bb018508a31
--- /dev/null
+++ b/htdocs/blockedlog/ajax/check_signature.php
@@ -0,0 +1,30 @@
+global->BLOCKEDLOG_AUTHORITY_URL)) exit('BLOCKEDLOG_AUTHORITY_URL not set');
+
+ require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
+ require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/authority.class.php';
+
+ $auth=new BlockedLogAuthority($db);
+ $auth->syncSignatureWithAuthority();
+
+ $block_static = new BlockedLog($db);
+
+ $blocks = $block_static->getLog('just_certified', 0, 0, 1) ;
+
+ $auth->signature = $block_static->getSignature();
+
+ foreach($blocks as &$b) {
+ $auth->blockchain.=$b->signature;
+
+ }
+
+ $hash = $auth->getBlockchainHash();
+
+ $url = $conf->global->BLOCKEDLOG_AUTHORITY_URL.'/blockedlog/ajax/authority.php?s='.$auth->signature.'&h='.$hash;
+
+ $res = file_get_contents($url);
+ //echo $url;
+ echo $res;
\ No newline at end of file
diff --git a/htdocs/blockedlog/class/authority.class.php b/htdocs/blockedlog/class/authority.class.php
new file mode 100644
index 00000000000..b422fb130d7
--- /dev/null
+++ b/htdocs/blockedlog/class/authority.class.php
@@ -0,0 +1,333 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Class to manage certif authority
+ */
+class BlockedLogAuthority
+{
+
+ /**
+ * Id of the log
+ * @var int
+ */
+ public $id;
+
+ /**
+ * Unique fingerprint of the blockchain to store
+ * @var string
+ */
+ public $signature = '';
+
+ /**
+ * Entire fingerprints blockchain
+ * @var string
+ */
+ public $blockchain = '';
+
+ /**
+ * timestamp
+ * @var int
+ */
+ public $tms = 0;
+
+ /**
+ * Constructor
+ *
+ * @param DoliDB $db Database handler
+ */
+ public function __construct($db) {
+
+ $this->db = $db;
+
+ }
+
+ /**
+ * Get the blockchain
+ *
+ * @return string blockchain
+ */
+ public function getLocalBlockChain() {
+
+ $block_static = new BlockedLog($this->db);
+
+ $this->signature = $block_static->getSignature();
+
+ $blocks = $block_static->getLog('all', 0, 0, 1) ;
+
+ $this->blockchain = '';
+
+ foreach($blocks as &$b) {
+ $this->blockchain.=$b->signature;
+
+ }
+
+ return $this->blockchain;
+ }
+
+ /**
+ * Get hash of the block chain to check
+ *
+ * @return string hash md5 of blockchain
+ */
+ public function getBlockchainHash() {
+
+ return md5($this->signature.$this->blockchain);
+
+ }
+
+ /**
+ * Get hash of the block chain to check
+ *
+ * @param string $hash hash md5 of blockchain to test
+ * @return boolean
+ */
+ public function checkBlockchain($hash) {
+
+ return ($hash === $this->getBlockchainHash() );
+
+ }
+
+ /**
+ * Add a new block to the chain
+ *
+ * @param string $block new block to chain
+ */
+ public function addBlock($block) {
+
+ $this->blockchain.=$block;
+
+ }
+
+ /**
+ * hash already exist into chain ?
+ *
+ * @param string $block new block to chain
+ * @return boolean
+ */
+ public function checkBlock($block) {
+
+ if(strlen($block)!=64) return false;
+
+ $blocks = str_split($this->blockchain,64);
+
+ if(!in_array($block,$blocks)) {
+ return true;
+ }
+ else{
+ return false;
+ }
+ }
+
+
+ /**
+ * Get object from database
+ *
+ * @param int $id Id of object to load
+ * @param string $signature Signature of object to load
+ * @return int >0 if OK, <0 if KO, 0 if not found
+ */
+ public function fetch($id, $signature='') {
+
+ global $langs;
+
+ dol_syslog(get_class($this)."::fetch id=".$id, LOG_DEBUG);
+
+ if (empty($id) && empty($signature))
+ {
+ $this->error='BadParameter';
+ return -1;
+ }
+
+ $langs->load("blockedlog");
+
+ $sql = "SELECT b.rowid, b.signature, b.blockchain, b.tms";
+ $sql.= " FROM ".MAIN_DB_PREFIX."blockedlog_authority as b";
+
+ if ($id) $sql.= " WHERE b.rowid = ". $id;
+ else if($signature)$sql.= " WHERE b.signature = '". $this->db->escape( $signature ) ."'" ;
+
+ $resql=$this->db->query($sql);
+ if ($resql)
+ {
+ if ($this->db->num_rows($resql))
+ {
+ $obj = $this->db->fetch_object($resql);
+
+ $this->id = $obj->rowid;
+ $this->ref = $obj->rowid;
+
+ $this->signature = $obj->signature;
+ $this->blockchain = $obj->blockchain;
+
+ $this->tms = $this->db->jdate($obj->tms);
+
+ return 1;
+ }
+ else
+ {
+ $this->error=$langs->trans("RecordNotFound");
+ return 0;
+ }
+ }
+ else
+ {
+ $this->error=$this->db->error();
+ return -1;
+ }
+
+ }
+
+ /**
+ * Create authority in database.
+ *
+ * @param User $user Object user that create
+ * @return int <0 if KO, >0 if OK
+ */
+ public function create($user) {
+
+ global $conf,$langs,$hookmanager;
+
+ $langs->load('blockedlog');
+
+ $error=0;
+
+ dol_syslog(get_class($this).'::create', LOG_DEBUG);
+
+ $this->db->begin();
+
+ $sql = "INSERT INTO ".MAIN_DB_PREFIX."blockedlog_authority (";
+ $sql.= " signature,";
+ $sql.= " blockchain";
+ $sql.= ") VALUES (";
+ $sql.= "'".$this->db->escape($this->signature)."',";
+ $sql.= "'".$this->db->escape($this->blockchain)."'";
+ $sql.= ")";
+
+ $res = $this->db->query($sql);
+ if ($res)
+ {
+ $id = $this->db->last_insert_id(MAIN_DB_PREFIX."blockedlog_authority");
+
+ if ($id > 0)
+ {
+ $this->id = $id;
+
+ $this->db->commit();
+
+ return $this->id;
+ }
+ else
+ {
+ $this->db->rollback();
+ return -2;
+ }
+ }
+ else
+ {
+ $this->error=$this->db->error();
+ $this->db->rollback();
+ return -1;
+ }
+
+ }
+
+ /**
+ * Create authority in database.
+ *
+ * @param User $user Object user that create
+ * @return int <0 if KO, >0 if OK
+ */
+ public function update($user) {
+
+ global $conf,$langs,$hookmanager;
+
+ $langs->load('blockedlog');
+
+ $error=0;
+
+ dol_syslog(get_class($this).'::create', LOG_DEBUG);
+
+ $this->db->begin();
+
+ $sql = "UPDATE ".MAIN_DB_PREFIX."blockedlog_authority SET ";
+ $sql.= " blockchain='".$this->db->escape($this->blockchain)."'";
+ $sql.= " WHERE rowid=".$this->id;
+
+ $res = $this->db->query($sql);
+ if ($res)
+ {
+ $this->db->commit();
+
+ return 1;
+ }
+ else
+ {
+ $this->error=$this->db->error();
+ $this->db->rollback();
+ return -1;
+ }
+
+ }
+
+ /**
+ * For cron to sync to authority.
+ *
+ * @return int <0 if KO, >0 if OK
+ */
+ public function syncSignatureWithAuthority() {
+ global $conf, $langs;
+
+ //TODO create cron task on activation
+
+ if(empty($conf->global->BLOCKEDLOG_AUTHORITY_URL)) {
+ $this->error = $langs->trans('NoAuthorityURLDefined');
+ return -2;
+ }
+
+ require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
+
+ $block_static = new BlockedLog($this->db);
+
+ $blocks = $block_static->getLog('not_certified', 0, 0, 1);
+
+ $signature=$block_static->getSignature();
+
+ foreach($blocks as &$block) {
+
+ $url = $conf->global->BLOCKEDLOG_AUTHORITY_URL.'/blockedlog/ajax/authority.php?s='.$signature.'&b='.$block->signature;
+
+ $res = file_get_contents($url);
+ echo $block->signature.' '.$url. ' '.$res.'
';
+ if($res === 'blockalreadyadded' || $res === 'blockadded') {
+
+ $block->setCertified();
+
+ }
+ else {
+
+ $this->error = $langs->trans('ImpossibleToContactAuthority ',$url);
+ return -1;
+ }
+
+
+ }
+
+ return 1;
+ }
+
+}
\ No newline at end of file
diff --git a/htdocs/blockedlog/class/blockedlog.class.php b/htdocs/blockedlog/class/blockedlog.class.php
new file mode 100644
index 00000000000..1f93eb3cc26
--- /dev/null
+++ b/htdocs/blockedlog/class/blockedlog.class.php
@@ -0,0 +1,529 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * Class to manage Blocked Log
+ */
+
+class BlockedLog
+{
+
+ /**
+ * Id of the log
+ * @var int
+ */
+ public $id;
+
+ /**
+ * Unique fingerprint of the log
+ * @var string
+ */
+ public $signature = '';
+
+ /**
+ * Unique fingerprint of the line log content
+ * @var string
+ */
+ public $signature_line = '';
+
+ public $amounts = null;
+
+ /**
+ * trigger action
+ * @var string
+ */
+ public $action = '';
+
+ /**
+ * Object element
+ * @var string
+ */
+ public $element = '';
+
+ /**
+ * Object id
+ * @var int
+ */
+ public $fk_object = 0;
+
+ /**
+ * Log certified by remote authority or not
+ * @var boolean
+ */
+ public $certified = false;
+
+ /**
+ * Author
+ * @var int
+ */
+ public $fk_user = 0;
+
+ public $date_object = 0;
+
+ public $ref_object = '';
+
+ public $object_data = null;
+
+
+ /**
+ * Constructor
+ *
+ * @param DoliDB $db Database handler
+ */
+ public function __construct(DoliDB $db)
+ {
+ $this->db = $db;
+
+ }
+
+ /**
+ * try to retrieve logged object
+ */
+ public function getObject() {
+ global $langs;
+
+ if($this->element === 'facture') {
+ require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
+
+ $object = new Facture($this->db);
+ if($object->fetch($this->fk_object)>0) {
+ return $object->getNomUrl(1);
+ }
+ }
+ else if($this->element === 'payment') {
+ require_once DOL_DOCUMENT_ROOT.'/compta/paiement/class/paiement.class.php';
+
+ $object = new Paiement($this->db);
+ if($object->fetch($this->fk_object)>0) {
+ return $object->getNomUrl(1);
+ }
+ }
+
+ return $langs->trans('ImpossibleToReloadObject', $this->element, $this->fk_object);
+
+ }
+
+ /**
+ * try to retrieve user author
+ */
+ public function getUser() {
+ global $langs, $cachedUser;
+
+ if(empty($cachedUser))$cachedUser=array();
+
+ if(empty($cachedUser[$this->fk_user])) {
+ $u=new User($this->db);
+ if($u->fetch($this->fk_user)>0) {
+ $cachedUser[$this->fk_user] = $u;
+ }
+ }
+
+ if(!empty($cachedUser[$this->fk_user])) {
+ return $cachedUser[$this->fk_user]->getNomUrl(1);
+ }
+
+ return $langs->trans('ImpossibleToRetrieveUser', $this->fk_user);
+ }
+
+ /**
+ * populate log by object
+ *
+ * @param payment|facture $object object to store
+ */
+ public function setObjectData(&$object) {
+
+ if($object->element=='payment') {
+ $this->date_object = $object->datepaye;
+ }
+ else{
+ $this->date_object = $object->date;
+ }
+
+ $this->ref_object = $object->ref;
+ $this->element = $object->element;
+ $this->fk_object = $object->id;
+
+ $this->object_data=new stdClass();
+
+ if($this->element === 'facture') {
+ if(empty($object->thirdparty))$object->fetch_thirdparty();
+ $this->object_data->thirdparty = new stdClass();
+
+ foreach($object->thirdparty as $key=>$value) {
+ if(!is_object($value)) $this->object_data->thirdparty->{$key} = $value;
+ }
+
+ $this->object_data->total_ht = (double) $object->total_ht;
+ $this->object_data->total_tva = (double) $object->total_tva;
+ $this->object_data->total_ttc = (double) $object->total_ttc;
+ $this->object_data->total_localtax1= (double) $object->total_localtax1;
+ $this->object_data->total_localtax2= (double) $object->total_localtax2;
+ $this->object_data->note_public = (double) $object->note_public;
+ $this->object_data->note_private= (double) $object->note_private;
+
+ }
+ elseif($this->element==='payment'){
+
+ $this->object_data->amounts = $object->amounts;
+
+ }
+
+
+ }
+
+ /**
+ * Get object from database
+ *
+ * @param int $id Id of object to load
+ * @return int >0 if OK, <0 if KO, 0 if not found
+ */
+ public function fetch($id) {
+
+ global $langs;
+
+ dol_syslog(get_class($this)."::fetch id=".$id, LOG_DEBUG);
+
+ if (empty($id))
+ {
+ $this->error='BadParameter';
+ return -1;
+ }
+
+ $langs->load("blockedlog");
+
+ $sql = "SELECT b.rowid, b.signature, b.amounts, b.action, b.element, b.fk_object, b.certified, b.tms, b.fk_user, b.date_object, b.ref_object, b.object_data";
+ $sql.= " FROM ".MAIN_DB_PREFIX."blockedlog as b";
+ if ($id) $sql.= " WHERE b.rowid = ". $id;
+
+ $resql=$this->db->query($sql);
+ if ($resql)
+ {
+ if ($this->db->num_rows($resql))
+ {
+ $obj = $this->db->fetch_object($resql);
+
+ $this->id = $obj->rowid;
+ $this->ref = $obj->rowid;
+
+ $this->signature = $obj->signature;
+ $this->amounts = (double) $obj->amounts;
+ $this->action = $obj->action;
+ $this->element = $obj->element;
+
+ $this->fk_object = trim($obj->fk_object);
+ $this->date_object = $this->db->jdate($obj->date_object);
+ $this->ref_object = $obj->ref_object;
+
+ $this->certified = ($obj->certified == 1);
+
+ $this->fk_user = $obj->fk_user;
+
+ $this->tms = $this->db->jdate($obj->tms);
+
+ $this->object_data = unserialize($obj->object_data);
+
+ return 1;
+ }
+ else
+ {
+ $this->error=$langs->trans("RecordNotFound");
+ return 0;
+ }
+ }
+ else
+ {
+ $this->error=$this->db->error();
+ return -1;
+ }
+
+ }
+
+ /**
+ * Set block certified by authority
+ *
+ * @return boolean
+ */
+ public function setCertified() {
+
+ $res = $this->db->query("UPDATE ".MAIN_DB_PREFIX."blockedlog SET certified=1 WHERE rowid=".$this->id);
+ if($res===false) return false;
+
+ return true;
+
+
+ }
+
+ /**
+ * Create blocked log in database.
+ *
+ * @param User $user Object user that create
+ * @return int <0 if KO, >0 if OK
+ */
+ public function create($user) {
+
+ global $conf,$langs,$hookmanager;
+
+ $langs->load('blockedlog');
+
+ $error=0;
+
+ dol_syslog(get_class($this).'::create', LOG_DEBUG);
+
+ $this->getSignatureRecursive();
+
+
+ if (is_null($this->amounts))
+ {
+ $this->error=$langs->trans("BlockLogNeedAmountsValue");
+ dol_syslog($this->error, LOG_WARNING);
+ return -1;
+ }
+
+ if(empty($this->element)) {
+ $this->error=$langs->trans("BlockLogNeedElement");
+ dol_syslog($this->error, LOG_WARNING);
+ return -2;
+ }
+
+ if(empty($this->action)) {
+ $this->error=$langs->trans("BlockLogNeedAction");
+ dol_syslog($this->error, LOG_WARNING);
+ return -3;
+ }
+
+ $this->fk_user = $user->id;
+
+ $this->db->begin();
+
+ $sql = "INSERT INTO ".MAIN_DB_PREFIX."blockedlog (";
+ $sql.= "action,";
+ $sql.= " amounts,";
+ $sql.= " signature,";
+ $sql.= " signature_line,";
+ $sql.= " element,";
+ $sql.= " fk_object,";
+ $sql.= " date_object,";
+ $sql.= " ref_object,";
+ $sql.= " object_data,";
+ $sql.= " certified,";
+ $sql.= " fk_user,";
+ $sql.= " entity";
+ $sql.= ") VALUES (";
+ $sql.= "'".$this->db->escape($this->action)."',";
+ $sql.= "".$this->amounts.",";
+ $sql.= "'".$this->db->escape($this->signature)."',";
+ $sql.= "'".$this->db->escape($this->signature_line)."',";
+ $sql.= "'".$this->db->escape($this->element)."',";
+ $sql.= "".$this->fk_object.",";
+ $sql.= "'".$this->db->idate($this->date_object)."',";
+ $sql.= "'".$this->db->escape($this->ref_object)."',";
+ $sql.= "'".$this->db->escape(serialize($this->object_data))."',";
+ $sql.= "0,";
+ $sql.= "".$user->id.",";
+ $sql.= $conf->entity;
+ $sql.= ")";
+
+ $res = $this->db->query($sql);
+ if ($res)
+ {
+ $id = $this->db->last_insert_id(MAIN_DB_PREFIX."blockedlog");
+
+ if ($id > 0)
+ {
+ $this->id = $id;
+
+ $this->db->commit();
+
+ return $this->id;
+ }
+ else
+ {
+ $this->db->rollback();
+ return -2;
+ }
+ }
+ else
+ {
+ $this->error=$this->db->error();
+ $this->db->rollback();
+ return -1;
+ }
+
+ }
+
+ /**
+ * return crypted value.
+ *
+ * @param string $value string to crypt
+ * @return string crypted string
+ */
+ private function crypt($value) {
+
+ return hash('sha256',$value);
+
+ }
+
+ /**
+ * check if current signature still correct compare to the chain
+ *
+ * @return boolean
+ */
+ public function checkSignature() {
+
+ $signature_to_test = $this->signature;
+
+ $this->getSignatureRecursive();
+
+ return ($signature_to_test=== $this->signature);
+
+ }
+
+ /**
+ * set current signatures
+ */
+ private function getSignatureRecursive(){
+
+ $this->signature_line = $this->crypt( $this->action . $this->getSignature() . $this->amounts . print_r($this->object_data, true) );
+ /*if($this->signature=='d6320580a02c1ab67fcc0a6d49d453c7d96dda0148901736f7f55725bfe1b900' || $this->signature=='ea65d435ff12ca929936a406aa9d707d99fb334c127878d256b602a5541bbbc9') {
+ var_dump($this->signature_line,$this->action ,$this->getSignature() , $this->amounts , $this->object_data);
+ }*/
+ $this->signature = $this->signature_line;
+
+ $logs = $this->getLog('all', 0, 0, 1) ;
+ if($logs!==false) {
+ foreach($logs as &$b) {
+
+ if($this->id>0 && $b->id == $this->id) break; // on arrête sur un enregistrement précis pour recalculer une signature
+
+ $b->getCurrentValue(); // on récupère la valeur actuelle en base de l'élément enregistré
+
+ $this->signature = $this->crypt($this->signature. $this->action . $b->signature . $b->amounts);
+ }
+ }
+
+ }
+
+ /**
+ * return log object for a element.
+ *
+ * @param string $element element to search
+ * @param int $fk_object id of object to search
+ * @param int $limit max number of element, 0 for all
+ * @param string $order sort of query
+ * @return array array of object log
+ */
+ public function getLog($element, $fk_object, $limit = 0, $order = -1) {
+ global $conf,$cachedlogs ;
+
+ /* $cachedlogs allow fastest search */
+ if(empty($cachedlogs)) $cachedlogs=array();
+
+
+ if($element=='all') {
+
+ $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
+ WHERE entity=".$conf->entity;
+
+ }
+ else if($element=='not_certified') {
+ $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
+ WHERE entity=".$conf->entity." AND certified = 0";
+
+ }
+ else if($element=='just_certified') {
+ $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
+ WHERE entity=".$conf->entity." AND certified = 1";
+
+ }
+ else{
+ $sql="SELECT rowid FROM ".MAIN_DB_PREFIX."blockedlog
+ WHERE element='".$element."' AND fk_object=".(int) $fk_object;
+
+ }
+
+ $sql.=($order<0 ? ' ORDER BY rowid DESC ' : ' ORDER BY rowid ASC ');
+
+ if($limit > 0 )$sql.=' LIMIT '.$limit;
+
+ $res = $this->db->query($sql);
+
+ if($res) {
+
+ $results=array();
+
+ while($obj = $this->db->fetch_object($res)) {
+
+ if(!isset($cachedlogs[$obj->rowid])) {
+ $b=new BlockedLog($this->db);
+ $b->fetch($obj->rowid);
+
+ $cachedlogs[$obj->rowid] = $b;
+ }
+
+ $results[] = $cachedlogs[$obj->rowid];
+
+ }
+
+ return $results;
+ }
+ else{
+ return false;
+ }
+ }
+
+ /**
+ * set amounts of log from current element value in order to compare signature.
+ */
+ private function getCurrentValue() {
+
+ if($this->action === 'PAYMENT_CUSTOMER_CREATE'
+ || $this->action === 'PAYMENT_ADD_TO_BANK') {
+ $sql="SELECT amount FROM ".MAIN_DB_PREFIX."paiement WHERE rowid=".$this->fk_object;
+
+ $res = $this->db->query($sql);
+
+ if($res && $obj = $this->db->fetch_object($res)) {
+ $this->amounts = (double) $obj->amount;
+ }
+ }
+
+ }
+
+ /**
+ * return and set the entity signature included into line signature
+ *
+ * @return string current entity signature
+ */
+ public function getSignature() {
+ global $db,$conf,$mysoc;
+
+ if(empty($conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT)) { // creation of a unique fingerprint
+
+ require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
+
+ $fingerprint = $this->crypt(print_r($mysoc,true).time().rand(0,1000));
+
+ dolibarr_set_const($db, 'BLOCKEDLOG_ENTITY_FINGERPRINT', $fingerprint, 'chaine',0,'Numeric Unique Fingerprint', $conf->entity);
+
+ $conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT= $fingerprint;
+ }
+
+ return $conf->global->BLOCKEDLOG_ENTITY_FINGERPRINT;
+ }
+
+}
+
diff --git a/htdocs/blockedlog/lib/blockedlog.lib.php b/htdocs/blockedlog/lib/blockedlog.lib.php
new file mode 100644
index 00000000000..a7fca40d116
--- /dev/null
+++ b/htdocs/blockedlog/lib/blockedlog.lib.php
@@ -0,0 +1,57 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file htdocs/blockedlog/lib/blockedlog.lib.php
+ * \ingroup system
+ * \brief Library for common blockedlog functions
+ */
+
+/**
+ * Define head array for tabs of blockedlog tools setup pages
+ *
+ * @return Array of head
+ */
+function blockedlogadmin_prepare_head()
+{
+ global $langs, $conf;
+
+ $h = 0;
+ $head = array();
+
+ $head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/blockedlog.php";
+ $head[$h][1] = $langs->trans("BlockedLog");
+ $head[$h][2] = 'blockedlog';
+ $h++;
+
+ $head[$h][0] = DOL_URL_ROOT."/blockedlog/admin/fingerprints.php";
+ $head[$h][1] = $langs->trans("Fingerprints");
+ $head[$h][2] = 'fingerprints';
+ $h++;
+
+ $object=new stdClass();
+
+ // Show more tabs from modules
+ // Entries must be declared in modules descriptor with line
+ // $this->tabs = array('entity:+tabname:Title:@mymodule:/mymodule/mypage.php?id=__ID__'); to add new tab
+ // $this->tabs = array('entity:-tabname); to remove a tab
+ complete_head_from_modules($conf,$langs,$object,$head,$h,'blockedlog');
+
+ complete_head_from_modules($conf,$langs,$object,$head,$h,'blockedlog','remove');
+
+ return $head;
+}
diff --git a/htdocs/blockedlog/lib/index.html b/htdocs/blockedlog/lib/index.html
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/htdocs/compta/paiement/class/paiement.class.php b/htdocs/compta/paiement/class/paiement.class.php
index 03a7759e52c..c8d7f4d3759 100644
--- a/htdocs/compta/paiement/class/paiement.class.php
+++ b/htdocs/compta/paiement/class/paiement.class.php
@@ -196,7 +196,7 @@ class Paiement extends CommonObject
$this->db->begin();
- $ref = $this->getNextNumRef('');
+ $this->ref = $this->getNextNumRef('');
if ($way == 'dolibarr')
{
@@ -210,7 +210,7 @@ class Paiement extends CommonObject
}
$sql = "INSERT INTO ".MAIN_DB_PREFIX."paiement (entity, ref, datec, datep, amount, multicurrency_amount, fk_paiement, num_paiement, note, fk_user_creat)";
- $sql.= " VALUES (".$conf->entity.", '".$ref."', '". $this->db->idate($now)."', '".$this->db->idate($this->datepaye)."', '".$total."', '".$mtotal."', ".$this->paiementid.", '".$this->num_paiement."', '".$this->db->escape($this->note)."', ".$user->id.")";
+ $sql.= " VALUES (".$conf->entity.", '".$this->ref."', '". $this->db->idate($now)."', '".$this->db->idate($this->datepaye)."', '".$total."', '".$mtotal."', ".$this->paiementid.", '".$this->num_paiement."', '".$this->db->escape($this->note)."', ".$user->id.")";
dol_syslog(get_class($this)."::Create insert paiement", LOG_DEBUG);
$resql = $this->db->query($sql);
diff --git a/htdocs/core/modules/modBlockedLog.class.php b/htdocs/core/modules/modBlockedLog.class.php
index c3e2cb6345a..b149471cd20 100644
--- a/htdocs/core/modules/modBlockedLog.class.php
+++ b/htdocs/core/modules/modBlockedLog.class.php
@@ -60,7 +60,7 @@ class modBlockedLog extends DolibarrModules
// Config pages
//-------------
- $this->config_page_url = array();
+ $this->config_page_url = array('blockedlog.php@blockedlog');
// Dependancies
//-------------
diff --git a/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php
new file mode 100644
index 00000000000..5a52777cbb9
--- /dev/null
+++ b/htdocs/core/triggers/interface_50_modBlockedlog_ActionsBlockedLog.class.php
@@ -0,0 +1,94 @@
+
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+/**
+ * \file htdocs/core/triggers/interface_50_modAgenda_ActionsBlockedLog.class.php
+ * \ingroup system
+ * \brief Trigger file for blockedlog module
+ */
+
+require_once DOL_DOCUMENT_ROOT.'/core/triggers/dolibarrtriggers.class.php';
+require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
+
+/**
+ * Class of triggered functions for agenda module
+ */
+class InterfaceActionsBlockedLog extends DolibarrTriggers
+{
+ public $family = 'system';
+ public $description = "Triggers of this module add blocklog.";
+ public $version = self::VERSION_DOLIBARR;
+ public $picto = 'system';
+
+ /**
+ * Function called on Dolibarrr payment or invoice event.
+ *
+ * @param string $action Event action code
+ * @param Object $object Object
+ * @param User $user Object user
+ * @param Translate $langs Object langs
+ * @param conf $conf Object conf
+ * @return int <0 if KO, 0 if no triggered ran, >0 if OK
+ */
+ public function runTrigger($action, $object, User $user, Translate $langs, Conf $conf)
+ {
+ // Do not log events not enabled for this action
+ if (empty($conf->blockedlog->enabled)) {
+ return 0;
+ }
+
+ if($action==='BILL_VALIDATE' || $action === 'BILL_PAYED' || $action==='BILL_UNPAYED') {
+ $amounts= (double) $object->total_ttc;
+ }
+ else if($action === 'PAYMENT_CUSTOMER_CREATE' || $action === 'PAYMENT_ADD_TO_BANK') {
+ $amounts = 0;
+ if(!empty($object->amounts)) {
+ foreach($object->amounts as $amount) {
+ $amounts+= price2num($amount);
+ }
+ }
+
+
+ }
+ else if(strpos($action,'PAYMENT')!==false) {
+ $amounts= (double) $object->amount;
+ }
+ else {
+ return 0; // not implemented action log
+ }
+
+ $b=new BlockedLog($this->db);
+ $b->action = $action;
+ $b->amounts= $amounts;
+ $b->setObjectData($object);
+
+ $res = $b->create($user);
+
+ if($res<0) {
+ setEventMessage($b->error,'errors');
+
+ return -1;
+ }
+ else {
+
+ return 1;
+ }
+
+
+ }
+
+}
diff --git a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql
index 09d6c4de475..2fa0cf48728 100644
--- a/htdocs/install/mysql/migration/5.0.0-6.0.0.sql
+++ b/htdocs/install/mysql/migration/5.0.0-6.0.0.sql
@@ -397,3 +397,37 @@ ALTER TABLE llx_usergroup_rights DROP INDEX fk_usergroup;
ALTER TABLE llx_usergroup_rights ADD UNIQUE INDEX uk_usergroup_rights (entity, fk_usergroup, fk_id);
ALTER TABLE llx_usergroup_rights ADD CONSTRAINT fk_usergroup_rights_fk_usergroup FOREIGN KEY (fk_usergroup) REFERENCES llx_usergroup (rowid);
+CREATE TABLE llx_blockedlog
+(
+ rowid integer AUTO_INCREMENT PRIMARY KEY,
+ tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ action varchar(50),
+ amounts real NOT NULL,
+ signature varchar(100) NOT NULL,
+ signature_line varchar(100) NOT NULL,
+ element varchar(50),
+ fk_object integer,
+ ref_object varchar(100),
+ date_object datetime,
+ object_data text,
+ fk_user integer,
+ entity integer DEFAULT 1,
+ certified integer
+) ENGINE=innodb;
+
+ALTER TABLE llx_blockedlog ADD INDEX signature (signature);
+ALTER TABLE llx_blockedlog ADD INDEX fk_object_element (fk_object,element);
+ALTER TABLE llx_blockedlog ADD INDEX entity (entity);
+ALTER TABLE llx_blockedlog ADD INDEX fk_user (fk_user);
+ALTER TABLE llx_blockedlog ADD INDEX entity_action (entity,action);
+ALTER TABLE llx_blockedlog ADD INDEX entity_action_certified (entity,action,certified);
+
+CREATE TABLE llx_blockedlog_authority
+(
+ rowid integer AUTO_INCREMENT PRIMARY KEY,
+ blockchain longtext NOT NULL,
+ signature varchar(100) NOT NULL,
+ tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=innodb;
+
+ALTER TABLE llx_blockedlog_authority ADD INDEX signature (signature);
diff --git a/htdocs/install/mysql/tables/llx_blockedlog.key.sql b/htdocs/install/mysql/tables/llx_blockedlog.key.sql
new file mode 100644
index 00000000000..4312a056894
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_blockedlog.key.sql
@@ -0,0 +1,6 @@
+ALTER TABLE llx_blockedlog ADD INDEX signature (signature);
+ALTER TABLE llx_blockedlog ADD INDEX fk_object_element (fk_object,element);
+ALTER TABLE llx_blockedlog ADD INDEX entity (entity);
+ALTER TABLE llx_blockedlog ADD INDEX fk_user (fk_user);
+ALTER TABLE llx_blockedlog ADD INDEX entity_action (entity,action);
+ALTER TABLE llx_blockedlog ADD INDEX entity_action_certified (entity,action,certified);
diff --git a/htdocs/install/mysql/tables/llx_blockedlog.sql b/htdocs/install/mysql/tables/llx_blockedlog.sql
new file mode 100644
index 00000000000..afc0f0d4375
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_blockedlog.sql
@@ -0,0 +1,19 @@
+
+CREATE TABLE llx_blockedlog
+(
+ rowid integer AUTO_INCREMENT PRIMARY KEY,
+ tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+ action varchar(50),
+ amounts real NOT NULL,
+ signature varchar(100) NOT NULL,
+ signature_line varchar(100) NOT NULL,
+ element varchar(50),
+ fk_object integer,
+ ref_object varchar(100),
+ date_object datetime,
+ object_data text,
+ fk_user integer,
+ entity integer DEFAULT 1,
+ certified integer
+) ENGINE=innodb;
+
diff --git a/htdocs/install/mysql/tables/llx_blockedlog_authority.key.sql b/htdocs/install/mysql/tables/llx_blockedlog_authority.key.sql
new file mode 100644
index 00000000000..289b6dbde56
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_blockedlog_authority.key.sql
@@ -0,0 +1 @@
+ALTER TABLE llx_blockedlog_authority ADD INDEX signature (signature);
diff --git a/htdocs/install/mysql/tables/llx_blockedlog_authority.sql b/htdocs/install/mysql/tables/llx_blockedlog_authority.sql
new file mode 100644
index 00000000000..eb491f93af4
--- /dev/null
+++ b/htdocs/install/mysql/tables/llx_blockedlog_authority.sql
@@ -0,0 +1,7 @@
+CREATE TABLE llx_blockedlog_authority
+(
+ rowid integer AUTO_INCREMENT PRIMARY KEY,
+ blockchain longtext NOT NULL,
+ signature varchar(100) NOT NULL,
+ tms timestamp DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
+) ENGINE=innodb;