* Copyright (C) 2023-2024 Lionel Vessiller * Copyright (C) 2023-2024 Patrice Andreani * Copyright (C) 2024 Frédéric France * Copyright (C) 2024 MDW * * 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/webportal/class/html.formlistwebportal.class.php * \ingroup webportal * \brief File of class with all html predefined components for WebPortal */ require_once DOL_DOCUMENT_ROOT . '/societe/class/societe.class.php'; require_once DOL_DOCUMENT_ROOT . '/core/class/discount.class.php'; require_once DOL_DOCUMENT_ROOT . '/webportal/class/html.formwebportal.class.php'; /** * Class to manage generation of HTML components * Only common components for WebPortal must be here. * */ class FormListWebPortal { /** * @var string Action */ public $action = ''; /** * @var DoliDB Database */ public $db; /** * @var Form Instance of the Form */ public $form; /** * @var CommonObject Object */ public $object; /** * @var int Limit (-1 to get limit from conf, 0 no limit, or Nb to show) */ public $limit = -1; /** * @var int Page (1 by default) */ public $page = 1; /** * @var string Sort field */ public $sortfield = ''; /** * @var string Sort order */ public $sortorder = ''; /** * @var string Title key to translate */ public $titleKey = ''; /** * @var string Title desc key to translate */ public $titleDescKey = ''; /** * @var string Page context */ public $contextpage = ''; /** * @var array Search filters */ public $search = array(); /** * @var array Array of fields */ public $arrayfields = array(); /** * @var array Company static list (cache) */ public $companyStaticList = array(); /** * Constructor * * @param DoliDB $db Database handler */ public function __construct($db) { $this->db = $db; $this->form = new FormWebPortal($this->db); } /** * Init * * @param string $elementEn Element (english) : "propal", "order", "invoice" * @return void */ public function init($elementEn) { // keep compatibility if ($elementEn == 'commande') { $elementEn = 'order'; } elseif ($elementEn == 'facture') { $elementEn = 'invoice'; } // load module libraries dol_include_once('/webportal/class/webportal' . $elementEn . '.class.php'); // Initialize a technical objects $objectclass = 'WebPortal' . ucfirst($elementEn); $object = new $objectclass($this->db); // set form list $this->action = GETPOST('action', 'aZ09'); $this->object = $object; $this->limit = GETPOSTISSET('limit') ? GETPOSTINT('limit') : -1; $this->sortfield = GETPOST('sortfield', 'aZ09comma'); $this->sortorder = GETPOST('sortorder', 'aZ09comma'); $this->page = GETPOSTISSET('page') ? GETPOSTINT('page') : 1; $this->titleKey = $objectclass . 'ListTitle'; // Initialize array of search criteria //$search_all = GETPOST('search_all', 'alphanohtml'); $search = array(); foreach ($object->fields as $key => $val) { if (GETPOST('search_' . $key, 'alpha') !== '') { $search[$key] = GETPOST('search_' . $key, 'alpha'); } if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { $postDateStart = GETPOST('search_' . $key . '_dtstart', 'alphanohtml'); $postDateEnd = GETPOST('search_' . $key . '_dtend', 'alphanohtml'); // extract date YYYY-MM-DD for year, month and day $dateStartArr = explode('-', $postDateStart); $dateEndArr = explode('-', $postDateEnd); if (count($dateStartArr) == 3) { $dateStartYear = (int) $dateStartArr[0]; $dateStartMonth = (int) $dateStartArr[1]; $dateStartDay = (int) $dateStartArr[2]; $search[$key . '_dtstart'] = dol_mktime(0, 0, 0, $dateStartMonth, $dateStartDay, $dateStartYear); } if (count($dateEndArr) == 3) { $dateEndYear = (int) $dateEndArr[0]; $dateEndMonth = (int) $dateEndArr[1]; $dateEndDay = (int) $dateEndArr[2]; $search[$key . '_dtend'] = dol_mktime(23, 59, 59, $dateEndMonth, $dateEndDay, $dateEndYear); } } } $this->search = $search; // List of fields to search into when doing a "search in all" //$fieldstosearchall = array(); // Definition of array of fields for columns $arrayfields = array(); foreach ($object->fields as $key => $val) { // If $val['visible']==0, then we never show the field if (!empty($val['visible'])) { $visible = (int) dol_eval((string) $val['visible'], 1); $arrayfields['t.' . $key] = array( 'label' => $val['label'], 'checked' => (($visible < 0) ? 0 : 1), 'enabled' => (abs($visible) != 3 && (bool) dol_eval($val['enabled'], 1)), 'position' => $val['position'], 'help' => isset($val['help']) ? $val['help'] : '' ); } } if ($elementEn == 'invoice') { $arrayfields['remain_to_pay'] = array('type' => 'price', 'label' => 'RemainderToPay', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10000, 'help' => '',); } $arrayfields['download_link'] = array('label' => 'File', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10001, 'help' => '',); if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) { $arrayfields['signature_link'] = array('label' => 'Signature', 'checked' => 1, 'enabled' => 1, 'visible' => 1, 'position' => 10002, 'help' => '',); } $object->fields = dol_sort_array($object->fields, 'position'); //$arrayfields['anotherfield'] = array('type'=>'integer', 'label'=>'AnotherField', 'checked'=>1, 'enabled'=>1, 'position'=>90, 'csslist'=>'right'); $arrayfields = dol_sort_array($arrayfields, 'position'); $this->arrayfields = $arrayfields; } /** * Do actions * * @return void */ public function doActions() { $object = $this->object; $search = $this->search; // Purge search criteria if (GETPOST('button_removefilter_x', 'alpha') || GETPOST('button_removefilter.x', 'alpha') || GETPOST('button_removefilter', 'alpha')) { // All tests are required to be compatible with all browsers foreach ($object->fields as $key => $val) { $search[$key] = ''; if (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { $search[$key . '_dtstart'] = ''; $search[$key . '_dtend'] = ''; } } $this->search = $search; } } /** * List for an element in the page context * * @param Context $context Context object * @return string Html output */ public function elementList($context) { global $conf, $hookmanager, $langs; $html = ''; // initialize $action = $this->action; $object = $this->object; $limit = $this->limit; $page = $this->page; $sortfield = $this->sortfield; $sortorder = $this->sortorder; $titleKey = $this->titleKey; $contextpage = $this->contextpage; $search = $this->search; $arrayfields = $this->arrayfields; $elementEn = $object->element; if ($object->element == 'commande') { $elementEn = 'order'; } elseif ($object->element == 'facture') { $elementEn = 'invoice'; } // specific for invoice and remain to pay $discount = null; if ($elementEn == 'invoice') { $discount = new DiscountAbsolute($this->db); } // empty value for select $emptyValueKey = ($elementEn == 'order' ? -5 : -1); if ($limit < 0) { $limit = $conf->liste_limit; } if ($page <= 0) { $page = 1; } $offset = $limit * ($page - 1); if (!$sortfield) { reset($object->fields); // Reset is required to avoid key() to return null. $sortfield = 't.' . key($object->fields); // Set here default search field. By default 1st field in definition. } if (!$sortorder) { $sortorder = 'DESC'; } $socid = (int) $context->logged_thirdparty->id; // Build and execute select // -------------------------------------------------------------------- $sql = "SELECT "; $sql .= $object->getFieldList('t'); $sql .= ", t.entity as element_entity"; // Add fields from hooks $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 " . $this->db->prefix() . $object->table_element . " as t"; // Add table from hooks $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, (GETPOSTINT('search_current_entity') ? 0 : 1)) . ")"; } else { $sql .= " WHERE 1 = 1"; } // filter on logged third-party $sql .= " AND t.fk_soc = " . ((int) $socid); // discard record with status draft $sql .= " AND t.fk_statut <> 0"; foreach ($search as $key => $val) { if (array_key_exists($key, $object->fields)) { if (($key == 'status' || $key == 'fk_statut') && $search[$key] == $emptyValueKey) { continue; } $mode_search = (($object->isInt($object->fields[$key]) || $object->isFloat($object->fields[$key])) ? 1 : 0); if ((strpos($object->fields[$key]['type'], 'integer:') === 0) || (strpos($object->fields[$key]['type'], 'sellist:') === 0) || !empty($object->fields[$key]['arrayofkeyval'])) { if ($search[$key] == "$emptyValueKey" || ($search[$key] === '0' && (empty($object->fields[$key]['arrayofkeyval']) || !array_key_exists('0', $object->fields[$key]['arrayofkeyval'])))) { $search[$key] = ''; } $mode_search = 2; } if ($search[$key] != '') { $sql .= natural_search("t." . $this->db->escape($key), $search[$key], (($key == 'status' || $key == 'fk_statut') ? ($search[$key] < 0 ? 1 : 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." . $this->db->escape($columnName) . " >= '" . $this->db->idate($search[$key]) . "'"; } if (preg_match('/_dtend$/', $key)) { $sql .= " AND t." . $this->db->escape($columnName) . " <= '" . $this->db->idate($search[$key]) . "'"; } } } } } //if ($search_all) { // $sql .= natural_search(array_keys($fieldstosearchall), $search_all); //} // Add where from hooks $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListWhere', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $sql .= $hookmanager->resPrint; // Count total nb of records $nbtotalofrecords = 0; if (!getDolGlobalInt('MAIN_DISABLE_FULL_SCANLIST')) { /* The fast and low memory method to get and count full list converts the sql into a sql count */ $sqlforcount = preg_replace('/^' . preg_quote($sqlfields, '/') . '/', 'SELECT COUNT(*) as nbtotalofrecords', $sql); $sqlforcount = preg_replace('/GROUP BY .*$/', '', $sqlforcount); $resql = $this->db->query($sqlforcount); if ($resql) { $objforcount = $this->db->fetch_object($resql); $nbtotalofrecords = (int) $objforcount->nbtotalofrecords; } else { dol_print_error($this->db); } if ($offset > $nbtotalofrecords) { // if total resultset is smaller than the paging size (filtering), goto and load page 1 $page = 1; $offset = 0; } $this->db->free($resql); } // Complete request and execute it with limit $sql .= $this->db->order($sortfield, $sortorder); if ($limit) { $sql .= $this->db->plimit($limit, $offset); } $resql = $this->db->query($sql); if (!$resql) { dol_print_error($this->db); return ''; } $num = $this->db->num_rows($resql); if ($limit > 0) { $nbpages = ceil($nbtotalofrecords / $limit); } if ($nbpages <= 0) { $nbpages = 1; } // make array[sort field => sort order] for this list $sortList = array(); $sortFieldList = explode(",", $sortfield); $sortOrderList = explode(",", $sortorder); $sortFieldIndex = 0; if (!empty($sortFieldList)) { foreach ($sortFieldList as $sortField) { if (isset($sortOrderList[$sortFieldIndex])) { $sortList[$sortField] = $sortOrderList[$sortFieldIndex]; } $sortFieldIndex++; } } $param = ''; $param .= '&contextpage=' . urlencode($contextpage); $param .= '&limit=' . $limit; foreach ($search as $key => $val) { if (is_array($search[$key])) { foreach ($search[$key] as $skey) { if ($skey != '') { $param .= '&search_' . $key . '[]=' . urlencode($skey); } } } elseif (preg_match('/(_dtstart|_dtend)$/', $key) && !empty($val)) { $param .= '&search_' . $key . 'month=' . (GETPOSTINT('search_' . $key . 'month')); $param .= '&search_' . $key . 'day=' . (GETPOSTINT('search_' . $key . 'day')); $param .= '&search_' . $key . 'year=' . (GETPOSTINT('search_' . $key . 'year')); } elseif ($search[$key] != '') { $param .= '&search_' . $key . '=' . urlencode($search[$key]); } } // Add $param from hooks $parameters = array(); $reshook = $hookmanager->executeHooks('printFieldListSearchParam', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $param .= $hookmanager->resPrint; $url_file = $context->getControllerUrl($context->controller); $html .= '
' . "\n"; $html .= $context->getFormToken(); $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; $html .= ''; // pagination $pagination_param = $param . '&sortfield=' . $sortfield . '&sortorder=' . $sortorder; $html .= ''; // table with search filters and column titles $html .= ''; // title and desc for table //if ($titleKey != '') { // $html .= ''; //} $html .= ''; // Fields title search // -------------------------------------------------------------------- $html .= ''; // Action column // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { $html .= ''; // } foreach ($object->fields as $key => $val) { if (!empty($arrayfields['t.' . $key]['checked'])) { $html .= ''; } } // Fields from hook $parameters = array('arrayfields' => $arrayfields); $reshook = $hookmanager->executeHooks('printFieldListOption', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $html .= $hookmanager->resPrint; // Remain to pay if (!empty($arrayfields['remain_to_pay']['checked'])) { $html .= ''; } // Download link if (!empty($arrayfields['download_link']['checked'])) { $html .= ''; } $html .= ''; // Signature link if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) { if (!empty($arrayfields['signature_link']['checked'])) { $html .= ''; } } $totalarray = array(); $totalarray['nbfield'] = 0; // Fields title label // -------------------------------------------------------------------- $html .= ''; // Action column // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { $html .= ''; $totalarray['nbfield']++; // } foreach ($object->fields as $key => $val) { $tableKey = 't.' . $key; if (!empty($arrayfields[$tableKey]['checked'])) { $tableOrder = ''; if (array_key_exists($tableKey, $sortList)) { $tableOrder = strtolower($sortList[$tableKey]); } $url_param = $url_file . '&sortfield=' . $tableKey . '&sortorder=' . ($tableOrder == 'desc' ? 'asc' : 'desc') . $param; $html .= ''; $totalarray['nbfield']++; } } // Remain to pay if (!empty($arrayfields['remain_to_pay']['checked'])) { $html .= ''; $totalarray['nbfield']++; } // Download link if (!empty($arrayfields['download_link']['checked'])) { $html .= ''; $totalarray['nbfield']++; } // Signature link if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) { if (!empty($arrayfields['signature_link']['checked'])) { $html .= ''; $totalarray['nbfield']++; } } // Hook fields $parameters = array('arrayfields' => $arrayfields, 'sortfield' => $sortfield, 'sortorder' => $sortorder, 'totalarray' => &$totalarray); $reshook = $hookmanager->executeHooks('printFieldListTitle', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $html .= $hookmanager->resPrint; $html .= ''; $html .= ''; $html .= ''; // Store company $idCompany = (int) $socid; if (!isset($this->companyStaticList[$socid])) { $companyStatic = new Societe($this->db); $companyStatic->fetch($idCompany); $this->companyStaticList[$idCompany] = $companyStatic; } $companyStatic = $this->companyStaticList[$socid]; // Loop on record // -------------------------------------------------------------------- $i = 0; $totalarray = [ 'nbfield' => 0, 'totalizable' => [], ]; $imaxinloop = ($limit ? min($num, $limit) : $num); while ($i < $imaxinloop) { $obj = $this->db->fetch_object($resql); if (empty($obj)) { break; // Should not happen } // Store properties in $object $object->setVarsFromFetchObj($obj); // specific to get invoice status (depends on payment) $payment = -1; if ($elementEn == 'invoice') { // paid sum $payment = $object->getSommePaiement(); $totalcreditnotes = $object->getSumCreditNotesUsed(); $totaldeposits = $object->getSumDepositsUsed(); // remain to pay $totalpay = $payment + $totalcreditnotes + $totaldeposits; $remaintopay = price2num($object->total_ttc - $totalpay); if ($object->status == Facture::STATUS_CLOSED && $object->close_code == 'discount_vat') { // If invoice closed with discount for anticipated payment $remaintopay = 0; } if ($object->type == Facture::TYPE_CREDIT_NOTE && $obj->paye == 1 && $discount) { $remaincreditnote = $discount->getAvailableDiscounts($companyStatic, '', 'rc.fk_facture_source=' . $object->id); $remaintopay = -$remaincreditnote; } } // Show line of result $html .= ''; // if (getDolGlobalString('MAIN_CHECKBOX_LEFT_COLUMN')) { $html .= ''; if (!$i) { $totalarray['nbfield']++; } // } foreach ($object->fields as $key => $val) { if (!empty($arrayfields['t.' . $key]['checked'])) { $html .= ''; if (!$i) { $totalarray['nbfield']++; } if (!empty($val['isameasure']) && $val['isameasure'] == 1) { if (!$i) { $totalarray['pos'][$totalarray['nbfield']] = 't.' . $key; } if (!isset($totalarray['val'])) { $totalarray['val'] = array(); } if (!isset($totalarray['val']['t.' . $key])) { $totalarray['val']['t.' . $key] = 0; } $totalarray['val']['t.' . $key] += $object->$key; } } } // Remain to pay if (!empty($arrayfields['remain_to_pay']['checked'])) { $html .= ''; if (!$i) { $totalarray['nbfield']++; } } // Download link if (!empty($arrayfields['download_link']['checked'])) { $element = $object->element; $html .= ''; if (!$i) { $totalarray['nbfield']++; } } // Signature link if ($elementEn == "propal" && getDolGlobalString("PROPOSAL_ALLOW_ONLINESIGN") != 0) { if (!empty($arrayfields['signature_link']['checked'])) { $html .= ''; if (!$i) { $totalarray['nbfield']++; } } } // Fields from hook $parameters = array('arrayfields' => $arrayfields, 'object' => $object, 'obj' => $obj, 'i' => $i, 'totalarray' => &$totalarray); $reshook = $hookmanager->executeHooks('printFieldListValue', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $html .= $hookmanager->resPrint; $html .= ''; $i++; } // Move fields of totalizable into the common array pos and val if (!empty($totalarray['totalizable']) && is_array($totalarray['totalizable'])) { foreach ($totalarray['totalizable'] as $keytotalizable => $valtotalizable) { $totalarray['pos'][$valtotalizable['pos']] = $keytotalizable; $totalarray['val'][$keytotalizable] = isset($valtotalizable['total']) ? $valtotalizable['total'] : 0; } } // Show total line if (isset($totalarray['pos'])) { $html .= ''; $i = 0; while ($i < $totalarray['nbfield']) { $i++; if (!empty($totalarray['pos'][$i])) { $html .= ''; } else { if ($i == 1) { $html .= ''; } else { $html .= ''; } } } $html .= ''; } // If no record found if ($num == 0) { $colspan = 1; foreach ($arrayfields as $key => $val) { if (!empty($val['checked'])) { $colspan++; } } $html .= ''; } $html .= ''; $this->db->free($resql); $parameters = array('arrayfields' => $arrayfields, 'sql' => $sql); $reshook = $hookmanager->executeHooks('printFieldListFooter', $parameters, $object, $action); // Note that $action and $object may have been modified by hook $html .= $hookmanager->resPrint; $html .= '
'; // $html .= $langs->trans($titleKey) . '
'; // if ($titleDescKey != '') { // $html .= '' . $langs->trans($titleDescKey) . ''; // } // $html .= '
'; $html .= ' '; $html .= ' '; $html .= ''; if (!empty($val['arrayofkeyval']) && is_array($val['arrayofkeyval'])) { $html .= $this->form->selectarray('search_' . $key, $val['arrayofkeyval'], (isset($search[$key]) ? $search[$key] : ''), $val['notnull'], 0, 0, '', 1, 0, 0, '', ''); } elseif (preg_match('/^(date|timestamp|datetime)/', $val['type'])) { $postDateStart = GETPOST('search_' . $key . '_dtstart', 'alphanohtml'); $postDateEnd = GETPOST('search_' . $key . '_dtend', 'alphanohtml'); $html .= '
'; $html .= $this->form->inputDate('search_' . $key . '_dtstart', $postDateStart ? $postDateStart : '', $langs->trans('From')); $html .= '
'; $html .= '
'; $html .= $this->form->inputDate('search_' . $key . '_dtend', $postDateEnd ? $postDateEnd : '', $langs->trans('to')); $html .= '
'; } else { $html .= ''; } $html .= '
'; $html .= ''; $html .= '
'; $html .= '
'; $html .= ''; $html .= $langs->trans($arrayfields['t.' . $key]['label']); $html .= ''; $html .= ''; $html .= $langs->trans($arrayfields['remain_to_pay']['label']); $html .= ''; $html .= $langs->trans($arrayfields['download_link']['label']); $html .= ''; $html .= $langs->trans($arrayfields['signature_link']['label']); $html .= '
'; $html .= ''; if ($key == 'status' || $key == 'fk_statut') { if ($elementEn == 'invoice') { // specific to get invoice status (depends on payment) $html .= $object->getLibStatut(5, $payment); } else { $html .= $object->getLibStatut(5); } } elseif ($key == 'rowid') { $html .= $this->form->showOutputFieldForObject($object, $val, $key, $object->id, ''); } else { $html .= $this->form->showOutputFieldForObject($object, $val, $key, $object->$key, ''); } $html .= ''; $html .= $this->form->showOutputFieldForObject($object, $arrayfields['remain_to_pay'], 'remain_to_pay', $remaintopay, ''); //$html .= price($remaintopay); $html .= ''; $filename = dol_sanitizeFileName($obj->ref); $filedir = $conf->{$element}->multidir_output[$obj->element_entity] . '/' . dol_sanitizeFileName($obj->ref); $html .= $this->form->getDocumentsLink($element, $filename, $filedir); $html .= ''; if ($object->fk_statut == Propal::STATUS_VALIDATED) { $html .= $this->form->getSignatureLink('proposal', $object); } $html .= '
'; $html .= price(!empty($totalarray['val'][$totalarray['pos'][$i]]) ? $totalarray['val'][$totalarray['pos'][$i]] : 0); $html .= '' . $langs->trans("Total") . '
' . $langs->trans("NoRecordFound") . '
'; $html .= '
'; return $html; } /** * Generate with pagination navigaion * * @param string $url Url of current page * @param int $nbPages Total of pages results * @param int $currentPage Number of current page * @return string */ public static function generatePageListNav(string $url, int $nbPages, int $currentPage) { global $langs; // Return nothing (no navigation bar), if there is only 1 page. if ($nbPages <= 1) { return ''; } $pSep = strpos($url, '?') === false ? '?' : '&'; $html = '
    '; if ($currentPage > 1) { $html .= '
  • '; } $maxPaginItem = min($nbPages, 5); $minPageNum = max(1, $currentPage - 3); $maxPageNum = min($nbPages, $currentPage + 3); if ($minPageNum > 1) { $html .= '
  • 1
  • '; $html .= '
  • '; } for ($p = $minPageNum; $p <= $maxPageNum; $p++) { $html .= '
  • ' . $p . '
  • '; } if ($maxPaginItem < $nbPages) { $html .= '
  • '; $html .= '
  • ' . $nbPages . '
  • '; } if ($currentPage < $nbPages) { $html .= '
  • = $nbPages ? ' disabled' : '') . '>
  • '; } $html .= '
'; return $html; } }