/** * Display a wildcard in the back end * * @return string */ public function generate() { if (TL_MODE == 'BE') { $objTemplate = new \BackendTemplate('be_wildcard'); $objTemplate->wildcard = '### ISOTOPE ECOMMERCE: PRODUCT FILTERS ###'; $objTemplate->title = $this->headline; $objTemplate->id = $this->id; $objTemplate->link = $this->name; $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id; return $objTemplate->parse(); } $this->generateAjax(); // Initialize module data. if (!$this->initializeFilters()) { return ''; } // Hide product list in reader mode if the respective setting is enabled if ($this->iso_hide_list && Input::getAutoItem('product', false, true) != '') { return ''; } $strBuffer = parent::generate(); // Cache request in the database and redirect to the unique requestcache ID if ($this->blnUpdateCache) { $objCache = Isotope::getRequestCache()->saveNewConfiguration(); // Include \Environment::base or the URL would not work on the index page \Controller::redirect(\Environment::get('base') . Url::addQueryString('isorc=' . $objCache->id, $this->jumpTo ?: null)); } return $strBuffer; }
/** * @inheritdoc */ protected function findCurrent() { $alias = (string) Input::getAutoItem($this->getUrlKey(), false, true); if ('' === $alias) { return null; } /** @var PageModel $objPage */ global $objPage; if (($calendars = FaqCategoryModel::findBy('jumpTo', $objPage->id)) === null) { return null; } return FaqModel::findPublishedByParentAndIdOrAlias($alias, $calendars->fetchEach('id')); }
/** * @inheritdoc */ protected function findCurrent() { $alias = (string) Input::getAutoItem($this->getUrlKey(), false, true); if ('' === $alias) { return null; } /** @var PageModel $objPage */ global $objPage; if (($archives = NewsArchiveModel::findBy('jumpTo', $objPage->id)) === null) { return null; } // Fix Contao bug that returns a collection (see contao-changelanguage#71) $options = ['limit' => 1, 'return' => 'Model']; return NewsModel::findPublishedByParentAndIdOrAlias($alias, $archives->fetchEach('id'), $options); }
/** * Display a wildcard in the back end * * @return string */ public function generate() { if (TL_MODE == 'BE') { $objTemplate = new \BackendTemplate('be_wildcard'); $objTemplate->wildcard = '### ISOTOPE CHECKOUT ###'; $objTemplate->title = $this->headline; $objTemplate->id = $this->id; $objTemplate->link = $this->name; $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id; return $objTemplate->parse(); } $this->strCurrentStep = \Haste\Input\Input::getAutoItem('step'); if ($this->strCurrentStep == '') { $this->redirectToNextStep(); } return parent::generate(); }
/** * Find all products we need to list. * @param array|null * @return array */ protected function findProducts($arrCacheIds = null) { $arrIds = array(0); $objProduct = Product::findAvailableByIdOrAlias(Input::getAutoItem('product')); if (null === $objProduct) { return array(); } $objRelated = RelatedProduct::findByProductAndCategories($objProduct, $this->iso_related_categories); if (null !== $objRelated) { while ($objRelated->next()) { $ids = trimsplit(',', $objRelated->products); if (!empty($ids) && is_array($ids)) { $arrIds = array_unique(array_merge($arrIds, $ids)); } } } $objProducts = Product::findAvailableByIds($arrIds, array('order' => \Database::getInstance()->findInSet(Product::getTable() . '.id', $arrIds))); return null === $objProducts ? array() : $objProducts->getModels(); }
/** * Generate module * @return void */ protected function compile() { global $objPage; global $objIsotopeListPage; $objProduct = Product::findAvailableByIdOrAlias(\Haste\Input\Input::getAutoItem('product')); if (null === $objProduct) { $objHandler = new $GLOBALS['TL_PTY']['error_404'](); $objHandler->generate($objPage->id); exit; } $arrConfig = array('module' => $this, 'template' => $this->iso_reader_layout ?: $objProduct->getRelated('type')->reader_template, 'gallery' => $this->iso_gallery ?: $objProduct->getRelated('type')->reader_gallery, 'buttons' => deserialize($this->iso_buttons, true), 'useQuantity' => $this->iso_use_quantity, 'jumpTo' => $objIsotopeListPage ?: $objPage); if (\Environment::get('isAjaxRequest') && \Input::post('AJAX_MODULE') == $this->id && \Input::post('AJAX_PRODUCT') == $objProduct->getProductId()) { $objResponse = new HtmlResponse($objProduct->generate($arrConfig)); $objResponse->send(); } $arrCSS = deserialize($objProduct->cssID, true); $this->Template->product = $objProduct->generate($arrConfig); $this->Template->product_id = $arrCSS[0] != '' ? ' id="' . $arrCSS[0] . '"' : ''; $this->Template->product_class = trim('product ' . ($objProduct->isNew() ? 'new ' : '') . $arrCSS[1]); $this->Template->referer = 'javascript:history.go(-1)'; $this->Template->back = $GLOBALS['TL_LANG']['MSC']['goBack']; $this->addMetaTags($objProduct); $this->addCanonicalProductUrls($objProduct); }
/** * Display a wildcard in the back end * * @return string */ public function generate() { if (TL_MODE == 'BE') { /** @var \BackendTemplate|object $objTemplate */ $objTemplate = new \BackendTemplate('be_wildcard'); $objTemplate->wildcard = '### ISOTOPE ECOMMERCE: CUMULATIVE FILTER ###'; $objTemplate->title = $this->headline; $objTemplate->id = $this->id; $objTemplate->link = $this->name; $objTemplate->href = 'contao/main.php?do=themes&table=tl_module&act=edit&id=' . $this->id; return $objTemplate->parse(); } // Hide product list in reader mode if the respective setting is enabled if ($this->iso_hide_list && Input::getAutoItem('product', false, true) != '') { return ''; } if (empty($this->iso_cumulativeFields)) { return ''; } return parent::generate(); }
/** * Show product name in breadcrumb * * @param array $arrItems * @param object $objModule * * @return array */ public function addProductToBreadcrumb($arrItems, $objModule) { if (\Haste\Input\Input::getAutoItem('product', false, true) != '') { $objProduct = Product::findAvailableByIdOrAlias(\Haste\Input\Input::getAutoItem('product', false, true)); if (null !== $objProduct) { global $objPage; global $objIsotopeListPage; $last = count($arrItems) - 1; // If we have a reader page, rename the last item (the reader) to the product title if (null !== $objIsotopeListPage) { $arrItems[$last]['title'] = $this->prepareMetaDescription($objProduct->meta_title ?: $objProduct->name); $arrItems[$last]['link'] = $objProduct->name; } else { $arrItems[$last]['href'] = \Controller::generateFrontendUrl($arrItems[$last]['data']); $arrItems[$last]['isActive'] = false; $arrItems[] = array('isRoot' => false, 'isActive' => true, 'href' => $objProduct->generateUrl($objPage), 'title' => $this->prepareMetaDescription($objProduct->meta_title ?: $objProduct->name), 'link' => $objProduct->name, 'data' => $objPage->row()); } } } return $arrItems; }
protected function getCacheKey() { return md5('relatedproducts=' . $this->id . ':' . 'product=' . Input::getAutoItem('product', false, true)); }
/** * Find product based on InsertTag parameter * * @param int $id * * @return IsotopeProduct|null */ private function findCurrentProduct($id = null) { if (null !== $id) { return Product::findAvailableByPk($id); } elseif (Product::getActive() !== null) { return Product::getActive(); } else { return Product::findAvailableByIdOrAlias(Input::getAutoItem('product', false, true)); } }
/** * Compile product list. * * This function is specially designed so you can keep it in your child classes and only override findProducts(). * You will automatically gain product caching (see class property), grid classes, pagination and more. */ protected function compile() { // return message if no filter is set if ($this->iso_emptyFilter && !\Input::get('isorc') && !\Input::get('keywords')) { $this->Template->message = $this->replaceInsertTags($this->iso_noFilter); $this->Template->type = 'noFilter'; $this->Template->products = array(); return; } global $objPage; $intPage = $this->iso_category_scope == 'article' ? $GLOBALS['ISO_CONFIG']['current_article']['pid'] : $objPage->id; $arrProducts = null; $arrCacheIds = null; // Try to load the products from cache if ($this->blnCacheProducts && ($objCache = ProductCache::findForPageAndModule($intPage, $this->id)) !== null) { $arrCacheIds = $objCache->getProductIds(); // Use the cache if keywords match. Otherwise we will use the product IDs as a "limit" for findProducts() if ($objCache->keywords == \Input::get('keywords')) { $arrCacheIds = $this->generatePagination($arrCacheIds); $objProducts = Product::findAvailableByIds($arrCacheIds, array('order' => \Database::getInstance()->findInSet(Product::getTable() . '.id', $arrCacheIds))); $arrProducts = null === $objProducts ? array() : $objProducts->getModels(); // Cache is wrong, drop everything and run findProducts() if (count($arrProducts) != count($arrCacheIds)) { $arrCacheIds = null; $arrProducts = null; } } } if (!is_array($arrProducts)) { // Display "loading products" message and add cache flag if ($this->blnCacheProducts) { $blnCacheMessage = (bool) $this->iso_productcache[$intPage][(int) \Input::get('isorc')]; if ($blnCacheMessage && !\Input::get('buildCache')) { // Do not index or cache the page $objPage->noSearch = 1; $objPage->cache = 0; $this->Template = new \Isotope\Template('mod_iso_productlist_caching'); $this->Template->message = $GLOBALS['TL_LANG']['MSC']['productcacheLoading']; return; } // Start measuring how long it takes to load the products $start = microtime(true); // Load products $arrProducts = $this->findProducts($arrCacheIds); // Decide if we should show the "caching products" message the next time $end = microtime(true) - $start; $this->blnCacheProducts = $end > 1 ? true : false; $arrCacheMessage = $this->iso_productcache; if ($blnCacheMessage != $this->blnCacheProducts) { $arrCacheMessage[$intPage][(int) \Input::get('isorc')] = $this->blnCacheProducts; \Database::getInstance()->prepare("UPDATE tl_module SET iso_productcache=? WHERE id=?")->execute(serialize($arrCacheMessage), $this->id); } // Do not write cache if table is locked. That's the case if another process is already writing cache if (ProductCache::isWritable()) { \Database::getInstance()->lockTables(array(ProductCache::getTable() => 'WRITE', 'tl_iso_product' => 'READ')); $arrIds = array(); foreach ($arrProducts as $objProduct) { $arrIds[] = $objProduct->id; } // Delete existing cache if necessary ProductCache::deleteForPageAndModuleOrExpired($intPage, $this->id); $objCache = ProductCache::createForPageAndModule($intPage, $this->id); $objCache->expires = $this->getProductCacheExpiration(); $objCache->setProductIds($arrIds); $objCache->save(); \Database::getInstance()->unlockTables(); } } else { $arrProducts = $this->findProducts(); } if (!empty($arrProducts)) { $arrProducts = $this->generatePagination($arrProducts); } } // No products found if (!is_array($arrProducts) || empty($arrProducts)) { $this->compileEmptyMessage(); return; } $arrBuffer = array(); $arrDefaultOptions = $this->getDefaultProductOptions(); /** @var \Isotope\Model\Product\Standard $objProduct */ foreach ($arrProducts as $objProduct) { $arrConfig = array('module' => $this, 'template' => $this->iso_list_layout ?: $objProduct->getRelated('type')->list_template, 'gallery' => $this->iso_gallery ?: $objProduct->getRelated('type')->list_gallery, 'buttons' => deserialize($this->iso_buttons, true), 'useQuantity' => $this->iso_use_quantity, 'jumpTo' => $this->findJumpToPage($objProduct)); if (\Environment::get('isAjaxRequest') && \Input::post('AJAX_MODULE') == $this->id && \Input::post('AJAX_PRODUCT') == $objProduct->getProductId()) { $objResponse = new HtmlResponse($objProduct->generate($arrConfig)); $objResponse->send(); } $objProduct->mergeRow($arrDefaultOptions); // Must be done after setting options to generate the variant config into the URL if ($this->iso_jump_first && \Haste\Input\Input::getAutoItem('product', false, true) == '') { \Controller::redirect($objProduct->generateUrl($arrConfig['jumpTo'])); } $arrCSS = deserialize($objProduct->cssID, true); $arrBuffer[] = array('cssID' => $arrCSS[0] != '' ? ' id="' . $arrCSS[0] . '"' : '', 'class' => trim('product ' . ($objProduct->isNew() ? 'new ' : '') . $arrCSS[1]), 'html' => $objProduct->generate($arrConfig), 'product' => $objProduct); } // HOOK: to add any product field or attribute to mod_iso_productlist template if (isset($GLOBALS['ISO_HOOKS']['generateProductList']) && is_array($GLOBALS['ISO_HOOKS']['generateProductList'])) { foreach ($GLOBALS['ISO_HOOKS']['generateProductList'] as $callback) { $objCallback = \System::importStatic($callback[0]); $arrBuffer = $objCallback->{$callback}[1]($arrBuffer, $arrProducts, $this->Template, $this); } } RowClass::withKey('class')->addCount('product_')->addEvenOdd('product_')->addFirstLast('product_')->addGridRows($this->iso_cols)->addGridCols($this->iso_cols)->applyTo($arrBuffer); $this->Template->products = $arrBuffer; }
/** * The ids of all pages we take care of. This is what should later be used eg. for filter data. * * @return array */ protected function findCategories() { if (null === $this->arrCategories) { if ($this->defineRoot && $this->rootPage > 0) { $objPage = \PageModel::findWithDetails($this->rootPage); } else { global $objPage; } $t = \PageModel::getTable(); $arrCategories = null; $arrUnpublished = array(); $strWhere = "{$t}.type!='error_403' AND {$t}.type!='error_404'"; if (!BE_USER_LOGGED_IN) { $time = time(); $objUnpublished = \PageModel::findBy(array("({$t}.start!='' AND {$t}.start>{$time}) OR ({$t}.stop!='' AND {$t}.stop<{$time}) OR {$t}.published=?"), array('')); $arrUnpublished = $objUnpublished->fetchEach('id'); //$strWhere .= " AND ($t.start='' OR $t.start<$time) AND ($t.stop='' OR $t.stop>$time) AND $t.published='1'"; } switch ($this->iso_category_scope) { case 'global': $arrCategories = array($objPage->rootId); $arrCategories = \Database::getInstance()->getChildRecords($objPage->rootId, 'tl_page', false, $arrCategories, $strWhere); $arrCategories = array_diff($arrCategories, $arrUnpublished); break; case 'current_and_first_child': $arrCategories = \Database::getInstance()->execute("SELECT id FROM tl_page WHERE pid={$objPage->id} AND {$strWhere}")->fetchEach('id'); $arrCategories[] = $objPage->id; break; case 'current_and_all_children': $arrCategories = array($objPage->id); $arrCategories = \Database::getInstance()->getChildRecords($objPage->id, 'tl_page', false, $arrCategories, $strWhere); $arrCategories = array_diff($arrCategories, $arrUnpublished); break; case 'parent': $arrCategories = array($objPage->pid); break; case 'product': /** @var \Isotope\Model\Product\Standard $objProduct */ $objProduct = Product_Model::findAvailableByIdOrAlias(\Haste\Input\Input::getAutoItem('product')); if ($objProduct !== null) { $arrCategories = $objProduct->getCategories(true); } else { $arrCategories = array(0); } break; case 'article': $arrCategories = array($GLOBALS['ISO_CONFIG']['current_article']['pid'] ?: $objPage->id); break; case '': case 'current_category': $arrCategories = array($objPage->id); break; default: if (isset($GLOBALS['ISO_HOOKS']['findCategories']) && is_array($GLOBALS['ISO_HOOKS']['findCategories'])) { foreach ($GLOBALS['ISO_HOOKS']['findCategories'] as $callback) { $objCallback = \System::importStatic($callback[0]); $arrCategories = $objCallback->{$callback}[1]($this); if ($arrCategories !== false) { break; } } } break; } $this->arrCategories = empty($arrCategories) ? array(0) : $arrCategories; } return $this->arrCategories; }
/** * Return the demanded frontend module or content element parsed as html string * * Required GET data: * * action: "reload-element" * * element: "ce::id" or "mod::id" (replace 'id' with the element's id) * * page: "id" (optionally, replace 'id' with the current page's id) * * auto_item: (an optional auto_item which will be set before fetching the element) */ public function getModuleOrContentElement() { if (!\Environment::get('isAjaxRequest') || Input::get('action') != 'reload-element') { return; } global $objPage; // Set page object as it may be needed for the language e.g. if (!$objPage && (int) Input::get('page')) { $objPage = \PageModel::findWithDetails((int) Input::get('page')); } $GLOBALS['TL_LANGUAGE'] = null !== $objPage ? $objPage->language : $GLOBALS['TL_LANGUAGE']; list($strElementType, $intElementId) = trimsplit('::', Input::get('element')); $strError = ''; $return = ''; // Authenticate front end user, e.g. for insert tags if (FE_USER_LOGGED_IN) { /** @noinspection PhpUndefinedMethodInspection */ $this->import('FrontendUser', 'User'); /** @var \FrontendUser $this ->User */ $this->User->authenticate(); } // Load default language file \System::loadLanguageFile('default'); // Set a given auto_item to fetch the correct version of a module or content element if ($strAutoItem = Input::get('auto_item')) { Input::setGet('auto_item', $strAutoItem); } switch ($strElementType) { case 'mod': /** @type \Model $objModule */ $objModule = \ModuleModel::findByPk($intElementId); if (null === $objModule) { $strError = sprintf('Could not find module ID %s', $intElementId); continue; } if (!$objModule->allowAjaxReload) { $strError = sprintf('Module ID %u is not allowed to fetch', $intElementId); continue; } $return = \Controller::getFrontendModule($objModule); break; case 'ce': /** @type \Model $objContent */ $objContent = ContentModel::findByPk($intElementId); if (null === $objContent) { $strError = sprintf('Could not find content element ID %s', $intElementId); continue; } if (!$objContent->allowAjaxReload) { $strError = sprintf('Content element ID %u is not allowed to fetch', $intElementId); continue; } $return = \Controller::getContentElement($objContent); break; default: $strError = 'Could not determine whether the element is a module or content element'; break; } $arrResponse = array(); if ($strError) { $arrResponse['status'] = 'error'; $arrResponse['error'] = $strError; } else { $arrResponse['status'] = 'ok'; $arrResponse['html'] = $return; } $objResponse = new JsonResponse($arrResponse); $objResponse->send(); }