Merge branch 'develop' into develop_new_rework_webportal_lists

This commit is contained in:
Laurent Destailleur
2025-11-27 00:36:18 +01:00
committed by GitHub
553 changed files with 24366 additions and 5259 deletions

View File

@@ -18,7 +18,7 @@ jobs:
contents: read
steps:
- name: Check out code
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Cleanup
run: |
gh extension install actions/gh-actions-cache

View File

@@ -0,0 +1,29 @@
---
# This is a basic workflow to check the lock on major version (to lock some files on certified versions)
name: Check fileset lock
on: [push, pull_request]
concurrency:
group: check-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
checkmajorversion:
name: Check lock on fileset unalterable_files with generate_filelist_xml.php
runs-on: ubuntu-latest
# Do not run schedule on forks
if: |
github.repository == 'Dolibarr/dolibarr'
|| github.event.schedule == false
steps:
- uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
coverage: none # disable xdebug, pcov
- name: Run generate_filelist_xml.php
run: |
# shellcheck disable=2086
dev/build/generate_filelist_xml.php checklock=auto unalterable_files

View File

@@ -33,7 +33,7 @@ jobs:
pull-requests: write
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:

View File

@@ -33,7 +33,7 @@ jobs:
github.repository == 'Dolibarr/dolibarr'
|| github.event.schedule == false
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:

View File

@@ -37,7 +37,7 @@ jobs:
# Steps represent a sequence of tasks that will be executed as part of the job
steps:
# Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it
- uses: actions/checkout@v5
- uses: actions/checkout@v6
# Get PHP and addons
- name: Setup PHP
id: setup-php

View File

@@ -30,7 +30,7 @@ jobs:
# if: false
# Checkout git sources to analyze
- uses: actions/checkout@v5
- uses: actions/checkout@v6
# Try to get the list of modified files into steps.changed-php.outputs.all_changed_files
#- name: Get changed files
@@ -109,6 +109,10 @@ jobs:
coverage: none # disable xdebug, pcov
tools: phpcs
# Install perltidy and perlcritic
- name: Install perltidy and perlcritic
run: sudo apt-get update && sudo apt-get install -y perltidy libperl-critic-perl
# Run all the precommit tools (defined into pre-commit-config.yaml).
# We can force exclusion of some of them here.
- name: Run pre-commit hooks

View File

@@ -37,7 +37,7 @@ jobs:
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:

View File

@@ -274,3 +274,21 @@ repos:
|htdocs/install/pgsql/functions/functions.*\.sql
|htdocs/modulebuilder/template/sql/.*\.sql
)$
- repo: https://github.com/perltidy/perltidy
rev: '20250105.03'
hooks:
- id: perltidy
# virtualmin excuded - reason https://github.com/Dolibarr/dolibarr/pull/36370#issuecomment-3565101823
exclude: (?x)^
(dev/build/perl/virtualmin/dolibarr.pl
)$
args: [ --tabs, --nola ]
- repo: https://github.com/henryykt/pre-commit-perl
rev: v0.0.5
hooks:
- id: perlcritic
# virtualmin excuded - reason https://github.com/Dolibarr/dolibarr/pull/36370#issuecomment-3565101823
exclude: (?x)^
(dev/build/perl/virtualmin/dolibarr.pl
)$

204
ChangeLog
View File

@@ -6,36 +6,212 @@ English Dolibarr ChangeLog
For users:
----------
NEW: Need PHP 7.2 as minimum version
NEW: Module datapolicy moved as stable (for anonimization features)
NEW: #31723 - Improve project overview: Hide paid orders (#35524)
NEW: #35700 : Throw an error when validating a propal, order, supplier with a product no more in sale/purchase… (#35709)
NEW: Accountancy - Accounting by payment type (#34729)
NEW: Accountancy - Add accounting for discounts (#35977)
NEW: Accountancy - Add a protection on various payment for auxiliary account on general account not centralized (#35720)
NEW: Accountancy - Add field centralized on import/export (#35872)
NEW: Accountancy - Add hook on export filename (#35188)
NEW: Accountancy - Add reconcile on general accounting account - SQL part (#35994)
NEW: Accountancy - Analytical axis (SQL Structure) (#34738)
NEW: Accountancy - Manual input - Add script to greyed out subledger_account if general ledger is not centralized (#35855)
NEW: Accountancy - Transaction - Add verification on centralized account (#35824)
NEW: Accountancy - Various payment - Add script to greyed out subledger_account if general ledger is not centralized (#35842)
NEW: Add a boolean for lines in api and $properties (#34293)
NEW: Add accounting export mode for ISTEA (#36006)
NEW: add a limit to avoid too many answer in agenda view. Add warning if limit has been reached.
NEW: Add a page to edit http security headers of application (#34941)
NEW: Add auto-reference generation for tasks (like in project) in API (#35981)
NEW: Add column ref_ext and note_private for membership
NEW: Add column thirdparty ref_customer and ref_supplier in project list
NEW: Add column title in emailing and add more filters
NEW: add combining characters (accents, cedilla...) codes in dol_string_unaccent() (#35130)
NEW: add company date birth (SQL structure) (#34854) (UI) (#34861)
NEW: add conditional supplier price display (#35900)
NEW: Add configuration for default timesheet menu (#35805)
NEW: Add contact tab on product service (#35914)
NEW: Add directory navigation to Web Portal Shared Documents (#35443)
NEW: Added Messaging and agenda tabs on order and shipments (#34859)
NEW: Add event when installing a module in the security event list.
NEW: add extrafield option "empty on clone" (#34866)
NEW: add fields usage_opportunity, usage_task, usage_bill_time for project import (#35301)
NEW: Add filter on agenda event progression on agenda page
NEW: add free numbering module for members (#35636)
NEW: add global search for resource object (#36043)
NEW: add hidden const to get response header in geturlcontent function (#34781) (#34824)
NEW: add hook getListOfModels (#34626)
NEW: Add hook on calcula_price() and get_default_tva()
NEW: Add hooks in webportal (#35326)
NEW: add hook when printing new card button on thirdparty list (#36350)
NEW: add hourly rate in list of users
NEW: Add missing parameters for menus on webportal hook (#35550)
NEW: Add option PDF_PURCHASE_INVOICE_HIDE_VAT
NEW: Add option PROJECT_CAN_ALWAYS_LINK_TO_ALL_CUSTOMERS
NEW: Add option to create simple standalone shipment of non origin (#35651)
NEW: Add option to create standalone reception (#36134)
NEW: add private and public note on ticket (#35303)
NEW: add product_type field on fichinter (preparing subtotal) (#36196)
NEW: Add regions for CONGO, THE DEMOCRATIC REPUBLIC OF THE;CD (#36340)
NEW: ADD Send mail for reception / Delivery (#34829)
NEW: Add setup page to concat natively files on invoice PDF.
NEW: add shipping address to propal (#34441)
NEW: Add Sign feature on shipments (#34640)
NEW: Add sms reminder in reminder of agenda events (#35239)
NEW: Add SQL table for expensereport line extrafields support (#36251)
NEW: add supplier invoice, order and supplier order tag filter and bulk insert and statistics order and supplier order (#35399)
NEW: add supplier payment mail template (#35877)
NEW: add tags on proposals and supplier proposals and in statistics (#35553)
NEW: Add task categories 1/3 (#35848)
NEW: Add tasks card hooks (#35616)
NEW: Add the "Dispute status" in list of invoice.
NEW: add the option to not synchronize thirdparty <--> member (#36033)
NEW: Add the widget funnel of opportunities
NEW: Add tpl files for standalone shipment (#35624)
NEW: Add tpl loader for discounts.tpl.php (#34798)
NEW: Add Transfer Number (#35665)
NEW: Add Type, Description columns to Project Overview Expense Reports (#36214)
NEW: Add user permission for create/edit/delete supplier prices (#35940)
NEW: Allow omission of ODT template name when generating ODT and PDF (#35701)
NEW: Better navigation and report into database admin tools
NEW: Button to create a proposal and sale order from a contract always on
NEW: Can add info of main IT service provider in setup.
NEW: can admin payments extrafields (#34822)
NEW: Can drag and drop in BOM card
NEW: Can force_install_dolibarrpassword for automatic installation (#34537)
NEW: Can have a tooltip picto on title of column and keep autotruncation of label. Tooltip is show at begin of text.
NEW: Can show both currency code and symbol into the select of currency
NEW: Can sort on employee in holiday balance. Add link to go to history.
NEW: Can update value of timespent with last task hourly rate (#36018)
NEW: Can urlencode substitution variable of constants
NEW: Can view/list/edit the dispute status of an invoice
NEW: Change the path for the mailing files (#34878)
NEW: Constant ORDER_MASS_ACTION_BILLED_LINK_EXPEDITIONS (#34617)
NEW: const MENU_HIDE_EMAIL_TEMPLATES to hide email templates setup in Tools menu (#35739)
NEW: Create simple shipment of non origin (#35604)
NEW: Currency for the Democratic Republic of Congo added. (#36104)
NEW: Customized step in duration select (#34652)
NEW: Dashboard - Add option in ihm to disable MRP thumb (#36185)
NEW: date function related to holiday can accept country id in addition to country code
NEW: DEV Can set color of the on/off button.
NEW: Disable by default obfuscation methods and function in extrafields evaluable strings. Can re-enable with MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL=1
NEW: Discount split more than two parts (#34782)
NEW: Display company logo on kanban view (#34520)
NEW: dol_sort_array can be used with 2 sorting criteria.
NEW: Enhanced layout feature for emailing
NEW: Enhance popup for image preview (show size in title, can restore small view, always show the Rotate button).
NEW: Enhance the system for warnings on module activation
NEW: execute hook addMoreActionsButtons on bank card (#35598)
NEW: extrafields: add field to link a field to a module (#34416)
NEW: Feature to merge duplicate members (#35308)
NEW: generate renewal proposal for contracts (#35120)
NEW: Holiday - Allow to specify a specific mail address from (#36184)
NEW: hook allowing external modules to replace the behavior of fetchObjectLinked() (#34724)
NEW: hooks `showInputExtraField` and `showOutputExtraField` to override ExtraFields::show(Input|Output)Field (#35496)
NEW: implement box on product and interventional index page (#34629)
NEW: import subscriptions (#35612)
NEW: Introduce getCurrency(). $conf is no more allowed into computed formulae.
NEW: Invert logic of default date in proposal/order/invoice creation: Need option to NOT autofill instead of the opposite.
NEW: line input multicurrency price with tax (#35064)
NEW: Major overhaul of DataPolicyCron and add Recruitment policy (#34704)
NEW: Make the public contact form with experimental status
NEW: Minimal version of PHP is now 7.2
NEW: More index for memberships table
NEW: More information on the user credential section
NEW: More webportal feature - Can add shared files and thirdparty documents (#35391)
NEW: New hook mergePdf (#34707)
NEW: On invoice, show also nb of credit notes notyet converted for consumption
NEW: Option to clone parent categories on variant creation (#35806)
NEW: Output of category tag is nicer for long subcategories.
NEW: Parent project column in list of projects (#36177)
NEW: Paymentok validate invoice if not already done (#35564)
NEW: PDF Show customer balance on invoice date (#34800)
NEW: possibility to define global entity in user param (#35908)
NEW: Add messaging and agenda features to proposals (#34883)
NEW: public and private note can be shown on contact list
NEW: Public donation page (#35565)
NEW: public pages donation, ticket and member use captcha setup (#35913)
NEW: Rework of the management of the card and fields on the web portal (#36076)
NEW: Save the BAN and RUM for SEPA into database not just file.
NEW: search all facture rec (#34563)
NEW: Show cron last result and output in info (#36028)
NEW: Show full date with seconds in the tooltip of date of event
NEW: Show the link to download the zip of a module on module setup
NEW: Show warning on banner when an email is not valid
NEW: The check file feature can limit check on unalterable files only
NEW: The flag "Dispute open" make the status in Red.
NEW: The PHP info is in a popup in install page.
NEW: update country list (#34865)
NEW: Update Incoterms to 2025 standards and add new terms (#36041)
NEW: Upgrade ACE editor to 1.43.12
NEW: User/Date in the Follow tab are more condensed.
NEW: We added a hook to allow us to modify the Prospect Customer drop down… (#25635)
NEW: Withholdingtax how VatRefund (#34649)
For developers:
---------------
* Introducing the TRIGGER_PREFIX property to force developers to use unique triggerkey per business object, limit also code to business CRUD events and identity when it is not.
NEW: Introducing the TRIGGER_PREFIX property to force developers to use unique triggerkey per business object, to limit code to business CRUD events and report warning when it is not.
NEW: add extraparams field in llx_categorie (#35975)
NEW: Add prepare() method to DoliDB class (rebuild) (#35249)
NEW: Add function to split a discount in 2 by API REST (#34786)
NEW: Add a new API "product lots" (#36243)
NEW: Add API for Holidays/Leaves
NEW: add api for members statistics (#35851)
NEW: add api List VAT (#35920)
NEW: add api_paiements.class.php (#34756)
NEW: Add contact management on project Api (#35459)
NEW: Add contact support on products in REST API (#35925)
NEW: Add country ID resolution from country code for thirdparty api (#36345)
NEW: Add getcontacts on api of interventional and proposal (#35589)
NEW: Add hook initialization for interventions API (#35203)
NEW: Add option API_ENABLE_COUNT_CALLS
NEW: Add thirdparty search on api list (#34634)
NEW: add timespent API endpoints for projects and tasks add also cascading assignment of contacts to tasks (#35897)
NEW: add upload api feature for shipment (#34639)
NEW: Allow creating contact via api with ISO code (#36322)
NEW: API endpoint for getting products in a warehouse (#35918)
NEW: API for getting, adding, deleting and/or modifying email templates (#35853)
NEW: API for handling mass mailing targets (#35603)
NEW: API GET endpoint for thirdparties types listing (c_typent) (#34751)
NEW: Api mass emailing (#35531)
NEW: API user/groups/ POST, PUT, DELETE + some hurl tests (#35903)
NEW: API User - Remove user from group (#35453)
NEW: Implement listTimespent method in api_projects.class.php (#36093)
NEW: qual fixes on api contract (#36066)
NEW: stock API GET movement (#36193)
WARNING:
--------
The following changes may create regressions for some external modules, but were necessary to make Dolibarr better:
* The deprecated column egroupware_id has been dropped.
* The deprecated column "egroupware_id" has been dropped from table llx_user.
* The property $sumpayed (duplicated of $totalpaid), $sumdeposit (duplicate of $totaldeposits) and $sumcreditnote (duplicate of $totalcreditnotes) has
been removed (there are replaced with the property that was a duplication of (same for $sumpayed_multicurrency, $sumdeposit_multicurrency, $sumcreditnote_multicurrency).
* Parameters $maxlen and $notooltip of Contract have been inverted to follow the standard. It was breaking the common use of getNomUrl() but if you were
using the parameter maxlen (rare) by using the old signature, result may be a tooltip that is no more visible on ref printed by your module.
* Removed array $MAP_ID_TO_CODE that was a duplicate of array "array_flip($categ->MAP_ID)"
using the parameter maxlen (rare) by using the old signature, result may be just a tooltip that is no more visible when mous over the contract ref shown by your module.
* Removed array $MAP_ID_TO_CODE that was a duplicate of the array "array_flip($categ->MAP_ID)"
* The signature of the Sale order ->cancel() method and shipment ->cancel() has been modified to introduce the $user param like for other methods that modify a status.
* Adding new document templates must be done by adding files into the mymodule/core/modules/xxx/doc directory. Adding files into custom directory with the
same path than the core path, without using a "mymodule" directory is now forbidden.
* The directory theme/common/octicons has been removed
* The library timepicker.js has been removed. Was no more used by Dolibarr.
* Because of new TRIGGER_PREFIX property triggers SUPPLIER_PRODUCT_BUYPRICE_XXX are renamed to PRODUCT_BUYPRICE_XXX.
* Function img_pdf() has been removed. Replace it with img_picto('', 'pdf.png') if you were using it.
* Adding new "document generation templates" must be done by adding files into the mymodule/core/modules/xxx/doc directory. Adding files into custom directory with the
same path than the core path, without using a "mymodule" directory, is now forbidden.
* The directory theme/common/octicons has been removed. No resource was used by Dolibarrfrom this directory.
* The library timepicker.js has been removed. Was no more used by Dolibarr since a long time.
* Because of the new TRIGGER_PREFIX property, the triggers SUPPLIER_PRODUCT_BUYPRICE_XXX are renamed into PRODUCT_BUYPRICE_XXX.
* Function img_pdf() has been removed. Replace it with img_picto('', 'pdf') if you were using it.
* The method run_trigger() was deprecated 10+ years ago in favor of runTrigger(). It has been removed. Change your trigger file if you still use it.
* Property ->picto of module descriptors must contains the image extension if it is not a font awesome tag. Example: $this->picto="mymoduleimg.png";
* Stock movement API GET method output variable names has been harmonized with POST input parameter names
* $conf is no more allowed into computed formulae. You ca replace use of $conf->currency by NEW Introduce getCurrency() and $conf->global->xxx by getDolGlobalString('xxx')
* Concatenation into computed property of extrafields is off by default. You can enable it from conf.php file by adding dol_concat to list of allowed function in $dolibarr_main_restrict_eval_methods,
For example: $dolibarr_main_restrict_eval_methods='getDolGlobalString,getDolGlobalInt,getDolCurrency,fetchNoCompute,hasRight,isModEnabled,isStringVarMatching,abs,min,max,round,dol_now,dol_concat,preg_match';
* Old variable $obj and $object are no more allowed into on the fly evaluated strings like computed or conditions on extrafields. Use $objectoffield to get current object
Also if you were using temporary variables int a computed extrafields, the nameot temporary variable must match $var123.
* The hidden constant MAIN_ALLOW_UNSECURED_SPECIAL_CHARS_IN_DOL_EVAL in database has been replaced with the
variable $dolibarr_main_allow_unsecured_special_chars_in_dol_eval into file conf/conf.php
* $conf use into "computed formulae" of etrafields is now deprecated (not yet forbidden). You can replace use of $conf->currency by the new method getCurrency() and $conf->global->xxx by getDolGlobalString('xxx').
* $user->rights->module->perms is also deprecated. You can use $user->hasRight() instead.
* The API endpoint /proposals/{id}/contact/{contactid}/{type}/{source} is now {id}/contact/{contactid}/{type} to match same behaviour than order and invoices.
***** ChangeLog for 22.0.3 compared to 22.0.2 *****

View File

@@ -2,7 +2,7 @@
![Downloads per day](https://img.shields.io/sourceforge/dw/dolibarr.svg)
![Docker hub pulls](https://img.shields.io/docker/pulls/dolibarr/dolibarr.svg)
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.1-8892BF.svg?style=flat-square)](https://php.net/)
[![Minimum PHP Version](https://img.shields.io/badge/php-%3E%3D%207.2-8892BF.svg?style=flat-square)](https://php.net/)
[![GitHub release](https://img.shields.io/github/v/release/Dolibarr/dolibarr)](https://github.com/Dolibarr/dolibarr)
[![CII Best Practices](https://bestpractices.coreinfrastructure.org/projects/5521/badge)](https://bestpractices.coreinfrastructure.org/projects/5521)

View File

@@ -1,54 +1,94 @@
#!/usr/bin/perl
##no critic (InputOutput::RequireBriefOpen)
use strict;
use warnings;
#--------------------------------------------------------------------
# Start the generation of the development documentation with doxygen
#--------------------------------------------------------------------
# Determine the patho of this script
($DIR=$0) =~ s/([^\/\\]+)$//;
$DIR||='.';
( my $DIR = $0 ) =~ s/([^\/\\]+)$//;
$DIR ||= '.';
$DIR =~ s/([^\/\\])[\\\/]+$/$1/;
$OPTIONS="";
my $OPTIONS = "";
#$OPTIONS="-d Preprocessor";
$CONFFILE="dolibarr-doxygen.doxyfile";
my $CONFFILE = "dolibarr-doxygen.doxyfile";
use Cwd;
my $dir = getcwd;
print "Current dir is: $dir\n";
#print "Running dir for doxygen must be: $DIR\n";
if (! -s "dev/build/doxygen/$CONFFILE")
{
print "Error: current directory for building Dolibarr doxygen documentation is not correct.\n";
print "\n";
if ( !-s "dev/build/doxygen/$CONFFILE" ) {
print
"Error: current directory for building Dolibarr doxygen documentation is not correct.\n";
print "\n";
print "Change your current directory then, to launch the script, run:\n";
print '> perl .\dolibarr-doxygen-build.pl (on Windows)'."\n";
print '> perl ../dolibarr-doxygen-build.pl (on Linux or BSD)'."\n";
sleep 4;
exit 1;
print '> perl .\dolibarr-doxygen-build.pl (on Windows)' . "\n";
print '> perl ../dolibarr-doxygen-build.pl (on Linux or BSD)' . "\n";
sleep 4;
exit 1;
}
$SOURCE=".";
my $SOURCE = ".";
# Get version $MAJOR, $MINOR and $BUILD
$result = open( IN, "< " . $SOURCE . "/htdocs/filefunc.inc.php" );
if ( !$result ) { die "Error: Can't open descriptor file " . $SOURCE . "/htdocs/filefunc.inc.php\n"; }
while (<IN>) {
if ( $_ =~ /define\('DOL_VERSION', '([\d\.a-z\-]+)'\)/ ) { $PROJVERSION = $1; break; }
my $result = open( my $IN, "<", $SOURCE . "/htdocs/filefunc.inc.php" );
if ( !$result ) {
die "Error: Can't open descriptor file " . $SOURCE
. "/htdocs/filefunc.inc.php\n";
}
close IN;
($MAJOR,$MINOR,$BUILD)=split(/\./,$PROJVERSION,3);
if ($MINOR eq '') { die "Error can't detect version into ".$SOURCE . "/htdocs/filefunc.inc.php"; }
my $PROJVERSION = "";
while (<$IN>) {
if ( $_ =~ /define\('DOL_VERSION', '([\d\.a-z\-]+)'\)/ ) {
$PROJVERSION = $1;
last;
}
}
close $IN;
if ( $PROJVERSION eq "" ) {
my $DOL_MAJOR_VERSION;
my $DOL_MINOR_VERSION;
my @VERSION_FILES = ( "filefunc.inc.php", "version.inc.php" );
foreach my $file (@VERSION_FILES) {
$result = open( my $IN, "<", $SOURCE . "/htdocs/$file" );
if ( !$result ) {
die "Error: Can't open descriptor file " . $SOURCE
. "/htdocs/$file\n";
}
while (<$IN>) {
if ( $_ =~ /define\('DOL_MAJOR_VERSION', '([\d\.a-z\-]+)'\)/ ) {
$DOL_MAJOR_VERSION = $1;
}
if ( $_ =~ /define\('DOL_MINOR_VERSION', '([\d\.a-z\-]+)'\)/ ) {
$DOL_MINOR_VERSION = $1;
}
}
close $IN;
}
$PROJVERSION = $DOL_MAJOR_VERSION . '.' . $DOL_MINOR_VERSION;
}
$version=$MAJOR.".".$MINOR.".".$BUILD;
( my $MAJOR, my $MINOR, my $BUILD ) = split( /\./, $PROJVERSION, 3 );
if ( !defined($MINOR) || $MINOR eq '' ) {
die "Error can't detect version from " . $SOURCE
. "/htdocs/filefunc.inc.php";
}
my $version = $MAJOR . "." . $MINOR . "." . $BUILD;
print "Running doxygen for version ".$version.", please wait...\n";
print "cat dev/build/doxygen/$CONFFILE | sed -e 's/x\.y\.z/".$version."/' | doxygen $OPTIONS - 2>&1\n";
$result=`cat dev/build/doxygen/$CONFFILE | sed -e 's/x\.y\.z/$version/' | doxygen $OPTIONS - 2>&1`;
print "Running doxygen for version " . $version . ", please wait...\n";
print "cat dev/build/doxygen/$CONFFILE | sed -e 's/x\.y\.z/" . $version
. "/' | doxygen $OPTIONS - 2>&1\n";
$result =
`cat dev/build/doxygen/$CONFFILE | sed -e 's/x\.y\.z/$version/' | doxygen $OPTIONS - 2>&1`;
print $result;

View File

@@ -4,85 +4,79 @@
# on PHP source files before running Doxygen.
# \author Laurent Destailleur
#--------------------------------------------------------------------
## no critic (InputOutput::RequireBriefOpen)
use strict;
use warnings;
# Usage: dolibarr-doxygen-filter.pl pathtofilefromdolibarrroot
$file=$ARGV[0];
if (! $file)
{
my $file = $ARGV[0];
if ( !$file ) {
print "Usage: dolibarr-doxygen-filter.pl pathtofilefromdolibarrroot\n";
exit;
}
open(FILE,$file) || die "Failed to open file $file";
while (<FILE>)
{
if ($_ =~ /\\version\s/i)
{
open( my $fh, "<", $file ) || die "Failed to open file $file";
while (<$fh>) {
if ( $_ =~ /\\version\s/i ) {
$_ =~ s/\$Id://i;
$_ =~ s/(Exp|)\s\$$//i;
$_ =~ s/(\\version\s+)[^\s]+\s/$1/i;
$_ =~ s/(\w)\s(\w)/$1_$2/g;
}
$_ =~ s/exit\s*;/exit(0);/i;
$i=0;
$len=length($_);
$s="";
$insidequote=0;
$insidedquote=0;
$ignore="";
while ($i < $len)
{
$c=substr($_,$i,1);
if ($c eq "\\")
{
if ($insidequote) { $ignore="'"; };
if ($insidedquote) { $ignore="\""; };
my $i = 0;
my $len = length($_);
my $s = "";
my $insidequote = 0;
my $insidedquote = 0;
my $ignore = "";
while ( $i < $len ) {
my $c = substr( $_, $i, 1 );
if ( $c eq "\\" ) {
if ($insidequote) { $ignore = "'"; }
if ($insidedquote) { $ignore = "\""; }
}
else
{
if ($c eq "'")
{
if (! $insidedquote)
{
$c="\"";
else {
if ( $c eq "'" ) {
if ( !$insidedquote ) {
$c = "\"";
#print "X".$ignore;
if ($ignore ne "'")
{
if ( $ignore ne "'" ) {
#print "Z".$ignore;
$insidequote++;
if ($insidequote == 2)
{
$insidequote=0;
if ( $insidequote == 2 ) {
$insidequote = 0;
}
}
}
#print "X".$insidequote;
}
elsif ($c eq "\"")
{
elsif ( $c eq "\"" ) {
#print "Y".$insidequote;
if ($insidequote)
{
$c="'";
if ($insidequote) {
$c = "'";
}
else
{
if ($ignore ne "\"")
{
else {
if ( $ignore ne "\"" ) {
$insidedquote++;
if ($insidedquote == 2)
{
$insidedquote=0;
if ( $insidedquote == 2 ) {
$insidedquote = 0;
}
}
}
}
$ignore="";
$ignore = "";
}
$s.=$c;
$s .= $c;
$i++;
}
print $s;
}
close(FILE);
close($fh);

View File

@@ -1,4 +1,7 @@
#!/usr/bin/perl
use strict;
use warnings;
#--------------------------------------------------------------------
# Script to get version of a source file
# Does not work with cygwin cvs command on Windows.
@@ -7,15 +10,18 @@
# Usage: dolibarr-doxygen-getversion.pl pathtofilefromdolibarrroot
$file=$ARGV[0];
if (! $file)
{
$file = $ARGV[0];
if ( !$file ) {
print "Usage: dolibarr-doxygen-getversion.pl pathtofilefromdolibarrroot\n";
exit;
}
$commande='cvs status "'.$file.'" | sed -n \'s/^[ \]*Working revision:[ \t]*\([0-9][0-9\.]*\).*/\1/p\'';
$commande =
'cvs status "'
. $file
. '" | sed -n \'s/^[ \]*Working revision:[ \t]*\([0-9][0-9\.]*\).*/\1/p\'';
#print $commande;
$result=`$commande 2>&1`;
$result = `$commande 2>&1`;
print $result;

View File

@@ -26,6 +26,7 @@
if (!defined('NOREQUIREDB')) {
define('NOREQUIREDB', '1'); // Do not create database handler $db
}
define('NOREQUIREVIRTUALURL', 1);
$sapi_type = php_sapi_name();
$script_file = basename(__FILE__);
@@ -37,6 +38,8 @@ if (substr($sapi_type, 0, 3) == 'cgi') {
exit(1);
}
define('DOL_DOCUMENT_ROOT', dirname(dirname($path)).'/htdocs');
require_once $path."../../htdocs/master.inc.php";
require_once DOL_DOCUMENT_ROOT."/core/lib/files.lib.php";
@@ -200,6 +203,9 @@ if ($release) {
}
$needtoclose = 0;
// Build the XML file
if ($release) {
$checksumconcat = array();
@@ -227,8 +233,6 @@ if ($release) {
fputs($fp, '<?xml version="1.0" encoding="UTF-8" ?>'."\n");
fputs($fp, '<checksum_list version="'.$release.'" date="'.dol_print_date(dol_now(), 'dayhourrfc').'" generator="'.$script_file.'" gitcommit="'.$gitcommit.'">'."\n");
$needtoclose = 0;
foreach ($includeconstants as $countrycode => $tmp) {
fputs($fp, '<dolibarr_constants country="'.$countrycode.'">'."\n");
foreach ($tmp as $constname => $constvalue) {
@@ -382,6 +386,7 @@ foreach ($arrayofunalterablefiles as $entry) {
}
} else {
$file = $entry['dir'].'/'.$entry['file'];
$dir = '';
$newdir = str_replace(DOL_DOCUMENT_ROOT, '', dirname($file));
$newdir = str_replace(dirname(__FILE__).'/../../htdocs', '', dirname($file));
if (!file_exists($file)) {
@@ -461,24 +466,29 @@ if ($release) {
if ($checklock) {
print "Signature for unalterable files: ".$md5unalterable_files."\n";
$lockedfile = DOL_DOCUMENT_ROOT.'/../dev/lockedfiles.txt';
$checksuminlockedfile = '';
// Now we check the content of lockedfiles.txt
$arraylocked = file(DOL_DOCUMENT_ROOT.'/../dev/lockedfiles.txt');
foreach ($arraylocked as $line) {
$tmparray = preg_split("/\s+/", $line, 3);
if ($tmparray[0] == $checklockmajorversion) {
$checksuminlockedfile = $tmparray[2];
if (!file_exists($lockedfile)) {
print "Can't find the file ".$lockedfile.". No checksum to check\n";
} else {
// Now we check the content of lockedfiles.txt
$arraylocked = file($lockedfile);
foreach ($arraylocked as $line) {
$tmparray = preg_split("/\s+/", $line, 3);
if ($tmparray[0] == $checklockmajorversion) {
$checksuminlockedfile = $tmparray[2];
}
}
}
if (empty($checksuminlockedfile)) {
print "The major version ".$checklockmajorversion." is not locked on the scope ".$checksource." (no entry found into dev/lockedfiles.txt).\n";
} elseif ($checksuminlockedfile != $md5unalterable_files) {
print "The major version ".$checklockmajorversion." is locked on scope '".$checksource."' to checksum ".$checksuminlockedfile."\n";
if ($checklockmajorversion != $checksource) {
print "The checksum now differs from the locked one, so we return an error.\n";
print "\n";
exit(10);
if (empty($checksuminlockedfile)) {
print "The major version ".$checklockmajorversion." is not locked on the scope ".$checksource." (file found but no matching entry found into dev/lockedfiles.txt).\n";
} elseif ($checksuminlockedfile != $md5unalterable_files) {
print "The major version ".$checklockmajorversion." is locked on scope '".$checksource."' to checksum ".$checksuminlockedfile."\n";
if ($checklockmajorversion != $checksource) {
print "The checksum now differs from the locked one, so we return an error.\n";
print "\n";
exit(10);
}
}
}
}

View File

@@ -1,4 +1,5 @@
#!/usr/bin/perl
## no critic (InputOutput::RequireBriefOpen)
#fetch Gravatars
use strict;
@@ -10,39 +11,44 @@ use Digest::MD5 qw(md5_hex);
my $size = 90;
my $output_dir = './avatars';
die("no .git/ directory found in current path\n") unless -d './avatars';
die("no .git repository found in current path\n") unless -r './.git';
mkdir($output_dir) unless -d $output_dir;
open(GITLOG, q/git log --pretty=format:"%ae|%an" |/) or die("failed to read git-log: $!\n");
open( my $GITLOG, '-|', q/git log --pretty=format:"%ae|%an" --reverse/ )
or die("failed to read git-log: $!\n");
my %processed_authors;
while(<GITLOG>) {
chomp;
my($email, $author) = split(/\|/, $_);
while (<$GITLOG>) {
chomp;
my ( $email, $author ) = split( /\|/, $_ );
next if $processed_authors{$author}++;
next if $processed_authors{$author}++;
my $author_image_file = $output_dir . '/' . $author . '.png';
my $author_image_file = $output_dir . '/' . $author . '.png';
#skip images we have
next if -e $author_image_file;
#skip images we have
next if -e $author_image_file;
#try and fetch image
#try and fetch image
my $grav_url = "http://www.gravatar.com/avatar/".md5_hex(lc $email)."?d=404&size=".$size;
my $grav_url =
"https://www.gravatar.com/avatar/"
. md5_hex( lc $email )
. "?d=404&size="
. $size;
warn "fetching image for '$author' $email ($grav_url)...\n";
warn "fetching image for '$author' $email ($grav_url)...\n";
my $rc = getstore($grav_url, $author_image_file);
my $rc = getstore( $grav_url, $author_image_file );
sleep(1);
sleep(1);
if($rc != 200) {
unlink($author_image_file);
next;
}
if ( $rc != 200 ) {
unlink($author_image_file);
next;
}
}
close GITLOG;
close $GITLOG;

File diff suppressed because it is too large Load Diff

View File

@@ -5,223 +5,288 @@
# \author (c)2005-2014 Laurent Destailleur <eldy@users.sourceforge.net>
# \contributor (c)2017 Nicolas ZABOURI <info@inovea-conseil.com>
#----------------------------------------------------------------------------
## no critic (InputOutput::ProhibitExplicitStdin,InputOutput::RequireBriefOpen)
use strict;
use warnings;
use Cwd;
use Term::ANSIColor;
$OWNER="ldestailleur";
$GROUP="ldestailleur";
$OWNER = "ldestailleur";
$GROUP = "ldestailleur";
@LISTETARGET=("ZIP"); # Possible packages
%REQUIREMENTTARGET=( # Tool requirement for each package
"TGZ"=>"tar",
"ZIP"=>"7z"
@LISTETARGET = ("ZIP"); # Possible packages
%REQUIREMENTTARGET = ( # Tool requirement for each package
"TGZ" => "tar",
"ZIP" => "7z"
);
%ALTERNATEPATH=(
);
%ALTERNATEPATH = ();
use vars qw/ $REVISION $VERSION /;
$REVISION='1.0';
$VERSION="3.5 (build $REVISION)";
$REVISION = '1.0';
$VERSION = "3.5 (build $REVISION)";
#------------------------------------------------------------------------------
# MAIN
#------------------------------------------------------------------------------
($DIR=$0) =~ s/([^\/\\]+)$//; ($PROG=$1) =~ s/\.([^\.]*)$//; $Extension=$1;
$DIR||='.'; $DIR =~ s/([^\/\\])[\\\/]+$/$1/;
( $DIR = $0 ) =~ s/([^\/\\]+)$//;
( $PROG = $1 ) =~ s/\.([^\.]*)$//;
$Extension = $1;
$DIR ||= '.';
$DIR =~ s/([^\/\\])[\\\/]+$/$1/;
# Detect OS type
# --------------
if ("$^O" =~ /linux/i || (-d "/etc" && -d "/var" && "$^O" !~ /cygwin/i)) { $OS='linux'; $CR=''; }
elsif (-d "/etc" && -d "/Users") { $OS='macosx'; $CR=''; }
elsif ("$^O" =~ /cygwin/i || "$^O" =~ /win32/i) { $OS='windows'; $CR="\r"; }
if (! $OS) {
print "$PROG.$Extension was not able to detect your OS.\n";
if ( "$^O" =~ /linux/i || ( -d "/etc" && -d "/var" && "$^O" !~ /cygwin/i ) ) {
$OS = 'linux';
$CR = '';
}
elsif ( -d "/etc" && -d "/Users" ) { $OS = 'macosx'; $CR = ''; }
elsif ( "$^O" =~ /cygwin/i || "$^O" =~ /win32/i ) {
$OS = 'windows';
$CR = "\r";
}
if ( !$OS ) {
print "$PROG.$Extension was not able to detect your OS.\n";
print "Can't continue.\n";
print "$PROG.$Extension aborted.\n";
sleep 2;
sleep 2;
exit 1;
}
# Define buildroot
# ----------------
if ($OS =~ /linux/) {
$TEMP=$ENV{"TEMP"}||$ENV{"TMP"}||"/tmp";
if ( $OS =~ /linux/ ) {
$TEMP = $ENV{"TEMP"} || $ENV{"TMP"} || "/tmp";
}
if ($OS =~ /macos/) {
$TEMP=$ENV{"TEMP"}||$ENV{"TMP"}||"/tmp";
if ( $OS =~ /macos/ ) {
$TEMP = $ENV{"TEMP"} || $ENV{"TMP"} || "/tmp";
}
if ($OS =~ /windows/) {
$TEMP=$ENV{"TEMP"}||$ENV{"TMP"}||"c:/temp";
$PROGPATH=$ENV{"ProgramFiles"};
if ( $OS =~ /windows/ ) {
$TEMP = $ENV{"TEMP"} || $ENV{"TMP"} || "c:/temp";
$PROGPATH = $ENV{"ProgramFiles"};
}
if (! $TEMP || ! -d $TEMP) {
print "Error: A temporary directory can not be find.\n";
print "Check that TEMP or TMP environment variable is set correctly.\n";
if ( !$TEMP || !-d $TEMP ) {
print "Error: A temporary directory can not be find.\n";
print "Check that TEMP or TMP environment variable is set correctly.\n";
print "$PROG.$Extension aborted.\n";
sleep 2;
exit 2;
sleep 2;
exit 2;
}
$BUILDROOT="$TEMP/dolibarr-buildroot";
$BUILDROOT = "$TEMP/dolibarr-buildroot";
my $copyalreadydone = 0;
my $batch = 0;
my $copyalreadydone=0;
my $batch=0;
for (0..@ARGV-1) {
if ($ARGV[$_] =~ /^-*target=(\w+)/i) { $target=$1; $batch=1; }
if ($ARGV[$_] =~ /^-*desti=(.+)/i) { $DESTI=$1; }
if ($ARGV[$_] =~ /^-*prefix=(.+)/i) {
$PREFIX=$1;
$FILENAMESNAPSHOT.="-".$PREFIX;
}
for ( 0 .. @ARGV - 1 ) {
if ( $ARGV[$_] =~ /^-*target=(\w+)/i ) { $target = $1; $batch = 1; }
if ( $ARGV[$_] =~ /^-*desti=(.+)/i ) { $DESTI = $1; }
if ( $ARGV[$_] =~ /^-*prefix=(.+)/i ) {
$PREFIX = $1;
$FILENAMESNAPSHOT .= "-" . $PREFIX;
}
}
$SOURCE="$DIR/../..";
$DESTI="$SOURCE/dev/build";
if ($ENV{"DESTIMODULES"}) { $DESTI = $ENV{"DESTIMODULES"}; } # Force output dir if env DESTIMODULES is defined
$NEWDESTI=$DESTI;
$SOURCE = "$DIR/../..";
$DESTI = "$SOURCE/dev/build";
if ( $ENV{"DESTIMODULES"} ) {
$DESTI = $ENV{"DESTIMODULES"};
} # Force output dir if env DESTIMODULES is defined
$NEWDESTI = $DESTI;
print "Makepack for modules version $VERSION\n";
print "Source directory: $SOURCE\n";
print "Target directory: $NEWDESTI\n";
# Ask module
print "Enter name for your module (mymodule, mywonderfulmondule, ... or 'all') : ";
$PROJECTINPUT=<STDIN>;
print
"Enter name for your module (mymodule, mywonderfullmodule, ... or 'all') : ";
my $PROJECTINPUT = <STDIN>;
chomp($PROJECTINPUT);
print "Move to ".$DIR." directory.\n";
print "Move to " . $DIR . " directory.\n";
chdir($DIR);
my @PROJECTLIST=();
if ($PROJECTINPUT eq "all")
{
opendir(DIR, $DIR) || return;
local @rv = grep { /^makepack\-(.*)\.conf$/ } sort readdir(DIR);
closedir(DIR);
foreach my $xxx (0..@rv-1) {
if ($rv[$xxx] =~ /^makepack\-(.*)\.conf$/)
{
@PROJECTLIST[$xxx]=$1;
}
}
my @PROJECTLIST = ();
if ( $PROJECTINPUT eq "all" ) {
opendir( my $DIR, $DIR ) or return;
local @rv = grep { /^makepack\-(.*)\.conf$/ } sort readdir($DIR);
closedir($DIR);
foreach my $xxx ( 0 .. @rv - 1 ) {
if ( $rv[$xxx] =~ /^makepack\-(.*)\.conf$/ ) {
@PROJECTLIST[$xxx] = $1;
}
}
}
else
{
@PROJECTLIST=($PROJECTINPUT);
else {
@PROJECTLIST = ($PROJECTINPUT);
}
# Loop on each projects
foreach my $PROJECT (@PROJECTLIST) {
$PROJECTLC=lc($PROJECT);
$PROJECTLC = lc($PROJECT);
if (! -f "makepack-".$PROJECT.".conf")
{
print "Error: can't open conf file makepack-".$PROJECT.".conf\n";
if ( !-f "makepack-" . $PROJECT . ".conf" ) {
print "Error: can't open conf file makepack-" . $PROJECT . ".conf\n";
print "\n";
print "For help on building a module package, see web page\n";
print "http://wiki.dolibarr.org/index.php/Module_development#Create_a_package_to_distribute_and_install_your_module\n";
print
"http://wiki.dolibarr.org/index.php/Module_development#Create_a_package_to_distribute_and_install_your_module\n";
print "makepack-dolibarrmodule.pl aborted.\n";
sleep 2;
exit 2;
sleep 2;
exit 2;
}
# Get version $MAJOR, $MINOR and $BUILD
print "Version detected for module ".$PROJECT." in file ".$SOURCE."/htdocs/".$PROJECTLC."/core/modules/mod".ucfirst($PROJECT).".class.php";
$result=open(IN,"<".$SOURCE."/htdocs/".$PROJECTLC."/core/modules/mod".ucfirst($PROJECT).".class.php");
$custom=false;
if (! $result) {
$result=open(IN,"<".$SOURCE."/htdocs/custom/".$PROJECTLC."/core/modules/mod".ucfirst($PROJECT).".class.php");
if (! $result) {
die "Error: Can't open descriptor file ".$SOURCE."/htdocs/(or /htdocs/custom/)".$PROJECTLC."/core/modules/mod".ucfirst($PROJECT).".class.php for reading.\n";
}else{
$custom = true;
}
}
while(<IN>)
{
if ($_ =~ /this->version\s*=\s*'([\d\.]+)'/) { $PROJVERSION=$1; break; }
}
close IN;
print $PROJVERSION."\n";
print "Version detected for module "
. $PROJECT
. " in file "
. $SOURCE
. "/htdocs/"
. $PROJECTLC
. "/core/modules/mod"
. ucfirst($PROJECT)
. ".class.php";
$result = open(
my $IN,
"<",
$SOURCE
. "/htdocs/"
. $PROJECTLC
. "/core/modules/mod"
. ucfirst($PROJECT)
. ".class.php"
);
$custom = false;
if ( !$result ) {
$result = open(
my $IN,
"<",
$SOURCE
. "/htdocs/custom/"
. $PROJECTLC
. "/core/modules/mod"
. ucfirst($PROJECT)
. ".class.php"
);
if ( !$result ) {
die "Error: Can't open descriptor file "
. $SOURCE
. "/htdocs/(or /htdocs/custom/)"
. $PROJECTLC
. "/core/modules/mod"
. ucfirst($PROJECT)
. ".class.php for reading.\n";
}
}
else {
$custom = true;
}
while (<$IN>) {
if ( $_ =~ /this->version\s*=\s*'([\d\.]+)'/ ) {
$PROJVERSION = $1;
break;
}
}
close $IN;
print $PROJVERSION. "\n";
($MAJOR,$MINOR,$BUILD)=split(/\./,$PROJVERSION,3);
if ($MINOR eq '')
{
print "Enter value for minor version for module ".$PROJECT.": ";
$MINOR=<STDIN>;
chomp($MINOR);
( $MAJOR, $MINOR, $BUILD ) = split( /\./, $PROJVERSION, 3 );
if ( $MINOR eq '' ) {
print "Enter value for minor version for module " . $PROJECT . ": ";
$MINOR = <STDIN>;
chomp($MINOR);
}
$FILENAME="$PROJECTLC";
$FILENAMETGZ="module_$PROJECTLC-$MAJOR.$MINOR".($BUILD ne ''?".$BUILD":"");
$FILENAMEZIP="module_$PROJECTLC-$MAJOR.$MINOR".($BUILD ne ''?".$BUILD":"");
if (-d "/usr/src/redhat") {
# redhat
$RPMDIR="/usr/src/redhat";
}
if (-d "/usr/src/RPM") {
# mandrake
$RPMDIR="/usr/src/RPM";
}
$FILENAME = "$PROJECTLC";
$FILENAMETGZ =
"module_$PROJECTLC-$MAJOR.$MINOR" . ( $BUILD ne '' ? ".$BUILD" : "" );
$FILENAMEZIP =
"module_$PROJECTLC-$MAJOR.$MINOR" . ( $BUILD ne '' ? ".$BUILD" : "" );
if ( -d "/usr/src/redhat" ) {
# redhat
$RPMDIR = "/usr/src/redhat";
}
if ( -d "/usr/src/RPM" ) {
# mandrake
$RPMDIR = "/usr/src/RPM";
}
# Choose package targets
#-----------------------
$target="ZIP"; # Dolibarr modules are this format
$CHOOSEDTARGET{uc($target)}=1;
$target = "ZIP"; # Dolibarr modules are this format
$CHOOSEDTARGET{ uc($target) } = 1;
# Test if requirement is ok
#--------------------------
foreach my $target (keys %CHOOSEDTARGET) {
foreach my $req (split(/[,\s]/,$REQUIREMENTTARGET{$target})) {
# Test
print "Test requirement for target $target: Search '$req'... ";
$ret=`"$req" 2>&1`;
$coderetour=$?; $coderetour2=$coderetour>>8;
if ($coderetour != 0 && (($coderetour2 == 1 && $OS =~ /windows/ && $ret !~ /Usage/i) || ($coderetour2 == 127 && $OS !~ /windows/)) && $PROGPATH) {
# Not found error, we try in PROGPATH
$ret=`"$PROGPATH/$ALTERNATEPATH{$req}/$req\" 2>&1`;
$coderetour=$?; $coderetour2=$coderetour>>8;
$REQUIREMENTTARGET{$target}="$PROGPATH/$ALTERNATEPATH{$req}/$req";
}
foreach my $target ( keys %CHOOSEDTARGET ) {
foreach my $req ( split( /[,\s]/, $REQUIREMENTTARGET{$target} ) ) {
if ($coderetour != 0 && (($coderetour2 == 1 && $OS =~ /windows/ && $ret !~ /Usage/i) || ($coderetour2 == 127 && $OS !~ /windows/))) {
# Not found error
print "Not found\nCan't build target $target. Requirement '$req' not found in PATH\n";
$CHOOSEDTARGET{$target}=-1;
last;
} else {
# Pas erreur ou erreur autre que programme absent
print " Found ".$REQUIREMENTTARGET{$target}."\n";
}
}
# Test
print "Test requirement for target $target: Search '$req'... ";
$ret = `"$req" 2>&1`;
$coderetour = $?;
$coderetour2 = $coderetour >> 8;
if (
$coderetour != 0
&& (
(
$coderetour2 == 1
&& $OS =~ /windows/
&& $ret !~ /Usage/i
)
|| ( $coderetour2 == 127 && $OS !~ /windows/ )
)
&& $PROGPATH
)
{
# Not found error, we try in PROGPATH
$ret = `"$PROGPATH/$ALTERNATEPATH{$req}/$req\" 2>&1`;
$coderetour = $?;
$coderetour2 = $coderetour >> 8;
$REQUIREMENTTARGET{$target} =
"$PROGPATH/$ALTERNATEPATH{$req}/$req";
}
if (
$coderetour != 0
&& (
(
$coderetour2 == 1
&& $OS =~ /windows/
&& $ret !~ /Usage/i
)
|| ( $coderetour2 == 127 && $OS !~ /windows/ )
)
)
{
# Not found error
print
"Not found\nCan't build target $target. Requirement '$req' not found in PATH\n";
$CHOOSEDTARGET{$target} = -1;
last;
}
else {
# Pas erreur ou erreur autre que programme absent
print " Found " . $REQUIREMENTTARGET{$target} . "\n";
}
}
}
print "\n";
# Check if there is at least on target to build
# Check if there is at least one target to build
#----------------------------------------------
$nboftargetok=0;
$nboftargetneedbuildroot=0;
$nboftargetneedcvs=0;
foreach my $target (keys %CHOOSEDTARGET) {
if ($CHOOSEDTARGET{$target} < 0) { next; }
if ($target ne 'EXE' && $target ne 'EXEDOLIWAMP')
{
$nboftargetok = 0;
$nboftargetneedbuildroot = 0;
$nboftargetneedcvs = 0;
foreach my $target ( keys %CHOOSEDTARGET ) {
if ( $CHOOSEDTARGET{$target} < 0 ) { next; }
if ( $target ne 'EXE' && $target ne 'EXEDOLIWAMP' ) {
$nboftargetneedbuildroot++;
}
if ($target eq 'SNAPSHOT')
{
if ( $target eq 'SNAPSHOT' ) {
$nboftargetneedcvs++;
}
$nboftargetok++;
@@ -229,178 +294,211 @@ foreach my $PROJECT (@PROJECTLIST) {
if ($nboftargetok) {
# Update CVS if required
#-----------------------
if ($nboftargetneedcvs)
{
print "Go to directory $SOURCE\n";
$olddir=getcwd();
chdir("$SOURCE");
print "Run cvs update -P -d\n";
$ret=`cvs update -P -d 2>&1`;
chdir("$olddir");
# Update CVS if required
#-----------------------
if ($nboftargetneedcvs) {
print "Go to directory $SOURCE\n";
$olddir = getcwd();
chdir("$SOURCE");
print "Run cvs update -P -d\n";
$ret = `cvs update -P -d 2>&1`;
chdir("$olddir");
}
# Update buildroot if required
#-----------------------------
if ($nboftargetneedbuildroot)
{
if (! $copyalreadydone) {
print "Delete directory $BUILDROOT\n";
$ret=`rm -fr "$BUILDROOT"`;
# Update buildroot if required
#-----------------------------
if ($nboftargetneedbuildroot) {
if ( !$copyalreadydone ) {
print "Delete directory $BUILDROOT\n";
$ret = `rm -fr "$BUILDROOT"`;
mkdir "$BUILDROOT";
mkdir "$BUILDROOT/$PROJECTLC";
mkdir "$BUILDROOT";
mkdir "$BUILDROOT/$PROJECTLC";
print "Now, we will copy all files declared in the makepack-".$PROJECT.".conf into the directory $BUILDROOT\n";
print "Now, we will copy all files declared in the makepack-"
. $PROJECT
. ".conf into the directory $BUILDROOT\n";
$result=open(IN,"<makepack-".$PROJECT.".conf");
if (! $result) { die "Error: Can't open conf file makepack-".$PROJECT.".conf for reading.\n"; }
while(<IN>)
{
$entry=$_;
open( my $IN2, "<", "makepack-" . $PROJECT . ".conf" )
or die "Error: Can't open conf file makepack-"
. $PROJECT
. ".conf for reading.\n";
while (<$IN2>) {
$entry = $_;
if ($entry =~ /^#/) { next; } # Do not process comments
if ( $entry =~ /^#/ ) { next; } # Do not process comments
$entry =~ s/\n//;
if ($entry =~ /^!(.*)$/) # Exclude so remove file/dir
{
print "Remove $BUILDROOT/$PROJECTLC/$1\n";
$ret=`rm -fr "$BUILDROOT/$PROJECTLC/"$1`;
if ($? != 0) { die "Failed to delete a file to exclude declared into makepack-".$PROJECT.".conf file (Failed on the line ".$entry.")\n"; }
next;
}
if ( $entry =~ /^!(.*)$/ ) # Exclude so remove file/dir
{
print "Remove $BUILDROOT/$PROJECTLC/$1\n";
$ret = `rm -fr "$BUILDROOT/$PROJECTLC/"$1`;
if ( $? != 0 ) {
die
"Failed to delete a file to exclude declared into makepack-"
. $PROJECT
. ".conf file (Failed on the line "
. $entry . ")\n";
}
next;
}
$entry =~ /^(.*)\/[^\/]+/;
print "Create directory $BUILDROOT/$PROJECTLC/$1\n";
$ret=`mkdir -p "$BUILDROOT/$PROJECTLC/$1"`;
if ($entry !~ /version\-/)
{
print "Copy $SOURCE/$entry into $BUILDROOT/$PROJECTLC/$entry\n";
$ret=`cp -pr "$SOURCE/$entry" "$BUILDROOT/$PROJECTLC/$entry"`;
if ($? != 0) { die "Failed to make copy of a file declared into makepack-".$PROJECT.".conf file (Failed on the line '".$entry."')\n"; }
}
print "Create directory $BUILDROOT/$PROJECTLC/$1\n";
$ret = `mkdir -p "$BUILDROOT/$PROJECTLC/$1"`;
if ( $entry !~ /version\-/ ) {
print
"Copy $SOURCE/$entry into $BUILDROOT/$PROJECTLC/$entry\n";
$ret =
`cp -pr "$SOURCE/$entry" "$BUILDROOT/$PROJECTLC/$entry"`;
if ( $? != 0 ) {
die
"Failed to make copy of a file declared into makepack-"
. $PROJECT
. ".conf file (Failed on the line '"
. $entry . "')\n";
}
}
}
close IN;
close $IN2;
@timearray=localtime(time());
$fulldate=($timearray[5]+1900).'-'.($timearray[4]+1).'-'.$timearray[3].' '.$timearray[2].':'.$timearray[1];
#open(VF,">$BUILDROOT/$PROJECTLC/dev/build/version-".$PROJECTLC.".txt");
#print "Create version file $BUILDROOT/$PROJECTLC/dev/build/version-".$PROJECTLC.".txt with date ".$fulldate."\n";
#$ret=`mkdir -p "$BUILDROOT/$PROJECTLC/dev/build"`;
#print VF "Version: ".$MAJOR.".".$MINOR.($BUILD ne ''?".$BUILD":"")."\n";
#print VF "Build : ".$fulldate."\n";
#close VF;
}
print "Clean $BUILDROOT\n";
$ret=`rm -fr $BUILDROOT/$PROJECTLC/.cache`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/.git`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/.project`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/.settings`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/index.php`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/dev/build/html`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/documents`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/document`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/htdocs/conf/conf.php.mysql`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/htdocs/conf/conf.php.old`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/htdocs/conf/conf.php.postgres`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/htdocs/conf/conf*sav*`;
if ($custom) {
$ret=`cp -r $BUILDROOT/$PROJECTLC/htdocs/custom/* $BUILDROOT/$PROJECTLC/htdocs/.`;
}
$ret=`rm -fr $BUILDROOT/$PROJECTLC/htdocs/custom`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/htdocs/custom2`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/test`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/Thumbs.db $BUILDROOT/$PROJECTLC/*/Thumbs.db $BUILDROOT/$PROJECTLC/*/*/Thumbs.db $BUILDROOT/$PROJECTLC/*/*/*/Thumbs.db $BUILDROOT/$PROJECTLC/*/*/*/*/Thumbs.db`;
$ret=`rm -fr $BUILDROOT/$PROJECTLC/CVS* $BUILDROOT/$PROJECTLC/*/CVS* $BUILDROOT/$PROJECTLC/*/*/CVS* $BUILDROOT/$PROJECTLC/*/*/*/CVS* $BUILDROOT/$PROJECTLC/*/*/*/*/CVS* $BUILDROOT/$PROJECTLC/*/*/*/*/*/CVS*`;
@timearray = localtime( time() );
$fulldate =
( $timearray[5] + 1900 ) . '-'
. ( $timearray[4] + 1 ) . '-'
. $timearray[3] . ' '
. $timearray[2] . ':'
. $timearray[1];
#open(VF,">$BUILDROOT/$PROJECTLC/dev/build/version-".$PROJECTLC.".txt");
#print "Create version file $BUILDROOT/$PROJECTLC/dev/build/version-".$PROJECTLC.".txt with date ".$fulldate."\n";
#$ret=`mkdir -p "$BUILDROOT/$PROJECTLC/dev/build"`;
#print VF "Version: ".$MAJOR.".".$MINOR.($BUILD ne ''?".$BUILD":"")."\n";
#print VF "Build : ".$fulldate."\n";
#close VF;
}
print "Clean $BUILDROOT\n";
$ret = `rm -fr $BUILDROOT/$PROJECTLC/.cache`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/.git`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/.project`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/.settings`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/index.php`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/dev/build/html`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/documents`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/document`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/htdocs/conf/conf.php.mysql`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/htdocs/conf/conf.php.old`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/htdocs/conf/conf.php.postgres`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/htdocs/conf/conf*sav*`;
if ($custom) {
$ret =
`cp -r $BUILDROOT/$PROJECTLC/htdocs/custom/* $BUILDROOT/$PROJECTLC/htdocs/.`;
}
$ret = `rm -fr $BUILDROOT/$PROJECTLC/htdocs/custom`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/htdocs/custom2`;
$ret = `rm -fr $BUILDROOT/$PROJECTLC/test`;
$ret =
`rm -fr $BUILDROOT/$PROJECTLC/Thumbs.db $BUILDROOT/$PROJECTLC/*/Thumbs.db $BUILDROOT/$PROJECTLC/*/*/Thumbs.db $BUILDROOT/$PROJECTLC/*/*/*/Thumbs.db $BUILDROOT/$PROJECTLC/*/*/*/*/Thumbs.db`;
$ret =
`rm -fr $BUILDROOT/$PROJECTLC/CVS* $BUILDROOT/$PROJECTLC/*/CVS* $BUILDROOT/$PROJECTLC/*/*/CVS* $BUILDROOT/$PROJECTLC/*/*/*/CVS* $BUILDROOT/$PROJECTLC/*/*/*/*/CVS* $BUILDROOT/$PROJECTLC/*/*/*/*/*/CVS*`;
}
# Build package for each target
#------------------------------
foreach my $target (keys %CHOOSEDTARGET) {
if ($CHOOSEDTARGET{$target} < 0) { next; }
# Build package for each target
#------------------------------
foreach my $target ( keys %CHOOSEDTARGET ) {
if ( $CHOOSEDTARGET{$target} < 0 ) { next; }
print "\nBuild package for target $target\n";
print "\nBuild package for target $target\n";
if ($target eq 'TGZ') {
$NEWDESTI=$DESTI;
if (-d $DESTI.'/../modules') { $NEWDESTI=$DESTI.'/../modules'; }
if ( $target eq 'TGZ' ) {
$NEWDESTI = $DESTI;
if ( -d $DESTI . '/../modules' ) {
$NEWDESTI = $DESTI . '/../modules';
}
print "Remove target $FILENAMETGZ.tgz...\n";
unlink("$NEWDESTI/$FILENAMETGZ.tgz");
print "Compress $BUILDROOT/* into $FILENAMETGZ.tgz...\n";
$cmd="tar --exclude-vcs --exclude *.tgz --directory \"$BUILDROOT\" --mode=go-w --group=500 --owner=500 -czvf \"$FILENAMETGZ.tgz\" .";
$ret=`$cmd`;
if ($OS =~ /windows/i) {
print "Move $FILENAMETGZ.tgz to $NEWDESTI/$FILENAMETGZ.tgz\n";
$ret=`mv "$FILENAMETGZ.tgz" "$NEWDESTI/$FILENAMETGZ.tgz"`;
}
else
{
$ret=`mv "$FILENAMETGZ.tgz" "$NEWDESTI/$FILENAMETGZ.tgz"`;
}
next;
}
print "Remove target $FILENAMETGZ.tgz...\n";
unlink("$NEWDESTI/$FILENAMETGZ.tgz");
print "Compress $BUILDROOT/* into $FILENAMETGZ.tgz...\n";
$cmd =
"tar --exclude-vcs --exclude *.tgz --directory \"$BUILDROOT\" --mode=go-w --group=500 --owner=500 -czvf \"$FILENAMETGZ.tgz\" .";
$ret = `$cmd`;
if ( $OS =~ /windows/i ) {
print
"Move $FILENAMETGZ.tgz to $NEWDESTI/$FILENAMETGZ.tgz\n";
$ret = `mv "$FILENAMETGZ.tgz" "$NEWDESTI/$FILENAMETGZ.tgz"`;
}
else {
$ret = `mv "$FILENAMETGZ.tgz" "$NEWDESTI/$FILENAMETGZ.tgz"`;
}
next;
}
if ($target eq 'ZIP') {
$NEWDESTI=$DESTI;
if (-d $DESTI.'/../modules') { $NEWDESTI=$DESTI.'/../modules'; }
if ( $target eq 'ZIP' ) {
$NEWDESTI = $DESTI;
if ( -d $DESTI . '/../modules' ) {
$NEWDESTI = $DESTI . '/../modules';
}
print "Remove target $FILENAMEZIP.zip...\n";
unlink "$NEWDESTI/$FILENAMEZIP.zip";
print "Compress $FILENAMEZIP into $FILENAMEZIP.zip...\n";
print "Remove target $FILENAMEZIP.zip...\n";
unlink "$NEWDESTI/$FILENAMEZIP.zip";
print "Compress $FILENAMEZIP into $FILENAMEZIP.zip...\n";
print "Go to directory $BUILDROOT/$PROJECTLC\n";
$olddir=getcwd();
chdir("$BUILDROOT/$PROJECTLC");
$cmd= "7z a -r -tzip -mx $BUILDROOT/$FILENAMEZIP.zip *";
print $cmd."\n";
$ret= `$cmd`;
chdir("$olddir");
print "Go to directory $BUILDROOT/$PROJECTLC\n";
$olddir = getcwd();
chdir("$BUILDROOT/$PROJECTLC");
$cmd = "7z a -r -tzip -mx $BUILDROOT/$FILENAMEZIP.zip *";
print $cmd. "\n";
$ret = `$cmd`;
chdir("$olddir");
print "Move $FILENAMEZIP.zip to $NEWDESTI/$FILENAMEZIP.zip\n";
$ret=`mv "$BUILDROOT/$FILENAMEZIP.zip" "$NEWDESTI/$FILENAMEZIP.zip"`;
$ret=`chown $OWNER:$GROUP "$NEWDESTI/$FILENAMEZIP.zip"`;
next;
}
print "Move $FILENAMEZIP.zip to $NEWDESTI/$FILENAMEZIP.zip\n";
$ret =
`mv "$BUILDROOT/$FILENAMEZIP.zip" "$NEWDESTI/$FILENAMEZIP.zip"`;
$ret = `chown $OWNER:$GROUP "$NEWDESTI/$FILENAMEZIP.zip"`;
next;
}
if ($target eq 'EXE') {
$NEWDESTI=$DESTI;
if (-d $DESTI.'/../modules') { $NEWDESTI=$DESTI.'/../modules'; }
if ( $target eq 'EXE' ) {
$NEWDESTI = $DESTI;
if ( -d $DESTI . '/../modules' ) {
$NEWDESTI = $DESTI . '/../modules';
}
print "Remove target $FILENAMEEXE.exe...\n";
unlink "$NEWDESTI/$FILENAMEEXE.exe";
print "Compress into $FILENAMEEXE.exe by $FILENAMEEXE.nsi...\n";
$command="\"$REQUIREMENTTARGET{$target}\" /DMUI_VERSION_DOT=$MAJOR.$MINOR.$BUILD /X\"SetCompressor bzip2\" \"$SOURCE\\dev\\build\\exe\\$FILENAME.nsi\"";
print "$command\n";
$ret=`$command`;
print "Move $FILENAMEEXE.exe to $NEWDESTI\n";
rename("$SOURCE\\dev\\build\\exe\\$FILENAMEEXE.exe","$NEWDESTI/$FILENAMEEXE.exe");
next;
}
print "Remove target $FILENAMEEXE.exe...\n";
unlink "$NEWDESTI/$FILENAMEEXE.exe";
print "Compress into $FILENAMEEXE.exe by $FILENAMEEXE.nsi...\n";
$command =
"\"$REQUIREMENTTARGET{$target}\" /DMUI_VERSION_DOT=$MAJOR.$MINOR.$BUILD /X\"SetCompressor bzip2\" \"$SOURCE\\dev\\build\\exe\\$FILENAME.nsi\"";
print "$command\n";
$ret = `$command`;
print "Move $FILENAMEEXE.exe to $NEWDESTI\n";
rename( "$SOURCE\\dev\\build\\exe\\$FILENAMEEXE.exe",
"$NEWDESTI/$FILENAMEEXE.exe" );
next;
}
}
}
}
print "\n----- Summary -----\n";
foreach my $target (keys %CHOOSEDTARGET) {
if ($CHOOSEDTARGET{$target} < 0) {
print "Package $target not built (bad requirement).\n";
} else {
print "Package $target built successfully in $NEWDESTI\n";
}
foreach my $target ( keys %CHOOSEDTARGET ) {
if ( $CHOOSEDTARGET{$target} < 0 ) {
print "Package $target not built (bad requirement).\n";
}
else {
print "Package $target built successfully in $NEWDESTI\n";
}
}
}
if (! $batch) {
print "\nPress key to finish...";
my $WAITKEY=<STDIN>;
if ( !$batch ) {
print "\nPress key to finish...";
my $WAITKEY = <STDIN>;
}
0;

View File

@@ -4,268 +4,294 @@
# \brief Script to build a theme Package for Dolibarr
# \author (c)2005-2009 Laurent Destailleur <eldy@users.sourceforge.net>
#-----------------------------------------------------------------------------
## no critic (InputOutput::ProhibitExplicitStdin)
use strict;
use warnings;
use Cwd;
use Term::ANSIColor;
$PROJECT="dolibarr";
$PROJECT = "dolibarr";
@LISTETARGET=("TGZ"); # Possible packages
%REQUIREMENTTARGET=( # Tool requirement for each package
"TGZ"=>"tar",
"ZIP"=>"7z",
"RPM"=>"rpmbuild",
"DEB"=>"dpkg-buildpackage",
"EXE"=>"makensis.exe"
@LISTETARGET = ("TGZ"); # Possible packages
%REQUIREMENTTARGET = ( # Tool requirement for each package
"TGZ" => "tar",
"ZIP" => "7z",
"RPM" => "rpmbuild",
"DEB" => "dpkg-buildpackage",
"EXE" => "makensis.exe"
);
%ALTERNATEPATH=(
"7z"=>"7-ZIP",
"makensis.exe"=>"NSIS"
%ALTERNATEPATH = (
"7z" => "7-ZIP",
"makensis.exe" => "NSIS"
);
use vars qw/ $REVISION $VERSION /;
$REVISION='1.11';
$VERSION="1.0 (build $REVISION)";
$REVISION = '1.11';
$VERSION = "1.0 (build $REVISION)";
#------------------------------------------------------------------------------
# MAIN
#------------------------------------------------------------------------------
($DIR=$0) =~ s/([^\/\\]+)$//; ($PROG=$1) =~ s/\.([^\.]*)$//; $Extension=$1;
$DIR||='.'; $DIR =~ s/([^\/\\])[\\\/]+$/$1/;
( $DIR = $0 ) =~ s/([^\/\\]+)$//;
( $PROG = $1 ) =~ s/\.([^\.]*)$//;
$Extension = $1;
$DIR ||= '.';
$DIR =~ s/([^\/\\])[\\\/]+$/$1/;
# Detect OS type
# --------------
if ("$^O" =~ /linux/i || (-d "/etc" && -d "/var" && "$^O" !~ /cygwin/i)) { $OS='linux'; $CR=''; }
elsif (-d "/etc" && -d "/Users") { $OS='macosx'; $CR=''; }
elsif ("$^O" =~ /cygwin/i || "$^O" =~ /win32/i) { $OS='windows'; $CR="\r"; }
if (! $OS) {
print "$PROG.$Extension was not able to detect your OS.\n";
if ( "$^O" =~ /linux/i || ( -d "/etc" && -d "/var" && "$^O" !~ /cygwin/i ) ) {
$OS = 'linux';
$CR = '';
}
elsif ( -d "/etc" && -d "/Users" ) { $OS = 'macosx'; $CR = ''; }
elsif ( "$^O" =~ /cygwin/i || "$^O" =~ /win32/i ) {
$OS = 'windows';
$CR = "\r";
}
if ( !$OS ) {
print "$PROG.$Extension was not able to detect your OS.\n";
print "Can't continue.\n";
print "$PROG.$Extension aborted.\n";
sleep 2;
sleep 2;
exit 1;
}
# Define buildroot
# ----------------
if ($OS =~ /linux/) {
$TEMP=$ENV{"TEMP"}||$ENV{"TMP"}||"/tmp";
if ( $OS =~ /linux/ ) {
$TEMP = $ENV{"TEMP"} || $ENV{"TMP"} || "/tmp";
}
if ($OS =~ /macos/) {
$TEMP=$ENV{"TEMP"}||$ENV{"TMP"}||"/tmp";
if ( $OS =~ /macos/ ) {
$TEMP = $ENV{"TEMP"} || $ENV{"TMP"} || "/tmp";
}
if ($OS =~ /windows/) {
$TEMP=$ENV{"TEMP"}||$ENV{"TMP"}||"c:/temp";
$PROGPATH=$ENV{"ProgramFiles"};
if ( $OS =~ /windows/ ) {
$TEMP = $ENV{"TEMP"} || $ENV{"TMP"} || "c:/temp";
$PROGPATH = $ENV{"ProgramFiles"};
}
if (! $TEMP || ! -d $TEMP) {
print "Error: A temporary directory can not be find.\n";
print "Check that TEMP or TMP environment variable is set correctly.\n";
if ( !$TEMP || !-d $TEMP ) {
print "Error: A temporary directory can not be find.\n";
print "Check that TEMP or TMP environment variable is set correctly.\n";
print "makepack-dolibarrtheme.pl aborted.\n";
sleep 2;
exit 2;
sleep 2;
exit 2;
}
$BUILDROOT="$TEMP/dolibarr-buildroot";
$BUILDROOT = "$TEMP/dolibarr-buildroot";
my $copyalreadydone=0;
my $batch=0;
my $copyalreadydone = 0;
my $batch = 0;
print "Makepack theme version $VERSION\n";
print "Enter name of theme(s) to package (separated with space): ";
$PROJECT=<STDIN>;
$PROJECT = <STDIN>;
chomp($PROJECT);
@PROJECTLIST=split(/ /,$PROJECT);
$PROJECT=join('',@PROJECTLIST);
@PROJECTLIST = split( / /, $PROJECT );
$PROJECT = join( '', @PROJECTLIST );
# Ask and set version $MAJOR and $MINOR
print "Enter value for version: ";
$PROJVERSION=<STDIN>;
$PROJVERSION = <STDIN>;
chomp($PROJVERSION);
($MAJOR,$MINOR)=split(/\./,$PROJVERSION,2);
if ($MINOR eq '')
{
( $MAJOR, $MINOR ) = split( /\./, $PROJVERSION, 2 );
if ( $MINOR eq '' ) {
print "Enter value for minor version: ";
$MINOR=<STDIN>;
$MINOR = <STDIN>;
chomp($MINOR);
}
$FILENAME = "$PROJECT";
$FILENAMETGZ = "theme_$PROJECT-$MAJOR.$MINOR";
$FILENAMEZIP = "theme_$PROJECT-$MAJOR.$MINOR";
$FILENAME="$PROJECT";
$FILENAMETGZ="theme_$PROJECT-$MAJOR.$MINOR";
$FILENAMEZIP="theme_$PROJECT-$MAJOR.$MINOR";
if ( -d "/usr/src/redhat" ) {
if (-d "/usr/src/redhat") {
# redhat
$RPMDIR="/usr/src/redhat";
# redhat
$RPMDIR = "/usr/src/redhat";
}
if (-d "/usr/src/RPM") {
# mandrake
$RPMDIR="/usr/src/RPM";
if ( -d "/usr/src/RPM" ) {
# mandrake
$RPMDIR = "/usr/src/RPM";
}
$SOURCE="$DIR/../..";
$DESTI="$SOURCE/build";
$SOURCE = "$DIR/../..";
$DESTI = "$SOURCE/build";
# Choose package targets
#-----------------------
$target="ZIP"; # Packages uses this format
$target = "ZIP"; # Packages uses this format
if ($target) {
$CHOOSEDTARGET{uc($target)}=1;
$CHOOSEDTARGET{ uc($target) } = 1;
}
else {
my $found=0;
my $NUM_SCRIPT;
while (! $found) {
my $cpt=0;
printf(" %d - %3s (%s)\n",$cpt,"All","Need ".join(",",values %REQUIREMENTTARGET));
foreach my $target (@LISTETARGET) {
$cpt++;
printf(" %d - %3s (%s)\n",$cpt,$target,"Need ".$REQUIREMENTTARGET{$target});
}
my $found = 0;
my $NUM_SCRIPT;
while ( !$found ) {
my $cpt = 0;
printf( " %d - %3s (%s)\n",
$cpt, "All", "Need " . join( ",", values %REQUIREMENTTARGET ) );
foreach my $target (@LISTETARGET) {
$cpt++;
printf( " %d - %3s (%s)\n",
$cpt, $target, "Need " . $REQUIREMENTTARGET{$target} );
}
# Are asked to select the file to move
print "Choose one package number or several separated with space: ";
$NUM_SCRIPT=<STDIN>;
chomp($NUM_SCRIPT);
if ($NUM_SCRIPT =~ s/-//g) {
# Do not do copy
$copyalreadydone=1;
}
if ($NUM_SCRIPT !~ /^[0-$cpt\s]+$/)
{
print "This is not a valid package number list.\n";
$found = 0;
}
else
{
$found = 1;
}
}
print "\n";
if ($NUM_SCRIPT) {
foreach my $num (split(/\s+/,$NUM_SCRIPT)) {
$CHOOSEDTARGET{$LISTETARGET[$num-1]}=1;
}
}
else {
foreach my $key (@LISTETARGET) {
$CHOOSEDTARGET{$key}=1;
}
}
# Are asked to select the file to move
print "Choose one package number or several separated with space: ";
$NUM_SCRIPT = <STDIN>;
chomp($NUM_SCRIPT);
if ( $NUM_SCRIPT =~ s/-//g ) {
# Do not do copy
$copyalreadydone = 1;
}
if ( $NUM_SCRIPT !~ /^[0-$cpt\s]+$/ ) {
print "This is not a valid package number list.\n";
$found = 0;
}
else {
$found = 1;
}
}
print "\n";
if ($NUM_SCRIPT) {
foreach my $num ( split( /\s+/, $NUM_SCRIPT ) ) {
$CHOOSEDTARGET{ $LISTETARGET[ $num - 1 ] } = 1;
}
}
else {
foreach my $key (@LISTETARGET) {
$CHOOSEDTARGET{$key} = 1;
}
}
}
# Test if requirement is ok
#--------------------------
foreach my $target (keys %CHOOSEDTARGET) {
foreach my $req (split(/[,\s]/,$REQUIREMENTTARGET{$target})) {
# Test
print "Test requirement for target $target: Search '$req'... ";
$ret=`"$req" 2>&1`;
$coderetour=$?; $coderetour2=$coderetour>>8;
if ($coderetour != 0 && (($coderetour2 == 1 && $OS =~ /windows/ && $ret !~ /Usage/i) || ($coderetour2 == 127 && $OS !~ /windows/)) && $PROGPATH) {
# Not found error, we try in PROGPATH
$ret=`"$PROGPATH/$ALTERNATEPATH{$req}/$req\" 2>&1`;
$coderetour=$?; $coderetour2=$coderetour>>8;
$REQUIREMENTTARGET{$target}="$PROGPATH/$ALTERNATEPATH{$req}/$req";
}
foreach my $target ( keys %CHOOSEDTARGET ) {
foreach my $req ( split( /[,\s]/, $REQUIREMENTTARGET{$target} ) ) {
if ($coderetour != 0 && (($coderetour2 == 1 && $OS =~ /windows/ && $ret !~ /Usage/i) || ($coderetour2 == 127 && $OS !~ /windows/))) {
# Not found error
print "Not found\nCan't build target $target. Requirement '$req' not found in PATH\n";
$CHOOSEDTARGET{$target}=-1;
last;
} else {
# Pas erreur ou erreur autre que programme absent
print " Found ".$REQUIREMENTTARGET{$target}."\n";
}
}
# Test
print "Test requirement for target $target: Search '$req'... ";
$ret = `"$req" 2>&1`;
$coderetour = $?;
$coderetour2 = $coderetour >> 8;
if (
$coderetour != 0
&& ( ( $coderetour2 == 1 && $OS =~ /windows/ && $ret !~ /Usage/i )
|| ( $coderetour2 == 127 && $OS !~ /windows/ ) )
&& $PROGPATH
)
{
# Not found error, we try in PROGPATH
$ret = `"$PROGPATH/$ALTERNATEPATH{$req}/$req\" 2>&1`;
$coderetour = $?;
$coderetour2 = $coderetour >> 8;
$REQUIREMENTTARGET{$target} = "$PROGPATH/$ALTERNATEPATH{$req}/$req";
}
if (
$coderetour != 0
&& ( ( $coderetour2 == 1 && $OS =~ /windows/ && $ret !~ /Usage/i )
|| ( $coderetour2 == 127 && $OS !~ /windows/ ) )
)
{
# Not found error
print
"Not found\nCan't build target $target. Requirement '$req' not found in PATH\n";
$CHOOSEDTARGET{$target} = -1;
last;
}
else {
# Pas erreur ou erreur autre que programme absent
print " Found " . $REQUIREMENTTARGET{$target} . "\n";
}
}
}
print "\n";
# Check if there is at least on target to build
#----------------------------------------------
$nboftargetok=0;
foreach my $target (keys %CHOOSEDTARGET) {
if ($CHOOSEDTARGET{$target} < 0) { next; }
$nboftargetok++;
$nboftargetok = 0;
foreach my $target ( keys %CHOOSEDTARGET ) {
if ( $CHOOSEDTARGET{$target} < 0 ) { next; }
$nboftargetok++;
}
if ($nboftargetok) {
# Update buildroot
#-----------------
if (! $copyalreadydone) {
print "Delete directory $BUILDROOT\n";
$ret=`rm -fr "$BUILDROOT"`;
mkdir "$BUILDROOT";
mkdir "$BUILDROOT/htdocs";
mkdir "$BUILDROOT/htdocs/theme";
# Update buildroot
#-----------------
if ( !$copyalreadydone ) {
print "Delete directory $BUILDROOT\n";
$ret = `rm -fr "$BUILDROOT"`;
mkdir "$BUILDROOT";
mkdir "$BUILDROOT/htdocs";
mkdir "$BUILDROOT/htdocs/theme";
print "Copy $SOURCE into $BUILDROOT\n";
mkdir "$BUILDROOT";
foreach my $tmp (@PROJECTLIST)
{
$ret=`cp -pr "$SOURCE/htdocs/theme/$tmp" "$BUILDROOT/htdocs/theme"`;
print "Copy $SOURCE into $BUILDROOT\n";
mkdir "$BUILDROOT";
foreach my $tmp (@PROJECTLIST) {
$ret =
`cp -pr "$SOURCE/htdocs/theme/$tmp" "$BUILDROOT/htdocs/theme"`;
}
}
print "Clean $BUILDROOT\n";
$ret=`rm -fr $BUILDROOT/htdocs/theme/$PROJECT/Thumbs.db $BUILDROOT/htdocs/theme/$PROJECT/*/Thumbs.db $BUILDROOT/htdocs/theme/$PROJECT/*/*/Thumbs.db $BUILDROOT/htdocs/theme/$PROJECT/*/*/*/Thumbs.db`;
$ret=`rm -fr $BUILDROOT/htdocs/theme/$PROJECT/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/*/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/*/*/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/*/*/*/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/*/*/*/*/CVS*`;
}
print "Clean $BUILDROOT\n";
$ret =
`rm -fr $BUILDROOT/htdocs/theme/$PROJECT/Thumbs.db $BUILDROOT/htdocs/theme/$PROJECT/*/Thumbs.db $BUILDROOT/htdocs/theme/$PROJECT/*/*/Thumbs.db $BUILDROOT/htdocs/theme/$PROJECT/*/*/*/Thumbs.db`;
$ret =
`rm -fr $BUILDROOT/htdocs/theme/$PROJECT/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/*/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/*/*/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/*/*/*/CVS* $BUILDROOT/htdocs/theme/$PROJECT/*/*/*/*/*/CVS*`;
# Build package for each target
#------------------------------
foreach my $target ( keys %CHOOSEDTARGET ) {
if ( $CHOOSEDTARGET{$target} < 0 ) { next; }
# Build package for each target
#------------------------------
foreach my $target (keys %CHOOSEDTARGET) {
if ($CHOOSEDTARGET{$target} < 0) { next; }
print "\nBuild package for target $target\n";
print "\nBuild package for target $target\n";
if ( $target eq 'TGZ' ) {
unlink $FILENAMETGZ . tgz;
print "Compress $BUILDROOT/htdocs into $FILENAMETGZ.tgz...\n";
$cmd =
"tar --exclude-vcs --exclude-from \"$DESTI/tgz/tar_exclude.txt\" --directory \"$BUILDROOT\" --mode=go-w --group=500 --owner=500 -czvf \"$FILENAMETGZ.tgz\" htdocs";
$ret = `$cmd`;
if ( $OS =~ /windows/i ) {
print "Move $FILENAMETGZ.tgz to $DESTI/$FILENAMETGZ.tgz\n";
$ret = `mv "$FILENAMETGZ.tgz" "$DESTI/$FILENAMETGZ.tgz"`;
}
next;
}
if ($target eq 'TGZ') {
unlink $FILENAMETGZ.tgz;
print "Compress $BUILDROOT/htdocs into $FILENAMETGZ.tgz...\n";
$cmd="tar --exclude-vcs --exclude-from \"$DESTI/tgz/tar_exclude.txt\" --directory \"$BUILDROOT\" --mode=go-w --group=500 --owner=500 -czvf \"$FILENAMETGZ.tgz\" htdocs";
$ret=`$cmd`;
if ($OS =~ /windows/i) {
print "Move $FILENAMETGZ.tgz to $DESTI/$FILENAMETGZ.tgz\n";
$ret=`mv "$FILENAMETGZ.tgz" "$DESTI/$FILENAMETGZ.tgz"`;
}
next;
}
if ($target eq 'ZIP') {
unlink $FILENAMEZIP.zip;
print "Compress $FILENAMETGZ into $FILENAMEZIP.zip...\n";
chdir("$BUILDROOT");
$ret=`7z a -r -tzip -mx $BUILDROOT/$FILENAMEZIP.zip htdocs`;
if ( $target eq 'ZIP' ) {
unlink $FILENAMEZIP . zip;
print "Compress $FILENAMETGZ into $FILENAMEZIP.zip...\n";
chdir("$BUILDROOT");
$ret = `7z a -r -tzip -mx $BUILDROOT/$FILENAMEZIP.zip htdocs`;
print "Move $FILENAMEZIP.zip to $DESTI\n";
$ret=`mv "$FILENAMEZIP.zip" "$DESTI/$FILENAMEZIP.zip"`;
next;
}
$ret = `mv "$FILENAMEZIP.zip" "$DESTI/$FILENAMEZIP.zip"`;
next;
}
}
}
}
print "\n----- Summary -----\n";
foreach my $target (keys %CHOOSEDTARGET) {
if ($CHOOSEDTARGET{$target} < 0) {
print "Package $target not built (bad requirement).\n";
} else {
print "Package $target built successfully in $DESTI\n";
}
foreach my $target ( keys %CHOOSEDTARGET ) {
if ( $CHOOSEDTARGET{$target} < 0 ) {
print "Package $target not built (bad requirement).\n";
}
else {
print "Package $target built successfully in $DESTI\n";
}
}
if (! $btach) {
print "\nPress key to finish...";
my $WAITKEY=<STDIN>;
if ( !$btach ) {
print "\nPress key to finish...";
my $WAITKEY = <STDIN>;
}
0;

View File

@@ -1,7 +1,7 @@
#----------------------------------------------------------------------------
# \file dolibarr.pl
# \brief Dolibarr script install for Virtualmin Pro
# \author (c)2009-2020 Regis Houssin <regis.houssin@inodbox.com>
# \author (c)2009-2025 Regis Houssin <regis.houssin@inodbox.com>
#----------------------------------------------------------------------------
@@ -30,7 +30,15 @@ return "Regis Houssin";
# script_dolibarr_versions()
sub script_dolibarr_versions
{
return ( "14.0.5", "13.0.5", "12.0.5", "11.0.5", "10.0.7", "9.0.4", "8.0.6", "7.0.5" );
return ( "22.0.3", "21.0.4", "20.0.4", "19.0.4", "18.0.8", "17.0.4", "16.0.5" );
}
sub script_dolibarr_version_desc
{
local ($ver) = @_;
my ($major_ver) = $ver =~ /^(\d+)\..*/;
return $major_ver == 22 ? "$ver (Latest)" :
$major_ver == 18 ? "$ver (LTS)" : "$ver";
}
sub script_dolibarr_release
@@ -38,6 +46,11 @@ sub script_dolibarr_release
return 2; # for mysqli fix
}
sub script_dolibarr_testable
{
return 1;
}
sub script_dolibarr_category
{
return "Commerce";
@@ -45,14 +58,23 @@ return "Commerce";
sub script_dolibarr_php_vers
{
return ( 5 );
return ( 7 );
}
sub script_dolibarr_php_vars
{
return ( [ 'memory_limit', '128M', '+' ] );
}
sub script_dolibarr_php_modules
{
local ($d, $ver, $phpver, $opts) = @_;
local ($dbtype, $dbname) = split(/_/, $opts->{'db'}, 2);
return $dbtype eq "mysql" ? ("mysql") : ("pgsql");
local @modules = ("xml", "mbstring", "gd", "iconv",
"curl", "intl", "zip");
push(@modules, ($dbtype eq "mysql" ? "mysql" : "pgsql"));
return @modules;
}
sub script_dolibarr_dbs
@@ -61,34 +83,10 @@ local ($d, $ver) = @_;
return ("mysql", "postgres");
}
# script_dolibarr_depends(&domain, version)
sub script_dolibarr_depends
sub script_dolibarr_php_fullver
{
local ($d, $ver, $sinfo, $phpver) = @_;
local @rv;
if ($ver >= 3.6) {
# Check for PHP 5.3+
local $phpv = &get_php_version($phpver || 5, $d);
if (!$phpv) {
push(@rv, "Could not work out exact PHP version");
}
elsif ($phpv < 5.3) {
push(@rv, "Dolibarr requires PHP version 5.3 or later");
}
}
if ($ver >= 12.0) {
# Check for PHP 5.6+
local $phpv = &get_php_version($phpver || 5, $d);
if (!$phpv) {
push(@rv, "Could not work out exact PHP version");
}
elsif ($phpv < 5.6) {
push(@rv, "Dolibarr requires PHP version 5.6 or later");
}
}
return @rv;
local ($d, $ver, $sinfo) = @_;
return "7.1";
}
# script_dolibarr_params(&domain, version, &upgrade-info)
@@ -195,7 +193,7 @@ local $dbpass = $dbtype eq "mysql" ? &mysql_pass($d) : &postgres_pass($d, 1);
local $dbphptype = $dbtype eq "mysql" && $version < 3.6 ? "mysql" :
$dbtype eq "mysql" ? "mysqli" : "pgsql";
local $dbhost = &get_database_host($dbtype, $d);
local $dberr = &check_script_db_connection($dbtype, $dbname, $dbuser, $dbpass);
local $dberr = &check_script_db_connection($d, $dbtype, $dbname, $dbuser, $dbpass);
return (0, "Database connection failed : $dberr") if ($dberr);
# Extract tar file to temp dir and copy to target
@@ -252,7 +250,7 @@ if ($upgrade) {
&copy_source_dest_as_domain_user($d, $oldcfile, $cfile);
&copy_source_dest_as_domain_user($d, $olddocdir, $docdir);
&copy_source_dest_as_domain_user($d, $oldaltdir, $altdir);
# First page (Update database schema)
local @params = ( [ "action", "upgrade" ],
[ "versionfrom", $upgrade->{'version'} ],
@@ -260,7 +258,7 @@ if ($upgrade) {
);
local $err = &call_dolibarr_wizard_page(\@params, "upgrade", $d, $opts);
return (-1, "Dolibarr wizard failed : $err") if ($err);
# Second page (Migrate some data)
local @params = ( [ "action", "upgrade" ],
[ "versionfrom", $upgrade->{'version'} ],
@@ -268,7 +266,7 @@ if ($upgrade) {
);
local $err = &call_dolibarr_wizard_page(\@params, "upgrade2", $d, $opts);
return (-1, "Dolibarr wizard failed : $err") if ($err);
# Third page (Update version number)
local @params = ( [ "action", "upgrade" ],
[ "versionfrom", $upgrade->{'version'} ],
@@ -278,12 +276,12 @@ if ($upgrade) {
local $p = $ver >= 3.8 ? "step5" : "etape5";
local $err = &call_dolibarr_wizard_page(\@params, $p, $d, $opts);
return (-1, "Dolibarr wizard failed : $err") if ($err);
# Remove the installation directory. (deprecated)
# local $dinstall = "$opts->{'dir'}/install";
# $dinstall =~ s/\/$//;
# $out = &run_as_domain_user($d, "rm -rf ".quotemeta($dinstall));
}
else {
# First page (Db connection and config file creation)
@@ -295,6 +293,9 @@ else {
[ "db_name", $dbname ],
[ "db_user", $dbuser ],
[ "db_pass", $dbpass ],
[ "db_prefix", 'llx_' ],
[ "db_port", '3306' ],
[ "selectlang", 'en_US' ],
[ "action", "set" ],
[ "main_force_https", $opts->{'forcehttps'} ],
[ "dolibarr_main_db_character_set", $charset ],
@@ -305,13 +306,13 @@ else {
local $p = $ver >= 3.8 ? "step1" : "etape1";
local $err = &call_dolibarr_wizard_page(\@params, $p, $d, $opts);
return (-1, "Dolibarr wizard failed : $err") if ($err);
# Second page (Populate database)
local @params = ( [ "action", "set" ] );
local $p = $ver >= 3.8 ? "step2" : "etape2";
local $err = &call_dolibarr_wizard_page(\@params, $p, $d, $opts);
return (-1, "Dolibarr wizard failed : $err") if ($err);
# Third page (Add administrator account)
local @params = ( [ "action", "set" ],
[ "login", "admin" ],
@@ -322,17 +323,17 @@ else {
local $p = $ver >= 3.8 ? "step5" : "etape5";
local $err = &call_dolibarr_wizard_page(\@params, $p, $d, $opts);
return (-1, "Dolibarr wizard failed : $err") if ($err);
# Remove the installation directory (deprecated)
# local $dinstall = "$opts->{'dir'}/install";
# $dinstall =~ s/\/$//;
# $out = &run_as_domain_user($d, "rm -rf ".quotemeta($dinstall));
# Protect config file
&set_permissions_as_domain_user($d, 0644, $cfile);
&set_permissions_as_domain_user($d, 0755, $cfiledir);
}
# Return a URL for the user
local $rp = $opts->{'dir'};
$rp =~ s/^$d->{'home'}\///;
@@ -400,24 +401,32 @@ sub script_dolibarr_check_latest
{
local ($ver) = @_;
local @vers = &osdn_package_versions("dolibarr",
$ver >= 14.0 ? "dolibarr\\-(12\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 13.0 ? "dolibarr\\-(12\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 12.0 ? "dolibarr\\-(12\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 11.0 ? "dolibarr\\-(11\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 10.0 ? "dolibarr\\-(10\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 9.0 ? "dolibarr\\-(9\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 8.0 ? "dolibarr\\-(8\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 7.0 ? "dolibarr\\-(7\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 6.0 ? "dolibarr\\-(6\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 5.0 ? "dolibarr\\-(5\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 4.0 ? "dolibarr\\-(4\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.9 ? "dolibarr\\-(3\\.9\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.8 ? "dolibarr\\-(3\\.8\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.7 ? "dolibarr\\-(3\\.7\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.6 ? "dolibarr\\-(3\\.6\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.5 ? "dolibarr\\-(3\\.5\\.[0-9\\.]+)\\.tgz" :
$ver >= 2.9 ? "dolibarr\\-(2\\.9\\.[0-9\\.]+)\\.tgz" :
"dolibarr\\-(2\\.8\\.[0-9\\.]+)\\.tgz");
$ver >= 22.0 ? "dolibarr\\-(22\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 21.0 ? "dolibarr\\-(21\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 20.0 ? "dolibarr\\-(20\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 19.0 ? "dolibarr\\-(19\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 18.0 ? "dolibarr\\-(18\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 17.0 ? "dolibarr\\-(17\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 16.0 ? "dolibarr\\-(16\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 15.0 ? "dolibarr\\-(15\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 14.0 ? "dolibarr\\-(14\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 13.0 ? "dolibarr\\-(13\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 12.0 ? "dolibarr\\-(12\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 11.0 ? "dolibarr\\-(11\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 10.0 ? "dolibarr\\-(10\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 9.0 ? "dolibarr\\-(9\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 8.0 ? "dolibarr\\-(8\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 7.0 ? "dolibarr\\-(7\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 6.0 ? "dolibarr\\-(6\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 5.0 ? "dolibarr\\-(5\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 4.0 ? "dolibarr\\-(4\\.0\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.9 ? "dolibarr\\-(3\\.9\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.8 ? "dolibarr\\-(3\\.8\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.7 ? "dolibarr\\-(3\\.7\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.6 ? "dolibarr\\-(3\\.6\\.[0-9\\.]+)\\.tgz" :
$ver >= 3.5 ? "dolibarr\\-(3\\.5\\.[0-9\\.]+)\\.tgz" :
$ver >= 2.9 ? "dolibarr\\-(2\\.9\\.[0-9\\.]+)\\.tgz" :
"dolibarr\\-(2\\.8\\.[0-9\\.]+)\\.tgz");
return "Failed to find versions" if (!@vers);
return $ver eq $vers[0] ? undef : $vers[0];
}
@@ -432,4 +441,26 @@ sub script_dolibarr_passmode
return 2;
}
sub script_dolibarr_db_conn_desc
{
my $db_conn_desc =
{ 'conf/conf.php' =>
{
'dbpass' =>
{
'func' => 'php_quotemeta',
'func_params' => 1,
'replace' => [ '\$dolibarr_main_db_pass\s*=' =>
'$dolibarr_main_db_pass=\'$$sdbpass\';' ],
},
'dbuser' =>
{
'replace' => [ '\$dolibarr_main_db_user\s*=' =>
'$dolibarr_main_db_user=\'$$sdbuser\';' ],
},
}
};
return $db_conn_desc;
}
1;

View File

@@ -663,7 +663,7 @@ parameters:
-
message: '#^If condition is always true\.$#'
identifier: if.alwaysTrue
count: 4
count: 3
path: ../../../htdocs/admin/mails_templates.php
-
@@ -1314,12 +1314,6 @@ parameters:
count: 1
path: ../../../htdocs/barcode/printsheet.php
-
message: '#^Variable \$contextpage in empty\(\) always exists and is not falsy\.$#'
identifier: empty.variable
count: 1
path: ../../../htdocs/blockedlog/admin/blockedlog_list.php
-
message: '#^Strict comparison using \=\=\= between ''facture'' and ''facture'' will always evaluate to true\.$#'
identifier: identical.alwaysTrue
@@ -8346,12 +8340,6 @@ parameters:
count: 1
path: ../../../htdocs/core/tpl/document_actions_post_headers.tpl.php
-
message: '#^Variable \$modulepart might not be defined\.$#'
identifier: variable.undefined
count: 3
path: ../../../htdocs/core/tpl/document_actions_post_headers.tpl.php
-
message: '#^Variable \$permissiontoadd might not be defined\.$#'
identifier: variable.undefined
@@ -8370,12 +8358,6 @@ parameters:
count: 1
path: ../../../htdocs/core/tpl/document_actions_post_headers.tpl.php
-
message: '#^Variable \$upload_dir might not be defined\.$#'
identifier: variable.undefined
count: 1
path: ../../../htdocs/core/tpl/document_actions_post_headers.tpl.php
-
message: '#^Variable \$object might not be defined\.$#'
identifier: variable.undefined
@@ -8418,12 +8400,6 @@ parameters:
count: 1
path: ../../../htdocs/core/tpl/filemanager.tpl.php
-
message: '#^Variable \$limit might not be defined\.$#'
identifier: variable.undefined
count: 2
path: ../../../htdocs/core/tpl/list_print_total.tpl.php
-
message: '#^Variable \$num might not be defined\.$#'
identifier: variable.undefined
@@ -9630,12 +9606,6 @@ parameters:
count: 2
path: ../../../htdocs/fourn/facture/card.php
-
message: '#^Property CommonInvoice\:\:\$subtype \(int\|null\) does not accept string\.$#'
identifier: assign.propertyType
count: 1
path: ../../../htdocs/fourn/facture/card.php
-
message: '#^Property FactureFournisseur\:\:\$type \(int\) does not accept string\.$#'
identifier: assign.propertyType
@@ -11784,18 +11754,6 @@ parameters:
count: 1
path: ../../../htdocs/product/class/api_products.class.php
-
message: '#^Method Products\:\:getPurchasePrices\(\) should return array\<ProductFournisseur\> but returns object\.$#'
identifier: return.type
count: 1
path: ../../../htdocs/product/class/api_products.class.php
-
message: '#^Parameter \#1 \$object of method Products\:\:_cleanObjectDatas\(\) expects object, array\<ProductFournisseur\>\|int given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/product/class/api_products.class.php
-
message: '#^Strict comparison using \=\=\= between 2 and 2 will always evaluate to true\.$#'
identifier: identical.alwaysTrue
@@ -12763,22 +12721,10 @@ parameters:
path: ../../../htdocs/projet/card.php
-
message: '#^Parameter \#1 \$object of method Projects\:\:_cleanObjectDatas\(\) expects object, string given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/projet/class/api_projects.class.php
-
message: '#^Method Tasks\:\:getRoles\(\) should return array\<int, string\> but returns list\<object\>\.$#'
message: '#^Method Projects\:\:getRoles\(\) should return array\<object\> but returns list\<string\>\.$#'
identifier: return.type
count: 1
path: ../../../htdocs/projet/class/api_tasks.class.php
-
message: '#^Parameter \#1 \$object of method Tasks\:\:_cleanObjectDatas\(\) expects object, string given\.$#'
identifier: argument.type
count: 1
path: ../../../htdocs/projet/class/api_tasks.class.php
path: ../../../htdocs/projet/class/api_projects.class.php
-
message: '#^Call to function method_exists\(\) with \$this\(Project\) and ''getLibStatut'' will always evaluate to true\.$#'

View File

@@ -30,27 +30,17 @@ Alias /dolibarr /usr/share/dolibarr/htdocs
ErrorDocument 404 /public/error-404.php
# OPTIMIZE: To use gzip compressed files (for Dolibarr already compressed files).
# Note that constant MAIN_OPTIMIZE_SPEED must have a value with bit 0 set.
#AddType text/javascript .jgz
#AddEncoding gzip .jgz
# OPTIMIZE: To use gzip compression (on the fly).
# Note that you must also enable the module mod_deflate.
# You can also set this with constant MAIN_OPTIMIZE_SPEED and bit 2 set.
#TODO
# OPTIMIZE: To use cache on static pages (A259200 = 1 month).
# Note that you must also enable the module mod_expires.
#ExpiresActive On
#ExpiresByType image/x-icon A2592000
#ExpiresByType image/gif A2592000
#ExpiresByType image/png A2592000
#ExpiresByType image/jpeg A2592000
#ExpiresByType text/css A2592000
#ExpiresByType text/javascript A2592000
#ExpiresByType application/x-javascript A2592000
#ExpiresByType application/javascript A2592000
# OPTIMIZE: To use cache on static pages (A259200 = 1 month).
# Note that you must also enable the module mod_expires.
#ExpiresActive On
#ExpiresByType image/x-icon A2592000
#ExpiresByType image/gif A2592000
#ExpiresByType image/png A2592000
#ExpiresByType image/jpeg A2592000
#ExpiresByType text/css A2592000
#ExpiresByType text/javascript A2592000
#ExpiresByType application/x-javascript A2592000
#ExpiresByType application/javascript A2592000
</DirectoryMatch>

3
dev/resources/dbmodel/.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
/dolibarr_schema.mwb.bak
/dolibarr_schema.mwb.sav.mwb
/dolibarr_schema.mwb.sav.mwb.bak

View File

@@ -0,0 +1,33 @@
README (English)
--------------------------------
##############
RapidAPI
##############
RapidAPI for Mac is a full-featured HTTP client that let's you test and describe the APIs you build or consume.
It has a beautiful native macOS interface to compose requests, inspect server responses, generate client code and export API definitions.
https://paw.cloud/
@@@
(@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@%
@@@@@@, @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@,
@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
(@@@@@@( @@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@* @@@@@@@@@@@. /@@@@@@@@@@@@@@@@@(
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* @@@@@@@@@@@. @@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ /@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@# @@@@@@@@@@@ @@@@@@@@@@@@&
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ &@@@@@@@@@@@@@@@@@@@@@@
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ &@@@@@@@@@@@@@@@@@@@@/
@@@@@@@@@@@@@@@@@@@@@@@@@@& .@@@@@@@@@@@ &@@@@@@@@@@@@@@@@@@/
(@@@@@@@@@@@@@@@@@@@@@@@ .@@@@@@@@@@@ &@@@@@@@@@@@@@@@(
@@@@@ @@@@@ .@@@@@@@@@@@ @@@@@@@@@@@*

View File

@@ -11,351 +11,386 @@
# Pour les cles autoincrement: rowid integer AUTO_INCREMENT PRIMARY KEY,
# Mettre les index dans fichier.key.sql
#------------------------------------------------------------------------------
## no critic (InputOutput::ProhibitExplicitStdin,InputOutput::RequireBriefOpen)
use Data::Dumper;
use Getopt::Long;
use strict;
use warnings;
use vars qw/ $DIR $PROG $Extension $SOURCE $DESTI %filelist $stop /;
# command line options
my( $opt_debug, $opt_help);
my ( $opt_debug, $opt_help );
# general values
my ($out, $size);
my ( $out, $size );
# variables for constructing pre-create-table entities
my $create_sql=''; # if empty we are not making a create statement
my $create_index=''; # if empty we are not making a create statement
my %enum_datafield=(); # holds enumeration choices
my (@column_values,$enum_column, $seq);
my $table="";
my $create_sql = ''; # if empty we are not making a create statement
my $create_index = ''; # if empty we are not making a create statement
my %enum_datafield = (); # holds enumeration choices
my ( @column_values, $enum_column, $seq );
my $table = "";
#------------------------------------------------------------------------------
# MAIN
#------------------------------------------------------------------------------
($DIR=$0) =~ s/([^\/\\]+)$//; ($PROG=$1) =~ s/\.([^\.]*)$//; $Extension=$1;
$DIR||='.'; $DIR =~ s/([^\/\\])[\\\/]+$/$1/;
( $DIR = $0 ) =~ s/([^\/\\]+)$//;
( $PROG = $1 ) =~ s/\.([^\.]*)$//;
$Extension = $1;
$DIR ||= '.';
$DIR =~ s/([^\/\\])[\\\/]+$/$1/;
$SOURCE="$DIR/install/mysql/tables";
$DESTI="$DIR/install/pgsql/tables";
$SOURCE = "$DIR/install/mysql/tables";
$DESTI = "$DIR/install/pgsql/tables";
# Recherche tous les fichiers .sql
opendir(DIR, $SOURCE);
foreach my $file (readdir(DIR)) {
if ($file =~ /\.sql$/ && -f "$SOURCE/$file") {
print "Found file $file\n";
$filelist{$file}=1;
}
}
closedir(DIR);
opendir( my $dir, $SOURCE );
foreach my $file ( readdir($dir) ) {
if ( $file =~ /\.sql$/ && -f "$SOURCE/$file" ) {
print "Found file $file\n";
$filelist{$file} = 1;
}
}
closedir($dir);
# Boucle sur tous les fichiers de SOURCE
#---------------------------------------
foreach my $file (keys %filelist) {
foreach my $file ( keys %filelist ) {
$ARGV[0]="$SOURCE/$file";
$ARGV[1]="$DESTI/$file";
local $ARGV[0] = "$SOURCE/$file";
local $ARGV[1] = "$DESTI/$file";
print "Convert file $ARGV[0] into $ARGV[1]\n";
print "Convert file $ARGV[0] into $ARGV[1]\n";
# MySQL to PostgreSQL dump file converter
#
# For usage: perl mysql2pgsql.perl --help
#
# homepage: http://www.rot13.org/~dpavlin/projects.html
# 1999-12-15 DbP -- Dobrica Pavlinusic <dpavlin@rot13.org>
# 1999-12-26 DbP don't make serial from auto_increment, create all manually
# (to set start value right)
# 2000-01-11 DbP now creates sequences with correct value
# 2000-04-25 DbP import into CVS (at cvs.linux.hr)
# 2001-01-29 tpo -- Tomas Pospisek <tpo@sourcepole.ch>:
# 1) make script comply to usage:
# 2) make script output to STDOUT instead of STERR
# 3) change verbosity behaveour
# 4) add debug option
# see rest of changelog at http://cvs.linux.hr/cvsweb.cgi/sql/mysql2pgsql
# 2003-12-16 jsp -- Joe Speigle <joe.speigle@jklh.us>:
# converts: s/\) *Type=MyISAM;/);/i, enum data type -> references,
# auto_increment->sequences
# 2004-01-13 jsp -- moved project to gborg; both the above declined ownership
# 2004-06-29 converts: year(4), year(2)
# homepage: gborg.postgresql.org
# MySQL to PostgreSQL dump file converter
#
# For usage: perl mysql2pgsql.perl --help
#
# homepage: http://www.rot13.org/~dpavlin/projects.html
# 1999-12-15 DbP -- Dobrica Pavlinusic <dpavlin@rot13.org>
# 1999-12-26 DbP don't make serial from auto_increment, create all manually
# (to set start value right)
# 2000-01-11 DbP now creates sequences with correct value
# 2000-04-25 DbP import into CVS (at cvs.linux.hr)
# 2001-01-29 tpo -- Tomas Pospisek <tpo@sourcepole.ch>:
# 1) make script comply to usage:
# 2) make script output to STDOUT instead of STERR
# 3) change verbosity behaveour
# 4) add debug option
# see rest of changelog at http://cvs.linux.hr/cvsweb.cgi/sql/mysql2pgsql
# 2003-12-16 jsp -- Joe Speigle <joe.speigle@jklh.us>:
# converts: s/\) *Type=MyISAM;/);/i, enum data type -> references,
# auto_increment->sequences
# 2004-01-13 jsp -- moved project to gborg; both the above declined ownership
# 2004-06-29 converts: year(4), year(2)
# homepage: gborg.postgresql.org
GetOptions("debug", "help");
GetOptions( "debug", "help" );
my $DEBUG = $opt_debug || 0;
my $HELP = $opt_help || 0;
my $DEBUG = $opt_debug || 0;
my $HELP = $opt_help || 0;
if ( ($HELP) || !defined( $ARGV[0] ) || !defined( $ARGV[1] ) ) {
print
"Usage: perl $0 {--verbose|--help|--debug} mysql_dump_file.sql pg_dump_file.sql\n";
print "\t* OPTIONS\n";
print
"\t--verbose tees to pg_dump_file.sql and STDOUT during conversion\n";
print "\t--debug does ?? \n";
print "\t--help prints this message \n";
print "\t* REQUIRED ARGUMENTS\n";
if ( defined( $ARGV[0] ) ) {
print "\tmysql_dump_file.sql ($ARGV[0])\n";
}
else {
print "\tmysql_dump_file.sql (undefined)\n";
}
if ( defined( $ARGV[1] ) ) {
print "\tpg_dump_file.sql ($ARGV[1])\n";
}
else {
print "\tpg_dump_file.sql (undefined)\n";
}
exit 1;
}
if (($HELP) || ! defined($ARGV[0]) || ! defined($ARGV[1])) {
print "Usage: perl $0 {--verbose|--help|--debug} mysql_dump_file.sql pg_dump_file.sql\n";
print "\t* OPTIONS\n";
print "\t--verbose tees to pg_dump_file.sql and STDOUT during conversion\n";
print "\t--debug does ?? \n";
print "\t--help prints this message \n";
print "\t* REQUIRED ARGUMENTS\n";
if (defined ($ARGV[0])) {
print "\tmysql_dump_file.sql ($ARGV[0])\n";
} else {
print "\tmysql_dump_file.sql (undefined)\n";
}
if (defined ($ARGV[1])) {
print "\tpg_dump_file.sql ($ARGV[1])\n";
} else {
print "\tpg_dump_file.sql (undefined)\n";
}
exit 1;
}
open( my $in, "<", "$ARGV[0]" )
|| die "can't open mysql dump file $ARGV[0]";
open( my $out, ">", "$ARGV[1]" ) || die "can't open pg dump file $ARGV[1]";
print $out "-- Generated by $PROG\n";
print $out "-- (c) 2004, PostgreSQL Inc.\n";
print $out "-- (c) 2005, Laurent Destailleur.\n";
print $out "\n";
open(IN,"<$ARGV[0]") || die "can't open mysql dump file $ARGV[0]";
open(OUT,">$ARGV[1]") || die "can't open pg dump file $ARGV[1]";
print OUT "-- Generated by $PROG\n";
print OUT "-- (c) 2004, PostgreSQL Inc.\n";
print OUT "-- (c) 2005, Laurent Destailleur.\n";
print OUT "\n";
# Output for create table and create index
sub output_create {
# Output for create table and create index
sub output_create {
# If command ends with "xxx,);", we change to "xxx);"
$create_sql =~ s/,(\s*)\);/$1\);/m;
# If command ends with "xxx, -- yyy );", we change to "xxx -- yyy);"
$create_sql =~ s/,(\s*\-\-[^\)\n]*)(\s*)\);/$1\n\);/m;
# If command ends with "xxx,);", we change to "xxx);"
$create_sql =~ s/,(\s*)\);/$1\);/m;
print OUT $create_sql;
if ($create_index) {
print OUT "\n";
print OUT $create_index;
}
}
# If command ends with "xxx, -- yyy );", we change to "xxx -- yyy);"
$create_sql =~ s/,(\s*\-\-[^\)\n]*)(\s*)\);/$1\n\);/m;
# Reset when moving from each "create table" to "insert" part of dump
sub reset_vars() {
$create_sql="";
$create_index="";
%enum_datafield=();
$enum_column='';
}
print $out $create_sql;
if ($create_index) {
print $out "\n";
print $out $create_index;
}
return;
}
# Reset when moving from each "create table" to "insert" part of dump
sub reset_vars() {
$create_sql = "";
$create_index = "";
%enum_datafield = ();
$enum_column = '';
return;
}
# Boucle sur contenu fichier source
#----------------------------------
while(<IN>) {
# Boucle sur contenu fichier source
#----------------------------------
while (<$in>) {
# comments or empty lines
if (/^-- \$Id/) {
$_ =~ s/\$//g;
print OUT $_;
if (/^-- \$Id/) {
$_ =~ s/\$//g;
print $out $_;
next;
}
# comments or empty lines
if (/^#/ || /^$/ || /^--/) {
print OUT $_;
next;
}
if (/^USE\s*([^;]*);/) {
print OUT "\\c ". $1;
next;
}
if ($create_sql ne "") { # we are inside create table statement so let's process datatypes
if ( /^#/ || /^$/ || /^--/ ) {
print $out $_;
next;
}
if (/^USE\s*([^;]*);/) {
print $out "\\c " . $1;
next;
}
if ( $create_sql ne "" )
{ # we are inside create table statement so let's process datatypes
if (/\);/i) { # end of create table sequence
$create_sql =~ s/,$//g; # strip last , inside create table
&output_create;
&reset_vars();
next;
# LDR Added "innodb" and "engine"
}
elsif (/(ISAM|innodb)/i) { # end of create table sequence
s/\) *type=(MyISAM|innodb);/);/i;
s/\) *engine=(MyISAM|innodb);/);/i;
$create_sql =~ s/,$//g; # strip last , inside create table
$create_sql .= $_;
&output_create;
&reset_vars();
next;
}
if (/\);/i) { # end of create table sequence
$create_sql =~ s/,$//g; # strip last , inside create table
&output_create;
&reset_vars();
next;
# enum -> check
if (/([\w\"]*)\s+enum\s*\(((?:['"][\?\w]+['"]\s*,)+['"][\?\w]+['"])\)(.*)$/i) {
$enum_column=$1;
$enum_datafield{$enum_column}=$2; # 'abc','def', ...
my $suite=$3;
my $maxlength=0;
foreach my $enum (split(',',$enum_datafield{$enum_column})) {
$enum =~ s/[\"\']//g;
if ($maxlength<length($enum)) { $maxlength=length($enum); }
}
$enum_datafield{$enum_column} =~ s/\"/\'/g;
$_ = qq~ $enum_column CHAR($maxlength) CHECK ($enum_column IN ($enum_datafield{$enum_column})) $suite\n~;
# int, auto_increment -> serial
} elsif (/^[\s\t]*(\w*)\s*.*int.*auto_increment/i) {
$seq = qq~${table}_${1}_seq~;
s/[\s\t]*([a-zA-Z_0-9]*)\s*.*int.*auto_increment[^,]*/ $1 SERIAL PRIMARY KEY/ig;
$create_sql.=$_;
next;
# int type conversion
} elsif (/(\w*)int\(\d+\)/i) {
$size=$1;
$size =~ tr [A-Z] [a-z];
if ($size eq "tiny" || $size eq "small") {
$out = "int2";
} elsif ($size eq "big") {
$out = "int8";
} else {
$out = "int4";
}
s/\w*int\(\d+\)/$out/g;
}
# tinyint -> smallint
elsif (/tinyint/i) {
s/tinyint/smallint/g;
}
# LDR Added "innodb" and "engine"
}
elsif (/(ISAM|innodb)/i) { # end of create table sequence
s/\) *type=(MyISAM|innodb);/);/i;
s/\) *engine=(MyISAM|innodb);/);/i;
$create_sql =~ s/,$//g; # strip last , inside create table
$create_sql .= $_;
&output_create;
&reset_vars();
next;
}
# nuke unsigned
s/(int\w+|smallint)\s+unsigned/$1/gi;
# enum -> check
if (
/([\w\"]*)\s+enum\s*\(((?:['"][\?\w]+['"]\s*,)+['"][\?\w]+['"])\)(.*)$/i
)
{
$enum_column = $1;
$enum_datafield{$enum_column} = $2; # 'abc','def', ...
my $suite = $3;
my $maxlength = 0;
foreach my $enum ( split( ',', $enum_datafield{$enum_column} ) )
{
$enum =~ s/[\"\']//g;
if ( $maxlength < length($enum) ) {
$maxlength = length($enum);
}
}
$enum_datafield{$enum_column} =~ s/\"/\'/g;
$_ =
qq~ $enum_column CHAR($maxlength) CHECK ($enum_column IN ($enum_datafield{$enum_column})) $suite\n~;
# int, auto_increment -> serial
}
elsif (/^[\s\t]*(\w*)\s*.*int.*auto_increment/i) {
$seq = qq~${table}_${1}_seq~;
s/[\s\t]*([a-zA-Z_0-9]*)\s*.*int.*auto_increment[^,]*/ $1 SERIAL PRIMARY KEY/ig;
$create_sql .= $_;
next;
# blob -> text
s/\w*blob/text/gi;
# int type conversion
}
elsif (/(\w*)int\(\d+\)/i) {
$size = $1;
$size =~ tr [A-Z] [a-z];
if ( $size eq "tiny" || $size eq "small" ) {
$out = "int2";
}
elsif ( $size eq "big" ) {
$out = "int8";
}
else {
$out = "int4";
}
s/\w*int\(\d+\)/$out/g;
}
# tinytext/mediumtext -> text
s/tinytext/text/gi;
s/mediumtext/text/gi;
# tinyint -> smallint
elsif (/tinyint/i) {
s/tinyint/smallint/g;
}
# char -> varchar
# PostgreSQL would otherwise pad with spaces as opposed
# to MySQL! Your user interface may depend on this!
s/(\s+)char/${1}varchar/gi;
# nuke unsigned
s/(int\w+|smallint)\s+unsigned/$1/gi;
# nuke date representation (not supported in PostgreSQL)
s/datetime default '[^']+'/datetime/i;
s/date default '[^']+'/datetime/i;
s/time default '[^']+'/datetime/i;
# blob -> text
s/\w*blob/text/gi;
# change not null datetime field to null valid ones
# (to support remapping of "zero time" to null
s/datetime not null/datetime/i;
s/datetime/timestamp/i;
# tinytext/mediumtext -> text
s/tinytext/text/gi;
s/mediumtext/text/gi;
# nuke size of timestamp
s/timestamp\([^)]*\)/timestamp/i;
# char -> varchar
# PostgreSQL would otherwise pad with spaces as opposed
# to MySQL! Your user interface may depend on this!
s/(\s+)char/${1}varchar/gi;
# double -> numeric
s/^double/numeric/i;
s/(\s*)double/${1}numeric/i;
# nuke date representation (not supported in PostgreSQL)
s/datetime default '[^']+'/datetime/i;
s/date default '[^']+'/datetime/i;
s/time default '[^']+'/datetime/i;
# float -> numeric
s/^float/numeric/i;
s/(\s*)float/${1}numeric/i;
# change not null datetime field to null valid ones
# (to support remapping of "zero time" to null
s/datetime not null/datetime/i;
s/datetime/timestamp/i;
# unique key(field1,field2)
if (/unique key\s*\((\w+\s*,\s*\w+)\)/i) {
s/unique key\s*\((\w+\s*,\s*\w+)\)/UNIQUE\($1\)/i;
$create_sql.=$_;
next;
}
# unique index(field1,field2)
if (/unique index\s*\((\w+\s*,\s*\w+)\)/i) {
s/unique index\s*\((\w+\s*,\s*\w+)\)/UNIQUE\($1\)/i;
$create_sql.=$_;
next;
}
# nuke size of timestamp
s/timestamp\([^)]*\)/timestamp/i;
# unique key [name] (field)
if (/unique key\s*(\w*)\s*\((\w+)\)/i) {
s/unique key\s*(\w*)\s*\((\w+)\)/UNIQUE\($2\)/i;
my $idxname=($1?"$1":"idx_${table}_$2");
$create_sql.=$_;
$create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
next;
}
# unique index [name] (field)
if (/unique index\s*(\w*)\s*\((\w+)\)/i) {
s/unique index\s*(\w*)\s*\((\w+)\)/UNIQUE\($2\)/i;
my $idxname=($1?"$1":"idx_${table}_$2");
$create_sql.=$_;
$create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
next;
}
# unique (field) et unique (field1, field2 ...)
if (/unique\s*\(([\w,\s]+)\)/i) {
s/unique\s*\(([\w,\s]+)\)/UNIQUE\($1\)/i;
my $fieldlist="$1";
my $idxname="idx_${table}_${fieldlist}";
$idxname =~ s/\W/_/g; $idxname =~ tr/_/_/s;
$create_sql.=$_;
$create_index .= "CREATE INDEX $idxname ON $table ($fieldlist);\n";
next;
}
# double -> numeric
s/^double/numeric/i;
s/(\s*)double/${1}numeric/i;
# index(field)
if (/index\s*(\w*)\s*\((\w+)\)/i) {
my $idxname=($1?"$1":"idx_${table}_$2");
$create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
next;
}
# float -> numeric
s/^float/numeric/i;
s/(\s*)float/${1}numeric/i;
# primary key
if (/\bkey\b/i && !/^\s+primary key\s+/i) {
s/KEY(\s+)[^(]*(\s+)/$1 UNIQUE $2/i; # hack off name of the non-primary key
}
# unique key(field1,field2)
if (/unique key\s*\((\w+\s*,\s*\w+)\)/i) {
s/unique key\s*\((\w+\s*,\s*\w+)\)/UNIQUE\($1\)/i;
$create_sql .= $_;
next;
}
# key(xxx)
if (/key\s*\((\w+)\)/i) {
my $idxname="idx_${table}_$1";
$create_index .= "CREATE INDEX $idxname ON $table ($1);\n";
next;
}
# unique index(field1,field2)
if (/unique index\s*\((\w+\s*,\s*\w+)\)/i) {
s/unique index\s*\((\w+\s*,\s*\w+)\)/UNIQUE\($1\)/i;
$create_sql .= $_;
next;
}
# Quote column names
s/(^\s*)([^\s\-\(]+)(\s*)/$1"$2"$3/gi if (!/\bkey\b/i);
# unique key [name] (field)
if (/unique key\s*(\w*)\s*\((\w+)\)/i) {
s/unique key\s*(\w*)\s*\((\w+)\)/UNIQUE\($2\)/i;
my $idxname = ( $1 ? "$1" : "idx_${table}_$2" );
$create_sql .= $_;
$create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
next;
}
# Remap columns with names of existing system attribute
if (/"oid"/i) {
s/"oid"/"_oid"/g;
print STDERR "WARNING: table $table uses column \"oid\" which is renamed to \"_oid\"\nYou should fix application manually! Press return to continue.";
my $wait=<STDIN>;
}
s/oid/_oid/i if (/key/i && /oid/i); # fix oid in key
$create_sql.=$_;
} # END of if ($create_sql ne "") i.e. were inside create table statement so processed datatypes
else { # not inside create table
#---- fix data in inserted data: (from MS world)
# FIX: disabled for now
if (00 && /insert into/i) {
s!\x96!-!g; # --
s!\x93!"!g; # ``
s!\x94!"!g; # ''
s!\x85!... !g; # \ldots
s!\x92!`!g;
}
# unique index [name] (field)
if (/unique index\s*(\w*)\s*\((\w+)\)/i) {
s/unique index\s*(\w*)\s*\((\w+)\)/UNIQUE\($2\)/i;
my $idxname = ( $1 ? "$1" : "idx_${table}_$2" );
$create_sql .= $_;
$create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
next;
}
# fix dates '0000-00-00 00:00:00' (should be null)
s/'0000-00-00 00:00:00'/null/gi;
s/'0000-00-00'/null/gi;
s/'00:00:00'/null/gi;
s/([12]\d\d\d)([01]\d)([0-3]\d)([0-2]\d)([0-6]\d)([0-6]\d)/'$1-$2-$3 $4:$5:$6'/;
# unique (field) et unique (field1, field2 ...)
if (/unique\s*\(([\w,\s]+)\)/i) {
s/unique\s*\(([\w,\s]+)\)/UNIQUE\($1\)/i;
my $fieldlist = "$1";
my $idxname = "idx_${table}_${fieldlist}";
$idxname =~ s/\W/_/g;
$idxname =~ tr/_/_/s;
$create_sql .= $_;
$create_index .=
"CREATE INDEX $idxname ON $table ($fieldlist);\n";
next;
}
if (/create\s+table\s+(\w+)/i) {
$create_sql = $_;
/create\s*table\s*(\w+)/i;
$table=$1 if (defined($1));
} else {
print OUT $_;
}
} # end of if inside create_table
} # END while(<IN>)
# index(field)
if (/index\s*(\w*)\s*\((\w+)\)/i) {
my $idxname = ( $1 ? "$1" : "idx_${table}_$2" );
$create_index .= "CREATE INDEX $idxname ON $table ($2);\n";
next;
}
close IN;
close OUT;
# primary key
if ( /\bkey\b/i && !/^\s+primary key\s+/i ) {
s/KEY(\s+)[^(]*(\s+)/$1 UNIQUE $2/i
; # hack off name of the non-primary key
}
# key(xxx)
if (/key\s*\((\w+)\)/i) {
my $idxname = "idx_${table}_$1";
$create_index .= "CREATE INDEX $idxname ON $table ($1);\n";
next;
}
# Quote column names
s/(^\s*)([^\s\-\(]+)(\s*)/$1"$2"$3/gi if ( !/\bkey\b/i );
# Remap columns with names of existing system attribute
if (/"oid"/i) {
s/"oid"/"_oid"/g;
print STDERR
"WARNING: table $table uses column \"oid\" which is renamed to \"_oid\"\nYou should fix application manually! Press return to continue.";
my $wait = <STDIN>;
}
s/oid/_oid/i if ( /key/i && /oid/i ); # fix oid in key
$create_sql .= $_;
} # END of if ($create_sql ne "") i.e. were inside create table statement so processed datatypes
else { # not inside create table
#---- fix data in inserted data: (from MS world)
# FIX: disabled for now
if ( 00 && /insert into/i ) {
s!\x96!-!g; # --
s!\x93!"!g; # ``
s!\x94!"!g; # ''
s!\x85!... !g; # \ldots
s!\x92!`!g;
}
# fix dates '0000-00-00 00:00:00' (should be null)
s/'0000-00-00 00:00:00'/null/gi;
s/'0000-00-00'/null/gi;
s/'00:00:00'/null/gi;
s/([12]\d\d\d)([01]\d)([0-3]\d)([0-2]\d)([0-6]\d)([0-6]\d)/'$1-$2-$3 $4:$5:$6'/;
if (/create\s+table\s+(\w+)/i) {
$create_sql = $_;
/create\s*table\s*(\w+)/i;
$table = $1 if ( defined($1) );
}
else {
print $out $_;
}
} # end of if inside create_table
} # END while(<IN>)
close $in;
close $out;
}
print "\n";
print "Build ".(scalar keys %filelist)." file(s).\n";
print "Build " . ( scalar keys %filelist ) . " file(s).\n";
print "\n";
print "Press a key to finish...\n";
$stop=<STDIN>;
$stop = <STDIN>;
0;

View File

@@ -12,19 +12,24 @@ cp "$0" /tmp/github_commits_perversion.sh
TEMP_DIR=/tmp/git
DOL_GIT="$TEMP_DIR/dolibarr"
if ! git rev-parse ; then
echo "Delete $TEMP_DIR"
echo "/tmp/git/dolibarr is not a git repo. Delete $TEMP_DIR"
rm -fr "$TEMP_DIR"
echo "Create '$TEMP_DIR' and cd to it"
mkdir "$TEMP_DIR"
mkdir -p "$TEMP_DIR"
cd "$TEMP_DIR" || exit
git clone https://github.com/Dolibarr/dolibarr.git
cd "${DOL_GIT}" || exit
else
echo "/tmp/git/dolibarr is a git repo."
mkdir -p ${DOL_GIT}
if [ -r "${DOL_GIT}" ] ; then
echo git worktree remove "${DOL_GIT}"
git worktree remove "${DOL_GIT}"
rm -rf "${DOL_GIT}" >& /dev/null
fi
echo git worktree add --force "${DOL_GIT}" develop
git worktree add --force "${DOL_GIT}" develop
cd "$DOL_GIT" || exit
git pull
@@ -36,7 +41,10 @@ Releases=("3.9" "4.0" "5.0" "6.0" "7.0" "8.0" "9.0" "10.0" "11.0" "12.0" "13.0"
target_version=$(sed -n "s/.*define('DOL_VERSION',[[:space:]]*'\\([0-9]*\\.[0-9]*\\).*/\\1/p" htdocs/version.inc.php)
# Default target version in case getting it from filefunc.inc failed
target_version=${target_version:=20.0}
target_version=${target_version:=23}
echo "Last version to test target_version = $target_version";
# Setup loop to append required versions
target_major=${target_version%%.*}
@@ -90,8 +98,9 @@ do
done
# Clean up git directory if it is a worktree
if [ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ] ; then
cd "$TEMP_DIR" || exit
git -C "$DOL_GIT" worktree remove "$DOL_GIT"
fi
#if [ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ] ; then
# cd "$TEMP_DIR" || exit
# git -C "$DOL_GIT" worktree remove "$DOL_GIT"
#fi
exit

View File

@@ -10,13 +10,13 @@
return [
// # Issue statistics:
// PhanUndeclaredProperty : 420+ occurrences
// PhanTypeMismatchProperty : 100+ occurrences
// PhanTypeMismatchProperty : 95+ occurrences
// PhanTypeMismatchArgument : 65+ occurrences
// PhanUndeclaredGlobalVariable : 60+ occurrences
// PhanTypeMismatchArgumentNullable : 20+ occurrences
// PhanTypeInvalidDimOffset : 15+ occurrences
// PhanTypeMismatchDimFetch : 10+ occurrences
// PhanUndeclaredMethod : 7 occurrences
// PhanUndeclaredMethod : 6 occurrences
// PhanTypeArraySuspiciousNull : 5 occurrences
// PhanTypeExpectedObjectPropAccess : 5 occurrences
// PhanPluginDuplicateArrayKey : 4 occurrences
@@ -76,7 +76,6 @@ return [
'htdocs/core/ajax/selectobject.php' => ['PhanTypeMismatchArgumentNullable'],
'htdocs/core/class/CMailFile.class.php' => ['PhanTypeMismatchArgument'],
'htdocs/core/class/canvas.class.php' => ['PhanUndeclaredMethod'],
'htdocs/core/class/ccountry.class.php' => ['PhanUndeclaredProperty'],
'htdocs/core/class/cgenericdic.class.php' => ['PhanUndeclaredProperty'],
'htdocs/core/class/commonobject.class.php' => ['PhanParamTooMany', 'PhanTypeMismatchArgument', 'PhanUndeclaredProperty'],
'htdocs/core/class/commonpeople.class.php' => ['PhanUndeclaredProperty'],
@@ -146,7 +145,6 @@ return [
'htdocs/core/tpl/objectline_view.tpl.php' => ['PhanUndeclaredProperty'],
'htdocs/core/tpl/passwordreset.tpl.php' => ['PhanUndeclaredGlobalVariable'],
'htdocs/core/tpl/resource_view.tpl.php' => ['PhanUndeclaredProperty'],
'htdocs/core/triggers/interface_20_modWorkflow_WorkflowManager.class.php' => ['PhanUndeclaredProperty'],
'htdocs/core/triggers/interface_50_modAgenda_ActionsAuto.class.php' => ['PhanUndeclaredProperty'],
'htdocs/delivery/class/delivery.class.php' => ['PhanUndeclaredProperty'],
'htdocs/emailcollector/class/emailcollector.class.php' => ['PhanUndeclaredProperty'],
@@ -165,7 +163,7 @@ return [
'htdocs/fourn/class/fournisseur.commande.class.php' => ['PhanUndeclaredProperty'],
'htdocs/fourn/commande/card.php' => ['PhanUndeclaredProperty'],
'htdocs/fourn/facture/card-rec.php' => ['PhanTypeMismatchArgument', 'PhanUndeclaredGlobalVariable', 'PhanUndeclaredProperty'],
'htdocs/fourn/facture/card.php' => ['PhanTypeMismatchArgument', 'PhanTypeMismatchProperty'],
'htdocs/fourn/facture/card.php' => ['PhanTypeMismatchArgument'],
'htdocs/fourn/facture/rapport.php' => ['PhanTypeMismatchArgument'],
'htdocs/fourn/facture/tpl/linkedobjectblock.tpl.php' => ['PhanUndeclaredProperty'],
'htdocs/holiday/card_group.php' => ['PhanTypeMismatchArgument'],
@@ -255,11 +253,9 @@ return [
'htdocs/user/class/usergroup.class.php' => ['PhanUndeclaredProperty'],
'htdocs/variants/tpl/productattributevalueline_edit.tpl.php' => ['PhanUndeclaredProperty'],
'htdocs/variants/tpl/productattributevalueline_view.tpl.php' => ['PhanUndeclaredProperty'],
'htdocs/viewimage.php' => ['PhanUndeclaredMethod'],
'htdocs/webhook/class/target.class.php' => ['PhanUndeclaredMethod'],
'htdocs/webhook/target_card.php' => ['PhanUndeclaredGlobalVariable'],
'htdocs/webportal/admin/setup.php' => ['PhanTypeMismatchArgument'],
'htdocs/webportal/class/html.formcardwebportal.class.php' => ['PhanTypeMismatchArgument', 'PhanUndeclaredProperty'],
'htdocs/webportal/class/html.formlistwebportal.class.php' => ['PhanUndeclaredProperty'],
'htdocs/webportal/class/webportalpropal.class.php' => ['PhanUndeclaredProperty'],
'htdocs/webservices/server_project.php' => ['PhanUndeclaredProperty'],

View File

@@ -46,7 +46,7 @@ $ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$confirm = GETPOST('confirm', 'alpha');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : str_replace('_', '', basename(dirname(__FILE__)).basename(__FILE__, '.php')); // To manage different context of search
$backtopage = GETPOST('backtopage', 'alpha'); // if not set, a default page will be used
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); // if not set, $backtopage will be used

View File

@@ -50,7 +50,7 @@ require_once DOL_DOCUMENT_ROOT.'/accountancy/class/lettering.class.php';
$langs->loadLangs(array("accountancy", "bills", "compta"));
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$confirm = GETPOST('confirm', 'alpha');
$type = GETPOST('type', 'alpha');
@@ -682,7 +682,7 @@ if ($action == 'create') {
print '<table class="nobordernopadding centpercent"><tr><td>';
print $langs->trans('Ref');
print '</td>';
if ($action != 'editref') {
if ($action != 'editref' && empty($object->date_validation)) {
print '<td class="right">';
if ($permissiontoadd && $numRefModel === 'mod_bookkeeping_neon') {
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editref&token='.newToken().'&piece_num='.((int) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
@@ -691,7 +691,7 @@ if ($action == 'create') {
}
print '</tr></table>';
print '</td><td>';
if ($action == 'editref') {
if ($action == 'editref' && empty($object->date_validation)) {
print '<form name="setref" action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
@@ -717,14 +717,14 @@ if ($action == 'create') {
print '</td>';
if ($action != 'editdocref') {
print '<td class="right">';
if ($permissiontoadd) {
if ($permissiontoadd && empty($object->date_validation)) {
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editdocref&token='.newToken().'&piece_num='.((int) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
}
print '</td>';
}
print '</tr></table>';
print '</td><td>';
if ($action == 'editdocref') {
if ($action == 'editdocref' && empty($object->date_validation)) {
print '<form name="setdocref" action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
@@ -750,14 +750,14 @@ if ($action == 'create') {
print '</td>';
if ($action != 'editdate') {
print '<td class="right">';
if ($permissiontoadd) {
if ($permissiontoadd && empty($object->date_validation)) {
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editdate&token='.newToken().'&piece_num='.((int) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('SetDate'), 1).'</a>';
}
print '</td>';
}
print '</tr></table>';
print '</td><td colspan="3">';
if ($action == 'editdate') {
if ($action == 'editdate' && empty($object->date_validation)) {
print '<form name="setdate" action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
@@ -783,14 +783,14 @@ if ($action == 'create') {
print '</td>';
if ($action != 'editjournal') {
print '<td class="right">';
if ($permissiontoadd) {
if ($permissiontoadd && empty($object->date_validation)) {
print '<a class="editfielda reposition" href="'.$_SERVER["PHP_SELF"].'?action=editjournal&token='.newToken().'&piece_num='.((int) $object->piece_num).'&mode='.urlencode((string) $mode).'">'.img_edit($langs->transnoentitiesnoconv('Edit'), 1).'</a>';
}
print '</td>';
}
print '</tr></table>';
print '</td><td>';
if ($action == 'editjournal') {
if ($action == 'editjournal' && empty($object->date_validation)) {
print '<form name="setjournal" action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
@@ -851,7 +851,7 @@ if ($action == 'create') {
print '<tr>';
print '<td class="titlefield">' . $langs->trans("DateExport") . '</td>';
print '<td>';
print $object->date_export ? dol_print_date($object->date_export, 'dayhour') : '&nbsp;';
print $object->date_export ? img_picto($langs->trans("TransactionExportDesc"), 'fa-file-export', 'class="pictofixedwidth"').dol_print_date($object->date_export, 'dayhour') : '&nbsp;';
print '</td>';
print '</tr>';
@@ -859,7 +859,7 @@ if ($action == 'create') {
print '<tr>';
print '<td class="titlefield">' . $langs->trans("DateValidation") . '</td>';
print '<td>';
print $object->date_validation ? dol_print_date($object->date_validation, 'dayhour') : '&nbsp;';
print $object->date_validation ? img_picto($langs->trans("TransactionBlockedLockedDesc"), 'fa-lock', 'class="pictofixedwidth"').dol_print_date($object->date_validation, 'dayhour') : '&nbsp;';
print '</td>';
print '</tr>';
@@ -966,21 +966,23 @@ if ($action == 'create') {
// List of movements
print load_fiche_titre($langs->trans("ListeMvts"), '', '');
print '<form action="'.$_SERVER["PHP_SELF"].'?piece_num='.((int) $object->piece_num).'" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="'.$optioncss.'">';
if (empty($object->date_validation)) {
print '<form action="' . $_SERVER["PHP_SELF"] . '?piece_num=' . ((int) $object->piece_num) . '" method="POST">';
if ($optioncss != '') {
print '<input type="hidden" name="optioncss" value="' . $optioncss . '">';
}
print '<input type="hidden" name="token" value="' . newToken() . '">';
print '<input type="hidden" name="doc_date" value="' . $object->doc_date . '">' . "\n";
print '<input type="hidden" name="doc_type" value="' . $object->doc_type . '">' . "\n";
print '<input type="hidden" name="doc_ref" value="' . $object->doc_ref . '">' . "\n";
print '<input type="hidden" name="ref" value="' . $object->ref . '">' . "\n";
print '<input type="hidden" name="code_journal" value="' . $object->code_journal . '">' . "\n";
print '<input type="hidden" name="fk_doc" value="' . $object->fk_doc . '">' . "\n";
print '<input type="hidden" name="fk_docdet" value="' . $object->fk_docdet . '">' . "\n";
print '<input type="hidden" name="mode" value="' . $mode . '">' . "\n";
print '<input type="hidden" name="backtopage" value="' . $backtopage . '">';
print '<input type="hidden" name="type" value="' . $type . '">';
}
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="doc_date" value="'.$object->doc_date.'">'."\n";
print '<input type="hidden" name="doc_type" value="'.$object->doc_type.'">'."\n";
print '<input type="hidden" name="doc_ref" value="'.$object->doc_ref.'">'."\n";
print '<input type="hidden" name="ref" value="'.$object->ref.'">'."\n";
print '<input type="hidden" name="code_journal" value="'.$object->code_journal.'">'."\n";
print '<input type="hidden" name="fk_doc" value="'.$object->fk_doc.'">'."\n";
print '<input type="hidden" name="fk_docdet" value="'.$object->fk_docdet.'">'."\n";
print '<input type="hidden" name="mode" value="'.$mode.'">'."\n";
print '<input type="hidden" name="backtopage" value="'.$backtopage.'">';
print '<input type="hidden" name="type" value="'.$type.'">';
if (count($object->linesmvt) > 0) {
print '<div class="div-table-responsive-no-min">';
@@ -1005,7 +1007,7 @@ if ($action == 'create') {
print "</tr>\n";
// Add an empty line if there is not yet
if (!empty($object->linesmvt[0])) {
if (!empty($object->linesmvt[0]) && empty($object->date_validation)) {
$tmpline = $object->linesmvt[0];
if (!empty($tmpline->numero_compte)) {
$line = new BookKeepingLine($db);
@@ -1017,7 +1019,7 @@ if ($action == 'create') {
$total_debit += $line->debit;
$total_credit += $line->credit;
if ($action == 'update' && $line->id == $id) {
if ($action == 'update' && $line->id == $id && empty($object->date_validation)) {
print '<tr class="oddeven" data-lineid="'.((int) $line->id).'">';
print '<!-- td columns in edit mode -->';
print '<td>';
@@ -1044,7 +1046,7 @@ if ($action == 'create') {
print '<input type="submit" class="button" name="update" value="'.$langs->trans("Update").'">';
print '</td>';
print "</tr>\n";
} elseif (empty($line->numero_compte) || (empty($line->debit) && empty($line->credit))) {
} elseif ((empty($line->numero_compte) || (empty($line->debit) && empty($line->credit))) && empty($object->date_validation)) {
if (($action == "" || $action == 'add') && $permissiontoadd) {
print '<tr class="oddeven" data-lineid="'.((int) $line->id).'">';
print '<!-- td columns in add mode -->';

View File

@@ -1420,6 +1420,12 @@ while ($i < min($num, $limit)) {
$object->piece_num = $line->piece_num;
$object->ref = $line->ref;
print $object->getNomUrl(1, '', 0, '', 1);
if (!empty($line->date_export)) {
print img_picto($langs->trans("DateExport").": ".dol_print_date($line->date_export, 'dayhour')." (".$langs->trans("TransactionExportDesc").")", 'fa-file-export', 'class="paddingleft pictofixedwidth"');
}
if (!empty($line->date_validation)) {
print img_picto($langs->trans("DateValidation").": ".dol_print_date($line->date_validation, 'dayhour')." (".$langs->trans("TransactionBlockedLockedDesc").")", 'fa-lock', 'class="paddingleft pictofixedwidth"');
}
print '</td>';
if (!$i) {
$totalarray['nbfield']++;

View File

@@ -759,7 +759,7 @@ class AccountingJournal extends CommonObject
}
// Build SQL - Customer invoices closed by discount
$sql = "SELECT f.rowid, f.ref, f.datef, f.fk_soc, f.total_ttc";
$sql = "SELECT f.rowid, f.ref, f.datef, f.date_closing, f.fk_soc, f.total_ttc";
$sql .= " FROM ".MAIN_DB_PREFIX."facture as f";
$sql .= " WHERE f.entity IN (".getEntity('invoice', 0).')'; // We don't share object for accountancy, we use source object sharing
$sql .= " AND f.fk_statut > 0";
@@ -770,19 +770,21 @@ class AccountingJournal extends CommonObject
}
$sql .= " AND f.close_code = 'discount_vat'";
if ($date_start && $date_end) {
$sql .= " AND f.datef >= '".$this->db->idate($date_start)."' AND f.datef <= '".$this->db->idate($date_end)."'";
$sql .= " AND f.date_closing >= '".$this->db->idate($date_start)."' AND f.date_closing <= '".$this->db->idate($date_end)."'";
}
if (getDolGlobalString('ACCOUNTING_DATE_START_BINDING')) {
$sql .= " AND f.datef >= '".$this->db->idate(getDolGlobalInt('ACCOUNTING_DATE_START_BINDING'))."'";
$sql .= " AND f.date_closing >= '".$this->db->idate(getDolGlobalInt('ACCOUNTING_DATE_START_BINDING'))."'";
}
if ($in_bookkeeping == 'already') {
$sql .= " AND EXISTS (SELECT 1 FROM ".MAIN_DB_PREFIX."accounting_bookkeeping ab";
$sql .= " WHERE ab.doc_type = 'customer_invoice' AND ab.fk_doc = f.rowid AND ab.piece_num LIKE 'OD-ESC-%')";
$sql .= " WHERE ab.doc_type = 'customer_invoice' AND ab.fk_doc = f.rowid";
$sql .= " AND ab.code_journal = '".$this->db->escape($this->code)."')";
} elseif ($in_bookkeeping == 'notyet') {
$sql .= " AND NOT EXISTS (SELECT 1 FROM ".MAIN_DB_PREFIX."accounting_bookkeeping ab";
$sql .= " WHERE ab.doc_type = 'customer_invoice' AND ab.fk_doc = f.rowid AND ab.piece_num LIKE 'OD-ESC-%')";
$sql .= " WHERE ab.doc_type = 'customer_invoice' AND ab.fk_doc = f.rowid";
$sql .= " AND ab.code_journal = '".$this->db->escape($this->code)."')";
}
$sql .= " ORDER BY f.datef";
$sql .= " ORDER BY f.date_closing";
dol_syslog(__METHOD__, LOG_DEBUG);
$resql = $this->db->query($sql);
@@ -825,7 +827,8 @@ class AccountingJournal extends CommonObject
}
$bookkeeping_static = new BookKeeping($this->db);
$label_discount = $bookkeeping_static->accountingLabelForOperation($customer_static->getNomUrl(1, 'customer'), $invoice_static->ref, $langs->trans('DiscountGranted'));
$thirdpartyname = (string) $customer_static->name;
$label_discount = $bookkeeping_static->accountingLabelForOperation($thirdpartyname, $invoice_static->ref, $langs->trans('DiscountGranted'));
// Distribution including VAT by rate
$ttcByRate = array();
@@ -853,7 +856,9 @@ class AccountingJournal extends CommonObject
'blocks' => array(),
);
$docdate = $this->db->jdate($obj->datef);
$closingdate = !empty($obj->date_closing) ? $obj->date_closing : $obj->datef;
$docdate = $this->db->jdate($closingdate);
$docdate_fmt = dol_print_date($docdate, 'day');
$sumTTC = 0.0;
@@ -1063,7 +1068,7 @@ class AccountingJournal extends CommonObject
}
// SQL - Supplier invoices closed by discount
$sql = "SELECT ff.rowid, ff.ref, ff.datef, ff.fk_soc, ff.total_ttc";
$sql = "SELECT ff.rowid, ff.ref, ff.datef, ff.date_closing, ff.fk_soc, ff.total_ttc";
$sql .= " FROM ".MAIN_DB_PREFIX."facture_fourn as ff";
$sql .= " WHERE ff.entity IN (".getEntity('facture_fourn', 0).")"; // We don't share object for accountancy
$sql .= " AND ff.fk_statut > 0";
@@ -1074,19 +1079,21 @@ class AccountingJournal extends CommonObject
}
$sql .= " AND ff.close_code = 'discount_vat'";
if ($date_start && $date_end) {
$sql .= " AND ff.datef >= '".$this->db->idate($date_start)."' AND ff.datef <= '".$this->db->idate($date_end)."'";
$sql .= " AND ff.date_closing >= '".$this->db->idate($date_start)."' AND ff.date_closing <= '".$this->db->idate($date_end)."'";
}
if (getDolGlobalString('ACCOUNTING_DATE_START_BINDING')) {
$sql .= " AND ff.datef >= '".$this->db->idate(getDolGlobalInt('ACCOUNTING_DATE_START_BINDING'))."'";
$sql .= " AND ff.date_closing >= '".$this->db->idate(getDolGlobalInt('ACCOUNTING_DATE_START_BINDING'))."'";
}
if ($in_bookkeeping == 'already') {
$sql .= " AND EXISTS (SELECT 1 FROM ".MAIN_DB_PREFIX."accounting_bookkeeping ab";
$sql .= " WHERE ab.doc_type = 'supplier_invoice' AND ab.fk_doc = ff.rowid AND ab.piece_num LIKE 'OD-ESC-FRS-%')";
$sql .= " WHERE ab.doc_type = 'supplier_invoice' AND ab.fk_doc = ff.rowid";
$sql .= " AND ab.code_journal = '".$this->db->escape($this->code)."')";
} elseif ($in_bookkeeping == 'notyet') {
$sql .= " AND NOT EXISTS (SELECT 1 FROM ".MAIN_DB_PREFIX."accounting_bookkeeping ab";
$sql .= " WHERE ab.doc_type = 'supplier_invoice' AND ab.fk_doc = ff.rowid AND ab.piece_num LIKE 'OD-ESC-FRS-%')";
$sql .= " WHERE ab.doc_type = 'supplier_invoice' AND ab.fk_doc = ff.rowid";
$sql .= " AND ab.code_journal = '".$this->db->escape($this->code)."')";
}
$sql .= " ORDER BY ff.datef";
$sql .= " ORDER BY ff.date_closing";
dol_syslog(__METHOD__, LOG_DEBUG);
$resql = $this->db->query($sql);
@@ -1129,7 +1136,8 @@ class AccountingJournal extends CommonObject
}
$bookkeeping_static = new BookKeeping($this->db);
$label_discount = $bookkeeping_static->accountingLabelForOperation($supplier_static->getNomUrl(1, 'supplier'), $invoicesupplier_static->ref, $langs->trans('DiscountReceived'));
$thirdpartyname = (string) $supplier_static->name;
$label_discount = $bookkeeping_static->accountingLabelForOperation($thirdpartyname, $invoicesupplier_static->ref, $langs->trans('DiscountReceived'));
// Distribution including VAT by rate
$ttcByRate = array();
@@ -1157,7 +1165,9 @@ class AccountingJournal extends CommonObject
'blocks' => array(),
);
$docdate = $this->db->jdate($obj->datef);
$closingdate = !empty($obj->date_closing) ? $obj->date_closing : $obj->datef;
$docdate = $this->db->jdate($closingdate);
$docdate_fmt = dol_print_date($docdate, 'day');
$sumTTC = 0.0;

View File

@@ -3,7 +3,7 @@
* Copyright (C) 2007-2010 Jean Heimburger <jean@tiaris.info>
* Copyright (C) 2011 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2013-2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2013-2016 Olivier Geffroy <jeff@jeffinfo.com>
* Copyright (C) 2013-2016 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
@@ -30,15 +30,6 @@
* \brief Page with expense reports journal
*/
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/report.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/bookkeeping.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -47,6 +38,14 @@ require_once DOL_DOCUMENT_ROOT.'/accountancy/class/bookkeeping.class.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/report.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingaccount.class.php';
require_once DOL_DOCUMENT_ROOT.'/expensereport/class/expensereport.class.php';
require_once DOL_DOCUMENT_ROOT.'/user/class/user.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/bookkeeping.class.php';
// Load translation files required by the page
$langs->loadLangs(array("commercial", "compta", "bills", "other", "accountancy", "trips", "errors"));
@@ -356,6 +355,16 @@ if ($action == 'writebookkeeping' && !$error && $user->hasRight('accounting', 'b
if (!$errorforline) {
foreach ($tabht[$key] as $k => $mt) {
if ($mt) {
if (empty($conf->cache['accountingaccountincurrententity'][$k])) {
$accountingaccount = new AccountingAccount($db);
$accountingaccount->fetch(0, $k, true);
$conf->cache['accountingaccountincurrententity'][$k] = $accountingaccount;
} else {
$accountingaccount = $conf->cache['accountingaccountincurrententity'][$k];
}
$account_label = $accountingaccount->label;
// get compte id and label
if ($accountingaccount->fetch(0, $k, true)) {
$bookkeeping = new BookKeeping($db);
@@ -370,9 +379,10 @@ if ($action == 'writebookkeeping' && !$error && $user->hasRight('accounting', 'b
$bookkeeping->subledger_label = '';
$bookkeeping->numero_compte = $k;
$bookkeeping->label_compte = $accountingaccount->label;
$bookkeeping->label_compte = $account_label;
$bookkeeping->label_operation = $bookkeepingstatic->accountingLabelForOperation($userstatic->name, '', $account_label);
$bookkeeping->label_operation = $bookkeepingstatic->accountingLabelForOperation($userstatic->name, '', $accountingaccount->label);
$bookkeeping->montant = $mt;
$bookkeeping->sens = ($mt < 0) ? 'C' : 'D';
$bookkeeping->debit = ($mt > 0) ? $mt : 0;
@@ -418,12 +428,12 @@ if ($action == 'writebookkeeping' && !$error && $user->hasRight('accounting', 'b
foreach ($arrayofvat[$key] as $k => $mt) {
if ($mt) {
if (empty($conf->cache['accountingaccountincurrententity'][$k])) {
if (empty($conf->cache['accountingaccountincurrententity_vat'][$k])) {
$accountingaccount = new AccountingAccount($db);
$accountingaccount->fetch(0, $k, true);
$conf->cache['accountingaccountincurrententity'][$k] = $accountingaccount;
$conf->cache['accountingaccountincurrententity_vat'][$k] = $accountingaccount;
} else {
$accountingaccount = $conf->cache['accountingaccountincurrententity'][$k];
$accountingaccount = $conf->cache['accountingaccountincurrententity_vat'][$k];
}
$account_label = $accountingaccount->label;

View File

@@ -666,7 +666,14 @@ if ($action == 'writebookkeeping' && !$error && $user->hasRight('accounting', 'b
foreach ($arrayofvat[$key] as $k => $mt) {
if ($mt) {
$accountingaccount->fetch(0, $k, true); // TODO Use a cache for label
if (empty($conf->cache['accountingaccountincurrententity_vat'][$k])) {
$accountingaccount = new AccountingAccount($db);
$accountingaccount->fetch(0, $k, true);
$conf->cache['accountingaccountincurrententity_vat'][$k] = $accountingaccount;
} else {
$accountingaccount = $conf->cache['accountingaccountincurrententity_vat'][$k];
}
$label_account = $accountingaccount->label;
$bookkeeping = new BookKeeping($db);

View File

@@ -4,7 +4,7 @@
* Copyright (C) 2011 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2013 Christophe Battarel <christophe.battarel@altairis.fr>
* Copyright (C) 2013-2024 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2013-2025 Alexandre Spangaro <alexandre@inovea-conseil.com>
* Copyright (C) 2013-2016 Florian Henry <florian.henry@open-concept.pro>
* Copyright (C) 2013-2016 Olivier Geffroy <jeff@jeffinfo.com>
* Copyright (C) 2014 Raphaël Doursenaud <rdoursenaud@gpcsolutions.fr>
@@ -747,7 +747,14 @@ if ($action == 'writebookkeeping' && !$error && $user->hasRight('accounting', 'b
foreach ($arrayofvat[$key] as $k => $mt) {
if ($mt) {
$accountingaccount->fetch(0, $k, true); // TODO Use a cache for label
if (empty($conf->cache['accountingaccountincurrententity_vat'][$k])) {
$accountingaccount = new AccountingAccount($db);
$accountingaccount->fetch(0, $k, true);
$conf->cache['accountingaccountincurrententity_vat'][$k] = $accountingaccount;
} else {
$accountingaccount = $conf->cache['accountingaccountincurrententity_vat'][$k];
}
$label_account = $accountingaccount->label;
$bookkeeping = new BookKeeping($db);
@@ -808,7 +815,14 @@ if ($action == 'writebookkeeping' && !$error && $user->hasRight('accounting', 'b
if (isset($tabrevenuestamp[$key]) && is_array($tabrevenuestamp[$key])) {
foreach ($tabrevenuestamp[$key] as $k => $mt) {
if ($mt) {
$accountingaccount->fetch(0, $k, true); // TODO Use a cache for label
if (empty($conf->cache['accountingaccountincurrententity_rs'][$k])) {
$accountingaccount = new AccountingAccount($db);
$accountingaccount->fetch(0, $k, true);
$conf->cache['accountingaccountincurrententity_rs'][$k] = $accountingaccount;
} else {
$accountingaccount = $conf->cache['accountingaccountincurrententity_rs'][$k];
}
$label_account = $accountingaccount->label;
$bookkeeping = new BookKeeping($db);

View File

@@ -93,7 +93,9 @@ if ($action == 'update') {
$res = dolibarr_set_const($db, "MEMBER_MIN_AMOUNT", $minamount, 'chaine', 0, '', $conf->entity);
$res = dolibarr_set_const($db, "MEMBER_COUNTERS_ARE_PUBLIC", $publiccounters, 'chaine', 0, '', $conf->entity);
$res = dolibarr_set_const($db, "MEMBER_SKIP_TABLE", $showtable ? 0 : 1, 'chaine', 0, '', $conf->entity); // Logic is reversed for retrocompatibility: "skip -> show"
$res = dolibarr_set_const($db, "MEMBER_HIDE_VOTE_ALLOWED", $showvoteallowed ? 0 : 1, 'chaine', 0, '', $conf->entity); // Logic is reversed for retrocompatibility: "hide -> show"
if (GETPOSTISSET('MEMBER_HIDE_VOTE_ALLOWED')) {
$res = dolibarr_set_const($db, "MEMBER_HIDE_VOTE_ALLOWED", $showvoteallowed ? 0 : 1, 'chaine', 0, '', $conf->entity); // Logic is reversed for retrocompatibility: "hide -> show"
}
$res = dolibarr_set_const($db, "MEMBER_NEWFORM_PAYONLINE", $payonline, 'chaine', 0, '', $conf->entity);
if ($forcetype < 0) {
$res = dolibarr_del_const($db, "MEMBER_NEWFORM_FORCETYPE", $conf->entity);
@@ -225,6 +227,24 @@ if (getDolGlobalString('MEMBER_ENABLE_PUBLIC')) {
print '<td></td>';
print "</tr>\n";
// Show the table of all available membership types. If not, show a form (as the default was for Dolibarr <=16.0)
$skiptable = getDolGlobalInt('MEMBER_SKIP_TABLE');
print '<tr class="oddeven" id="tredit"><td>';
print $langs->trans("MembersShowMembershipTypesTable");
print '</td><td>';
print $form->selectyesno("MEMBER_SHOW_TABLE", (int) !$skiptable, 1, false, 0, 1); // Reverse the logic "hide -> show" for retrocompatibility
print "</td></tr>\n";
// Show "vote allowed" setting for membership types
if (!$skiptable) {
$hidevoteallowed = getDolGlobalInt('MEMBER_HIDE_VOTE_ALLOWED');
print '<tr class="oddeven" id="tredit"><td>';
print $langs->trans("MembersShowVotesAllowed");
print '</td><td>';
print $form->selectyesno("MEMBER_SHOW_VOTE_ALLOWED", (int) !$hidevoteallowed, 1, false, 0, 1); // Reverse the logic "hide -> show" for retrocompatibility
print "</td></tr>\n";
}
// Force Type
$adht = new AdherentType($db);
print '<tr class="oddeven drag" id="trforcetype"><td>';
@@ -272,22 +292,6 @@ if (getDolGlobalString('MEMBER_ENABLE_PUBLIC')) {
print $form->selectyesno("MEMBER_COUNTERS_ARE_PUBLIC", getDolGlobalInt('MEMBER_COUNTERS_ARE_PUBLIC'), 1, false, 0, 1);
print "</td></tr>\n";
// Show the table of all available membership types. If not, show a form (as the default was for Dolibarr <=16.0)
$skiptable = getDolGlobalInt('MEMBER_SKIP_TABLE');
print '<tr class="oddeven" id="tredit"><td>';
print $langs->trans("MembersShowMembershipTypesTable");
print '</td><td>';
print $form->selectyesno("MEMBER_SHOW_TABLE", (int) !$skiptable, 1, false, 0, 1); // Reverse the logic "hide -> show" for retrocompatibility
print "</td></tr>\n";
// Show "vote allowed" setting for membership types
$hidevoteallowed = getDolGlobalInt('MEMBER_HIDE_VOTE_ALLOWED');
print '<tr class="oddeven" id="tredit"><td>';
print $langs->trans("MembersShowVotesAllowed");
print '</td><td>';
print $form->selectyesno("MEMBER_SHOW_VOTE_ALLOWED", (int) !$hidevoteallowed, 1, false, 0, 1); // Reverse the logic "hide -> show" for retrocompatibility
print "</td></tr>\n";
// Jump to an online payment page
print '<tr class="oddeven" id="trpayment"><td>';
print $langs->trans("MEMBER_NEWFORM_PAYONLINE");

View File

@@ -30,12 +30,6 @@
// Load Dolibarr environment
require '../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -43,10 +37,33 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent_type.class.php';
require_once DOL_DOCUMENT_ROOT.'/contact/class/contact.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
// Load translation files required by the page
$langs->loadLangs(array('companies', 'members'));
$action = GETPOST('action', 'aZ09');
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : getDolDefaultContextPage(__FILE__);
if (GETPOSTISARRAY('actioncode')) {
$actioncode = GETPOST('actioncode', 'array:alpha', 3);
if (!count($actioncode)) {
$actioncode = '0';
}
} else {
$actioncode = GETPOST("actioncode", "alpha", 3) ? GETPOST("actioncode", "alpha", 3) : (GETPOST("actioncode") == '0' ? '0' : getDolGlobalString('AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECT'));
}
$search_rowid = GETPOST('search_rowid');
$search_agenda_label = GETPOST('search_agenda_label');
$search_complete = GETPOST('search_complete');
$search_dateevent_start = GETPOSTDATE('dateevent_start');
$search_dateevent_end = GETPOSTDATE('dateevent_end');
// Get Parameters
$id = GETPOSTINT('id') ? GETPOSTINT('id') : GETPOSTINT('rowid');
@@ -66,20 +83,9 @@ if (!$sortfield) {
$sortfield = 'a.datep,a.id';
}
if (!$sortorder) {
$sortorder = 'DESC';
$sortorder = 'DESC,DESC';
}
if (GETPOST('actioncode', 'array')) {
$actioncode = GETPOST('actioncode', 'array', 3);
if (!count($actioncode)) {
$actioncode = '0';
}
} else {
$actioncode = GETPOST("actioncode", "alpha", 3) ? GETPOST("actioncode", "alpha", 3) : (GETPOST("actioncode") == '0' ? '0' : getDolGlobalString('AGENDA_DEFAULT_FILTER_TYPE_FOR_OBJECT'));
}
$search_rowid = GETPOST('search_rowid');
$search_agenda_label = GETPOST('search_agenda_label');
// Get object canvas (By default, this is not defined, so standard usage of dolibarr)
$objcanvas = null;
@@ -104,7 +110,7 @@ if ($result > 0) {
* Actions
*/
$parameters = array('id'=>$id, 'objcanvas'=>$objcanvas);
$parameters = array('id' => $id, 'objcanvas' => $objcanvas);
$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');
@@ -122,6 +128,7 @@ if (empty($reshook)) {
$actioncode = '';
$search_rowid = '';
$search_agenda_label = '';
$search_complete = '';
}
}
@@ -172,6 +179,8 @@ if ($object->id > 0) {
print '</div>';
print '<div class="clearboth"></div>';
print dol_get_fiche_end();
@@ -187,7 +196,7 @@ if ($object->id > 0) {
$newcardbutton .= dolGetButtonTitle($langs->trans('MessageListViewType'), '', 'fa fa-bars imgforviewmode', $messagingUrl, '', 2);
if (isModEnabled('agenda')) {
$newcardbutton .= dolGetButtonTitle($langs->trans('AddAction'), '', 'fa fa-plus-circle', dolBuildUrl(DOL_URL_ROOT.'/comm/action/card.php', ['action' => 'create', 'backtopage' => dolBuildUrl($_SERVER['PHP_SELF'], ['id' => $object->id, 'origin' => 'member', 'originid' => $id])]));
$newcardbutton .= dolGetButtonTitle($langs->trans('AddAction'), '', 'fa fa-plus-circle', dolBuildUrl(DOL_URL_ROOT.'/comm/action/card.php', ['action' => 'create', 'origin' => 'member', 'originid' => $id, 'backtopage' => dolBuildUrl($_SERVER['PHP_SELF'], ['id' => $object->id, 'origin' => 'member', 'originid' => $id])]));
}
if (isModEnabled('agenda') && ($user->hasRight('agenda', 'myactions', 'read') || $user->hasRight('agenda', 'allactions', 'read'))) {
@@ -195,12 +204,35 @@ if ($object->id > 0) {
$param = '&id='.$id;
if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) {
$param .= '&contextpage='.$contextpage;
$param .= '&contextpage='.urlencode($contextpage);
}
if ($limit > 0 && $limit != $conf->liste_limit) {
$param .= '&limit='.$limit;
$param .= '&limit='.((int) $limit);
}
if ($search_rowid) {
$param .= '&search_rowid='.urlencode($search_rowid);
}
if ($actioncode !== '' && $actioncode !== '-1') {
$param .= '&actioncode='.urlencode($actioncode);
}
if ($search_agenda_label) {
$param .= '&search_agenda_label='.urlencode($search_agenda_label);
}
if ($search_complete != '') {
$param .= '&search_complete='.urlencode($search_complete);
}
if ($search_dateevent_start != '') {
$param .= '&dateevent_startyear='.GETPOSTINT('dateevent_startyear');
$param .= '&dateevent_startmonth='.GETPOSTINT('dateevent_startmonth');
$param .= '&dateevent_startday='.GETPOSTINT('dateevent_startday');
}
if ($search_dateevent_end != '') {
$param .= '&dateevent_endyear='.GETPOSTINT('dateevent_endyear');
$param .= '&dateevent_endmonth='.GETPOSTINT('dateevent_endmonth');
$param .= '&dateevent_endday='.GETPOSTINT('dateevent_endday');
}
// Try to know count of actioncomm from cache
require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
$cachekey = 'count_events_member_'.$object->id;
$nbEvent = dol_getcache($cachekey);
@@ -210,15 +242,16 @@ if ($object->id > 0) {
$titlelist = $langs->trans("Actions").(is_numeric($nbEvent) ? '<span class="opacitymedium colorblack paddingleft">('.$nbEvent.')</span>' : '');
}
print_barre_liste($titlelist, 0, $_SERVER["PHP_SELF"], '', $sortfield, $sortorder, '', 0, -1, '', 0, $newcardbutton, '', 0, 1, 0);
print_barre_liste($titlelist, 0, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, '', 0, -1, '', 0, $newcardbutton, '', 0, 1, 0);
// List of all actions
$filters = array();
$filters['search_agenda_label'] = $search_agenda_label;
$filters['search_rowid'] = $search_rowid;
$filters['search_complete'] = $search_complete; // Can be 'na', '0', '100', '50'
// TODO Replace this with same code than into list.php
show_actions_done($conf, $langs, $db, $object, null, 0, $actioncode, '', $filters, $sortfield, $sortorder);
show_actions_done($conf, $langs, $db, $object, null, 0, $actioncode, '', $filters, $sortfield, $sortorder, $object->module);
}
}

View File

@@ -1914,16 +1914,16 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
// Login
if (!getDolGlobalString('ADHERENT_LOGIN_NOT_REQUIRED')) {
print '<tr><td class="titlefield">'.$langs->trans("Login").' / '.$langs->trans("Id").'</td><td class="valeur">'.dol_escape_htmltag($object->login).'</td></tr>';
print '<tr><td class="titlefieldmiddle">'.$langs->trans("Login").' / '.$langs->trans("Id").'</td><td class="valeur">'.dol_escape_htmltag($object->login).'</td></tr>';
}
// Type
print '<tr><td class="titlefield">'.$langs->trans("Type").'</td>';
print '<tr><td class="titlefieldmiddle">'.$langs->trans("Type").'</td>';
print '<td class="valeur">'.$adht->getNomUrl(1)."</td></tr>\n";
// Morphy
print '<tr><td>'.$langs->trans("MemberNature").'</td>';
print '<td class="valeur" >'.$object->getmorphylib('', 1).'</td>';
print '<td class="valeur">'.$object->getmorphylib('', 1).'</td>';
print '</tr>';
// Company
@@ -1988,13 +1988,13 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
// Tags / Categories
if (isModEnabled('category') && $user->hasRight('categorie', 'lire')) {
print '<tr><td>'.$langs->trans("Categories").'</td>';
print '<td colspan="2">';
print '<td>';
print $form->showCategories($object->id, Categorie::TYPE_MEMBER, 1);
print '</td></tr>';
}
// Birth Date
print '<tr><td class="titlefield">'.$langs->trans("DateOfBirth").'</td><td class="valeur">'.dol_print_date($object->birth, 'day').'</td></tr>';
print '<tr><td class="titlefieldmiddle">'.$langs->trans("DateOfBirth").'</td><td class="valeur">'.dol_print_date($object->birth, 'day').'</td></tr>';
// Default language
if (getDolGlobalInt('MAIN_MULTILANGS')) {
@@ -2095,7 +2095,7 @@ if (is_object($objcanvas) && $objcanvas->displayCanvasExists($action)) {
// Send
if (empty($user->socid)) {
if (Adherent::STATUS_VALIDATED == $object->status) {
print '<a class="butAction" href="'.dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id, 'action' => 'presend', 'mode' => 'init'], true).'#formmailbeforetitle">'.$langs->trans('SendMail').'</a>'."\n";
print dolGetButtonAction('', $langs->trans('SendMail'), 'email', dolBuildUrl($_SERVER["PHP_SELF"], ['id' => $object->id, 'action' => 'presend', 'mode' => 'init'], true).'#formmailbeforetitle', '');
}
}

View File

@@ -2164,6 +2164,7 @@ class Adherent extends CommonObject
// Generate PDF (whatever is option MAIN_DISABLE_PDF_AUTOUPDATE) so we can include it into email
//if (!getDolGlobalString('MAIN_DISABLE_PDF_AUTOUPDATE'))
$invoice->fetch($invoice->id); // Reload invoice object data
$invoice->generateDocument($invoice->model_pdf, $outputlangs);
}
}

View File

@@ -674,7 +674,7 @@ class AdherentType extends CommonObject
* Return the array of all amounts per membership type id
*
* @param int $status Filter on status of type
* @return array<int,string> Array of membership type
* @return array<int,float> Array of membership type
*/
public function amountByType($status = null)
{
@@ -696,7 +696,7 @@ class AdherentType extends CommonObject
while ($i < $nump) {
$obj = $this->db->fetch_object($resql);
$amountbytype[$obj->rowid] = $obj->amount;
$amountbytype[$obj->rowid] = (float) $obj->amount;
$i++;
}
}

View File

@@ -544,9 +544,12 @@ class Members extends DolibarrApi
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
/**
* Clean sensible object datas
* @phpstan-template T
*
* @param Object $object Object to clean
* @return Object Object with cleaned properties
* @phpstan-param T $object
* @phpstan-return T
*/
public function _cleanObjectDatas($object)
{

View File

@@ -289,9 +289,12 @@ class MembersTypes extends DolibarrApi
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
/**
* Clean sensible object datas
* @phpstan-template T
*
* @param Object $object Object to clean
* @return Object Object with cleaned properties
* @phpstan-param T $object
* @phpstan-return T
*/
protected function _cleanObjectDatas($object)
{

View File

@@ -147,11 +147,11 @@ if (is_object($adht)) {
}
// Type
print '<tr><td>'.$langs->trans("Type").'</td>';
print '<tr><td class="titlefield">'.$langs->trans("Type").'</td>';
print '<td class="valeur">'.$adht->getNomUrl(1)."</td></tr>\n";
// Morphy
print '<tr><td class="titlefield">'.$langs->trans("MemberNature").'</td>';
print '<tr><td>'.$langs->trans("MemberNature").'</td>';
print '<td class="valeur" >'.$object->getmorphylib('', 1).'</td>';
print '</tr>';

View File

@@ -50,7 +50,7 @@ $id = GETPOSTINT('rowid') ? GETPOSTINT('rowid') : GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$confirm = GETPOST('confirm', 'alpha');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'partnershipcard'; // To manage different context of search
$backtopage = GETPOST('backtopage', 'alpha');
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');

View File

@@ -26,9 +26,6 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -36,6 +33,8 @@ require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
$graphwidth = 700;
$mapratio = 0.5;
@@ -191,9 +190,9 @@ foreach ($data as $val) {
print '<td>'.$memberstatic->getmorphylib($val['label']).'</td>';
print '<td class="right">'.$nb.'</td>';
print '<td class="right">'.$nbactive.'</td>';
print '<td class="center">'.dol_print_date($val['lastdate'], 'dayhour').'</td>';
print '<td class="center">'.dol_print_date($val['lastdate'], 'dayhour', 'auto', null, false, 1).'</td>';
print '<td class="right">'.$nbsubscriptions.'</td>';
print '<td class="center">'.dol_print_date($val['lastsubscriptiondate'], 'dayhour').'</td>';
print '<td class="center">'.dol_print_date($val['lastsubscriptiondate'], 'dayhour', 'auto', null, false, 1).'</td>';
print '</tr>';
}

View File

@@ -26,11 +26,6 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -38,12 +33,16 @@ require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/functions2.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/dolgraph.class.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
$graphwidth = DolGraph::getDefaultGraphSizeForStats('width', '700');
$mapratio = 0.5;
$graphheight = round($graphwidth * $mapratio);
$mode = GETPOST('mode') ? GETPOST('mode') : '';
$mode = GETPOST('mode');
// Security check
@@ -54,7 +53,7 @@ if ($user->socid > 0) {
restrictedArea($user, 'adherent', '', '', 'cotisation');
$year = (int) dol_print_date(dol_now('gmt'), "%Y", 'gmt');
$startyear = $year - (!getDolGlobalString('MAIN_STATS_GRAPHS_SHOW_N_YEARS') ? 2 : max(1, min(10, getDolGlobalString('MAIN_STATS_GRAPHS_SHOW_N_YEARS'))));
$startyear = $year - (getDolGlobalString('MAIN_STATS_GRAPHS_SHOW_N_YEARS') ? max(1, min(10, getDolGlobalString('MAIN_STATS_GRAPHS_SHOW_N_YEARS'))) : 2);
$endyear = $year;
// Load translation files required by the page
@@ -322,8 +321,8 @@ if ($mode) {
print '<td class="center">'.$val['label2'].'</td>';
}
print '<td class="right">'.$val['nb'].'</td>';
print '<td class="center">'.dol_print_date($val['lastdate'], 'dayhour').'</td>';
print '<td class="center">'.dol_print_date($val['lastsubscriptiondate'], 'dayhour').'</td>';
print '<td class="center">'.dol_print_date($val['lastdate'], 'dayhour', 'auto', null, false, 1).'</td>';
print '<td class="center">'.dol_print_date($val['lastsubscriptiondate'], 'dayhour', 'auto', null, false, 1).'</td>';
print '</tr>';
}

View File

@@ -31,6 +31,14 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/member.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/adherents/class/adherent.class.php';
@@ -41,15 +49,6 @@ require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php';
require_once DOL_DOCUMENT_ROOT.'/product/class/product.class.php';
require_once DOL_DOCUMENT_ROOT.'/accountancy/class/accountingjournal.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Societe $mysoc
* @var Translate $langs
* @var User $user
*/
$langs->loadLangs(array("companies", "bills", "members", "users", "mails", 'other'));
$action = GETPOST('action', 'aZ09');
@@ -61,7 +60,7 @@ $id = GETPOSTINT('rowid') ? GETPOSTINT('rowid') : GETPOSTINT('id');
$rowid = $id;
$ref = GETPOST('ref', 'alphanohtml');
$typeid = GETPOSTINT('typeid');
$cancel = GETPOST('cancel');
$cancel = GETPOST('cancel', 'alpha');
// Load variable for pagination
$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
@@ -509,11 +508,11 @@ print '<table class="border centpercent tableforfield">';
// Login
if (!getDolGlobalString('ADHERENT_LOGIN_NOT_REQUIRED')) {
print '<tr><td class="titlefield">'.$langs->trans("Login").' / '.$langs->trans("Id").'</td><td class="valeur">'.dol_escape_htmltag($object->login).'</td></tr>';
print '<tr><td class="titlefieldmiddle">'.$langs->trans("Login").' / '.$langs->trans("Id").'</td><td class="valeur">'.dol_escape_htmltag($object->login).'</td></tr>';
}
// Type
print '<tr><td class="titlefield">'.$langs->trans("Type").'</td>';
print '<tr><td class="titlefieldmiddle">'.$langs->trans("Type").'</td>';
print '<td class="valeur">'.$adht->getNomUrl(1)."</td></tr>\n";
// Morphy
@@ -589,7 +588,7 @@ if (isModEnabled('category') && $user->hasRight('categorie', 'lire')) {
}
// Birth Date
print '<tr><td class="titlefield">'.$langs->trans("DateOfBirth").'</td><td class="valeur">'.dol_print_date($object->birth, 'day').'</td></tr>';
print '<tr><td class="titlefieldmiddle">'.$langs->trans("DateOfBirth").'</td><td class="valeur">'.dol_print_date($object->birth, 'day').'</td></tr>';
// Default language
if (getDolGlobalInt('MAIN_MULTILANGS')) {
@@ -1021,7 +1020,7 @@ if (($action == 'addsubscription' || $action == 'create_thirdparty') && $user->h
if ($adht->subscription) {
// Amount
print '<tr><td class="fieldrequired">'.$langs->trans("Amount").'</td>';
print '<td><input autofocus class="width50" type="text" name="subscription" value="'.(GETPOSTISSET('subscription') ? GETPOST('subscription') : (is_null($adht->amount) ? '' : price($adht->amount, 0, '', 0))).'"> '.$langs->trans("Currency".$conf->currency) .'</td></tr>';
print '<td><input autofocus class="width50" type="text" name="subscription" value="'.(GETPOSTISSET('subscription') ? GETPOST('subscription') : (is_null($adht->amount) ? '' : price($adht->amount, 0, '', 0))).'"> '.$langs->trans("Currency".getDolCurrency()) .'</td></tr>';
// Label
print '<tr><td>'.$langs->trans("Label").'</td>';

View File

@@ -485,7 +485,13 @@ if (!$rowid && $action != 'create' && $action != 'edit') {
print '<td class="center">'.yn($objp->subscription).'</td>';
}
if (!empty($arrayfields['t.amount']['checked'])) {
print '<td class="center"><span class="amount">'.(is_null($objp->amount) || $objp->amount === '' ? '' : price($objp->amount)).'</span></td>';
print '<td class="center">';
$amount = (is_null($objp->amount) || $objp->amount === '' ? '' : price($objp->amount));
print '<span class="amount">'.$amount.'</span>';
if ($amount && $amount < (float) getDolGlobalInt("MEMBER_MIN_AMOUNT")) {
print img_warning('Amount lower than minimum of '.price(getDolGlobalInt("MEMBER_MIN_AMOUNT")).' defined in setup');
}
print '</td>';
}
if (!empty($arrayfields['t.caneditamount']['checked'])) {
print '<td class="center">'.yn($objp->caneditamount).'</td>';
@@ -645,7 +651,11 @@ if ($rowid > 0) {
// Amount
print '<tr><td class="titlefield">'.$langs->trans("Amount").'</td><td>';
print((is_null($object->amount) || $object->amount === '') ? '' : '<span class="amount">'.price($object->amount).'</span>');
$amount = ((is_null($object->amount) || $object->amount === '') ? '' : price($object->amount));
print '<span class="amount">'.$amount.'</span>';
if ($amount && $amount < (float) getDolGlobalInt("MEMBER_MIN_AMOUNT")) {
print ' '.img_warning('Amount lower than minimum of '.price(getDolGlobalInt("MEMBER_MIN_AMOUNT")).' defined in setup');
}
print '</tr>';
print '<tr><td>'.$form->textwithpicto($langs->trans("CanEditAmountShort"), $langs->transnoentities("CanEditAmount")).'</td><td>';
@@ -944,7 +954,6 @@ if ($rowid > 0) {
$adh->firstname = $objp->firstname;
$adh->datefin = $datefin;
$adh->need_subscription = $objp->subscription;
$adh->statut = $objp->status;
$adh->status = $objp->status;
$adh->email = $objp->email;
$adh->photo = $objp->photo;
@@ -1089,8 +1098,9 @@ if ($rowid > 0) {
print '</td></tr>';
print '<tr><td>'.$langs->trans("Amount").'</td><td>';
$amount = ((is_null($object->amount) || $object->amount === '') ? '' : price($object->amount));
print '<input name="amount" size="5" value="';
print((is_null($object->amount) || $object->amount === '') ? '' : price($object->amount));
print $amount;
print '">';
print '</td></tr>';

View File

@@ -470,7 +470,7 @@ print '<input name="name" id="name" maxlength="'.$mysoc->fields['nom']['length']
// Main currency
print '<tr class="oddeven"><td class="fieldrequired"><label for="currency">'.$langs->trans("CompanyCurrency").'</label></td><td>';
print img_picto('', 'multicurrency', 'class="pictofixedwidth"');
print $form->selectCurrency($conf->currency, "currency", 2);
print $form->selectCurrency(getDolCurrency(), "currency", 2);
print '</td></tr>'."\n";
// Country

View File

@@ -67,7 +67,7 @@ $id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$confirm = GETPOST('confirm', 'alpha');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'emailcollectorcard'; // To manage different context of search
$backtopage = GETPOST('backtopage', 'alpha');

View File

@@ -44,7 +44,7 @@ $langs->loadLangs(array("admin", "eventorganization", "categories"));
// Parameters
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
$value = GETPOST('value', 'alpha');

View File

@@ -46,7 +46,7 @@ $langs->loadLangs(array("admin", "eventorganization", "categories"));
// Parameters
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
$value = GETPOST('value', 'alpha');

View File

@@ -341,7 +341,7 @@ foreach ($rules as $rule) {
if ($action == 'edit' && $object->id == $rule->id) {
echo '<input type="text" value="' . price2num($object->amount) . '" name="amount" class="amount width50 right" />';
} else {
echo price($rule->amount, 0, $langs, 1, -1, -1, $conf->currency);
echo price($rule->amount, 0, $langs, 1, -1, -1, getDolCurrency());
}
echo '</td>';

View File

@@ -83,12 +83,23 @@ if (getDolGlobalString('MAIN_MOTD_SETUPPAGE')) {
print '<span class="opacitymedium hideonsmartphone">';
print $langs->trans("SetupDescription1").'<br>';
//print $langs->trans("AreaForAdminOnly").' ';
print '<br>';
print $langs->trans("SetupDescription2", $langs->transnoentities("MenuCompanySetup"), $langs->transnoentities("Modules"));
if (!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') || !getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY') || getDolGlobalString('MAIN_INFO_SOCIETE_SETUP_TODO_WARNING')) {
$setupcompanynotcomplete = 1;
} else {
$setupcompanynotcomplete = 0;
}
if ($setupcompanynotcomplete) {
print '<br>';
print $langs->trans("SetupDescription2", $langs->transnoentities("MenuCompanySetup"), $langs->transnoentities("Modules"));
}
print "<br><br>";
print '</span>';
// Show info depending on country if defined
$constkey = 'MAIN_INFO_SETUP_FOR_COUNTRY_'.$mysoc->country_code;
//$conf->global->$constkey = 'rrr';
@@ -104,12 +115,6 @@ print '<br>';
// Show info setup company
if (!getDolGlobalString('MAIN_INFO_SOCIETE_NOM') || !getDolGlobalString('MAIN_INFO_SOCIETE_COUNTRY') || getDolGlobalString('MAIN_INFO_SOCIETE_SETUP_TODO_WARNING')) {
$setupcompanynotcomplete = 1;
} else {
$setupcompanynotcomplete = 0;
}
print '<section class="setupsection setupcompany cursorpointer">';
print img_picto('', 'company', 'class="paddingright valignmiddle double"');

View File

@@ -42,12 +42,12 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/price.lib.php';
$langs->loadLangs(array('companies', 'products', 'admin'));
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$currencycode = GETPOST('currencycode', 'alpha');
if (isModEnabled('multicompany') && getDolGlobalString('MULTICURRENCY_USE_LIMIT_BY_CURRENCY')) {
// When MULTICURRENCY_USE_LIMIT_BY_CURRENCY is on, we use always a defined currency code instead of '' even for default.
$currencycode = (!empty($currencycode) ? $currencycode : $conf->currency);
$currencycode = (!empty($currencycode) ? $currencycode : getDolCurrency());
}
$mainmaxdecimalsunit = 'MAIN_MAX_DECIMALS_UNIT'.(!empty($currencycode) ? '_'.$currencycode : '');
@@ -137,14 +137,14 @@ llxHeader('', $title, $help_url, '', 0, 0, '', '', '', 'mod-admin page-limits');
print load_fiche_titre($title, '', 'title_setup');
$aCurrencies = array($conf->currency); // Default currency always first position
$aCurrencies = array(getDolCurrency()); // Default currency always first position
if (isModEnabled('multicompany') && getDolGlobalString('MULTICURRENCY_USE_LIMIT_BY_CURRENCY')) {
require_once DOL_DOCUMENT_ROOT . '/core/lib/multicurrency.lib.php';
$sql = "SELECT rowid, code FROM " . MAIN_DB_PREFIX . "multicurrency";
$sql .= " WHERE entity = " . ((int) $conf->entity);
$sql .= " AND code <> '" . $db->escape($conf->currency) . "'"; // Default currency always first position
$sql .= " AND code <> '" . $db->escape(getDolCurrency()) . "'"; // Default currency always first position
$resql = $db->query($sql);
if ($resql) {
while ($obj = $db->fetch_object($resql)) {

View File

@@ -45,7 +45,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/geturl.lib.php';
$langs->loadLangs(array("companies", "products", "admin", "mails", "other", "errors"));
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$trackid = GETPOST('trackid');

View File

@@ -42,7 +42,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$langs->loadLangs(array('companies', 'products', 'admin', 'mails', 'other', 'errors'));
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$usersignature = $user->signature;
// For action = test or send, we ensure that content is not html, even for signature, because this we want a test with NO html.

View File

@@ -41,7 +41,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$langs->loadLangs(array("companies", "products", "admin", "mails", "other", "errors"));
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$trackid = GETPOST('trackid');

View File

@@ -42,7 +42,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$langs->loadLangs(array('companies', 'products', 'admin', 'mails', 'other', 'errors'));
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$usersignature = $user->signature;
// For action = test or send, we ensure that content is not html, even for signature, because this we want a test with NO html.

View File

@@ -38,6 +38,13 @@
// Load Dolibarr environment
require '../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
@@ -47,14 +54,6 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/cemailtemplate.class.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
// Load translation files required by the page
$langsArray = array("errors", "admin", "mails", "languages");
@@ -115,6 +114,26 @@ if (empty($sortorder)) {
// Initialize a technical object to manage hooks of page. Note that conf->hooks_modules contains an array of hook context
$hookmanager->initHooks(array('emailtemplates'));
$object = new CEmailTemplate($db);
// Definition of array of fields for columns from ->fields
$tableprefix = 't';
$arrayfields = array();
foreach ($object->fields as $key => $val) {
// If $val['visible']==0, then we never show the field
if (!empty($val['visible'])) {
$visible = (int) dol_eval((string) $val['visible'], 1);
$arrayfields[$tableprefix.'.'.$key] = array(
'label' => $val['label'],
'checked' => (($visible < 0) ? '0' : '1'),
'enabled' => (string) (int) (abs($visible) != 3 && (bool) dol_eval((string) $val['enabled'], 1)),
'position' => $val['position'],
'help' => isset($val['help']) ? $val['help'] : ''
);
}
}
// Old way to define field.
// Name of SQL tables of dictionaries
$tabname = array();
@@ -123,7 +142,7 @@ $tabname[25] = MAIN_DB_PREFIX."c_email_templates";
// Nom des champs en resultat de select pour affichage du dictionnaire
// Names of fields in select results for dictionary display (AI translated)
$tabfield = array();
$tabfield[25] = "label,lang,type_template,fk_user,private,position,module,topic,joinfiles,defaultfortype,content";
$tabfield[25] = "label,lang,type_template,fk_user,position,module,topic,joinfiles,defaultfortype,content";
if (getDolGlobalString('MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES')) {
$tabfield[25] .= ',content_lines';
}
@@ -145,10 +164,6 @@ if (getDolGlobalString('MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES')) {
}
$tabfieldinsert[25] .= ',entity'; // Must be at end because not into other arrays
// Condition to show dictionary in setup page
$tabcond = array();
$tabcond[25] = true;
// List of help for fields
// Set MAIN_EMAIL_TEMPLATES_FOR_OBJECT_LINES to allow edit of template for lines
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php';
@@ -638,7 +653,8 @@ if (!empty($user->admin) && (empty($_SESSION['leftmenu']) || $_SESSION['leftmenu
$morejs = array();
$morecss = array();
$sql = "SELECT rowid as rowid, module, label, type_template, lang, fk_user, private, position, topic, email_from,joinfiles, defaultfortype, content_lines, content, enabled, active, tms, datec";
$sql = "SELECT rowid as rowid, module, label, type_template, lang, fk_user, private, position, topic, email_from, joinfiles, defaultfortype,";
$sql .= " content_lines, content, enabled, active, tms, datec";
$sql .= " FROM ".MAIN_DB_PREFIX."c_email_templates";
$sql .= " WHERE entity IN (".getEntity('email_template').")";
if (!$user->admin) {
@@ -1025,9 +1041,14 @@ foreach ($fieldlist as $field => $value) {
}*/
// Status
print '<td></td>';
// Have to expand the id="Title line with search boxes" with 2 extra fields because the line below id="Title of lines" are 2 fields longer
print '<td></td>'; // tms / Modif. date
print '<td></td>'; // datec / Date creation
if (!empty($arrayfields['t.tms']['checked'])) {
print '<td></td>'; // tms / Modif. date
}
if (!empty($arrayfields['t.datec']['checked'])) {
print '<td></td>'; // datec / Date creation
}
// Action column
if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
print '<td class="liste_titre center" width="64">';
@@ -1104,6 +1125,12 @@ foreach ($fieldlist as $field => $value) {
$valuetoshow = $langs->trans("ContentForLines");
$showfield = 0;
}
if ($value == 'tms' && empty($arrayfields['t'.$value]['checked'])) {
$showfield = 0;
}
if ($value == 'datec' && empty($arrayfields['t.'.$value]['checked'])) {
$showfield = 0;
}
// Show fields
if ($showfield) {
@@ -1153,16 +1180,19 @@ if ($num) {
$colspan = 0;
print '<tr><td colspan="12">';
print '<input type="hidden" name="page" value="'.$page.'">';
print '<input type="hidden" name="rowid" value="'.$rowid.'">';
print '<div name="'.(!empty($obj->rowid) ? $obj->rowid : $obj->code).'"></div>';
if ($action == 'edit') {
print '<input type="submit" class="button buttongen button-save" name="actionmodify" value="'.$langs->trans("Save").'">';
}
print '<input type="submit" class="button buttongen button-cancel" name="actioncancel" value="'.$langs->trans("Cancel").'">';
print '</td></tr>';
// Action column
if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) {
print '<td class="center">';
print '<input type="hidden" name="page" value="'.$page.'">';
print '<input type="hidden" name="rowid" value="'.$rowid.'">';
if ($action == 'edit') {
print '<input type="submit" class="button buttongen button-save" name="actionmodify" value="'.$langs->trans("Modify").'">';
}
print '<div name="'.(!empty($obj->rowid) ? $obj->rowid : $obj->code).'"></div>';
print '<input type="submit" class="button buttongen button-cancel" name="actioncancel" value="'.$langs->trans("Cancel").'">';
print '</td>';
$colspan++;
}
@@ -1348,6 +1378,11 @@ if ($num) {
$fuser = new User($db);
$fuser->fetch($valuetoshow);
$valuetoshow = $fuser->getNomUrl(-1);
if ($obj->private) {
$valuetoshow = img_picto($langs->transnoentitiesnoconv("Private"), 'lock', 'class="pictofixedwidth"').$valuetoshow;
}
$class .= ' tdoverflowmax100';
}
}
@@ -1375,6 +1410,13 @@ if ($num) {
$class .= ' '.$css;
}
if ($value == 'tms' && empty($arrayfields['t'.$value]['checked'])) {
$showfield = 0;
}
if ($value == 'datec' && empty($arrayfields['t.'.$value]['checked'])) {
$showfield = 0;
}
// Show value for field
if ($showfield) {
print '<!-- '.$fieldlist[$field].' -->';

View File

@@ -42,7 +42,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
$langs->loadLangs(array('companies', 'products', 'admin', 'mails', 'other', 'errors'));
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$usersignature = $user->signature;
// For action = test or send, we ensure that content is not html, even for signature, because this we want a test with NO html.

View File

@@ -724,9 +724,6 @@ if ($mode == 'common' || $mode == 'commonkanban') {
$deschelp .= '<br>';
}
}
//if ($mode == 'marketplace') {
// $deschelp = '<div class="info hideonsmartphone">'.$langs->trans("ModulesMarketPlaceDesc")."<br></div><br>\n";
//}
if ($mode == 'deploy') {
$deschelp = '<div class="info hideonsmartphone">'.$langs->trans("ModulesDeployDesc", $langs->transnoentitiesnoconv("AvailableModules"))."<br></div><br>\n";
}
@@ -801,7 +798,7 @@ if ($mode == 'common' || $mode == 'commonkanban') {
$moreforfilter .= '<input type="submit" name="buttonsubmit" class="button small nomarginleft" value="'.dolPrintHTMLForAttribute($langs->trans("Refresh")).'">';
if ($search_keyword || ($search_nature && $search_nature != '-1') || ($search_version && $search_version != '-1') || ($search_status && $search_status != '-1')) {
$moreforfilter .= ' ';
$moreforfilter .= '<input type="submit" name="buttonreset" class="buttonreset noborderbottom nomargintop nomarginbottom" value="'.dolPrintHTMLForAttribute($langs->trans("Reset")).'">';
$moreforfilter .= '<input type="submit" name="buttonreset" class="buttonreset noborderall nomargintop nomarginbottom" value="'.dolPrintHTMLForAttribute($langs->trans("Reset")).'">';
}
$moreforfilter .= '</div>';
$moreforfilter .= '</div>';
@@ -1421,7 +1418,7 @@ if ($mode == 'marketplace') {
<div id="listing-content" class="div-table-responsive" <?php if (empty($categories_tree)) { ?>style="width:100%;"<?php } ?>>
<table summary="list_of_modules" id="list_of_modules" class="productlist centpercent">
<tbody id="listOfModules">
<?php //echo $remotestore->get_products($nbmaxtoshow); ?>
<!-- $product_list is $remotestore->getProducts($options) done previously -->
<?php print $products_list; ?>
</tbody>
</table>

View File

@@ -289,7 +289,7 @@ print '<table class="noborder centpercent nomarginbottom">';
print '<tr class="liste_titre">';
print '<td>'.$form->textwithpicto($langs->trans("CurrenciesUsed"), $langs->transnoentitiesnoconv("CurrenciesUsed_help_to_add")).'</td>'."\n";
print '<td class="right">'.$langs->trans("Rate").' / '.$langs->getCurrencySymbol($conf->currency).'</td>'."\n";
print '<td class="right">'.$langs->trans("Rate").' / '.$langs->getCurrencySymbol(getDolCurrency()).'</td>'."\n";
print '</tr>';
print '<form method="POST" action="'.$_SERVER['PHP_SELF'].'">';
@@ -308,10 +308,10 @@ print '</form>';
// Main currency
print '<tr class="oddeven">';
print '<td>'.$conf->currency;
print ' ('.$langs->getCurrencySymbol($conf->currency).')';
print '<td>'.getDolCurrency();
print ' ('.$langs->getCurrencySymbol(getDolCurrency()).')';
print $form->textwithpicto(' ', $langs->trans("BaseCurrency"));
if (!empty($TAvailableCurrency[$conf->currency]) && empty($TAvailableCurrency[$conf->currency]['active'])) {
if (!empty($TAvailableCurrency[getDolCurrency()]) && empty($TAvailableCurrency[getDolCurrency()]['active'])) {
print img_warning('Warning: This code has been disabled into Home - Setup - Dictionaries - Currencies');
}
print '</td>';
@@ -319,7 +319,7 @@ print '<td class="right">1</td>';
print '</tr>';
foreach ($TCurrency as &$currency) {
if ($currency->code == $conf->currency) {
if ($currency->code == getDolCurrency()) {
continue;
}
@@ -334,7 +334,7 @@ foreach ($TCurrency as &$currency) {
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="update_currency">';
print '<input type="hidden" name="fk_multicurrency" value="'.$currency->id.'">';
print '1 '.$conf->currency.' = ';
print '1 '.getDolCurrency().' = ';
print '<input type="text" name="rate" class="width125 right" value="'.($currency->rate->rate ? $currency->rate->rate : '').'">&nbsp;'.$currency->code.'&nbsp;';
print '<input type="submit" name="updatecurrency" class="button button-edit smallpaddingimp" value="'.$langs->trans("Modify").'">&nbsp;';
print '<input type="submit" name="deletecurrency" class="button smallpaddingimp" value="'.$langs->trans("Delete").'">';

View File

@@ -168,7 +168,7 @@ if ($action == 'update') {
}
// add file to concat
foreach (array('MAIN_INFO_PROPAL_TERMSOFSALE', 'MAIN_INFO_ORDER_TERMSOFSALE', 'MAIN_INFO_INVOICE_TERMSOFSALE') as $varname) {
if ($_FILES[$varname]["name"]) {
if (isset($_FILES[$varname]) && $_FILES[$varname]["name"]) {
if (!preg_match('/(\.pdf)$/i', $_FILES[$varname]["name"])) { // Document can be used on a lot of different places. Only pdf can be supported.
$langs->load("errors");
setEventMessages($langs->trans("ErrorBadFormat"), null, 'errors');

View File

@@ -366,6 +366,22 @@ class ExternalModules
$this->numberTotalOfProducts = 0;
// Special case of category goodies
if ($this->categorie == 87) {
$html = '<div class="shop-container">
<div class="shop-image">
<a href="https://merch.dolibarr.org/" target="_blank">
<img src="https://www.dolistore.com/medias/image/marketplace/img/goodies-shop.jpg" width="50%" alt="DoliStore Merch and Gifts" />
<div class="shop-overlay">
<button target="new" class="shop-button">'.$langs->trans("GoodiesButtonTitle").' <i class="icon-chevron-right"></i></button>
</div>
</a>
</div>
</div>';
return $html;
}
// Fetch the products from Dolistore source
$dolistoreProducts = array();
@@ -638,7 +654,7 @@ class ExternalModules
$this->numberOfProducts = count($this->products);
return $html ;
return $html;
}
/**

View File

@@ -195,17 +195,48 @@ textarea.row4{
h2.appTitle small{
font-weight: normal;
}
/*
tr.NotCompatible{
opacity: 0.5;
opacity: 1;
}
tr.NotCompatible:hover{
opacity: 0.7;
opacity: 0.9;
}
*/
span.details{
font-size: 1em;
margin-left: 10px;
vertical-align: super;
}
.storedesc {
opacity: 0.5;
}
.shop-container {
border-top: none;
overflow: hidden;
text-align: center;
background-color: #fff;
box-shadow: 0 8px 20px rgba(0, 0, 0, .1);
transition: transform .3s;
transform: scale(1);
}
.shop-button {
position: absolute;
top: 20%;
left: 50%;
padding: 10px 20px;
border: none;
border-radius: 8px;
text-transform: uppercase;
font-size: 16px;
color: #fff;
background-color: #ff5f57;
box-shadow: 0 5px 15px rgba(255, 95, 87, .3);
transition: background-color .3s, transform .3s;
transform: translate(-50%, -50%);
cursor: pointer;
}
@media only screen and (min-width: 1150px) {

View File

@@ -45,7 +45,7 @@ if (!$user->admin) {
}
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$forceCSP = getDolGlobalString("MAIN_SECURITY_FORCECSP");
$selectarrayCSPDirectives = GetContentPolicyDirectives();

View File

@@ -39,7 +39,7 @@ require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
$langs->loadLangs(array("companies", "admin", "products", "sms", "other", "errors"));
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
if (!$user->admin) {
accessforbidden();

View File

@@ -35,6 +35,13 @@ require '../../main.inc.php';
* @var string $conffile // $conffile is defined into filefunc.inc.php
* @var string $dolibarr_main_prod
* @var string $dolibarr_main_document_root
* @var string $dolibarr_main_restrict_os_commands
* @var string $dolibarr_main_restrict_eval_methods
* @var string $dolibarr_main_restrict_ip
* @var string $dolibarr_main_db_pass
* @var string $dolibarr_main_db_encrypted_pass
* @var string $dolibarr_main_stream_to_disable
* @var string $dolibarr_nocsrfcheck
*/
require_once DOL_DOCUMENT_ROOT.'/core/lib/memory.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
@@ -387,6 +394,15 @@ if (empty($dolibarr_main_restrict_os_commands)) {
print ' &nbsp; &nbsp; <span class="opacitymedium">('.$langs->trans("RecommendedValueIs", 'mysqldump, mysql, pg_dump, pg_restore, mariadb, mariadb-dump, clamdscan').')</span>';
print '<br>';
print '<strong>$dolibarr_main_restrict_eval_methods</strong>: ';
if (empty($dolibarr_main_restrict_eval_methods)) {
print $langs->trans("None");
} else {
print $dolibarr_main_restrict_eval_methods;
}
print ' &nbsp; &nbsp; <span class="opacitymedium">('.$langs->trans("RecommendedValueIs", 'getDolGlobalString,getDolGlobalInt,getDolCurrency,fetchNoCompute,hasRight,isAdmin,isModEnabled,isStringVarMatching,abs,min,max,round,dol_now,dol_concat,preg_match').')</span>';
print '<br>';
if (!getDolGlobalString('SECURITY_DISABLE_TEST_ON_OBFUSCATED_CONF')) {
print '<strong>$dolibarr_main_db_pass</strong>: ';
if (!empty($dolibarr_main_db_pass) && empty($dolibarr_main_db_encrypted_pass)) {
@@ -964,16 +980,16 @@ print '<strong>MAIN_RESTRICTHTML_REMOVE_ALSO_BAD_ATTRIBUTES</strong> = '.(getDol
print ' &nbsp; <span class="opacitymedium">('.$langs->trans("Recommended").": 1 - does not work on HTML5 with some old libxml libs)</span><br>";
print '<br>';
// MAIN_DISALLOW_URL_INTO_DESCRIPTIONS = 1, disallow url links except if on /medias
// MAIN_DISALLOW_URL_INTO_DESCRIPTIONS = 2, disallow all external urls link
print '<strong>MAIN_DISALLOW_URL_INTO_DESCRIPTIONS</strong> = '.getDolGlobalString('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS', '<span class="opacitymedium">'.$langs->trans("Undefined").' &nbsp; ('.$langs->trans("Recommended").': 1=only local links allowed or 2=no links at all)</span>')."<br>";
// MAIN_DISALLOW_URL_INTO_DESCRIPTIONS = 1, disallow url links except if on the local wrapper document.php or viewimage.php
// MAIN_DISALLOW_URL_INTO_DESCRIPTIONS = 2, disallow all urls link
print '<strong>MAIN_DISALLOW_URL_INTO_DESCRIPTIONS</strong> = '.getDolGlobalString('MAIN_DISALLOW_URL_INTO_DESCRIPTIONS', '<span class="opacitymedium">'.$langs->trans("Undefined").' &nbsp; ('.$langs->trans("Recommended").': 1=only local links allowed (to wrapper document.php or image.php) or 2=no links at all)</span>')."<br>";
print '<br>';
print '<strong>MAIN_ALLOW_SVG_FILES_AS_EXTERNAL_LINKS</strong> = '.getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_EXTERNAL_LINKS', '<span class="opacitymedium">'.$langs->trans("Undefined").' &nbsp; ('.$langs->trans("Recommended").': '.$langs->trans("Undefined").' '.$langs->trans("or").' 0)</span>')."<br>";
print '<br>';
print '<strong>MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL</strong> = '.(getDolGlobalString('MAIN_DISALLOW_STRING_OBFUSCATION_IN_DOL_EVAL') ? '1' : '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>');
print ' &nbsp; <span class="opacitymedium">('.$langs->trans("Recommended").": 1 - may break use of concatenation function like . or dol_concatdesc into extra fields conditions or formula)</span><br>";
print '<strong>MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL</strong> = '.getDolGlobalString('MAIN_ALLOW_OBFUSCATION_METHODS_IN_DOL_EVAL', '<span class="opacitymedium">'.$langs->trans("Undefined").'</span>');
print ' &nbsp; <span class="opacitymedium">('.$langs->trans("Recommended").": 0 - The value 1 allows the use of concatenation functions like . or dol_concat into extra fields conditions or formula but is not secured)</span><br>";
print '<br>';

View File

@@ -494,9 +494,9 @@ if ($result) {
$userstatic->email = $obj->email;
if (isModEnabled('multicompany') && $userstatic->admin && !$userstatic->entity) {
print img_picto($langs->trans("SuperAdministratorDesc"), 'redstar', 'class="valignmiddle paddingright"');
print img_picto($langs->trans("SuperAdministratorDesc"), 'superadmin', 'class="valignmiddle paddingright"');
} elseif ($userstatic->admin) {
print img_picto($langs->trans("AdministratorDesc"), 'star', 'class="valignmiddle paddingright"');
print img_picto($langs->trans("AdministratorDesc"), 'admin', 'class="valignmiddle paddingright"');
}
//print $userstatic->getLoginUrl(-1);

View File

@@ -252,6 +252,34 @@ class Documentation
'submenu' => array(),
'summary' => array(),
),
'UxDolibarrContext' => array(
'url' => dol_buildpath($this->baseUrl.'/experimental/experiments/dolibarr-context/index.php', 1),
'icon' => 'fas fa-flask',
'submenu' => array(
'UxDolibarrContextHowItWork' => array(
'url' => dol_buildpath($this->baseUrl.'/experimental/experiments/dolibarr-context/index.php', 1),
'icon' => 'fas fa-flask',
'submenu' => array(),
'summary' => array(
'Introduction' => '#titlesection-basicusage',
'ConsoleHelp' => '#titlesection-console-help',
'JSDolibarrhooks' => '#titlesection-hooks',
'JSDolibarrhooksReadyVsInit' => '#titlesection-event-init-vs-ready',
'JSDolibarrAwaitHooks' => '#titlesection-await-hooks',
'ExampleOfCreatingNewContextTool' => '#titlesection-create-tool-example',
'SetEventMessageTool' => '#titlesection-tool-seteventmessage',
'SetAndUseContextVars' => '#titlesection-contextvars',
),
),
'UxDolibarrContextLangsTool' => array(
'url' => dol_buildpath($this->baseUrl.'/experimental/experiments/dolibarr-context/langs-tool.php', 1),
'icon' => 'fas fa-flask',
'submenu' => array(),
'summary' => array(),
),
),
'summary' => array(),
),
)
);
@@ -470,7 +498,13 @@ class Documentation
if ($showsubmenu && !empty($menu['submenu'])) {
foreach ($menu['submenu'] as $key => $item) {
print '<li class="summary-title ">';
if (!empty($item['url'])) {
print '<h3 class="level-'.$level.'"><a href="'.dolBuildUrl($item['url']).'" >'.$langs->trans($key).'</a></h3>';
} else {
print '<h3 class="level-'.$level.'">'.$langs->trans($key).'</h3>';
}
if ($showsubmenu_summary) {
$this->displaySummary($item, $level);
}

View File

@@ -0,0 +1,171 @@
/** This file is purely for IDE autocompletion and developer convenience.
* It is never executed or loaded in Dolibarr itself.
*
* MOCK DEFINITION: Dolibarr.tools
* This mock helps your code editor understand the structure of Dolibarr.tools
* and provides autocomplete hints, parameter hints, and inline documentation.
* You can safely edit this file to add all standard Dolibarr tools for autocompletion.
*
* @SEE dolibarr-context.umd.js
*
*/
var Dolibarr = {
tools: {
/**
* Displays a Dolibarr notification message (success, warning, or error).
* This is the JavaScript equivalent of the PHP setEventMessage tool.
*
* @param {string} msg The message text to display
* @param {string=} type Optional: 'mesgs' (default), 'warnings', or 'errors'
* @param {boolean=} sticky Optional: true if the message should stay until manually closed
*
* Example usage in your IDE:
* Dolibarr.tools.setEventMessage('Operation successful', 'success');
*/
setEventMessage: function(msg, type, sticky) {},
/**
* TThe langs tool
*/
langs: {
/**
* Load a single locale from cache or fetch
* @param {string} domain
* @param {string} locale
* @returns {Promise<Object>} translation object
*/
loadLocale(domain, locale) {},
/**
* Load translations for a domain (multiple locales)
* @param {string} domain
* @param {string} locales - comma-separated list
* @returns {Promise<Object>}
*/
load(domain, locales = currentLocale) {},
/**
* Set the current locale to use for translations
* @param {string} locale
*/
setLocale(locale) {},
/**
* Translate a key using current locale
* Supports placeholders like %s, %d, %f (simple sprintf)
* @param {string} key
* @param {...any} args
* @returns {string}
*/
trans(key, ...args) {},
},
// You can add more standard Dolibarr tools here for IDE autocompletion.
// Example:
// alertUser: function(msg) {},
},
/**
* Defines a new secure tool.
* @param {string} name Name of the tool
* @param {*} value Function, class or object
* @param {boolean} overwrite Explicitly allow overwriting an existing tool
*
* See also dolibarr-context.mock.js for defining all standard Dolibarr tools and creating mock implementations to improve code completion and editor support.
*/
defineTool(name, value, overwrite = false, triggerHook = true) {},
/**
* Check if tool exists
* @param {string} name Tool name
* @returns {boolean} true if exists
*/
checkToolExist(name) {},
/**
* Get read-only snapshot of context variables
*/
ContextVars() {},
/**
* Defines a new context variable.
* @param {string} key
* @param {string|number|boolean} value
* @param {boolean} overwrite Allow overwriting existing value
*/
setContextVar(key, value, overwrite = false) {},
/**
* Set multiple context variables
* @param {Object} vars Object of key/value pairs
* @param {boolean} overwrite Allow overwriting existing values
*/
setContextVars(vars, overwrite = false) {},
/**
* Get a context variable safely
* @param {string} key
* @param {*} fallback Optional fallback if variable not set
* @returns {*}
*/
getContextVar(key, fallback = null) {},
/**
* Enable or disable debug mode
* @param {boolean} state
*/
debugMode(state) {},
/**
* Enable or disable debug mode
* @returns {int}
*/
getDebugMode() {},
/**
* Internal logger
* Only prints when debug mode is enabled
* @param {string} msg
*/
log(msg) {},
/**
* Executes a hook-like JS event with CustomEvent.
* @param {string} hookName Hook identifier
* @param {object} data Extra information passed to listeners
*/
executeHook(hookName, data = {}) {},
/**
* Registers an event listener.
* @param {string} eventName Event to listen to
* @param {function} callback Listener function
*/
on(eventName, callback) {},
/**
* Unregister an event listener
* @param {string} eventName
* @param {function} callback
*/
off(eventName, callback) {},
/**
* Register an asynchronous hook
* @param {string} eventName
* @param {function} fn Async function receiving previous result
* @param {Object} opts Optional {before, after, id} to control order
* @returns {string} The hook ID
*/
onAwait(eventName, fn, opts = {}) {},
/**
* Execute async hooks sequentially
* @param {string} eventName
* @param {*} data Input data for first hook
* @returns {Promise<*>} Final result after all hooks
*/
async executeHookAwait(eventName, data) {},
};

View File

@@ -0,0 +1,443 @@
// CustomEvent doesnt show up until IE 11 and Safari 10. Fortunately a simple polyfill pushes support back to any IE 9.
(function () {
if ( typeof window.CustomEvent === "function" ) return false;
function CustomEvent ( event, params ) {
params = params || { bubbles: false, cancelable: false, detail: undefined };
var evt = document.createEvent( 'CustomEvent' );
evt.initCustomEvent( event, params.bubbles, params.cancelable, params.detail );
return evt;
}
CustomEvent.prototype = window.Event.prototype;
window.CustomEvent = CustomEvent;
})();
// End old browsers support
/**
* Dolibarr Global Context (UMD)
* Provides a secure global object window.Dolibarr
* with non-replaceable tools, events and debug mode.
*
* See also dolibarr-context.mock.js for defining all standard Dolibarr tools and creating mock implementations to improve code completion and editor support.
*
*/
(function (root, factory) {
// Support AMD
if (typeof define === "function" && define.amd) {
define([], factory);
// Support CommonJS (Node, bundlers)
} else if (typeof exports === "object") {
module.exports = factory();
// Fallback global (browser)
} else {
root.Dolibarr = root.Dolibarr || factory();
}
})(typeof self !== "undefined" ? self : this, function () {
// Prevent double initialization if script loaded twice
if (typeof window !== "undefined" && window.Dolibarr) {
return window.Dolibarr;
}
// Private storage for secure tools (non-replaceable)
const _tools = {};
// Private storage for secure context vars or constants (non-replaceable)
const _contextVars = {};
// Internal map to track proxies for events
const _proxies = new Map();
// Native event dispatcher (standard DOM)
const _events = new EventTarget();
const _awaitHooks = {}; // Async hooks storage
// Debug flag (disabled by default)
let _debug = false;
// -------------------------
// Internal helper functions
// -------------------------
function _ensureEvent(name) { if (!_awaitHooks[name]) _awaitHooks[name] = []; }
function _generateId() { return 'hook_' + Math.random().toString(36).slice(2); }
function _idExists(name, id) { return _awaitHooks[name].some(h => h.id === id); }
/**
* Insert a new hook entry in the array respecting optional before/after lists
*/
function _insertWithOrder(arr, entry, beforeList, afterList) {
if ((!beforeList || beforeList.length === 0) && (!afterList || afterList.length === 0)) {
arr.push(entry);
return arr;
}
let ordered = [...arr];
let index = ordered.length;
if (beforeList && beforeList.length > 0) {
for (const target of beforeList) {
const i = ordered.findIndex(h => h.id === target);
if (i !== -1 && i < index) index = i;
}
}
if (afterList && afterList.length > 0) {
for (const target of afterList) {
const i = ordered.findIndex(h => h.id === target);
if (i !== -1 && i >= index) index = i + 1;
}
}
if (index > ordered.length) index = ordered.length;
ordered.splice(index, 0, entry);
return ordered;
}
// -------------------------
// Dolibarr object
// -------------------------
const Dolibarr = {
/**
* Returns a frozen copy of the registered tools.
* Tools cannot be modified or replaced from outside.
*/
get tools() {
return Object.freeze({ ..._tools });
},
/**
* Defines a new secure tool.
* @param {string} name Name of the tool
* @param {*} value Function, class or object
* @param {boolean} overwrite Explicitly allow overwriting an existing tool
*
* See also dolibarr-context.mock.js for defining all standard Dolibarr tools and creating mock implementations to improve code completion and editor support.
*/
defineTool(name, value, overwrite = false, triggerHook = true) {
// Prevent silent overrides unless "overwrite" is true
if (!overwrite && this.checkToolExist(name)) {
throw new Error(`Dolibarr: Tool '${name}' already defined`);
}
// Define the tool as read-only and non-configurable
Object.defineProperty(_tools, name, {
value,
writable: false,
configurable: false,
enumerable: true,
});
this.log(`Tool defined: ${name}, triggerHook: ${triggerHook}, overwrite: ${overwrite} `);
if(triggerHook) {
this.executeHook('defineTool', { toolName: name, overwrite });
}
},
/**
* Check if tool exists
* @param {string} name Tool name
* @returns {boolean} true if exists
*/
checkToolExist(name) {
return Object.prototype.hasOwnProperty.call(_tools, name);
},
/**
* Get read-only snapshot of context variables
*/
get ContextVars() {
return Object.freeze({ ..._contextVars });
},
/**
* Defines a new context variable.
* @param {string} key
* @param {string|number|boolean} value
* @param {boolean} overwrite Allow overwriting existing value
*/
setContextVar(key, value, overwrite = false) {
// Accept only string, number, or boolean
const type = typeof value;
if (type !== 'string' && type !== 'number' && type !== 'boolean') {
throw new TypeError(`Dolibarr: ContextVar '${key}' must be a string, number, or boolean`);
}
if (!overwrite && _contextVars.hasOwnProperty(key)) {
throw new Error(`Dolibarr: ContextVar '${key}' already defined`);
}
Object.defineProperty(_contextVars, key, {
value,
writable: false,
configurable: false,
enumerable: true
});
this.log(`ContextVar set: ${key} = ${value} (overwrite: ${overwrite})`);
this.executeHook('setContextVar', { key, value, overwrite });
},
/**
* Set multiple context variables
* @param {Object} vars Object of key/value pairs
* @param {boolean} overwrite Allow overwriting existing values
*/
setContextVars(vars, overwrite = false) {
if (typeof vars !== 'object' || vars === null) {
throw new Error('Dolibarr: setContextVars expects an object');
}
for (const [key, value] of Object.entries(vars)) {
this.setContextVar(key, value, overwrite);
}
},
/**
* Get a context variable safely
* @param {string} key
* @param {*} fallback Optional fallback if variable not set
* @returns {*}
*/
getContextVar(key, fallback = null) {
return _contextVars.hasOwnProperty(key) ? _contextVars[key] : fallback;
},
/**
* Enable or disable debug mode
* @param {boolean} state
*/
debugMode(state) {
_debug = !!state;
// save in localStorage
if (typeof window !== "undefined" && window.localStorage) {
localStorage.setItem('DolibarrDebugMode', _debug ? '1' : '0');
}
this.log(`Debug mode: ${_debug}`);
},
/**
* Enable or disable debug mode
* @returns {int}
*/
getDebugMode() {
return _debug ? 1 : 0
},
/**
* Internal logger
* Only prints when debug mode is enabled
* @param {string} msg
*/
log(msg) {
if (_debug) console.log(`Dolibarr: ${msg}`);
},
/**
* Executes a hook-like JS event with CustomEvent.
* @param {string} hookName Hook identifier
* @param {object} data Extra information passed to listeners
*/
executeHook(hookName, data = {}) {
this.log(`Hook executed: ${hookName}`);
const ev = new CustomEvent(hookName, { detail: data });
// Dispatch on internal EventTarget
_events.dispatchEvent(ev);
// Dispatch globally on document for backward compatibility
if (typeof document !== "undefined") {
document.dispatchEvent(new CustomEvent('Dolibarr:' + hookName, { detail: data }));
}
// Notify Dolibarr.on() listeners with data directly
const listeners = _events.listeners?.[hookName] || [];
listeners.forEach(fn => fn(data));
},
/**
* Registers an event listener.
* @param {string} eventName Event to listen to
* @param {function} callback Listener function
*/
on(eventName, callback) {
// Create a proxy to extract e.detail
const proxy = function(e) {
callback(e.detail);
};
// Store the proxy so we can remove it later
if (!_proxies.has(eventName)) _proxies.set(eventName, new Map());
_proxies.get(eventName).set(callback, proxy);
// Attach proxy to the internal EventTarget
_events.addEventListener(eventName, proxy);
},
/**
* Unregister an event listener
* @param {string} eventName
* @param {function} callback
*/
off(eventName, callback) {
const map = _proxies.get(eventName);
if (!map) return;
const proxy = map.get(callback);
if (!proxy) return;
// Remove proxy from EventTarget
_events.removeEventListener(eventName, proxy);
map.delete(callback);
// Cleanup if no proxies remain for this event
if (map.size === 0) _proxies.delete(eventName);
},
/**
* Register an asynchronous hook
* @param {string} eventName
* @param {function} fn Async function receiving previous result
* @param {Object} opts Optional {before, after, id} to control order
* @returns {string} The hook ID
*/
onAwait(eventName, fn, opts = {}) {
_ensureEvent(eventName);
let id = opts.id || _generateId();
if (_idExists(eventName, id)) throw new Error(`onAwait: ID '${id}' already used for '${eventName}'`);
const before = Array.isArray(opts.before) ? opts.before : (opts.before ? [opts.before] : []);
const after = Array.isArray(opts.after) ? opts.after : (opts.after ? [opts.after] : []);
_awaitHooks[eventName] = _insertWithOrder(_awaitHooks[eventName], { id, fn }, before, after);
return id;
},
/**
* Execute async hooks sequentially
* @param {string} eventName
* @param {*} data Input data for first hook
* @returns {Promise<*>} Final result after all hooks
*/
async executeHookAwait(eventName, data) {
this.log(`Await Hook executed: ${eventName}`);
_ensureEvent(eventName);
let result = data;
for (const h of _awaitHooks[eventName]) {
result = await h.fn(result);
}
return result;
}
};
// Lock Dolibarr core object
Object.freeze(Dolibarr);
// Expose Dolibarr to window in a protected, non-writable way
if (typeof window !== "undefined") {
Object.defineProperty(window, "Dolibarr", {
value: Dolibarr,
writable: false,
configurable: false,
enumerable: true,
});
}
// Restore debug mode from localStorage
if (typeof window !== "undefined" && window.localStorage) {
const saved = localStorage.getItem('DolibarrDebugMode');
if (saved === '1') {
Dolibarr.debugMode(true);
}
}
// Force initialise hook init and Ready in good execution order
(function triggerDolibarrHooks() {
// Fire Init first
const fireInit = () => {
Dolibarr.executeHook('Init', { context: Dolibarr });
Dolibarr.log('Context Init done');
// Only after Init is done, fire Ready
fireReady();
};
const fireReady = () => {
Dolibarr.executeHook('Ready', { context: Dolibarr });
Dolibarr.log('Context Ready done');
};
if (document.readyState === 'complete' || document.readyState === 'interactive') {
// DOM already ready, trigger Init -> Ready in order
fireInit();
} else {
// Wait for DOM ready, then trigger Init -> Ready
document.addEventListener('DOMContentLoaded', fireInit);
}
})();
/**
* Display help in console log
*/
Dolibarr.defineTool('showConsoleHelp', () => {
console.groupCollapsed(
"%cDolibarr JS Developers HELP",
"background-color: #95cf04; color: #ffffff; font-weight: bold; padding: 4px;"
);
console.log("Show this help : %cDolibarr.tools.showConsoleHelp();","font-weight: bold;");
console.log(`Documentation for admin only on : %cModule builder ➜ UX Components Doc`,"font-weight: bold;");
// DEBUG MODE
console.groupCollapsed("Dolibarr debug mode");
console.log(
"When help was displayed, status was: %c" + (Dolibarr.getDebugMode() ? "ENABLED" : "DISABLED"),
"font-weight: bold; color:" + (Dolibarr.getDebugMode() ? "green" : "red") + ";"
);
console.log(
"Activate debug mode : %cDolibarr.debugMode(true);",
"font-weight: bold;"
);
console.log(
"Disable debug mode : %cDolibarr.debugMode(false);",
"font-weight: bold;"
);
console.log("Note : debug mode status is persistent.");
console.groupEnd();
// HOOKS
console.groupCollapsed("Hooks helpers");
console.log(
"Run a hook manually : %cDolibarr.executeHook('hookName', {...})",
"font-weight: bold;"
);
console.log(
"Run await hooks manually : %cawait Dolibarr.executeHookAwait('hookName', {...})",
"font-weight: bold;"
);
console.groupEnd();
console.groupEnd(); // END MAIN GROUP
}, false, false);
// Auto-show help when console is opened
Dolibarr.tools.showConsoleHelp();
return Dolibarr;
});

View File

@@ -0,0 +1,232 @@
document.addEventListener('Dolibarr:Init', function(e) {
/**
* Dolibarr.tools.langs
* --------------------
* Manage translations in JS context with IndexedDB cache, multi-locale support and fallback.
* Parallel loading of language files for performance.
* Automatic cache invalidation if Dolibarr version changes.
*
* Require Dolibarr context vars
* DOL_LANG_INTERFACE_URL, MAIN_LANG_DEFAULT, DOL_VERSION
*
*/
const langs = function() {
const ONE_DAY = 86400000;
let currentLocale = Dolibarr.getContextVar('MAIN_LANG_DEFAULT', 'en_US');
let translations = {}; // { en_US: {KEY: TEXT}, fr_FR: {...} }
let domainsLoaded = {}; // { en_US: Set(['main','other']), fr_FR: Set([...]) }
if (!domainsLoaded[currentLocale]) domainsLoaded[currentLocale] = new Set();
let domainsRequested = new Set(); // Set of domain names that were requested at least once
/**
* Open or create IndexedDB for caching translations
* @returns {Promise<IDBDatabase>}
*/
async function openDB() {
return new Promise((resolve, reject) => {
const request = indexedDB.open('DolibarrLangs', 1);
request.onupgradeneeded = e => {
const db = e.target.result;
if (!db.objectStoreNames.contains('langs')) db.createObjectStore('langs');
};
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
/**
* Get cached translation for a domain + locale
* @param {string} domain
* @param {string} locale
* @returns {Promise<Object|null>}
*/
async function getCache(domain, locale) {
try {
const db = await openDB();
const tx = db.transaction('langs', 'readonly');
const store = tx.objectStore('langs');
return new Promise((resolve, reject) => {
const request = store.get(`${domain}@${locale}`);
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
} catch (err) {
return null;
}
}
/**
* Set cached translation for a domain + locale
* @param {string} domain
* @param {string} locale
* @param {Object} data
*/
async function setCache(domain, locale, data) {
try {
const db = await openDB();
const tx = db.transaction('langs', 'readwrite');
const store = tx.objectStore('langs');
const dolibarrVersion = Dolibarr.getContextVar('DOL_VERSION', 0);
await store.put({ key: `${domain}@${locale}`, data, timestamp: Date.now(), dolibarrVersion }, `${domain}@${locale}`);
} catch (err) {
// fail silently
Dolibarr.log('Save langs in cache fail');
}
}
/**
* Clear all cached translations in IndexedDB and in-memory
*/
async function clearCache(clearMemory = false) {
if(clearMemory) {
translations = {};
domainsLoaded = {};
}
try {
const db = await openDB();
const tx = db.transaction('langs', 'readwrite');
const store = tx.objectStore('langs');
await store.clear();
Dolibarr.log('Dolibarr.tools.langs: cache cleared');
} catch (err) {
console.error('Dolibarr.tools.langs: failed to clear cache', err);
}
}
/**
* Load a single locale from cache or fetch
* @param {string} domain
* @param {string} locale
* @returns {Promise<Object>} translation object
*/
async function loadLocale(domain, locale) {
const cache = await getCache(domain, locale);
const now = Date.now();
const dolibarrVersion = Dolibarr.getContextVar('DOL_VERSION', 0);
if (cache && cache.data && (now - cache.timestamp < ONE_DAY) && cache.dolibarrVersion === dolibarrVersion) {
Dolibarr.log('Langs tool : Load lang from cache');
return cache.data;
}
const langInterfaceUrl = Dolibarr.getContextVar('DOL_LANG_INTERFACE_URL', false);
if(!langInterfaceUrl) {
console.error('Dolibarr langs: missing DOL_LANG_INTERFACE_URL')
return;
}
Dolibarr.log('Langs tool : Load lang from interface');
const params = new URLSearchParams({ domain, local: locale });
const resp = await fetch(`${langInterfaceUrl}?${params.toString()}`);
const json = await resp.json();
const data = json[locale] || {};
await setCache(domain, locale, data);
return data;
}
/**
* Load translations for a domain (multiple locales)
* @param {string} domain
* @param {string} locales - comma-separated list
* @returns {Promise<Object>}
*/
async function load(domain, locales = currentLocale) {
const list = locales.split(',');
// flag domaine as requested for future load when local change
domainsRequested.add(domain);
const results = await Promise.all(list.map(loc => loadLocale(domain, loc)));
list.forEach((loc, i) => {
if (!translations[loc]) translations[loc] = {};
Object.assign(translations[loc], results[i]);
if (!domainsLoaded[loc]) domainsLoaded[loc] = new Set();
domainsLoaded[loc].add(domain);
});
return translations;
}
/**
* Set the current locale to use for translations
* @param {string} locale
*/
async function setLocale(locale, noDomainReload = false) {
if (!locale || locale === currentLocale) return;
const prev = currentLocale;
currentLocale = locale;
if (!domainsLoaded[locale]) domainsLoaded[locale] = new Set();
if (!noDomainReload) {
// priorité : domainsLoaded[prev], sinon fallback sur domainsRequested
let toReload = Array.from(domainsLoaded[prev] || []);
if (toReload.length === 0) {
// aucun domaine marqué comme "loaded" pour prev : utiliser la liste des domaines demandés
toReload = Array.from(domainsRequested);
}
for (const domain of toReload) {
// load(domain, locale) accepte le param locale ; l'appel charge et met domainsLoaded
if (domainsLoaded[locale].size === 0) {
await load(domain, locale);
}
}
}
Dolibarr.log(`Locale changed: ${prev} -> ${locale}`);
}
/**
* Translate a key using current locale
* Supports placeholders like %s, %d, %f (simple sprintf)
* @param {string} key
* @param {...any} args
* @returns {string}
*/
function trans(key, ...args) {
const text = translations[currentLocale]?.[key] || key;
if (!args.length) return text;
// Utilisation de la fonction sprintf pour le formatage
return sprintf(text, ...args);
}
function sprintf(fmt, ...args) {
let i = 0;
return fmt.replace(/%[%bcdeEfFgGosuxX]/g, (match) => {
if (match === '%%') return '%';
const arg = args[i++];
switch (match) {
case '%s': return String(arg);
case '%d':
case '%u': return Number(arg);
case '%f':
case '%F': return parseFloat(arg);
case '%b': return Number(arg).toString(2);
case '%o': return Number(arg).toString(8);
case '%x': return Number(arg).toString(16);
case '%X': return Number(arg).toString(16).toUpperCase();
case '%c': return String.fromCharCode(Number(arg));
default: return match;
}
});
}
return {
load,
clearCache,
setLocale,
trans,
get currentLocale() { return currentLocale; }
};
};
Dolibarr.defineTool('langs',langs());
});

View File

@@ -0,0 +1,53 @@
document.addEventListener('Dolibarr:Init', function(e) {
// this tool allow overwrite because of DISABLE_JQUERY_JNOTIFY conf
/**
* status : 'mesgs' by default, 'warnings', 'errors'
*/
Dolibarr.defineTool('setEventMessage', (msg, status = 'mesgs', sticky = false) =>{
// Normalize status to match jNotify expected values
const normalizeStatus = (s) => {
s = (s || '').toLowerCase();
if (s === 'error' || s === 'errors') return 'error';
if (s === 'warning' || s === 'warnings') return 'warning';
return '';
};
const type = normalizeStatus(status);
let jnotifyConf = {
delay: 1500 // the default time to show each notification (in milliseconds)
, type : type
, sticky: sticky // determines if the message should be considered "sticky" (user must manually close notification)
, closeLabel: "&times;" // the HTML to use for the "Close" link
, showClose: true // determines if the "Close" link should be shown if notification is also sticky
, fadeSpeed: 150 // the speed to fade messages out (in milliseconds)
, slideSpeed: 250 // the speed used to slide messages out (in milliseconds)
}
if(msg.length > 0){
if (typeof $.jnotify === "function") {
$.jnotify(msg, jnotifyConf);
} else {
const container = document.getElementById('alert-message-container');
if (container) {
// Add message to #alert-message-container if exist
const div = document.createElement('div');
div.className = type; // error, warning, success
div.textContent = msg; // safer than innerHTML
container.appendChild(div);
} else {
console.warn("jnotify is missing and setEventMessage tool wasn't replaced so use alert fallback instead");
// fallback prefix
let prefix = '';
if (type === 'error') prefix = 'Error: ';
else if (type === 'warning') prefix = 'Warning: ';
window.alert(prefix + msg);
}
}
}
else{
Dolibarr.log('setEventMessage : Message is empty');
}
}, true);
});

View File

@@ -0,0 +1,680 @@
<?php
/*
* Copyright (C) 2025 Anthony Damhet <a.damhet@progiseize.fr>
* Copyright (C) 2025 Frédéric France <frederic.france@free.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
* 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 <https://www.gnu.org/licenses/>.
*/
// Load Dolibarr environment
require '../../../../../../main.inc.php';
/**
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
// Protection if external user
if ($user->socid > 0) {
accessforbidden();
}
// Includes
require_once DOL_DOCUMENT_ROOT . '/admin/tools/ui/class/documentation.class.php';
// Load documentation translations
$langs->load('uxdocumentation');
//
$documentation = new Documentation($db);
$group = 'ExperimentalUx';
$experimentName = 'UxDolibarrContext';
$experimentAssetsPath = $documentation->baseUrl . '/experimental/experiments/dolibarr-context/assets/';
$js = [
'/includes/ace/src/ace.js',
'/includes/ace/src/ext-statusbar.js',
'/includes/ace/src/ext-language_tools.js',
$experimentAssetsPath . '/dolibarr-context.umd.js',
$experimentAssetsPath . '/dolibarr-tool.seteventmessage.js',
];
$css = [];
// Output html head + body - Param is Title
$documentation->docHeader($langs->trans($experimentName, $group), $js, $css);
// Set view for menu and breadcrumb
$documentation->view = [$group, $experimentName];
// Output sidebar
$documentation->showSidebar(); ?>
<div class="doc-wrapper">
<?php $documentation->showBreadCrumb(); ?>
<div class="doc-content-wrapper">
<h1 class="documentation-title"><?php echo $langs->trans($experimentName); ?> : <?php echo $langs->trans('UxDolibarrContextHowItWork'); ?></h1>
<?php $documentation->showSummary(); ?>
<div class="documentation-section">
<h2 id="titlesection-basicusage" class="documentation-title">Introduction</h2>
<p>
DolibarrContext is a secure global JavaScript context for Dolibarr.
It provides a single global object <code>window.Dolibarr</code>, which cannot be replaced.
It allows defining non-replaceable tools and managing hooks/events in a modular and secure way.
</p>
<p>
This system is designed to provide long-term flexibility and maintainability. You can define reusable tools
that encapsulate functionality such as standardized AJAX requests and responses, ensuring consistent data handling across Dolibarr modules.
For example, tools can be created to wrap API calls and automatically process returned data in a uniform format,
reducing repetitive code and preventing errors.
</p>
<p>
Beyond DOM-based events, DolibarrContext allows monitoring and reacting to business events using a
hook-like mechanism. For instance, you can listen to events such as
<code>Dolibarr.on('addline:load:productPricesList', function(data) { ... });</code>
without relying on DOM changes. This enables creating logic that reacts directly to application state changes.
</p>
<p>
Similarly, you can define tools that act as global helpers, like <code>Dolibarr.tools.setEventMessage()</code>.
This tool can display notifications (similar to PHP's <code>setEventMessage()</code> in Dolibarr),
initially using jNotify or any other library. In the future, the underlying library can change without affecting
the way modules or external code call this tool, maintaining compatibility and reducing maintenance.
</p>
<p>
In summary, DolibarrContext provides a secure, extensible foundation for adding tools, monitoring business events,
and standardizing interactions across Dolibarr's frontend modules.
</p>
</div>
<div class="documentation-section">
<h2 id="titlesection-console-help" class="documentation-title">Console help</h2>
<p>
Open your browser console with <code>F12</code> to view the available commands.<br/>
If the help does not appear automatically, type <code>Dolibarr.tools.showConsoleHelp();</code> in the console to display it.
</p>
</div>
<div class="documentation-section">
<h2 class="documentation-title">Dolibarr.log() and debugMode()</h2>
<p>
<code>Dolibarr.log()</code> is a lightweight logging utility provided by the Dolibarr JS context.
It does <strong>not</strong> replace <code>console.log()</code>, but it gives an important advantage:
you can enable or disable all logs globally using <code>Dolibarr.debugMode()</code>.
</p>
<h3>Why use Dolibarr.log() instead of console.log()?</h3>
<ul>
<li>You can enable or disable logging dynamically from the browser console.</li>
<li>Avoid polluting the console for end-users: logs appear only when debug mode is enabled.</li>
<li>Ideal for module development: switch between quiet mode and verbose mode instantly.</li>
<li>Useful in production debugging: you can activate logs without modifying any code.</li>
</ul>
<h3>How it works</h3>
<p>
When <code>debugMode</code> is disabled (default state), calls to <code>Dolibarr.log()</code> do nothing.
When enabled, <code>Dolibarr.log()</code> behaves like <code>console.log()</code>.
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script>',
' // Log something (will only appear if debug mode is ON)',
' Dolibarr.log("My debug message");',
'',
' // Enable verbose logs',
' Dolibarr.debugMode(true);',
'',
' // Disable logs again',
' Dolibarr.debugMode(false);',
'</script>',
);
echo $documentation->showCode($lines, 'php');
?>
</div>
<h3>Summary</h3>
<ul>
<li><code>console.log()</code> → always prints messages, noisy, not controllable, but useful during active development and debugging.</li>
<li><code>Dolibarr.log()</code> → prints messages only when debug mode is ON; fully controllable. Ideal for Dolibarr core logs or when you want to keep logs available but silent in production.</li>
<li><code>Dolibarr.debugMode(true)</code> → enable verbose logs, activating <code>Dolibarr.log()</code> output.</li>
<li><code>Dolibarr.debugMode(false)</code> → disable all <code>Dolibarr.log()</code> output, silencing debug messages.</li>
</ul>
</div>
<div class="documentation-section">
<h2 id="titlesection-hooks" class="documentation-title">JS Dolibarr hooks</h2>
<p>
Dolibarr provides a hook system to allow modules and scripts to communicate with each other
through named events. There are two ways to listen to these events in JS:
<code>Dolibarr.on()</code> and <code>document.addEventListener()</code>.
</p>
<h3>Event listener example</h3>
<p>
You can use <code>Dolibarr.on()</code> to listen to a hook. The main difference with standard
document events is that the callback receives <strong>directly the data object</strong> passed
when the hook is executed, without needing to access <code>e.detail</code>.
</p>
<p>
For backward compatibility and standard DOM integration, the same hook can also be caught
using <code>document.addEventListener()</code>, but in this case the data is inside
<code>e.detail</code> and the event name is prefixed by <code>Dolibarr:</code> so for a hook named A event name is <code>Dolibarr:A</code>
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script>',
' // Add a listener to the Dolibarr theEventName event',
' Dolibarr.on(\'theEventName\', function(data) {',
' console.log(\'Dolibarr theEventName\', data);',
' });',
'',
' // But this work too on document',
' document.addEventListener(\'Dolibarr:theEventName\', function(e) {',
' console.log(\'Dolibarr theEventName\', e.detail);',
' });',
'</script>',
);
echo $documentation->showCode($lines, 'php'); ?>
</div>
<h3>Practical usage</h3>
<p>
When Dolibarr is ready (DOM loaded and JS context initialized), you can register your hooks
or trigger them. Both <code>Dolibarr.on()</code> and <code>document.addEventListener()</code>
are valid, but <code>Dolibarr.on()</code> is simpler and more convenient because you get the
data directly.
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script>',
' document.addEventListener(\'Dolibarr:Ready\', function(e) {',
' // the dom is ready and you are sure Dolibarr js context is loaded',
' ...',
' // Do your stuff',
' ...',
'',
' // Add a listener to the yourCustomHookName event with dolibarr.on()',
' Dolibarr.on(\'yourCustomHookName\', function(data) {',
' console.log(\'With Dolibarr.on : data will contain { data01: \'stuff\', data02: \'other stuff\' }\', data);',
' });',
'',
' // Or you can do : Add a listener to the yourCustomHookName document.addEventListener()',
' document.addEventListener(\'Dolibarr:yourCustomHookName\', function(e) {',
' console.log(\'With document.addEventListener : e.detail will contain { data01: \'stuff\', data02: \'other stuff\' }\', e.detail);',
' });',
'',
' // you can trigger js hooks',
' document.getElementById(\'try-event-yourCustomHookName\').addEventListener(\'click\', function(e) {',
' Dolibarr.executeHook(\'yourCustomHookName\', { data01: \'stuff\', data02: \'other stuff\' })',
' });',
'',
' ...',
' // Do your stuff',
' ...',
' });',
'</script>',
);
echo $documentation->showCode($lines, 'php'); ?>
Open your console <code>F12</code> and click on <button class="button" id="try-event-yourCustomHookName">try</button>
<script nonce="<?php print getNonce() ?>" >
document.addEventListener('Dolibarr:Ready', function(e) {
// the dom is ready and you are sure Dolibarr js context is loaded
// Add a listener to the yourCustomHookName event
Dolibarr.on('yourCustomHookName', function(data) {
console.log('With Dolibarr.on : data will contain { data01: stuff, data02: other stuff }', data);
});
document.addEventListener('Dolibarr:yourCustomHookName', function(e) {
console.log('With document.addEventListener : e.detail will contain { data01: stuff, data02: other stuff }', e.detail);
});
document.getElementById('try-event-yourCustomHookName').addEventListener('click', function(e) {
// you can create js hooks
Dolibarr.executeHook('yourCustomHookName', { data01: 'stuff', data02: 'other stuff' })
});
});
</script>
</div>
</div>
<div id="titlesection-event-init-vs-ready" class="documentation-section">
<h2 class="documentation-title">Difference between Dolibarr:Init and Dolibarr:Ready event</h2>
<p>
Dolibarr provides two main initialization events for its JavaScript context: <code>Dolibarr:Init</code> and <code>Dolibarr:Ready</code>.
Understanding their difference is important when developing modules or tools.
</p>
<ul>
<li>
<strong>Dolibarr:Init</strong> is triggered immediately when the Dolibarr context is created.
This event is intended for:
<ul>
<li>Defining or registering new tools via <code>Dolibarr.defineTool()</code>.</li>
<li>Setting context variables (<code>Dolibarr.setContextVar()</code> / <code>Dolibarr.setContextVars()</code>).</li>
<li>Preparing configuration that must be available before the DOM is fully loaded.</li>
</ul>
It occurs <em>before</em> <code>Dolibarr:Ready</code>, so it is ideal for setup tasks that other tools may depend on.
</li>
<li>
<strong>Dolibarr:Ready</strong> is triggered once the DOM is ready, similar to <code>$(document).ready()</code> in jQuery.
This event is intended for:
<ul>
<li>Running code that interacts with the DOM.</li>
<li>Attaching event listeners to elements on the page.</li>
<li>Executing functionality that requires all tools and context variables to be fully initialized.</li>
</ul>
</li>
</ul>
<p>
In short, use <code>Dolibarr:Init</code> for setting up tools and context variables, and <code>Dolibarr:Ready</code> for code that needs the DOM and fully initialized context.
</p>
<h3>Examples of usage</h3>
<div class="documentation-example">
<?php
$lines = array(
'<script>',
' // Example: Dolibarr:Init - define a tool and set context variables early',
' document.addEventListener(\'Dolibarr:Init\', function(e) {',
' console.log("Init event fired, Dolibarr is initialised and receive context vars a tools");',
' });',
'',
' // Example: Dolibarr:Ready - interact with DOM and use tools',
' document.addEventListener(\'Dolibarr:Ready\', function(e) {',
' console.log("Ready event fired, DOM is ready");',
'',
'',
' // Attach event listener to a DOM element',
' const btn = document.getElementById("myButton");',
' if(btn) {',
' btn.addEventListener("click", function() {',
' alert("Button clicked! Context value: " + Dolibarr.getContextVar("mySetting"));',
' });',
' }',
' });',
'</script>',
);
echo $documentation->showCode($lines, 'php'); ?>
</div>
<p>
In summary:
<ul>
<li><code>Dolibarr:Init</code> → early setup, tools, context variables, configuration.</li>
<li><code>Dolibarr:Ready</code> → DOM is ready, safe to manipulate elements and use tools defined in Init.</li>
</ul>
</p>
</div>
<div class="documentation-section">
<h2 id="titlesection-await-hooks" class="documentation-title">Async Hooks (Await Hooks) - sequential execution</h2>
<p>
Dolibarr supports <strong>asynchronous hooks</strong> using <code>Dolibarr.onAwait()</code> and <code>Dolibarr.executeHookAwait()</code>.
These hooks allow you to register functions that execute <em>in sequence</em> and can modify data before passing it to the next hook.
They are useful for complex workflows where multiple modules or scripts need to process or enrich the same data asynchronously.
</p>
<p>
Each hook can optionally specify <code>before</code> or <code>after</code> to control the execution order relative to other hooks.
Every hook registration returns a unique <code>id</code>, which can be used to reference or unregister the hook later.
</p>
<p>
Unlike standard synchronous hooks registered with <code>Dolibarr.on()</code>, await hooks return a <code>Promise</code> when executed.
This means you can <code>await</code> their results in your code, and any asynchronous operations inside a hook (e.g., API calls, timers) will be handled correctly before moving to the next hook.
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script nonce="<?php print getNonce() ?>">',
' document.addEventListener(\'Dolibarr:Ready\', async function(e) {',
'',
' // Register async hooks will be executed in first place',
' Dolibarr.onAwait(\'calculateDiscount\', async function(order) {',
' order.total *= 0.9; // Apply 10% discount',
' return order;',
' }, { id: \'discount10\' });',
'',
' // Register async hooks will be executed in third place',
' Dolibarr.onAwait(\'calculateDiscount\', async function(order) {',
' if(order.total > 1000) order.total -= 50; // Extra discount over 1000',
' return order;',
' }, { id: \'discountOver1000\', after: \'discount10\' });',
'',
' // Register async hooks will be executed in second place',
' // this hook item as no id so plus10HookItemId will receive a unique random id ',
' let plus10HookItemId = Dolibarr.onAwait(\'calculateDiscount\', async function(order) {',
' order.newObjectAttribute = \'My value\';',
' order.total += 10;',
' return order;',
' }, { before: \'discountOver1000\' });',
'',
' document.getElementById(\'try-event-yourCustomAwaitHookName\').addEventListener(\'click\', async function(e) {',
' // Execute all registered await hooks sequentially',
' let order = {total: 1200};',
' order = await Dolibarr.executeHookAwait(\'calculateDiscount\', order);',
' console.log(order); // order.total : 1200 -> 1080 -> 1090 -> 1040',
' });',
'',
' });',
'</script>',
);
echo $documentation->showCode($lines, 'php'); ?>
Open your console <code>F12</code> and click on <button class="button" id="try-event-yourCustomAwaitHookName">try</button>
<script nonce="<?php print getNonce() ?>">
document.addEventListener('Dolibarr:Ready', async function(e) {
// Register async hooks will be executed in first place
Dolibarr.onAwait('calculateDiscount', async function(order) {
order.total *= 0.9; // Apply 10% discount
return order;
}, { id: 'discount10' });
// Register async hooks will be executed in third place
Dolibarr.onAwait('calculateDiscount', async function(order) {
if(order.total > 1000) order.total -= 50; // Extra discount over 1000
return order;
}, { id: 'discountOver1000', after: 'discount10' });
// Register async hooks will be executed in second place
Dolibarr.onAwait('calculateDiscount', async function(order) {
order.newObjectAttribute = 'My value';
order.total+= 10;
return order;
}, { before: 'discountOver1000' });
document.getElementById('try-event-yourCustomAwaitHookName').addEventListener('click', async function(e) {
// Execute all registered await hooks sequentially
let order = {total: 1200};
order = await Dolibarr.executeHookAwait('calculateDiscount', order);
console.log(order); // order.total : 1200 -> 1080 -> 1090 -> 1040
});
});
</script>
</div>
</div>
<div class="documentation-section">
<h2 id="titlesection-create-tool-example" class="documentation-title">Example of creating a new context tool</h2>
<h3>Defining Tools</h3>
<p>
You can define reusable and protected tools in the Dolibarr context using <code>Dolibarr.defineTool</code>.
</p>
<p>See also <code>dolibarr-context.mock.js</code> for defining all standard Dolibarr tools and creating mock implementations to improve code completion and editor support.</p>
<p><b>Note :</b> a tool can be a class not only a function</p>
<div class="documentation-example">
<?php
$lines = array(
'<script>',
'document.addEventListener(\'Dolibarr:Init\', function(e) {',
' // Define a simple tool',
' let overwrite = false; // Once a tool is defined, it cannot be replaced.',
' Dolibarr.defineTool(\'alertUser\', (msg) => alert(\'[Dolibarr] \' + msg), overwrite);',
'});',
'',
'document.addEventListener(\'Dolibarr:Ready\', function(e) {',
' // Use the tool',
' Dolibarr.tools.alertUser(\'hello world\');',
'});',
'</script>',
);
echo $documentation->showCode($lines, 'php'); ?>
</div>
<h3>Protected Tools</h3>
<p>
Once a tool is defined on overwrite false, it cannot be replaced. Attempting to redefine it without overwrite will throw an error:
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script>',
' try {',
' Dolibarr.defineTool(\'alertUser\', () => {});',
' } catch (e) {',
' console.error(e.message);',
' }',
'</script>',
);
echo $documentation->showCode($lines, 'php'); ?>
</div>
<h3>Reading Tools</h3>
<p>
You can read the list of available tools using <code>Dolibarr.tools</code>. It returns a frozen copy:
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script>',
' console.log(Dolibarr.tools);',
' if(Dolibarr.checkToolExist(\'Tool name to check\')){/* ... */}else{/* ... */}; ',
'</script>',
);
echo $documentation->showCode($lines, 'php'); ?>
</div>
</div>
<div class="documentation-section">
<h2 id="titlesection-tool-seteventmessage" class="documentation-title">Set event message tool</h2>
<p>
Instead of calling JNotify directly in your code, use Dolibarrs setEventMessage tool.
Dolibarr provides the configuration option DISABLE_JQUERY_JNOTIFY, which disables the jQuery JNotify system, usually because another notification library will be used instead.
</p>
<p>
If you rely on Dolibarr.tools.setEventMessage(), your code remains compatible even if the underlying notification system changes.
The setEventMessage tool can be replaced internally without requiring any changes in your modules or custom scripts.
</p>
<p>
This means all developers can write features without worrying about frontend compatibility or future library replacements. Enjoy!
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script nonce="<?php print getNonce() ?>" >',
' document.addEventListener(\'Dolibarr:Ready\', function(e) {',
'',
' document.getElementById(\'setEventMessage-success\').addEventListener(\'click\', function(e) {',
' Dolibarr.tools.setEventMessage(\'Success Test\');',
' });',
'',
' document.getElementById(\'setEventMessage-error\').addEventListener(\'click\', function(e) {',
' Dolibarr.tools.setEventMessage(\'Error Test\', \'errors\');',
' });',
'',
' document.getElementById(\'setEventMessage-error-sticky\').addEventListener(\'click\', function(e) {',
' Dolibarr.tools.setEventMessage(\'Error Test\', \'errors\', true);',
' });',
'',
' document.getElementById(\'setEventMessage-warning\').addEventListener(\'click\', function(e) {',
' Dolibarr.tools.setEventMessage(\'Warning Test\', \'warnings\');',
' });',
'',
' });',
'</script>',
);
echo $documentation->showCode($lines, 'php'); ?>
<script nonce="<?php print getNonce() ?>" >
document.addEventListener('Dolibarr:Ready', function(e) {
document.getElementById('setEventMessage-success').addEventListener('click', function(e) {
Dolibarr.tools.setEventMessage('Success Test')
});
document.getElementById('setEventMessage-error').addEventListener('click', function(e) {
Dolibarr.tools.setEventMessage('Error Test', 'errors');
});
document.getElementById('setEventMessage-error-sticky').addEventListener('click', function(e) {
Dolibarr.tools.setEventMessage('Error Test', 'errors', true);
});
document.getElementById('setEventMessage-warning').addEventListener('click', function(e) {
Dolibarr.tools.setEventMessage('Warning Test', 'warnings');
});
});
</script>
<button id="setEventMessage-success" class="button">Alert success</button>
<button id="setEventMessage-error" class="button">Alert error</button>
<button id="setEventMessage-error-sticky" class="button">Alert error sticky</button>
<button id="setEventMessage-warning" class="button">Alert warning</button>
</div>
</div>
<div class="documentation-section">
<h2 id="titlesection-contextvars" class="documentation-title">Set and use context vars</h2>
<p>
The <strong>Context Vars</strong> system allows you to define and manage variables that are globally accessible within the Dolibarr JavaScript context. These variables can store configuration data, URLs, tokens, user IDs, object references, or any other values needed by your frontend code and tools.
By using context vars, you can:
<ul>
<li>Pass server-side data (from PHP) to JavaScript safely and consistently.</li>
<li>Provide reusable configuration for Dolibarr tools, widgets, or modules without hardcoding values.</li>
<li>Define overridable or non-overridable vars to protect critical values while allowing flexible overrides when necessary.</li>
<li>Use <code>Dolibarr.setContextVar</code> for single values or <code>Dolibarr.setContextVars</code> to pass multiple values at once.</li>
<li>Access these variables anywhere in your code via <code>Dolibarr.getContextVar(key)</code>.</li>
<li>Ensure that all your modules and tools can rely on consistent and up-to-date context information, improving maintainability and interoperability.</li>
</ul>
This system is particularly useful for setting up base URLs, API endpoints, user-specific information, or runtime data that needs to be shared across multiple Dolibarr frontend tools.
</p>
<h3>Add context var (overridable or not)</h3>
<div class="documentation-example">
<?php
$lines = array(
'<script nonce="<?php print getNonce() ?>" >',
' document.addEventListener(\'Dolibarr:Init\', function(e) {',
' // Add no overridable context var',
' Dolibarr.setContextVar(\'yourKey\', \'YourValue\');',
'',
' // Add overridable context var',
' Dolibarr.setContextVar(\'yourKey2\', \'YourValue\', true);',
' });',
'</script>',
);
echo $documentation->showCode($lines, 'php');
?>
</div>
<h3>Add multiple context vars (overridable or not)</h3>
<div class="documentation-example">
<?php
$lines = array(
'<?php',
' $contextConst = [',
' \'DOL_URL_ROOT\' => DOL_URL_ROOT,',
' \'token\' => newToken(),',
' \'cardObjectElement\' => $object->element,',
' \'cardObjectId\' => $object->id,',
' \'currentUserId\' => $user->id',
' // ...',
' ];',
'',
' $contextVars = [',
' \'lastCardDataRefresh\' => time(),',
' // ...',
' ]',
'?>',
'<script nonce="<?php print getNonce() ?>" >',
' document.addEventListener(\'Dolibarr:Init\', function(e) {',
' Dolibarr.setContextVars(<?php print json_encode($contextConst); ?>);',
' Dolibarr.setContextVars(<?php print json_encode($contextVars); ?>, true);',
' });',
'</script>',
);
echo $documentation->showCode($lines, 'php');
?>
</div>
<h3>Get context var</h3>
<div class="documentation-example">
<?php
$lines = array(
'<script nonce="<?php print getNonce() ?>" >',
' document.addEventListener(\'Dolibarr:Ready\', function(e) {',
' let url = Dolibarr.getContextVar(\'DOL_URL_ROOT\', \'The optional fallback value\'));',
' console.log(url);',
' });',
'</script>',
);
echo $documentation->showCode($lines, 'php');
?>
</div>
</div>
</div>
</div>
</div>
<?php
// Output close body + html
$documentation->docFooter();
?>

View File

@@ -0,0 +1,110 @@
<?php
/*
* Copyright (C) ---Replace with your own copyright and developer email---
*
* 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 <https://www.gnu.org/licenses/>.
*/
//if (! defined('NOREQUIREDB')) define('NOREQUIREDB', '1'); // Do not create database handler $db
//if (! defined('NOREQUIREUSER')) define('NOREQUIREUSER', '1'); // Do not load object $user
if (! defined('NOREQUIRESOC')) define('NOREQUIRESOC', '1'); // Do not load object $mysoc
//if (! defined('NOREQUIRETRAN')) define('NOREQUIRETRAN', '1'); // Do not load object $langs
//if (! defined('NOSCANGETFORINJECTION')) define('NOSCANGETFORINJECTION', '1'); // Do not check injection attack on GET parameters
//if (! defined('NOSCANPOSTFORINJECTION')) define('NOSCANPOSTFORINJECTION', '1'); // Do not check injection attack on POST parameters
//if (! defined('NOTOKENRENEWAL')) define('NOTOKENRENEWAL', '1'); // Do not roll the Anti CSRF token (used if MAIN_SECURITY_CSRF_WITH_TOKEN is on)
//if (! defined('NOSTYLECHECK')) define('NOSTYLECHECK', '1'); // Do not check style html tag into posted data
if (! defined('NOREQUIREMENU')) define('NOREQUIREMENU', '1'); // If there is no need to load and show top and left menu
//if (! defined('NOREQUIREHTML')) define('NOREQUIREHTML', '1'); // If we don't need to load the html.form.class.php
//if (! defined('NOREQUIREAJAX')) define('NOREQUIREAJAX', '1'); // Do not load ajax.lib.php library
//if (! defined("NOLOGIN")) define("NOLOGIN", '1'); // If this page is public (can be called outside logged session). This include the NOIPCHECK too.
//if (! defined('NOIPCHECK')) define('NOIPCHECK', '1'); // Do not check IP defined into conf $dolibarr_main_restrict_ip
//if (! defined("MAIN_LANG_DEFAULT")) define('MAIN_LANG_DEFAULT', 'auto'); // Force lang to a particular value
//if (! defined("MAIN_AUTHENTICATION_MODE")) define('MAIN_AUTHENTICATION_MODE', 'aloginmodule'); // Force authentication handler
//if (! defined('CSRFCHECK_WITH_TOKEN')) define('CSRFCHECK_WITH_TOKEN', '1'); // Force use of CSRF protection with tokens even for GET
//if (! defined('NOBROWSERNOTIF')) define('NOBROWSERNOTIF', '1'); // Disable browser notification
// Load Dolibarr environment
require '../../../../../../main.inc.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
/**
* INTERFACE FOR JS CONTEXT LANG
*/
if (empty($dolibarr_nocache)) {
$delaycache = '86400';
header('Cache-Control: max-age=' . $delaycache . ', public, must-revalidate');
header('Pragma: cache'); // This is to avoid to have Pragma: no-cache set by proxy or web server
header('Expires: ' . gmdate('D, d M Y H:i:s', time() + (int) $delaycache) . ' GMT'); // This is to avoid to have Expires set by proxy or web server
} else {
// If any cache on files were disable by config file (for test purpose)
header('Cache-Control: no-cache');
}
header('Content-Type: application/json; charset=utf-8');
$local = GETPOST('local');
if (empty($local)) {
$local = $langs->getDefaultLang();
}
$domain = GETPOST('domain');
if (empty($domain)) {
echo json_encode(['error' => 'Missing domain']);
exit;
}
if (!preg_match('/^[A-Za-z0-9_-]+(?:@[A-Za-z0-9_-]+)?$/', $domain)) {
echo json_encode(['error' => 'Invalid domain']);
exit;
}
if (!preg_match('/^[a-z]{1,2}_[A-Z]{1,2}(?:,[a-z]{1,2}_[A-Z]{1,2})*$/', $local)) {
echo json_encode(['error' => 'Invalid langs codes']);
exit;
}
/*
Format for JS:
{
fr_FR : {KEY:"TEXT", ...},
en_US : {KEY:"TEXT", ...}
}
*/
$locals = explode(',', $local);
$json = new stdClass();
foreach ($locals as $langCode) {
$json->$langCode = [];
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($langCode);
$outputlangs->load($domain);
foreach ($outputlangs->tab_translate as $k => $v) {
$json->$langCode[$k] = dolPrintHTML($v); // to escape js and other stuff
}
}
print json_encode($json, JSON_PRETTY_PRINT);

View File

@@ -0,0 +1,207 @@
<?php
/*
* Copyright (C) 2025 Anthony Damhet <a.damhet@progiseize.fr>
* Copyright (C) 2025 Frédéric France <frederic.france@free.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
* 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 <https://www.gnu.org/licenses/>.
*/
// Load Dolibarr environment
require '../../../../../../main.inc.php';
/**
* @var DoliDB $db
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*/
// Protection if external user
if ($user->socid > 0) {
accessforbidden();
}
// Includes
require_once DOL_DOCUMENT_ROOT . '/admin/tools/ui/class/documentation.class.php';
// Load documentation translations
$langs->load('uxdocumentation');
//
$documentation = new Documentation($db);
$group = 'ExperimentalUx';
$experimentName = 'UxDolibarrContextLangsTool';
$experimentAssetsPath = $documentation->baseUrl . '/experimental/experiments/dolibarr-context/assets/';
$js = [
'/includes/ace/src/ace.js',
'/includes/ace/src/ext-statusbar.js',
'/includes/ace/src/ext-language_tools.js',
$experimentAssetsPath . '/dolibarr-context.umd.js',
$experimentAssetsPath . '/dolibarr-tool.langs.js',
$experimentAssetsPath . '/dolibarr-tool.seteventmessage.js',
];
$css = [];
// Output html head + body - Param is Title
$documentation->docHeader($langs->trans($experimentName, $group), $js, $css);
// Set view for menu and breadcrumb
$documentation->view = [$group, 'UxDolibarrContext', $experimentName];
// Output sidebar
$documentation->showSidebar(); ?>
<script>
Dolibarr.setContextVars(<?php print json_encode([
'DOL_VERSION' => DOL_VERSION,
'MAIN_LANG_DEFAULT' => $langs->getDefaultLang(),
'DOL_LANG_INTERFACE_URL' => dol_buildpath('admin/tools/ui/experimental/experiments/dolibarr-context/langs-tool-interface.php', 1),
]) ?>);
</script>
<div class="doc-wrapper">
<?php $documentation->showBreadCrumb(); ?>
<div class="doc-content-wrapper">
<h1 class="documentation-title"><?php echo $langs->trans($experimentName); ?></h1>
<?php $documentation->showSummary(); ?>
<div class="documentation-section">
<h2 id="titlesection-basicusage" class="documentation-title">Introduction</h2>
<p>
The Dolibarr Context Langs Tool is a powerful JavaScript utility to manage translations and locales dynamically.<br/>
It allows you to load translation domains, set the current language, clear cache, and retrieve translated strings in your scripts.
</p>
</div>
<div class="documentation-section">
<h2 id="titlesection-setup-contextvars" class="documentation-title">Setup Context Variables</h2>
<p>
Before using the tool, you should declare the necessary context variables on your page.<br/>
These variables allow the tool to know the current Dolibarr version, the default language, and the interface URL used to fetch translations.
</p>
<p>
However, like the setEventMessage tool, the Langs tool is a core tool and is always loaded by Dolibarr.<br/>
Therefore, in most cases, you do not need to set these variables manually, as they are already defined.
</p>
<div class="documentation-example">
<?php
$lines = array(
'<script nonce="'.getNonce().'" >',
'Dolibarr.setContextVars(<?php print json_encode([',
' \'DOL_VERSION\' => DOL_VERSION,',
' \'MAIN_LANG_DEFAULT\' => $langs->getDefaultLang(),',
' \'DOL_LANG_INTERFACE_URL\' => dol_buildpath(\'admin/tools/ui/experimental/experiments/dolibarr-context/langs-tool-interface.php\',1),',
']) ?>);',
'</script>',
);
echo $documentation->showCode($lines, 'php');
?>
</div>
</div>
<div class="documentation-section">
<h2 id="titlesection-basic-usage" class="documentation-title">Basic Usage</h2>
<p>
The main features of the Langs tool are:
</p>
<ul>
<li>Load translations for a specific domain with caching</li>
<li>Set or change the current locale</li>
<li>Clear cached translations</li>
<li>Retrieve a translated string by key</li>
</ul>
<p>Example:</p>
<div class="documentation-example">
<?php
$lines = array(
'<script nonce="'.getNonce().'" >',
'document.addEventListener(\'Dolibarr:Ready\', async function(e) {',
'',
' if(Dolibarr.checkToolExist(\'langs\')){ // not mandatory because langs tool will be a core tool',
'',
' // Load langs',
' Dolibarr.tools.langs.load(\'uxdocumentation\'); // will use cache but need to load lang in new local',
'',
' // Clear cache',
' document.getElementById(\'clearCache\').addEventListener(\'click\', async function(e) {',
' await Dolibarr.tools.langs.clearCache();',
' const txt = Dolibarr.tools.langs.trans(\'CacheCleared\');',
' Dolibarr.tools.setEventMessage(txt);',
' });',
'',
' // SET lang in fr_FR',
' document.getElementById(\'setlangFr\').addEventListener(\'click\', async function(e) {',
' await Dolibarr.tools.langs.setLocale(\'fr_FR\');',
' const txt = Dolibarr.tools.langs.trans(\'LangsLocalChangedTo\', \'fr_FR\');',
' Dolibarr.tools.setEventMessage(txt);',
' });',
'',
' // SET lang in en_US',
' document.getElementById(\'setlangEn\').addEventListener(\'click\', async function(e) {',
' await Dolibarr.tools.langs.setLocale(\'en_US\');',
' const txt = Dolibarr.tools.langs.trans(\'LangsLocalChangedTo\', \'en_US\');',
' Dolibarr.tools.setEventMessage(txt);',
' });',
'',
' // pop a message in current lang',
' document.getElementById(\'popmessage\').addEventListener(\'click\', async function(e) {',
' const txt = Dolibarr.tools.langs.trans(\'ContextLangToolTest\');',
' Dolibarr.tools.setEventMessage(txt);',
' });',
' }',
'});',
'</script>',
);
echo $documentation->showCode($lines, 'php');
print implode("\n", $lines);
?>
<p>1. Set the current lang</p>
<div >
<button id="setlangFr" class="button">Set lang in french</button>
<button id="setlangEn" class="button">Set lang in english</button>
<button id="clearCache" class="button">Clear cache</button>
</div>
<p>2. Pop translated message</p>
<div>
<button id="popmessage" class="button">pop message</button>
</div>
</div>
</div>
</div>
</div>
</div>
<?php
// Output close body + html
$documentation->docFooter();
?>

View File

@@ -46,7 +46,7 @@ $arrayofai = getListOfAIServices();
// Parameters
$action = GETPOST('action', 'aZ09');
$backtopage = GETPOST('backtopage', 'alpha');
$cancel = GETPOST('cancel');
$cancel = GETPOST('cancel', 'alpha');
$modulepart = GETPOST('modulepart', 'aZ09'); // Used by actions_setmoduleoptions.inc.php
$functioncode = GETPOST('functioncode', 'alpha');

View File

@@ -29,6 +29,10 @@
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/api.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
@@ -117,6 +121,10 @@ $linkback = '<a href="'.dolBuildUrl(DOL_URL_ROOT.'/admin/modules.php', ['restore
print load_fiche_titre($langs->trans("ApiSetup"), $linkback, 'title_setup');
$head = api_admin_prepare_head();
print dol_get_fiche_head($head, 'parameter', '', -1);
print '<span class="opacitymedium">'.$langs->trans("ApiDesc")."</span><br>\n";
print "<br>\n";

View File

@@ -0,0 +1,319 @@
<?php
/* Copyright (C) 2004 Rodolphe Quiedeville <rodolphe@quiedeville.org>
* Copyright (C) 2005-2016 Laurent Destailleur <eldy@users.sourceforge.org>
* Copyright (C) 2011 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2012-2018 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2015 Jean-François Ferry <jfefe@aternatik.fr>
* Copyright (C) 2024 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2024 Frédéric France <frederic.france@free.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
* 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 <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/api/admin/index.php
* \ingroup api
* \brief Page to setup Webservices REST module
*/
// Load Dolibarr environment
require '../../main.inc.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/api.lib.php';
/**
* @var Conf $conf
* @var DoliDB $db
* @var Form $form
* @var HookManager $hookmanager
* @var Translate $langs
* @var User $user
*
* @var string $dolibarr_main_url_root
*/
// Load translation files required by the page
$langs->loadLangs(array('admin', 'users'));
$error = 0;
if (!$user->admin) {
accessforbidden();
}
// Retrieve needed GETPOSTS for this file
// Action / Massaction
$action = GETPOST('action', 'aZ09');
$massaction = GETPOST('massaction', 'alpha');
$confirm = GETPOST('confirm', 'alpha');
$toselect = GETPOST('toselect', 'array');
// List filters
$search_user = GETPOST('search_user', 'alpha');
$search_entity = GETPOST('search_entity', 'alpha');
$search_datec_startday = GETPOSTINT('search_datec_startday');
$search_datec_startmonth = GETPOSTINT('search_datec_startmonth');
$search_datec_startyear = GETPOSTINT('search_datec_startyear');
$search_datec_endday = GETPOSTINT('search_datec_endday');
$search_datec_endmonth = GETPOSTINT('search_datec_endmonth');
$search_datec_endyear = GETPOSTINT('search_datec_endyear');
$search_datec_start = dol_mktime(0, 0, 0, $search_datec_startmonth, $search_datec_startday, $search_datec_startyear);
$search_datec_end = dol_mktime(23, 59, 59, $search_datec_endmonth, $search_datec_endday, $search_datec_endyear);
$search_tms_startday = GETPOSTINT('search_tms_startday');
$search_tms_startmonth = GETPOSTINT('search_tms_startmonth');
$search_tms_startyear = GETPOSTINT('search_tms_startyear');
$search_tms_endday = GETPOSTINT('search_tms_endday');
$search_tms_endmonth = GETPOSTINT('search_tms_endmonth');
$search_tms_endyear = GETPOSTINT('search_tms_endyear');
$search_tms_start = dol_mktime(0, 0, 0, $search_tms_startmonth, $search_tms_startday, $search_tms_startyear);
$search_tms_end = dol_mktime(23, 59, 59, $search_tms_endmonth, $search_tms_endday, $search_tms_endyear);
// Pagination
$limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit;
$sortfield = GETPOST('sortfield', 'aZ09comma');
$sortorder = GETPOST('sortorder', 'aZ09comma');
$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page");
if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) {
$page = 0;
}
$offset = $limit * $page;
$pageprev = $page - 1;
$pagenext = $page + 1;
if (!$sortfield) {
$sortfield = 'oat.tms';
}
if (!$sortorder) {
$sortorder = 'DESC';
}
$arrayfields = array(
'u.login' => array('label' => "User", 'checked' => '1'),
'e.label' => array('label' => "Entity", 'checked' => '1'),
'oat.datec' => array('label' => "DateCreation", 'checked' => '1'),
'oat.tms' => array('label' => "DateModification", 'checked' => '1'),
);
/*
* Action
*/
if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers
$search_user = '';
$search_entity = '';
$search_datec_startday = '';
$search_datec_startmonth = '';
$search_datec_startyear = '';
$search_datec_endday = '';
$search_datec_endmonth = '';
$search_datec_endyear = '';
$search_datec_start = '';
$search_datec_end = '';
$search_tms_startday = '';
$search_tms_startmonth = '';
$search_tms_startyear = '';
$search_tms_endday = '';
$search_tms_endmonth = '';
$search_tms_endyear = '';
$search_tms_start = '';
$search_tms_end = '';
$toselect = array();
}
if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')
|| GETPOST('button_search_x', 'alpha') || GETPOST('button_search.x', 'alpha') || GETPOST('button_search', 'alpha')) {
$massaction = ''; // Protection to avoid mass action if we force a new search during a mass action confirmation
}
if (($action == 'delete' && $confirm == 'yes')) {
$db->begin();
$nbok = 0;
$TMsg = array();
//$toselect could contain duplicate entries, cf https://github.com/Dolibarr/dolibarr/issues/26244
$unique_arr = array_unique($toselect);
foreach ($unique_arr as $toselectid) {
$sql = "DELETE FROM ".MAIN_DB_PREFIX."oauth_token";
$sql .= " WHERE rowid = ".((int) $toselectid);
$sql .= " AND service = 'dolibarr_rest_api'";
$result = $db->query($sql);
if ($result > 0) {
$nbok++;
} else {
setEventMessages($db->error(), null, 'errors');
$error++;
break;
}
}
if (empty($error)) {
// Message for elements well deleted
if ($nbok > 1) {
setEventMessages($langs->trans("RecordsDeleted", $nbok), null, 'mesgs');
} elseif ($nbok > 0) {
setEventMessages($langs->trans("RecordDeleted", $nbok), null, 'mesgs');
} else {
setEventMessages($langs->trans("NoRecordDeleted"), null, 'mesgs');
}
$db->commit();
} else {
$db->rollback();
}
//var_dump($listofobjectthirdparties);exit;
}
/*
* View
*/
$nbtotalofrecords = '';
if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) {
/* The fast and low memory method to get and count full list converts the sql into a sql count */
$sqlforcount = 'SELECT COUNT(*) as nbtotalofrecords';
$sqlforcount .= " FROM ".MAIN_DB_PREFIX."oauth_token as oat";
$sqlforcount .= " WHERE entity IN (0, ".((int) $conf->entity).")";
$sqlforcount .= " AND service = 'dolibarr_rest_api'";
$resql = $db->query($sqlforcount);
if ($resql) {
$objforcount = $db->fetch_object($resql);
$nbtotalofrecords = $objforcount->nbtotalofrecords;
} else {
dol_print_error($db);
}
if (($page * $limit) > $nbtotalofrecords) { // if total resultset is smaller then paging size (filtering), goto and load page 0
$page = 0;
$offset = 0;
}
$db->free($resql);
}
$sql = "SELECT oat.rowid, oat.tokenstring, oat.entity, oat.state as rights, oat.fk_user, oat.datec as date_creation, oat.tms as date_modification,";
$sql .= " oat.lastaccess, oat.apicount_total";
$sql .= " FROM ".MAIN_DB_PREFIX."oauth_token as oat";
$sql .= " WHERE service = 'dolibarr_rest_api'";
$sql .= " AND EXISTS(SELECT 'exist' FROM llx_user as u WHERE u.api_key IS NOT NULL AND u.rowid = oat.fk_user)";
if ($search_user) {
$sql .= " AND EXISTS (SELECT 'exist' FROM ".MAIN_DB_PREFIX."user u";
$sql .= " WHERE (u.lastname LIKE '%".$db->escape($search_user)."%'";
$sql .= " OR u.firstname LIKE '%".$db->escape($search_user)."%')";
$sql .= " AND oat.fk_user = u.rowid))";
}
if ($search_datec_start) {
$sql .= " AND oat.datec >= '".$db->idate($search_datec_start)."'";
}
if ($search_datec_end) {
$sql .= " AND oat.datec <= '".$db->idate($search_datec_end)."'";
}
if ($search_tms_start) {
$sql .= " AND oat.tms >= '".$db->idate($search_tms_start)."'";
}
if ($search_tms_end) {
$sql .= " AND oat.tms <= '".$db->idate($search_tms_end)."'";
}
$sql .= $db->order($sortfield, $sortorder);
if ($limit) {
$sql .= $db->plimit($limit + 1, $offset);
}
$resql = $db->query($sql);
$num = $db->num_rows($resql);
llxHeader('', '', '', '', 0, 0, '', '', '', 'mod-api page-admin-index');
$param = '';
if ($limit > 0 && $limit != $conf->liste_limit) {
$param .= '&limit='.((int) $limit);
}
if ($search_datec_startday) {
$param .= '&search_date_startday='.urlencode((string) ($search_datec_startday));
}
if ($search_datec_startmonth) {
$param .= '&search_date_startmonth='.urlencode((string) ($search_datec_startmonth));
}
if ($search_datec_startyear) {
$param .= '&search_date_startyear='.urlencode((string) ($search_datec_startyear));
}
if ($search_datec_endday) {
$param .= '&search_date_endday='.urlencode((string) ($search_datec_endday));
}
if ($search_datec_endmonth) {
$param .= '&search_date_endmonth='.urlencode((string) ($search_datec_endmonth));
}
if ($search_datec_endyear) {
$param .= '&search_date_endyear='.urlencode((string) ($search_datec_endyear));
}
if ($search_tms_startday) {
$param .= '&search_date_startday='.urlencode((string) ($search_tms_startday));
}
if ($search_tms_startmonth) {
$param .= '&search_date_startmonth='.urlencode((string) ($search_tms_startmonth));
}
if ($search_tms_startyear) {
$param .= '&search_date_startyear='.urlencode((string) ($search_tms_startyear));
}
if ($search_tms_endday) {
$param .= '&search_date_endday='.urlencode((string) ($search_tms_endday));
}
if ($search_tms_endmonth) {
$param .= '&search_date_endmonth='.urlencode((string) ($search_tms_endmonth));
}
if ($search_tms_endyear) {
$param .= '&search_date_endyear='.urlencode((string) ($search_tms_endyear));
}
$arrayofselected = is_array($toselect) ? $toselect : array();
$linkback = '<a href="'.DOL_URL_ROOT.'/admin/modules.php?restore_lastsearch_values=1">'.$langs->trans("BackToModuleList").'</a>';
print load_fiche_titre($langs->trans("ApiSetup"), $linkback, 'title_setup');
$head = api_admin_prepare_head();
print dol_get_fiche_head($head, 'token_list', '', -1);
$arrayofmassactions = array(
'predelete' => img_picto('', 'delete', 'class="pictofixedwidth"').$langs->trans("Delete")
);
if (GETPOSTINT('nomassaction') || in_array($massaction, array('presend', 'predelete'))) {
$arrayofmassactions = array();
}
$massactionbutton = $form->selectMassAction('', $arrayofmassactions);
$morehtmlright = '';
$tmpurlforbutton = DOL_URL_ROOT.'/user/api_token/card.php?action=create&backtopage='.urlencode(DOL_URL_ROOT.'/api/admin/token_list.php');
$morehtmlright .= dolGetButtonTitle($langs->trans('New'), '', 'fa fa-plus-circle', $tmpurlforbutton);
print '<form method="POST" action="'.$_SERVER["PHP_SELF"].'">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="formfilteraction" id="formfilteraction" value="list">';
print '<input type="hidden" name="action" value="list">';
print '<input type="hidden" name="sortfield" value="'.$sortfield.'">';
print '<input type="hidden" name="sortorder" value="'.$sortorder.'">';
// @phan-suppress-next-line PhanPluginSuspiciousParamOrder
print_barre_liste($langs->trans("ListOfTokensForAllUsers"), $page, $_SERVER["PHP_SELF"], $param, $sortfield, $sortorder, $massactionbutton, $num, $nbtotalofrecords, 'fa-at', 0, $morehtmlright, '', $limit, 0, 0, 1);
include DOL_DOCUMENT_ROOT.'/core/tpl/massactions_pre.tpl.php';
$colspan = 6; // Base colspan for empty list
include DOL_DOCUMENT_ROOT.'/core/tpl/apitoken_list.tpl.php';
print '</form>';
llxFooter();
$db->close();

View File

@@ -91,7 +91,7 @@ class DolibarrApiAccess implements iAuthenticate
public function __isAllowed()
{
// phpcs:enable
global $conf, $db, $langs, $user;
global $conf, $langs, $user;
$login = '';
$stored_key = '';
@@ -132,20 +132,34 @@ class DolibarrApiAccess implements iAuthenticate
if ($api_key) {
$userentity = 0;
$token_rowid = 0;
$sql = "SELECT u.login, u.datec, u.api_key,";
$sql .= " u.tms as date_modification, u.entity";
$sql .= " FROM ".MAIN_DB_PREFIX."user as u";
$sql .= " WHERE u.api_key = '".$this->db->escape($api_key)."' OR u.api_key = '".$this->db->escape(dolEncrypt($api_key, '', '', 'dolibarr'))."'";
if (!getDolGlobalString('API_IN_TOKEN_TABLE')) {
$sql = "SELECT u.login, u.datec, u.api_key as use_api, u.entity, u.api_key as api_key, u.entity as token_entity, 0 as token_rowid,";
$sql .= " u.tms as date_modification";
$sql .= " FROM ".MAIN_DB_PREFIX."user as u";
$sql .= " WHERE u.api_key = '".$this->db->escape($api_key)."' OR u.api_key = '".$this->db->escape(dolEncrypt($api_key, '', '', 'dolibarr'))."'";
} else {
$sql = "SELECT u.login, u.datec, u.api_key as use_api, u.entity, oat.tokenstring as api_key, oat.entity as token_entity, rowid as token_rowid,";
$sql .= " oat.tms as date_modification";
$sql .= " FROM ".MAIN_DB_PREFIX."oauth_token AS oat";
$sql .= " JOIN ".MAIN_DB_PREFIX."user AS u ON u.rowid = oat.fk_user";
$sql .= " WHERE (oat.tokenstring = '".$this->db->escape($api_key)."'";
$sql .= " OR oat.tokenstring = '".$this->db->escape(dolEncrypt($api_key, '', '', 'dolibarr'))."')";
$sql .= " AND oat.service = 'dolibarr_rest_api'";
}
$result = $this->db->query($sql);
if ($result) {
$nbrows = $this->db->num_rows($result);
if ($nbrows == 1) {
$obj = $this->db->fetch_object($result);
$login = $obj->login;
$stored_key = dolDecrypt($obj->api_key);
$userentity = $obj->entity;
$token_entity = $obj->token_entity;
$token_rowid = $obj->token_rowid;
if (!defined("DOLENTITY") && $conf->entity != ($obj->entity ? $obj->entity : 1)) { // If API was not forced with HTTP_DOLENTITY, and user is on another entity, so we reset entity to entity of user
$conf->entity = ($obj->entity ? $obj->entity : 1);
@@ -157,7 +171,7 @@ class DolibarrApiAccess implements iAuthenticate
// see master.inc.php
require_once DOL_DOCUMENT_ROOT.'/societe/class/societe.class.php';
$fmysoc = new Societe($db);
$fmysoc = new Societe($this->db);
$fmysoc->setMysoc($conf);
// We set some specific default values according to country
@@ -194,7 +208,7 @@ class DolibarrApiAccess implements iAuthenticate
$mysoc = $fmysoc;
// Reload langs
$langcode = (!getDolGlobalString('MAIN_LANG_DEFAULT') ? 'auto' : $conf->global->MAIN_LANG_DEFAULT);
$langcode = getDolGlobalString('MAIN_LANG_DEFAULT', 'auto');
if (!empty($user->conf->MAIN_LANG_DEFAULT)) {
$langcode = $user->conf->MAIN_LANG_DEFAULT;
}
@@ -204,6 +218,10 @@ class DolibarrApiAccess implements iAuthenticate
$langs->loadLangs(array('main'));
}
}
if ($conf->entity != ($token_entity ? $token_entity : 1)) {
throw new RestException(401, "functions_isallowed::check_user_api_key Authentication KO for '".$login."': Token not valid (may be a typo or a wrong entity)");
}
} elseif ($nbrows > 1) {
throw new RestException(503, 'Error when fetching user api_key : More than 1 user with this apikey');
}
@@ -256,12 +274,27 @@ class DolibarrApiAccess implements iAuthenticate
throw new RestException(401, $genericmessageerroruser);
}
// TODO
// Increase counter of API access
if (getDolGlobalString('API_COUNTER_ENABLED')) {
include DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
dolibarr_set_const($this->db, 'API_COUNTER_COUNT', getDolGlobalInt('API_COUNTER_COUNT') + 1);
//var_dump('eeee');exit;
if (!getDolGlobalString('API_IN_TOKEN_TABLE')) {
// Update the counter into table llx_const
include DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php';
dolibarr_set_const($this->db, 'API_COUNTER_COUNT', getDolGlobalInt('API_COUNTER_COUNT') + 1);
//var_dump('eeee');exit;
} else {
// Update the counter into table llx_oauth_token
$tmpnow = dol_getdate(dol_now('gmt'), true, 'gmt');
$sqlforcounter = "UPDATE ".$this->db->prefix()."oauth_token SET ";
$sqlforcounter .= " apicount_total = apicount_total + 1,";
$sqlforcounter .= " apicount_month = apicount_month + 1,";
// if last access was done during previous month, we save pageview_month into pageviews_previous_month
$sqlforcounter .= " pageviews_previous_month = ".$this->db->ifsql("lastaccess < '".$this->db->idate(dol_mktime(0, 0, 0, $tmpnow['mon'], 1, $tmpnow['year'], 'gmt', 0), 'gmt')."'", 'apicount_month', 'apicount_previous_month').",";
$sqlforcounter .= " lastaccess = '".$this->db->idate(dol_now('gmt'), 'gmt')."'";
$sqlforcounter .= " WHERE rowid = ".((int) $token_rowid);
$this->db->query($sqlforcounter);
}
}
// User seems valid

View File

@@ -1,6 +1,7 @@
<?php
/*
/* Copyright (C) 2025 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
* Copyright (C) 2025 Frédéric France <frederic.france@free.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
@@ -500,14 +501,15 @@ class EmailTemplates extends DolibarrApi
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
/**
* Clean sensible object datas
* @phpstan-template T
*
* @param Object $object Object to clean
* @phan-param CEmailTemplate $object
* @phpstan-param CEmailTemplate $object
* @phpstan-param T $object
*
* @return Object Object with cleaned properties
* @phan-return CEmailTemplate
* @phpstan-return CEmailTemplate
* @phpstan-return T
*/
protected function _cleanObjectDatas($object)
{

View File

@@ -408,14 +408,15 @@ class ObjectLinks extends DolibarrApi
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
/**
* Clean sensible object datas
* @phpstan-template T
*
* @param Object $object Object to clean
* @phan-param ObjectLink $object
* @phpstan-param ObjectLink $object
* @phpstan-param T $object
*
* @return Object Object with cleaned properties
* @phan-return ObjectLink
* @phpstan-return ObjectLink
* @phpstan-return T
*/
protected function _cleanObjectDatas($object)
{

View File

@@ -1,13 +1,13 @@
<?php
/* Copyright (C) 2016 Xebax Christy <xebax@wanadoo.fr>
* Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2017 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2017 Neil Orley <neil.orley@oeris.fr>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2022 Thibault FOUCART <support@ptibogxiv.net>
* Copyright (C) 2024 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
/* Copyright (C) 2016 Xebax Christy <xebax@wanadoo.fr>
* Copyright (C) 2016 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2017-2025 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2017 Neil Orley <neil.orley@oeris.fr>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2018-2022 Thibault FOUCART <support@ptibogxiv.net>
* Copyright (C) 2024 Jon Bendtsen <jon.bendtsen.github@jonb.dk>
* Copyright (C) 2024-2025 MDW <mdeweerd@users.noreply.github.com>
* Copyright (C) 2025 Charlene Benke <charlene@patas-monkey.com>
* Copyright (C) 2025 Charlene Benke <charlene@patas-monkey.com>
*
*
* This program is free software; you can redistribute it and/or modify
@@ -836,9 +836,12 @@ class Setup extends DolibarrApi
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.PublicUnderscore
/**
* Clean sensible object datas
* @phpstan-template T
*
* @param Object $object Object to clean
* @return Object Object with cleaned properties
* @phpstan-param T $object
* @phpstan-return T
*/
protected function _cleanObjectDatas($object)
{
@@ -974,6 +977,10 @@ class Setup extends DolibarrApi
{
$list = array();
if (!DolibarrApiAccess::$user->hasRight('expensereport', 'lire')) {
throw new RestException(403);
}
$sql = "SELECT id, code, label, accountancy_code, active, module, position";
$sql .= " FROM ".MAIN_DB_PREFIX."c_type_fees as t";
$sql .= " WHERE t.active = ".((int) $active);
@@ -1016,6 +1023,165 @@ class Setup extends DolibarrApi
return $list;
}
/**
* Get the list of holiday types.
*
* @param string $sortfield Sort field
* @param string $sortorder Sort order
* @param int $limit Number of items per page
* @param int $page Page number (starting from zero)
* @param string $fk_country To filter on country
* @param int $active Holiday is active or not {@min 0} {@max 1}
* @param string $lang Code of the language the label of the holiday must be translated to
* @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
* @return array List of holiday types
* @phan-return array<Object|false>
* @phpstan-return array<Object|false>
*
* @url GET dictionary/holiday_types
*
* @throws RestException 400 Bad value for sqlfilters
* @throws RestException 503 Error when retrieving list of holiday types
*/
public function getListOfHolidayTypes($sortfield = "sortorder", $sortorder = 'ASC', $limit = 100, $page = 0, $fk_country = '', $active = 1, $lang = '', $sqlfilters = '')
{
global $langs;
$langs->loadLangs(array('holiday'));
if (!DolibarrApiAccess::$user->hasRight('holiday', 'lire')) {
throw new RestException(403);
}
$list = array();
$sql = "SELECT rowid, code, label, affect, delay, newbymonth, fk_country";
$sql .= " FROM ".MAIN_DB_PREFIX."c_holiday_types as t";
$sql .= " WHERE t.entity IN (".getEntity('c_holiday_types').")";
$sql .= " AND t.active = ".((int) $active);
if ($fk_country) {
$sql .= " AND (t.fk_country = ".((int) $fk_country);
$sql .= " OR t.fk_country is null)";
}
// Add sql filters
if ($sqlfilters) {
$errormessage = '';
$sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
if ($errormessage) {
throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
}
}
$sql .= $this->db->order($sortfield, $sortorder);
if ($limit) {
if ($page < 0) {
$page = 0;
}
$offset = $limit * $page;
$sql .= $this->db->plimit($limit, $offset);
}
$result = $this->db->query($sql);
if ($result) {
$num = $this->db->num_rows($result);
$min = min($num, ($limit <= 0 ? $num : $limit));
for ($i = 0; $i < $min; $i++) {
$holiday = $this->db->fetch_object($result);
$tmplabel = $langs->trans($holiday->code);
if ($tmplabel != $holiday->code) {
$holiday->label = $tmplabel;
}
//$this->translateLabel($holiday, $lang, 'Holiday', array('dict'));
$list[] = $holiday;
}
} else {
throw new RestException(503, 'Error when retrieving list of holiday : '.$this->db->lasterror());
}
return $list;
}
/**
* Get the list of public holiday.
*
* @param string $sortfield Sort field
* @param string $sortorder Sort order
* @param int $limit Number of items per page
* @param int $page Page number (starting from zero)
* @param string $fk_country To filter on country
* @param int $active Holiday is active or not {@min 0} {@max 1}
* @param string $lang Code of the language the label of the holiday must be translated to
* @param string $sqlfilters Other criteria to filter answers separated by a comma. Syntax example "(t.code:like:'A%') and (t.active:>=:0)"
* @return array List of public holiday
* @phan-return array<Object|false>
* @phpstan-return array<Object|false>
*
* @url GET dictionary/public_holiday
*
* @throws RestException 400 Bad value for sqlfilters
* @throws RestException 503 Error when retrieving list of holiday types
*/
public function getListOfPublicHolidays($sortfield = "code", $sortorder = 'ASC', $limit = 100, $page = 0, $fk_country = '', $active = 1, $lang = '', $sqlfilters = '')
{
global $langs;
$langs->loadLangs(array('hrm'));
if (!DolibarrApiAccess::$user->hasRight('holiday', 'lire')) {
throw new RestException(403);
}
$list = array();
$sql = "SELECT id, code, dayrule, day, month, year, fk_country, code as label";
$sql .= " FROM ".MAIN_DB_PREFIX."c_hrm_public_holiday as t";
$sql .= " WHERE t.entity IN (".getEntity('c_hrm_public_holiday').")";
$sql .= " AND t.active = ".((int) $active);
if ($fk_country) {
$sql .= " AND (t.fk_country = ".((int) $fk_country);
$sql .= " OR t.fk_country is null)";
}
// Add sql filters
if ($sqlfilters) {
$errormessage = '';
$sql .= forgeSQLFromUniversalSearchCriteria($sqlfilters, $errormessage);
if ($errormessage) {
throw new RestException(400, 'Error when validating parameter sqlfilters -> '.$errormessage);
}
}
$sql .= $this->db->order($sortfield, $sortorder);
if ($limit) {
if ($page < 0) {
$page = 0;
}
$offset = $limit * $page;
$sql .= $this->db->plimit($limit, $offset);
}
$result = $this->db->query($sql);
if ($result) {
$num = $this->db->num_rows($result);
$min = min($num, ($limit <= 0 ? $num : $limit));
for ($i = 0; $i < $min; $i++) {
$holiday = $this->db->fetch_object($result);
$tmplabel = $langs->trans($holiday->code);
if ($tmplabel != $holiday->code) {
$holiday->label = $tmplabel;
}
//$this->translateLabel($holiday, $lang, 'Holiday', array('dict'));
$list[] = $holiday;
}
} else {
throw new RestException(503, 'Error when retrieving list of public holiday : '.$this->db->lasterror());
}
return $list;
}
/**
* Get the list of contacts types.

View File

@@ -45,7 +45,7 @@ $langs->loadLangs(array("assets", "companies"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');

View File

@@ -46,7 +46,7 @@ $langs->loadLangs(array("assets", "other"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
if (GETPOST('actioncode', 'array')) {

View File

@@ -47,7 +47,7 @@ $id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$confirm = GETPOST('confirm', 'alpha');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'assetcard'; // To manage different context of search
$backtopage = GETPOST('backtopage', 'alpha');
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');
@@ -408,7 +408,7 @@ if ($object->id > 0 && (empty($action) || ($action != 'edit' && $action != 'crea
if (empty($reshook)) {
// Send
if (empty($user->socid)) {
print dolGetButtonAction('', $langs->trans('SendMail'), 'default', $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=presend&mode=init&token=' . newToken() . '#formmailbeforetitle');
print dolGetButtonAction('', $langs->trans('SendMail'), 'email', $_SERVER["PHP_SELF"] . '?id=' . $object->id . '&action=presend&mode=init&token=' . newToken() . '#formmailbeforetitle');
}
// Back to draft

View File

@@ -45,7 +45,7 @@ $langs->loadLangs(array("assets", "companies"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
// Initialize a technical objects

View File

@@ -44,7 +44,7 @@ $langs->loadLangs(array("assets", "companies"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha'); // if not set, $backtopage will be used

View File

@@ -44,7 +44,7 @@ $langs->loadLangs(array("assets", "companies"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
// Initialize a technical objects

View File

@@ -44,7 +44,7 @@ $langs->loadLangs(array("assets", "companies"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
// Initialize a technical objects

View File

@@ -46,7 +46,7 @@ $langs->loadLangs(array("assets", "other"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
if (GETPOST('actioncode', 'array')) {

View File

@@ -48,7 +48,7 @@ $id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$confirm = GETPOST('confirm', 'alpha');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : 'assetmodelcard'; // To manage different context of search
$backtopage = GETPOST('backtopage', 'alpha');
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');

View File

@@ -44,7 +44,7 @@ $langs->loadLangs(array("assets", "companies"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel');
$backtopage = GETPOST('backtopage', 'alpha');
$backtopageforcancel = GETPOST('backtopageforcancel', 'alpha');

View File

@@ -43,7 +43,7 @@ $langs->loadLangs(array("assets", "companies"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
// Initialize a technical objects

View File

@@ -43,7 +43,7 @@ $langs->loadLangs(array("assets", "companies"));
$id = GETPOSTINT('id');
$ref = GETPOST('ref', 'alpha');
$action = GETPOST('action', 'aZ09');
$cancel = GETPOST('cancel', 'aZ09');
$cancel = GETPOST('cancel', 'alpha');
$backtopage = GETPOST('backtopage', 'alpha');
// Initialize a technical objects

View File

@@ -41,22 +41,21 @@ 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';
require_once DOL_DOCUMENT_ROOT.'/core/lib/date.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
// Load translation files required by the page
$langs->loadLangs(array('admin', 'banks', 'bills', 'blockedlog', 'other'));
// Access Control
if ((!$user->admin && !$user->hasRight('blockedlog', 'read')) || empty($conf->blockedlog->enabled)) {
accessforbidden();
}
// Get Parameters
$action = GETPOST('action', 'aZ09');
$confirm = GETPOST('confirm', 'aZ09');
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : getDolDefaultContextPage(__FILE__); // To manage different context of search
$backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page
$optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print')
$hmacexportkey = GETPOST('hmacexportkey', 'password');
$search_showonlyerrors = GETPOSTINT('search_showonlyerrors');
if ($search_showonlyerrors < 0) {
$search_showonlyerrors = 0;
@@ -113,6 +112,14 @@ if (empty($sortorder)) {
$block_static = new BlockedLog($db);
$block_static->loadTrackedEvents();
// Access Control
if ((!$user->admin && !$user->hasRight('blockedlog', 'read')) || !isModEnabled('blockedlog')) {
accessforbidden();
}
// We force also permission to write because it does not exists and we need it to upload a file
$user->rights->blockedlog->create = 1;
$result = restrictedArea($user, 'blockedlog', 0, '');
// Execution Time
@@ -126,6 +133,14 @@ if ($max_time && $max_time < $max_execution_time_for_importexport) {
$MAXLINES = getDolGlobalInt('BLOCKEDLOG_MAX_LINES', 10000);
$MAXFORSHOWNLINKS = getDolGlobalInt('BLOCKEDLOG_MAX_FOR_SHOWN_LINKS', 100);
$permission = $user->hasRight('blockedlog', 'read');
$permissiontoadd = $user->hasRight('blockedlog', 'read'); // Permission is to upload new files to scan them
$permtoedit = $permissiontoadd;
$upload_dir = getMultidirOutput($block_static, 'blockedlog').'/archives';
dol_mkdir($upload_dir);
/*
* Actions
@@ -152,7 +167,9 @@ if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x'
$search_array_options = array();
}
if (GETPOST('downloadcsv', 'alpha')) {
include DOL_DOCUMENT_ROOT.'/core/actions_linkedfiles.inc.php';
if (GETPOST('action') == 'upload' && $user->hasRight('blockedlog', 'read')) { // read is read/upload for blockedlog
$error = 0;
$previoushash = '';
@@ -161,7 +178,13 @@ if (GETPOST('downloadcsv', 'alpha')) {
if (! (GETPOSTINT('yeartoexport') > 0)) {
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Year")), null, "errors");
$error++;
} else {
}
if (empty($hmacexportkey)) {
setEventMessages($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Password")), null, "errors");
$error++;
}
if (!$error) {
// Get the ID of the first line qualified
$sql = "SELECT rowid,date_creation,tms,user_fullname,action,amounts,element,fk_object,date_object,ref_object,signature,fk_user,object_data";
$sql .= " FROM ".MAIN_DB_PREFIX."blockedlog";
@@ -191,7 +214,7 @@ if (GETPOST('downloadcsv', 'alpha')) {
}
}
if (! $error) {
if (!$error) {
// We record the export as a new line into the unalterable logs
require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
$b = new BlockedLog($db);
@@ -237,28 +260,32 @@ if (GETPOST('downloadcsv', 'alpha')) {
$resql = $db->query($sql);
if ($resql) {
$nameofdownoadedfile = "unalterable-log-archive-".$dolibarr_main_db_name."-".(GETPOSTINT('yeartoexport') > 0 ? GETPOSTINT('yeartoexport').(GETPOSTINT('monthtoexport') > 0 ? sprintf("%02d", GETPOSTINT('monthtoexport')) : '').'-' : '').dol_print_date(dol_now(), 'dayhourlog', 'gmt').'UTC-DONOTMODIFY';
$yearmonthtoexport = GETPOSTINT('yeartoexport').(GETPOSTINT('monthtoexport') > 0 ? sprintf("%02d", GETPOSTINT('monthtoexport')) : '');
$yearmonthdateofexport = dol_print_date(dol_now(), 'dayhourlog', 'gmt');
$tmpfile = $conf->admin->dir_temp.'/unalterable-log-archive-tmp-'.$user->id.'.csv';
$nameofdownoadedfile = "unalterable-log-archive-".$dolibarr_main_db_name."-".$yearmonthtoexport.'-'.$yearmonthdateofexport.'UTC-DONOTMODIFY.csv';
//$tmpfile = $conf->admin->dir_temp.'/unalterable-log-archive-tmp-'.$user->id.'.csv';
$tmpfile = getMultidirOutput($block_static, 'blockedlog').'/archives/'.$nameofdownoadedfile;
$fh = fopen($tmpfile, 'w');
// Print line with title
fwrite($fh, $langs->transnoentities('Id')
fwrite($fh, "BEGIN - date=".$yearmonthdateofexport
.';'.$langs->transnoentities('Id')
.';'.$langs->transnoentities('DateCreation')
.';'.$langs->transnoentities('Action')
.';'.$langs->transnoentities('Amounts')
.';'.$langs->transnoentities('Ref')
.';'.$langs->transnoentities('Date')
.';'.$langs->transnoentities('User')
.';'.$langs->transnoentities('Action')
.';'.$langs->transnoentities('Element')
.';'.$langs->transnoentities('Amounts')
.';'.$langs->transnoentities('ObjectId')
.';'.$langs->transnoentities('Date')
.';'.$langs->transnoentities('Ref')
.';'.$langs->transnoentities('LinkTo')
.';'.$langs->transnoentities('LinkType')
.';'.$langs->transnoentities('FullData')
.';'.$langs->transnoentities('Version')
.';'.$langs->transnoentities('Fingerprint')
.';'.$langs->transnoentities('Status')
.';'.$langs->transnoentities('Note')
.';'.$langs->transnoentities('Version')
.';'.$langs->transnoentities('FullData')
.';'.$langs->transnoentities('DebugInfo')
.';'.$langs->transnoentities('FingerprintExport')
."\n");
$loweridinerror = 0;
@@ -323,21 +350,24 @@ if (GETPOST('downloadcsv', 'alpha')) {
$statusofrecordnote = $langs->trans("PreviousFingerprint").': '.$previoushash.($statusofrecordnote ? ' - '.$statusofrecordnote : '');
}
fwrite($fh, $block_static->id
$signatureexport = 'TODO';
fwrite($fh,
';'.$block_static->id
.';'.$block_static->date_creation
.';"'.str_replace('"', '""', $block_static->user_fullname).'";'
.$block_static->action
.';'.$block_static->element
.';'.$block_static->action
.';'.$block_static->amounts // Can be 1.20000000 with 8 digits. TODO Clean to have 8 digits in V1
.';'.$block_static->fk_object
.';'.$block_static->date_object
.';"'.str_replace('"', '""', $block_static->ref_object).'";"'
.$block_static->signature.'";'
.$statusofrecord
.';'.$statusofrecordnote
.';'.$block_static->object_version
.';"'.str_replace('"', '""', $obj->object_data).'"' // We must the string to decode into object with dolDecodeBlockedData
.';"'.str_replace('"', '""', $block_static->debuginfo).'"'
.';"'.str_replace('"', '""', $block_static->ref_object).'";'
.$block_static->date_object
.';"'.str_replace('"', '""', $block_static->user_fullname).'";"'
.str_replace('"', '""', $block_static->linktoref).'";"'
.str_replace('"', '""', $block_static->linktype).'";"'
.str_replace('"', '""', $obj->object_data).'"' // We must the string to decode into object with dolDecodeBlockedData
.';"'.str_replace('"', '""', $block_static->object_version).'";"'
.str_replace('"', '""', $block_static->signature).'";"'
.str_replace('"', '""', $statusofrecord).'";"'
.str_replace('"', '""', $signatureexport).'"'
//.';'.$statusofrecordnote
."\n");
// Set new previous hash for next fetch
@@ -349,18 +379,14 @@ if (GETPOST('downloadcsv', 'alpha')) {
fclose($fh);
// Calculate the md5 of the file (the last line has a return line)
$md5value = md5_file($tmpfile);
$algo = 'sha256';
$secretkey = 'TODOASKBEFOREEXPORT';
$hmacsha256 = hash_hmac_file($algo, $tmpfile, $secretkey);
// Now add a signature to check integrity at end of file
file_put_contents($tmpfile, 'END - md5='.$md5value, FILE_APPEND);
file_put_contents($tmpfile, 'END - hmac_sha256='.$hmacsha256, FILE_APPEND);
header('Content-Type: application/octet-stream');
header("Content-Transfer-Encoding: Binary");
header("Content-disposition: attachment; filename=\"".$nameofdownoadedfile.".csv\"");
readfile($tmpfile);
exit;
setEventMessages($langs->trans("FileGenerated"), null);
} else {
setEventMessages($db->lasterror, null, 'errors');
}
@@ -476,14 +502,37 @@ if (GETPOST('withtab', 'alpha')) {
// Add $param from extra fields
//include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php';
print '<form method="POST" id="searchFormList" action="'.$_SERVER["PHP_SELF"].'?output=file">';
if ($action == 'deletefile') {
$langs->load("companies"); // Need for string DeleteFile+ConfirmDeleteFiles
print $form->formconfirm(
$_SERVER["PHP_SELF"].'?urlfile='.urlencode(GETPOST("urlfile")).'&linkid='.GETPOSTINT('linkid').(empty($param) ? '' : $param),
$langs->trans('DeleteFile'),
$langs->trans('ConfirmDeleteFile'),
'confirm_deletefile',
'',
'',
1
);
}
print '<form method="POST" id="exportArchives" action="'.$_SERVER["PHP_SELF"].'?output=file">';
print '<input type="hidden" name="token" value="'.newToken().'">';
print '<input type="hidden" name="action" value="upload">';
print '<div class="right">';
print $langs->trans("RestrictYearToExport").': ';
// Month
print $formother->select_month((string) GETPOSTINT('monthtoexport'), 'monthtoexport', 1, 0, 'minwidth50 maxwidth75imp valignmiddle', true);
print $formother->select_month((string) GETPOSTINT('monthtoexport'), 'monthtoexport', $langs->trans("Month"), 0, 'minwidth50 maxwidth75imp valignmiddle', true);
print '<input type="text" name="yeartoexport" class="valignmiddle maxwidth75imp" value="'.GETPOST('yeartoexport').'" placeholder="'.$langs->trans("Year").'">';
print ' ';
print '<input type="text" name="hmacexportkey" class="valignmiddle minwidth150imp maxwidth300imp" required value="'.GETPOST('hmacexportkey').'" placeholder="'.$langs->trans("Password").'">';
print ' ';
print '<input type="hidden" name="withtab" value="'.GETPOST('withtab', 'alpha').'">';
print '<input type="submit" name="downloadcsv" class="button" value="'.$langs->trans('DownloadLogCSV').'">';
/*if (getDolGlobalString('BLOCKEDLOG_USE_REMOTE_AUTHORITY')) {
@@ -493,6 +542,8 @@ print ' </div><br>';
print '</form>';
/*
print '<form method="POST" id="searchFormList" action="'.dolBuildUrl($_SERVER["PHP_SELF"]).'">';
if ($optioncss != '') {
@@ -508,13 +559,85 @@ print '<input type="hidden" name="contextpage" value="'.$contextpage.'">';
print '<input type="hidden" name="withtab" value="'.GETPOST('withtab', 'alpha').'">';
print '<div class="div-table-responsive">'; // You can use div-table-responsive-no-min if you don't need reserved height for your table
*/
// TODO List of archive files into blockedlog/archives
$filearray = dol_dir_list($upload_dir, 'files', 0, '', null, 'name', SORT_ASC, 1);
$modulepart = 'blockedlog';
$relativepathwithnofile = 'archives/';
$disablemove = 1;
/*
$param = '&id='.$object->id.'&entity='.(empty($object->entity) ? getDolEntity() : $object->entity);
include DOL_DOCUMENT_ROOT.'/core/tpl/document_actions_post_headers.tpl.php';
*/
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
$formfile = new FormFile($db);
$savingdocmask = '';
$object = $block_static;
// Get the form to add files (upload and links)
$tmparray = $formfile->form_attach_new_file(
$_SERVER["PHP_SELF"],
'',
0,
0,
$permission,
$conf->browser->layout == 'phone' ? 40 : 60,
$object,
'',
1,
$savingdocmask,
1,
'formuserfile',
'',
'',
0,
0,
0,
2
);
$formToUploadAFile = '';
if (is_array($tmparray) && !empty($tmparray)) {
$formToUploadAFile = $tmparray['formToUploadAFile'];
}
// List of document
// TODO Replace with specific code to list files with mass action, ...
$formfile->list_of_documents(
$filearray,
null,
$modulepart,
$param,
0,
$relativepathwithnofile, // relative path with no file. For example "0/1"
$permission,
0,
'',
0,
$langs->transnoentitiesnoconv('Archives'),
'',
0,
$permtoedit,
$upload_dir,
$sortfield,
$sortorder,
$disablemove,
0,
-1,
'',
array('afteruploadtitle' => $formToUploadAFile, 'showhideaddbutton' => 1)
);
/*
print '</div>';
print '</form>';
*/
// Javascript to manage the showinfo popup
print '<script type="text/javascript">

View File

@@ -47,11 +47,6 @@ require_once DOL_DOCUMENT_ROOT.'/core/class/html.formother.class.php';
// Load translation files required by the page
$langs->loadLangs(array('admin', 'banks', 'bills', 'blockedlog', 'other'));
// Access Control
if ((!$user->admin && !$user->hasRight('blockedlog', 'read')) || empty($conf->blockedlog->enabled)) {
accessforbidden();
}
// Get Parameters
$action = GETPOST('action', 'aZ09');
$contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : getDolDefaultContextPage(__FILE__); // To manage different context of search
@@ -114,6 +109,11 @@ if (empty($sortorder)) {
$block_static = new BlockedLog($db);
$block_static->loadTrackedEvents();
// Access Control
if ((!$user->admin && !$user->hasRight('blockedlog', 'read')) || !isModEnabled('blockedlog')) {
accessforbidden();
}
$result = restrictedArea($user, 'blockedlog', 0, '');
// Execution Time
@@ -741,7 +741,9 @@ if (is_array($blocks)) {
// Note
if (!$checkresult[$block->id] || ($loweridinerror && $block->id >= $loweridinerror)) { // If error
if ($checkresult[$block->id]) {
print $form->textwithpicto('', $langs->trans('OkCheckFingerprintValidityButChainIsKo'));
if (getDolGlobalString("BLOCKEDLOG_DEBUG")) {
print $form->textwithpicto('', $langs->trans('OkCheckFingerprintValidityButChainIsKo'));
}
}
}
@@ -753,7 +755,7 @@ if (is_array($blocks)) {
print '</td>';
// Link to debug information object
if (getDolGlobalString('MAIN_FEATURES_LEVEL') > 0) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
if (getDolGlobalString("BLOCKEDLOG_DEBUG")) { // If in experimental or develop mode, we add some debug information. It may help developers to find origin of bugs.
print '<td class="tdoverflowmax150"'.(preg_match('/<a/', $object_link) ? '' : 'title="'.dol_escape_htmltag(dol_string_nohtmltag($object_link.($object_link_title ? ' - '.$object_link_title : ''))).'"').'>';
print '<!-- object_link -->'; // $object_link can be a '<a href' link or a text
print $object_link;

View File

@@ -78,7 +78,7 @@ $htmltooltip .= $langs->trans("VersionLastInstall").': '.getDolGlobalString('MAI
$htmltooltip .= $langs->trans("VersionLastUpgrade").': '.getDolGlobalString('MAIN_VERSION_LAST_UPGRADE').'<br>'."\n";
print '<tr class="oddeven nohover"><td width="300">'.$langs->trans("VersionProgram").'</td><td>';
print '<span class="valignmiddle">'.DOL_VERSION.'</span>';
print '<span class="badge-text badge-secondary valignmiddle">'.DOL_VERSION.'</span>';
// If current version differs from last upgrade
if (!getDolGlobalString('MAIN_VERSION_LAST_UPGRADE')) {
// Compare version with last install database version (upgrades never occurred)

View File

@@ -48,7 +48,11 @@ require '../../main.inc.php';
$id = GETPOSTINT('id');
$element = GETPOST('element', 'alpha');
$action = GETPOST('action', 'aZ09');
$action = GETPOST('action', 'aZ09'); // Can be DOC_PREVIEW or DOC_DOWNLOAD
if (! in_array($action, array('DOC_PREVIEW', 'DOC_DOWNLOAD'))) {
accessforbidden('Bad value for action. Must be DOC_PREVIEW or DOC_DOWNLOAD');
}
if ($element === 'facture') {
restrictedArea($user, 'facture', $id, '', '', 'fk_soc', 'rowid', 0);
@@ -63,17 +67,20 @@ if ($element === 'facture') {
top_httphead();
if (empty($action)) {
print 'No action logged. Empty action code.';
exit;
}
if ($element === 'facture') { // Test on permission done in top of page
require_once DOL_DOCUMENT_ROOT.'/blockedlog/class/blockedlog.class.php';
require_once DOL_DOCUMENT_ROOT.'/compta/facture/class/facture.class.php';
$facture = new Facture($db);
if ($facture->fetch($id) > 0) {
// Increase counter by 1
$sql = "UPDATE ".MAIN_DB_PREFIX."facture SET pos_print_counter = pos_print_counter + 1";
$sql .= " WHERE rowid = ".((int) $facture->id);
$db->query($sql);
//$facture->pos_print_counter += 1;
//$facture->update($user, 1); // We disable trigger here because we already call the trigger $action = DOC_PREVIEW or DOC_DOWNLOAD just after
$facture->call_trigger($action, $user);
}

Some files were not shown because too many files have changed in this diff Show More