diff --git a/COPYRIGHT b/COPYRIGHT index f7edb08cae8..c38873a2f07 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -13,8 +13,9 @@ Component Version License GPL Compatible ------------------------------------------------------------------------------------- PHP libraries: AdoDb-Date 0.33 Modified BSD License Yes Date convertion (not into rpm package) -ChromePHP 4.3.3 Apache Software License 2.0 Yes Return server log to chrome browser console +ChromePHP 4.1.0 Apache Software License 2.0 Yes Return server log to chrome browser console CKEditor 4.3.3 LGPL-2.1+ Yes Editor WYSIWYG +EvalMath 1.0 BSD Yes Safe math expressions evaluation FPDI 1.5.2 Apache Software License 2.0 Yes PDF templates management GeoIP 1.4 LGPL-2.1+ Yes Sample code to make geoip convert (not into deb package) Mobiledetect 2.8.3 MIT License Yes Detect mobile devices browsers @@ -24,9 +25,10 @@ odtPHP 1.0.1 GPL-2+ b Yes PHPExcel 1.8.0 LGPL-2.1+ Yes Read/Write XLS files, read ODS files php-iban 1.4.7 LGPL-3+ Yes Parse and validate IBAN (and IIBAN) bank account information in PHP PHPPrintIPP 1.3 GPL-2+ Yes Library to send print IPP requests +Raven-php 0.12.1 MIT License Yes Used for server-side error logging with Sentry logger Restler 3.0 LGPL-3+ Yes Library to develop REST Web services TCPDF 6.2.6 LGPL-3+ Yes PDF generation -EvalMath 1.0 BSD Yes Safe math expressions evaluation +TCPDI 1.0.0 LGPL-3+ / Apache 2.0 Yes FPDI replacement JS libraries: jQuery 1.11.3 MIT License Yes JS library @@ -49,7 +51,8 @@ jQuery TableDnD 0.6 GPL and MIT License Yes jQuery Timepicker 1.1.0 GPL and MIT License Yes JS library Timepicker addon for Datepicker jQuery Tiptip 1.3 GPL and MIT License Yes JS library for tooltips jsGantt 1.2 BSD License Yes JS library (to build Gantt reports) -JsTimezoneDetect 1.0.4 MIT Licence Yes JS library to detect user timezone +JsTimezoneDetect 1.0.4 MIT License Yes JS library to detect user timezone +Raven.js 1.1.19 MIT License Yes Used for client-side error logging with Sentry logger For licenses compatibility informations: http://www.fsf.org/licensing/licenses/index_html diff --git a/ChangeLog b/ChangeLog index 7113b325833..ff8a5763008 100644 --- a/ChangeLog +++ b/ChangeLog @@ -240,6 +240,11 @@ FIX [ bug #3288 ] Tasks box is not properly drawn FIX [ bug #3211 ] Outstading bill amount of a client showed wrong amounts FIX [ bug #3321 ] Users with certain permissions were shown a "forbidden access" page even if they had the rights FIX [ bug #3426 ] Unable to create an invoice from a contract with extrafields +FIX [ bug #3431 ] Invoice bank account is not respected +FIX [ bug #3432 ] Spaces should be removed from IBAN when formatting it +FIX [ bug #3358 ] Tasks box does not work with PostgreSQL +FIX [ bug #3383 ] Company name is overlapped with company direction in PDF models +FIX [ bug #3460 ] Bank account is not saved when creating a customer invoice and facing an error message NEW: Created new ContratLigne::insert function diff --git a/build/debian/README.howto b/build/debian/README.howto index e2efcb6cf48..92397130a3a 100644 --- a/build/debian/README.howto +++ b/build/debian/README.howto @@ -331,7 +331,7 @@ http://packages.qa.debian.org Use this to move from unstable to testing. -reportbug -B debian +reportbug -B debian --smtphost=smtp.gmail.com:587 --smtpuser=xxxx --smtppasswd=yyyy --tls Choose package "release.debian.org" Then "unblock" Then name of package "dolibarr" @@ -345,15 +345,25 @@ After discussion with ..., it appears that security holes are enough to request Use this to request an update of a stable package -reportbug -B debian +reportbug -B debian --smtphost=smtp.gmail.com:587 --smtpuser=xxxx --smtppasswd=yyyy --tls Choose package "release.debian.org" Then "unblock" Then name of package "dolibarr" Fill message, for example: -"Please unblock package dolibarr -A security error CVE-2015-3935 was reported and is fixed into package 3.5.7. -Note that package 3.5.7 contains not only fixed for bugs reported to debian. It includes other fixes, but they are all related to stability or security, -so it is a better solution to validate this maintenance release than applying a patch of the only CVE-2015-3935. -After discussion with ..., it appears that security holes are enough to request this unblock request." +" +A security error CVE-2015-3935 was reported for Dolibarr ERP CRM package. This bug is fixed into official package 3.5.7 of Dolibarr. +Package 3.5.7 is a maintenance release compared to 3.5.5 and contains only fixes. But not only bugs reported to debian, it includes also other fixes (but they are all related to stability or security). +I think it is a better solution to validate this maintenance release based on the new upstream version of Dolibarr than applying a patch of the only CVE-2015-3935. +Pro are: +- It fixes all debian reported bugs (including security one) +- It fixes also stability bugs +- Patches were already tested because deployed and used by several thousands of users. +- It is easier for package maintener to include this official set of fixes than applying one patch after one patch for each debian report or backported each patch into a dedicated version. +- Debian maintenance version matches with official project maintenance version (better when all fixes are not related to the way the software is packaged) +Cons are: +- The patch include more than the only one security reported fxes +So I just need to know if it's ok to push such a version 3.5.7 (fixes for 3.5.* branch) instead of only one fix for only the few (the only) reported debian bugs, +since it provides more stability and is or me a more secured process. +" diff --git a/composer.json b/composer.json index ed75d3ca61d..d5e2d7f411e 100644 --- a/composer.json +++ b/composer.json @@ -12,21 +12,31 @@ }, "require": { "php": ">=5.3.0", - "ext-gd": "*", "ext-curl": "*", - "restler/framework": "3.0.*" + "ccampbell/chromephp": "^4.1", + "ckeditor/ckeditor": "4.3.3", + "mobiledetect/mobiledetectlib": "2.8.3", + "phpoffice/phpexcel": "1.8.0", + "restler/framework": "^3.0", + "tecnick.com/tcpdf": "6.2.6", + "raven/raven": "^0.12.0" }, "suggest": { - "ext-mysqli": "*", - "ext-pgsql": "*", - "ext-mssql": "*", - "ext-pdo_sqlite": "*", - "ext-imagick": "*", - "ext-mcrypt": "*", - "ext-openssl": "*", - "ext-mbstring": "*", - "ext-soap": "*", - "ext-zip": "*", - "ext-xml": "*" + "ext-mysqlnd": "To use with MySQL or MariaDB", + "ext-mysqli": "To use with MySQL or MariaDB", + "ext-pgsql": "To use with PostgreSQL", + "ext-mssql": "To use with MSSQL (experimental)", + "ext-pdo_sqlite": "To use with SQLite (experimental)", + "ext-gd": "Image manipulation (Required but maybe built-in PHP)", + "ext-imagick": "Image manipulation (TCPDF)", + "ext-mcrypt": "(Required but maybe built-in PHP)", + "ext-openssl": "Secure connections (Emails, SOAP…)", + "ext-mbstring": "Handle non UTF-8 databases", + "ext-soap": "Native SOAP", + "ext-zip": "ODT and Excel support", + "ext-xml": "Excel support" + }, + "config": { + "vendor-dir": "htdocs/includes" } } diff --git a/composer.lock b/composer.lock new file mode 100644 index 00000000000..2856ef0b749 --- /dev/null +++ b/composer.lock @@ -0,0 +1,405 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "32e1fa78cc95c32b154a38e07706874c", + "packages": [ + { + "name": "ccampbell/chromephp", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/ccampbell/chromephp.git", + "reference": "c3c297615d48ae5b2a86a82311152d1ed095fcef" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ccampbell/chromephp/zipball/c3c297615d48ae5b2a86a82311152d1ed095fcef", + "reference": "c3c297615d48ae5b2a86a82311152d1ed095fcef", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "ChromePhp": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "Apache-2.0" + ], + "authors": [ + { + "name": "Craig Campbell", + "email": "iamcraigcampbell@gmail.com", + "homepage": "http://craig.is", + "role": "Developer" + } + ], + "description": "Log variables to the Chrome console (via Chrome Logger Google Chrome extension).", + "homepage": "http://github.com/ccampbell/chromephp", + "keywords": [ + "log", + "logging" + ], + "time": "2013-06-26 03:44:33" + }, + { + "name": "ckeditor/ckeditor", + "version": "4.3.3", + "source": { + "type": "git", + "url": "https://github.com/ckeditor/ckeditor-releases.git", + "reference": "0068dd540ce8bf1815abb7b5455c55354bc56334" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ckeditor/ckeditor-releases/zipball/0068dd540ce8bf1815abb7b5455c55354bc56334", + "reference": "0068dd540ce8bf1815abb7b5455c55354bc56334", + "shasum": "" + }, + "type": "library", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "GPL-2.0+", + "LGPL-2.1+", + "MPL-1.1+" + ], + "authors": [ + { + "name": "CKSource - Frederico Knabben", + "homepage": "http://cksource.com" + } + ], + "description": "JavaScript WYSIWYG web text editor.", + "homepage": "http://ckeditor.com", + "keywords": [ + "CKEditor", + "editor", + "fckeditor", + "html", + "javascript", + "richtext", + "text", + "wysiwyg" + ], + "time": "2014-02-26 15:34:37" + }, + { + "name": "mobiledetect/mobiledetectlib", + "version": "2.8.3", + "source": { + "type": "git", + "url": "https://github.com/serbanghita/Mobile-Detect.git", + "reference": "f5753e4b90daffe50c902e99df5ce3c58fca3fee" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/serbanghita/Mobile-Detect/zipball/f5753e4b90daffe50c902e99df5ce3c58fca3fee", + "reference": "f5753e4b90daffe50c902e99df5ce3c58fca3fee", + "shasum": "" + }, + "require": { + "php": ">=5.0.0" + }, + "require-dev": { + "phpunit/phpunit": "*" + }, + "type": "library", + "autoload": { + "classmap": [ + "Mobile_Detect.php" + ], + "psr-0": { + "Detection": "namespaced/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Serban Ghita", + "email": "serbanghita@gmail.com", + "homepage": "http://ghita.org", + "role": "Developer" + } + ], + "description": "Mobile_Detect is a lightweight PHP class for detecting mobile devices. It uses the User-Agent string combined with specific HTTP headers to detect the mobile environment.", + "homepage": "https://github.com/serbanghita/Mobile-Detect", + "keywords": [ + "detect mobile devices", + "mobile", + "mobile detect", + "mobile detector", + "php mobile detect" + ], + "time": "2014-07-10 20:00:25" + }, + { + "name": "phpoffice/phpexcel", + "version": "1.8.0", + "source": { + "type": "git", + "url": "https://github.com/PHPOffice/PHPExcel.git", + "reference": "e69a5e4d0ffa7fb6f171859e0a04346e580df30b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHPOffice/PHPExcel/zipball/e69a5e4d0ffa7fb6f171859e0a04346e580df30b", + "reference": "e69a5e4d0ffa7fb6f171859e0a04346e580df30b", + "shasum": "" + }, + "require": { + "ext-xml": "*", + "ext-xmlwriter": "*", + "php": ">=5.2.0" + }, + "type": "library", + "autoload": { + "psr-0": { + "PHPExcel": "Classes/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL" + ], + "authors": [ + { + "name": "Maarten Balliauw", + "homepage": "http://blog.maartenballiauw.be" + }, + { + "name": "Mark Baker" + }, + { + "name": "Franck Lefevre", + "homepage": "http://blog.rootslabs.net" + }, + { + "name": "Erik Tilt" + } + ], + "description": "PHPExcel - OpenXML - Read, Create and Write Spreadsheet documents in PHP - Spreadsheet engine", + "homepage": "http://phpexcel.codeplex.com", + "keywords": [ + "OpenXML", + "excel", + "php", + "spreadsheet", + "xls", + "xlsx" + ], + "time": "2014-03-02 15:22:49" + }, + { + "name": "raven/raven", + "version": "0.12.1", + "source": { + "type": "git", + "url": "https://github.com/getsentry/raven-php.git", + "reference": "b325984c792ff89f985b73da9a3ad8ed8b520bca" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/getsentry/raven-php/zipball/b325984c792ff89f985b73da9a3ad8ed8b520bca", + "reference": "b325984c792ff89f985b73da9a3ad8ed8b520bca", + "shasum": "" + }, + "require": { + "ext-curl": "*", + "php": ">=5.2.4" + }, + "require-dev": { + "fabpot/php-cs-fixer": "^1.8.0", + "phpunit/phpunit": "^4.6.6" + }, + "bin": [ + "bin/raven" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "0.12.x-dev" + } + }, + "autoload": { + "psr-0": { + "Raven_": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD" + ], + "authors": [ + { + "name": "David Cramer", + "email": "dcramer@gmail.com" + } + ], + "description": "A PHP client for Sentry (http://getsentry.com)", + "homepage": "http://getsentry.com", + "keywords": [ + "log", + "logging" + ], + "time": "2015-08-25 22:38:46" + }, + { + "name": "restler/framework", + "version": "3.0.0", + "target-dir": "Luracast/Restler", + "source": { + "type": "git", + "url": "https://github.com/Luracast/Restler-Framework.git", + "reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Luracast/Restler-Framework/zipball/6ee10b3e5dbc6376916fed55ec2340a37cce436b", + "reference": "6ee10b3e5dbc6376916fed55ec2340a37cce436b", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "replace": { + "luracast/restler": "3.*" + }, + "require-dev": { + "bshaffer/oauth2-server-php": "v1.0", + "luracast/explorer": "*", + "mustache/mustache": "dev-master", + "rodneyrehm/plist": "dev-master", + "symfony/yaml": "*", + "twig/twig": "v1.13.0", + "zendframework/zendamf": "dev-master" + }, + "suggest": { + "bshaffer/oauth2-server-php": "Restler can provide OAuth2 authentication using this library (see require-dev for details)", + "luracast/explorer": "Restler's very own api explorer (see require-dev for details)", + "mustache/mustache": "Restler can render HtmlView using mustache/handlebar templates (see require-dev for details)", + "rodneyrehm/plist": "Restler supports tho Apple plist xml format (see require-dev for details)", + "symfony/yaml": "Restler can produce content in yaml format as well (see require-dev for details)", + "twig/twig": "Restler can render HtmlView using twig templates (see require-dev for details)", + "zendframework/zendamf": "Support for the amf document format (see require-dev for details)" + }, + "type": "library", + "extra": { + "branch-alias": { + "master": "v3.0.x-dev" + } + }, + "autoload": { + "psr-0": { + "Luracast\\Restler": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPL-2.1" + ], + "authors": [ + { + "name": "Luracast", + "email": "arul@luracast.com" + }, + { + "name": "Nick nickl- Lombard", + "email": "github@jigsoft.co.za" + } + ], + "description": "Just the Restler Framework without the tests and examples", + "homepage": "http://luracast.com/products/restler/", + "keywords": [ + "api", + "framework", + "rest", + "server" + ], + "time": "2015-08-04 07:52:49" + }, + { + "name": "tecnick.com/tcpdf", + "version": "6.2.6", + "source": { + "type": "git", + "url": "https://github.com/tecnickcom/TCPDF.git", + "reference": "a2e8f5b505a7a14a4ed960313c4baf699fd1f4bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/tecnickcom/TCPDF/zipball/a2e8f5b505a7a14a4ed960313c4baf699fd1f4bb", + "reference": "a2e8f5b505a7a14a4ed960313c4baf699fd1f4bb", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "fonts", + "config", + "include", + "tcpdf.php", + "tcpdf_parser.php", + "tcpdf_import.php", + "tcpdf_barcodes_1d.php", + "tcpdf_barcodes_2d.php", + "include/tcpdf_colors.php", + "include/tcpdf_filters.php", + "include/tcpdf_font_data.php", + "include/tcpdf_fonts.php", + "include/tcpdf_images.php", + "include/tcpdf_static.php", + "include/barcodes/datamatrix.php", + "include/barcodes/pdf417.php", + "include/barcodes/qrcode.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "LGPLv3" + ], + "authors": [ + { + "name": "Nicola Asuni", + "email": "info@tecnick.com", + "homepage": "http://nicolaasuni.tecnick.com" + } + ], + "description": "TCPDF is a PHP class for generating PDF documents and barcodes.", + "homepage": "http://www.tcpdf.org/", + "keywords": [ + "PDFD32000-2008", + "TCPDF", + "barcodes", + "datamatrix", + "pdf", + "pdf417", + "qrcode" + ], + "time": "2015-01-28 18:51:40" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": [], + "prefer-stable": false, + "prefer-lowest": false, + "platform": { + "php": ">=5.3.0", + "ext-curl": "*" + }, + "platform-dev": [] +} diff --git a/dev/dolibarr_changes.txt b/dev/dolibarr_changes.txt index bf68293d369..9d449070a5f 100644 --- a/dev/dolibarr_changes.txt +++ b/dev/dolibarr_changes.txt @@ -69,7 +69,7 @@ Add tcpdi.php Add tcpdi_parser.php and replace: require_once(dirname(__FILE__).'/include/tcpdf_filters.php'); with: -require_once(dirname(__FILE__).'/../tcpdf/include/tcpdf_filters.php'); +require_once(dirname(__FILE__).'/../tecnick.com/tcpdf/include/tcpdf_filters.php'); diff --git a/dev/test/testtcpdf.php b/dev/test/testtcpdf.php index 4b8b6f36d53..6b600c630d3 100755 --- a/dev/test/testtcpdf.php +++ b/dev/test/testtcpdf.php @@ -27,8 +27,8 @@ * @since 2008-03-04 */ -require_once('../../htdocs/includes/tcpdf/config/lang/eng.php'); -require_once('../../htdocs/includes/tcpdf/tcpdf.php'); +require_once('../../htdocs/includes/tecnick.com/tcpdf/config/tcpdf_config.php'); +require_once('../../htdocs/includes/tecnick.com/tcpdf/tcpdf.php'); // create new PDF document $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); diff --git a/dev/test/testutf.php b/dev/test/testutf.php index 5812b0c8639..f60f9d195a3 100644 --- a/dev/test/testutf.php +++ b/dev/test/testutf.php @@ -47,8 +47,8 @@ print 'Files has been created. Check its name from your explorer'."\n"; * @since 2008-09-15 */ -require_once('../../htdocs/includes/tcpdf/config/lang/eng.php'); -require_once('../../htdocs/includes/tcpdf/tcpdf.php'); +require_once('../../htdocs/includes/tecnick.com/tcpdf/config/tcpdf_config.php'); +require_once('../../htdocs/includes/tecnick.com/tcpdf/tcpdf.php'); // create new PDF document $pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false); diff --git a/htdocs/.gitignore b/htdocs/.gitignore index e26ae1a0f45..0b4d7b82790 100644 --- a/htdocs/.gitignore +++ b/htdocs/.gitignore @@ -12,3 +12,4 @@ /test.php /ultimatepdf* /lead +/dolimed* diff --git a/htdocs/accountancy/admin/account.php b/htdocs/accountancy/admin/account.php index bff9dbe4265..0bf37a7753e 100644 --- a/htdocs/accountancy/admin/account.php +++ b/htdocs/accountancy/admin/account.php @@ -173,11 +173,12 @@ if ($result) { print ''; print ''; - $var = true; + $var = false; $accountstatic=new AccountingAccount($db); - while ( $i < min($num, $limit) ) { + while ( $i < min($num, $limit) ) + { $obj = $db->fetch_object($resql); $accountstatic->id=$obj->rowid; diff --git a/htdocs/accountancy/admin/export.php b/htdocs/accountancy/admin/export.php index 3da29bd9cc1..856670e18e2 100644 --- a/htdocs/accountancy/admin/export.php +++ b/htdocs/accountancy/admin/export.php @@ -99,12 +99,13 @@ print_fiche_titre($langs->trans('ConfigAccountingExpert'),$linkback,'title_setup $head = admin_accounting_prepare_head(); -dol_fiche_head($head, 'export', $langs->trans("Configuration"), 0, 'cron'); print '
'; print ''; print ''; +dol_fiche_head($head, 'export', $langs->trans("Configuration"), 0, 'cron'); + print ''; $var = true; @@ -169,7 +170,9 @@ if ($num) print "
\n"; } -print '
'; +dol_fiche_end(); + +print '
'; print '
'; diff --git a/htdocs/accountancy/admin/index.php b/htdocs/accountancy/admin/index.php index fc8994c4836..44b2268dc06 100644 --- a/htdocs/accountancy/admin/index.php +++ b/htdocs/accountancy/admin/index.php @@ -157,12 +157,13 @@ print_fiche_titre($langs->trans('ConfigAccountingExpert'),$linkback,'title_setup $head = admin_accounting_prepare_head($accounting); -dol_fiche_head($head, 'general', $langs->trans("Configuration"), 0, 'cron'); print '
'; print ''; print ''; +dol_fiche_head($head, 'general', $langs->trans("Configuration"), 0, 'cron'); + print ''; // Cas du parametre ACCOUNTING_MODE @@ -291,7 +292,11 @@ print ''; print "
\n"; -print '

'; +dol_fiche_end(); + +print '
'; + print '
'; + llxFooter(); $db->close(); diff --git a/htdocs/accountancy/admin/journal.php b/htdocs/accountancy/admin/journal.php new file mode 100644 index 00000000000..e1d4894788a --- /dev/null +++ b/htdocs/accountancy/admin/journal.php @@ -0,0 +1,174 @@ + + * Copyright (C) 2013-2015 Alexandre Spangaro + * Copyright (C) 2014 Florian Henry + * Copyright (C) 2014 Marcos García + * Copyright (C) 2014 Juanjo Menent + * Copyright (C) 2015 Jean-François Ferry + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +/** + * \file htdocs/accountancy/admin/journal.php + * \ingroup Accounting Expert + * \brief Setup page to configure accounting expert module + */ + +require '../../main.inc.php'; + +// Class +require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php'; +require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; + +$langs->load("accountancy"); + +// Security check +if (!$user->admin) + accessforbidden(); + +$action = GETPOST('action', 'alpha'); + +// Other parameters ACCOUNTING_* +$list = array ( + 'ACCOUNTING_SELL_JOURNAL', + 'ACCOUNTING_PURCHASE_JOURNAL', + 'ACCOUNTING_SOCIAL_JOURNAL', + 'ACCOUNTING_MISCELLANEOUS_JOURNAL', + 'ACCOUNTING_EXPENSEREPORT_JOURNAL' +); + +/* + * Actions + */ + +if ($action == 'update') { + $error = 0; + + foreach ( $list as $constname ) { + $constvalue = GETPOST($constname, 'alpha'); + + if (! dolibarr_set_const($db, $constname, $constvalue, 'chaine', 0, '', $conf->entity)) { + $error ++; + } + } + + if (! $error) { + setEventMessage($langs->trans("SetupSaved")); + } else { + setEventMessage($langs->trans("Error"), 'errors'); + } +} + +/* + * View + */ + +llxHeader(); + +$form = new Form($db); + +$linkback=''.$langs->trans("BackToModuleList").''; +print_fiche_titre($langs->trans('ConfigAccountingExpert'),$linkback,'title_setup'); + +$head = admin_accounting_prepare_head(null); + +print '
'; +print ''; +print ''; + +dol_fiche_head($head, 'journal', $langs->trans("Configuration"), 0, 'cron'); + +print ''; +print ''; +print ''; +print "\n"; + +foreach ( $list as $key ) { + $var = ! $var; + + print ''; + + // Param + $label = $langs->trans($key); + print ''; + + // Value + print ''; +} + +print "
' . $langs->trans('Journaux') . '
'; + print ''; + print '
\n"; + + +print '
'; + + +print ''; +print ''; +print ''; +print "\n"; + +// Bank account +$sql = "SELECT rowid, label, accountancy_journal"; +$sql.= " FROM ".MAIN_DB_PREFIX."bank_account"; +$sql.= " WHERE entity = ".$conf->entity; +$sql.= " AND clos = 0"; +$sql.= " ORDER BY label"; + +$resql = $db->query($sql); +if ($resql) +{ + $numr = $db->num_rows($resql); + $i = 0; + + if ($numr > 0) + + while ($i < $numr) + { + $objp = $db->fetch_object($resql); + + $var = ! $var; + + print ''; + + // Param + print ''; + + // Value + print ''; + + $i++; + } +} +else dol_print_error($db); +$db->free($resql); + +print "
' . $langs->trans('JournalFinancial') . '
'; + print ''; + print '
\n"; + +dol_fiche_end(); + +print '
'; + +print '
'; + + +llxFooter(); +$db->close(); \ No newline at end of file diff --git a/htdocs/accountancy/admin/journaux.php b/htdocs/accountancy/admin/journaux.php deleted file mode 100644 index e2359ebd8ee..00000000000 --- a/htdocs/accountancy/admin/journaux.php +++ /dev/null @@ -1,172 +0,0 @@ - - * Copyright (C) 2013-2015 Alexandre Spangaro - * Copyright (C) 2014 Florian Henry - * Copyright (C) 2014 Marcos García - * Copyright (C) 2014 Juanjo Menent - * Copyright (C) 2015 Jean-François Ferry - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - * - */ - -/** - * \file htdocs/accountancy/admin/journaux.php - * \ingroup Accounting Expert - * \brief Setup page to configure accounting expert module - */ - -require '../../main.inc.php'; - -// Class -require_once DOL_DOCUMENT_ROOT.'/core/lib/admin.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/accounting.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/core/lib/bank.lib.php'; -require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; - -$langs->load("accountancy"); - -// Security check -if (!$user->admin) - accessforbidden(); - -$action = GETPOST('action', 'alpha'); - -// Other parameters ACCOUNTING_* -$list = array ( - 'ACCOUNTING_SELL_JOURNAL', - 'ACCOUNTING_PURCHASE_JOURNAL', - 'ACCOUNTING_SOCIAL_JOURNAL', - 'ACCOUNTING_MISCELLANEOUS_JOURNAL', - 'ACCOUNTING_EXPENSEREPORT_JOURNAL' -); - -/* - * Actions - */ - -if ($action == 'update') { - $error = 0; - - foreach ( $list as $constname ) { - $constvalue = GETPOST($constname, 'alpha'); - - if (! dolibarr_set_const($db, $constname, $constvalue, 'chaine', 0, '', $conf->entity)) { - $error ++; - } - } - - if (! $error) { - setEventMessage($langs->trans("SetupSaved")); - } else { - setEventMessage($langs->trans("Error"), 'errors'); - } -} - -/* - * View - */ - -llxHeader(); - -$form = new Form($db); - -$linkback=''.$langs->trans("BackToModuleList").''; -print_fiche_titre($langs->trans('ConfigAccountingExpert'),$linkback,'title_setup'); - -$head = admin_accounting_prepare_head(null); - -dol_fiche_head($head, 'journal', $langs->trans("Configuration"), 0, 'cron'); - -print '
'; -print ''; -print ''; - -print ''; -print ''; -print ''; -print "\n"; - -foreach ( $list as $key ) { - $var = ! $var; - - print ''; - - // Param - $label = $langs->trans($key); - print ''; - - // Value - print ''; -} - -print "
' . $langs->trans('Journaux') . '
'; - print ''; - print '
\n"; - -print '
'; -print '
'; - -print '
'; - -print ''; -print ''; -print ''; -print "\n"; - -// Bank account -$sql = "SELECT rowid, label, accountancy_journal"; -$sql.= " FROM ".MAIN_DB_PREFIX."bank_account"; -$sql.= " WHERE entity = ".$conf->entity; -$sql.= " AND clos = 0"; -$sql.= " ORDER BY label"; - -$resql = $db->query($sql); -if ($resql) -{ - $numr = $db->num_rows($resql); - $i = 0; - - if ($numr > 0) - - while ($i < $numr) - { - $objp = $db->fetch_object($resql); - - $var = ! $var; - - print ''; - - // Param - print ''; - - // Value - print ''; - - $i++; - } -} -else dol_print_error($db); -$db->free($resql); - -print "
' . $langs->trans('JournalFinancial') . '
'; - print ''; - print '
\n"; - -print ''; - -print '
'; - -llxFooter(); -$db->close(); \ No newline at end of file diff --git a/htdocs/accountancy/admin/productaccount.php b/htdocs/accountancy/admin/productaccount.php index 315c9f55325..9898064f6d5 100644 --- a/htdocs/accountancy/admin/productaccount.php +++ b/htdocs/accountancy/admin/productaccount.php @@ -287,8 +287,9 @@ if ($result) { print '' . $langs->trans("Accountancy_code_sell_suggest") . ''; } // print_liste_field_titre(''); - print '' . $langs->trans("Ventilate") . '
/' . ''; + print_liste_field_titre($langs->trans("Ventilate") . '
/ '); print ''; + print ''; print ''; print ''; @@ -359,7 +360,7 @@ if ($result) { // print '' . $compta_prodbuy . ''; // TODO: we shoul set a user defined value to adjust user square / wide screen size // $trunclenghform = defined('ACCOUNTING_LENGTH_DESCRIPTION_ACCOUNT') ? ACCOUNTING_LENGTH_DESCRIPTION_ACCOUNT : 50; - print ''; + print ''; print $form->select_account($compta_prodbuy_id, 'codeventil_'.$product_static->id, 1); print ''; } else { @@ -369,7 +370,7 @@ if ($result) { // TODO: replace by select // TODO: we shoul set a user defined value to adjust user square / wide screen size // $trunclenghform = defined('ACCOUNTING_LENGTH_DESCRIPTION_ACCOUNT') ? ACCOUNTING_LENGTH_DESCRIPTION_ACCOUNT : 50; - print ''; + print ''; print $form->select_account($compta_prodsell_id, 'codeventil_'.$product_static->id, 1); print ''; } diff --git a/htdocs/accountancy/journal/bankjournal.php b/htdocs/accountancy/journal/bankjournal.php index f4d6400449f..250d9001679 100644 --- a/htdocs/accountancy/journal/bankjournal.php +++ b/htdocs/accountancy/journal/bankjournal.php @@ -571,7 +571,7 @@ else // Title print ''; - print ''.$langs->trans("ReportName").''; + print ''.$langs->trans("ReportName").''; print ''.$namereport.''; print ''; print ''; @@ -588,7 +588,7 @@ else // Description print ''; - print ''.$langs->trans("ReportDescription").''; + print ''.$langs->trans("ReportDescription").''; print ''.$description.''; print ''; diff --git a/htdocs/adherents/card.php b/htdocs/adherents/card.php index 94e31fc24ba..a74e19648ea 100644 --- a/htdocs/adherents/card.php +++ b/htdocs/adherents/card.php @@ -1619,131 +1619,133 @@ else * Hotbar */ print '
'; - - if ($action != 'valid' && $action != 'editlogin' && $action != 'editthirdparty') - { - // Modify - if ($user->rights->adherent->creer) - { - print '"; - } - else - { - print '
'.$langs->trans("Modify").'
'; - } - - // Validate - if ($object->statut == -1) + $parameters = array(); + $reshook = $hookmanager->executeHooks('addMoreActionsButtons', $parameters, $object, $action); // Note that $action and $object may have been + if (empty($reshook)) { + if ($action != 'valid' && $action != 'editlogin' && $action != 'editthirdparty') { + // Modify if ($user->rights->adherent->creer) { - print '\n"; + print '"; } else { - print '
'.$langs->trans("Validate").'
'; + print '
'.$langs->trans("Modify").'
'; } - } - - // Reactivate - if ($object->statut == 0) - { + + // Validate + if ($object->statut == -1) + { + if ($user->rights->adherent->creer) + { + print '\n"; + } + else + { + print '
'.$langs->trans("Validate").'
'; + } + } + + // Reactivate + if ($object->statut == 0) + { + if ($user->rights->adherent->creer) + { + print '\n"; + } + else + { + print '
'.$langs->trans("Reenable")."
"; + } + } + + // Send card by email if ($user->rights->adherent->creer) { - print '\n"; + if ($object->statut >= 1) + { + if ($object->email) print '\n"; + else print '\n"; + } + else + { + print '
'.$langs->trans("SendCardByMail")."
"; + } } else { - print '
'.$langs->trans("Reenable")."
"; + print '
'.$langs->trans("SendCardByMail")."
"; } - } - - // Send card by email - if ($user->rights->adherent->creer) - { + + // Terminate if ($object->statut >= 1) { - if ($object->email) print '\n"; - else print '\n"; + if ($user->rights->adherent->supprimer) + { + print '\n"; + } + else + { + print '
'.$langs->trans("Resiliate")."
"; + } } - else + + // Create third party + if (! empty($conf->societe->enabled) && ! $object->fk_soc) { - print '
'.$langs->trans("SendCardByMail")."
"; + if ($user->rights->societe->creer) + { + if ($object->statut != -1) print ''; + else print ''; + } + else + { + print '
'.$langs->trans("CreateDolibarrThirdParty")."
"; + } } - } - else - { - print '
'.$langs->trans("SendCardByMail")."
"; - } - - // Terminate - if ($object->statut >= 1) - { + + // Create user + if (! $user->societe_id && ! $object->user_id) + { + if ($user->rights->user->user->creer) + { + if ($object->statut != -1) print ''; + else print ''; + } + else + { + print '
'.$langs->trans("CreateDolibarrLogin")."
"; + } + } + + // Delete if ($user->rights->adherent->supprimer) { - print '\n"; + print '\n"; } else { - print '
'.$langs->trans("Resiliate")."
"; + print '
'.$langs->trans("Delete")."
"; } - } - - // Create third party - if (! empty($conf->societe->enabled) && ! $object->fk_soc) - { - if ($user->rights->societe->creer) + + // Action SPIP + if (! empty($conf->mailmanspip->enabled) && ! empty($conf->global->ADHERENT_USE_SPIP)) { - if ($object->statut != -1) print ''; - else print ''; - } - else - { - print '
'.$langs->trans("CreateDolibarrThirdParty")."
"; + $isinspip = $mailmanspip->is_in_spip($object); + + if ($isinspip == 1) + { + print '\n"; + } + if ($isinspip == 0) + { + print '\n"; + } } + } - - // Create user - if (! $user->societe_id && ! $object->user_id) - { - if ($user->rights->user->user->creer) - { - if ($object->statut != -1) print ''; - else print ''; - } - else - { - print '
'.$langs->trans("CreateDolibarrLogin")."
"; - } - } - - // Delete - if ($user->rights->adherent->supprimer) - { - print '\n"; - } - else - { - print '
'.$langs->trans("Delete")."
"; - } - - // Action SPIP - if (! empty($conf->mailmanspip->enabled) && ! empty($conf->global->ADHERENT_USE_SPIP)) - { - $isinspip = $mailmanspip->is_in_spip($object); - - if ($isinspip == 1) - { - print '\n"; - } - if ($isinspip == 0) - { - print '\n"; - } - } - } - print '
'; if ($isinspip == -1) diff --git a/htdocs/admin/syslog.php b/htdocs/admin/syslog.php index 3eebfebb37b..7d713760382 100644 --- a/htdocs/admin/syslog.php +++ b/htdocs/admin/syslog.php @@ -106,8 +106,17 @@ if ($action == 'set') $activeModules = $newActiveModules; dolibarr_set_const($db, 'SYSLOG_HANDLERS', json_encode($activeModules), 'chaine',0,'',0); + // Check configuration + foreach ($activeModules as $modulename) { + /** + * @var LogHandler + */ + $module = new $modulename; + $error = $module->checkConfiguration(); + } - if (! $error) + + if (! $error) { $db->commit(); setEventMessage($langs->trans("SetupSaved")); @@ -115,7 +124,8 @@ if ($action == 'set') else { $db->rollback(); - setEventMessage($langs->trans("Error"),'errors'); + setEventMessage($error, 'errors'); + } } diff --git a/htdocs/comm/askpricesupplier/card.php b/htdocs/comm/askpricesupplier/card.php index 51368cb09a7..22732c1a19d 100644 --- a/htdocs/comm/askpricesupplier/card.php +++ b/htdocs/comm/askpricesupplier/card.php @@ -1664,7 +1664,7 @@ if ($action == 'create') // Create an order if (! empty($conf->commande->enabled) && $object->statut == 2) { - if ($user->rights->commande->creer) { + if ($user->rights->fournisseur->commande->creer) { print ''; } } diff --git a/htdocs/comm/mailing/list.php b/htdocs/comm/mailing/list.php index 43441306e34..8ca6b889310 100644 --- a/htdocs/comm/mailing/list.php +++ b/htdocs/comm/mailing/list.php @@ -40,10 +40,9 @@ $pagenext = $page + 1; if (! $sortorder) $sortorder="DESC"; if (! $sortfield) $sortfield="m.date_creat"; -$sall=isset($_GET["sall"])?$_GET["sall"]:$_POST["sall"]; -$sref=isset($_GET["sref"])?$_GET["sref"]:$_POST["sref"]; - -$filteremail=$_REQUEST["filteremail"]?$_REQUEST["filteremail"]:''; +$sall=GETPOST("sall","alpha"); +$sref=GETPOST("sref","alpha"); +$filteremail=GETPOST('filteremail','alpha'); @@ -62,8 +61,8 @@ if ($filteremail) $sql.= " FROM ".MAIN_DB_PREFIX."mailing as m, ".MAIN_DB_PREFIX."mailing_cibles as mc"; $sql.= " WHERE m.rowid = mc.fk_mailing AND m.entity = ".$conf->entity; $sql.= " AND mc.email = '".$db->escape($filteremail)."'"; - if ($sref) $sql.= " AND m.rowid = '".$sref."'"; - if ($sall) $sql.= " AND (m.titre like '%".$sall."%' OR m.sujet like '%".$sall."%' OR m.body like '%".$sall."%')"; + if ($sref) $sql.= " AND m.rowid = '".$db->escape($sref)."'"; + if ($sall) $sql.= " AND (m.titre like '%".$db->escape($sall)."%' OR m.sujet like '%".$db->escape($sall)."%' OR m.body like '%".$db->escape($sall)."%')"; if (! $sortorder) $sortorder="ASC"; if (! $sortfield) $sortfield="m.rowid"; $sql.= $db->order($sortfield,$sortorder); @@ -74,8 +73,8 @@ else $sql = "SELECT m.rowid, m.titre, m.nbemail, m.statut, m.date_creat as datec, m.date_envoi as date_envoi"; $sql.= " FROM ".MAIN_DB_PREFIX."mailing as m"; $sql.= " WHERE m.entity = ".$conf->entity; - if ($sref) $sql.= " AND m.rowid = '".$sref."'"; - if ($sall) $sql.= " AND (m.titre like '%".$sall."%' OR m.sujet like '%".$sall."%' OR m.body like '%".$sall."%')"; + if ($sref) $sql.= " AND m.rowid = '".$db->escape($sref)."'"; + if ($sall) $sql.= " AND (m.titre like '%".$db->escape($sall)."%' OR m.sujet like '%".$db->escape($sall)."%' OR m.body like '%".$db->escape($sall)."%')"; if (! $sortorder) $sortorder="ASC"; if (! $sortfield) $sortfield="m.rowid"; $sql.= $db->order($sortfield,$sortorder); @@ -94,7 +93,7 @@ if ($result) $i = 0; - $param = "&sall=".$sall; + $param = "&sall=".urlencode($sall); if ($filteremail) $param.='&filteremail='.urlencode($filteremail); print '
'; @@ -111,11 +110,11 @@ if ($result) print ''; print ''; - print ''; + print ''; print ''; // Title print ''; - print ''; + print ''; print ''; print ' '; if (! $filteremail) print ' '; diff --git a/htdocs/commande/orderstoinvoice.php b/htdocs/commande/orderstoinvoice.php index e75a9dda73b..cb3ede38f24 100644 --- a/htdocs/commande/orderstoinvoice.php +++ b/htdocs/commande/orderstoinvoice.php @@ -394,6 +394,9 @@ if ($action == 'create' && !$error) print ''; print ''; print ''; + + dol_fiche_head(); + print ''; // Ref @@ -495,16 +498,20 @@ if ($action == 'create' && !$error) $i++; } + dol_fiche_end(); + // Button "Create Draft" - print '
'; + print '
'; print "\n"; print ''; print "
\n"; + + } -//Mode liste -if (($action != 'create' && $action != 'add') && !$error) +// Mode liste +if (($action != 'create' && $action != 'add') || ($action == 'create' && $error)) { llxHeader(); ?> diff --git a/htdocs/compta/bank/class/account.class.php b/htdocs/compta/bank/class/account.class.php index 72aa176b7b6..a7e55afb3cb 100644 --- a/htdocs/compta/bank/class/account.class.php +++ b/htdocs/compta/bank/class/account.class.php @@ -122,8 +122,10 @@ class Account extends CommonObject */ function canBeConciliated() { + global $conf; + if (empty($this->rappro)) return -1; - if ($this->courant == 2) return -2; + if ($this->courant == 2 && empty($conf->global->BANK_CAN_RECONCILIATE_CASHACCOUNT)) return -2; if ($this->clos) return -3; return 1; } diff --git a/htdocs/compta/facture.php b/htdocs/compta/facture.php index 33734d2a84b..a4a3c8a1e83 100644 --- a/htdocs/compta/facture.php +++ b/htdocs/compta/facture.php @@ -2242,6 +2242,10 @@ if ($action == 'create') print ''; // Bank Account + if (isset($_POST['fk_account'])) { + $fk_account = $_POST['fk_account']; + } + print '' . $langs->trans('BankAccount') . ''; $form->select_comptes($fk_account, 'fk_account', 0, '', 1); print ''; diff --git a/htdocs/compta/facture/mergepdftool.php b/htdocs/compta/facture/mergepdftool.php index 27741077f6d..5acca49d1b1 100644 --- a/htdocs/compta/facture/mergepdftool.php +++ b/htdocs/compta/facture/mergepdftool.php @@ -165,10 +165,12 @@ if ($action == 'presend' && GETPOST('sendmail')) '__CHECK_READ__' => '', //'__LASTNAME__' => $obj2->lastname, //'__FIRSTNAME__' => $obj2->firstname, - '__REF__' => $object->ref, + '__FACREF__' => $object->ref, // For backward compatibility + '__REF__' => $object->ref, '__REFCLIENT__' => $object->thirdparty->name ); + $subject=make_substitutions($subject, $substitutionarray); $message=make_substitutions($message, $substitutionarray); $actiontypecode='AC_FAC'; @@ -196,7 +198,7 @@ if ($action == 'presend' && GETPOST('sendmail')) } else { - //$result=$mailfile->sendfile(); + $result=$mailfile->sendfile(); if ($result) { $resultmasssend.=$langs->trans('MailSuccessfulySent',$mailfile->getValidAddress($from,2),$mailfile->getValidAddress($sendto,2)); // Must not contain " @@ -246,12 +248,11 @@ if ($action == 'presend' && GETPOST('sendmail')) } } else - { + { $nbignored++; $langs->load("other"); $resultmasssend.='
'.$langs->trans('ErrorCantReadFile',$file).'
'; - dol_syslog('Failed to read file: '.$file); - break ; + dol_syslog('Failed to read file: '.$file, LOG_WARNING); } } } @@ -263,7 +264,7 @@ if ($action == 'presend' && GETPOST('sendmail')) } else { - setEventMessage($langs->trans("NoRemindSent"), 'warnings'); + setEventMessage($langs->trans("NoRemindSent"), 'warnings'); // May be object has no generated PDF file } } } @@ -556,9 +557,7 @@ if ($resql) include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; $formmail = new FormMail($db); - print '
'; - print_fiche_titre($langs->trans("SendRemind"),'',''); - print '
'; + dol_fiche_head(null, '', $langs->trans("SendRemind")); $topicmail="MailTopicSendRemindUnpaidInvoices"; $modelmail="facture_relance"; @@ -598,7 +597,8 @@ if ($resql) $formmail->param['returnurl']=$_SERVER["PHP_SELF"].'?id='.$object->id; print $formmail->get_form(); - print '
'."\n"; + + dol_fiche_end(); } print ''; diff --git a/htdocs/compta/index.php b/htdocs/compta/index.php index 11296bda93e..5fb1d638a88 100644 --- a/htdocs/compta/index.php +++ b/htdocs/compta/index.php @@ -4,6 +4,7 @@ * Copyright (C) 2005-2012 Regis Houssin * Copyright (C) 2015 Juanjo Menent * Copyright (C) 2015 Jean-François Ferry + * Copyright (C) 2015 Raphaël Doursenaud * * 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 @@ -606,7 +607,7 @@ if (! empty($conf->tax->enabled) && $user->rights->tax->charges->lire) $chargestatic->lib=$obj->libelle; $chargestatic->paye=$obj->paye; print ''.$chargestatic->getNomUrl(1).''; - print ''.dol_print_date($obj->date_ech,'day').''; + print ''.dol_print_date($db->jdate($obj->date_ech),'day').''; print ''.price($obj->amount).''; print ''.price($obj->sumpaid).''; print ''.$chargestatic->getLibStatut(3).''; @@ -966,7 +967,7 @@ if ($resql) $obj = $db->fetch_object($resql); $var=!$var; - print "".dol_print_date($obj->da,"day").""; + print "".dol_print_date($db->jdate($obj->da),"day").""; print ''.$obj->libelle.' '.$obj->label.''; $i++; } diff --git a/htdocs/contrat/card.php b/htdocs/contrat/card.php index 1d2e3355b24..429547ed602 100644 --- a/htdocs/contrat/card.php +++ b/htdocs/contrat/card.php @@ -97,34 +97,19 @@ $permissiondellink=$user->rights->contrat->creer; // Used by the include of acti * Actions */ -include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be include, not includ_once - -include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be include, not include_once - -if ($action == 'confirm_active' && $confirm == 'yes' && $user->rights->contrat->activer) +$parameters = array('socid' => $socid); +$reshook = $hookmanager->executeHooks('doActions', $parameters, $object, $action); // Note that $action and $object may have been modified by some hooks +if ($reshook < 0) setEventMessages($hookmanager->error, $hookmanager->errors, 'errors'); +if (empty($reshook)) { - $result = $object->active_line($user, GETPOST('ligne'), GETPOST('date'), GETPOST('dateend'), GETPOST('comment')); - - if ($result > 0) - { - header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); - exit; - } - else { - setEventMessage($object->error,'errors'); - } -} - -else if ($action == 'confirm_closeline' && $confirm == 'yes' && $user->rights->contrat->activer) -{ - if (! GETPOST('dateend')) + include DOL_DOCUMENT_ROOT.'/core/actions_setnotes.inc.php'; // Must be include, not includ_once + + include DOL_DOCUMENT_ROOT.'/core/actions_dellink.inc.php'; // Must be include, not include_once + + if ($action == 'confirm_active' && $confirm == 'yes' && $user->rights->contrat->activer) { - $error++; - setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("DateEnd")),'errors'); - } - if (! $error) - { - $result = $object->close_line($user, GETPOST('ligne'), GETPOST('dateend'), urldecode(GETPOST('comment'))); + $result = $object->active_line($user, GETPOST('ligne'), GETPOST('date'), GETPOST('dateend'), GETPOST('comment')); + if ($result > 0) { header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); @@ -134,602 +119,604 @@ else if ($action == 'confirm_closeline' && $confirm == 'yes' && $user->rights->c setEventMessage($object->error,'errors'); } } -} - -// Si ajout champ produit predefini -if (GETPOST('mode')=='predefined') -{ - $date_start=''; - $date_end=''; - if (GETPOST('date_startmonth') && GETPOST('date_startday') && GETPOST('date_startyear')) - { - $date_start=dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), 0, GETPOST('date_startmonth'), GETPOST('date_startday'), GETPOST('date_startyear')); - } - if (GETPOST('date_endmonth') && GETPOST('date_endday') && GETPOST('date_endyear')) - { - $date_end=dol_mktime(GETPOST('date_endhour'), GETPOST('date_endmin'), 0, GETPOST('date_endmonth'), GETPOST('date_endday'), GETPOST('date_endyear')); - } -} - -// Si ajout champ produit libre -if (GETPOST('mode')=='libre') -{ - $date_start_sl=''; - $date_end_sl=''; - if (GETPOST('date_start_slmonth') && GETPOST('date_start_slday') && GETPOST('date_start_slyear')) - { - $date_start_sl=dol_mktime(GETPOST('date_start_slhour'), GETPOST('date_start_slmin'), 0, GETPOST('date_start_slmonth'), GETPOST('date_start_slday'), GETPOST('date_start_slyear')); - } - if (GETPOST('date_end_slmonth') && GETPOST('date_end_slday') && GETPOST('date_end_slyear')) - { - $date_end_sl=dol_mktime(GETPOST('date_end_slhour'), GETPOST('date_end_slmin'), 0, GETPOST('date_end_slmonth'), GETPOST('date_end_slday'), GETPOST('date_end_slyear')); - } -} - -// Param dates -$date_contrat=''; -$date_start_update=''; -$date_end_update=''; -$date_start_real_update=''; -$date_end_real_update=''; -if (GETPOST('date_start_updatemonth') && GETPOST('date_start_updateday') && GETPOST('date_start_updateyear')) -{ - $date_start_update=dol_mktime(GETPOST('date_start_updatehour'), GETPOST('date_start_updatemin'), 0, GETPOST('date_start_updatemonth'), GETPOST('date_start_updateday'), GETPOST('date_start_updateyear')); -} -if (GETPOST('date_end_updatemonth') && GETPOST('date_end_updateday') && GETPOST('date_end_updateyear')) -{ - $date_end_update=dol_mktime(GETPOST('date_end_updatehour'), GETPOST('date_end_updatemin'), 0, GETPOST('date_end_updatemonth'), GETPOST('date_end_updateday'), GETPOST('date_end_updateyear')); -} -if (GETPOST('date_start_real_updatemonth') && GETPOST('date_start_real_updateday') && GETPOST('date_start_real_updateyear')) -{ - $date_start_real_update=dol_mktime(GETPOST('date_start_real_updatehour'), GETPOST('date_start_real_updatemin'), 0, GETPOST('date_start_real_updatemonth'), GETPOST('date_start_real_updateday'), GETPOST('date_start_real_updateyear')); -} -if (GETPOST('date_end_real_updatemonth') && GETPOST('date_end_real_updateday') && GETPOST('date_end_real_updateyear')) -{ - $date_end_real_update=dol_mktime(GETPOST('date_end_real_updatehour'), GETPOST('date_end_real_updatemin'), 0, GETPOST('date_end_real_updatemonth'), GETPOST('date_end_real_updateday'), GETPOST('date_end_real_updateyear')); -} -if (GETPOST('remonth') && GETPOST('reday') && GETPOST('reyear')) -{ - $datecontrat = dol_mktime(GETPOST('rehour'), GETPOST('remin'), 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')); -} - -// Add contract -if ($action == 'add' && $user->rights->contrat->creer) -{ - // Check - if (empty($datecontrat)) + + else if ($action == 'confirm_closeline' && $confirm == 'yes' && $user->rights->contrat->activer) { - $error++; - setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Date")),'errors'); - $action='create'; + if (! GETPOST('dateend')) + { + $error++; + setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("DateEnd")),'errors'); + } + if (! $error) + { + $result = $object->close_line($user, GETPOST('ligne'), GETPOST('dateend'), urldecode(GETPOST('comment'))); + if ($result > 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else + { + setEventMessage($object->error,'errors'); + } + } } - - if ($socid<1) + + // Si ajout champ produit predefini + if (GETPOST('mode')=='predefined') { - setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Customer")),'errors'); - $action='create'; - $error++; - } - - // Fill array 'array_options' with data from add form - $ret = $extrafields->setOptionalsFromPost($extralabels, $object); - if ($ret < 0) { - $error ++; - $action = 'create'; - } - - if (! $error) - { - $object->socid = $socid; - $object->date_contrat = $datecontrat; - - $object->commercial_suivi_id = GETPOST('commercial_suivi_id','int'); - $object->commercial_signature_id = GETPOST('commercial_signature_id','int'); - - $object->note_private = GETPOST('note_private','alpha'); - $object->note_public = GETPOST('note_public','alpha'); - $object->fk_project = GETPOST('projectid','int'); - $object->remise_percent = GETPOST('remise_percent','alpha'); - $object->ref = GETPOST('ref','alpha'); - $object->ref_supplier = GETPOST('ref_supplier','alpha'); - - // If creation from another object of another module (Example: origin=propal, originid=1) - if ($_POST['origin'] && $_POST['originid']) + $date_start=''; + $date_end=''; + if (GETPOST('date_startmonth') && GETPOST('date_startday') && GETPOST('date_startyear')) { - // Parse element/subelement (ex: project_task) - $element = $subelement = $_POST['origin']; - if (preg_match('/^([^_]+)_([^_]+)/i',$_POST['origin'],$regs)) - { - $element = $regs[1]; - $subelement = $regs[2]; - } - - // For compatibility - if ($element == 'order') { $element = $subelement = 'commande'; } - if ($element == 'propal') { $element = 'comm/propal'; $subelement = 'propal'; } - - $object->origin = $_POST['origin']; - $object->origin_id = $_POST['originid']; - - // Possibility to add external linked objects with hooks - $object->linked_objects[$object->origin] = $object->origin_id; - if (is_array($_POST['other_linked_objects']) && ! empty($_POST['other_linked_objects'])) - { - $object->linked_objects = array_merge($object->linked_objects, $_POST['other_linked_objects']); - } - - $id = $object->create($user); - if ($id < 0) { - setEventMessage($object->error,'errors'); - } - - if ($id > 0) - { - dol_include_once('/'.$element.'/class/'.$subelement.'.class.php'); - - $classname = ucfirst($subelement); - $srcobject = new $classname($db); - - dol_syslog("Try to find source object origin=".$object->origin." originid=".$object->origin_id." to add lines"); - $result=$srcobject->fetch($object->origin_id); - if ($result > 0) - { - $srcobject->fetch_thirdparty(); - $lines = $srcobject->lines; - if (empty($lines) && method_exists($srcobject,'fetch_lines')) - { - $srcobject->fetch_lines(); - $lines = $srcobject->lines; - } - - $fk_parent_line=0; - $num=count($lines); - - for ($i=0;$i<$num;$i++) - { - $product_type=($lines[$i]->product_type?$lines[$i]->product_type:0); - - if ($product_type == 1 || (! empty($conf->global->CONTRACT_SUPPORT_PRODUCTS) && in_array($product_type, array(0,1)))) { // TODO Exclude also deee - // service prédéfini - if ($lines[$i]->fk_product > 0) - { - $product_static = new Product($db); - - // Define output language - if (! empty($conf->global->MAIN_MULTILANGS) && ! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) + $date_start=dol_mktime(GETPOST('date_starthour'), GETPOST('date_startmin'), 0, GETPOST('date_startmonth'), GETPOST('date_startday'), GETPOST('date_startyear')); + } + if (GETPOST('date_endmonth') && GETPOST('date_endday') && GETPOST('date_endyear')) + { + $date_end=dol_mktime(GETPOST('date_endhour'), GETPOST('date_endmin'), 0, GETPOST('date_endmonth'), GETPOST('date_endday'), GETPOST('date_endyear')); + } + } + + // Si ajout champ produit libre + if (GETPOST('mode')=='libre') + { + $date_start_sl=''; + $date_end_sl=''; + if (GETPOST('date_start_slmonth') && GETPOST('date_start_slday') && GETPOST('date_start_slyear')) + { + $date_start_sl=dol_mktime(GETPOST('date_start_slhour'), GETPOST('date_start_slmin'), 0, GETPOST('date_start_slmonth'), GETPOST('date_start_slday'), GETPOST('date_start_slyear')); + } + if (GETPOST('date_end_slmonth') && GETPOST('date_end_slday') && GETPOST('date_end_slyear')) + { + $date_end_sl=dol_mktime(GETPOST('date_end_slhour'), GETPOST('date_end_slmin'), 0, GETPOST('date_end_slmonth'), GETPOST('date_end_slday'), GETPOST('date_end_slyear')); + } + } + + // Param dates + $date_contrat=''; + $date_start_update=''; + $date_end_update=''; + $date_start_real_update=''; + $date_end_real_update=''; + if (GETPOST('date_start_updatemonth') && GETPOST('date_start_updateday') && GETPOST('date_start_updateyear')) + { + $date_start_update=dol_mktime(GETPOST('date_start_updatehour'), GETPOST('date_start_updatemin'), 0, GETPOST('date_start_updatemonth'), GETPOST('date_start_updateday'), GETPOST('date_start_updateyear')); + } + if (GETPOST('date_end_updatemonth') && GETPOST('date_end_updateday') && GETPOST('date_end_updateyear')) + { + $date_end_update=dol_mktime(GETPOST('date_end_updatehour'), GETPOST('date_end_updatemin'), 0, GETPOST('date_end_updatemonth'), GETPOST('date_end_updateday'), GETPOST('date_end_updateyear')); + } + if (GETPOST('date_start_real_updatemonth') && GETPOST('date_start_real_updateday') && GETPOST('date_start_real_updateyear')) + { + $date_start_real_update=dol_mktime(GETPOST('date_start_real_updatehour'), GETPOST('date_start_real_updatemin'), 0, GETPOST('date_start_real_updatemonth'), GETPOST('date_start_real_updateday'), GETPOST('date_start_real_updateyear')); + } + if (GETPOST('date_end_real_updatemonth') && GETPOST('date_end_real_updateday') && GETPOST('date_end_real_updateyear')) + { + $date_end_real_update=dol_mktime(GETPOST('date_end_real_updatehour'), GETPOST('date_end_real_updatemin'), 0, GETPOST('date_end_real_updatemonth'), GETPOST('date_end_real_updateday'), GETPOST('date_end_real_updateyear')); + } + if (GETPOST('remonth') && GETPOST('reday') && GETPOST('reyear')) + { + $datecontrat = dol_mktime(GETPOST('rehour'), GETPOST('remin'), 0, GETPOST('remonth'), GETPOST('reday'), GETPOST('reyear')); + } + + // Add contract + if ($action == 'add' && $user->rights->contrat->creer) + { + // Check + if (empty($datecontrat)) + { + $error++; + setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Date")),'errors'); + $action='create'; + } + + if ($socid<1) + { + setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentitiesnoconv("Customer")),'errors'); + $action='create'; + $error++; + } + + // Fill array 'array_options' with data from add form + $ret = $extrafields->setOptionalsFromPost($extralabels, $object); + if ($ret < 0) { + $error ++; + $action = 'create'; + } + + if (! $error) + { + $object->socid = $socid; + $object->date_contrat = $datecontrat; + + $object->commercial_suivi_id = GETPOST('commercial_suivi_id','int'); + $object->commercial_signature_id = GETPOST('commercial_signature_id','int'); + + $object->note_private = GETPOST('note_private','alpha'); + $object->note_public = GETPOST('note_public','alpha'); + $object->fk_project = GETPOST('projectid','int'); + $object->remise_percent = GETPOST('remise_percent','alpha'); + $object->ref = GETPOST('ref','alpha'); + $object->ref_supplier = GETPOST('ref_supplier','alpha'); + + // If creation from another object of another module (Example: origin=propal, originid=1) + if ($_POST['origin'] && $_POST['originid']) + { + // Parse element/subelement (ex: project_task) + $element = $subelement = $_POST['origin']; + if (preg_match('/^([^_]+)_([^_]+)/i',$_POST['origin'],$regs)) + { + $element = $regs[1]; + $subelement = $regs[2]; + } + + // For compatibility + if ($element == 'order') { $element = $subelement = 'commande'; } + if ($element == 'propal') { $element = 'comm/propal'; $subelement = 'propal'; } + + $object->origin = $_POST['origin']; + $object->origin_id = $_POST['originid']; + + // Possibility to add external linked objects with hooks + $object->linked_objects[$object->origin] = $object->origin_id; + if (is_array($_POST['other_linked_objects']) && ! empty($_POST['other_linked_objects'])) + { + $object->linked_objects = array_merge($object->linked_objects, $_POST['other_linked_objects']); + } + + $id = $object->create($user); + if ($id < 0) { + setEventMessage($object->error,'errors'); + } + + if ($id > 0) + { + dol_include_once('/'.$element.'/class/'.$subelement.'.class.php'); + + $classname = ucfirst($subelement); + $srcobject = new $classname($db); + + dol_syslog("Try to find source object origin=".$object->origin." originid=".$object->origin_id." to add lines"); + $result=$srcobject->fetch($object->origin_id); + if ($result > 0) + { + $srcobject->fetch_thirdparty(); + $lines = $srcobject->lines; + if (empty($lines) && method_exists($srcobject,'fetch_lines')) + { + $srcobject->fetch_lines(); + $lines = $srcobject->lines; + } + + $fk_parent_line=0; + $num=count($lines); + + for ($i=0;$i<$num;$i++) + { + $product_type=($lines[$i]->product_type?$lines[$i]->product_type:0); + + if ($product_type == 1 || (! empty($conf->global->CONTRACT_SUPPORT_PRODUCTS) && in_array($product_type, array(0,1)))) { // TODO Exclude also deee + // service prédéfini + if ($lines[$i]->fk_product > 0) { - $prod = new Product($db); - $prod->id=$lines[$i]->fk_product; - $prod->getMultiLangs(); - - $outputlangs = $langs; - $newlang=''; - if (empty($newlang) && GETPOST('lang_id')) $newlang=GETPOST('lang_id'); - if (empty($newlang)) $newlang=$srcobject->thirdparty->default_lang; - if (! empty($newlang)) + $product_static = new Product($db); + + // Define output language + if (! empty($conf->global->MAIN_MULTILANGS) && ! empty($conf->global->PRODUIT_TEXTS_IN_THIRDPARTY_LANGUAGE)) { - $outputlangs = new Translate("",$conf); - $outputlangs->setDefaultLang($newlang); + $prod = new Product($db); + $prod->id=$lines[$i]->fk_product; + $prod->getMultiLangs(); + + $outputlangs = $langs; + $newlang=''; + if (empty($newlang) && GETPOST('lang_id')) $newlang=GETPOST('lang_id'); + if (empty($newlang)) $newlang=$srcobject->thirdparty->default_lang; + if (! empty($newlang)) + { + $outputlangs = new Translate("",$conf); + $outputlangs->setDefaultLang($newlang); + } + + $label = (! empty($prod->multilangs[$outputlangs->defaultlang]["libelle"])) ? $prod->multilangs[$outputlangs->defaultlang]["libelle"] : $lines[$i]->product_label; } - - $label = (! empty($prod->multilangs[$outputlangs->defaultlang]["libelle"])) ? $prod->multilangs[$outputlangs->defaultlang]["libelle"] : $lines[$i]->product_label; + else + { + $label = $lines[$i]->product_label; + } + + if ($conf->global->PRODUIT_DESC_IN_FORM) + $desc .= ($lines[$i]->desc && $lines[$i]->desc!=$lines[$i]->libelle)?dol_htmlentitiesbr($lines[$i]->desc):''; } - else - { - $label = $lines[$i]->product_label; - } - - if ($conf->global->PRODUIT_DESC_IN_FORM) - $desc .= ($lines[$i]->desc && $lines[$i]->desc!=$lines[$i]->libelle)?dol_htmlentitiesbr($lines[$i]->desc):''; + else { + $desc = dol_htmlentitiesbr($lines[$i]->desc); + } + + $result = $object->addline( + $desc, + $lines[$i]->subprice, + $lines[$i]->qty, + $lines[$i]->tva_tx, + $lines[$i]->localtax1_tx, + $lines[$i]->localtax2_tx, + $lines[$i]->fk_product, + $lines[$i]->remise_percent, + $lines[$i]->date_start, + $lines[$i]->date_end, + 'HT', + 0, + $lines[$i]->info_bits, + $lines[$i]->fk_fournprice, + $lines[$i]->pa_ht, + array(), + $lines[$i]->fk_unit + ); + + if ($result < 0) + { + $error++; + break; + } + } - else { - $desc = dol_htmlentitiesbr($lines[$i]->desc); - } - - $result = $object->addline( - $desc, - $lines[$i]->subprice, - $lines[$i]->qty, - $lines[$i]->tva_tx, - $lines[$i]->localtax1_tx, - $lines[$i]->localtax2_tx, - $lines[$i]->fk_product, - $lines[$i]->remise_percent, - $lines[$i]->date_start, - $lines[$i]->date_end, - 'HT', - 0, - $lines[$i]->info_bits, - $lines[$i]->fk_fournprice, - $lines[$i]->pa_ht, - array(), - $lines[$i]->fk_unit - ); - - if ($result < 0) - { - $error++; - break; - } - + } + } + else + { + setEventMessage($srcobject->error,'errors'); + $error++; + } + } + else + { + setEventMessage($object->error,'errors'); + $error++; + } + } + else + { + $result = $object->create($user); + if ($result > 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else { + setEventMessage($object->error,'errors'); + } + $action='create'; + } + } + } + + else if ($action == 'classin' && $user->rights->contrat->creer) + { + $object->setProject(GETPOST('projectid')); + } + + // Add a new line + else if ($action == 'addline' && $user->rights->contrat->creer) + { + // Set if we used free entry or predefined product + $predef=''; + $product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):''); + if (GETPOST('prod_entry_mode') == 'free') + { + $idprod=0; + $price_ht = GETPOST('price_ht'); + $tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); + } + else + { + $idprod=GETPOST('idprod', 'int'); + $price_ht = ''; + $tva_tx = ''; + } + + $qty = GETPOST('qty'.$predef); + $remise_percent=GETPOST('remise_percent'.$predef); + + if ($qty == '') + { + setEventMessage($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Qty")),'errors'); + $error++; + } + if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) + { + setEventMessage($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Description")),'errors'); + $error++; + } + + // Extrafields + $extrafieldsline = new ExtraFields($db); + $extralabelsline = $extrafieldsline->fetch_name_optionals_label($object->table_element_line); + $array_options = $extrafieldsline->getOptionalsFromPost($extralabelsline, $predef); + // Unset extrafield + if (is_array($extralabelsline)) { + // Get extra fields + foreach ($extralabelsline as $key => $value) { + unset($_POST["options_" . $key]); + } + } + + if (! $error) + { + // Clean parameters + $date_start=dol_mktime(GETPOST('date_start'.$predef.'hour'), GETPOST('date_start'.$predef.'min'), GETPOST('date_start'.$predef.'sec'), GETPOST('date_start'.$predef.'month'), GETPOST('date_start'.$predef.'day'), GETPOST('date_start'.$predef.'year')); + $date_end=dol_mktime(GETPOST('date_end'.$predef.'hour'), GETPOST('date_end'.$predef.'min'), GETPOST('date_end'.$predef.'sec'), GETPOST('date_end'.$predef.'month'), GETPOST('date_end'.$predef.'day'), GETPOST('date_end'.$predef.'year')); + $price_base_type = (GETPOST('price_base_type', 'alpha')?GETPOST('price_base_type', 'alpha'):'HT'); + + // Ecrase $pu par celui du produit + // Ecrase $desc par celui du produit + // Ecrase $txtva par celui du produit + // Ecrase $base_price_type par celui du produit + if ($idprod > 0) + { + $prod = new Product($db); + $prod->fetch($idprod); + + $tva_tx = get_default_tva($mysoc,$object->thirdparty,$prod->id); + $tva_npr = get_default_npr($mysoc,$object->thirdparty,$prod->id); + $pu_ht = $prod->price; + $pu_ttc = $prod->price_ttc; + $price_min = $prod->price_min; + $price_base_type = $prod->price_base_type; + + // On defini prix unitaire + if ($conf->global->PRODUIT_MULTIPRICES && $object->thirdparty->price_level) + { + $pu_ht = $prod->multiprices[$object->thirdparty->price_level]; + $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level]; + $price_min = $prod->multiprices_min[$object->thirdparty->price_level]; + $price_base_type = $prod->multiprices_base_type[$object->thirdparty->price_level]; + } + elseif (! empty($conf->global->PRODUIT_CUSTOMER_PRICES)) + { + require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php'; + + $prodcustprice = new Productcustomerprice($db); + + $filter = array('t.fk_product' => $prod->id,'t.fk_soc' => $object->thirdparty->id); + + $result = $prodcustprice->fetch_all('', '', 0, 0, $filter); + if ($result) { + if (count($prodcustprice->lines) > 0) { + $pu_ht = price($prodcustprice->lines [0]->price); + $pu_ttc = price($prodcustprice->lines [0]->price_ttc); + $price_base_type = $prodcustprice->lines [0]->price_base_type; + $prod->tva_tx = $prodcustprice->lines [0]->tva_tx; } + } + } + + // On reevalue prix selon taux tva car taux tva transaction peut etre different + // de ceux du produit par defaut (par exemple si pays different entre vendeur et acheteur). + if ($tva_tx != $prod->tva_tx) + { + if ($price_base_type != 'HT') + { + $pu_ht = price2num($pu_ttc / (1 + ($tva_tx/100)), 'MU'); + } + else + { + $pu_ttc = price2num($pu_ht * (1 + ($tva_tx/100)), 'MU'); } } - else - { - setEventMessage($srcobject->error,'errors'); - $error++; - } + + $desc=$prod->description; + $desc=dol_concatdesc($desc,$product_desc); + $fk_unit = $prod->fk_unit; + } + else + { + $pu_ht=GETPOST('price_ht'); + $price_base_type = 'HT'; + $tva_tx=GETPOST('tva_tx')?str_replace('*','',GETPOST('tva_tx')):0; // tva_tx field may be disabled, so we use vat rate 0 + $tva_npr=preg_match('/\*/',GETPOST('tva_tx'))?1:0; + $desc=$product_desc; + $fk_unit= GETPOST('units', 'alpha'); + } + + $localtax1_tx=get_localtax($tva_tx,1,$object->thirdparty); + $localtax2_tx=get_localtax($tva_tx,2,$object->thirdparty); + + // ajout prix achat + $fk_fournprice = $_POST['fournprice']; + if ( ! empty($_POST['buying_price']) ) + $pa_ht = $_POST['buying_price']; + else + $pa_ht = null; + + $info_bits=0; + if ($tva_npr) $info_bits |= 0x01; + + if($price_min && (price2num($pu_ht)*(1-price2num($remise_percent)/100) < price2num($price_min))) + { + $object->error = $langs->trans("CantBeLessThanMinPrice",price(price2num($price_min,'MU'),0,$langs,0,0,-1,$conf->currency)); + $result = -1 ; + } + else + { + // Insert line + $result = $object->addline( + $desc, + $pu_ht, + $qty, + $tva_tx, + $localtax1_tx, + $localtax2_tx, + $idprod, + $remise_percent, + $date_start, + $date_end, + $price_base_type, + $pu_ttc, + $info_bits, + $fk_fournprice, + $pa_ht, + $array_options, + $fk_unit + ); + } + + if ($result > 0) + { + // Define output language + if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) + { + $outputlangs = $langs; + $newlang = ''; + if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id')) $newlang = GETPOST('lang_id','alpha'); + if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $object->thirdparty->default_lang; + if (! empty($newlang)) { + $outputlangs = new Translate("", $conf); + $outputlangs->setDefaultLang($newlang); + } + + $ret = $object->fetch($id); // Reload to get new records + + $object->generateDocument($object->modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + } + + unset($_POST ['prod_entry_mode']); + + unset($_POST['qty']); + unset($_POST['type']); + unset($_POST['remise_percent']); + unset($_POST['price_ht']); + unset($_POST['price_ttc']); + unset($_POST['tva_tx']); + unset($_POST['product_ref']); + unset($_POST['product_label']); + unset($_POST['product_desc']); + unset($_POST['fournprice']); + unset($_POST['buying_price']); + unset($_POST ['np_marginRate']); + unset($_POST ['np_markRate']); + unset($_POST['dp_desc']); + unset($_POST['idprod']); + + unset($_POST['date_starthour']); + unset($_POST['date_startmin']); + unset($_POST['date_startsec']); + unset($_POST['date_startday']); + unset($_POST['date_startmonth']); + unset($_POST['date_startyear']); + unset($_POST['date_endhour']); + unset($_POST['date_endmin']); + unset($_POST['date_endsec']); + unset($_POST['date_endday']); + unset($_POST['date_endmonth']); + unset($_POST['date_endyear']); } else { - setEventMessage($object->error,'errors'); - $error++; + setEventMessage($object->error,'errors'); + } + } + } + + else if ($action == 'updateligne' && $user->rights->contrat->creer && ! GETPOST('cancel')) + { + $objectline = new ContratLigne($db); + if ($objectline->fetch(GETPOST('elrowid'))) + { + $db->begin(); + + if ($date_start_real_update == '') $date_start_real_update=$objectline->date_ouverture; + if ($date_end_real_update == '') $date_end_real_update=$objectline->date_cloture; + + $localtax1_tx=get_localtax(GETPOST('eltva_tx'),1,$object->thirdparty); + $localtax2_tx=get_localtax(GETPOST('eltva_tx'),2,$object->thirdparty); + + // ajout prix d'achat + $fk_fournprice = $_POST['fournprice']; + if ( ! empty($_POST['buying_price']) ) + $pa_ht = $_POST['buying_price']; + else + $pa_ht = null; + + $fk_unit = GETPOST('unit', 'alpha'); + + $objectline->description=GETPOST('product_desc'); + $objectline->price_ht=GETPOST('elprice'); + $objectline->subprice=GETPOST('elprice'); + $objectline->qty=GETPOST('elqty'); + $objectline->remise_percent=GETPOST('elremise_percent'); + $objectline->tva_tx=GETPOST('eltva_tx')?GETPOST('eltva_tx'):0; // Field may be disabled, so we use vat rate 0 + $objectline->localtax1_tx=$localtax1_tx; + $objectline->localtax2_tx=$localtax2_tx; + $objectline->date_ouverture_prevue=$date_start_update; + $objectline->date_ouverture=$date_start_real_update; + $objectline->date_fin_validite=$date_end_update; + $objectline->date_cloture=$date_end_real_update; + $objectline->fk_user_cloture=$user->id; + $objectline->fk_fournprice=$fk_fournprice; + $objectline->pa_ht=$pa_ht; + + if ($fk_unit > 0) { + $objectline->fk_unit = GETPOST('unit'); + } else { + $objectline->fk_unit = null; + } + + // Extrafields + $extrafieldsline = new ExtraFields($db); + $extralabelsline = $extrafieldsline->fetch_name_optionals_label($objectline->table_element); + $array_options = $extrafieldsline->getOptionalsFromPost($extralabelsline, $predef); + $objectline->array_options=$array_options; + + // TODO verifier price_min si fk_product et multiprix + + $result=$objectline->update($user); + if ($result > 0) + { + $db->commit(); + } + else + { + setEventMessage($objectline->error,'errors'); + $db->rollback(); } } else { - $result = $object->create($user); - if ($result > 0) - { - header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); - exit; - } - else { - setEventMessage($object->error,'errors'); - } - $action='create'; - } - } -} - -else if ($action == 'classin' && $user->rights->contrat->creer) -{ - $object->setProject(GETPOST('projectid')); -} - -// Add a new line -else if ($action == 'addline' && $user->rights->contrat->creer) -{ - // Set if we used free entry or predefined product - $predef=''; - $product_desc=(GETPOST('dp_desc')?GETPOST('dp_desc'):''); - if (GETPOST('prod_entry_mode') == 'free') - { - $idprod=0; - $price_ht = GETPOST('price_ht'); - $tva_tx = (GETPOST('tva_tx') ? GETPOST('tva_tx') : 0); - } - else - { - $idprod=GETPOST('idprod', 'int'); - $price_ht = ''; - $tva_tx = ''; - } - - $qty = GETPOST('qty'.$predef); - $remise_percent=GETPOST('remise_percent'.$predef); - - if ($qty == '') - { - setEventMessage($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Qty")),'errors'); - $error++; - } - if (GETPOST('prod_entry_mode') == 'free' && empty($idprod) && empty($product_desc)) - { - setEventMessage($langs->trans("ErrorFieldRequired", $langs->transnoentitiesnoconv("Description")),'errors'); - $error++; - } - - // Extrafields - $extrafieldsline = new ExtraFields($db); - $extralabelsline = $extrafieldsline->fetch_name_optionals_label($object->table_element_line); - $array_options = $extrafieldsline->getOptionalsFromPost($extralabelsline, $predef); - // Unset extrafield - if (is_array($extralabelsline)) { - // Get extra fields - foreach ($extralabelsline as $key => $value) { - unset($_POST["options_" . $key]); - } - } - - if (! $error) - { - // Clean parameters - $date_start=dol_mktime(GETPOST('date_start'.$predef.'hour'), GETPOST('date_start'.$predef.'min'), GETPOST('date_start'.$predef.'sec'), GETPOST('date_start'.$predef.'month'), GETPOST('date_start'.$predef.'day'), GETPOST('date_start'.$predef.'year')); - $date_end=dol_mktime(GETPOST('date_end'.$predef.'hour'), GETPOST('date_end'.$predef.'min'), GETPOST('date_end'.$predef.'sec'), GETPOST('date_end'.$predef.'month'), GETPOST('date_end'.$predef.'day'), GETPOST('date_end'.$predef.'year')); - $price_base_type = (GETPOST('price_base_type', 'alpha')?GETPOST('price_base_type', 'alpha'):'HT'); - - // Ecrase $pu par celui du produit - // Ecrase $desc par celui du produit - // Ecrase $txtva par celui du produit - // Ecrase $base_price_type par celui du produit - if ($idprod > 0) - { - $prod = new Product($db); - $prod->fetch($idprod); - - $tva_tx = get_default_tva($mysoc,$object->thirdparty,$prod->id); - $tva_npr = get_default_npr($mysoc,$object->thirdparty,$prod->id); - $pu_ht = $prod->price; - $pu_ttc = $prod->price_ttc; - $price_min = $prod->price_min; - $price_base_type = $prod->price_base_type; - - // On defini prix unitaire - if ($conf->global->PRODUIT_MULTIPRICES && $object->thirdparty->price_level) - { - $pu_ht = $prod->multiprices[$object->thirdparty->price_level]; - $pu_ttc = $prod->multiprices_ttc[$object->thirdparty->price_level]; - $price_min = $prod->multiprices_min[$object->thirdparty->price_level]; - $price_base_type = $prod->multiprices_base_type[$object->thirdparty->price_level]; - } - elseif (! empty($conf->global->PRODUIT_CUSTOMER_PRICES)) - { - require_once DOL_DOCUMENT_ROOT . '/product/class/productcustomerprice.class.php'; - - $prodcustprice = new Productcustomerprice($db); - - $filter = array('t.fk_product' => $prod->id,'t.fk_soc' => $object->thirdparty->id); - - $result = $prodcustprice->fetch_all('', '', 0, 0, $filter); - if ($result) { - if (count($prodcustprice->lines) > 0) { - $pu_ht = price($prodcustprice->lines [0]->price); - $pu_ttc = price($prodcustprice->lines [0]->price_ttc); - $price_base_type = $prodcustprice->lines [0]->price_base_type; - $prod->tva_tx = $prodcustprice->lines [0]->tva_tx; - } - } - } - - // On reevalue prix selon taux tva car taux tva transaction peut etre different - // de ceux du produit par defaut (par exemple si pays different entre vendeur et acheteur). - if ($tva_tx != $prod->tva_tx) - { - if ($price_base_type != 'HT') - { - $pu_ht = price2num($pu_ttc / (1 + ($tva_tx/100)), 'MU'); - } - else - { - $pu_ttc = price2num($pu_ht * (1 + ($tva_tx/100)), 'MU'); - } - } - - $desc=$prod->description; - $desc=dol_concatdesc($desc,$product_desc); - $fk_unit = $prod->fk_unit; - } - else - { - $pu_ht=GETPOST('price_ht'); - $price_base_type = 'HT'; - $tva_tx=GETPOST('tva_tx')?str_replace('*','',GETPOST('tva_tx')):0; // tva_tx field may be disabled, so we use vat rate 0 - $tva_npr=preg_match('/\*/',GETPOST('tva_tx'))?1:0; - $desc=$product_desc; - $fk_unit= GETPOST('units', 'alpha'); - } - - $localtax1_tx=get_localtax($tva_tx,1,$object->thirdparty); - $localtax2_tx=get_localtax($tva_tx,2,$object->thirdparty); - - // ajout prix achat - $fk_fournprice = $_POST['fournprice']; - if ( ! empty($_POST['buying_price']) ) - $pa_ht = $_POST['buying_price']; - else - $pa_ht = null; - - $info_bits=0; - if ($tva_npr) $info_bits |= 0x01; - - if($price_min && (price2num($pu_ht)*(1-price2num($remise_percent)/100) < price2num($price_min))) - { - $object->error = $langs->trans("CantBeLessThanMinPrice",price(price2num($price_min,'MU'),0,$langs,0,0,-1,$conf->currency)); - $result = -1 ; - } - else - { - // Insert line - $result = $object->addline( - $desc, - $pu_ht, - $qty, - $tva_tx, - $localtax1_tx, - $localtax2_tx, - $idprod, - $remise_percent, - $date_start, - $date_end, - $price_base_type, - $pu_ttc, - $info_bits, - $fk_fournprice, - $pa_ht, - $array_options, - $fk_unit - ); - } - - if ($result > 0) - { - // Define output language - if (empty($conf->global->MAIN_DISABLE_PDF_AUTOUPDATE)) - { - $outputlangs = $langs; - $newlang = ''; - if ($conf->global->MAIN_MULTILANGS && empty($newlang) && GETPOST('lang_id')) $newlang = GETPOST('lang_id','alpha'); - if ($conf->global->MAIN_MULTILANGS && empty($newlang)) $newlang = $object->thirdparty->default_lang; - if (! empty($newlang)) { - $outputlangs = new Translate("", $conf); - $outputlangs->setDefaultLang($newlang); - } - - $ret = $object->fetch($id); // Reload to get new records - - $object->generateDocument($object->modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); - } - - unset($_POST ['prod_entry_mode']); - - unset($_POST['qty']); - unset($_POST['type']); - unset($_POST['remise_percent']); - unset($_POST['price_ht']); - unset($_POST['price_ttc']); - unset($_POST['tva_tx']); - unset($_POST['product_ref']); - unset($_POST['product_label']); - unset($_POST['product_desc']); - unset($_POST['fournprice']); - unset($_POST['buying_price']); - unset($_POST ['np_marginRate']); - unset($_POST ['np_markRate']); - unset($_POST['dp_desc']); - unset($_POST['idprod']); - - unset($_POST['date_starthour']); - unset($_POST['date_startmin']); - unset($_POST['date_startsec']); - unset($_POST['date_startday']); - unset($_POST['date_startmonth']); - unset($_POST['date_startyear']); - unset($_POST['date_endhour']); - unset($_POST['date_endmin']); - unset($_POST['date_endsec']); - unset($_POST['date_endday']); - unset($_POST['date_endmonth']); - unset($_POST['date_endyear']); - } - else - { - setEventMessage($object->error,'errors'); - } - } -} - -else if ($action == 'updateligne' && $user->rights->contrat->creer && ! GETPOST('cancel')) -{ - $objectline = new ContratLigne($db); - if ($objectline->fetch(GETPOST('elrowid'))) - { - $db->begin(); - - if ($date_start_real_update == '') $date_start_real_update=$objectline->date_ouverture; - if ($date_end_real_update == '') $date_end_real_update=$objectline->date_cloture; - - $localtax1_tx=get_localtax(GETPOST('eltva_tx'),1,$object->thirdparty); - $localtax2_tx=get_localtax(GETPOST('eltva_tx'),2,$object->thirdparty); - - // ajout prix d'achat - $fk_fournprice = $_POST['fournprice']; - if ( ! empty($_POST['buying_price']) ) - $pa_ht = $_POST['buying_price']; - else - $pa_ht = null; - - $fk_unit = GETPOST('unit', 'alpha'); - - $objectline->description=GETPOST('product_desc'); - $objectline->price_ht=GETPOST('elprice'); - $objectline->subprice=GETPOST('elprice'); - $objectline->qty=GETPOST('elqty'); - $objectline->remise_percent=GETPOST('elremise_percent'); - $objectline->tva_tx=GETPOST('eltva_tx')?GETPOST('eltva_tx'):0; // Field may be disabled, so we use vat rate 0 - $objectline->localtax1_tx=$localtax1_tx; - $objectline->localtax2_tx=$localtax2_tx; - $objectline->date_ouverture_prevue=$date_start_update; - $objectline->date_ouverture=$date_start_real_update; - $objectline->date_fin_validite=$date_end_update; - $objectline->date_cloture=$date_end_real_update; - $objectline->fk_user_cloture=$user->id; - $objectline->fk_fournprice=$fk_fournprice; - $objectline->pa_ht=$pa_ht; - - if ($fk_unit > 0) { - $objectline->fk_unit = GETPOST('unit'); - } else { - $objectline->fk_unit = null; + setEventMessage($objectline->error,'errors'); } - - // Extrafields - $extrafieldsline = new ExtraFields($db); - $extralabelsline = $extrafieldsline->fetch_name_optionals_label($objectline->table_element); - $array_options = $extrafieldsline->getOptionalsFromPost($extralabelsline, $predef); - $objectline->array_options=$array_options; - - // TODO verifier price_min si fk_product et multiprix - - $result=$objectline->update($user); - if ($result > 0) - { - $db->commit(); - } - else - { - setEventMessage($objectline->error,'errors'); - $db->rollback(); - } - } - else - { - setEventMessage($objectline->error,'errors'); - } -} - -else if ($action == 'confirm_deleteline' && $confirm == 'yes' && $user->rights->contrat->creer) -{ - $result = $object->deleteline(GETPOST('lineid'),$user); - - if ($result >= 0) - { - header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); - exit; - } - else - { - setEventMessage($object->error,'errors'); - } -} - -else if ($action == 'confirm_valid' && $confirm == 'yes' && $user->rights->contrat->creer) -{ - $result = $object->validate($user); -} - -// Close all lines -else if ($action == 'confirm_close' && $confirm == 'yes' && $user->rights->contrat->creer) -{ - $object->cloture($user); -} - -else if ($action == 'confirm_delete' && $confirm == 'yes' && $user->rights->contrat->supprimer) -{ - $result=$object->delete($user); - if ($result >= 0) - { - header("Location: index.php"); - return; } - else + + else if ($action == 'confirm_deleteline' && $confirm == 'yes' && $user->rights->contrat->creer) { - setEventMessage($object->error,'errors'); + $result = $object->deleteline(GETPOST('lineid'),$user); + + if ($result >= 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else + { + setEventMessage($object->error,'errors'); + } } -} - -else if ($action == 'confirm_move' && $confirm == 'yes' && $user->rights->contrat->creer) -{ - if (GETPOST('newcid') > 0) + + else if ($action == 'confirm_valid' && $confirm == 'yes' && $user->rights->contrat->creer) { - $contractline = new ContratLigne($db); - $result=$contractline->fetch(GETPOST('lineid')); - $contractline->fk_contrat = GETPOST('newcid'); - $result=$contractline->update($user,1); + $result = $object->validate($user); + } + + // Close all lines + else if ($action == 'confirm_close' && $confirm == 'yes' && $user->rights->contrat->creer) + { + $object->cloture($user); + } + + else if ($action == 'confirm_delete' && $confirm == 'yes' && $user->rights->contrat->supprimer) + { + $result=$object->delete($user); if ($result >= 0) { - header("Location: ".$_SERVER['PHP_SELF']."?id=".$id); + header("Location: index.php"); return; } else @@ -737,162 +724,181 @@ else if ($action == 'confirm_move' && $confirm == 'yes' && $user->rights->contra setEventMessage($object->error,'errors'); } } - else + + else if ($action == 'confirm_move' && $confirm == 'yes' && $user->rights->contrat->creer) { - setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentities("RefNewContract")),'errors'); - } -} else if ($action == 'update_extras') { - // Fill array 'array_options' with data from update form - $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); - $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); - if ($ret < 0) - $error ++; - - if (! $error) { - - $result = $object->insertExtraFields(); - if ($result < 0) { - $error ++; - } - } else if ($reshook < 0) - $error ++; - - if ($error) { - $action = 'edit_extras'; - setEventMessage($object->error,'errors'); - } -} elseif ($action=='setref_supplier') { - $cancelbutton = GETPOST('cancel'); - - if (!$cancelbutton) { - - $result = $object->fetch($id); - if ($result < 0) { - setEventMessage($object->errors, 'errors'); - } - - $result = $object->setValueFrom('ref_supplier',GETPOST('ref_supplier','alpha')); - if ($result < 0) { - setEventMessage($object->errors, 'errors'); - $action = 'editref_supplier'; - } else { - header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); - exit; - } - } - else { - header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $id); - exit; - } -} elseif ($action=='setref') { - $cancelbutton = GETPOST('cancel'); - - if (!$cancelbutton) { - $result = $object->fetch($id); - if ($result < 0) { - setEventMessage($object->errors, 'errors'); - } - - $result = $object->setValueFrom('ref',GETPOST('ref','alpha'));; - if ($result < 0) { - setEventMessage($object->errors, 'errors'); - $action = 'editref'; - } else { - header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id); - exit; - } - } - else { - header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $id); - exit; - } -} - -// Generation doc (depuis lien ou depuis cartouche doc) -else if ($action == 'builddoc' && $user->rights->contrat->creer) { - if (GETPOST('model')) { - $object->setDocModel($user, GETPOST('model')); - } - - // Define output language - $outputlangs = $langs; - if (! empty($conf->global->MAIN_MULTILANGS)) { - $outputlangs = new Translate("", $conf); - $newlang = (GETPOST('lang_id') ? GETPOST('lang_id') : $object->thirdparty->default_lang); - $outputlangs->setDefaultLang($newlang); - } - $ret = $object->fetch($id); // Reload to get new records - $result = $object->generateDocument($object->modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); - if ($result <= 0) - { - setEventMessages($object->error, $object->errors, 'errors'); - $action=''; - } -} - -// Remove file in doc form -else if ($action == 'remove_file' && $user->rights->contrat->creer) { - if ($object->id > 0) { - require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; - - $langs->load("other"); - $upload_dir = $conf->contrat->dir_output; - $file = $upload_dir . '/' . GETPOST('file'); - $ret = dol_delete_file($file, 0, 0, 0, $object); - if ($ret) setEventMessage($langs->trans("FileWasRemoved", GETPOST('file'))); - else setEventMessage($langs->trans("ErrorFailToDeleteFile", GETPOST('file')), 'errors'); - } -} - -if (! empty($conf->global->MAIN_DISABLE_CONTACTS_TAB) && $user->rights->contrat->creer) -{ - if ($action == 'addcontact') - { - $contactid = (GETPOST('userid') ? GETPOST('userid') : GETPOST('contactid')); - $result = $object->add_contact($contactid, GETPOST('type'), GETPOST('source')); - - if ($result >= 0) + if (GETPOST('newcid') > 0) { - header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); - exit; - } - else - { - if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') + $contractline = new ContratLigne($db); + $result=$contractline->fetch(GETPOST('lineid')); + $contractline->fk_contrat = GETPOST('newcid'); + $result=$contractline->update($user,1); + if ($result >= 0) { - $langs->load("errors"); - setEventMessage($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"),'errors'); + header("Location: ".$_SERVER['PHP_SELF']."?id=".$id); + return; } else { setEventMessage($object->error,'errors'); } } - } - - // bascule du statut d'un contact - else if ($action == 'swapstatut') - { - $result=$object->swapContactStatus(GETPOST('ligne')); - } - - // Efface un contact - else if ($action == 'deletecontact') - { - $result = $object->delete_contact(GETPOST('lineid')); - - if ($result >= 0) + else { - header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); - exit; + setEventMessage($langs->trans("ErrorFieldRequired",$langs->transnoentities("RefNewContract")),'errors'); } - else { + } else if ($action == 'update_extras') { + // Fill array 'array_options' with data from update form + $extralabels = $extrafields->fetch_name_optionals_label($object->table_element); + $ret = $extrafields->setOptionalsFromPost($extralabels, $object, GETPOST('attribute')); + if ($ret < 0) + $error ++; + + if (! $error) { + + $result = $object->insertExtraFields(); + if ($result < 0) { + $error ++; + } + } else if ($reshook < 0) + $error ++; + + if ($error) { + $action = 'edit_extras'; setEventMessage($object->error,'errors'); } + } elseif ($action=='setref_supplier') { + $cancelbutton = GETPOST('cancel'); + + if (!$cancelbutton) { + + $result = $object->fetch($id); + if ($result < 0) { + setEventMessage($object->errors, 'errors'); + } + + $result = $object->setValueFrom('ref_supplier',GETPOST('ref_supplier','alpha')); + if ($result < 0) { + setEventMessage($object->errors, 'errors'); + $action = 'editref_supplier'; + } else { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + } + else { + header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $id); + exit; + } + } elseif ($action=='setref') { + $cancelbutton = GETPOST('cancel'); + + if (!$cancelbutton) { + $result = $object->fetch($id); + if ($result < 0) { + setEventMessage($object->errors, 'errors'); + } + + $result = $object->setValueFrom('ref',GETPOST('ref','alpha'));; + if ($result < 0) { + setEventMessage($object->errors, 'errors'); + $action = 'editref'; + } else { + header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $object->id); + exit; + } + } + else { + header("Location: " . $_SERVER['PHP_SELF'] . "?id=" . $id); + exit; + } + } + + // Generation doc (depuis lien ou depuis cartouche doc) + else if ($action == 'builddoc' && $user->rights->contrat->creer) { + if (GETPOST('model')) { + $object->setDocModel($user, GETPOST('model')); + } + + // Define output language + $outputlangs = $langs; + if (! empty($conf->global->MAIN_MULTILANGS)) { + $outputlangs = new Translate("", $conf); + $newlang = (GETPOST('lang_id') ? GETPOST('lang_id') : $object->thirdparty->default_lang); + $outputlangs->setDefaultLang($newlang); + } + $ret = $object->fetch($id); // Reload to get new records + $result = $object->generateDocument($object->modelpdf, $outputlangs, $hidedetails, $hidedesc, $hideref); + if ($result <= 0) + { + setEventMessages($object->error, $object->errors, 'errors'); + $action=''; + } + } + + // Remove file in doc form + else if ($action == 'remove_file' && $user->rights->contrat->creer) { + if ($object->id > 0) { + require_once DOL_DOCUMENT_ROOT . '/core/lib/files.lib.php'; + + $langs->load("other"); + $upload_dir = $conf->contrat->dir_output; + $file = $upload_dir . '/' . GETPOST('file'); + $ret = dol_delete_file($file, 0, 0, 0, $object); + if ($ret) setEventMessage($langs->trans("FileWasRemoved", GETPOST('file'))); + else setEventMessage($langs->trans("ErrorFailToDeleteFile", GETPOST('file')), 'errors'); + } + } + + if (! empty($conf->global->MAIN_DISABLE_CONTACTS_TAB) && $user->rights->contrat->creer) + { + if ($action == 'addcontact') + { + $contactid = (GETPOST('userid') ? GETPOST('userid') : GETPOST('contactid')); + $result = $object->add_contact($contactid, GETPOST('type'), GETPOST('source')); + + if ($result >= 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else + { + if ($object->error == 'DB_ERROR_RECORD_ALREADY_EXISTS') + { + $langs->load("errors"); + setEventMessage($langs->trans("ErrorThisContactIsAlreadyDefinedAsThisType"),'errors'); + } + else + { + setEventMessage($object->error,'errors'); + } + } + } + + // bascule du statut d'un contact + else if ($action == 'swapstatut') + { + $result=$object->swapContactStatus(GETPOST('ligne')); + } + + // Efface un contact + else if ($action == 'deletecontact') + { + $result = $object->delete_contact(GETPOST('lineid')); + + if ($result >= 0) + { + header("Location: ".$_SERVER['PHP_SELF']."?id=".$object->id); + exit; + } + else { + setEventMessage($object->error,'errors'); + } + } } } - /* * View */ diff --git a/htdocs/core/actions_sendmails.inc.php b/htdocs/core/actions_sendmails.inc.php index d27e6e59fb4..67458fd401d 100644 --- a/htdocs/core/actions_sendmails.inc.php +++ b/htdocs/core/actions_sendmails.inc.php @@ -47,7 +47,7 @@ if (GETPOST('addfile')) /* * Remove file in email form */ -if (! empty($_POST['removedfile'])) +if (! empty($_POST['removedfile']) && empty($_POST['removAll'])) { require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php'; @@ -60,11 +60,41 @@ if (! empty($_POST['removedfile'])) $action='presend'; } +/* + * Remove all files in email form + */ + +if(! empty($_POST['removAll'])) +{ + $listofpaths=array(); + $listofnames=array(); + $listofmimes=array(); + if (! empty($_SESSION["listofpaths"])) $listofpaths=explode(';',$_SESSION["listofpaths"]); + if (! empty($_SESSION["listofnames"])) $listofnames=explode(';',$_SESSION["listofnames"]); + if (! empty($_SESSION["listofmimes"])) $listofmimes=explode(';',$_SESSION["listofmimes"]); + + include_once DOL_DOCUMENT_ROOT.'/core/class/html.formmail.class.php'; + $formmail = new FormMail($db); + + foreach($listofpaths as $key => $value) + { + $pathtodelete = $value; + $filetodelete = $listofnames[$key]; + $result = dol_delete_file($pathtodelete,1); // Delete uploded Files + + $langs->load("other"); + setEventMessage($langs->trans("FileWasRemoved",$filetodelete)); + + $formmail->remove_attached_files($key); // Update Session + } +} + /* * Send mail */ -if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_POST['removedfile'] && ! $_POST['cancel'] && !$_POST['modelselected']) +if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_POST['removAll'] && ! $_POST['removedfile'] && ! $_POST['cancel'] && !$_POST['modelselected']) { + if($conf->dolimail->enabled) $langs->load("dolimail@dolimail"); $langs->load('mails'); $subject='';$actionmsg='';$actionmsg2=''; @@ -81,7 +111,37 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO else if ($object->element == 'societe') { $thirdparty=$object; - $sendtosocid=$thirdparty->id; + if ($thirdparty->id > 0) $sendtosocid=$thirdparty->id; + elseif($conf->dolimail->enabled) + { + $dolimail = new Dolimail($db); + $possibleaccounts=$dolimail->get_societe_by_email($_POST['sendto'],"1"); + $possibleuser=$dolimail->get_from_user_by_mail($_POST['sendto'],"1"); // suche in llx_societe and socpeople + if (!$possibleaccounts && !$possibleuser) + { + setEventMessage($langs->trans('ErrorFailedToFindSocieteRecord',$_POST['sendto']),'errors'); + } + elseif (count($possibleaccounts)>1) + { + $sendtosocid=$possibleaccounts[1]['id']; + $result=$object->fetch($sendtosocid); + + setEventMessage($langs->trans('ErrorFoundMoreThanOneRecordWithEmail',$_POST['sendto'],$object->name)); + } + else + { + if($possibleaccounts){ + $sendtosocid=$possibleaccounts[1]['id']; + $result=$object->fetch($sendtosocid); + }elseif($possibleuser){ + $sendtosocid=$possibleuser[0]['id']; + + $result=$uobject->fetch($sendtosocid); + $object=$uobject; + } + + } + } } else dol_print_error('','Use actions_sendmails.in.php for a type that is not supported'); @@ -163,6 +223,46 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO $trackid = GETPOST('trackid','aZ'); + if($conf->dolimail->enabled) + { + $mailfromid = explode ("#", $_POST['frommail'],3); + if (count($mailfromid)==0) $from = $_POST['fromname'] . ' <' . $_POST['frommail'] .'>'; + else + { + $mbid = $mailfromid[1]; + + /*IMAP Postbox*/ + $mailboxconfig = new IMAP($db); + $mailboxconfig->fetch($mbid); + if ($mailboxconfig->mailbox_imap_host) $ref=$mailboxconfig->get_ref(); + + $mailboxconfig->folder_id=$mailboxconfig->mailbox_imap_outbox; + $mailboxconfig->userfolder_fetch(); + + if ($mailboxconfig->mailbox_save_sent_mails == 1) + { + + $folder=str_replace($ref, '', $mailboxconfig->folder_cache_key); + if (!$folder) $folder = "Sent"; + + $mailboxconfig->mbox = imap_open($mailboxconfig->get_connector_url().$folder, $mailboxconfig->mailbox_imap_login, $mailboxconfig->mailbox_imap_password); + if (FALSE === $mailboxconfig->mbox) + { + $info = FALSE; + $err = $langs->trans('Error3_Imap_Connection_Error'); + setEventMessage($err,$mailboxconfig->element,'errors'); + } + else + { + $mailboxconfig->mailboxid=$_POST['frommail']; + $mailboxconfig->foldername=$folder; + $from = $mailfromid[0] . $mailfromid[2]; + $imap=1; + } + + } + } + } // Send mail require_once DOL_DOCUMENT_ROOT.'/core/class/CMailFile.class.php'; $mailfile = new CMailFile($subject,$sendto,$from,$message,$filepath,$mimetype,$filename,$sendtocc,$sendtobcc,$deliveryreceipt,-1,'','',$trackid); @@ -176,7 +276,26 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO if ($result) { $error=0; - + if($conf->dolimail->enabled) + { + $mid = (GETPOST('mid','int') ? GETPOST('mid','int') : 0); + if ($mid) + { + // set imap flag answered if it is a answered mail + + $dolimail=new DoliMail($db); + $dolimail->id = $mid; + $res=$dolimail->set_prop($user, 'answered',1); + } + if ($imap==1) + { + // write mail to IMAP Server + $movemail = $mailboxconfig->putMail($subject,$sendto,$from,$message,$filepath,$mimetype,$filename,$sendtocc,$folder,$deliveryreceipt,$mailfile); + if ($movemail) setEventMessage($langs->trans("MailMovedToImapFolder",$folder),'mesgs'); + else setEventMessage($langs->trans("MailMovedToImapFolder_Warning",$folder),'warnings'); + } + } + // Initialisation donnees $object->socid = $sendtosocid; // To link to a company $object->sendtoid = $sendtoid; // To link to a contact/address @@ -205,24 +324,27 @@ if (($action == 'send' || $action == 'relance') && ! $_POST['addfile'] && ! $_PO // This avoid sending mail twice if going out and then back to page $mesg=$langs->trans('MailSuccessfulySent',$mailfile->getValidAddress($from,2),$mailfile->getValidAddress($sendto,2)); setEventMessage($mesg); - header('Location: '.$_SERVER["PHP_SELF"].'?'.($paramname?$paramname:'id').'='.$object->id); + if($conf->dolimail->enabled) header('Location: '.$_SERVER["PHP_SELF"].'?'.($paramname?$paramname:'id').'='.$object->id.'&'.($paramname2?$paramname2:'mid').'='.$parm2val); + else header('Location: '.$_SERVER["PHP_SELF"].'?'.($paramname?$paramname:'id').'='.$object->id); exit; } } else { $langs->load("other"); + $mesg='
'; if ($mailfile->error) { - $mesg=''; $mesg.=$langs->trans('ErrorFailedToSendMail',$from,$sendto); $mesg.='
'.$mailfile->error; - setEventMessage($mesg,'errors'); } else { - setEventMessage('No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS', 'warnings'); + $mesg.='No mail sent. Feature is disabled by option MAIN_DISABLE_ALL_MAILS'; } + $mesg.='
'; + + setEventMessage($mesg,'warnings'); $action = 'presend'; } } diff --git a/htdocs/core/boxes/box_prospect.php b/htdocs/core/boxes/box_prospect.php index b3f2144b28b..3895a27a9f1 100644 --- a/htdocs/core/boxes/box_prospect.php +++ b/htdocs/core/boxes/box_prospect.php @@ -155,7 +155,6 @@ class box_prospect extends ModeleBoxes ); } } else { - dol_syslog("box_prospect::loadBox not allowed de read this box content",LOG_ERR); $this->info_box_contents[0][0] = array( 'td' => 'align="left"', 'text' => $langs->trans("ReadPermissionNotAllowed"), diff --git a/htdocs/core/boxes/box_task.php b/htdocs/core/boxes/box_task.php index 8ee42c44731..d6a65884ffb 100644 --- a/htdocs/core/boxes/box_task.php +++ b/htdocs/core/boxes/box_task.php @@ -82,7 +82,7 @@ class box_task extends ModeleBoxes $sql = "SELECT pt.fk_statut, count(pt.rowid) as nb, sum(ptt.task_duration) as durationtot, sum(pt.planned_workload) as plannedtot"; $sql.= " FROM ".MAIN_DB_PREFIX."projet_task as pt, ".MAIN_DB_PREFIX."projet_task_time as ptt"; - $sql.= " WHERE DATE_FORMAT(pt.datec,'%Y') = ".date("Y")." "; + $sql.= " WHERE DATE_FORMAT(pt.datec,'%Y') = '".date("Y")."' "; $sql.= " AND pt.rowid = ptt.fk_task"; $sql.= " GROUP BY pt.fk_statut "; $sql.= " ORDER BY pt.fk_statut DESC"; diff --git a/htdocs/core/lib/accounting.lib.php b/htdocs/core/lib/accounting.lib.php index 196dcfe0997..6e6da6bd686 100644 --- a/htdocs/core/lib/accounting.lib.php +++ b/htdocs/core/lib/accounting.lib.php @@ -47,12 +47,12 @@ function admin_accounting_prepare_head(AccountingAccount $object=null) // $this->tabs = array('entity:-tabname); to remove a tab complete_head_from_modules($conf, $langs, $object, $head, $h, 'accounting_admin'); - $head[$h][0] = dol_buildpath('/accountancy/admin/journaux.php', 1); + $head[$h][0] = DOL_URL_ROOT.'/accountancy/admin/journal.php'; $head[$h][1] = $langs->trans("Journaux"); $head[$h][2] = 'journal'; $h ++; - $head[$h][0] = dol_buildpath('/accountancy/admin/export.php', 1); + $head[$h][0] = DOL_URL_ROOT.'/accountancy/admin/export.php'; $head[$h][1] = $langs->trans("Export"); $head[$h][2] = 'export'; $h ++; @@ -75,7 +75,7 @@ function accounting_prepare_head(AccountingAccount $object) $h = 0; $head = array (); - $head[$h][0] = dol_buildpath('/accountancy/admin/card.php', 1) . '?id=' . $object->id; + $head[$h][0] = DOL_URL_ROOT.'/accountancy/admin/card.php?id=' . $object->id; $head[$h][1] = $langs->trans("Card"); $head[$h][2] = 'card'; $h ++; diff --git a/htdocs/core/lib/admin.lib.php b/htdocs/core/lib/admin.lib.php index d59e3c9a864..7469777fa89 100644 --- a/htdocs/core/lib/admin.lib.php +++ b/htdocs/core/lib/admin.lib.php @@ -838,13 +838,13 @@ function unActivateModule($value, $requiredby=1) } else { - // TODO Replace this afte DolibarrModules is moved as abstract class with a try catch to show module is bugged + // TODO Replace this after DolibarrModules is moved as abstract class with a try catch to show module is bugged $genericMod = new DolibarrModules($db); $genericMod->name=preg_replace('/^mod/i','',$modName); $genericMod->rights_class=strtolower(preg_replace('/^mod/i','',$modName)); $genericMod->const_name='MAIN_MODULE_'.strtoupper(preg_replace('/^mod/i','',$modName)); dol_syslog("modules::unActivateModule Failed to find module file, we use generic function with name " . $modName); - $genericMod->_remove(); + $genericMod->_remove(''); } // Desactivation des modules qui dependent de lui @@ -853,6 +853,7 @@ function unActivateModule($value, $requiredby=1) $countrb=count($objMod->requiredby); for ($i = 0; $i < $countrb; $i++) { + var_dump($objMod->requiredby[$i]); unActivateModule($objMod->requiredby[$i]); } } diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 2f197d8b645..8f6471f4af0 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -202,6 +202,329 @@ function dol_dir_list($path, $types="all", $recursive=0, $filter="", $excludefil } } +/** + * Scan a directory and return a array of files/directories from a selection. + * Content for string is UTF8 and dir separator is "/". + * + * @param int $fk_soc select socid - for your selection in array + * @param string $module_get Starting path from which to search + * @param string $sortorder SORT_ASC or SORT_DESC + * @param array $excludefiles Array of Regex for exclude filter (example: array('(\.meta|_preview\.png)$','^\.')) + * @return array Array of array( filefolder=> array( filelabel=> array( file=> array('name'=>'xxx','date'=>'yyy','size'=>99,'type'=>'dir|file')))) + */ +function get_soc_file_array($fk_soc, $module_get = false, $sortorder = false, $excludefiles = false) +{ + global $user, $conf, $db; + + $sortfield = "date"; + + if(!$sortorder){ + $sorting = SORT_DESC; + }else{ + $sorting = $sortorder; + } + + $ar_modules_get = array(); + if (is_array($module_get)) $ar_modules_get = $module_get; + elseif (strlen($module_get) > 0) $ar_modules_get[$module_get] = $module_get; + else + { + $ar_modules_get['company'] = 'company'; + $ar_modules_get['dolimail'] = 'dolimail'; + $ar_modules_get['actions'] = 'actions'; + $ar_modules_get['invoice'] = 'invoice'; + $ar_modules_get['order'] = 'order'; + $ar_modules_get['propal'] = 'propal'; + $ar_modules_get['contract'] = 'contract'; + $ar_modules_get['project'] = 'project'; + $ar_modules_get['invoice_supplier'] = 'invoice_supplier'; + $ar_modules_get['order_supplier'] = 'order_supplier'; + } + + + // rights + if (count($ar_modules_get) > 0) + foreach($ar_modules_get as $curmodule) + { + switch($curmodule) + { + case 'company': + if (! empty($conf->societe->enabled)) // Recht Alle oder nur die Vertriebspartneradressen + $ar_modules_secure['company']['outputdir'] = $conf->societe->dir_output; + break; + case 'dolimail': + if (! empty($conf->dolimail->enabled) && ($user->rights->dolimail->read || $user->admin)) + $ar_modules_secure[$curmodule]['outputdir']=$conf->dolimail->dir_output.'/attachments'; + break; + case 'actions': + if (! empty($conf->agenda->enabled) || ($user->rights->agenda->allactions->read || $user->admin)) + $ar_modules_secure[$curmodule]['outputdir']=$conf->agenda->dir_output; + break; + case 'invoice': + if (! empty($conf->facture->enabled) && ($user->rights->facture->lire || $user->admin)) + $ar_modules_secure[$curmodule]['outputdir']=$conf->facture->dir_output; + break; + case 'order': + if (!empty($conf->commande->enabled) && ($user->rights->commande->lire || $user->admin)) + $ar_modules_secure[$curmodule]['outputdir']=$conf->commande->dir_output; + break; + case 'propal': + if (!empty($conf->propal->enabled) && ($user->rights->propale->lire || $user->admin)) + $ar_modules_secure[$curmodule]['outputdir']=$conf->propal->dir_output; + break; + case 'project': + if (! empty($conf->projet->enabled) && ($user->rights->projet->lire || $user->admin)) + $ar_modules_secure[$curmodule]['outputdir']=$conf->projet->dir_output; + break; + case 'invoice_supplier': + if (! empty($conf->fournisseur->enabled) && ($user->rights->fournisseur->facture->lire || $user->admin)) + $ar_modules_secure[$curmodule]['outputdir']=$conf->fournisseur->dir_output.'/facture'; + break; + case 'order_supplier': + if (! empty($conf->fournisseur->enabled) && ($user->rights->fournisseur->commande->lire || $user->admin)) + $ar_modules_secure[$curmodule]['outputdir']=$conf->fournisseur->dir_output.'/commande'; + break; + } + } + /* TODO make a outputdir*/ + //unset($ar_modules_secure['dolimail']); + unset($ar_modules_secure['project']); // project (list with project) is "ref" + unset($ar_modules_secure['actions']); + unset($ar_modules_secure['contract']); + /* TODO make a outputdir*/ + unset($curmodule); + + if($fk_soc > 0) + $ar_modules_get = $ar_modules_secure; + + $xy=0; + if (count($ar_modules_get)>0) + foreach($ar_modules_get as $curmodule => $myarray) + { + if($fk_soc > 0 && $curmodule != "company") + { + + // SQL to find documents (ref number) + + if($curmodule == "invoice") $sql = "SELECT facnumber as refstr FROM ".MAIN_DB_PREFIX."facture"; + elseif($curmodule == "order") $sql = "SELECT ref as refstr FROM ".MAIN_DB_PREFIX."commande"; + elseif($curmodule == "invoice_supplier") $sql = "SELECT rowid as refstr FROM ".MAIN_DB_PREFIX."facture_fourn"; + elseif($curmodule == "order_supplier") $sql = "SELECT ref as refstr FROM ".MAIN_DB_PREFIX."commande_fournisseur"; + elseif($curmodule == "propal") $sql = "SELECT ref as refstr FROM ".MAIN_DB_PREFIX."propal"; + elseif($curmodule == "contract") $sql = "SELECT ref as refstr FROM ".MAIN_DB_PREFIX."contrat"; + elseif($curmodule == "dolimail") $sql = "SELECT uid as refstr, subject FROM ".MAIN_DB_PREFIX."mails"; + + $sql.= ' WHERE entity IN ('.getEntity('societe', 1).')'; + $sql.= " AND fk_soc = '".$fk_soc."'"; + + + $res = $db->query($sql); + if ($res && $db->num_rows($res) > 0) + { + while($obj = $db->fetch_object($res)) + { + $ar_modules_secure[$curmodule]['socref'][] = $obj->refstr; + if($curmodule == "dolimail") $ar_modules_secure['dolimail']['subject'][$obj->refstr] = $obj->subject; + } + } + else + { + unset($ar_modules_secure[$curmodule]); + continue; + $errors[]="SQL Error: ".$sql; + $error++; + } + + + }else{ + if($curmodule == "dolimail") $sql = "SELECT uid as refstr, subject FROM ".MAIN_DB_PREFIX."mails"; + + $res = $db->query($sql); + if ($res && $db->num_rows($res) > 0) + { + while($obj = $db->fetch_object($res)) + { + if($curmodule == "dolimail") $ar_modules_secure['dolimail']['subject'][$obj->refstr] = $obj->subject; + } + } + } + + // Data in Array + // Get Array from ar_module + + $output[$curmodule]=dol_dir_list($myarray['outputdir'],"files",1,'', $excludefiles, $sortfield, $sorting,1); + if($fk_soc > 0) + { + if($curmodule == "company") + { + foreach($output["company"] as $label => $filedata) + { + if($filedata['level1name'] != $fk_soc) + { + unset($output['company'][$label]); + } + } + } + + elseif($curmodule == "invoice") + { + if (! is_array($ar_modules_secure[$curmodule]['socref'])) { unset($output[$curmodule]); continue; } // wenn no file exsit + foreach($output["invoice"] as $label => $filedata) + { + if (! in_array($filedata['level1name'], $ar_modules_secure[$curmodule]['socref'])) + { + unset($output[$curmodule][$label]); // throw all ref number who are not in ($fac_supp_N_arr) array + } + + } + } + elseif($curmodule == "invoice_supplier") + { + if (! is_array($ar_modules_secure[$curmodule]['socref'])) { unset($output[$curmodule]); } // throw all ref number who are not in ($fac_invoice_arr) array + foreach($output["invoice_supplier"] as $label => $filedata) + { + if (! in_array($filedata['level1name'], $ar_modules_secure[$curmodule]['socref'])) { + unset($output[$curmodule][$label]); // throw all ref number who are not in ($fac_supp_N_arr) array + } + } + } + elseif($curmodule == "order") + { + if (! is_array($ar_modules_secure[$curmodule]['socref'])) { unset($output[$curmodule]); continue; } // throw all ref number who are not in ($fac_invoice_arr) array + foreach($output["order"] as $label => $filedata) + { + if (! in_array($filedata['level1name'], $ar_modules_secure[$curmodule]['socref'])) { + unset($output[$curmodule][$label]); // throw all ref number who are not in ($ref_order_arr) array + } + } + } + elseif($curmodule == "order_supplier") + { + if (! is_array($ar_modules_secure[$curmodule]['socref'])) { unset($output[$curmodule]); continue; } // throw all ref number who are not in ($fac_invoice_arr) array + foreach($output["order_supplier"] as $label => $filedata) + { + if (! in_array($filedata['level1name'], $ar_modules_secure[$curmodule]['socref'])) { + unset($output[$curmodule][$label]); // throw all ref number who are not in ($ref_order_supp_arr) array + } + } + } + elseif($curmodule == "propal") + { + if (! is_array($ar_modules_secure[$curmodule]['socref'])) { unset($output[$curmodule]); continue; } // throw all ref number who are not in ($fac_invoice_arr) array + foreach($output["propal"] as $label => $filedata) + { + if (! in_array($filedata['level1name'], $ar_modules_secure[$curmodule]['socref'])) { + unset($output[$curmodule][$label]); // throw all ref number who are not in ($ref_propal_arr) array + } + } + } + elseif($curmodule == "contract") + { + if (! is_array($ar_modules_secure[$curmodule]['socref'])) { unset($output[$curmodule]); continue; } // throw all ref number who are not in ($fac_invoice_arr) array + foreach($output["contract"] as $label => $filedata) + { + if (! in_array($filedata['level1name'], $ar_modules_secure[$curmodule]['socref'])) { + unset($output[$curmodule][$label]); // throw all ref number who are not in ($ref_contract_arr) array + } + } + } + elseif($curmodule == "dolimail") + { + + if (! is_array($ar_modules_secure[$curmodule]['socref'])) { unset($output[$curmodule]); continue; } // throw all ref number who are not in ($fac_invoice_arr) array + foreach($output["dolimail"] as $label => $filedata) + { + if($filedata['name'] == "winmail.dat" || $filedata['name'] == "smime.p7s") unset($output[$curmodule][$label]); + else + if (! in_array($filedata['level1name'], $ar_modules_secure[$curmodule]['socref'])) { + unset($output[$curmodule][$label]); // throw all ref number who are not in ($ref_propal_arr) array + } + } + } + // Error if ther isn't any File + if(count($output[$curmodule]) == 0) + { + $error++; + $errors[]="Error [404]: No File found for User: ".$fk_soc." in module: ".$curmodule; + unset($output[$curmodule]); + } + } + + // Extra for Mail attachments + if($curmodule == "dolimail" && count($output["dolimail"])>0) + { + foreach($output["dolimail"] as $label => $filedata) + { + $output[$curmodule][$label]['subject'] = $ar_modules_secure['dolimail']['subject'][$filedata['level1name']]; + } + } + + // Extra for Mail attachments + if($curmodule == "invoice" && count($output["invoice"])>0) + { + foreach($output["invoice"] as $label => $filedata) + { + $output[$curmodule][$label]['subject'] = $filedata['level1name']; + } + } + + // Extra for Mail attachments + if($curmodule == "invoice_supplier" && count($output["invoice_supplier"])>0) + { + foreach($output["invoice_supplier"] as $label => $filedata) + { + $output[$curmodule][$label]['subject'] = $filedata['level1name']; + } + } + + // Extra for Mail attachments + if($curmodule == "contract" && count($output["contract"])>0) + { + foreach($output["contract"] as $label => $filedata) + { + $output[$curmodule][$label]['subject'] = $filedata['level1name']; + } + } + + $xy++; + } + + if(count($output) > 0) + { + return $output; + } + else + { + return -1; + } +} + +/** + * Calculate Bytes to kb, mb and translate it to current language + * + * @param int $byt Bytes + * @return string calculated string + */ +function calculate_byte($byt) +{ + global $langs; + + if ($byt < 1024) { + $unit = ' '.$langs->trans("b"); + $mailsize=$byt; + } else if ($byt / 1024 > 1024) { + $mailsize = $byt / 1024 / 1024; + $unit = ' '.$langs->trans("Mb"); + } else { + $mailsize = $byt / 1024; + $unit = ' '.$langs->trans("Kb"); + } + + $val = number_format($mailsize, 2).$unit; + + return $val; +} + /** * Fast compare of 2 files identified by their properties ->name, ->date and ->size * diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index 62170e7d277..8ac512856e4 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -142,14 +142,14 @@ function getEntity($element=false, $shared=0) */ function getBrowserInfo($user_agent) { - include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetect.class.php'; + include_once DOL_DOCUMENT_ROOT.'/includes/mobiledetect/mobiledetectlib/Mobile_Detect.php'; $name='unknown'; $version=''; $os='unknown'; $phone = ''; - $detectmobile = new MobileDetect(null, $user_agent); + $detectmobile = new Mobile_Detect(null, $user_agent); $tablet = $detectmobile->isTablet(); if ($detectmobile->isMobile()) { diff --git a/htdocs/core/lib/pdf.lib.php b/htdocs/core/lib/pdf.lib.php index da5be961519..8beae962d09 100644 --- a/htdocs/core/lib/pdf.lib.php +++ b/htdocs/core/lib/pdf.lib.php @@ -676,7 +676,8 @@ function pdf_bank(&$pdf,$outputlangs,$curx,$cury,$account,$onlynumber=0,$default if ($account->getCountryCode() == 'IN') $ibankey="IFSC"; if (! empty($account->iban)) { - $ibanDisplay_temp = $outputlangs->convToOutputCharset($account->iban); + //Remove whitespaces to ensure we are dealing with the format we expect + $ibanDisplay_temp = str_replace(' ', '', $outputlangs->convToOutputCharset($account->iban)); $ibanDisplay = ""; for($i = 0; $i < dol_strlen($ibanDisplay_temp); $i++) diff --git a/htdocs/core/lib/report.lib.php b/htdocs/core/lib/report.lib.php index 79ab5fe0ead..8946449d5ac 100644 --- a/htdocs/core/lib/report.lib.php +++ b/htdocs/core/lib/report.lib.php @@ -61,7 +61,7 @@ function report_header($nom,$variante,$period,$periodlink,$description,$builddat // Ligne de titre print ''; - print ''.$langs->trans("ReportName").''; + print ''.$langs->trans("ReportName").''; if (! $variantexxx) print ''; else print ''; print $nom; @@ -73,7 +73,7 @@ function report_header($nom,$variante,$period,$periodlink,$description,$builddat if ($calcmode) { print ''; - print ''.$langs->trans("CalculationMode").''; + print ''.$langs->trans("CalculationMode").''; if (! $variante) print ''; else print ''; print $calcmode; @@ -94,7 +94,7 @@ function report_header($nom,$variante,$period,$periodlink,$description,$builddat // Ligne de description print ''; - print ''.$langs->trans("ReportDescription").''; + print ''.$langs->trans("ReportDescription").''; print ''.$description.''; print ''; diff --git a/htdocs/core/modules/DolibarrModules.class.php b/htdocs/core/modules/DolibarrModules.class.php index de2870d7a9f..d61296f0cc1 100644 --- a/htdocs/core/modules/DolibarrModules.class.php +++ b/htdocs/core/modules/DolibarrModules.class.php @@ -32,7 +32,7 @@ * * Parent class for module descriptor class files */ -abstract class DolibarrModules +class DolibarrModules // Can not be abstract, because we need to instantiant it into unActivateModule to be able to disable a module whose files were removed. { /** * @var DoliDb Database handler @@ -194,16 +194,21 @@ abstract class DolibarrModules */ public $core_enabled; + /** * Constructor. Define names, constants, directories, boxes, permissions * * @param DoliDB $db Database handler */ - //public function __construct($db); // We should but can't set this as abstract because this will make dolibarr hang // after migration due to old module not implementing. We must wait PHP is able to make // a try catch on Fatal error to manage this correctly. - + function __construct($db) + { + $this->db=$db; + } + + /** * Enables a module. * Inserts all informations into database diff --git a/htdocs/core/modules/commande/doc/pdf_einstein.modules.php b/htdocs/core/modules/commande/doc/pdf_einstein.modules.php index 20a80cb8be1..cc1e6aeedca 100644 --- a/htdocs/core/modules/commande/doc/pdf_einstein.modules.php +++ b/htdocs/core/modules/commande/doc/pdf_einstein.modules.php @@ -1325,9 +1325,11 @@ class pdf_einstein extends ModelePDFCommandes $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } diff --git a/htdocs/core/modules/commande/doc/pdf_proforma.modules.php b/htdocs/core/modules/commande/doc/pdf_proforma.modules.php index 5afc0fe5c46..603f151fbfd 100644 --- a/htdocs/core/modules/commande/doc/pdf_proforma.modules.php +++ b/htdocs/core/modules/commande/doc/pdf_proforma.modules.php @@ -1217,9 +1217,11 @@ class pdf_proforma extends ModelePDFCommandes $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } diff --git a/htdocs/core/modules/contract/doc/pdf_strato.modules.php b/htdocs/core/modules/contract/doc/pdf_strato.modules.php index 150529ae30d..21944afad4f 100644 --- a/htdocs/core/modules/contract/doc/pdf_strato.modules.php +++ b/htdocs/core/modules/contract/doc/pdf_strato.modules.php @@ -647,9 +647,11 @@ class pdf_strato extends ModelePDFContract $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $this->recipient->name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($this->recipient->name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } } diff --git a/htdocs/core/modules/expedition/doc/pdf_merou.modules.php b/htdocs/core/modules/expedition/doc/pdf_merou.modules.php index 3f4315f640c..107dbb783b9 100644 --- a/htdocs/core/modules/expedition/doc/pdf_merou.modules.php +++ b/htdocs/core/modules/expedition/doc/pdf_merou.modules.php @@ -650,9 +650,11 @@ class pdf_merou extends ModelePdfExpedition $pdf->SetXY($blDestX,$Yoff); $pdf->MultiCell($blW,3, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information - $pdf->SetFont('','', $default_font_size - 3); - $pdf->SetXY($blDestX,$Yoff+(dol_nboflines_bis($carac_client_name,50)*4)); - $pdf->MultiCell($blW,2, $carac_client, 0, 'L'); + $pdf->SetFont('','', $default_font_size - 1); + $pdf->SetXY($posx+2,$posy); + $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } } diff --git a/htdocs/core/modules/expedition/doc/pdf_rouget.modules.php b/htdocs/core/modules/expedition/doc/pdf_rouget.modules.php index 5123f713a6e..e90e11a952d 100644 --- a/htdocs/core/modules/expedition/doc/pdf_rouget.modules.php +++ b/htdocs/core/modules/expedition/doc/pdf_rouget.modules.php @@ -694,9 +694,11 @@ class pdf_rouget extends ModelePdfExpedition $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } diff --git a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php index a38d64ba826..48be2c907c8 100644 --- a/htdocs/core/modules/facture/doc/pdf_crabe.modules.php +++ b/htdocs/core/modules/facture/doc/pdf_crabe.modules.php @@ -958,9 +958,9 @@ class pdf_crabe extends ModelePDFFactures // If payment mode not forced or forced to VIR, show payment with BAN if (empty($object->mode_reglement_code) || $object->mode_reglement_code == 'VIR') { - if (! empty($object->fk_bank) || ! empty($conf->global->FACTURE_RIB_NUMBER)) + if (! empty($object->fk_account) || ! empty($conf->global->FACTURE_RIB_NUMBER)) { - $bankid=(empty($object->fk_bank)?$conf->global->FACTURE_RIB_NUMBER:$object->fk_bank); + $bankid=(empty($object->fk_account)?$conf->global->FACTURE_RIB_NUMBER:$object->fk_account); $account = new Account($this->db); $account->fetch($bankid); @@ -1611,11 +1611,13 @@ class pdf_crabe extends ModelePDFFactures // Show recipient name $pdf->SetXY($posx+2,$posy+3); $pdf->SetFont('','B', $default_font_size); - $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $pdf->MultiCell($widthrecbox, 2, $carac_client_name, 0, 'L'); + + $posy = $pdf->getY(); // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } diff --git a/htdocs/core/modules/fichinter/doc/pdf_soleil.modules.php b/htdocs/core/modules/fichinter/doc/pdf_soleil.modules.php index 116cab79185..98485be3207 100644 --- a/htdocs/core/modules/fichinter/doc/pdf_soleil.modules.php +++ b/htdocs/core/modules/fichinter/doc/pdf_soleil.modules.php @@ -641,9 +641,11 @@ class pdf_soleil extends ModelePDFFicheinter $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } } diff --git a/htdocs/core/modules/livraison/doc/pdf_typhon.modules.php b/htdocs/core/modules/livraison/doc/pdf_typhon.modules.php index b6f674e424d..747401be88c 100644 --- a/htdocs/core/modules/livraison/doc/pdf_typhon.modules.php +++ b/htdocs/core/modules/livraison/doc/pdf_typhon.modules.php @@ -875,9 +875,11 @@ class pdf_typhon extends ModelePDFDeliveryOrder $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } diff --git a/htdocs/core/modules/propale/doc/pdf_azur.modules.php b/htdocs/core/modules/propale/doc/pdf_azur.modules.php index c3da51fc4c4..d54b175750c 100644 --- a/htdocs/core/modules/propale/doc/pdf_azur.modules.php +++ b/htdocs/core/modules/propale/doc/pdf_azur.modules.php @@ -1517,9 +1517,11 @@ class pdf_azur extends ModelePDFPropales $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } diff --git a/htdocs/core/modules/societe/doc/doc_generic_odt.modules.php b/htdocs/core/modules/societe/doc/doc_generic_odt.modules.php index 90ced6322b7..8a274f91ac7 100644 --- a/htdocs/core/modules/societe/doc/doc_generic_odt.modules.php +++ b/htdocs/core/modules/societe/doc/doc_generic_odt.modules.php @@ -408,8 +408,18 @@ class doc_generic_odt extends ModeleThirdPartyDoc } } else { - try { - $odfHandler->saveToDisk($file); + try { + $odfHandler->creator = $user->getFullName($outputlangs); + $odfHandler->title = $object->builddoc_filename; + $odfHandler->subject = $object->builddoc_filename; + + if (! empty($conf->global->ODT_ADD_DOLIBARR_ID)) + { + $odfHandler->userdefined['dol_id'] = $object->id; + $odfHandler->userdefined['dol_element'] = $object->element; + } + + $odfHandler->saveToDisk($file); }catch (Exception $e){ $this->error=$e->getMessage(); return -1; diff --git a/htdocs/core/modules/supplier_invoice/pdf/pdf_canelle.modules.php b/htdocs/core/modules/supplier_invoice/pdf/pdf_canelle.modules.php index 9445d71ce8a..265d43ca1fc 100644 --- a/htdocs/core/modules/supplier_invoice/pdf/pdf_canelle.modules.php +++ b/htdocs/core/modules/supplier_invoice/pdf/pdf_canelle.modules.php @@ -1132,9 +1132,11 @@ class pdf_canelle extends ModelePDFSuppliersInvoices $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } } diff --git a/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php b/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php index 8e126dcd18d..b2ca0ee0a60 100644 --- a/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php +++ b/htdocs/core/modules/supplier_order/pdf/pdf_muscadet.modules.php @@ -1184,9 +1184,11 @@ class pdf_muscadet extends ModelePDFSuppliersOrders $pdf->SetFont('','B', $default_font_size); $pdf->MultiCell($widthrecbox, 4, $carac_client_name, 0, 'L'); + $posy = $pdf->getY(); + // Show recipient information $pdf->SetFont('','', $default_font_size - 1); - $pdf->SetXY($posx+2,$posy+4+(dol_nboflines_bis($carac_client_name,50)*4)); + $pdf->SetXY($posx+2,$posy); $pdf->MultiCell($widthrecbox, 4, $carac_client, 0, 'L'); } } diff --git a/htdocs/core/modules/syslog/mod_syslog_chromephp.php b/htdocs/core/modules/syslog/mod_syslog_chromephp.php index 9ea39caaf50..e47a0059828 100644 --- a/htdocs/core/modules/syslog/mod_syslog_chromephp.php +++ b/htdocs/core/modules/syslog/mod_syslog_chromephp.php @@ -51,10 +51,11 @@ class mod_syslog_chromephp extends LogHandler implements LogHandlerInterface global $conf; try { - if (empty($conf->global->SYSLOG_CHROMEPHP_INCLUDEPATH)) $conf->global->SYSLOG_CHROMEPHP_INCLUDEPATH='/usr/share/php'; + if (empty($conf->global->SYSLOG_CHROMEPHP_INCLUDEPATH)) { + $conf->global->SYSLOG_CHROMEPHP_INCLUDEPATH = DOL_DOCUMENT_ROOT . '/includes/ccampbell/chromephp/'; + } set_include_path($conf->global->SYSLOG_CHROMEPHP_INCLUDEPATH); - //print 'rrrrr'.get_include_path(); $res = @include_once('ChromePhp.php'); if (! $res) $res=@include_once('ChromePhp.class.php'); @@ -86,9 +87,9 @@ class mod_syslog_chromephp extends LogHandler implements LogHandlerInterface array( 'name' => $langs->trans('IncludePath','SYSLOG_CHROMEPHP_INCLUDEPATH'), 'constant' => 'SYSLOG_CHROMEPHP_INCLUDEPATH', - 'default' => '/usr/share/php', + 'default' => DOL_DOCUMENT_ROOT . '/includes/ccampbell/chromephp/', 'attr' => 'size="60"', - 'example' => DOL_DOCUMENT_ROOT.'/includes/chromephp' + 'example' =>'/usr/share/php' ) ); } diff --git a/htdocs/core/modules/syslog/mod_syslog_sentry.php b/htdocs/core/modules/syslog/mod_syslog_sentry.php new file mode 100644 index 00000000000..3625405aeaa --- /dev/null +++ b/htdocs/core/modules/syslog/mod_syslog_sentry.php @@ -0,0 +1,187 @@ + + * Copyright (C) 2004-2009 Laurent Destailleur + * Copyright (C) 2015 Raphaël Doursenaud + * + * 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 . + */ + +require_once DOL_DOCUMENT_ROOT . '/includes/autoload.php'; +require_once DOL_DOCUMENT_ROOT . '/core/modules/syslog/logHandler.php'; + +/** + * Class to manage logging to Sentry + * + * @see https://docs.getsentry.com/on-premise/clients/php/ + */ +class mod_syslog_sentry extends LogHandler implements LogHandlerInterface +{ + /** + * @var string Log handler code + */ + public $code = 'sentry'; + + /** + * Return name of logger + * + * @return string Name of logger + */ + public function getName() + { + return 'Sentry'; + } + + /** + * Version of the module ('x.y.z' or 'dolibarr' or 'experimental' or 'development') + * + * @return string + */ + public function getVersion() + { + return 'dolibarr'; + } + + /** + * Content of the info tooltip. + * + * @return false|string + */ + public function getInfo() + { + global $langs; + return $langs->trans('SyslogSentryFromProject'); + } + + /** + * Is the module active ? + * + * @return int + */ + public function isActive() + { + return 1; + } + + /** + * Return array of configuration data + * + * @return array Return array of configuration data + */ + public function configure() + { + global $langs; + return array( + array( + 'constant' => 'SYSLOG_SENTRY_DSN', + 'name' => $langs->trans('SyslogSentryDSN'), + 'default' => '', + 'attr' => 'size="100" placeholder="https://:@app.getsentry.com/"' + ) + ); + } + + /** + * Return if configuration is valid + * + * @return array Array of errors. Empty array if ok. + */ + public function checkConfiguration() + { + global $conf; + + $errors = array(); + + $dsn = $conf->global->SYSLOG_SENTRY_DSN; + + try { + $client = new Raven_Client( + $dsn, + array('curl_method' => 'sync') + ); + } catch (InvalidArgumentException $ex) { + $errors[] = "ERROR: There was an error parsing your DSN:\n " . $ex->getMessage(); + } + + if (!$errors) { + // Send test event and check for errors + $client->captureMessage('TEST: Sentry syslog configuration check', null, Raven_Client::DEBUG); + $last_error = $client->getLastError(); + if ($last_error) { + $errors[] = $last_error; + } + } + + if (!$errors) { + // Install handlers + $error_handler = new Raven_ErrorHandler($client); + $error_handler->registerExceptionHandler(); + $error_handler->registerErrorHandler(); + $error_handler->registerShutdownFunction(); + } + + return $errors; + } + + /** + * Export the message + * + * @param array $content Array containing the info about the message + * @return void + */ + public function export($content) + { + global $conf; + $dsn = $conf->global->SYSLOG_SENTRY_DSN; + $client = new Raven_Client( + $dsn, + array('curl_method' => 'exec') + ); + + $client->user_context(array( + 'username' => ($content['user'] ? $content['user'] : ''), + 'ip_address' => $content['ip'] + )); + + $client->tags_context(array( + 'version' => DOL_VERSION + )); + + $client->registerSeverityMap(array( + LOG_EMERG => Raven_Client::FATAL, + LOG_ALERT => Raven_Client::FATAL, + LOG_CRIT => Raven_Client::ERROR, + LOG_ERR => Raven_Client::ERROR, + LOG_WARNING => Raven_Client::WARNING, + LOG_NOTICE => Raven_Client::WARNING, + LOG_INFO => Raven_Client::INFO, + LOG_DEBUG => Raven_Client::DEBUG, + )); + + if (substr($content['message'], 0, 3) === 'sql') { + global $db; + $query = substr($content['message'], 4, strlen($content['message'])); + $client->captureQuery( + $query, + $client->translateSeverity($content['level']), + $db->type + ); + } else { + $client->captureMessage( + $content['message'], + null, + $client->translateSeverity($content['level']) + ); + } + } +} diff --git a/htdocs/expedition/class/expedition.class.php b/htdocs/expedition/class/expedition.class.php index 3dc1d555d28..edd8aa0cd39 100644 --- a/htdocs/expedition/class/expedition.class.php +++ b/htdocs/expedition/class/expedition.class.php @@ -643,7 +643,15 @@ class Expedition extends CommonObject for ($i = 0; $i < $cpt; $i++) { $obj = $this->db->fetch_object($resql); - if ($obj->qty <= 0) continue; + if (empty($obj->edbrowid)) + { + $qty = $obj->qty; + } + else + { + $qty = $obj->edbqty; + } + if ($qty <= 0) continue; dol_syslog(get_class($this)."::valid movement index ".$i." ed.rowid=".$obj->rowid." edb.rowid=".$obj->edbrowid); //var_dump($this->lines[$i]); @@ -655,7 +663,7 @@ class Expedition extends CommonObject // line without batch detail // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record - $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $obj->qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref)); + $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref)); if ($result < 0) { $error++; break; } @@ -665,15 +673,16 @@ class Expedition extends CommonObject // line with batch detail // We decrement stock of product (and sub-products) -> update table llx_product_stock (key of this table is fk_product+fk_entrepot) and add a movement record - $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $obj->edbqty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref), '', $obj->eatby, $obj->sellby, $obj->batch); + $result=$mouvS->livraison($user, $obj->fk_product, $obj->fk_entrepot, $qty, $obj->subprice, $langs->trans("ShipmentValidatedInDolibarr",$numref), '', $obj->eatby, $obj->sellby, $obj->batch); if ($result < 0) { $error++; break; } // We update content of table llx_product_batch (will be rename into llx_product_stock_batch inantoher version) + // We can set livraison_batch to deprecated and adapt livraison to handle batch too (mouvS->_create also calls mouvS->_create_batch) if (! empty($conf->productbatch->enabled)) { - $result=$mouvS->livraison_batch($obj->fk_origin_stock, $obj->qty); // ->fk_origin_stock = id into table llx_product_batch (will be rename into llx_product_stock_batch in another version) + $result=$mouvS->livraison_batch($obj->fk_origin_stock, $qty); // ->fk_origin_stock = id into table llx_product_batch (will be rename into llx_product_stock_batch in another version) if ($result < 0) { $error++; $this->errors[]=$mouvS->error; break; } } } diff --git a/htdocs/filefunc.inc.php b/htdocs/filefunc.inc.php index beead03469c..c591359f424 100644 --- a/htdocs/filefunc.inc.php +++ b/htdocs/filefunc.inc.php @@ -205,11 +205,11 @@ define('MAIN_DB_PREFIX',$dolibarr_main_db_prefix); // Path to root libraries if (! defined('ADODB_PATH')) { define('ADODB_PATH', (!isset($dolibarr_lib_ADODB_PATH))?DOL_DOCUMENT_ROOT.'/includes/adodbtime/':(empty($dolibarr_lib_ADODB_PATH)?'':$dolibarr_lib_ADODB_PATH.'/')); } if (! defined('FPDF_PATH')) { define('FPDF_PATH', (empty($dolibarr_lib_FPDF_PATH))?DOL_DOCUMENT_ROOT.'/includes/fpdf/':$dolibarr_lib_FPDF_PATH.'/'); } // Used only for package that can't include tcpdf -if (! defined('TCPDF_PATH')) { define('TCPDF_PATH', (empty($dolibarr_lib_TCPDF_PATH))?DOL_DOCUMENT_ROOT.'/includes/tcpdf/':$dolibarr_lib_TCPDF_PATH.'/'); } +if (! defined('TCPDF_PATH')) { define('TCPDF_PATH', (empty($dolibarr_lib_TCPDF_PATH))?DOL_DOCUMENT_ROOT.'/includes/tecnick.com/tcpdf/':$dolibarr_lib_TCPDF_PATH.'/'); } if (! defined('FPDI_PATH')) { define('FPDI_PATH', (empty($dolibarr_lib_FPDI_PATH))?DOL_DOCUMENT_ROOT.'/includes/fpdfi/':$dolibarr_lib_FPDI_PATH.'/'); } if (! defined('TCPDI_PATH')) { define('TCPDI_PATH', (empty($dolibarr_lib_TCPDI_PATH))?DOL_DOCUMENT_ROOT.'/includes/tcpdi/':$dolibarr_lib_TCPDI_PATH.'/'); } if (! defined('NUSOAP_PATH')) { define('NUSOAP_PATH', (!isset($dolibarr_lib_NUSOAP_PATH))?DOL_DOCUMENT_ROOT.'/includes/nusoap/lib/':(empty($dolibarr_lib_NUSOAP_PATH)?'':$dolibarr_lib_NUSOAP_PATH.'/')); } -if (! defined('PHPEXCEL_PATH')) { define('PHPEXCEL_PATH', (!isset($dolibarr_lib_PHPEXCEL_PATH))?DOL_DOCUMENT_ROOT.'/includes/phpexcel/':(empty($dolibarr_lib_PHPEXCEL_PATH)?'':$dolibarr_lib_PHPEXCEL_PATH.'/')); } +if (! defined('PHPEXCEL_PATH')) { define('PHPEXCEL_PATH', (!isset($dolibarr_lib_PHPEXCEL_PATH))?DOL_DOCUMENT_ROOT.'/includes/phpoffice/phpexcel/Classes/':(empty($dolibarr_lib_PHPEXCEL_PATH)?'':$dolibarr_lib_PHPEXCEL_PATH.'/')); } if (! defined('GEOIP_PATH')) { define('GEOIP_PATH', (!isset($dolibarr_lib_GEOIP_PATH))?DOL_DOCUMENT_ROOT.'/includes/geoip/':(empty($dolibarr_lib_GEOIP_PATH)?'':$dolibarr_lib_GEOIP_PATH.'/')); } if (! defined('ODTPHP_PATH')) { define('ODTPHP_PATH', (!isset($dolibarr_lib_ODTPHP_PATH))?DOL_DOCUMENT_ROOT.'/includes/odtphp/':(empty($dolibarr_lib_ODTPHP_PATH)?'':$dolibarr_lib_ODTPHP_PATH.'/')); } if (! defined('ODTPHP_PATHTOPCLZIP')) { define('ODTPHP_PATHTOPCLZIP', (!isset($dolibarr_lib_ODTPHP_PATHTOPCLZIP))?DOL_DOCUMENT_ROOT.'/includes/odtphp/zip/pclzip/':(empty($dolibarr_lib_ODTPHP_PATHTOPCLZIP)?'':$dolibarr_lib_ODTPHP_PATHTOPCLZIP.'/')); } diff --git a/htdocs/holiday/card.php b/htdocs/holiday/card.php index 23c78641462..ebda47dd196 100644 --- a/htdocs/holiday/card.php +++ b/htdocs/holiday/card.php @@ -51,11 +51,12 @@ $now=dol_now(); * Actions */ +// If create a request if ($action == 'create') { $cp = new Holiday($db); - // Si pas le droit de créer une demande + // If no right to create a request if (($userid == $user->id && empty($user->rights->holiday->write)) || ($userid != $user->id && empty($user->rights->holiday->write_all))) { $error++; @@ -83,21 +84,21 @@ if ($action == 'create') $description = trim(GETPOST('description')); $userID = GETPOST('userID'); - // Si pas de date de début + // If no start date if (empty($date_debut)) { header('Location: card.php?action=request&error=nodatedebut'); exit; } - // Si pas de date de fin + // If no end date if (empty($date_fin)) { header('Location: card.php?action=request&error=nodatefin'); exit; } - // Si date de début après la date de fin + // If start date after end date if ($date_debut > $date_fin) { header('Location: card.php?action=request&error=datefin'); @@ -112,7 +113,7 @@ if ($action == 'create') exit; } - // Si aucun jours ouvrés dans la demande + // If there is no Business Days within request $nbopenedday=num_open_day($date_debut_gmt, $date_fin_gmt, 0, 1, $halfday); if($nbopenedday < 0.5) { @@ -120,7 +121,7 @@ if ($action == 'create') exit; } - // Si pas de validateur choisi + // If no validator designated if ($valideur < 1) { header('Location: card.php?action=request&error=Valideur'); @@ -137,7 +138,7 @@ if ($action == 'create') $verif = $cp->create($user); - // Si pas d'erreur SQL on redirige vers la fiche de la demande + // If no SQL error we redirect to the request card if ($verif > 0) { $db->commit(); @@ -149,7 +150,7 @@ if ($action == 'create') { $db->rollback(); - // Sinon on affiche le formulaire de demande avec le message d'erreur SQL + // Otherwise we display the request form with the SQL error message header('Location: card.php?action=request&error=SQL_Create&msg='.$cp->error); exit; } @@ -169,7 +170,7 @@ if ($action == 'update') else if ($starthalfday == 'afternoon') $halfday=-1; else if ($endhalfday == 'morning') $halfday=1; - // Si pas le droit de modifier une demande + // If no right to modify a request if (! $user->rights->holiday->write) { header('Location: card.php?action=request&error=CantUpdate'); @@ -181,40 +182,40 @@ if ($action == 'update') $canedit=(($user->id == $cp->fk_user && $user->rights->holiday->write) || ($user->id != $cp->fk_user && $user->rights->holiday->write_all)); - // Si en attente de validation + // If under validation if ($cp->statut == 1) { - // Si c'est le créateur ou qu'il a le droit de tout lire / modifier + // If this is the requestor or has read/write rights if ($canedit) { $valideur = $_POST['valideur']; $description = trim($_POST['description']); - // Si pas de date de début + // If no start date if (empty($_POST['date_debut_'])) { header('Location: card.php?id='.$_POST['holiday_id'].'&action=edit&error=nodatedebut'); exit; } - // Si pas de date de fin + // If no end date if (empty($_POST['date_fin_'])) { header('Location: card.php?id='.$_POST['holiday_id'].'&action=edit&error=nodatefin'); exit; } - // Si date de début après la date de fin + // If start date after end date if ($date_debut > $date_fin) { header('Location: card.php?id='.$_POST['holiday_id'].'&action=edit&error=datefin'); exit; } - // Si pas de valideur choisi + // If no validator designated if ($valideur < 1) { header('Location: card.php?id='.$_POST['holiday_id'].'&action=edit&error=Valideur'); exit; } - // Si pas de jours ouvrés dans la demande + // If there is no Business Days within request $nbopenedday=num_open_day($date_debut_gmt, $date_fin_gmt, 0, 1, $halfday); if ($nbopenedday < 0.5) { @@ -237,7 +238,7 @@ if ($action == 'update') } else { - // Sinon on affiche le formulaire de demande avec le message d'erreur SQL + // Otherwise we display the request form with the SQL error message header('Location: card.php?id='.$_POST['holiday_id'].'&action=edit&error=SQL_Create&msg='.$cp->error); exit; } @@ -248,7 +249,7 @@ if ($action == 'update') } } -// Si suppression de la demande +// If delete of request if ($action == 'confirm_delete' && GETPOST('confirm') == 'yes' && $user->rights->holiday->delete) { $error=0; @@ -260,7 +261,7 @@ if ($action == 'confirm_delete' && GETPOST('confirm') == 'yes' && $user->rights- $canedit=(($user->id == $cp->fk_user && $user->rights->holiday->write) || ($user->id != $cp->fk_user && $user->rights->holiday->write_all)); - // Si c'est bien un brouillon + // If this is a rough draft if ($cp->statut == 1 || $cp->statut == 3) { // Si l'utilisateur à le droit de lire cette demande, il peut la supprimer diff --git a/htdocs/includes/autoload.php b/htdocs/includes/autoload.php new file mode 100644 index 00000000000..be5223ff062 --- /dev/null +++ b/htdocs/includes/autoload.php @@ -0,0 +1,7 @@ +//samples/index.html - -For example: - - http://www.example.com/ckeditor/samples/index.html diff --git a/htdocs/includes/ckeditor/_source/CHANGES.md b/htdocs/includes/ckeditor/_source/CHANGES.md deleted file mode 100644 index a5bec2ba830..00000000000 --- a/htdocs/includes/ckeditor/_source/CHANGES.md +++ /dev/null @@ -1,442 +0,0 @@ -CKEditor 4 Changelog -==================== - -## CKEditor 4.3.3 - -Fixed Issues: - -* [#11500](http://dev.ckeditor.com/ticket/11500): [Webkit/Blink] Fixed: Selection lost when setting data in another inline editor. Additionally, [`selection.removeAllRanges()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-removeAllRanges) is now scoped to selection's [root](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-property-root). -* [#11104](http://dev.ckeditor.com/ticket/11104): [IE] Fixed: Various issues with scrolling and selection when focusing widgets. -* [#11487](http://dev.ckeditor.com/ticket/11487): Moving mouse over the [Enhanced Image](http://ckeditor.com/addon/image2) widget will no longer change the value returned by the [`editor.checkDirty()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-checkDirty) method. -* [#8673](http://dev.ckeditor.com/ticket/8673): [WebKit] Fixed: Cannot select and remove the [Page Break](http://ckeditor.com/addon/pagebreak). -* [#11413](http://dev.ckeditor.com/ticket/11413): Fixed: Incorrect [`editor.execCommand()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-execCommand) behavior. -* [#11438](http://dev.ckeditor.com/ticket/11438): Splitting table cells vertically is no longer changing table structure. -* [#8899](http://dev.ckeditor.com/ticket/8899): Fixed: Links in the [About CKEditor](http://ckeditor.com/addon/about) dialog window now open in a new browser window or tab. -* [#11490](http://dev.ckeditor.com/ticket/11490): Fixed: [Menu button](http://ckeditor.com/addon/menubutton) panel not showing in the source mode. -* [#11417](http://dev.ckeditor.com/ticket/11417): The [`widget.doubleclick`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget-event-doubleclick) event is not canceled anymore after editing was triggered. -* [#11253](http://dev.ckeditor.com/ticket/11253): [IE] Fixed: Clipped upload button in the [Enhanced Image](http://ckeditor.com/addon/image2) dialog window. -* [#11359](http://dev.ckeditor.com/ticket/11359): Standardized the way anchors are discovered by the [Link](http://ckeditor.com/addon/link) plugin. -* [#11058](http://dev.ckeditor.com/ticket/11058): [IE8] Fixed: Error when deleting a table row. -* [#11508](http://dev.ckeditor.com/ticket/11508): Fixed: [`htmlDataProcessor`](http://docs.ckeditor.com/#!/api/CKEDITOR.htmlDataProcessor) discovering protected attributes within other attributes' values. -* [#11533](http://dev.ckeditor.com/ticket/11533): Widgets: Avoid recurring upcasts if the DOM structure was modified during an upcast. -* [#11400](http://dev.ckeditor.com/ticket/11400): Fixed: The [`domObject.removeAllListeners()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.domObject-method-removeAllListeners) method does not remove custom listeners completely. -* [#11493](http://dev.ckeditor.com/ticket/11493): Fixed: The [`selection.getRanges()`](http://docs.ckeditor.com/#!/api/CKEDITOR.dom.selection-method-getRanges) method does not override cached ranges when used with the `onlyEditables` argument. -* [#11390](http://dev.ckeditor.com/ticket/11390): [IE] All [XML](http://ckeditor.com/addon/xml) plugin [methods](http://docs.ckeditor.com/#!/api/CKEDITOR.xml) now work in IE10+. -* [#11542](http://dev.ckeditor.com/ticket/11542): [IE11] Fixed: Blurry toolbar icons when Right-to-Left UI language is set. -* [#11504](http://dev.ckeditor.com/ticket/11504): Fixed: When [`config.fullPage`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-fullPage) is set to `true`, entities are not encoded in editor output. -* [#11004](http://dev.ckeditor.com/ticket/11004): Integrated [Enhanced Image](http://ckeditor.com/addon/image2) dialog window with [Advanced Content Filter](http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter). -* [#11439](http://dev.ckeditor.com/ticket/11439): Fixed: Properties get cloned in the Cell Properties dialog window if multiple cells are selected. - -## CKEditor 4.3.2 - -Fixed Issues: - -* [#11331](http://dev.ckeditor.com/ticket/11331): A menu button will have a changed label when selected instead of using the `aria-pressed` attribute. -* [#11177](http://dev.ckeditor.com/ticket/11177): Widget drag handler improvements: - * [#11176](http://dev.ckeditor.com/ticket/11176): Fixed: Initial position is not updated when the widget data object is empty. - * [#11001](http://dev.ckeditor.com/ticket/11001): Fixed: Multiple synchronous layout recalculations are caused by initial drag handler positioning causing performance issues. - * [#11161](http://dev.ckeditor.com/ticket/11161): Fixed: Drag handler is not repositioned in various situations. - * [#11281](http://dev.ckeditor.com/ticket/11281): Fixed: Drag handler and mask are duplicated after widget reinitialization. -* [#11207](http://dev.ckeditor.com/ticket/11207): [Firefox] Fixed: Misplaced [Enhanced Image](http://ckeditor.com/addon/image2) resizer in the inline editor. -* [#11102](http://dev.ckeditor.com/ticket/11102): `CKEDITOR.template` improvements: - * [#11102](http://dev.ckeditor.com/ticket/11102): Added newline character support. - * [#11216](http://dev.ckeditor.com/ticket/11216): Added "\\'" substring support. -* [#11121](http://dev.ckeditor.com/ticket/11121): [Firefox] Fixed: High Contrast mode is enabled when the editor is loaded in a hidden iframe. -* [#11350](http://dev.ckeditor.com/ticket/11350): The default value of [`config.contentsCss`](http://docs.ckeditor.com/#!/api/CKEDITOR.config-cfg-contentsCss) is affected by [`CKEDITOR.getUrl()`](http://docs.ckeditor.com/#!/api/CKEDITOR-method-getUrl). -* [#11097](http://dev.ckeditor.com/ticket/11097): Improved the [Autogrow](http://ckeditor.com/addon/autogrow) plugin performance when dealing with very big tables. -* [#11290](http://dev.ckeditor.com/ticket/11290): Removed redundant code in the [Source Dialog](http://ckeditor.com/addon/sourcedialog) plugin. -* [#11133](http://dev.ckeditor.com/ticket/11133): [Page Break](http://ckeditor.com/addon/pagebreak) becomes editable if pasted. -* [#11126](http://dev.ckeditor.com/ticket/11126): Fixed: Native Undo executed once the bottom of the snapshot stack is reached. -* [#11131](http://dev.ckeditor.com/ticket/11131): [Div Editing Area](http://ckeditor.com/addon/divarea): Fixed: Error thrown when switching to source mode if the selection was in widget's nested editable. -* [#11139](http://dev.ckeditor.com/ticket/11139): [Div Editing Area](http://ckeditor.com/addon/divarea): Fixed: Elements Path is not cleared after switching to source mode. -* [#10778](http://dev.ckeditor.com/ticket/10778): Fixed a bug with range enlargement. The range no longer expands to visible whitespace. -* [#11146](http://dev.ckeditor.com/ticket/11146): [IE] Fixed: Preview window switches Internet Explorer to Quirks Mode. -* [#10762](http://dev.ckeditor.com/ticket/10762): [IE] Fixed: JavaScript code displayed in preview window's URL bar. -* [#11186](http://dev.ckeditor.com/ticket/11186): Introduced the [`widgets.repository.addUpcastCallback()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-addUpcastCallback) method that allows to block upcasting given element to a widget. -* [#11307](http://dev.ckeditor.com/ticket/11307): Fixed: Paste as Plain Text conflict with the [MooTools](http://mootools.net) library. -* [#11140](http://dev.ckeditor.com/ticket/11140): [IE11] Fixed: Anchors are not draggable. -* [#11379](http://dev.ckeditor.com/ticket/11379): Changed default contents `line-height` to unitless values to avoid huge text overlapping (like in [#9696](http://dev.ckeditor.com/ticket/9696)). -* [#10787](http://dev.ckeditor.com/ticket/10787): [Firefox] Fixed: Broken replacement of text while pasting into `div`-based editor. -* [#10884](http://dev.ckeditor.com/ticket/10884): Widgets integration with the [Show Blocks](http://ckeditor.com/addon/showblocks) plugin. -* [#11021](http://dev.ckeditor.com/ticket/11021): Fixed: An error thrown when selecting entire editable contents while fake selection is on. -* [#11086](http://dev.ckeditor.com/ticket/11086): [IE8] Re-enable inline widgets drag&drop in Internet Explorer 8. -* [#11372](http://dev.ckeditor.com/ticket/11372): Widgets: Special characters encoded twice in nested editables. -* [#10068](http://dev.ckeditor.com/ticket/10068): Fixed: Support for protocol-relative URLs. -* [#11283](http://dev.ckeditor.com/ticket/11283): [Enhanced Image](http://ckeditor.com/addon/image2): A `
` element with `text-align: center` and an image inside is not recognised correctly. -* [#11196](http://dev.ckeditor.com/ticket/11196): [Accessibility Instructions](http://ckeditor.com/addon/a11yhelp): Allowed additional keyboard button labels to be translated in the dialog window. - -## CKEditor 4.3.1 - -**Important Notes:** - -* To match the naming convention, the `language` button is now `Language` ([#11201](http://dev.ckeditor.com/ticket/11201)). -* [Enhanced Image](http://ckeditor.com/addon/image2) button, context menu, command, and icon names match those of the [Image](http://ckeditor.com/addon/image) plugin ([#11222](http://dev.ckeditor.com/ticket/11222)). - -Fixed Issues: - -* [#11244](http://dev.ckeditor.com/ticket/11244): Changed: The [`widget.repository.checkWidgets()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-checkWidgets) method now fires the [`widget.repository.checkWidgets`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-event-checkWidgets) event, so from CKEditor 4.3.1 it is preferred to use the method rather than fire the event. -* [#11171](http://dev.ckeditor.com/ticket/11171): Fixed: [`editor.insertElement()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement) and [`editor.insertText()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertText) methods do not call the [`widget.repository.checkWidgets()`](http://docs.ckeditor.com/#!/api/CKEDITOR.plugins.widget.repository-method-checkWidgets) method. -* [#11085](http://dev.ckeditor.com/ticket/11085): [IE8] Replaced preview generated by the [Mathematical Formulas](http://ckeditor.com/addon/mathjax) widget with a placeholder. -* [#11044](http://dev.ckeditor.com/ticket/11044): Enhanced WAI-ARIA support for the [Language](http://ckeditor.com/addon/language) plugin drop-down menu. -* [#11075](http://dev.ckeditor.com/ticket/11075): With drop-down menu button focused, pressing the *Down Arrow* key will now open the menu and focus its first option. -* [#11165](http://dev.ckeditor.com/ticket/11165): Fixed: The [File Browser](http://ckeditor.com/addon/filebrowser) plugin cannot be removed from the editor. -* [#11159](http://dev.ckeditor.com/ticket/11159): [IE9-10] [Enhanced Image](http://ckeditor.com/addon/image2): Fixed buggy discovery of image dimensions. -* [#11101](http://dev.ckeditor.com/ticket/11101): Drop-down lists no longer break when given double quotes. -* [#11077](http://dev.ckeditor.com/ticket/11077): [Enhanced Image](http://ckeditor.com/addon/image2): Empty undo step recorded when resizing the image. -* [#10853](http://dev.ckeditor.com/ticket/10853): [Enhanced Image](http://ckeditor.com/addon/image2): Widget has paragraph wrapper when de-captioning unaligned image. -* [#11198](http://dev.ckeditor.com/ticket/11198): Widgets: Drag handler is not fully visible when an inline widget is in a heading. -* [#11132](http://dev.ckeditor.com/ticket/11132): [Firefox] Fixed: Caret is lost after drag and drop of an inline widget. -* [#11182](http://dev.ckeditor.com/ticket/11182): [IE10-11] Fixed: Editor crashes (IE11) or works with minor issues (IE10) if a page is loaded in Quirks Mode. See [`env.quirks`](http://docs.ckeditor.com/#!/api/CKEDITOR.env-property-quirks) for more details. -* [#11204](http://dev.ckeditor.com/ticket/11204): Added `figure` and `figcaption` styles to the `contents.css` file so [Enhanced Image](http://ckeditor.com/addon/image2) looks nicer. -* [#11202](http://dev.ckeditor.com/ticket/11202): Fixed: No newline in [BBCode](http://ckeditor.com/addon/bbcode) mode. -* [#10890](http://dev.ckeditor.com/ticket/10890): Fixed: Error thrown when pressing the *Delete* key in a list item. -* [#10055](http://dev.ckeditor.com/ticket/10055): [IE8-10] Fixed: *Delete* pressed on a selected image causes the browser to go back. -* [#11183](http://dev.ckeditor.com/ticket/11183): Fixed: Inserting a horizontal rule or a table in multiple row selection causes a browser crash. Additionally, the [`editor.insertElement()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-insertElement) method does not insert the element into every range of a selection any more. -* [#11042](http://dev.ckeditor.com/ticket/11042): Fixed: Selection made on an element containing a non-editable element was not auto faked. -* [#11125](http://dev.ckeditor.com/ticket/11125): Fixed: Keyboard navigation through menu and drop-down items will now cycle. -* [#11011](http://dev.ckeditor.com/ticket/11011): Fixed: The [`editor.applyStyle()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-applyStyle) method removes attributes from nested elements. -* [#11179](http://dev.ckeditor.com/ticket/11179): Fixed: [`editor.destroy()`](http://docs.ckeditor.com/#!/api/CKEDITOR.editor-method-destroy) does not cleanup content generated by the [Table Resize](http://ckeditor.com/addon/tableresize) plugin for inline editors. -* [#11237](http://dev.ckeditor.com/ticket/11237): Fixed: Table border attribute value is deleted when pasting content from Microsoft Word. -* [#11250](http://dev.ckeditor.com/ticket/11250): Fixed: HTML entities inside the ` - * ... - * CKEDITOR.replace( 'myfield' ); - * - * var textarea = document.body.appendChild( document.createElement( 'textarea' ) ); - * CKEDITOR.replace( textarea ); - * - * @param {Object/String} element The DOM element (textarea), its ID, or name. - * @param {Object} [config] The specific configuration to apply to this - * editor instance. Configuration set here will override the global CKEditor settings - * (see {@link CKEDITOR.config}). - * @returns {CKEDITOR.editor} The editor instance created. - */ - CKEDITOR.replace = function( element, config ) { - return createInstance( element, config, null, CKEDITOR.ELEMENT_MODE_REPLACE ); - }; - - /** - * Creates a new editor instance at the end of a specific DOM element. - * - *
- * ... - * CKEDITOR.appendTo( 'editorSpace' ); - * - * @param {Object/String} element The DOM element, its ID, or name. - * @param {Object} [config] The specific configuration to apply to this - * editor instance. Configuration set here will override the global CKEditor settings - * (see {@link CKEDITOR.config}). - * @param {String} [data] Since 3.3. Initial value for the instance. - * @returns {CKEDITOR.editor} The editor instance created. - */ - CKEDITOR.appendTo = function( element, config, data ) - { - return createInstance( element, config, data, CKEDITOR.ELEMENT_MODE_APPENDTO ); - }; - - /** - * Replaces all ` - data = protectElements( data, protectTextareaRegex ); - - // Before anything, we must protect the URL attributes as the - // browser may changing them when setting the innerHTML later in - // the code. - data = protectAttributes( data ); - - // Protect elements than can't be set inside a DIV. E.g. IE removes - // style tags from innerHTML. (#3710) - data = protectElements( data, protectElementsRegex ); - - // Certain elements has problem to go through DOM operation, protect - // them by prefixing 'cke' namespace. (#3591) - data = protectElementsNames( data ); - - // All none-IE browsers ignore self-closed custom elements, - // protecting them into open-close. (#3591) - data = protectSelfClosingElements( data ); - - // Compensate one leading line break after
 open as browsers
-			// eat it up. (#5789)
-			data = protectPreFormatted( data );
-
-			var fixBin = evtData.context || editor.editable().getName(),
-				isPre;
-
-			// Old IEs loose formats when load html into 
.
-			if ( CKEDITOR.env.ie && CKEDITOR.env.version < 9 && fixBin == 'pre' ) {
-				fixBin = 'div';
-				data = '
' + data + '
'; - isPre = 1; - } - - // Call the browser to help us fixing a possibly invalid HTML - // structure. - var el = editor.document.createElement( fixBin ); - // Add fake character to workaround IE comments bug. (#3801) - el.setHtml( 'a' + data ); - data = el.getHtml().substr( 1 ); - - // Restore shortly protected attribute names. - data = data.replace( new RegExp( ' data-cke-' + CKEDITOR.rnd + '-', 'ig' ), ' ' ); - - isPre && ( data = data.replace( /^
|<\/pre>$/gi, '' ) );
-
-			// Unprotect "some" of the protected elements at this point.
-			data = unprotectElementNames( data );
-
-			data = unprotectElements( data );
-
-			// Restore the comments that have been protected, in this way they
-			// can be properly filtered.
-			data = unprotectRealComments( data );
-
-			// Now use our parser to make further fixes to the structure, as
-			// well as apply the filter.
-			evtData.dataValue = CKEDITOR.htmlParser.fragment.fromHtml(
-				data, evtData.context, evtData.fixForBody === false ? false : getFixBodyTag( evtData.enterMode, editor.config.autoParagraph ) );
-		}, null, null, 5 );
-
-		// Filter incoming "data".
-		// Add element filter before htmlDataProcessor.dataFilter when purifying input data to correct html.
-		editor.on( 'toHtml', function( evt ) {
-			if ( evt.data.filter.applyTo( evt.data.dataValue, true, evt.data.dontFilter, evt.data.enterMode ) )
-				editor.fire( 'dataFiltered' );
-		}, null, null, 6 );
-
-		editor.on( 'toHtml', function( evt ) {
-			evt.data.dataValue.filterChildren( that.dataFilter, true );
-		}, null, null, 10 );
-
-		editor.on( 'toHtml', function( evt ) {
-			var evtData = evt.data,
-				data = evtData.dataValue,
-				writer = new CKEDITOR.htmlParser.basicWriter();
-
-			data.writeChildrenHtml( writer );
-			data = writer.getHtml( true );
-
-			// Protect the real comments again.
-			evtData.dataValue = protectRealComments( data );
-		}, null, null, 15 );
-
-
-		editor.on( 'toDataFormat', function( evt ) {
-			var data = evt.data.dataValue;
-
-			// #10854 - we need to strip leading blockless 
which FF adds - // automatically when editable contains only non-editable content. - // We do that for every browser (so it's a constant behavior) and - // not in BR mode, in which chance of valid leading blockless
is higher. - if ( evt.data.enterMode != CKEDITOR.ENTER_BR ) - data = data.replace( /^
/i, '' ); - - evt.data.dataValue = CKEDITOR.htmlParser.fragment.fromHtml( - data, evt.data.context, getFixBodyTag( evt.data.enterMode, editor.config.autoParagraph ) ); - }, null, null, 5 ); - - editor.on( 'toDataFormat', function( evt ) { - evt.data.dataValue.filterChildren( that.htmlFilter, true ); - }, null, null, 10 ); - - // Transform outcoming "data". - // Add element filter after htmlDataProcessor.htmlFilter when preparing output data HTML. - editor.on( 'toDataFormat', function( evt ) { - evt.data.filter.applyTo( evt.data.dataValue, false, true ); - }, null, null, 11 ); - - editor.on( 'toDataFormat', function( evt ) { - var data = evt.data.dataValue, - writer = that.writer; - - writer.reset(); - data.writeChildrenHtml( writer ); - data = writer.getHtml( true ); - - // Restore those non-HTML protected source. (#4475,#4880) - data = unprotectRealComments( data ); - data = unprotectSource( data, editor ); - - evt.data.dataValue = data; - }, null, null, 15 ); - }; - - CKEDITOR.htmlDataProcessor.prototype = { - /** - * Processes the input (potentially malformed) HTML to a purified form which - * is suitable for using in the WYSIWYG editable. - * - * This method fires the {@link CKEDITOR.editor#toHtml} event which makes it possible - * to hook into the process at various stages. - * - * **Note:** Since CKEditor 4.3 the signature of this method changed and all options - * are now grouped in one `options` object. Previously `context`, `fixForBody` and `dontFilter` - * were passed separately. - * - * @param {String} data The raw data. - * @param {Object} [options] The options object. - * @param {String} [options.context] The tag name of a context element within which - * the input is to be processed, default to be the editable element. - * If `null` is passed, then data will be parsed without context (as children of {@link CKEDITOR.htmlParser.fragment}). - * See {@link CKEDITOR.htmlParser.fragment#fromHtml} for more details. - * @param {Boolean} [options.fixForBody=true] Whether to trigger the auto paragraph for non-block contents. - * @param {CKEDITOR.filter} [options.filter] When specified, instead of using the {@link CKEDITOR.editor#filter main filter}, - * passed instance will be used to filter the content. - * @param {Boolean} [options.dontFilter] Do not filter data with {@link CKEDITOR.filter} (note: transformations - * will be still applied). - * @param {Number} [options.enterMode] When specified it will be used instead of the {@link CKEDITOR.editor#enterMode main enterMode}. - * @returns {String} - */ - toHtml: function( data, options, fixForBody, dontFilter ) { - var editor = this.editor, - context, filter, enterMode; - - // Typeof null == 'object', so check truthiness of options too. - if ( options && typeof options == 'object' ) { - context = options.context; - fixForBody = options.fixForBody; - dontFilter = options.dontFilter; - filter = options.filter; - enterMode = options.enterMode; - } - // Backward compatibility. Since CKEDITOR 4.3 every option was a separate argument. - else - context = options; - - // Fall back to the editable as context if not specified. - if ( !context && context !== null ) - context = editor.editable().getName(); - - return editor.fire( 'toHtml', { - dataValue: data, - context: context, - fixForBody: fixForBody, - dontFilter: dontFilter, - filter: filter || editor.filter, - enterMode: enterMode || editor.enterMode - } ).dataValue; - }, - - /** - * See {@link CKEDITOR.dataProcessor#toDataFormat}. - * - * This method fires the {@link CKEDITOR.editor#toDataFormat} event which makes it possible - * to hook into the process at various steps. - * - * @param {String} html - * @param {Object} [options] The options object. - * @param {String} [options.context] The tag name of a context element within which - * the input is to be processed, default to be the editable element. - * @param {CKEDITOR.filter} [options.filter] When specified, instead of using the {@link CKEDITOR.editor#filter main filter}, - * passed instance will be used to apply content transformations to the content. - * @param {Number} [options.enterMode] When specified it will be used instead of the {@link CKEDITOR.editor#enterMode main enterMode}. - * @returns {String} - */ - toDataFormat: function( html, options ) { - var context, filter, enterMode; - - // Do not shorten this to `options && options.xxx`, because - // falsy `options` will be passed instead of undefined. - if ( options ) { - context = options.context; - filter = options.filter; - enterMode = options.enterMode; - } - - // Fall back to the editable as context if not specified. - if ( !context && context !== null ) - context = this.editor.editable().getName(); - - return this.editor.fire( 'toDataFormat', { - dataValue: html, - filter: filter || this.editor.filter, - context: context, - enterMode: enterMode || this.editor.enterMode - } ).dataValue; - } - }; - - // Produce a set of filtering rules that handles bogus and filler node at the - // end of block/pseudo block, in the following consequence: - // 1. elements: - this filter removes any bogus node, then check - // if it's an empty block that requires a filler. - // 2. elements:
- After cleaned with bogus, this filter checks the real - // line-break BR to compensate a filler after it. - // - // Terms definitions: - // filler: An element that's either
or &NBSP; at the end of block that established line height. - // bogus: Whenever a filler is proceeded with inline content, it becomes a bogus which is subjected to be removed. - // - // Various forms of the filler: - // In output HTML: Filler should be consistently &NBSP;
at the end of block is always considered as bogus. - // In Wysiwyg HTML: Browser dependent - see env.needsBrFiller. Either BR for when needsBrFiller is true, or &NBSP; otherwise. - //
is NEVER considered as bogus when needsBrFiller is true. - function createBogusAndFillerRules( editor, type ) { - function createFiller( isOutput ) { - return isOutput || CKEDITOR.env.needsNbspFiller ? - new CKEDITOR.htmlParser.text( '\xa0' ) : - new CKEDITOR.htmlParser.element( 'br', { 'data-cke-bogus': 1 } ); - } - - // This text block filter, remove any bogus and create the filler on demand. - function blockFilter( isOutput, fillEmptyBlock ) { - - return function( block ) { - - // DO NOT apply the filer if it's a fragment node. - if ( block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ) - return; - - cleanBogus( block ); - - // [Opera] it's mandatory for the filler to present inside of empty block when in WYSIWYG. - if ( ( ( CKEDITOR.env.opera && !isOutput ) || - ( typeof fillEmptyBlock == 'function' ? fillEmptyBlock( block ) !== false : fillEmptyBlock ) ) && - isEmptyBlockNeedFiller( block ) ) - { - block.add( createFiller( isOutput ) ); - } - }; - } - - // Append a filler right after the last line-break BR, found at the end of block. - function brFilter( isOutput ) { - return function( br ) { - - // DO NOT apply the filer if parent's a fragment node. - if ( br.parent.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ) - return; - - var attrs = br.attributes; - // Dismiss BRs that are either bogus or eol marker. - if ( 'data-cke-bogus' in attrs || - 'data-cke-eol' in attrs ) { - delete attrs [ 'data-cke-bogus' ]; - return; - } - - // Judge the tail line-break BR, and to insert bogus after it. - var next = getNext( br ), previous = getPrevious( br ); - - if ( !next && isBlockBoundary( br.parent ) ) - append( br.parent, createFiller( isOutput ) ); - else if ( isBlockBoundary( next ) && previous && !isBlockBoundary( previous ) ) - createFiller( isOutput ).insertBefore( next ); - }; - } - - // Determinate whether this node is potentially a bogus node. - function maybeBogus( node, atBlockEnd ) { - - // BR that's not from IE<11 DOM, except for a EOL marker. - if ( !( isOutput && !CKEDITOR.env.needsBrFiller ) && - node.type == CKEDITOR.NODE_ELEMENT && node.name == 'br' && - !node.attributes[ 'data-cke-eol' ] ) - return true; - - var match; - // NBSP, possibly. - if ( node.type == CKEDITOR.NODE_TEXT && - ( match = node.value.match( tailNbspRegex ) ) ) - { - // We need to separate tail NBSP out of a text node, for later removal. - if ( match.index ) { - ( new CKEDITOR.htmlParser.text( node.value.substring( 0, match.index ) ) ).insertBefore( node ); - node.value = match[ 0 ]; - } - - // From IE<11 DOM, at the end of a text block, or before block boundary. - if ( !CKEDITOR.env.needsBrFiller && isOutput && ( !atBlockEnd || node.parent.name in textBlockTags ) ) - return true; - - // From the output. - if ( !isOutput ) { - var previous = node.previous; - - // Following a line-break at the end of block. - if ( previous && previous.name == 'br' ) - return true; - - // Or a single NBSP between two blocks. - if ( !previous || isBlockBoundary( previous ) ) - return true; - } - } - - return false; - } - - // Removes all bogus inside of this block, and to convert fillers into the proper form. - function cleanBogus( block ) { - var bogus = []; - var last = getLast( block ), node, previous; - if ( last ) { - - // Check for bogus at the end of this block. - // e.g.

foo

- maybeBogus( last, 1 ) && bogus.push( last ); - - while ( last ) { - - // Check for bogus at the end of any pseudo block contained. - if ( isBlockBoundary( last ) && - ( node = getPrevious( last ) ) && - maybeBogus( node ) ) - { - // Bogus must have inline proceeding, instead single BR between two blocks, - // is considered as filler, e.g.


- if ( ( previous = getPrevious( node ) ) && !isBlockBoundary( previous ) ) - bogus.push( node ); - // Convert the filler into appropriate form. - else { - createFiller( isOutput ).insertAfter( node ); - node.remove(); - } - } - - last = last.previous; - } - } - - // Now remove all bogus collected from above. - for ( var i = 0 ; i < bogus.length ; i++ ) - bogus[ i ].remove(); - } - - // Judge whether it's an empty block that requires a filler node. - function isEmptyBlockNeedFiller( block ) { - - // DO NOT fill empty editable in IE<11. - if ( !isOutput && !CKEDITOR.env.needsBrFiller && block.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ) - return false; - - // 1. For IE version >=8, empty blocks are displayed correctly themself in wysiwiyg; - // 2. For the rest, at least table cell and list item need no filler space. (#6248) - if ( !isOutput && !CKEDITOR.env.needsBrFiller && - ( document.documentMode > 7 || - block.name in CKEDITOR.dtd.tr || - block.name in CKEDITOR.dtd.$listItem ) ) { - return false; - } - - var last = getLast( block ); - return !last || block.name == 'form' && last.name == 'input' ; - } - - var rules = { elements: {} }; - var isOutput = type == 'html'; - - // Build the list of text blocks. - var textBlockTags = CKEDITOR.tools.extend( {}, blockLikeTags ); - for ( var i in textBlockTags ) { - if ( !( '#' in dtd[ i ] ) ) - delete textBlockTags[ i ]; - } - - for ( i in textBlockTags ) - rules.elements[ i ] = blockFilter( isOutput, editor.config.fillEmptyBlocks !== false ); - - // Editable element is to be checked separately. - rules.root = blockFilter( isOutput ); - rules.elements.br = brFilter( isOutput ); - return rules; - } - - function getFixBodyTag( enterMode, autoParagraph ) { - return ( enterMode != CKEDITOR.ENTER_BR && autoParagraph !== false ) ? enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'p' : false; - } - - // Regex to scan for   at the end of blocks, which are actually placeholders. - // Safari transforms the   to \xa0. (#4172) - var tailNbspRegex = /(?: |\xa0)$/; - - var protectedSourceMarker = '{cke_protected}'; - - function getLast( node ) { - var last = node.children[ node.children.length - 1 ]; - while ( last && isEmpty( last ) ) - last = last.previous; - return last; - } - - function getNext( node ) { - var next = node.next; - while ( next && isEmpty( next ) ) - next = next.next; - return next; - } - - function getPrevious( node ) { - var previous = node.previous; - while ( previous && isEmpty( previous ) ) - previous = previous.previous; - return previous; - } - - // Judge whether the node is an ghost node to be ignored, when traversing. - function isEmpty( node ) { - return node.type == CKEDITOR.NODE_TEXT && - !CKEDITOR.tools.trim( node.value ) || - node.type == CKEDITOR.NODE_ELEMENT && - node.attributes[ 'data-cke-bookmark' ]; - } - - // Judge whether the node is a block-like element. - function isBlockBoundary( node ) { - return node && - ( node.type == CKEDITOR.NODE_ELEMENT && node.name in blockLikeTags || - node.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT ); - } - - function append( parent, node ) { - var last = parent.children[ parent.children.length -1 ]; - parent.children.push( node ); - node.parent = parent; - if ( last ) { - last.next = node; - node.previous = last; - } - } - - function getNodeIndex( node ) { - return node.parent ? node.getIndex() : -1; - } - - var dtd = CKEDITOR.dtd, - // Define orders of table elements. - tableOrder = [ 'caption', 'colgroup', 'col', 'thead', 'tfoot', 'tbody' ], - // List of all block elements. - blockLikeTags = CKEDITOR.tools.extend( {}, dtd.$blockLimit, dtd.$block ); - - // - // DATA filter rules ------------------------------------------------------ - // - - var defaultDataFilterRulesEditableOnly = { - elements: { - input: protectReadOnly, - textarea: protectReadOnly - } - }; - - // These rules will also be applied to non-editable content. - var defaultDataFilterRulesForAll = { - attributeNames: [ - // Event attributes (onXYZ) must not be directly set. They can become - // active in the editing area (IE|WebKit). - [ ( /^on/ ), 'data-cke-pa-on' ], - - // Don't let some old expando enter editor. Concerns only IE8, - // but for consistency remove on all browsers. - [ ( /^data-cke-expando$/ ), '' ] - ] - }; - - // Disable form elements editing mode provided by some browsers. (#5746) - function protectReadOnly( element ) { - var attrs = element.attributes; - - // We should flag that the element was locked by our code so - // it'll be editable by the editor functions (#6046). - if ( attrs.contenteditable != 'false' ) - attrs[ 'data-cke-editable' ] = attrs.contenteditable ? 'true' : 1; - - attrs.contenteditable = 'false'; - } - - // - // HTML filter rules ------------------------------------------------------ - // - - var defaultHtmlFilterRulesEditableOnly = { - elements: { - embed: function( element ) { - var parent = element.parent; - - // If the is child of a , copy the width - // and height attributes from it. - if ( parent && parent.name == 'object' ) { - var parentWidth = parent.attributes.width, - parentHeight = parent.attributes.height; - if ( parentWidth ) - element.attributes.width = parentWidth; - if ( parentHeight ) - element.attributes.height = parentHeight; - } - }, - - // Remove empty link but not empty anchor. (#3829) - a: function( element ) { - if ( !( element.children.length || element.attributes.name || element.attributes[ 'data-cke-saved-name' ] ) ) - return false; - } - } - }; - - // These rules will also be applied to non-editable content. - var defaultHtmlFilterRulesForAll = { - elementNames: [ - // Remove the "cke:" namespace prefix. - [ ( /^cke:/ ), '' ], - - // Ignore tags. - [ ( /^\?xml:namespace$/ ), '' ] - ], - - attributeNames: [ - // Attributes saved for changes and protected attributes. - [ ( /^data-cke-(saved|pa)-/ ), '' ], - - // All "data-cke-" attributes are to be ignored. - [ ( /^data-cke-.*/ ), '' ], - - [ 'hidefocus', '' ] - ], - - elements: { - $: function( element ) { - var attribs = element.attributes; - - if ( attribs ) { - // Elements marked as temporary are to be ignored. - if ( attribs[ 'data-cke-temp' ] ) - return false; - - // Remove duplicated attributes - #3789. - var attributeNames = [ 'name', 'href', 'src' ], - savedAttributeName; - for ( var i = 0; i < attributeNames.length; i++ ) { - savedAttributeName = 'data-cke-saved-' + attributeNames[ i ]; - savedAttributeName in attribs && ( delete attribs[ attributeNames[ i ] ] ); - } - } - - return element; - }, - - // The contents of table should be in correct order (#4809). - table: function( element ) { - // Clone the array as it would become empty during the sort call. - var children = element.children.slice( 0 ); - children.sort( function( node1, node2 ) { - var index1, index2; - - // Compare in the predefined order. - if ( node1.type == CKEDITOR.NODE_ELEMENT && - node2.type == node1.type ) { - index1 = CKEDITOR.tools.indexOf( tableOrder, node1.name ); - index2 = CKEDITOR.tools.indexOf( tableOrder, node2.name ); - } - - // Make sure the sort is stable, if no order can be established above. - if ( !( index1 > -1 && index2 > -1 && index1 != index2 ) ) { - index1 = getNodeIndex( node1 ); - index2 = getNodeIndex( node2 ); - } - - return index1 > index2 ? 1 : -1; - } ); - }, - - // Restore param elements into self-closing. - param: function( param ) { - param.children = []; - param.isEmpty = true; - return param; - }, - - // Remove dummy span in webkit. - span: function( element ) { - if ( element.attributes[ 'class' ] == 'Apple-style-span' ) - delete element.name; - }, - - html: function( element ) { - delete element.attributes.contenteditable; - delete element.attributes[ 'class' ]; - }, - - body: function( element ) { - delete element.attributes.spellcheck; - delete element.attributes.contenteditable; - }, - - style: function( element ) { - var child = element.children[ 0 ]; - if ( child && child.value ) - child.value = CKEDITOR.tools.trim( child.value ); - - if ( !element.attributes.type ) - element.attributes.type = 'text/css'; - }, - - title: function( element ) { - var titleText = element.children[ 0 ]; - - // Append text-node to title tag if not present (i.e. non-IEs) (#9882). - !titleText && append( element, titleText = new CKEDITOR.htmlParser.text() ); - - // Transfer data-saved title to title tag. - titleText.value = element.attributes[ 'data-cke-title' ] || ''; - }, - - input: unprotectReadyOnly, - textarea: unprotectReadyOnly - }, - - attributes: { - 'class': function( value, element ) { - // Remove all class names starting with "cke_". - return CKEDITOR.tools.ltrim( value.replace( /(?:^|\s+)cke_[^\s]*/g, '' ) ) || false; - } - } - }; - - if ( CKEDITOR.env.ie ) { - // IE outputs style attribute in capital letters. We should convert - // them back to lower case, while not hurting the values (#5930) - defaultHtmlFilterRulesForAll.attributes.style = function( value, element ) { - return value.replace( /(^|;)([^\:]+)/g, function( match ) { - return match.toLowerCase(); - } ); - }; - } - - // Disable form elements editing mode provided by some browsers. (#5746) - function unprotectReadyOnly( element ) { - var attrs = element.attributes; - switch ( attrs[ 'data-cke-editable' ] ) { - case 'true': - attrs.contenteditable = 'true'; - break; - case '1': - delete attrs.contenteditable; - break; - } - } - - // - // Preprocessor filters --------------------------------------------------- - // - - var protectElementRegex = /<(a|area|img|input|source)\b([^>]*)>/gi, - // Be greedy while looking for protected attributes. This will let us avoid an unfortunate - // situation when "nested attributes", which may appear valid, are also protected. - // I.e. if we consider the following HTML: - // - // - // - // then the "non-greedy match" returns: - // - // 'href' => '"X"' // It's wrong! Href is not an attribute of . - // - // while greedy match returns: - // - // 'data-x' => '<a href="X"' - // - // which, can be easily filtered out (#11508). - protectAttributeRegex = /([\w-]+)\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|(?:[^ "'>]+))/gi, - protectAttributeNameRegex = /^(href|src|name)$/i; - - // Note: we use lazy star '*?' to prevent eating everything up to the last occurrence of or . - var protectElementsRegex = /(?:])[^>]*>[\s\S]*?<\/style>)|(?:<(:?link|meta|base)[^>]*>)/gi, - protectTextareaRegex = /(])[^>]*>)([\s\S]*?)(?:<\/textarea>)/gi, - encodedElementsRegex = /([^<]*)<\/cke:encoded>/gi; - - var protectElementNamesRegex = /(<\/?)((?:object|embed|param|html|body|head|title)[^>]*>)/gi, - unprotectElementNamesRegex = /(<\/?)cke:((?:html|body|head|title)[^>]*>)/gi; - - var protectSelfClosingRegex = /]*?)\/?>(?!\s*<\/cke:\1)/gi; - - function protectAttributes( html ) { - return html.replace( protectElementRegex, function( element, tag, attributes ) { - return '<' + tag + attributes.replace( protectAttributeRegex, function( fullAttr, attrName ) { - // Avoid corrupting the inline event attributes (#7243). - // We should not rewrite the existed protected attributes, e.g. clipboard content from editor. (#5218) - if ( protectAttributeNameRegex.test( attrName ) && attributes.indexOf( 'data-cke-saved-' + attrName ) == -1 ) - return ' data-cke-saved-' + fullAttr + ' data-cke-' + CKEDITOR.rnd + '-' + fullAttr; - - return fullAttr; - } ) + '>'; - } ); - } - - function protectElements( html, regex ) { - return html.replace( regex, function( match, tag, content ) { - // Encode < and > in textarea because this won't be done by a browser, since - // textarea will be protected during passing data through fix bin. - if ( match.indexOf( '/g, '>' ) + ''; - - return '' + encodeURIComponent( match ) + ''; - } ); - } - - function unprotectElements( html ) { - return html.replace( encodedElementsRegex, function( match, encoded ) { - return decodeURIComponent( encoded ); - } ); - } - - function protectElementsNames( html ) { - return html.replace( protectElementNamesRegex, '$1cke:$2' ); - } - - function unprotectElementNames( html ) { - return html.replace( unprotectElementNamesRegex, '$1$2' ); - } - - function protectSelfClosingElements( html ) { - return html.replace( protectSelfClosingRegex, '' ); - } - - function protectPreFormatted( html ) { - return CKEDITOR.env.opera ? html : html.replace( /(]*>)(\r\n|\n)/g, '$1$2$2' ); - } - - function protectRealComments( html ) { - return html.replace( //g, function( match ) { - return ''; - } ); - } - - function unprotectRealComments( html ) { - return html.replace( //g, function( match, data ) { - return decodeURIComponent( data ); - } ); - } - - function unprotectSource( html, editor ) { - var store = editor._.dataStore; - - return html.replace( //g, function( match, data ) { - return decodeURIComponent( data ); - } ).replace( /\{cke_protected_(\d+)\}/g, function( match, id ) { - return store && store[ id ] || ''; - } ); - } - - function protectSource( data, editor ) { - var protectedHtml = [], - protectRegexes = editor.config.protectedSource, - store = editor._.dataStore || ( editor._.dataStore = { id: 1 } ), - tempRegex = /<\!--\{cke_temp(comment)?\}(\d*?)-->/g; - - var regexes = [ - // Script tags will also be forced to be protected, otherwise - // IE will execute them. - ( //gi ), - - //