mirror of
https://github.com/Dolibarr/dolibarr.git
synced 2026-02-08 00:52:01 +01:00
Merge branch 'develop' into develop_new_rework_webportal_lists
This commit is contained in:
2
.github/workflows/ci-cache-clean-pr.yml
vendored
2
.github/workflows/ci-cache-clean-pr.yml
vendored
@@ -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
|
||||
|
||||
29
.github/workflows/ci-checkfilesetlock.yml
vendored
Normal file
29
.github/workflows/ci-checkfilesetlock.yml
vendored
Normal 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
|
||||
2
.github/workflows/ci-phpstan_baseline.yml
vendored
2
.github/workflows/ci-phpstan_baseline.yml
vendored
@@ -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:
|
||||
|
||||
2
.github/workflows/phan.yml
vendored
2
.github/workflows/phan.yml
vendored
@@ -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:
|
||||
|
||||
2
.github/workflows/phpstan.yml
vendored
2
.github/workflows/phpstan.yml
vendored
@@ -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
|
||||
|
||||
6
.github/workflows/pre-commit.yml
vendored
6
.github/workflows/pre-commit.yml
vendored
@@ -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
|
||||
|
||||
2
.github/workflows/windows-ci.yml
vendored
2
.github/workflows/windows-ci.yml
vendored
@@ -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:
|
||||
|
||||
@@ -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
204
ChangeLog
@@ -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 *****
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||

|
||||

|
||||
[](https://php.net/)
|
||||
[](https://php.net/)
|
||||
[](https://github.com/Dolibarr/dolibarr)
|
||||
[](https://bestpractices.coreinfrastructure.org/projects/5521)
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
©_source_dest_as_domain_user($d, $oldcfile, $cfile);
|
||||
©_source_dest_as_domain_user($d, $olddocdir, $docdir);
|
||||
©_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;
|
||||
|
||||
@@ -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\.$#'
|
||||
|
||||
@@ -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
3
dev/resources/dbmodel/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/dolibarr_schema.mwb.bak
|
||||
/dolibarr_schema.mwb.sav.mwb
|
||||
/dolibarr_schema.mwb.sav.mwb.bak
|
||||
Binary file not shown.
33
dev/tools/api/macos/README
Normal file
33
dev/tools/api/macos/README
Normal 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/
|
||||
|
||||
|
||||
|
||||
@@@
|
||||
(@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@%
|
||||
@@@@@@, @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@,
|
||||
@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
(@@@@@@( @@@@@@@@ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@* @@@@@@@@@@@. /@@@@@@@@@@@@@@@@@(
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@* @@@@@@@@@@@. @@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ /@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@# @@@@@@@@@@@ @@@@@@@@@@@@&
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ @@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ &@@@@@@@@@@@@@@@@@@@@@@
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @@@@@@@@@@@ &@@@@@@@@@@@@@@@@@@@@/
|
||||
@@@@@@@@@@@@@@@@@@@@@@@@@@& .@@@@@@@@@@@ &@@@@@@@@@@@@@@@@@@/
|
||||
(@@@@@@@@@@@@@@@@@@@@@@@ .@@@@@@@@@@@ &@@@@@@@@@@@@@@@(
|
||||
@@@@@ @@@@@ .@@@@@@@@@@@ @@@@@@@@@@@*
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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') : ' ';
|
||||
print $object->date_export ? img_picto($langs->trans("TransactionExportDesc"), 'fa-file-export', 'class="pictofixedwidth"').dol_print_date($object->date_export, 'dayhour') : ' ';
|
||||
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') : ' ';
|
||||
print $object->date_validation ? img_picto($langs->trans("TransactionBlockedLockedDesc"), 'fa-lock', 'class="pictofixedwidth"').dol_print_date($object->date_validation, 'dayhour') : ' ';
|
||||
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 -->';
|
||||
|
||||
@@ -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']++;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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', '');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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>';
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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>';
|
||||
}
|
||||
|
||||
|
||||
@@ -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>';
|
||||
}
|
||||
|
||||
|
||||
@@ -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>';
|
||||
|
||||
@@ -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>';
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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>';
|
||||
|
||||
|
||||
@@ -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"');
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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].' -->';
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 : '').'"> '.$currency->code.' ';
|
||||
print '<input type="submit" name="updatecurrency" class="button button-edit smallpaddingimp" value="'.$langs->trans("Modify").'"> ';
|
||||
print '<input type="submit" name="deletecurrency" class="button smallpaddingimp" value="'.$langs->trans("Delete").'">';
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 ' <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 ' <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 ' <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").' ('.$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").' ('.$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").' ('.$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 ' <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 ' <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>';
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {},
|
||||
};
|
||||
@@ -0,0 +1,443 @@
|
||||
// CustomEvent doesn’t 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;
|
||||
});
|
||||
@@ -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());
|
||||
});
|
||||
@@ -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: "×" // 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);
|
||||
});
|
||||
@@ -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 Dolibarr’s 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();
|
||||
?>
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
?>
|
||||
@@ -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');
|
||||
|
||||
@@ -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";
|
||||
|
||||
|
||||
319
htdocs/api/admin/token_list.php
Normal file
319
htdocs/api/admin/token_list.php
Normal 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();
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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')) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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')) {
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user