diff --git a/htdocs/core/modules/modVariants.class.php b/htdocs/core/modules/modVariants.class.php index 9dda6194b32..f2a1373b89c 100644 --- a/htdocs/core/modules/modVariants.class.php +++ b/htdocs/core/modules/modVariants.class.php @@ -110,15 +110,15 @@ class modVariants extends DolibarrModules $this->rights = array(); // Permission array used by this module $r = 0; - $this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used) + $this->rights[$r][0] = $this->numero + 1; // Permission id (must not be already used) $this->rights[$r][1] = 'Read attributes of variants'; // Permission label $this->rights[$r][4] = 'read'; // In php code, permission will be checked by test if ($user->rights->eventorganization->level1) $r++; - $this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used) + $this->rights[$r][0] = $this->numero + 2; // Permission id (must not be already used) $this->rights[$r][1] = 'Create/Update attributes of variants'; // Permission label $this->rights[$r][4] = 'write'; // In php code, permission will be checked by test if ($user->rights->eventorganization->level1) $r++; - $this->rights[$r][0] = $this->numero + $r; // Permission id (must not be already used) + $this->rights[$r][0] = $this->numero + 3; // Permission id (must not be already used) $this->rights[$r][1] = 'Delete attributes of variants'; // Permission label $this->rights[$r][4] = 'delete'; // In php code, permission will be checked by test if ($user->rights->eventorganization->level1) $r++; diff --git a/htdocs/install/step2.php b/htdocs/install/step2.php index 0686e19acae..76fccc5a621 100644 --- a/htdocs/install/step2.php +++ b/htdocs/install/step2.php @@ -93,6 +93,7 @@ dolibarr_install_syslog("--- step2: entering step2.php page"); '@phan-var-force string $dolibarr_main_db_prefix'; // From configuraiotn file or install/inc.php + /* * View */ diff --git a/htdocs/install/upgrade2.php b/htdocs/install/upgrade2.php index 49bc05c99aa..dbe3cfa158e 100644 --- a/htdocs/install/upgrade2.php +++ b/htdocs/install/upgrade2.php @@ -4198,7 +4198,7 @@ function migrate_delete_old_dir($db, $langs, $conf) * @param DoliDB $db Database handler * @param Translate $langs Object langs * @param Conf $conf Object conf - * @param array $listofmodule List of modules, like array('MODULE_KEY_NAME'=>', $reloadmode) + * @param array $listofmodule List of modules, like array('MODULE_KEY_NAME'=>$reloadmode, ...) * @param int $force 1=Reload module even if not already loaded * @return int Return integer <0 if KO, >0 if OK */ @@ -4244,6 +4244,7 @@ function migrate_reload_modules($db, $langs, $conf, $listofmodule = array(), $fo 'MAIN_MODULE_OPENSURVEY' => array('class' => 'modOpenSurvey', 'remove' => 1), 'MAIN_MODULE_PRODUCTBATCH' => array('class' => 'modProductBatch', 'remove' => 1), 'MAIN_MODULE_TAKEPOS' => array('class' => 'modTakePos', 'remove' => 1), + 'MAIN_MODULE_VARIANTS' => array('class' => 'modVariants', 'remove' => 1), 'MAIN_MODULE_EMAILCOLLECTOR' => array('class' => 'modEmailCollector', 'remove' => 1), ); diff --git a/htdocs/variants/class/ProductAttribute.class.php b/htdocs/variants/class/ProductAttribute.class.php index 1a2ea4baf8a..10f75a437c9 100644 --- a/htdocs/variants/class/ProductAttribute.class.php +++ b/htdocs/variants/class/ProductAttribute.class.php @@ -1222,6 +1222,49 @@ class ProductAttribute extends CommonObject return $result; } + /** + * Return a thumb for kanban views + * + * @param string $option Where point the link (0=> main card, 1,2 => shipment, 'nolink'=>No link) + * @param ?array $arraydata Array of data + * @return string HTML Code for Kanban thumb. + */ + public function getKanbanView($option = '', $arraydata = null) + { + global $conf, $langs; + + $selected = (empty($arraydata['selected']) ? 0 : $arraydata['selected']); + + $return = '
'; + $return .= '
'; + $return .= ''; + $return .= img_picto('', $this->picto); + $return .= ''; + $return .= '
'; + $return .= ''.(method_exists($this, 'getNomUrl') ? $this->getNomUrl() : $this->ref).''; + if ($selected >= 0) { + $return .= ''; + } + if (property_exists($this, 'label')) { + $return .= '
'.$this->label.'
'; + } + if (property_exists($this, 'thirdparty') && is_object($this->thirdparty)) { + $return .= '
'.$this->thirdparty->getNomUrl(1).'
'; + } + if (property_exists($this, 'amount')) { + $return .= '
'; + $return .= ''.price($this->amount, 0, $langs, 1, -1, -1, $conf->currency).''; + } + if (method_exists($this, 'getLibStatut')) { + $return .= '
'.$this->getLibStatut(3).'
'; + } + $return .= '
'; + $return .= '
'; + $return .= '
'; + + return $return; + } + /** * Return the label of the status * diff --git a/htdocs/variants/list.php b/htdocs/variants/list.php index 5b48a51fff6..c5172659a94 100644 --- a/htdocs/variants/list.php +++ b/htdocs/variants/list.php @@ -34,14 +34,14 @@ $langs->loadLangs(array("products", "other")); $action = GETPOST('action', 'aZ09') ? GETPOST('action', 'aZ09') : 'view'; // The action 'create'/'add', 'edit'/'update', 'view', ... $massaction = GETPOST('massaction', 'alpha'); // The bulk action (combo box choice into lists) -$show_files = GETPOSTINT('show_files'); // Show files area generated by bulk actions ? +$show_files = GETPOSTINT('show_files'); // Show files area generated by bulk actions $confirm = GETPOST('confirm', 'alpha'); // Result of a confirmation $cancel = GETPOST('cancel', 'alpha'); // We click on a Cancel button -$toselect = GETPOST('toselect', 'array'); // Array of ids of elements selected into a list +$toselect = GETPOST('toselect', 'array:int'); // Array of ids of elements selected into a list $contextpage = GETPOST('contextpage', 'aZ') ? GETPOST('contextpage', 'aZ') : str_replace('_', '', basename(dirname(__FILE__)).basename(__FILE__, '.php')); // To manage different context of search $backtopage = GETPOST('backtopage', 'alpha'); // Go back to a dedicated page $optioncss = GETPOST('optioncss', 'aZ'); // Option for the css output (always '' except when 'print') -$mode = GETPOST('mode', 'aZ'); // The output mode ('list', 'kanban', 'hierarchy', 'calendar', ...) +$mode = GETPOST('mode', 'aZ'); // The display mode ('list', 'kanban', 'hierarchy', 'calendar', 'gantt', ...) $id = GETPOSTINT('id'); @@ -49,7 +49,7 @@ $id = GETPOSTINT('id'); $limit = GETPOSTINT('limit') ? GETPOSTINT('limit') : $conf->liste_limit; $sortfield = GETPOST('sortfield', 'aZ09comma'); $sortorder = GETPOST('sortorder', 'aZ09comma'); -$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT("page"); +$page = GETPOSTISSET('pageplusone') ? (GETPOSTINT('pageplusone') - 1) : GETPOSTINT('page'); if (empty($page) || $page < 0 || GETPOST('button_search', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // If $page is not defined, or '' or -1 or if we click on clear filters $page = 0; @@ -78,7 +78,7 @@ if (!$sortorder) { } // Initialize array of search criteria -$search_all = GETPOST('search_all', 'alphanohtml'); +$search_all = trim(GETPOST('search_all', 'alphanohtml')); $search = array(); foreach ($object->fields as $key => $val) { if (GETPOST('search_'.$key, 'alpha') !== '') { @@ -92,8 +92,8 @@ foreach ($object->fields as $key => $val) { $search['nb_of_values'] = GETPOST('search_nb_of_values', 'alpha'); $search['nb_products'] = GETPOST('search_nb_products', 'alpha'); -// List of fields to search into when doing a "search in all" $fieldstosearchall = array(); +// List of fields to search into when doing a "search in all" foreach ($object->fields as $key => $val) { if (!empty($val['searchall'])) { $fieldstosearchall['t.'.$key] = $val['label']; @@ -231,8 +231,8 @@ $form = new Form($db); $now = dol_now(); -$help_url = ''; $title = $langs->trans("ProductAttributes"); +$help_url = ''; $morejs = array(); $morecss = array(); @@ -252,6 +252,9 @@ $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListSelect', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; $sql = preg_replace('/,\s*$/', '', $sql); + +$sqlfields = $sql; // $sql fields to remove for count total + $sql .= " FROM ".MAIN_DB_PREFIX.$object->table_element." as t"; //if (isset($extrafields->attributes[$object->table_element]['label']) && is_array($extrafields->attributes[$object->table_element]['label']) && count($extrafields->attributes[$object->table_element]['label'])) { // $sql .= " LEFT JOIN ".MAIN_DB_PREFIX.$object->table_element."_extrafields as ef on (t.rowid = ef.fk_object)"; @@ -263,7 +266,7 @@ $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListFrom', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; if ($object->ismultientitymanaged == 1) { - $sql .= " WHERE t.entity IN (".getEntity($object->element).")"; + $sql .= " WHERE t.entity IN (".getEntity($object->element, (GETPOST('search_current_entity', 'int') ? 0 : 1)).")"; } else { $sql .= " WHERE 1 = 1"; } @@ -280,17 +283,17 @@ foreach ($search as $key => $val) { $mode_search = 2; } if ($search[$key] != '') { - $sql .= natural_search("t.".$db->escape($key), $search[$key], (($key == 'status') ? 2 : $mode_search)); + $sql .= natural_search("t.".$db->sanitize($key), $search[$key], (($key == 'status') ? 2 : $mode_search)); } } else { if (preg_match('/(_dtstart|_dtend)$/', $key) && $search[$key] != '') { $columnName = preg_replace('/(_dtstart|_dtend)$/', '', $key); if (preg_match('/^(date|timestamp|datetime)/', $object->fields[$columnName]['type'])) { if (preg_match('/_dtstart$/', $key)) { - $sql .= " AND t.".$db->escape($columnName)." >= '".$db->idate($search[$key])."'"; + $sql .= " AND t.".$db->sanitize($columnName)." >= '".$db->idate($search[$key])."'"; } if (preg_match('/_dtend$/', $key)) { - $sql .= " AND t.".$db->escape($columnName)." <= '".$db->idate($search[$key])."'"; + $sql .= " AND t.".$db->sanitize($columnName)." <= '".$db->idate($search[$key])."'"; } } } @@ -310,7 +313,7 @@ $sql .= $hookmanager->resPrint; $hasgroupby = true; $sql .= " GROUP BY "; foreach ($object->fields as $key => $val) { - $sql .= "t." . $key . ", "; + $sql .= "t.".$db->sanitize($key).", "; } // Add fields from extrafields //if (!empty($extrafields->attributes[$object->table_element]['label'])) { @@ -320,7 +323,7 @@ foreach ($object->fields as $key => $val) { //} // Add where from hooks $parameters = array(); -$reshook = $hookmanager->executeHooks('printFieldListGroupBy', $parameters, $object); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldListGroupBy', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; $sql = preg_replace("/,\s*$/", "", $sql); @@ -333,7 +336,7 @@ if ($search['nb_products'] != '') { } // Add HAVING from hooks $parameters = array(); -$reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters, $object); // Note that $action and $object may have been modified by hook +$reshook = $hookmanager->executeHooks('printFieldListHaving', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $sql .= empty($hookmanager->resPrint) ? "" : " ".$hookmanager->resPrint; // Count total nb of records @@ -349,7 +352,9 @@ if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) { $nbtotalofrecords++; }*/ /* The fast and low memory method to get and count full list converts the sql into a sql count */ - $sqlforcount = preg_replace("/^SELECT[a-z0-9\._\s\(\),]+FROM/i", "SELECT COUNT(*) as nbtotalofrecords FROM", $sql); + $sqlforcount = preg_replace('/^'.preg_quote($sqlfields, '/').'/', 'SELECT COUNT(*) as nbtotalofrecords', $sql); + $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount); + $resql = $db->query($sqlforcount); if ($resql) { if ($hasgroupby) { @@ -382,10 +387,10 @@ $num = $db->num_rows($resql); // Direct jump if only one record found -if ($num == 1 && getDolGlobalString('MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE') && $search_all && !$page) { +if ($num == 1 && getDolGlobalInt('MAIN_SEARCH_DIRECT_OPEN_IF_ONLY_ONE') && $search_all && !$page) { $obj = $db->fetch_object($resql); $id = $obj->rowid; - header("Location: " . dol_buildpath('/variants/card.php', 2) . '?id=' . $id); + header("Location: " . dol_buildpath('/variants/card.php', 2) . '?id='.((int) $id)); exit; } @@ -407,6 +412,12 @@ if (!empty($contextpage) && $contextpage != $_SERVER["PHP_SELF"]) { if ($limit > 0 && $limit != $conf->liste_limit) { $param .= '&limit='.((int) $limit); } +if ($optioncss != '') { + $param .= '&optioncss='.urlencode($optioncss); +} +if ($groupby != '') { + $param .= '&groupby='.urlencode($groupby); +} foreach ($search as $key => $val) { if (is_array($search[$key])) { foreach ($search[$key] as $skey) { @@ -422,9 +433,6 @@ foreach ($search as $key => $val) { $param .= '&search_'.$key.'='.urlencode($search[$key]); } } -if ($optioncss != '') { - $param .= '&optioncss='.urlencode($optioncss); -} // Add $param from extra fields include DOL_DOCUMENT_ROOT.'/core/tpl/extrafields_list_search_param.tpl.php'; // Add $param from hooks @@ -528,7 +536,7 @@ if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { print ''; } foreach ($object->fields as $key => $val) { - $searchkey = empty($search[$key]) ? '' : $search[$key]; + //$searchkey = empty($search[$key]) ? '' : $search[$key]; $cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']); if ($key == 'status') { $cssforfield .= ($cssforfield ? ' ' : '').'center'; @@ -542,7 +550,7 @@ foreach ($object->fields as $key => $val) { if (!empty($arrayfields['t.'.$key]['checked'])) { print ''; if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) { - print $form->selectarray('search_'.$key, $val['arrayofkeyval'], (isset($search[$key]) ? $search[$key] : ''), $val['notnull'], 0, 0, '', 1, 0, 0, '', 'maxwidth100'.($key == 'status' ? ' search_status width100 onrightofpage' : ''), 1); + print $form->selectarray('search_'.$key, $val['arrayofkeyval'], (isset($search[$key]) ? $search[$key] : ''), 1, 0, 0, '', 1, 0, 0, '', 'maxwidth100'.($key == 'status' ? ' search_status width100 onrightofpage' : ''), 1); } elseif ((strpos($val['type'], 'integer:') === 0) || (strpos($val['type'], 'sellist:') === 0)) { print $object->showInputField($val, $key, (isset($search[$key]) ? $search[$key] : ''), '', '', 'search_', $cssforfield.' maxwidth250', 1); } elseif (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { @@ -555,7 +563,7 @@ foreach ($object->fields as $key => $val) { } elseif ($key == 'lang') { require_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php'; $formadmin = new FormAdmin($db); - print $formadmin->select_language($search[$key], 'search_lang', 0, null, 1, 0, 0, 'minwidth100imp maxwidth125', 2); + print $formadmin->select_language((isset($search[$key]) ? $search[$key] : ''), 'search_lang', 0, null, 1, 0, 0, 'minwidth100imp maxwidth125', 2); } else { print ''; } @@ -598,6 +606,7 @@ $totalarray['nbfield'] = 0; // Fields title label // -------------------------------------------------------------------- print ''; +// Action column if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { print getTitleFieldOfList($selectedfields, 0, $_SERVER["PHP_SELF"], '', '', '', '', $sortfield, $sortorder, 'center maxwidthsearch ')."\n"; $totalarray['nbfield']++; @@ -629,10 +638,12 @@ print $hookmanager->resPrint; $key = 'nb_of_values'; if (!empty($arrayfields[$key]['checked'])) { print getTitleFieldOfList($arrayfields[$key]['label'], 0, $_SERVER['PHP_SELF'], $key, '', $param, 'class="center"', $sortfield, $sortorder, 'center ')."\n"; + $totalarray['nbfield']++; } $key = 'nb_products'; if (!empty($arrayfields[$key]['checked'])) { print getTitleFieldOfList($arrayfields[$key]['label'], 0, $_SERVER['PHP_SELF'], $key, '', $param, 'class="center"', $sortfield, $sortorder, 'center ')."\n"; + $totalarray['nbfield']++; } // Action column if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { @@ -641,6 +652,7 @@ if (!getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { } // Move print getTitleFieldOfList('', 0, '', '', '', '', '', '', '', 'linecolmove ')."\n"; +$totalarray['nbfield']++; print ''."\n"; @@ -690,7 +702,7 @@ while ($i < $imaxinloop) { print ''; } } else { - // Show here line of result + // Show line of result $j = 0; print ''; // Action column @@ -708,6 +720,7 @@ while ($i < $imaxinloop) { $totalarray['nbfield']++; } } + // Fields foreach ($object->fields as $key => $val) { $cssforfield = (empty($val['csslist']) ? (empty($val['css']) ? '' : $val['css']) : $val['csslist']); if (in_array($val['type'], array('date', 'datetime', 'timestamp'))) { @@ -717,19 +730,19 @@ while ($i < $imaxinloop) { } if (in_array($val['type'], array('timestamp'))) { - $cssforfield .= ($cssforfield ? ' ' : '') . 'nowrap'; + $cssforfield .= ($cssforfield ? ' ' : '') . 'nowraponall'; } elseif ($key == 'ref') { - $cssforfield .= ($cssforfield ? ' ' : '') . 'nowrap'; + $cssforfield .= ($cssforfield ? ' ' : '') . 'nowraponall'; } - if (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price')) && !in_array($key, array('rowid', 'status')) && empty($val['arrayofkeyval'])) { + if (in_array($val['type'], array('double(24,8)', 'double(6,3)', 'integer', 'real', 'price')) && !in_array($key, array('id', 'rowid', 'ref', 'status')) && empty($val['arrayofkeyval'])) { $cssforfield .= ($cssforfield ? ' ' : '') . 'right'; } //if (in_array($key, array('fk_soc', 'fk_user', 'fk_warehouse'))) $cssforfield = 'tdoverflowmax100'; if (!empty($arrayfields['t.' . $key]['checked'])) { - print '$key)) { + print '$key)) { print ' title="'.dol_escape_htmltag($object->$key).'"'; } print '>'; @@ -807,6 +820,9 @@ while ($i < $imaxinloop) { print '' . img_down('default', 0, 'imgdownforline') . ''; } print ''; + if (!$i) { + $totalarray['nbfield']++; + } print '' . "\n"; } @@ -819,12 +835,13 @@ include DOL_DOCUMENT_ROOT.'/core/tpl/list_print_total.tpl.php'; // If no record found if ($num == 0) { - $colspan = 3; + $colspan = 1; foreach ($arrayfields as $key => $val) { if (!empty($val['checked'])) { $colspan++; } } + $colspan++; // For the move column print ''.$langs->trans("NoRecordFound").''; }