[ Index ]

PHP Cross Reference of Joomla 4.2.2 documentation

title

Body

[close]

/libraries/src/Pagination/ -> Pagination.php (source)

   1  <?php
   2  
   3  /**
   4   * Joomla! Content Management System
   5   *
   6   * @copyright  (C) 2006 Open Source Matters, Inc. <https://www.joomla.org>
   7   * @license    GNU General Public License version 2 or later; see LICENSE.txt
   8   */
   9  
  10  namespace Joomla\CMS\Pagination;
  11  
  12  use Joomla\CMS\Application\CMSApplication;
  13  use Joomla\CMS\Factory;
  14  use Joomla\CMS\HTML\HTMLHelper;
  15  use Joomla\CMS\Language\Text;
  16  use Joomla\CMS\Layout\LayoutHelper;
  17  use Joomla\CMS\Router\Route;
  18  
  19  // phpcs:disable PSR1.Files.SideEffects
  20  \defined('JPATH_PLATFORM') or die;
  21  // phpcs:enable PSR1.Files.SideEffects
  22  
  23  /**
  24   * Pagination Class. Provides a common interface for content pagination for the Joomla! CMS.
  25   *
  26   * @since  1.5
  27   */
  28  class Pagination
  29  {
  30      /**
  31       * @var    integer  The record number to start displaying from.
  32       * @since  1.5
  33       */
  34      public $limitstart = null;
  35  
  36      /**
  37       * @var    integer  Number of rows to display per page.
  38       * @since  1.5
  39       */
  40      public $limit = null;
  41  
  42      /**
  43       * @var    integer  Total number of rows.
  44       * @since  1.5
  45       */
  46      public $total = null;
  47  
  48      /**
  49       * @var    integer  Prefix used for request variables.
  50       * @since  1.6
  51       */
  52      public $prefix = null;
  53  
  54      /**
  55       * @var    integer  Value pagination object begins at
  56       * @since  3.0
  57       */
  58      public $pagesStart;
  59  
  60      /**
  61       * @var    integer  Value pagination object ends at
  62       * @since  3.0
  63       */
  64      public $pagesStop;
  65  
  66      /**
  67       * @var    integer  Current page
  68       * @since  3.0
  69       */
  70      public $pagesCurrent;
  71  
  72      /**
  73       * @var    integer  Total number of pages
  74       * @since  3.0
  75       */
  76      public $pagesTotal;
  77  
  78      /**
  79       * @var    boolean  The flag indicates whether to add limitstart=0 to URL
  80       * @since  3.9.0
  81       */
  82      public $hideEmptyLimitstart = false;
  83  
  84      /**
  85       * @var    boolean  View all flag
  86       * @since  3.0
  87       */
  88      protected $viewall = false;
  89  
  90      /**
  91       * Additional URL parameters to be added to the pagination URLs generated by the class.  These
  92       * may be useful for filters and extra values when dealing with lists and GET requests.
  93       *
  94       * @var    array
  95       * @since  3.0
  96       */
  97      protected $additionalUrlParams = array();
  98  
  99      /**
 100       * @var    CMSApplication  The application object
 101       * @since  3.4
 102       */
 103      protected $app = null;
 104  
 105      /**
 106       * Pagination data object
 107       *
 108       * @var    object
 109       * @since  3.4
 110       */
 111      protected $data;
 112  
 113      /**
 114       * Constructor.
 115       *
 116       * @param   integer         $total       The total number of items.
 117       * @param   integer         $limitstart  The offset of the item to start at.
 118       * @param   integer         $limit       The number of items to display per page.
 119       * @param   string          $prefix      The prefix used for request variables.
 120       * @param   CMSApplication  $app         The application object
 121       *
 122       * @since   1.5
 123       */
 124      public function __construct($total, $limitstart, $limit, $prefix = '', CMSApplication $app = null)
 125      {
 126          // Value/type checking.
 127          $this->total = (int) $total;
 128          $this->limitstart = (int) max($limitstart, 0);
 129          $this->limit = (int) max($limit, 0);
 130          $this->prefix = $prefix;
 131          $this->app = $app ?: Factory::getApplication();
 132  
 133          if ($this->limit > $this->total) {
 134              $this->limitstart = 0;
 135          }
 136  
 137          if (!$this->limit) {
 138              $this->limit = $total;
 139              $this->limitstart = 0;
 140          }
 141  
 142          /*
 143           * If limitstart is greater than total (i.e. we are asked to display records that don't exist)
 144           * then set limitstart to display the last natural page of results
 145           */
 146          if ($this->limitstart > $this->total - $this->limit) {
 147              $this->limitstart = max(0, (int) (ceil($this->total / $this->limit) - 1) * $this->limit);
 148          }
 149  
 150          // Set the total pages and current page values.
 151          if ($this->limit > 0) {
 152              $this->pagesTotal = (int) ceil($this->total / $this->limit);
 153              $this->pagesCurrent = (int) ceil(($this->limitstart + 1) / $this->limit);
 154          }
 155  
 156          // Set the pagination iteration loop values.
 157          $displayedPages = 10;
 158          $this->pagesStart = $this->pagesCurrent - ($displayedPages / 2);
 159  
 160          if ($this->pagesStart < 1) {
 161              $this->pagesStart = 1;
 162          }
 163  
 164          if ($this->pagesStart + $displayedPages > $this->pagesTotal) {
 165              $this->pagesStop = $this->pagesTotal;
 166  
 167              if ($this->pagesTotal < $displayedPages) {
 168                  $this->pagesStart = 1;
 169              } else {
 170                  $this->pagesStart = $this->pagesTotal - $displayedPages + 1;
 171              }
 172          } else {
 173              $this->pagesStop = $this->pagesStart + $displayedPages - 1;
 174          }
 175  
 176          // If we are viewing all records set the view all flag to true.
 177          if ($limit === 0) {
 178              $this->viewall = true;
 179          }
 180      }
 181  
 182      /**
 183       * Method to set an additional URL parameter to be added to all pagination class generated
 184       * links.
 185       *
 186       * @param   string  $key    The name of the URL parameter for which to set a value.
 187       * @param   mixed   $value  The value to set for the URL parameter.
 188       *
 189       * @return  mixed  The old value for the parameter.
 190       *
 191       * @since   1.6
 192       */
 193      public function setAdditionalUrlParam($key, $value)
 194      {
 195          // Get the old value to return and set the new one for the URL parameter.
 196          $result = $this->additionalUrlParams[$key] ?? null;
 197  
 198          // If the passed parameter value is null unset the parameter, otherwise set it to the given value.
 199          if ($value === null) {
 200              unset($this->additionalUrlParams[$key]);
 201          } else {
 202              $this->additionalUrlParams[$key] = $value;
 203          }
 204  
 205          return $result;
 206      }
 207  
 208      /**
 209       * Method to get an additional URL parameter (if it exists) to be added to
 210       * all pagination class generated links.
 211       *
 212       * @param   string  $key  The name of the URL parameter for which to get the value.
 213       *
 214       * @return  mixed  The value if it exists or null if it does not.
 215       *
 216       * @since   1.6
 217       */
 218      public function getAdditionalUrlParam($key)
 219      {
 220          return $this->additionalUrlParams[$key] ?? null;
 221      }
 222  
 223      /**
 224       * Return the rationalised offset for a row with a given index.
 225       *
 226       * @param   integer  $index  The row index
 227       *
 228       * @return  integer  Rationalised offset for a row with a given index.
 229       *
 230       * @since   1.5
 231       */
 232      public function getRowOffset($index)
 233      {
 234          return $index + 1 + $this->limitstart;
 235      }
 236  
 237      /**
 238       * Return the pagination data object, only creating it if it doesn't already exist.
 239       *
 240       * @return  \stdClass  Pagination data object.
 241       *
 242       * @since   1.5
 243       */
 244      public function getData()
 245      {
 246          if (!$this->data) {
 247              $this->data = $this->_buildDataObject();
 248          }
 249  
 250          return $this->data;
 251      }
 252  
 253      /**
 254       * Create and return the pagination pages counter string, ie. Page 2 of 4.
 255       *
 256       * @return  string   Pagination pages counter string.
 257       *
 258       * @since   1.5
 259       */
 260      public function getPagesCounter()
 261      {
 262          $html = null;
 263  
 264          if ($this->pagesTotal > 1) {
 265              $html .= Text::sprintf('JLIB_HTML_PAGE_CURRENT_OF_TOTAL', $this->pagesCurrent, $this->pagesTotal);
 266          }
 267  
 268          return $html;
 269      }
 270  
 271      /**
 272       * Create and return the pagination result set counter string, e.g. Results 1-10 of 42
 273       *
 274       * @return  string   Pagination result set counter string.
 275       *
 276       * @since   1.5
 277       */
 278      public function getResultsCounter()
 279      {
 280          $html = null;
 281          $fromResult = $this->limitstart + 1;
 282  
 283          // If the limit is reached before the end of the list.
 284          if ($this->limitstart + $this->limit < $this->total) {
 285              $toResult = $this->limitstart + $this->limit;
 286          } else {
 287              $toResult = $this->total;
 288          }
 289  
 290          // If there are results found.
 291          if ($this->total > 0) {
 292              $msg = Text::sprintf('JLIB_HTML_RESULTS_OF', $fromResult, $toResult, $this->total);
 293              $html .= "\n" . $msg;
 294          } else {
 295              $html .= "\n" . Text::_('JLIB_HTML_NO_RECORDS_FOUND');
 296          }
 297  
 298          return $html;
 299      }
 300  
 301      /**
 302       * Create and return the pagination page list string, ie. Previous, Next, 1 2 3 ... x.
 303       *
 304       * @return  string  Pagination page list string.
 305       *
 306       * @since   1.5
 307       */
 308      public function getPagesLinks()
 309      {
 310          // Build the page navigation list.
 311          $data = $this->_buildDataObject();
 312  
 313          $list           = array();
 314          $list['prefix'] = $this->prefix;
 315  
 316          $chromePath = JPATH_THEMES . '/' . $this->app->getTemplate() . '/html/pagination.php';
 317  
 318          if (is_file($chromePath)) {
 319              include_once $chromePath;
 320          }
 321  
 322          // Build the select list
 323          if ($data->all->base !== null) {
 324              $list['all']['active'] = true;
 325              $list['all']['data']   = $this->_item_active($data->all);
 326          } else {
 327              $list['all']['active'] = false;
 328              $list['all']['data']   = $this->_item_inactive($data->all);
 329          }
 330  
 331          if ($data->start->base !== null) {
 332              $list['start']['active'] = true;
 333              $list['start']['data']   = $this->_item_active($data->start);
 334          } else {
 335              $list['start']['active'] = false;
 336              $list['start']['data']   = $this->_item_inactive($data->start);
 337          }
 338  
 339          if ($data->previous->base !== null) {
 340              $list['previous']['active'] = true;
 341              $list['previous']['data']   = $this->_item_active($data->previous);
 342          } else {
 343              $list['previous']['active'] = false;
 344              $list['previous']['data']   = $this->_item_inactive($data->previous);
 345          }
 346  
 347          // Make sure it exists
 348          $list['pages'] = array();
 349  
 350          foreach ($data->pages as $i => $page) {
 351              if ($page->base !== null) {
 352                  $list['pages'][$i]['active'] = true;
 353                  $list['pages'][$i]['data']   = $this->_item_active($page);
 354              } else {
 355                  $list['pages'][$i]['active'] = false;
 356                  $list['pages'][$i]['data']   = $this->_item_inactive($page);
 357              }
 358          }
 359  
 360          if ($data->next->base !== null) {
 361              $list['next']['active'] = true;
 362              $list['next']['data']   = $this->_item_active($data->next);
 363          } else {
 364              $list['next']['active'] = false;
 365              $list['next']['data']   = $this->_item_inactive($data->next);
 366          }
 367  
 368          if ($data->end->base !== null) {
 369              $list['end']['active'] = true;
 370              $list['end']['data']   = $this->_item_active($data->end);
 371          } else {
 372              $list['end']['active'] = false;
 373              $list['end']['data']   = $this->_item_inactive($data->end);
 374          }
 375  
 376          if ($this->total > $this->limit) {
 377              return $this->_list_render($list);
 378          } else {
 379              return '';
 380          }
 381      }
 382  
 383      /**
 384       * Get the pagination links
 385       *
 386       * @param   string  $layoutId  Layout to render the links
 387       * @param   array   $options   Optional array with settings for the layout
 388       *
 389       * @return  string  Pagination links.
 390       *
 391       * @since   3.3
 392       */
 393      public function getPaginationLinks($layoutId = 'joomla.pagination.links', $options = array())
 394      {
 395          // Allow to receive a null layout
 396          $layoutId = $layoutId ?? 'joomla.pagination.links';
 397  
 398          $list = array(
 399              'prefix'       => $this->prefix,
 400              'limit'        => $this->limit,
 401              'limitstart'   => $this->limitstart,
 402              'total'        => $this->total,
 403              'limitfield'   => $this->getLimitBox(),
 404              'pagescounter' => $this->getPagesCounter(),
 405              'pages'        => $this->getPaginationPages(),
 406              'pagesTotal'   => $this->pagesTotal,
 407          );
 408  
 409          return LayoutHelper::render($layoutId, array('list' => $list, 'options' => $options));
 410      }
 411  
 412      /**
 413       * Create and return the pagination pages list, ie. Previous, Next, 1 2 3 ... x.
 414       *
 415       * @return  array  Pagination pages list.
 416       *
 417       * @since   3.3
 418       */
 419      public function getPaginationPages()
 420      {
 421          $list = array();
 422  
 423          if ($this->total > $this->limit) {
 424              // Build the page navigation list.
 425              $data = $this->_buildDataObject();
 426  
 427              // All
 428              $list['all']['active'] = $data->all->base !== null;
 429              $list['all']['data']   = $data->all;
 430  
 431              // Start
 432              $list['start']['active'] = $data->start->base !== null;
 433              $list['start']['data']   = $data->start;
 434  
 435              // Previous link
 436              $list['previous']['active'] = $data->previous->base !== null;
 437              $list['previous']['data']   = $data->previous;
 438  
 439              // Make sure it exists
 440              $list['pages'] = array();
 441  
 442              foreach ($data->pages as $i => $page) {
 443                  $list['pages'][$i]['active'] = $page->base !== null;
 444                  $list['pages'][$i]['data']   = $page;
 445              }
 446  
 447              $list['next']['active'] = $data->next->base !== null;
 448              $list['next']['data']   = $data->next;
 449  
 450              $list['end']['active'] = $data->end->base !== null;
 451              $list['end']['data']   = $data->end;
 452          }
 453  
 454          return $list;
 455      }
 456  
 457      /**
 458       * Return the pagination footer.
 459       *
 460       * @return  string  Pagination footer.
 461       *
 462       * @since   1.5
 463       */
 464      public function getListFooter()
 465      {
 466          // Keep B/C for overrides done with chromes
 467          $chromePath = JPATH_THEMES . '/' . $this->app->getTemplate() . '/html/pagination.php';
 468  
 469          if (is_file($chromePath)) {
 470              include_once $chromePath;
 471  
 472              if (\function_exists('pagination_list_footer')) {
 473                  @trigger_error(
 474                      'pagination_list_footer is deprecated. Use the layout joomla.pagination.links instead.',
 475                      E_USER_DEPRECATED
 476                  );
 477  
 478                  $list = array(
 479                      'prefix'       => $this->prefix,
 480                      'limit'        => $this->limit,
 481                      'limitstart'   => $this->limitstart,
 482                      'total'        => $this->total,
 483                      'limitfield'   => $this->getLimitBox(),
 484                      'pagescounter' => $this->getPagesCounter(),
 485                      'pageslinks'   => $this->getPagesLinks(),
 486                  );
 487  
 488                  return pagination_list_footer($list);
 489              }
 490          }
 491  
 492          return $this->getPaginationLinks();
 493      }
 494  
 495      /**
 496       * Creates a dropdown box for selecting how many records to show per page.
 497       *
 498       * @return  string  The HTML for the limit # input box.
 499       *
 500       * @since   1.5
 501       */
 502      public function getLimitBox()
 503      {
 504          $limits = array();
 505  
 506          // Make the option list.
 507          for ($i = 5; $i <= 30; $i += 5) {
 508              $limits[] = HTMLHelper::_('select.option', "$i");
 509          }
 510  
 511          $limits[] = HTMLHelper::_('select.option', '50', Text::_('J50'));
 512          $limits[] = HTMLHelper::_('select.option', '100', Text::_('J100'));
 513          $limits[] = HTMLHelper::_('select.option', '0', Text::_('JALL'));
 514  
 515          $selected = $this->viewall ? 0 : $this->limit;
 516  
 517          // Build the select list.
 518          if ($this->app->isClient('administrator')) {
 519              $html = HTMLHelper::_(
 520                  'select.genericlist',
 521                  $limits,
 522                  $this->prefix . 'limit',
 523                  'class="form-select" onchange="Joomla.submitform();"',
 524                  'value',
 525                  'text',
 526                  $selected
 527              );
 528          } else {
 529              $html = HTMLHelper::_(
 530                  'select.genericlist',
 531                  $limits,
 532                  $this->prefix . 'limit',
 533                  'class="form-select" onchange="this.form.submit()"',
 534                  'value',
 535                  'text',
 536                  $selected
 537              );
 538          }
 539  
 540          return $html;
 541      }
 542  
 543      /**
 544       * Return the icon to move an item UP.
 545       *
 546       * @param   integer  $i          The row index.
 547       * @param   boolean  $condition  True to show the icon.
 548       * @param   string   $task       The task to fire.
 549       * @param   string   $alt        The image alternative text string.
 550       * @param   boolean  $enabled    An optional setting for access control on the action.
 551       * @param   string   $checkbox   An optional prefix for checkboxes.
 552       *
 553       * @return  string   Either the icon to move an item up or a space.
 554       *
 555       * @since   1.5
 556       */
 557      public function orderUpIcon($i, $condition = true, $task = 'orderup', $alt = 'JLIB_HTML_MOVE_UP', $enabled = true, $checkbox = 'cb')
 558      {
 559          if (($i > 0 || ($i + $this->limitstart > 0)) && $condition) {
 560              return HTMLHelper::_('jgrid.orderUp', $i, $task, '', $alt, $enabled, $checkbox);
 561          } else {
 562              return '&#160;';
 563          }
 564      }
 565  
 566      /**
 567       * Return the icon to move an item DOWN.
 568       *
 569       * @param   integer  $i          The row index.
 570       * @param   integer  $n          The number of items in the list.
 571       * @param   boolean  $condition  True to show the icon.
 572       * @param   string   $task       The task to fire.
 573       * @param   string   $alt        The image alternative text string.
 574       * @param   boolean  $enabled    An optional setting for access control on the action.
 575       * @param   string   $checkbox   An optional prefix for checkboxes.
 576       *
 577       * @return  string   Either the icon to move an item down or a space.
 578       *
 579       * @since   1.5
 580       */
 581      public function orderDownIcon($i, $n, $condition = true, $task = 'orderdown', $alt = 'JLIB_HTML_MOVE_DOWN', $enabled = true, $checkbox = 'cb')
 582      {
 583          if (($i < $n - 1 || $i + $this->limitstart < $this->total - 1) && $condition) {
 584              return HTMLHelper::_('jgrid.orderDown', $i, $task, '', $alt, $enabled, $checkbox);
 585          } else {
 586              return '&#160;';
 587          }
 588      }
 589  
 590      /**
 591       * Create the HTML for a list footer
 592       *
 593       * @param   array  $list  Pagination list data structure.
 594       *
 595       * @return  string  HTML for a list footer
 596       *
 597       * @since   1.5
 598       */
 599      protected function _list_footer($list)
 600      {
 601          $html = "<div class=\"list-footer\">\n";
 602  
 603          $html .= "\n<div class=\"limit\">" . Text::_('JGLOBAL_DISPLAY_NUM') . $list['limitfield'] . "</div>";
 604          $html .= $list['pageslinks'];
 605          $html .= "\n<div class=\"counter\">" . $list['pagescounter'] . "</div>";
 606  
 607          $html .= "\n<input type=\"hidden\" name=\"" . $list['prefix'] . "limitstart\" value=\"" . $list['limitstart'] . "\">";
 608          $html .= "\n</div>";
 609  
 610          return $html;
 611      }
 612  
 613      /**
 614       * Create the html for a list footer
 615       *
 616       * @param   array  $list  Pagination list data structure.
 617       *
 618       * @return  string  HTML for a list start, previous, next,end
 619       *
 620       * @since   1.5
 621       */
 622      protected function _list_render($list)
 623      {
 624          return LayoutHelper::render('joomla.pagination.list', array('list' => $list));
 625      }
 626  
 627      /**
 628       * Method to create an active pagination link to the item
 629       *
 630       * @param   PaginationObject  $item  The object with which to make an active link.
 631       *
 632       * @return  string  HTML link
 633       *
 634       * @since   1.5
 635       */
 636      protected function _item_active(PaginationObject $item)
 637      {
 638          return LayoutHelper::render('joomla.pagination.link', ['data' => $item, 'active' => true]);
 639      }
 640  
 641      /**
 642       * Method to create an inactive pagination string
 643       *
 644       * @param   PaginationObject  $item  The item to be processed
 645       *
 646       * @return  string
 647       *
 648       * @since   1.5
 649       */
 650      protected function _item_inactive(PaginationObject $item)
 651      {
 652          return LayoutHelper::render('joomla.pagination.link', ['data' => $item, 'active' => false]);
 653      }
 654  
 655      /**
 656       * Create and return the pagination data object.
 657       *
 658       * @return  \stdClass  Pagination data object.
 659       *
 660       * @since   1.5
 661       */
 662      protected function _buildDataObject()
 663      {
 664          $data = new \stdClass();
 665  
 666          // Build the additional URL parameters string.
 667          $params = '';
 668  
 669          if (!empty($this->additionalUrlParams)) {
 670              foreach ($this->additionalUrlParams as $key => $value) {
 671                  $params .= '&' . $key . '=' . $value;
 672              }
 673          }
 674  
 675          $data->all = new PaginationObject(Text::_('JLIB_HTML_VIEW_ALL'), $this->prefix);
 676  
 677          if (!$this->viewall) {
 678              $data->all->base = '0';
 679              $data->all->link = Route::_($params . '&' . $this->prefix . 'limitstart=');
 680          }
 681  
 682          // Set the start and previous data objects.
 683          $data->start    = new PaginationObject(Text::_('JLIB_HTML_START'), $this->prefix);
 684          $data->previous = new PaginationObject(Text::_('JPREV'), $this->prefix);
 685  
 686          if ($this->pagesCurrent > 1) {
 687              $page = ($this->pagesCurrent - 2) * $this->limit;
 688  
 689              if ($this->hideEmptyLimitstart) {
 690                  $data->start->link = Route::_($params . '&' . $this->prefix . 'limitstart=');
 691              } else {
 692                  $data->start->link = Route::_($params . '&' . $this->prefix . 'limitstart=0');
 693              }
 694  
 695              $data->start->base    = '0';
 696              $data->previous->base = $page;
 697  
 698              if ($page === 0 && $this->hideEmptyLimitstart) {
 699                  $data->previous->link = $data->start->link;
 700              } else {
 701                  $data->previous->link = Route::_($params . '&' . $this->prefix . 'limitstart=' . $page);
 702              }
 703          }
 704  
 705          // Set the next and end data objects.
 706          $data->next = new PaginationObject(Text::_('JNEXT'), $this->prefix);
 707          $data->end  = new PaginationObject(Text::_('JLIB_HTML_END'), $this->prefix);
 708  
 709          if ($this->pagesCurrent < $this->pagesTotal) {
 710              $next = $this->pagesCurrent * $this->limit;
 711              $end  = ($this->pagesTotal - 1) * $this->limit;
 712  
 713              $data->next->base = $next;
 714              $data->next->link = Route::_($params . '&' . $this->prefix . 'limitstart=' . $next);
 715              $data->end->base  = $end;
 716              $data->end->link  = Route::_($params . '&' . $this->prefix . 'limitstart=' . $end);
 717          }
 718  
 719          $data->pages = array();
 720          $stop        = $this->pagesStop;
 721  
 722          for ($i = $this->pagesStart; $i <= $stop; $i++) {
 723              $offset = ($i - 1) * $this->limit;
 724  
 725              $data->pages[$i] = new PaginationObject($i, $this->prefix);
 726  
 727              if ($i != $this->pagesCurrent || $this->viewall) {
 728                  $data->pages[$i]->base = $offset;
 729  
 730                  if ($offset === 0 && $this->hideEmptyLimitstart) {
 731                      $data->pages[$i]->link = $data->start->link;
 732                  } else {
 733                      $data->pages[$i]->link = Route::_($params . '&' . $this->prefix . 'limitstart=' . $offset);
 734                  }
 735              } else {
 736                  $data->pages[$i]->active = true;
 737              }
 738          }
 739  
 740          return $data;
 741      }
 742  }


Generated: Wed Sep 7 05:41:13 2022 Chilli.vc Blog - For Webmaster,Blog-Writer,System Admin and Domainer