src/Rhyme/WMassArtsHub/Module/Profile/Lister.php line 196

Open in your IDE?
  1. <?php
  2. /**
  3.  * Copyright (C) 2021 Rhyme Digital, LLC.
  4.  *
  5.  * @author        Blair Winans <blair@rhyme.digital>
  6.  * @author        Adam Fisher <adam@rhyme.digital>
  7.  * @link        https://rhyme.digital
  8.  * @license        http://www.gnu.org/licenses/lgpl-3.0.html LGPL
  9.  */
  10. namespace Rhyme\WMassArtsHub\Module\Profile;
  11. use Contao\Input;
  12. use Contao\System;
  13. use Contao\Database;
  14. use Contao\Pagination;
  15. use Contao\StringUtil;
  16. use Contao\BackendTemplate;
  17. use Contao\Model\QueryBuilder;
  18. use Haste\Util\InsertTag as HasteInsertTag;
  19. use Haste\Generator\RowClass;
  20. use Rhyme\WMassArtsHub\Helper\FilterHelper;
  21. use Rhyme\WMassArtsHub\Model\Profile\Item as WMassArtsHub_ProfileItemModel;
  22. use Rhyme\WMassArtsHub\Model\Profile\Type as WMassArtsHub_ProfileTypeModel;
  23. /**
  24.  * Class Lister
  25.  * @package Rhyme\WMassArtsHub\Module\Collection
  26.  */
  27. class Lister extends Module
  28. {
  29.     /**
  30.      * Template
  31.      * @var string
  32.      */
  33.     protected $strTemplate 'mod_artshub_profile_lister';
  34.     /**
  35.      * Display a wildcard in the back end
  36.      * @return string
  37.      */
  38.     public function generate()
  39.     {
  40.         if (TL_MODE === 'BE')
  41.         {
  42.             $objTemplate = new BackendTemplate('be_wildcard');
  43.             $objTemplate->wildcard '### ARTSHUB PROFILE ITEM LISTER ###';
  44.             $objTemplate->title $this->headline;
  45.             $objTemplate->id $this->id;
  46.             $objTemplate->link $this->name;
  47.             $objTemplate->href 'contao/main.php?do=themes&amp;table=tl_module&amp;act=edit&amp;id=' $this->id;
  48.             return $objTemplate->parse();
  49.         }
  50.         if (\is_numeric(Input::get('perpage')) && \intval(Input::get('perpage')))
  51.         {
  52.             $this->perPage = \intval(Input::get('perpage'));
  53.         }
  54.         return parent::generate();
  55.     }
  56.     /**
  57.      * Compile
  58.      */
  59.     protected function compile()
  60.     {
  61.         // Haste AJAX reload
  62.         \Haste\Ajax\ReloadHelper::subscribe(
  63.             \Haste\Ajax\ReloadHelper::getUniqid(\Haste\Ajax\ReloadHelper::TYPE_MODULE$this->id),
  64.             ['reloadArtsHubProfileListItems']
  65.         );
  66.         //Get items
  67.         $arrProfileItems $this->findItems();
  68.         // No items found
  69.         if (!is_array($arrProfileItems) || empty($arrProfileItems)) {
  70.             $this->compileEmptyMessage();
  71.             return;
  72.         }
  73.         // Make sure this module has an ID
  74.         $arrCssID = (array)$this->cssID;
  75.         $arrCssID[0] = $arrCssID[0] ?: 'profilelister'.$this->id;
  76.         $this->cssID $arrCssID;
  77.         // Add scripts
  78.         $GLOBALS['TL_JAVASCRIPT']['jquerymosaic'] = 'web/bundles/rhymewmassartshub/assets/js/vendor/jquerymosaic/jquery.mosaic.js|static';
  79.         $GLOBALS['TL_CSS']['jquerymosaic'] = 'web/bundles/rhymewmassartshub/assets/js/vendor/jquerymosaic/jquery.mosaic.css|static';
  80.         $GLOBALS['TL_JAVASCRIPT']['profileListerModal'] = 'web/bundles/rhymewmassartshub/assets/js/frontend/profileLister/modal.js|static';
  81.         $GLOBALS['TL_JAVASCRIPT']['profileListerPagination'] = 'web/bundles/rhymewmassartshub/assets/js/frontend/profileLister/pagination.js|static';
  82.         $GLOBALS['TL_BODY']['historyjs'] = '<script src="https://cdnjs.cloudflare.com/ajax/libs/history.js/1.8/native.history.min.js" integrity="sha512-LW9lY12yhdxezhOnfueR/bF7GdraV3ZCZpdTTUqrq6ifCsp4wmnlBbw8/qTjs+9jqLXZn+cBVYN9rlQz52UVkA==" crossorigin="anonymous"></script>';
  83.         $GLOBALS['TL_BODY']['profileListerPagination'.$this->id] = "
  84.             <script>
  85.                 jQuery(document).ready(function(){
  86.                     ArtsHub.ProfileLister.Pagination.create(jQuery('#".$arrCssID[0]."'), {
  87.                     });
  88.                 });
  89.             </script>";
  90.         $arrBuffer = array();
  91.         foreach ($arrProfileItems as $objItem) {
  92.             $arrConfig = array(
  93.                 'module'        => $this,
  94.                 'template'      => $this->artshub_profile_list_layout ?: 'artshub_profile_list_default',
  95.                 'jumpTo'        => $this->findJumpToPage($objItem),
  96.             );
  97.             $arrCSS StringUtil::deserialize($objItem->cssIDtrue);
  98.             $arrBuffer[] = array(
  99.                 'cssID'     => ($arrCSS[0] != '') ? ' id="' $arrCSS[0] . '"' '',
  100.                 'class'     => trim('profileItem ' $arrCSS[1]),
  101.                 'html'      => $objItem->generate($arrConfig),
  102.                 'item'      => $objItem,
  103.             );
  104.         }
  105.         if (isset($GLOBALS['TL_HOOKS']['generateProfileList']) && is_array($GLOBALS['TL_HOOKS']['generateProfileList'])) {
  106.             foreach ($GLOBALS['TL_HOOKS']['generateProfileList'] as $callback) {
  107.                 $objCallback System::importStatic($callback[0]);
  108.                 $arrBuffer   $objCallback->{$callback[1]}($arrBuffer$arrProfileItems$this->Template$this);
  109.             }
  110.         }
  111.         RowClass::withKey('class')->addCount('profileItem_')->addEvenOdd('profileItem_')->addFirstLast('profileItem_')->addGridRows($this->artshub_profile_cols)->addGridCols($this->artshub_profile_cols)->applyTo($arrBuffer);
  112.         $this->Template->items $arrBuffer;
  113.         //Add jumpto Page
  114.         global $objPage;
  115.         $this->Template->jumpTo $this->jumpTo ?: $objPage->id;
  116.     }
  117.     /**
  118.      * Compile template to show a message if there are no items
  119.      *
  120.      * @param bool $disableSearchIndex
  121.      */
  122.     protected function compileEmptyMessage($disableSearchIndex true)
  123.     {
  124.         global $objPage;
  125.         // Do not index or cache the page
  126.         if ($disableSearchIndex) {
  127.             $objPage->noSearch 1;
  128.             $objPage->cache    0;
  129.         }
  130.         $message $this->artshub_profile_emptyMessage $this->artshub_profile_noListings $GLOBALS['TL_LANG']['MSC']['artshub_profile_noListings'];
  131.         $this->Template->empty    true;
  132.         $this->Template->type     'empty';
  133.         $this->Template->message  $message;
  134.         $this->Template->items = array();
  135.     }
  136.     /**
  137.      * Find all items we need to list.
  138.      * @return array
  139.      */
  140.     protected function findItems()
  141.     {
  142.         $arrColumns    = array();
  143.         //Get filters and sorting values
  144.         list($arrValues$strWhere$strSorting) = $this->getFiltersAndSorting();
  145.         //Handle no values
  146.         if (!is_array($arrValues)) {
  147.             $arrValues = array();
  148.         }
  149.         //Add categories to query
  150.         $t WMassArtsHub_ProfileItemModel::getTable();
  151.         if ($this->artshub_profile_list_where != '') {
  152.             $arrColumns[] = HasteInsertTag::replaceRecursively($this->artshub_profile_list_where);
  153.         }
  154.         //Add where query from filters/sorting
  155.         if ($strWhere != '') {
  156.             $arrColumns[] = $strWhere;
  157.         }
  158.         //Calculate the total on the query
  159.         $intTotal = static::countPublishedBy($arrColumns$arrValues);
  160.         // Use number of items too
  161.         if ($this->numberOfItems)
  162.         {
  163.             $intTotal min($this->numberOfItems$intTotal);
  164.         }
  165.         //Generate pagination and get offset
  166.         $offset $this->generatePagination($intTotal);
  167.         //Build options
  168.         $arrOptions = array
  169.         (
  170.             'offset'    => $offset,
  171.             'limit'        => ($this->numberOfItems && $this->perPage) ? min($this->numberOfItems$this->perPage) : ($this->perPage ?: $this->numberOfItems),
  172.             'order'        => $strSorting,
  173.         );
  174.         // Fix for category sorting values
  175.         //$arrColumns[] = "c.page_id IN (" . implode(',', $arrCategories) . ")";
  176.         //Run query
  177.         $objItems WMassArtsHub_ProfileItemModel::findPublishedBy(
  178.             $arrColumns,
  179.             $arrValues,
  180.             $arrOptions
  181.         );
  182.         return (null === $objItems) ? array() : $objItems->getModels();
  183.     }
  184.     /**
  185.      * Generate the pagination
  186.      * @param integer
  187.      * @return integer
  188.      */
  189.     protected function generatePagination($total)
  190.     {
  191.         // Add pagination
  192.         if ($this->perPage && $total 0)
  193.         {
  194.             if (Input::post('page'))
  195.             {
  196.                 Input::setGet('page'Input::post('page'));
  197.             }
  198.             $page Input::get('page') ? Input::get('page') : 1;
  199.             // Check the maximum page number
  200.             if ($page > ($total/$this->perPage))
  201.             {
  202.                 $page ceil($total/$this->perPage);
  203.             }
  204.             $offset = ($page 1) * $this->perPage;
  205.             $objPagination = new Pagination($total$this->perPage);
  206.             $this->Template->pagination $objPagination->generate("\n  ");
  207.             return $offset;
  208.         }
  209.         return 0;
  210.     }
  211.     /**
  212.      * Get filter & sorting configuration
  213.      * @return array
  214.      */
  215.     protected function getFiltersAndSorting($blnNativeSQL true)
  216.     {
  217.         $strWhere     '';
  218.         $strSorting '';
  219.         $arrWhere    = array();
  220.         $arrValues     = array();
  221.         $blnDefaultModuleSort false;
  222.         $c WMassArtsHub_ProfileItemModel::getTable();
  223.         // Sorting
  224.         if (Input::get('sorting'))
  225.         {
  226.             $arrSortField explode('-'Input::get('sorting'));
  227.             // Needs to be a field value in the table and either be asc or desc
  228.             if (Database::getInstance()->fieldExists($arrSortField[0], $c) && (strtolower($arrSortField[1])=='asc' || strtolower($arrSortField[1])=='desc'))
  229.             {
  230.                 $strSorting $arrSortField[0] . ' ' strtoupper($arrSortField[1]);
  231.             }
  232.         }
  233.         // Default sorting
  234.         if (!$strSorting && $this->artshub_profile_listingSortField && $this->artshub_profile_listingSortDirection)
  235.         {
  236.             $blnDefaultModuleSort true;
  237.             $strSorting $this->artshub_profile_listingSortField ' ' $this->artshub_profile_listingSortDirection;
  238.         }
  239.         $arrSortFields = array();
  240.         $arrSortValues = array();
  241.         // Keywords
  242.         if ($strKeywords = \trim(Input::get('keywords')))
  243.         {
  244.             $arrFields StringUtil::deserialize($this->artshub_profile_searchFieldstrue);
  245.             if (\count($arrFields))
  246.             {
  247.                 $where = array();
  248.                 include_once(TL_ROOT '/src/Rhyme/WMassArtsHub/Resources/contao/config/stopwords.php');
  249.                 $arrKeywords = \array_map(array(FilterHelper::class, 'uncleanChars'), \explode(','$strKeywords));
  250.                 foreach ($arrKeywords as $keyword)
  251.                 {
  252.                     // Look for all words in the fields
  253.                     $arrFinalKeywords = \explode(' '$keyword);
  254.                     foreach ($arrFinalKeywords as $finalKeyword)
  255.                     {
  256.                         $strTerm = \trim($finalKeyword);
  257.                         if (empty($strTerm) || \in_array(\strtolower($strTerm), \array_map('strtolower'$GLOBALS['KEYWORD_STOP_WORDS'])) || in_array(strtolower($strTerm), array_map('strtolower'$GLOBALS['KEYWORD_STOP_WORDS'])))
  258.                         {
  259.                             continue;
  260.                         }
  261.                         foreach ($arrFields as $field)
  262.                         {
  263.                             $where[] = "$c.$field REGEXP ?";
  264.                             $arrValues[] = $strTerm;
  265.                         }
  266.                     }
  267.                     // Do relevance sorting
  268.                     $intPriority 1;
  269.                     // See if the field matches the term exactly
  270.                     foreach ($arrFields as $field)
  271.                     {
  272.                         $strWholeTerm FilterHelper::uncleanChars($strKeywords);
  273.                         if ($strWholeTerm)
  274.                         {
  275.                             $arrSortFields[] = "CASE WHEN TRIM($c.$field) LIKE ? THEN $intPriority ELSE 9999999999 END";
  276.                             $arrSortValues[] = $strWholeTerm;
  277.                             $intPriority++;
  278.                         }
  279.                     }
  280.                     // Try to match as many terms as possible starting with the first
  281.                     foreach ($arrFields as $field)
  282.                     {
  283.                         foreach ($arrFinalKeywords as $finalKeyword)
  284.                         {
  285.                             $strTerm trim($finalKeyword);
  286.                             if (empty($strTerm) || in_array(strtolower($strTerm), array_map('strtolower'$GLOBALS['KEYWORD_STOP_WORDS'])) || in_array(strtolower($strTerm), array_map('strtolower'$GLOBALS['KEYWORD_STOP_WORDS'])))
  287.                             {
  288.                                 continue;
  289.                             }
  290.                             $arrSortFields[] = "CASE WHEN $c.$field REGEXP ? THEN $intPriority ELSE 9999999999 END";
  291.                             $arrSortValues[] = $strTerm;
  292.                             $intPriority++;
  293.                         }
  294.                     }
  295.                 }
  296.                 if (count($where))
  297.                 {
  298.                     $arrWhere['keywords'] = '('.implode(' OR '$where).')';
  299.                 }
  300.             }
  301.         }
  302.         // !HOOK: custom filter/sorting types
  303.         if (isset($GLOBALS['TL_HOOKS']['processProfileFiltersAndSorting']) && is_array($GLOBALS['TL_HOOKS']['processProfileFiltersAndSorting']))
  304.         {
  305.             foreach ($GLOBALS['TL_HOOKS']['processProfileFiltersAndSorting'] as $callback)
  306.             {
  307.                 $objCallback System::importStatic($callback[0]);
  308.                 list($arrValues$arrWhere$strSorting) = $objCallback->{$callback[1]}($arrValues$arrWhere$strSorting$this->findCategories(), $this);
  309.             }
  310.         }
  311.         // Do relevance sorting - todo: need a better way to do this so it can be passed to the hook
  312.         if (!$blnDefaultModuleSort && !$strSorting && !empty($arrSortFields) && !empty($arrSortValues))
  313.         {
  314.             $strSorting implode(','$arrSortFields);
  315.             foreach ($arrSortValues as $val)
  316.             {
  317.                 $arrValues[] = $val;
  318.             }
  319.         }
  320.         // Sort by category if nothing else
  321.         $strSorting $strSorting ?: "$c.sorting ".$this->artshub_profile_listingSortDirection;
  322.         // Now put together the entire WHERE
  323.         if(count($arrWhere) > 0)
  324.         {
  325.             $strWhere implode(' AND '$arrWhere);
  326.         }
  327.         return array($arrValues$strWhere$strSorting);
  328.     }
  329.     /**
  330.      * Find published products by condition
  331.      * @param   mixed
  332.      * @param   mixed
  333.      * @param   array
  334.      * @return  int
  335.      */
  336.     public static function countPublishedBy($arrColumns$arrValues$arrOptions=array())
  337.     {
  338.         $c WMassArtsHub_ProfileItemModel::getTable();
  339.         $arrValues = (array) $arrValues;
  340.         if (!is_array($arrColumns)) {
  341.             $arrColumns = array();
  342.         }
  343.         if (BE_USER_LOGGED_IN !== true) {
  344.             $arrColumns[] = "$c.published='1'";
  345.         }
  346.         $arrFind array_merge(array
  347.         (
  348.             'table'            => $c,
  349.             'column'        => $arrColumns,
  350.             'value'            => $arrValues,
  351.         ), (array)$arrOptions);
  352.         $strQuery QueryBuilder::find($arrFind);
  353.         $strQuery = static::replaceSectionsOfString($strQuery"SELECT ""FROM ""SELECT $c.id FROM "truefalse);
  354.         $arrIDs Database::getInstance()->prepare($strQuery)->execute($arrValues)->fetchEach('id');
  355.         // !HOOK: custom actions
  356.         if (isset($GLOBALS['TL_HOOKS']['passFoundProfileItems']) && is_array($GLOBALS['TL_HOOKS']['passFoundProfileItems']))
  357.         {
  358.             foreach ($GLOBALS['TL_HOOKS']['passFoundProfileItems'] as $callback)
  359.             {
  360.                 $objCallback System::importStatic($callback[0]);
  361.                 $objCallback->{$callback[1]}($arrIDs);
  362.             }
  363.         }
  364.         return (int) count($arrIDs);
  365.     }
  366. }