diff --git a/htdocs/core/class/html.formsetup.class.php b/htdocs/core/class/html.formsetup.class.php index 8f4c23ea5d7..9e7ed642c1d 100644 --- a/htdocs/core/class/html.formsetup.class.php +++ b/htdocs/core/class/html.formsetup.class.php @@ -82,6 +82,7 @@ class FormSetup */ public $errors = array(); + /** * Constructor * diff --git a/htdocs/core/db/DoliDB.class.php b/htdocs/core/db/DoliDB.class.php index a6469cf6939..a9769ef9a8a 100644 --- a/htdocs/core/db/DoliDB.class.php +++ b/htdocs/core/db/DoliDB.class.php @@ -114,18 +114,18 @@ abstract class DoliDB implements Database /** * Format a SQL REGEXP * - * @param string $subject string tested + * @param string $subject Field name to test * @param string $pattern SQL pattern to match - * @param int $sqlstring whether or not the string being tested is an SQL expression + * @param int $sqlstring 0=the string being tested is a hard coded string, 1=the string is a field * @return string SQL string */ public function regexpsql($subject, $pattern, $sqlstring = 0) { if ($sqlstring) { - return "(". $subject ." REGEXP '" . $pattern . "')"; + return "(". $subject ." REGEXP '" . $this->escape($pattern) . "')"; } - return "('". $subject ."' REGEXP '" . $pattern . "')"; + return "('". $this->escape($subject) ."' REGEXP '" . $this->escape($pattern) . "')"; } diff --git a/htdocs/core/db/pgsql.class.php b/htdocs/core/db/pgsql.class.php index 566cccf332a..cfd258fca9b 100644 --- a/htdocs/core/db/pgsql.class.php +++ b/htdocs/core/db/pgsql.class.php @@ -754,18 +754,18 @@ class DoliDBPgsql extends DoliDB /** * Format a SQL REGEXP * - * @param string $subject string tested + * @param string $subject Field name to test * @param string $pattern SQL pattern to match - * @param int $sqlstring whether or not the string being tested is an SQL expression + * @param int $sqlstring 0=the string being tested is a hard coded string, 1=the string is a field * @return string SQL string */ public function regexpsql($subject, $pattern, $sqlstring = 0) { if ($sqlstring) { - return "(". $subject ." ~ '" . $pattern . "')"; + return "(". $subject ." ~ '" . $this->escape($pattern) . "')"; } - return "('". $subject ."' ~ '" . $pattern . "')"; + return "('". $this->escape($subject) ."' ~ '" . $this->escape($pattern) . "')"; } diff --git a/htdocs/core/lib/files.lib.php b/htdocs/core/lib/files.lib.php index 29b956c9d7a..b568a87a64e 100644 --- a/htdocs/core/lib/files.lib.php +++ b/htdocs/core/lib/files.lib.php @@ -2644,7 +2644,11 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, } // Wrapping for miscellaneous medias files - if ($modulepart == 'medias' && !empty($dolibarr_main_data_root)) { + if ($modulepart == 'common') { + // Wrapping for some images + $accessallowed = 1; + $original_file = DOL_DOCUMENT_ROOT.'/public/theme/common/'.$original_file; + } elseif ($modulepart == 'medias' && !empty($dolibarr_main_data_root)) { if (empty($entity) || empty($conf->medias->multidir_output[$entity])) { return array('accessallowed'=>0, 'error'=>'Value entity must be provided'); } @@ -2662,7 +2666,7 @@ function dol_check_secure_access_document($modulepart, $original_file, $entity, // Wrapping for doctemplates of websites $accessallowed = ($fuser->rights->website->write && preg_match('/\.jpg$/i', basename($original_file))); $original_file = $dolibarr_main_data_root.'/doctemplates/websites/'.$original_file; - } elseif ($modulepart == 'packages' && !empty($dolibarr_main_data_root)) { + } elseif ($modulepart == 'packages' && !empty($dolibarr_main_data_root)) { // To download zip of modules // Wrapping for *.zip package files, like when used with url http://.../document.php?modulepart=packages&file=module_myfile.zip // Dir for custom dirs $tmp = explode(',', $dolibarr_main_document_root_alt); diff --git a/htdocs/core/lib/functions.lib.php b/htdocs/core/lib/functions.lib.php index d686883cd7f..1df5f6a4a4c 100644 --- a/htdocs/core/lib/functions.lib.php +++ b/htdocs/core/lib/functions.lib.php @@ -10847,8 +10847,6 @@ function ajax_autoselect($htmlname, $addlink = '', $textonlink = 'Link') */ function dolIsAllowedForPreview($file) { - global $conf; - // Check .noexe extension in filename if (preg_match('/\.noexe$/i', $file)) { return 0; diff --git a/htdocs/core/lib/images.lib.php b/htdocs/core/lib/images.lib.php index b1959900794..dfafcdf8537 100644 --- a/htdocs/core/lib/images.lib.php +++ b/htdocs/core/lib/images.lib.php @@ -65,8 +65,6 @@ function getDefaultImageSizes() */ function getListOfPossibleImageExt($acceptsvg = 0) { - global $conf; - $regeximgext = '\.gif|\.jpg|\.jpeg|\.png|\.bmp|\.webp|\.xpm|\.xbm'; // See also into product.class.php if ($acceptsvg || getDolGlobalString('MAIN_ALLOW_SVG_FILES_AS_IMAGES')) { $regeximgext .= '|\.svg'; // Not allowed by default. SVG can contains javascript diff --git a/htdocs/core/lib/website.lib.php b/htdocs/core/lib/website.lib.php index 5f0bfd8915d..3f2f01c47c7 100644 --- a/htdocs/core/lib/website.lib.php +++ b/htdocs/core/lib/website.lib.php @@ -1,4 +1,6 @@ * * This program is free software; you can redistribute it and/or modify @@ -968,6 +970,85 @@ function getSocialNetworkSharingLinks() return $out; } + +/** + * Return HTML content to add structured data for an article, news or Blog Post. + * + * @param Object $object Object + * @param int $no Numero of image (if there is several images. 1st one by default) + * @param string $extName Extension to differentiate thumb file name ('', '_small', '_mini') + * @param string $morecss More CSS + * @return string HTML img content or '' if no image found + */ +function getPublicImageOfObject($object, $no = 1, $extName = '', $morecss = '') +{ + global $db; + + $result = ''; + $image_path = ''; + $filename = ''; + + include_once DOL_DOCOUMENT_ROOT.'/core/lib/images.lib.php'; + $regexforimg = getListOfPossibleImageExt(0); + $regexforimg = '('.$regexforimg.')$'; + + $sql = "SELECT rowid, ref, share, filename, cover, position"; + $sql .= " FROM ".MAIN_DB_PREFIX."ecm_files"; + $sql .= " WHERE entity IN (".getEntity($object->element).")"; + $sql .= " AND src_object_type = '".$db->escape($object->element)."' AND src_object_id = ".((int) $object->id); // Filter on object + $sql .= " AND ".$db->regexpsql('filename', $regexforimg, 1); + $sql .= $db->order("cover,position,rowid", "ASC,ASC,ASC"); + + $resql = $db->query($sql); + if ($resql) { + $num = $db->num_rows($resql); + $i = 0; + $found = 0; + $foundnotshared = 0; + while ($i < $num) { + $obj = $db->fetch_object($resql); + if ($obj) { + if (empty($obj->share)) { + $foundnotshared++; + } else { + $found++; + + $filename = $obj->filename; + $image_path = DOL_URL_ROOT.'/viewimage.php?hashp='.urlencode($obj->share); + if ($extName) { + $image_path .= '&extname='.urlencode($extName); + } + } + } + if ($found >= $no) { + break; + } + $i++; + } + if (!$found && $foundnotshared) { + $image_path = DOL_URL_ROOT.'/viewimage.php?modulepart=common&file=nophotopublic.png'; + } + } + + //getImageFileNameForSize($dir.$file, '_small') + + if (empty($image_path)) { + $image_path = DOL_URL_ROOT.'/viewimage.php?modulepart=common&file=nophoto.png'; + } + + $result .= ''; + if ($image_path) { + $result .= ''; + } + + return $result; +} + + /** * Return list of containers object that match a criteria. * WARNING: This function can be used by websites. diff --git a/htdocs/core/modules/modTakePos.class.php b/htdocs/core/modules/modTakePos.class.php index 06b5b79dbf3..366f8a98549 100644 --- a/htdocs/core/modules/modTakePos.class.php +++ b/htdocs/core/modules/modTakePos.class.php @@ -295,11 +295,11 @@ class modTakePos extends DolibarrModules } } - //Create category if not exists + // Create product category DefaultPOSCatLabel if not exists $categories = new Categorie($this->db); $cate_arbo = $categories->get_full_arbo('product', 0, 1); if (is_array($cate_arbo)) { - if (!count($cate_arbo)) { + if (!count($cate_arbo) || !getDolGlobalString('TAKEPOS_ROOT_CATEGORY_ID')) { $category = new Categorie($this->db); $category->label = $langs->trans("DefaultPOSCatLabel"); @@ -308,6 +308,8 @@ class modTakePos extends DolibarrModules $result = $category->create($user); if ($result > 0) { + dolibarr_set_const($this->db, 'TAKEPOS_ROOT_CATEGORY_ID', $result, 'chaine', 0, 'Id of category for products visible in TakePOS', $conf->entity); + /* TODO Create a generic product only if there is no product yet. If 0 product, we create 1. If there is already product, it is better to show a message to ask to add product in the category */ /* $product = new Product($this->db); @@ -323,7 +325,7 @@ class modTakePos extends DolibarrModules } } - //Create cash account if not exists + // Create cash account CASH-POS / DefaultCashPOSLabel if not exists if (!getDolGlobalInt('CASHDESK_ID_BANKACCOUNT_CASH1')) { require_once DOL_DOCUMENT_ROOT.'/compta/bank/class/account.class.php'; $cashaccount = new Account($this->db); diff --git a/htdocs/install/mysql/migration/19.0.0-20.0.0.sql b/htdocs/install/mysql/migration/19.0.0-20.0.0.sql index e6363ba41eb..33490b212d0 100644 --- a/htdocs/install/mysql/migration/19.0.0-20.0.0.sql +++ b/htdocs/install/mysql/migration/19.0.0-20.0.0.sql @@ -201,3 +201,5 @@ CREATE TABLE llx_c_product_thirdparty_relation_type ALTER TABLE llx_c_tva ADD COLUMN type_vat smallint NOT NULL DEFAULT 0 AFTER fk_pays; +ALTER TABLE llx_product DROP COLUMN onportal; + diff --git a/htdocs/install/mysql/tables/llx_product.sql b/htdocs/install/mysql/tables/llx_product.sql index f63a4145569..0dd84d550f5 100644 --- a/htdocs/install/mysql/tables/llx_product.sql +++ b/htdocs/install/mysql/tables/llx_product.sql @@ -59,7 +59,6 @@ create table llx_product fk_user_modif integer, -- user making last change tosell tinyint DEFAULT 1, -- Product you sell tobuy tinyint DEFAULT 1, -- Product you buy - onportal tinyint DEFAULT 0, -- If it is a product you sell and you want to sell it from internal portal (module 'portal') tobatch tinyint DEFAULT 0 NOT NULL, -- Is it a product that need a batch management (eat-by or lot management) sell_or_eat_by_mandatory tinyint DEFAULT 0 NOT NULL, -- Make sell-by or eat-by date mandatory batch_mask varchar(32) DEFAULT NULL, -- If the product has batch feature, you may want to use a batch mask per product diff --git a/htdocs/langs/en_US/accountancy.lang b/htdocs/langs/en_US/accountancy.lang index 7939e82498d..1d9215fdedd 100644 --- a/htdocs/langs/en_US/accountancy.lang +++ b/htdocs/langs/en_US/accountancy.lang @@ -446,8 +446,8 @@ AccountancyOneUnletteringModifiedSuccessfully=One unreconcile successfully modif AccountancyUnletteringModifiedSuccessfully=%s unreconcile successfully modified ## Closure -AccountancyClosureStep1=Step 1 : Validate and lock movements -AccountancyClosureStep2=Step 2 : Close fiscal period +AccountancyClosureStep1=Step 1 : Validate and lock the movements +AccountancyClosureStep2=Step 2 : Close the fiscal period AccountancyClosureStep3=Step 3 : Extract entries (Optional) AccountancyClosureClose=Close fiscal period AccountancyClosureAccountingReversal=Extract and record "Retained earnings" entries diff --git a/htdocs/langs/en_US/main.lang b/htdocs/langs/en_US/main.lang index 8c40379b67b..227b546ba74 100644 --- a/htdocs/langs/en_US/main.lang +++ b/htdocs/langs/en_US/main.lang @@ -1095,7 +1095,7 @@ KeyboardShortcut=Keyboard shortcut AssignedTo=Assigned to Deletedraft=Delete draft ConfirmMassDraftDeletion=Draft mass delete confirmation -FileSharedViaALink=File shared with a public link +FileSharedViaALink=Public file shared via link SelectAThirdPartyFirst=Select a third party first... YouAreCurrentlyInSandboxMode=You are currently in the %s "sandbox" mode Inventory=Inventory diff --git a/htdocs/modulebuilder/index.php b/htdocs/modulebuilder/index.php index 2fbddca8900..5930dce7225 100644 --- a/htdocs/modulebuilder/index.php +++ b/htdocs/modulebuilder/index.php @@ -3470,7 +3470,8 @@ if ($module == 'initmodule') { foreach ($listofsetuppages as $setuppage) { //var_dump($setuppage); print ''; - print ' '.$langs->trans("SetupFile").' : '.$modulelowercase.'/admin/'.$setuppage['relativename'].''; + print ' '.$langs->trans("SetupFile").' : '; + print ''.$modulelowercase.'/admin/'.$setuppage['relativename'].''; print ''.img_picto($langs->trans("Edit"), 'edit').''; print ''; } diff --git a/htdocs/modulebuilder/template/admin/setup.php b/htdocs/modulebuilder/template/admin/setup.php index 875e9e03c38..34a94fbc016 100644 --- a/htdocs/modulebuilder/template/admin/setup.php +++ b/htdocs/modulebuilder/template/admin/setup.php @@ -143,7 +143,8 @@ $item = $formSetup->newItem('MYMODULE_MYPARAM10'); $item->setAsMultiSelect($TField); $item->helpText = $langs->transnoentities('MYMODULE_MYPARAM10'); - +// Setup conf for a category selection +$formSetup->newItem('MYMODULE_CATEGORY_ID_XXX')->setAsCategory('product'); // Setup conf MYMODULE_MYPARAM10 $item = $formSetup->newItem('MYMODULE_MYPARAM10'); diff --git a/htdocs/modulebuilder/template/langs/en_US/mymodule.lang b/htdocs/modulebuilder/template/langs/en_US/mymodule.lang index cc518391c33..8677fa58069 100644 --- a/htdocs/modulebuilder/template/langs/en_US/mymodule.lang +++ b/htdocs/modulebuilder/template/langs/en_US/mymodule.lang @@ -28,6 +28,7 @@ ModuleMyModuleDesc = MyModule description MyModuleSetup = MyModule setup Settings = Settings MyModuleSetupPage = MyModule setup page +NewSection=New section MYMODULE_MYPARAM1 = My param 1 MYMODULE_MYPARAM1Tooltip = My param 1 tooltip MYMODULE_MYPARAM2=My param 2 diff --git a/htdocs/public/theme/common/nophotopublic.png b/htdocs/public/theme/common/nophotopublic.png new file mode 100644 index 00000000000..8b052fe17ac Binary files /dev/null and b/htdocs/public/theme/common/nophotopublic.png differ diff --git a/htdocs/viewimage.php b/htdocs/viewimage.php index e2429115974..9492deb8fa5 100644 --- a/htdocs/viewimage.php +++ b/htdocs/viewimage.php @@ -70,6 +70,10 @@ if (isset($_GET["modulepart"])) { if ($_GET["modulepart"] == 'medias') { $needlogin = 0; } + // Common files (files into /public/theme/common) + if ($_GET["modulepart"] == 'common') { + $needlogin = 0; + } // User photo when user has made its profile public (for virtual credi card) if ($_GET["modulepart"] == 'userphotopublic') { $needlogin = 0; @@ -195,8 +199,7 @@ if (!empty($hashp)) { $original_file = (($tmp[1] ? $tmp[1].'/' : '').$ecmfile->filename); // this is relative to module dir } } else { - $langs->load("errors"); - httponly_accessforbidden($langs->trans("ErrorFileNotFoundWithSharedLink"), 403, 1); + httponly_accessforbidden("ErrorFileNotFoundWithSharedLink", 403, 1); } } @@ -231,7 +234,7 @@ if ($refname == 'thumbs') { // Check that file is allowed for view with viewimage.php if (!empty($original_file) && !dolIsAllowedForPreview($original_file)) { - httponly_accessforbidden('This file is not qualified for preview', 403); + httponly_accessforbidden('This file extension is not qualified for preview', 403); } // Security check @@ -256,7 +259,9 @@ if (!empty($hashp)) { $sqlprotectagainstexternals = ''; } elseif (GETPOSTINT("publictakepos")) { if (getDolGlobalString('TAKEPOS_AUTO_ORDER') && in_array($modulepart, array('product', 'category'))) { - $accessallowed = 1; // When TakePOS Public Auto Order is enabled, we accept to see all images of product and categories + $accessallowed = 1; // When TakePOS Public Auto Order is enabled, we accept to see all images of product and categories with no login + // TODO Replace this with a call of getPublicImageOfObject like used by website so + // only share images are visibles } } else { // Basic protection (against external users only) @@ -344,8 +349,8 @@ if ($modulepart == 'barcode') { // Output files on browser dol_syslog("viewimage.php return file $fullpath_original_file filename=$filename content-type=$type"); - // This test is to avoid error images when image is not available (for example thumbs). - if (!dol_is_file($fullpath_original_file) && empty($_GET["noalt"])) { + if (!dol_is_file($fullpath_original_file) && !GETPOSTINT("noalt", 1)) { + // This test is to replace error images with a nice "notfound image" when image is not available (for example when thumbs not yet generated). $fullpath_original_file = DOL_DOCUMENT_ROOT.'/public/theme/common/nophoto.png'; /*$error='Error: File '.$_GET["file"].' does not exists or filesystems permissions are not allowed'; print $error;