diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 46a61e60a61..6989d57de99 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -1159,29 +1159,43 @@ function dol_buildpath($path, $type = 0, $returnemptyifnotfound = 0) } /** - * Create a clone of instance of object (new instance with same value for properties) - * With native = 0: Property that are reference are also new object (full isolation clone). This means $this->db of new object may not be valid. + * Create a clone of instance of object (new instance with same value for each properties) + * With native = 0: Property that are reference are different memory area in the new object (full isolation clone). This means $this->db of new object may not be valid. * With native = 1: Use PHP clone. Property that are reference are same pointer. This means $this->db of new object is still valid but point to same this->db than original object. + * With native = 2: Property that are reference are different memory area in the new object (full isolation clone). Only scalar and array values are cloned. This means $this->db of new object is not valid. * * @param object $object Object to clone - * @param int $native 0=Full isolation method, 1=Native PHP method, 2=Full isolation method+destroy non scalar or array properties (recommended) + * @param int $native 0=Full isolation method, 1=Native PHP method, 2=Full isolation method keeping only scalar and array properties (recommended) * @return object Clone object * @see https://php.net/manual/language.oop5.cloning.php */ function dol_clone($object, $native = 0) { if ($native == 0) { + // deprecated method, use the method with native = 2 instead $tmpsavdb = null; if (isset($object->db) && isset($object->db->db) && is_object($object->db->db) && get_class($object->db->db) == 'PgSql\Connection') { $tmpsavdb = $object->db; unset($object->db); // Such property can not be serialized with pgsl (when object->db->db = 'PgSql\Connection') } - $myclone = unserialize(serialize($object)); // serialize then unserialize is hack to be sure to have a new object for all fields + $myclone = unserialize(serialize($object)); // serialize then unserialize is a hack to be sure to have a new object for all fields if (!empty($tmpsavdb)) { $object->db = $tmpsavdb; } + } elseif ($native == 2) { + // recommended method to have a full isolated cloned object + $myclone = new stdClass(); + $tmparray = get_object_vars($object); // return only public properties + + if (is_array($tmparray)) { + foreach ($tmparray as $propertykey => $propertyval) { + if (is_scalar($propertyval) || is_array($propertyval)) { + $myclone->$propertykey = $propertyval; + } + } + } } else { $myclone = clone $object; // PHP clone is a shallow copy only, not a real clone, so properties of references will keep the reference (refering to the same target/variable) } diff --git a/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php b/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php index 94b4b861e86..d53b74bcd2e 100644 --- a/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php +++ b/htdocs/core/triggers/interface_95_modWebhook_WebhookTriggers.class.php @@ -103,7 +103,17 @@ class InterfaceWebhookTriggers extends DolibarrTriggers foreach ($target_url as $key => $tmpobject) { $actionarray = explode(",", $tmpobject->trigger_codes); if (is_array($actionarray) && in_array($action, $actionarray)) { - $jsonstr = '{"triggercode":'.json_encode($action).',"object":'.json_encode($object).'}'; + // Build the answer object + $resobject = new stdClass(); + $resobject->triggercode = $action; + $resobject->object = dol_clone($object, 2); + + unset($resobject->object['fields']); + unset($resobject->object['error']); + unset($resobject->object['errors']); + + $jsonstr = json_encode($resobject); + $response = getURLContent($tmpobject->url, 'POST', $jsonstr, 1, array(), array('http', 'https'), 0, -1); if (empty($response['curl_error_no']) && $response['http_code'] >= 200 && $response['http_code'] < 300) { $nbPosts ++; diff --git a/htdocs/webhook/class/target.class.php b/htdocs/webhook/class/target.class.php index 30d541f33af..ba33f2b8995 100644 --- a/htdocs/webhook/class/target.class.php +++ b/htdocs/webhook/class/target.class.php @@ -105,6 +105,8 @@ class Target extends CommonObject 'rowid' => array('type'=>'integer', 'label'=>'TechnicalID', 'enabled'=>'1', 'position'=>1, 'notnull'=>1, 'visible'=>0, 'noteditable'=>'1', 'index'=>1, 'css'=>'left', 'comment'=>"Id"), 'ref' => array('type'=>'varchar(128)', 'label'=>'Ref', 'enabled'=>'1', 'position'=>20, 'notnull'=>1, 'visible'=>4, 'noteditable'=>'1', 'index'=>1, 'searchall'=>1, 'validate'=>'1', 'comment'=>"Reference of object"), 'label' => array('type'=>'varchar(255)', 'label'=>'Label', 'enabled'=>'1', 'position'=>30, 'notnull'=>0, 'visible'=>1, 'searchall'=>1, 'css'=>'minwidth300', 'cssview'=>'wordbreak', 'help'=>"Help text", 'showoncombobox'=>'2', 'validate'=>'1',), + 'url' => array('type'=>'varchar(255)', 'label'=>'Url', 'enabled'=>'1', 'position'=>50, 'notnull'=>1, 'visible'=>1,), + 'trigger_codes' => array('type'=>'text', 'label'=>'TriggerCodes', 'enabled'=>'1', 'position'=>55, 'notnull'=>1, 'visible'=>1, 'help'=>"TriggerCodeInfo",), 'description' => array('type'=>'text', 'label'=>'Description', 'enabled'=>'1', 'position'=>60, 'notnull'=>0, 'visible'=>3, 'validate'=>'1',), 'note_public' => array('type'=>'html', 'label'=>'NotePublic', 'enabled'=>'1', 'position'=>61, 'notnull'=>0, 'visible'=>0, 'cssview'=>'wordbreak', 'validate'=>'1',), 'note_private' => array('type'=>'html', 'label'=>'NotePrivate', 'enabled'=>'1', 'position'=>62, 'notnull'=>0, 'visible'=>0, 'cssview'=>'wordbreak', 'validate'=>'1',), @@ -113,9 +115,7 @@ class Target extends CommonObject 'fk_user_creat' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserAuthor', 'enabled'=>'1', 'position'=>510, 'notnull'=>1, 'visible'=>-2, 'foreignkey'=>'user.rowid',), 'fk_user_modif' => array('type'=>'integer:User:user/class/user.class.php', 'label'=>'UserModif', 'enabled'=>'1', 'position'=>511, 'notnull'=>-1, 'visible'=>-2,), 'import_key' => array('type'=>'varchar(14)', 'label'=>'ImportId', 'enabled'=>'1', 'position'=>1000, 'notnull'=>-1, 'visible'=>-2,), - 'status' => array('type'=>'integer', 'label'=>'Status', 'enabled'=>'1', 'position'=>2000, 'notnull'=>1, 'visible'=>3, 'index'=>1, 'arrayofkeyval'=>array('0'=>'Brouillon', '1'=>'Validé', '9'=>'Annulé'), 'validate'=>'1',), - 'url' => array('type'=>'varchar(255)', 'label'=>'Url', 'enabled'=>'1', 'position'=>50, 'notnull'=>1, 'visible'=>1,), - 'trigger_codes' => array('type'=>'text', 'label'=>'TriggerCodes', 'enabled'=>'1', 'position'=>50, 'notnull'=>1, 'visible'=>1, 'help'=>"TriggerCodeInfo",), + 'status' => array('type'=>'integer', 'label'=>'Status', 'enabled'=>'1', 'position'=>2000, 'notnull'=>1, 'visible'=>1, 'index'=>1, 'arrayofkeyval'=>array('0'=>'Disabled', '1'=>'Enabled'), 'validate'=>'1',), ); public $rowid; public $ref; diff --git a/htdocs/webhook/lib/webhook_target.lib.php b/htdocs/webhook/lib/webhook_target.lib.php index 894e3134c0c..da342da0065 100644 --- a/htdocs/webhook/lib/webhook_target.lib.php +++ b/htdocs/webhook/lib/webhook_target.lib.php @@ -38,49 +38,6 @@ function targetPrepareHead($object) $head[$h][2] = 'card'; $h++; - if (isset($object->fields['note_public']) || isset($object->fields['note_private'])) { - $nbNote = 0; - if (!empty($object->note_private)) { - $nbNote++; - } - if (!empty($object->note_public)) { - $nbNote++; - } - $head[$h][0] = dol_buildpath('/webhook/target_note.php', 1).'?id='.$object->id; - $head[$h][1] = $langs->trans('Notes'); - if ($nbNote > 0) { - $head[$h][1] .= (empty($conf->global->MAIN_OPTIMIZEFORTEXTBROWSER) ? ''.$nbNote.'' : ''); - } - $head[$h][2] = 'note'; - $h++; - } - - /*require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; - require_once DOL_DOCUMENT_ROOT.'/core/class/link.class.php'; - $upload_dir = $conf->webhook->dir_output."/target/".dol_sanitizeFileName($object->ref); - $nbFiles = count(dol_dir_list($upload_dir, 'files', 0, '', '(\.meta|_preview.*\.png)$')); - $nbLinks = Link::count($db, $object->element, $object->id); - $head[$h][0] = dol_buildpath("/webhook/target_document.php", 1).'?id='.$object->id; - $head[$h][1] = $langs->trans('Documents'); - if (($nbFiles + $nbLinks) > 0) { - $head[$h][1] .= ''.($nbFiles + $nbLinks).''; - } - $head[$h][2] = 'document'; - $h++;*/ - - /*$head[$h][0] = dol_buildpath("/webhook/target_agenda.php", 1).'?id='.$object->id; - $head[$h][1] = $langs->trans("Events"); - $head[$h][2] = 'agenda'; - $h++;*/ - - // Show more tabs from modules - // Entries must be declared in modules descriptor with line - //$this->tabs = array( - // 'entity:+tabname:Title:@webhook:/webhook/mypage.php?id=__ID__' - //); // to add new tab - //$this->tabs = array( - // 'entity:-tabname:Title:@webhook:/webhook/mypage.php?id=__ID__' - //); // to remove a tab complete_head_from_modules($conf, $langs, $object, $head, $h, 'target@webhook'); complete_head_from_modules($conf, $langs, $object, $head, $h, 'target@webhook', 'remove'); diff --git a/htdocs/webhook/target_card.php b/htdocs/webhook/target_card.php index 35e18a9cdbc..83f2008703f 100644 --- a/htdocs/webhook/target_card.php +++ b/htdocs/webhook/target_card.php @@ -160,8 +160,6 @@ if (empty($reshook)) { /* * View - * - * Put here all code to build page */ $form = new Form($db); @@ -459,25 +457,20 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea if (empty($reshook)) { // Send - if (empty($user->socid)) { + /*if (empty($user->socid)) { print dolGetButtonAction('', $langs->trans('SendMail'), 'default', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=presend&mode=init&token='.newToken().'#formmailbeforetitle'); - } - - // Back to draft - if ($object->status == $object::STATUS_VALIDATED) { - print dolGetButtonAction('', $langs->trans('SetToDraft'), 'default', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_setdraft&confirm=yes&token='.newToken(), '', $permissiontoadd); - } + }*/ print dolGetButtonAction('', $langs->trans('Modify'), 'default', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=edit&token='.newToken(), '', $permissiontoadd); - // Validate + // Disable + if ($object->status == $object::STATUS_VALIDATED) { + print dolGetButtonAction('', $langs->trans('Disable'), 'default', $_SERVER["PHP_SELF"].'?id='.$object->id.'&action=confirm_setdraft&confirm=yes&token='.newToken(), '', $permissiontoadd); + } + + // Enable if ($object->status == $object::STATUS_DRAFT) { - if (empty($object->table_element_line) || (is_array($object->lines) && count($object->lines) > 0)) { - print dolGetButtonAction('', $langs->trans('Validate'), 'default', $_SERVER['PHP_SELF'].'?id='.$object->id.'&action=confirm_validate&confirm=yes&token='.newToken(), '', $permissiontoadd); - } else { - $langs->load("errors"); - print dolGetButtonAction($langs->trans("ErrorAddAtLeastOneLineFirst"), $langs->trans("Validate"), 'default', '#', '', 0); - } + print dolGetButtonAction('', $langs->trans('Enable'), 'default', $_SERVER['PHP_SELF'].'?id='.$object->id.'&action=confirm_validate&confirm=yes&token='.newToken(), '', $permissiontoadd); } // Clone @@ -505,61 +498,6 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea } print ''."\n"; } - - - // Select mail models is same action as presend - if (GETPOST('modelselected')) { - $action = 'presend'; - } - - if ($action != 'presend') { - print '
'; - print ''; // ancre - - $includedocgeneration = 0; - - // Documents - if ($includedocgeneration) { - $objref = dol_sanitizeFileName($object->ref); - $relativepath = $objref.'/'.$objref.'.pdf'; - $filedir = $conf->webhook->dir_output.'/'.$object->element.'/'.$objref; - $urlsource = $_SERVER["PHP_SELF"]."?id=".$object->id; - $genallowed = $permissiontoread; // If you can read, you can build the PDF to read content - $delallowed = $permissiontoadd; // If you can create/edit, you can remove a file on card - print $formfile->showdocuments('webhook:Target', $object->element.'/'.$objref, $filedir, $urlsource, $genallowed, $delallowed, $object->model_pdf, 1, 0, 0, 28, 0, '', '', '', $langs->defaultlang); - } - - // Show links to link elements - $linktoelem = $form->showLinkToObjectBlock($object, null, array('target')); - $somethingshown = $form->showLinkedObjectBlock($object, $linktoelem); - - - print '
'; - - $MAXEVENT = 10; - - $morehtmlcenter = dolGetButtonTitle($langs->trans('SeeAll'), '', 'fa fa-list-alt imgforviewmode', dol_buildpath('/webhook/target_agenda.php', 1).'?id='.$object->id); - - // List of actions on element - include_once DOL_DOCUMENT_ROOT.'/core/class/html.formactions.class.php'; - $formactions = new FormActions($db); - $somethingshown = $formactions->showactions($object, $object->element.'@'.$object->module, (is_object($object->thirdparty) ? $object->thirdparty->id : 0), 1, '', $MAXEVENT, '', $morehtmlcenter); - - print '
'; - } - - //Select mail models is same action as presend - if (GETPOST('modelselected')) { - $action = 'presend'; - } - - // Presend form - $modelmail = 'target'; - $defaulttopic = 'InformationMessage'; - $diroutput = $conf->webhook->dir_output; - $trackid = 'target'.$object->id; - - include DOL_DOCUMENT_ROOT.'/core/tpl/card_presend.tpl.php'; } // End of page diff --git a/htdocs/webhook/target_list.php b/htdocs/webhook/target_list.php index 5d81bec967f..1db73639103 100644 --- a/htdocs/webhook/target_list.php +++ b/htdocs/webhook/target_list.php @@ -48,10 +48,12 @@ $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'ta $backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page $optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print') $mode = GETPOST('mode', 'aZ'); +if (empty($mode)) { + $mode = 'modulesetup'; +} $id = GETPOST('id', 'int'); - // Load variable for pagination $limit = GETPOST('limit', 'int') ? GETPOST('limit', 'int') : $conf->liste_limit; $sortfield = GETPOST('sortfield', 'aZ09comma'); diff --git a/htdocs/webhook/target_note.php b/htdocs/webhook/target_note.php deleted file mode 100644 index e09ace533a9..00000000000 --- a/htdocs/webhook/target_note.php +++ /dev/null @@ -1,174 +0,0 @@ - - * Copyright (C) 2022 Frédéric France - * - * 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/webhook/target_note.php - * \ingroup webhook - * \brief Tab for notes on Target - */ - -// Load Dolibarr environment -require '../main.inc.php'; -require_once DOL_DOCUMENT_ROOT.'/webhook/class/target.class.php'; -require_once DOL_DOCUMENT_ROOT.'/webhook/lib/webhook_target.lib.php'; - -// Load translation files required by the page -$langs->loadLangs(array('companies')); - -// Get parameters -$action = GETPOST('action', 'aZ09'); -$cancel = GETPOST('cancel', 'aZ09'); -$backtopage = GETPOST('backtopage', 'alpha'); - -$id = GETPOST('id', 'int'); -$ref = GETPOST('ref', 'alpha'); - -// Initialize technical objects -$object = new Target($db); -$extrafields = new ExtraFields($db); -$diroutputmassaction = $conf->webhook->dir_output.'/temp/massgeneration/'.$user->id; -$hookmanager->initHooks(array('targetnote', 'globalcard')); // Note that conf->hooks_modules contains array - -// Fetch optionals attributes and labels -$extrafields->fetch_name_optionals_label($object->table_element); - -// Load object -include DOL_DOCUMENT_ROOT.'/core/actions_fetchobject.inc.php'; // Must be include, not include_once // Must be include, not include_once. Include fetch and fetch_thirdparty but not fetch_optionals -if ($id > 0 || !empty($ref)) { - $upload_dir = $conf->webhook->multidir_output[!empty($object->entity) ? $object->entity : $conf->entity]."/".$object->id; -} - -// Permissions -// There is several ways to check permission. -// Set $enablepermissioncheck to 1 to enable a minimum low level of checks -$enablepermissioncheck = 0; -if ($enablepermissioncheck) { - $permissiontoread = $user->rights->webhook->target->read; - $permissiontoadd = $user->rights->webhook->target->write; - $permissionnote = $user->rights->webhook->target->write; // Used by the include of actions_setnotes.inc.php -} else { - $permissiontoread = 1; - $permissiontoadd = 1; - $permissionnote = 1; -} - -// Security check (enable the most restrictive one) -//if ($user->socid > 0) accessforbidden(); -//if ($user->socid > 0) $socid = $user->socid; -//$isdraft = (($object->status == $object::STATUS_DRAFT) ? 1 : 0); -//restrictedArea($user, $object->element, $object->id, $object->table_element, '', 'fk_soc', 'rowid', $isdraft); -if (!isModEnabled('webhook') || !$permissiontoread) { - accessforbidden(); -} - - -/* - * Actions - */ - -$parameters = array(); -$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks -if ($reshook < 0) { - setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); -} -if (empty($reshook)) { - include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be include, not include_once -} - - -/* - * View - */ - -$form = new Form($db); - -//$help_url='EN:Webhooks|FR:Webhooks_FR|ES:Webhooks_ES'; -$help_url = ''; -$title = $langs->trans('Target').' - '.$langs->trans("Notes"); -llxHeader('', $title, $help_url); - -if ($id > 0 || !empty($ref)) { - $object->fetch_thirdparty(); - - $head = targetPrepareHead($object); - - print dol_get_fiche_head($head, 'note', $langs->trans("Target"), -1, $object->picto); - - // Object card - // ------------------------------------------------------------ - $linkback = ''.$langs->trans("BackToList").''; - - $morehtmlref = '
'; - /* - // Ref customer - $morehtmlref.=$form->editfieldkey("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', 0, 1); - $morehtmlref.=$form->editfieldval("RefCustomer", 'ref_client', $object->ref_client, $object, 0, 'string', '', null, null, '', 1); - // Thirdparty - $morehtmlref.='
'.$langs->trans('ThirdParty') . ' : ' . (is_object($object->thirdparty) ? $object->thirdparty->getNomUrl(1) : ''); - // Project - if (isModEnabled('projet')) - { - $langs->load("projects"); - $morehtmlref.='
'.$langs->trans('Project') . ' '; - if ($permissiontoadd) - { - if ($action != 'classify') - //$morehtmlref.='' . img_edit($langs->transnoentitiesnoconv('SetProject')) . ' : '; - $morehtmlref.=' : '; - if ($action == 'classify') { - //$morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'projectid', 0, 0, 1, 1); - $morehtmlref.='
'; - $morehtmlref.=''; - $morehtmlref.=''; - $morehtmlref.=$formproject->select_projects($object->socid, $object->fk_project, 'projectid', $maxlength, 0, 1, 0, 1, 0, 0, '', 1); - $morehtmlref.=''; - $morehtmlref.='
'; - } else { - $morehtmlref.=$form->form_project($_SERVER['PHP_SELF'] . '?id=' . $object->id, $object->socid, $object->fk_project, 'none', 0, 0, 0, 1); - } - } else { - if (!empty($object->fk_project)) { - $proj = new Project($db); - $proj->fetch($object->fk_project); - $morehtmlref .= ': '.$proj->getNomUrl(); - } else { - $morehtmlref .= ''; - } - } - }*/ - $morehtmlref .= '
'; - - - dol_banner_tab($object, 'ref', $linkback, 1, 'ref', 'ref', $morehtmlref); - - - print '
'; - print '
'; - - - $cssclass = "titlefield"; - include DOL_DOCUMENT_ROOT.'/core/tpl/notes.tpl.php'; - - print '
'; - - print dol_get_fiche_end(); -} - -// End of page -llxFooter(); -$db->close(); diff --git a/test/phpunit/FunctionsLibTest.php b/test/phpunit/FunctionsLibTest.php index 5357f7c8061..12c8683bb8f 100644 --- a/test/phpunit/FunctionsLibTest.php +++ b/test/phpunit/FunctionsLibTest.php @@ -188,6 +188,8 @@ class FunctionsLibTest extends PHPUnit\Framework\TestCase $this->assertEquals($this->savdb->connected, 1, 'Savdb is connected'); $this->assertNotNull($newproduct1->db->db, 'newproduct1->db is not null'); + $newproductcloned2 = dol_clone($newproduct1, 2); + var_dump($newproductcloned2); //print __METHOD__." newproductcloned1->db must be null\n"; //$this->assertNull($newproductcloned1->db, 'newproductcloned1->db is null'); }