From 772337d1629f858310c2babd70fb7d18cb99edae Mon Sep 17 00:00:00 2001 From: Jon Bendtsen Date: Wed, 16 Apr 2025 14:49:39 +0200 Subject: [PATCH] redoing PR Api objectlinks #33738 (#33781) * redoing PR Api objectlinks #33738 * loading objectlinks api from main api folder * bringing $FIELDS up to develop branch standard * trying to tell phan the expected array types * phan return and input type specifications * trying to fix 'Method ObjectLink::_makeobject() should return int but return statement is missing.' * trying to return an ObjecTLink * get returns an ObjectLink object * making sure that function _makeobject always has an return * making sure deleteByValues always has a return statement * remove empty line * making sure getByValues always has a return value * trying to fix phan for _validate * no < around objectlink * trying to fix some phan and phan-stan errors * public variable target should be fk_target * making TRIGGER_PREFIX more like in core/class/commonobject.class.php * trying to save and use the notrigger value * typecast objectlink fk_source and fk_target to string in the hope that they are later made correctly to int * whitespace to trigger a build of my branch * back to being int - because that is what they are * trying to typecast * trying to help PHPStan to validate fk_source and fk_target * trying to use the same way as are used * splitting over multiple lines * Fix: update ObjectLink return type in API methods ### FIX: Update ObjectLink return type in API methods - Updated the return type of several methods from `ObjectLink` to `Object` to reflect a return type compatible with Luracast/Restler * fix: refactor field setting in ObjectLinks class # Fix: Refactor field setting in ObjectLinks class - Removed the static `$INTFIELDS` array and moved its logic to a new private method `_setObjectLinkField`. - Updated `create`, `getByValues`, and `deleteByValues` methods to use `_setObjectLinkField` for setting fields. - Cleaned up the code by removing redundant field validation and type casting logic. * noting that relation type can both be string and null * escaping strings before parsing them to the database * -> not global * trying to fix phpstan errors by flipping so the string part is first --------- Co-authored-by: Jon Bendtsen Co-authored-by: Laurent Destailleur Co-authored-by: MDW --- htdocs/api/class/api_objectlinks.class.php | 526 +++++++++++++++++++++ htdocs/core/class/objectlink.class.php | 340 +++++++++++++ htdocs/core/lib/functions2.lib.php | 2 +- 3 files changed, 867 insertions(+), 1 deletion(-) create mode 100644 htdocs/api/class/api_objectlinks.class.php create mode 100644 htdocs/core/class/objectlink.class.php diff --git a/htdocs/api/class/api_objectlinks.class.php b/htdocs/api/class/api_objectlinks.class.php new file mode 100644 index 00000000000..af800363222 --- /dev/null +++ b/htdocs/api/class/api_objectlinks.class.php @@ -0,0 +1,526 @@ + + * Copyright (C) 2025 MDW + * + * 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 . + */ + +use Luracast\Restler\RestException; + +require_once DOL_DOCUMENT_ROOT.'/api/class/api.class.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/functions.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/class/objectlink.class.php'; + + +/** + * API that gives shows links between objects in an Dolibarr instance. + * + * @access protected + * @class DolibarrApiAccess {@requires user,external} + */ +class ObjectLinks extends DolibarrApi +{ + /** + * @var string[] Mandatory fields, checked when create and update object + */ + public static $FIELDS = array( + 'fk_source', + 'sourcetype', + 'fk_target', + 'targettype' + ); + + /** + * @var ObjectLink {@type ObjectLink} + */ + public $objectlink; + + /** + * @var int notrigger is default 0, which means to trigger, else set notrigger: 1 + */ + private $notrigger; + + /** + * Constructor of the class + */ + public function __construct() + { + global $db; + $this->db = $db; + $this->objectlink = new ObjectLink($this->db); + } + + /** + * Get properties of a ObjectLink object + * + * Return an array with object link information + * + * @param int $id ID of objectlink + * @return Object Object with cleaned properties + * @phan-return ObjectLink + * @phpstan-return ObjectLink + * + * + * @url GET {id} + * + * @throws RestException 403 + * @throws RestException 404 + */ + public function getById($id) + { + return $this->_fetch($id); + } + + + + /** + * Set a field of $this->objectlink, with proper type + * + * @param string $field The field to set + * @param string|float $value The "unclean" value + * @return void No return value, but field is set in $this->objectlink + */ + private function _setObjectLinkField($field, $value) + { + + $clean_field = $this->_checkValForAPI($field, $value, $this->objectlink); + + /** + * Fields that are of integer type, used for casting during object creation and update + */ + $intFields = array( + 'fk_source', + 'fk_target' + ); + + if (in_array($field, $intFields)) { + $this->objectlink->$field = (int) $clean_field; + } else { + $this->objectlink->$field = (string) $clean_field; + } + } + + + /** + * Create object link + * + * Examples: Only set "notrigger": 1 because 0 is the default value. + * Linking subscriptions for when you sell membership as part of another sale + * {"fk_source":"1679","sourcetype":"propal","fk_target":"1233","targettype":"commande"} + * {"fk_source":"167","sourcetype":"facture","fk_target":"123","targettype":"subscription"} + * + * @param array $request_data Request data, see Example above for required parameters. Currently unused is relationtype. notrigger is default 0, which means to trigger, else set notrigger: 1 + * @phan-param ?array $request_data + * @phpstan-param ?array $request_data + * @return array + * @phan-return array> + * @phpstan-return array> + * + * @url POST + * + * @throws RestException 304 + * @throws RestException 403 + * @throws RestException 500 + */ + public function create($request_data = null) + { + // Check mandatory fields + $result = $this->_validate($request_data); + + foreach ($request_data as $field => $value) { + if ($field == 'notrigger') { + $this->notrigger = (int) $value; + } else { + $this->_setObjectLinkField($field, $value); + } + } + + // Permission check + $srctype = $this->objectlink->sourcetype; + if ($this->objectlink->sourcetype == 'subscription') { + $srctype = 'adherent'; + } + $tgttype = $this->objectlink->targettype; + if ($this->objectlink->targettype == 'subscription') { + $tgttype = 'adherent'; + } + if (!DolibarrApiAccess::$user->hasRight((string) $srctype, 'creer')) { + throw new RestException(403, 'denied access to create the objectlinks sourcetype='.$this->objectlink->sourcetype); + } + if (!DolibarrApiAccess::$user->hasRight((string) $tgttype, 'creer')) { + throw new RestException(403, 'denied access to create the objectlinks targettype='.$this->objectlink->targettype); + } + + $result = $this->objectlink->create(DolibarrApiAccess::$user, $this->objectlink->fk_source, $this->objectlink->sourcetype, $this->objectlink->fk_target, $this->objectlink->targettype, $this->objectlink->relationtype, $this->notrigger); + + if ($result < 0) { + throw new RestException(500, 'when create objectlink : '.$this->objectlink->error); + } + + if ($result == 0) { + throw new RestException(304, 'Object link already exists'); + } + + return array( + 'success' => array( + 'code' => 200, + 'message' => 'object link created' + ) + ); + } + + /** + * Delete an object link + * + * @param int $id object link ID + * @return array + * @phan-return array> + * @phpstan-return array> + * + * @url DELETE {id} + * + * @throws RestException 403 + * @throws RestException 404 + * @throws RestException 500 + */ + public function deleteById($id) + { + // Reverse permission check. First we find out which kind of objects are linked, and if the user has rights to that then we delete it. + $result = $this->objectlink->fetch($id); + if ($result) { + $srctype = $this->objectlink->sourcetype; + if ($this->objectlink->sourcetype == 'subscription') { + $srctype = 'adherent'; + } + $tgttype = $this->objectlink->targettype; + if ($this->objectlink->targettype == 'subscription') { + $tgttype = 'adherent'; + } + if (!DolibarrApiAccess::$user->hasRight(((string) $srctype), 'lire')) { + throw new RestException(403, 'denied access to the objectlinks sourcetype'); + } + if (!DolibarrApiAccess::$user->hasRight(((string) $tgttype), 'lire')) { + throw new RestException(403, 'denied access to the objectlinks targettype'); + } + } else { + throw new RestException(404, 'Object Link not found'); + } + + if (!$this->objectlink->delete(DolibarrApiAccess::$user)) { + throw new RestException(500, 'Error when delete objectlink : '.$this->objectlink->error); + } + + return array( + 'success' => array( + 'code' => 200, + 'message' => 'object link deleted' + ) + ); + } + + /** + * GET object link(s) By Values, not id + * + * @param int $fk_source source id of object we link from + * @param string $sourcetype type of the source object + * @param int $fk_target target id of object we link to + * @param string $targettype type of the target object + * @param string $relationtype type of the relation, usually null + * @return Object + * @phan-return ObjectLink + * @phpstan-return ObjectLink + * + * @url GET + * + * @throws RestException 403 + * @throws RestException 404 + * @throws RestException 500 + */ + public function getByValues($fk_source, $sourcetype, $fk_target, $targettype, $relationtype = null) + { + $request_data = array( + 'fk_source' => ((int) $fk_source), + 'sourcetype' => (string) $sourcetype, + 'fk_target' => ((int) $fk_target), + 'targettype' => (string) $targettype, + 'relationtype' => $relationtype, + ); + + // Check mandatory fields + $result = $this->_validate($request_data); + + foreach ($request_data as $field => $value) { + $this->_setObjectLinkField($field, $value); + } + + // Permission check + $srctype = $this->objectlink->sourcetype; + if ($this->objectlink->sourcetype == 'subscription') { + $srctype = 'adherent'; + } + $tgttype = $this->objectlink->targettype; + if ($this->objectlink->targettype == 'subscription') { + $tgttype = 'adherent'; + } + if (!DolibarrApiAccess::$user->hasRight((string) $srctype, 'creer')) { + throw new RestException(403, 'denied access to get the objectlinks sourcetype='.$this->objectlink->sourcetype); + } + if (!DolibarrApiAccess::$user->hasRight((string) $tgttype, 'creer')) { + throw new RestException(403, 'denied access to get the objectlinks targettype='.$this->objectlink->targettype); + } + + $findresult = $this->objectlink->fetchByValues($this->objectlink->fk_source, $this->objectlink->sourcetype, $this->objectlink->fk_target, $this->objectlink->targettype, $this->objectlink->relationtype); + + if ($findresult < 0) { + throw new RestException(500, 'Error when finding objectlink : '.$this->objectlink->error); + } elseif ($findresult > 0) { + return $this->_cleanObjectDatas($this->objectlink); + } else { + throw new RestException(404, 'Object Link not found'); + } + } + + + /** + * Delete object link By Values, not id + * + * @param int $fk_source source id of object we link from + * @param string $sourcetype type of the source object + * @param int $fk_target target id of object we link to + * @param string $targettype type of the target object + * @param string $relationtype type of the relation, usually null + * @param int $notrigger 1=Does not execute triggers, 0=execute triggers {@choice 0,1} + * @return array + * @phan-return array> + * @phpstan-return array> + * + * @url DELETE + * + * @throws RestException 403 + * @throws RestException 404 + * @throws RestException 500 + */ + public function deleteByValues($fk_source, $sourcetype, $fk_target, $targettype, $relationtype = null, $notrigger = 0) + { + $request_data = array( + 'fk_source' => ((int) $fk_source), + 'sourcetype' => (string) $sourcetype, + 'fk_target' => ((int) $fk_target), + 'targettype' => (string) $targettype, + 'relationtype' => $relationtype, + ); + + // Check mandatory fields + $result = $this->_validate($request_data); + + foreach ($request_data as $field => $value) { + $this->_setObjectLinkField($field, $value); + } + + // Permission check + $srctype = $this->objectlink->sourcetype; + if ($this->objectlink->sourcetype == 'subscription') { + $srctype = 'adherent'; + } + $tgttype = $this->objectlink->targettype; + if ($this->objectlink->targettype == 'subscription') { + $tgttype = 'adherent'; + } + if (!DolibarrApiAccess::$user->hasRight((string) $srctype, 'creer')) { + throw new RestException(403, 'denied access to delete the objectlinks sourcetype='.$this->objectlink->sourcetype); + } + if (!DolibarrApiAccess::$user->hasRight((string) $tgttype, 'creer')) { + throw new RestException(403, 'denied access to delete the objectlinks targettype='.$this->objectlink->targettype); + } + + $findresult = $this->objectlink->fetchByValues($this->objectlink->fk_source, $this->objectlink->sourcetype, $this->objectlink->fk_target, $this->objectlink->targettype, $this->objectlink->relationtype); + + if ($findresult < 0) { + throw new RestException(500, 'Error when finding objectlink : '.$this->objectlink->error); + } elseif ($findresult > 0) { + $result = $this->objectlink->delete(DolibarrApiAccess::$user, $notrigger); + + if ($result < 0) { + throw new RestException(500, 'Error when delete objectlink : '.$this->objectlink->error); + } + + return array( + 'success' => array( + 'code' => 200, + 'message' => 'object link deleted' + ) + ); + } else { + throw new RestException(404, 'Object Link not found'); + } + } + + /** + * Get properties of an object link + * + * Return an array with object links + * + * @param int $id ID of objectlink + * @return Object Object with cleaned properties + * @phan-return ObjectLink + * @phpstan-return ObjectLink + * + * @throws RestException 403 + * @throws RestException 404 + */ + private function _fetch($id) + { + $result = $this->objectlink->fetch($id); + if ($result) { + $srctype = $this->objectlink->sourcetype; + if ($this->objectlink->sourcetype == 'subscription') { + $srctype = 'adherent'; + } + $tgttype = $this->objectlink->targettype; + if ($this->objectlink->targettype == 'subscription') { + $tgttype = 'adherent'; + } + if (!DolibarrApiAccess::$user->hasRight(((string) $srctype), 'lire')) { + throw new RestException(403, 'denied access to the objectlinks sourcetype'); + } + if (!DolibarrApiAccess::$user->hasRight(((string) $tgttype), 'lire')) { + throw new RestException(403, 'denied access to the objectlinks targettype'); + } + } else { + throw new RestException(404, 'Object Link not found'); + } + + return $this->_cleanObjectDatas($this->objectlink); + } + + // phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore + /** + * Clean sensible object datas + * + * @param Object $object Object to clean + * @phan-param ObjectLink $object + * @phpstan-param ObjectLink $object + * + * @return Object Object with cleaned properties + * @phan-return ObjectLink + * @phpstan-return ObjectLink + */ + protected function _cleanObjectDatas($object) + { + // phpcs:enable + $object = parent::_cleanObjectDatas($object); + + unset($object->module); + unset($object->entity); + unset($object->import_key); + unset($object->array_languages); + unset($object->contacts_ids); + unset($object->linkedObjectsIds); + unset($object->canvas); + unset($object->fk_project); + unset($object->contact_id); + unset($object->user); + unset($object->origin_type); + unset($object->origin_id); + unset($object->ref); + unset($object->ref_ext); + unset($object->statut); + unset($object->status); + unset($object->country_id); + unset($object->country_code); + unset($object->state_id); + unset($object->region_id); + unset($object->barcode_type); + unset($object->barcode_type_coder); + unset($object->mode_reglement_id); + unset($object->cond_reglement_id); + unset($object->demand_reason_id); + unset($object->transport_mode_id); + unset($object->shipping_method_id); + unset($object->shipping_method); + unset($object->fk_multicurrency); + unset($object->multicurrency_code); + unset($object->multicurrency_tx); + unset($object->multicurrency_total_ht); + unset($object->multicurrency_total_tva); + unset($object->multicurrency_total_ttc); + unset($object->multicurrency_total_localtax1); + unset($object->multicurrency_total_localtax2); + unset($object->last_main_doc); + unset($object->fk_account); + unset($object->note_public); + unset($object->note_private); + unset($object->total_ht); + unset($object->total_tva); + unset($object->total_localtax1); + unset($object->total_localtax2); + unset($object->total_ttc); + unset($object->lines); + unset($object->actiontypecode); + unset($object->name); + unset($object->lastname); + unset($object->firstname); + unset($object->civility_id); + unset($object->date_creation); + unset($object->date_validation); + unset($object->date_modification); + unset($object->tms); + unset($object->date_cloture); + unset($object->user_author); + unset($object->user_creation); + unset($object->user_creation_id); + unset($object->user_valid); + unset($object->user_validation); + unset($object->user_validation_id); + unset($object->user_closing_id); + unset($object->user_modification); + unset($object->user_modification_id); + unset($object->fk_user_creat); + unset($object->fk_user_modif); + unset($object->totalpaid); + unset($object->product); + unset($object->cond_reglement_supplier_id); + unset($object->deposit_percent); + unset($object->retained_warranty_fk_cond_reglement); + unset($object->warehouse_id); + unset($object->target); + unset($object->array_options); + unset($object->extraparams); + unset($object->specimen); + + return $object; + } + + // source before modifications was api_orders.class.php + /** + * Validate fields before create or update object + * + * @param ?array $data Data to validate + * @return array Return array with validated mandatory fields and their value + * @phan-return array Return array with validated mandatory fields and their value + * + * @throws RestException 400 + */ + private function _validate($data) + { + $objectlink = array(); + foreach (ObjectLinks::$FIELDS as $field) { + if (!isset($data[$field])) { + throw new RestException(400, $field." field missing"); + } + $objectlink[$field] = $data[$field]; + } + return $objectlink; + } +} diff --git a/htdocs/core/class/objectlink.class.php b/htdocs/core/class/objectlink.class.php new file mode 100644 index 00000000000..2834b20bd0e --- /dev/null +++ b/htdocs/core/class/objectlink.class.php @@ -0,0 +1,340 @@ + + * + * 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/class/commonobject.class.php + * \ingroup core + * \brief File of parent class of all other business classes (invoices, contracts, proposals, orders, ...) + */ + +require_once DOL_DOCUMENT_ROOT.'/core/class/doldeprecationhandler.class.php'; + +/** + * Parent class of all other business classes (invoices, contracts, proposals, orders, ...) + * + * @phan-forbid-undeclared-magic-properties + */ +class ObjectLink extends CommonObject +{ + const TRIGGER_PREFIX = 'OBJECTLINK'; + /** + * @var string ID to identify managed object + */ + public $element = 'objectlink'; + + /** + * @var string Name of table without prefix where object is stored + */ + public $table_element = 'element_element'; + + /** + * @var int source id is a foreign key + */ + public $fk_source; + + /** + * @var string source type + */ + public $sourcetype; + + /** + * @var int target id is a foreign key + */ + public $fk_target; + + /** + * @var string source type + */ + public $targettype; + + /** + * @var null|string relation type, not sure if ever used, but it is in the database + */ + public $relationtype; + + /** + * Constructor of the class + * + * @param DoliDB $db Database handler + */ + public function __construct($db) + { + $this->db = $db; + } + + /** + * Get object link from database. + * + * @param int $rowid row Id of object link + * @return int >0 if OK, <0 if KO, 0 if not found + */ + public function fetch($rowid) + { + $sql = "SELECT rowid, fk_source, sourcetype, fk_target,"; + $sql .= " targettype, relationtype FROM"; + $sql .= " ".MAIN_DB_PREFIX.$this->table_element; + $sql .= " WHERE rowid = ".((int) $rowid); + + dol_syslog(get_class($this)."::fetch", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + $obj = $this->db->fetch_object($result); + if ($obj) { + $this->id = $obj->rowid; + //$this->entity = $obj->entity; + + $this->fk_source = (int) $obj->fk_source; + $this->sourcetype = (string) $obj->sourcetype; + $this->fk_target = (int) $obj->fk_target; + $this->targettype = (string) $obj->targettype; + $this->relationtype = $obj->relationtype; + + return 1; + } else { + $this->error = 'Object link with id '.((string) $rowid).' not found sql='.$sql; + return 0; + } + } else { + $this->error = $this->db->error(); + return -1; + } + } + + /** + * fetch object link By Values, not id + * + * @param int $fk_source source id of object we link from + * @param string $sourcetype type of the source object + * @param int $fk_target target id of object we link to + * @param string $targettype type of the target object + * @param string $relationtype type of the relation, usually null + * @return int Return integer <0 if KO, >0 if OK + */ + public function fetchByValues($fk_source, $sourcetype, $fk_target, $targettype, $relationtype = null) + { + $sql = "SELECT rowid, fk_source, sourcetype, fk_target,"; + $sql .= " targettype, relationtype FROM"; + $sql .= " ".MAIN_DB_PREFIX.$this->table_element; + $sql .= " WHERE fk_source=".((int) $fk_source); + $sql .= " AND sourcetype='".$this->db->escape($sourcetype)."'"; + $sql .= " AND fk_target=".((int) $fk_target); + $sql .= " AND targettype='".$this->db->escape($targettype)."'"; + if ($relationtype) { + $sql .= " AND relationtype='".$this->db->escape($relationtype)."'"; + } + + dol_syslog(get_class($this)."::fetch", LOG_DEBUG); + $result = $this->db->query($sql); + if ($result) { + $obj = $this->db->fetch_object($result); + if ($obj) { + $this->id = $obj->rowid; + //$this->entity = $obj->entity; + + $this->fk_source = (int) $obj->fk_source; + $this->sourcetype = (string) $obj->sourcetype; + $this->fk_target = (int) $obj->fk_target; + $this->targettype = (string) $obj->targettype; + $this->relationtype = $obj->relationtype; + + return 1; + } else { + $this->error = 'Object link not found sql='.$sql; + return 0; + } + } else { + $this->error = $this->db->error(); + return -1; + } + } + + /** + * Delete the object link + * + * @param User $user User object + * @param int $notrigger 1=Does not execute triggers, 0= execute triggers + * @return int Return integer <=0 if KO, >0 if OK + */ + public function delete($user, $notrigger = 0) + { + + global $conf, $langs; + require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; + + $error = 0; + + dol_syslog(get_class($this)."::delete ".$this->id, LOG_DEBUG); + + $this->db->begin(); + + if (!$notrigger) { + // Call trigger + $result = $this->call_trigger(self::TRIGGER_PREFIX.'_DELETE', $user); + if ($result < 0) { + $error++; + } + // End call triggers + } + + // Delete object link + if (!$error) { + $sql = "DELETE FROM ".MAIN_DB_PREFIX.$this->table_element." WHERE rowid = ".((int) $this->id); + $res = $this->db->query($sql); + if (!$res) { + $error++; + $this->error = $this->db->lasterror(); + $this->errors[] = $this->error; + dol_syslog(get_class($this)."::delete error ".$this->error, LOG_ERR); + } + } + + if (!$error) { + dol_syslog(get_class($this)."::delete ".$this->id." by ".$user->id, LOG_DEBUG); + $this->db->commit(); + return 1; + } else { + $this->db->rollback(); + return -1; + } + } + + /** + * Create object link + * + * @param User $user Object user that make creation + * @param int $fk_source source id of object we link from + * @param string $sourcetype type of the source object + * @param int $fk_target target id of object we link to + * @param string $targettype type of the target object + * @param string $relationtype type of the relation, usually null + * @param int $notrigger Disable all triggers + * @return int Return integer <0 if KO, >0 if OK + */ + public function create($user, $fk_source, $sourcetype, $fk_target, $targettype, $relationtype = null, $notrigger = 0) + { + global $conf, $langs; + $error = 0; + + $alreadyexists = $this->fetchByValues($fk_source, $sourcetype, $fk_target, $targettype, $relationtype); + if ($alreadyexists == 1) { + return 0; + } + + // create sourceobject and targetobject, make sure they exist with the respective numbers + $sourceobject = $this->_makeobject($fk_source, $sourcetype); + if ($sourceobject < 0 ) { + $this->error = "Error when looking for Object id=".$fk_source." of type=".$sourcetype; + return -2; + } + if ($sourceobject == 0 ) { + $this->error = "Object id ".$fk_source." of type ".$sourcetype." does not exist"; + return -1; + } + + $targetobject = $this->_makeobject($fk_target, $targettype); + if ($targetobject < 0 ) { + $this->error = "Error when looking for Object id=".$fk_target." of type=".$targettype; + return -2; + } + if ($targetobject == 0 ) { + $this->error = "Object id ".$fk_target." of type ".$targettype." does not exist"; + return -1; + } + + dol_syslog(get_class($this)."::create user=".$user->id); + + $this->db->begin(); + + if (!$notrigger) { + // Call trigger + $result = $this->call_trigger(self::TRIGGER_PREFIX.'_CREATE', $user); + if ($result < 0) { + $error++; + } + // End call triggers + } + + $sql = "INSERT INTO ".MAIN_DB_PREFIX."$this->table_element"; + if ($relationtype) { + $sql .= " (fk_source, sourcetype, fk_target, targettype, relationtype )"; + } else { + $sql .= " (fk_source, sourcetype, fk_target, targettype )"; + } + $sql .= " VALUES (".((int) $this->fk_source).", '".$this->db->escape($sourcetype)."', "; + $sql .= ((int) $this->fk_target).", '".$this->db->escape($targettype)."'"; + if ($relationtype) { + $sql .= ", '".$this->db->escape($relationtype)."'"; + } + $sql .= ")"; + + dol_syslog(get_class($this)."::create", LOG_DEBUG); + $resql = $this->db->query($sql); + if ($resql) { + $this->db->commit(); + return 1; + } else { + $this->error = $this->db->lasterror(); + $this->db->rollback(); + return -1; + } + } + /** + * Creates an object of the right kind and try to fetch it to make sure the id exists + * + * Return 1 if created, -1 if it does not exist + * + * @param int $objectid ID of the object + * @param string $objecttype ID of the object + * @return int 1 if created, -1 if it does not exist + * + */ + private function _makeobject($objectid, $objecttype) + { + if ($objecttype == 'adherent') { + require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php'; + $newobject = new Adherent($this->db); + $result = $newobject->fetch($objectid); + return $result; + } + if ($objecttype == 'commande') { + require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php'; + $newobject = new Commande($this->db); + $result = $newobject->fetch($objectid); + return $result; + } + if ($objecttype == 'facture') { + require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php'; + $newobject = new Facture($this->db); + $result = $newobject->fetch($objectid); + return $result; + } + if ($objecttype == 'propal') { + require_once DOL_DOCUMENT_ROOT.'/comm/propal/class/propal.class.php'; + $newobject = new Propal($this->db); + $result = $newobject->fetch($objectid); + return $result; + } + if ($objecttype == 'subscription') { + require_once DOL_DOCUMENT_ROOT.'/adherents/class/subscription.class.php'; + $newobject = new Subscription($this->db); + $result = $newobject->fetch($objectid); + return $result; + } + dol_syslog("objectlink->_makeobject called with unknown objecttype=".$objecttype, LOG_ERR); + return -2; + } +} diff --git a/htdocs/core/lib/functions2.lib.php b/htdocs/core/lib/functions2.lib.php index 7423f8d0a56..583f7e20439 100644 --- a/htdocs/core/lib/functions2.lib.php +++ b/htdocs/core/lib/functions2.lib.php @@ -2734,7 +2734,7 @@ function getModuleDirForApiClass($moduleobject) if ($moduleobject == 'contracts') { $moduledirforclass = 'contrat'; - } elseif (in_array($moduleobject, array('admin', 'login', 'setup', 'access', 'status', 'tools', 'documents'))) { + } elseif (in_array($moduleobject, array('admin', 'login', 'setup', 'access', 'status', 'tools', 'documents', 'objectlinks'))) { $moduledirforclass = 'api'; } elseif ($moduleobject == 'contact' || $moduleobject == 'contacts' || $moduleobject == 'customer' || $moduleobject == 'thirdparty' || $moduleobject == 'thirdparties') { $moduledirforclass = 'societe';