Merge branch 'develop' of git@github.com:Dolibarr/dolibarr.git into develop

This commit is contained in:
Laurent Destailleur 2023-08-16 18:08:21 +02:00
commit fb4e772e0a
9 changed files with 204 additions and 34 deletions

View file

@ -19,8 +19,8 @@ Following changes may create regressions for some external modules, but were nec
For uses:
---------
NEW: PHP 8.2 compatibility (test not yet completed).
NEW: Module Workstations Management upgraded to stable status.
NEW: PHP 8.2 compatibility (test not yet completed)
NEW: Module Workstations Management upgraded to stable status
NEW: Module Webhook upgraded to stable status
NEW: #23436 Group social networks fields
NEW: Accountancy - Add specific page to export accounting data rather than the journals page
@ -31,16 +31,15 @@ NEW: Accountancy - iSuiteExpert export model
NEW: Accountancy - Quadratus export with attachments in accountancy export
NEW: Accountancy - Can filter on a custom group of accounts. Perf or ledger list.
NEW: Accountancy - Can select the export format during export of journals
NEW: Accountancy - sort of column of custom group of account
NEW: Can upload a file with drag and drop on purchase invoice, vats, salaries and social contributions
NEW: Authentication: #22740 add OpenID Connect impl
NEW: Authentication: add experimental support for Google OAuth2 connexion
NEW: Authentication: can now edit service name for OAuth token
NEW: Add a status on supplier price ref (WIP to close a supplier ref)
NEW: add bookmarks in selectable landing pages for users
NEW: Add column ext_payment_site into societe_rib to allow multiple payment mode
NEW: add convertion of images to webp for a single image in website media editor
NEW: Add CRC for currency symbol before amount
NEW: add customer code to invoices listing
NEW: Add filter on nb of generation done in list of recurring invoices
NEW: Add filters and sort on product unit column
NEW: Add link to edit VAT list from error message of missing VAT
@ -48,10 +47,11 @@ NEW: add margins in paiement/card.php
NEW: Add mass action delete on VAT
NEW: Add possibility to choose format #21426
NEW: An external module can modify the quick search fields
NEW: Bank: Bank name no more mandatory on creation. Can be generated if empty.
NEW: Bank: Add fields zip, town, country for owner of a bank account
NEW: Bank: Bank name no more mandatory on creation. Can be generated if empty.
NEW: Bank: Add fields zip, town, country for owner of a bank account
NEW: batch referential objets
NEW: Can add the add now link on date into addfieldvalue()
NEW: Can add an array of several links in date selector
NEW: Can bin accounting line for a given month
NEW: Can edit inline the VAT number from supplier tab
NEW: Can go back to draft on shipment when stock change not on validate
@ -75,13 +75,11 @@ NEW: Email-Collector: add field reply-to in email collector as possible filter
NEW: Email-Collector: substitute date now in email collector
NEW: Email-Collector: operation type in email collector to load or create contact
NEW: Email-Collector: easier setup - can also use ! for negative search
NEW: Email-Templates: show module into list of email templates
NEW: Events: can add any contact on events if global MAIN_ACTIONCOM_CAN_ADD_ANY_CONTACT is set at 1
NEW: Events: list with color
NEW: Events: remove default percentage for event creation url
NEW: expend/collapse list of social networks
NEW: Filter on amount and qty on list of service's contracts
NEW: formconfirm can support field with format datetime
NEW: getCommonSubstitutionArray to have more substitute keys
NEW: GeoIP: Can test a geoip conversion from the geoip setup page
NEW: GUI: add a CSS editor into the admin GUI
NEW: GUI: add dropdown button actions (example on Create button on project)
@ -101,6 +99,7 @@ NEW: Import: map table to element for get entity in import
NEW: inc.php: handle parameters from argv
NEW: Installation: Auto activate some modules on install (Export/Import/Wysiwyg editor)
NEW: Invoice: show category of operations
NEW: Invoice: add customer code to invoices listing
NEW: Keep a link between user created from recruitment and application
NEW: Mass Actions: Better responsive for mass actions
NEW: Members: add numbering modules for members
@ -135,7 +134,7 @@ NEW: Print PDF: Option PDF_SHOW_EMAIL_AFTER_USER_CONTACT to show email after spe
NEW: product images on popup are cached
NEW: Products: Add origin info when create a product batch when created from a movement stock
NEW: Products: Add statistics by amount on statistics of products.
NEW: Products: Add SQL contraint on product_stock table to allow only exsting product and warehouse #23543
NEW: Products: Add SQL contraint on product_stock table to allow only existing product and warehouse #23543
NEW: Proposals: filter for Signed+Billed in proposals
NEW: Proposals: can modify margin rates in offers like VAT rates
NEW: Proposals: option filter for NoSalesRepresentativeAffected in proposals list
@ -144,23 +143,22 @@ NEW: Reception: can receive more than qty ordered on reception
NEW: referential objects of batch
NEW: remove keys whose table element is the same as element in map list
NEW: repair script skip views
NEW: search on time spent duration range
NEW: Security: Save date to invalidate other session into user table
NEW: Security: Invalidate all sessions of a user when password is modified.
NEW: search on time spent duration range
NEW: set payment default values when supplier order created from reception
NEW: Service Contracts: Filter on amount and qty on list of service's contracts
NEW: set today start time at beginning
NEW: Show counter of access of website in website list
NEW: Show main currency in company info user tooltip
NEW: Show module into list of emails templates
NEW: Show picto into the combobox of widgets
NEW: Show supplier invoice ref of direct debit transfer tab invoices
NEW: show supplier name in getNomUrl of supplier order
NEW: sort of column of custom group of account
NEW: Supplier Invoices: add ability of ODT support to supplier invoices
NEW: Social Networks: expend/collapse list of social networks
NEW: Stock limit for alert and desired optimal stock by product and warehouse import
NEW: Stock: Add warehouse create and modify triggers.
NEW: Stock: Can select several warehouses into the view stock at date in past
NEW: Stripe: add STRIPE_DEBUG, a way to log Stripe IPN
NEW: Supplier Invoices: add ability of ODT support to supplier invoices
NEW: Supplier Order: show supplier name in getNomUrl of supplier order
NEW: Supplier Order: set payment default values when supplier order created from reception
NEW: Supplier Price: Add a status on supplier price ref (WIP to close a supplier ref)
NEW: Support different bank account for several direct debit payments
NEW: Support multiselect in the warehouse selection combo box
NEW: Survey: Comment on survey is possible only after vote.
@ -180,8 +178,8 @@ NEW: Use by default the domain $dolibarr_main_url_root for SMTP HELO
NEW: Users: add a public virtual card page for each user
NEW: VAT can be modified during add of line
NEW: Website Module: Increment website counter on each page access in website module
NEW: write all fields and their properties in asciidoc format
NEW: Can add an array of several links in date selector
NEW: Website Module: Show counter of access of website in website list
NEW: Widgets: Show picto into the combobox of widgets
NEW: Widgets: Implement MAIN_ACTIVATE_FILECACHE on birthday widget
NEW: Widgets: Add widget "The next upcoming events"
NEW: Widgets: Add widget of open opportunities
@ -199,12 +197,14 @@ NEW: add helplist property to describe fields of objects
NEW: API: Support contact in post() document API
NEW: API: more APIs (update currency rate, upload of supplier documents, ...)
NEW: Hooks: printFieldListFrom in contact list
NEW: Hooks: add hook in loadLotStock() in html.formproduct.class.php file, add hook 'llxFooter', add hook online sign
NEW: Hooks: add hook in loadLotStock() in html.formproduct.class.php file
NEW: Hooks: add hook 'llxFooter'
NEW: Hooks: add hook online sign
NEW: Hooks: add sent info in the parameters provided to the hook sendMailAfter
NEW: Libraries: Update libs parsedownto 1.7.4, phpspreadsheet lib to v1.12, ESCPOS v3.0, jquery, Stripe.
NEW: ModuleBuilder: updating in modulbuilder on tab Menu when adding object
NEW: ModuleBuilder: add/edit permissions
NEW: ModuleBuilder: better generated documentation
NEW: add sent info in the parameters provided to the hook sendMailAfter
NEW: add setAsSelectUser into factory for generic setup page
NEW: add option keepspace into dol_string_nospecialchar()
NEW: dol_sort_array can sort on alphabetical order even if val is num
@ -212,6 +212,8 @@ NEW: dolExplodeIntoArray can accept regex
NEW: element time integration code + SQL
NEW: tables: llx_element_time to store time spent on several elements (mo, ticket...)
NEW: Provide the oldcopy value when calling setValueFrom() function with a trigger key
NEW: getCommonSubstitutionArray to have more substitute keys
NEW: write all fields and their properties in asciidoc format
WARNING:

View file

@ -1,7 +1,7 @@
<?php
/* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2006-2009 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2022 Frédéric France <frederic.france@netlogic.fr>
* Copyright (C) 2022-2023 Frédéric France <frederic.france@netlogic.fr>
*
* 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
@ -93,6 +93,15 @@ class mod_lot_free extends ModeleNumRefBatch
return $langs->trans("LeopardNumRefModelDesc");
}
/**
* Return an example of numbering
*
* @return string Example
*/
public function getExample()
{
return $this->getNextValue(null, null);
}
/**
* Return an example of result returned by getNextValue

View file

@ -89,6 +89,15 @@ class mod_sn_free extends ModeleNumRefBatch
return $langs->trans("LeopardNumRefModelDesc");
}
/**
* Return an example of numbering
*
* @return string Example
*/
public function getExample()
{
return $this->getNextValue(null, null);
}
/**
* Return an example of result returned by getNextValue
@ -99,7 +108,6 @@ class mod_sn_free extends ModeleNumRefBatch
*/
public function getNextValue($objsoc, $object)
{
global $langs;
return '';
}
}

View file

@ -344,6 +344,9 @@ class InterfaceWorkflowManager extends DolibarrTriggers
foreach ($order->linkedObjects as $type => $shipping_array) {
if ($type == 'shipping' && is_array($shipping_array) && count($shipping_array) > 0) {
foreach ($shipping_array as $shipping) {
if ($shipping->statut <= 0) {
continue;
}
if (is_array($shipping->lines) && count($shipping->lines) > 0) {
foreach ($shipping->lines as $shippingline) {
$qtyshipped[$shippingline->fk_product] += $shippingline->qty;

View file

@ -39,6 +39,9 @@ insert into llx_c_action_trigger (code,label,description,elementtype,rang) value
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_MODIFY','Third party update','Executed when you update third party','societe',1);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_SENTBYMAIL','Mails sent from third party card','Executed when you send email from third party card','societe',1);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_DELETE','Third party deleted','Executed when you delete third party','societe',1);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_RIB_CREATE','Third party payment information created','Executed when a third party payment information is created','societe',1);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_RIB_MODIFY','Third party payment information updated','Executed when a third party payment information is updated','societe',1);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_RIB_DELETE','Third party payment information deleted','Executed when a third party payment information is deleted','societe',1);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PROPAL_VALIDATE','Customer proposal validated','Executed when a commercial proposal is validated','propal',2);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PROPAL_MODIFY','Customer proposal modified','Executed when a customer proposal is modified','propal',2);
insert into llx_c_action_trigger (code,label,description,elementtype,rang) values ('PROPAL_SENTBYMAIL','Commercial proposal sent by mail','Executed when a commercial proposal is sent by mail','propal',3);

View file

@ -52,7 +52,11 @@ UPDATE llx_product_lot SET manufacturing_date = datec WHERE manufacturing_date I
UPDATE llx_societe_rib SET frstrecur = 'RCUR' WHERE frstrecur = 'RECUR';
-- Tip to copy vat rate into entity 2.
-- INSERT INTO llx_c_tva (entity, fk_pays, code, taux, localtax1, localtax1_type, localtax2, localtax2_type, use_default, recuperableonly, note, active, accountancy_code_sell, accountancy_code_buy) SELECT 2, fk_pays, code, taux, localtax1, localtax1_type, localtax2, localtax2_type, use_default, recuperableonly, note, active, accountancy_code_sell, accountancy_code_buy FROM llx_c_tva WHERE entity = 1;
-- INSERT INTO llx_c_tva (entity, fk_pays, code, taux, localtax1, localtax1_type, localtax2, localtax2_type, use_default, recuperableonly, note, active, accountancy_code_sell, accountancy_code_buy) SELECT 2, fk_pays, code, taux, localtax1, localtax1_type, localtax2, localtax2_type, use_default, recuperableonly, note, active, accountancy_code_sell, accountancy_code_buy FROM llx_c_tva WHERE entity = 1;
INSERT INTO llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_RIB_CREATE','Third party payment information created','Executed when a third party payment information is created','societe',1);
INSERT INTO llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_RIB_MODIFY','Third party payment information updated','Executed when a third party payment information is updated','societe',1);
INSERT INTO llx_c_action_trigger (code,label,description,elementtype,rang) values ('COMPANY_RIB_DELETE','Third party payment information deleted','Executed when a third party payment information is deleted','societe',1);
UPDATE llx_bank_url SET type = 'direct-debit' WHERE type = 'withdraw' AND url like '%compta/prelevement/card%';

View file

@ -179,4 +179,5 @@ CRUDRead=Read
CRUDCreateWrite=Create or Update
FailedToAddCodeIntoDescriptor=Failed to add code into descriptor. Check that the string comment "%s" is still present into the file.
DictionariesCreated=Dictionary <b>%s</b> created successfully
DictionaryDeleted=Dictionary <b>%s</b> removed successfully
DictionaryDeleted=Dictionary <b>%s</b> removed successfully
PropertyModuleUpdated=Property <b>%s</b> has been update successfully

View file

@ -179,4 +179,5 @@ CRUDRead=Lire
CRUDCreateWrite=Créer ou mettre à jour
FailedToAddCodeIntoDescriptor=Échec de l'ajout de code dans le descripteur. Vérifiez que le commentaire de chaîne "%s" est toujours présent dans le fichier.
DictionariesCreated = Le dictionnaire <b>%s</b> a été créé avec succès
DictionaryDeleted=Le dictionnaire <b>%s</b> a été supprimé avec succès
DictionaryDeleted=Le dictionnaire <b>%s</b> a été supprimé avec succès
PropertyModuleUpdated=Propriété <b>%s</b> a été modifiée avec succès

View file

@ -2787,6 +2787,83 @@ if ($dirins && $action == "modify_menu" && GETPOST('menukey', 'int') && GETPOST(
}
}
// update properties description of module
if ($dirins && $action == "update_props_module" && !empty(GETPOST('keydescription', 'alpha')) && empty($cancel)) {
if (isModEnabled(strtolower($module))) {
$result = unActivateModule(strtolower($module));
dolibarr_set_const($db, "MAIN_IHM_PARAMS_REV", (int) $conf->global->MAIN_IHM_PARAMS_REV + 1, 'chaine', 0, '', $conf->entity);
if ($result) {
setEventMessages($result, null, 'errors');
}
header("Location: ".DOL_URL_ROOT.'/modulebuilder/index.php?tab=menus&module='.$module);
setEventMessages($langs->trans('WarningModuleNeedRefrech', $langs->transnoentities($module)), null, 'warnings');
}
$pathtofile = $listofmodules[strtolower($module)]['moduledescriptorrelpath'];
$moduledescriptorfile = $dirins.'/'.strtolower($module).'/core/modules/mod'.$module.'.class.php';
$modulelogfile = $dirins.'/'.strtolower($module).'/ChangeLog.md';
dol_include_once($pathtofile);
$class = 'mod'.$module;
if (class_exists($class)) {
try {
$moduleobj = new $class($db);
} catch (Exception $e) {
$error++;
dol_print_error($db, $e->getMessage());
}
}
$keydescription = GETPOST('keydescription', 'alpha');
switch ($keydescription) {
case 'desc':
$propertyToUpdate = 'description';
break;
case 'version':
$propertyToUpdate = 'version';
break;
case 'family':
$propertyToUpdate = 'family';
break;
case 'editor_name':
$propertyToUpdate = 'editor_name';
break;
case 'editor_url':
$propertyToUpdate = 'editor_url';
break;
default:
$error = GETPOST('keydescription');
break;
}
if (isset($propertyToUpdate) && !empty(GETPOST('propsmodule'))) {
$newValue = GETPOST('propsmodule');
$lineToReplace = "\t\t\$this->$propertyToUpdate = ";
$newLine = "\t\t\$this->$propertyToUpdate = '$newValue';\n";
//for change version in log file
if ($propertyToUpdate === 'version') {
dolReplaceInFile($modulelogfile, array("## ".$moduleobj->$propertyToUpdate => $newValue));
}
$fileLines = file($moduledescriptorfile);
foreach ($fileLines as &$line) {
if (strpos($line, $lineToReplace) === 0) {
dolReplaceInFile($moduledescriptorfile, array($line => $newLine));
break;
}
}
clearstatcache(true);
if (function_exists('opcache_invalidate')) {
opcache_reset();
}
setEventMessages($langs->trans('PropertyModuleUpdated', $propertyToUpdate), null);
header("Location: ".DOL_URL_ROOT.'/modulebuilder/index.php?tab=description&module='.$module);
exit;
}
}
/*
* View
*/
@ -3253,7 +3330,12 @@ if ($module == 'initmodule') {
if (!empty($moduleobj)) {
print '<div class="underbanner clearboth"></div>';
print '<div class="fichecenter">';
print '<form action="'.$_SERVER["PHP_SELF"].'" method="POST">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="update_props_module">';
print '<input type="hidden" name="module" value="'.dol_escape_htmltag($module).'">';
print '<input type="hidden" name="tab" value="'.dol_escape_htmltag($tab).'">';
print '<input type="hidden" name="keydescription" value="'.dol_escape_htmltag(GETPOST('keydescription', 'alpha')).'">';
print '<table class="border centpercent">';
print '<tr class="liste_titre"><td class="titlefield">';
print $langs->trans("Parameter");
@ -3281,20 +3363,62 @@ if ($module == 'initmodule') {
print '<tr><td>';
print $langs->trans("Description");
print '</td><td>';
print $moduleobj->getDesc();
if ($action == 'edit_moduledescription' && GETPOST('keydescription', 'alpha') === 'desc') {
print '<input class="minwidth500" name="propsmodule" value="'.dol_escape_htmltag($moduleobj->description).'">';
print '<input class="reposition button smallpaddingimp" type="submit" name="modifydesc" value="'.$langs->trans("Modify").'"/>';
print '<input class="reposition button button-cancel smallpaddingimp" type="submit" name="cancel" value="'.$langs->trans("Cancel").'"/>';
} else {
print $moduleobj->getDesc();
print '<a class="editfielda reposition marginleftonly marginrighttonly paddingright paddingleft" href="'.$_SERVER["PHP_SELF"].'?action=edit_moduledescription&token='.newToken().'&tab='.urlencode($tab).'&module='.urlencode($module).'&keydescription=desc">'.img_edit().'</a>';
}
print '</td></tr>';
print '<tr><td>';
print $langs->trans("Version");
print '</td><td>';
print $moduleobj->getVersion();
if ($action == 'edit_moduledescription' && GETPOST('keydescription', 'alpha') === 'version') {
print '<input name="propsmodule" value="'.dol_escape_htmltag($moduleobj->getVersion()).'">';
print '<input class="reposition button smallpaddingimp" type="submit" name="modifydesc" value="'.$langs->trans("Modify").'"/>';
print '<input class="reposition button button-cancel smallpaddingimp" type="submit" name="cancel" value="'.$langs->trans("Cancel").'"/>';
} else {
print $moduleobj->getVersion();
print '<a class="editfielda reposition marginleftonly marginrighttonly paddingright paddingleft" href="'.$_SERVER["PHP_SELF"].'?action=edit_moduledescription&token='.newToken().'&tab='.urlencode($tab).'&module='.urlencode($module).'&keydescription=version">'.img_edit().'</a>';
}
print '</td></tr>';
print '<tr><td>';
print $langs->trans("Family");
//print "<br>'crm','financial','hr','projects','products','ecm','technic','interface','other'";
print '</td><td>';
print $moduleobj->family;
if ($action == 'edit_moduledescription' && GETPOST('keydescription', 'alpha') === 'family') {
print '<select name="propsmodule" id="family" class="minwidth400">';
$arrayoffamilies = array(
'hr' => "ModuleFamilyHr",
'crm' => "ModuleFamilyCrm",
'srm' => "ModuleFamilySrm",
'financial' => 'ModuleFamilyFinancial',
'products' => 'ModuleFamilyProducts',
'projects' => 'ModuleFamilyProjects',
'ecm' => 'ModuleFamilyECM',
'technic' => 'ModuleFamilyTechnic',
'portal' => 'ModuleFamilyPortal',
'interface' => 'ModuleFamilyInterface',
'base' => 'ModuleFamilyBase',
'other' => 'ModuleFamilyOther'
);
print '<option value="'.$moduleobj->family.'" data-html="'.dol_escape_htmltag($langs->trans($arrayoffamilies[$moduleobj->family]).' <span class="opacitymedium">- '.$moduleobj->family.'</span>').'">'.$langs->trans($arrayoffamilies[$moduleobj->family]).'</option>';
foreach ($arrayoffamilies as $key => $value) {
if ($key != $moduleobj->family) {
print '<option value="'.$key.'" data-html="'.dol_escape_htmltag($langs->trans($value).' <span class="opacitymedium">- '.$key.'</span>').'">'.$langs->trans($value).'</option>';
}
}
print '</select>';
print '<input class="reposition button smallpaddingimp" type="submit" name="modifydesc" value="'.$langs->trans("Modify").'"/>';
print '<input class="reposition button button-cancel smallpaddingimp" type="submit" name="cancel" value="'.$langs->trans("Cancel").'"/>';
} else {
print $moduleobj->family;
print '<a class="editfielda reposition marginleftonly marginrighttonly paddingright paddingleft" href="'.$_SERVER["PHP_SELF"].'?action=edit_moduledescription&token='.newToken().'&tab='.urlencode($tab).'&module='.urlencode($module).'&keydescription=family">'.img_edit().'</a>';
}
print '</td></tr>';
print '<tr><td>';
@ -3307,18 +3431,33 @@ if ($module == 'initmodule') {
print '<tr><td>';
print $langs->trans("EditorName");
print '</td><td>';
print $moduleobj->editor_name;
if ($action == 'edit_moduledescription' && GETPOST('keydescription', 'alpha') === 'editor_name') {
print '<input name="propsmodule" value="'.dol_escape_htmltag($moduleobj->editor_name).'">';
print '<input class="reposition button smallpaddingimp" type="submit" name="modifydesc" value="'.$langs->trans("Modify").'"/>';
print '<input class="reposition button button-cancel smallpaddingimp" type="submit" name="cancel" value="'.$langs->trans("Cancel").'"/>';
} else {
print $moduleobj->editor_name;
print '<a class="editfielda reposition marginleftonly marginrighttonly paddingright paddingleft" href="'.$_SERVER["PHP_SELF"].'?action=edit_moduledescription&token='.newToken().'&tab='.urlencode($tab).'&module='.urlencode($module).'&keydescription=editor_name">'.img_edit().'</a>';
}
print '</td></tr>';
print '<tr><td>';
print $langs->trans("EditorUrl");
print '</td><td>';
if (!empty($moduleobj->editor_url)) {
print '<a href="'.$moduleobj->editor_url.'" target="_blank" rel="noopener">'.$moduleobj->editor_url.' '.img_picto('', 'globe').'</a>';
if ($action == 'edit_moduledescription' && GETPOST('keydescription', 'alpha') === 'editor_url') {
print '<input name="propsmodule" value="'.dol_escape_htmltag($moduleobj->editor_url).'">';
print '<input class="reposition button smallpaddingimp" type="submit" name="modifydesc" value="'.$langs->trans("Modify").'"/>';
print '<input class="reposition button button-cancel smallpaddingimp" type="submit" name="cancel" value="'.$langs->trans("Cancel").'"/>';
} else {
if (!empty($moduleobj->editor_url)) {
print '<a href="'.$moduleobj->editor_url.'" target="_blank" rel="noopener">'.$moduleobj->editor_url.' '.img_picto('', 'globe').'</a>';
}
print '<a class="editfielda reposition marginleftonly marginrighttonly paddingright paddingleft" href="'.$_SERVER["PHP_SELF"].'?action=edit_moduledescription&token='.newToken().'&tab='.urlencode($tab).'&module='.urlencode($module).'&keydescription=editor_url">'.img_edit().'</a>';
}
print '</td></tr>';
print '</table>';
print '</form>';
} else {
print $langs->trans("ErrorFailedToLoadModuleDescriptorForXXX", $module).'<br>';
}