src/Twig/SlivkiTwigExtension.php line 1005

Open in your IDE?
  1. <?php
  2. namespace Slivki\Twig;
  3. use DateTimeInterface;
  4. use Doctrine\ORM\EntityManagerInterface;
  5. use IntlDateFormatter;
  6. use Slivki\Controller\SiteController;
  7. use Slivki\Dao\OfferCode\PurchaseCountDaoInterface;
  8. use Slivki\Entity\BankCurrency;
  9. use Slivki\Entity\BrandingBanner;
  10. use Slivki\Entity\Category;
  11. use Slivki\Entity\CategoryType;
  12. use Slivki\Entity\City;
  13. use Slivki\Entity\Comment;
  14. use Slivki\Entity\Director;
  15. use Slivki\Entity\InfoPage;
  16. use Slivki\Entity\MailingCampaign;
  17. use Slivki\Entity\Media;
  18. use Slivki\Entity\Media\OfferSupplierPhotoMedia;
  19. use Slivki\Entity\MediaType;
  20. use Slivki\Entity\NoticePopup;
  21. use Slivki\Entity\NoticePopupView;
  22. use Slivki\Entity\Offer;
  23. use Slivki\Entity\OfferExtensionVariant;
  24. use Slivki\Entity\PriceDeliveryType;
  25. use Slivki\Entity\ProductCategory;
  26. use Slivki\Entity\ProductFastDelivery;
  27. use Slivki\Entity\PurchaseCount;
  28. use Slivki\Entity\Sale;
  29. use Slivki\Entity\Seo;
  30. use Slivki\Entity\SiteSettings;
  31. use Slivki\Entity\UserGroup;
  32. use Slivki\Entity\Visit;
  33. use Slivki\Entity\VisitCounter;
  34. use Slivki\Enum\SwitcherFeatures;
  35. use Slivki\Repository\OfferRepository;
  36. use Slivki\Repository\ProductFastDeliveryRepository;
  37. use Slivki\Repository\SeoRepository;
  38. use Slivki\Services\BannerService;
  39. use Slivki\Services\CacheService;
  40. use Slivki\Services\ImageService;
  41. use Slivki\Services\RTBHouseService;
  42. use Slivki\Services\Sidebar\SidebarCacheService;
  43. use Slivki\Services\Switcher\ServerFeatureStateChecker;
  44. use Slivki\Services\TextCacheService;
  45. use Slivki\Twig\Filter\PhoneNumberFilterTwigRuntime;
  46. use Slivki\Twig\Filter\ShortPriceFilterTwigRuntime;
  47. use Slivki\Util\OAuth2Client\AbstractOAuth2Client;
  48. use Slivki\Util\SoftCache;
  49. use Slivki\Util\CommonUtil;
  50. use Symfony\Component\DependencyInjection\ParameterBag\ParameterBagInterface;
  51. use Symfony\Component\HttpFoundation\RequestStack;
  52. use Slivki\Entity\User;
  53. use Slivki\Util\Logger;
  54. use Symfony\Component\DomCrawler\Crawler;
  55. use Symfony\Component\HttpFoundation\Session\Session;
  56. use Slivki\Repository\SiteSettingsRepository;
  57. use Symfony\Component\HttpKernel\KernelInterface;
  58. use Symfony\Component\Security\Acl\Exception\Exception;
  59. use Twig\Environment;
  60. use Twig\Extension\AbstractExtension;
  61. use Twig\TwigFilter;
  62. use Twig\TwigFunction;
  63. use Twig\TwigTest;
  64. class SlivkiTwigExtension extends AbstractExtension {
  65.     private $partnerLogoURL null;
  66.     private $entityManager;
  67.     private $request;
  68.     private $imageService;
  69.     private $bannerService;
  70.     private $textCacheService;
  71.     private $kernel;
  72.     private $cacheService;
  73.     private $RTBHouseService;
  74.     private static $photoGuideMenuItem null;
  75.     private SidebarCacheService $sidebarCacheService;
  76.     private ServerFeatureStateChecker $serverFeatureStateChecker;
  77.     private ParameterBagInterface $parameterBag;
  78.     private PurchaseCountDaoInterface $purchaseCountDao;
  79.     public function __construct(
  80.         EntityManagerInterface $entityManager,
  81.         RequestStack $requestStack,
  82.         ImageService $imageService,
  83.         BannerService $bannerService,
  84.         TextCacheService $textCacheService,
  85.         KernelInterface $kernel,
  86.         CacheService $cacheService,
  87.         RTBHouseService $RTBHouseService,
  88.         SidebarCacheService $sidebarCacheService,
  89.         ServerFeatureStateChecker $serverFeatureStateChecker,
  90.         ParameterBagInterface $parameterBag,
  91.         PurchaseCountDaoInterface $purchaseCountDao
  92.     ) {
  93.         $this->entityManager $entityManager;
  94.         $this->request $requestStack->getMainRequest();
  95.         $this->imageService $imageService;
  96.         $this->bannerService $bannerService;
  97.         $this->textCacheService $textCacheService;
  98.         $this->kernel $kernel;
  99.         $this->cacheService $cacheService;
  100.         $this->RTBHouseService $RTBHouseService;
  101.         $this->sidebarCacheService $sidebarCacheService;
  102.         $this->serverFeatureStateChecker $serverFeatureStateChecker;
  103.         $this->parameterBag $parameterBag;
  104.         $this->purchaseCountDao $purchaseCountDao;
  105.     }
  106.     public function getFilters(): array
  107.     {
  108.         return [
  109.             new TwigFilter('plural', [$this'pluralFilter']),
  110.             new TwigFilter('preg_replace', [$this'pregReplaceFilter']),
  111.             new TwigFilter('localizedate', [$this'localizeDateFilter']),
  112.             new TwigFilter('localize_timestamp_date', [$this'localizeDateTimestampFilter']),
  113.             new TwigFilter('phone', [$this'phoneFilter']),
  114.             new TwigFilter('json_decode', [$this'jsonDecodeFilter']),
  115.             new TwigFilter('short_price', [ShortPriceFilterTwigRuntime::class, 'shortPriceFormat']),
  116.             new TwigFilter('phone_number', [PhoneNumberFilterTwigRuntime::class, 'phoneNumberFormat']),
  117.         ];
  118.     }
  119.     public function getFunctions() {
  120.         return array(
  121.             new TwigFunction("getTopLevelCategories", array($this"getTopLevelCategories")),
  122.             new TwigFunction("getMetaInfo", array($this"getMetaInfo")),
  123.             new TwigFunction("getURL", array($this"getURL")),
  124.             new TwigFunction("getCategoryURL", array($this"getCategoryURL")),
  125.             new TwigFunction("getCategoriesList", array($this"getCategoriesList")),
  126.             new TwigFunction("getCityList", array($this"getCityList")),
  127.             new TwigFunction("getTopCityList", array($this"getTopCityList")),
  128.             new TwigFunction("getCategoryTypeList", array($this"getCategoryTypeList")),
  129.             new TwigFunction("getImageURL", array($this"getImageURL")),
  130.             new TwigFunction("getSidebar", [$this"getSidebar"], ['is_safe' => ['html'], 'needs_environment' => true]),
  131.             new TwigFunction("getProfileImageURL", [$this"getProfileImageURL"]),
  132.             new TwigFunction("getSiteSettings", [$this"getSiteSettings"]),
  133.             new TwigFunction("staticCall", [$this"staticCall"]),
  134.             new TwigFunction("getVisitCount",[$this"getVisitCount"]),
  135.             new TwigFunction("getOfferVisitCount",[$this"getOfferVisitCount"]),
  136.             new TwigFunction("getSaleVisitCount",[$this"getSaleVisitCount"]),
  137.             new TwigFunction("getVideoGuideVisitCount",[$this"getVideoGuideVisitCount"]),
  138.             new TwigFunction("getOfferMonthlyPurchaseCount",[$this"getOfferMonthlyPurchaseCount"]),
  139.             new TwigFunction("getTopSiteBanner",[$this"getTopSiteBanner"], ['is_safe' => ['html'], 'needs_environment' => false]),
  140.             new TwigFunction("getCategoryBanner",[$this"getCategoryBanner"]),
  141.             new TwigFunction('getCommentsBanners',[GetCommentsBanners::class, 'getCommentsBanners']),
  142.             new TwigFunction("getGoogleBanner",[$this"getGoogleBanner"], ['is_safe' => ['html'], 'needs_environment' => true]),
  143.             new TwigFunction("getBankCurrencyList", array($this"getBankCurrencyList")),
  144.             new TwigFunction("getCategoryBreadcrumbs", array($this"getCategoryBreadcrumbs")),
  145.             new TwigFunction("getLastComments", [$this"getLastComments"]),
  146.             new TwigFunction("getCommentEntityByType", [$this"getCommentEntityByType"]),
  147.             new TwigFunction("getCommentsMenuItems", [$this"getCommentsMenuItems"]),
  148.             new TwigFunction("getMainMenu", [$this"getMainMenu"], ['is_safe' => ['html'], 'needs_environment' => true]),
  149.             new TwigFunction("getActiveSubCategories", [$this"getActiveSubCategories"]),
  150.             new TwigFunction("getActiveSalesCount", [$this"getActiveSalesCount"]),
  151.             new TwigFunction("getActiveOffersCount", [$this"getActiveOffersCount"]),
  152.             new TwigFunction("getPartnerLogoURL", [$this"getPartnerLogoURL"]),
  153.             new TwigFunction("getCommentsCountByUserID", [$this"getCommentsCountByUserID"]),
  154.             new TwigFunction("getInfoPages", [$this"getInfoPages"]),
  155.             new TwigFunction("getSaleShortDescription", [$this"getSaleShortDescription"]),
  156.             new TwigFunction("isMobileDevice", [$this"isMobileDevice"]),
  157.             new TwigFunction("getNoticePopup", [$this"getNoticePopup"], ['is_safe' => ['html'], 'needs_environment' => true]),
  158.             new TwigFunction("getBrandingBanner", [$this"getBrandingBanner"], ['is_safe' => ['html'], 'needs_environment' => true]),
  159.             new TwigFunction("addSchemeAndHttpHostToImageSrc", [$this"addSchemeAndHttpHostToImageSrc"]),
  160.             new TwigFunction("getSupplierOfferPhotoBlockByOfferID", [$this"getSupplierOfferPhotoBlockByOfferID"], ['is_safe' => ['html'], 'needs_environment' => true]),
  161.             new TwigFunction("getUserCommentsMediaBlockByEntityID", [$this"getUserCommentsMediaBlockByEntityID"], ['is_safe' => ['html'], 'needs_environment' => true]),
  162.             new TwigFunction("getEntityRatingWithCount", [$this"getEntityRatingWithCount"]),
  163.             new TwigFunction("getInfoLine", [$this"getInfoLine"], ['is_safe' => ['html'], 'needs_environment' => true]),
  164.             new TwigFunction("getTeaserWatermark", [$this"getTeaserWatermark"]),
  165.             new TwigFunction("getCompaniesRatingBlock", [$this"getCompaniesRatingBlock"], ['is_safe' => ['html'], 'needs_environment' => true]),
  166.             new TwigFunction("getTestMenuItem", [$this"getTestMenuItem"]),
  167.             new TwigFunction("getMailingCampaignEntityPositionByEntityID", [$this"getMailingCampaignEntityPositionByEntityID"]),
  168.             new TwigFunction("getUsersOnlineCount", [$this"getUsersOnlineCount"]),
  169.             new TwigFunction("getActiveCityList", [$this"getActiveCityList"]),
  170.             new TwigFunction("getActiveSortedCityList", [$this"getActiveSortedCityList"]),
  171.             new TwigFunction("getCurrentCity", [$this"getCurrentCity"]),
  172.             new TwigFunction("setSeenMicrophoneTooltip", [$this"setSeenMicrophoneTooltip"]),
  173.             new TwigFunction("getFlierProductCategories", [$this"getFlierProductCategories"],  ['is_safe' => ['html'], 'needs_environment' => true]),
  174.             new TwigFunction("getFlierProductSubCategories", [$this"getFlierProductSubCategories"],  ['is_safe' => ['html'], 'needs_environment' => true]),
  175.             new TwigFunction("getIPLocationData", [$this"getIPLocationData"]),
  176.             new TwigFunction("isInDefaultCity", [$this"isInDefaultCity"]),
  177.             new TwigFunction("showMyPromocodesMenuItem", [$this"showMyPromocodesMenuItem"]),
  178.             new TwigFunction("getFooter", [$this"getFooter"],  ['is_safe' => ['html'], 'needs_environment' => true]),
  179.             new TwigFunction("addLazyAndLightboxImagesInDescription", [$this"addLazyAndLightboxImagesInDescription"]),
  180.             new TwigFunction("getStatVisitCount", [$this"getStatVisitCount"]),
  181.             new TwigFunction("getSaleCategoriesSortedBySaleVisits", [$this"getSaleCategoriesSortedBySaleVisits"]),
  182.             new TwigFunction("getMedia", [$this"getMedia"]),
  183.             new TwigFunction("logWrite", [$this"logWrite"]),
  184.             new TwigFunction('getLastVisitedOffersByUserId', [GetLastVisitedOffersTwigRuntime::class, 'getLastVisitedOffersByUserId']),
  185.             new TwigFunction("getManagerPhoneNumber", [$this"getManagerPhoneNumber"]),
  186.             new TwigFunction("getSocialProviderLoginUrl", [$this"getSocialProviderLoginUrl"]),
  187.             new TwigFunction("getBannerCodeFromFile", [$this"getBannerCodeFromFile"]),
  188.             new TwigFunction("getCurrentCityURL", [$this"getCurrentCityURL"]),
  189.             new TwigFunction("getMobileFloatingBanner", [MobileFloatingBannerRuntime::class, "getMobileFloatingBanner"]),
  190.             new TwigFunction("isTireDirector", [$this"isTireDirector"]),
  191.             new TwigFunction('isProductFastDelivery', [$this'isProductFastDelivery']),
  192.             new TwigFunction('calcDeliveryPriceOffer', [$this'calcDeliveryPriceOffer']),
  193.             new TwigFunction('calcDishDiscount', [$this'calcDishDiscount']),
  194.             new TwigFunction('getRTBHouseUID', [$this'getRTBHouseUID']),
  195.             new TwigFunction('getOfferConversion', [$this'getOfferConversion']),
  196.             new TwigFunction('getVimeoEmbedPreview', [$this'getVimeoEmbedPreview']),
  197.             new TwigFunction('qrGeneratorMessage', [QRMessageGeneratorRuntime::class, 'qrGeneratorMessage']),
  198.             new TwigFunction('getFilterOrdersCount', [OnlineOrderHistoryTwigRuntime::class, 'getFilterOrdersCount']),
  199.             new TwigFunction('getUserBalanceCodesCount', [$this'getUserBalanceCodesCount']),
  200.             new TwigFunction('showAppInviteModal', [$this'showAppInviteModal']),
  201.             new TwigFunction('getOfferManagers', [UserGroupTwigRuntime::class, 'getOfferManagers']),
  202.             new TwigFunction('getLinkOnlineOrder', [GetLinkOnlineOrderRuntime::class, 'getLinkOnlineOrder']),
  203.             new TwigFunction('getLinkFoodOnlineOrder', [GetLinkOnlineOrderRuntime::class, 'getLinkFoodOnlineOrder']),
  204.             new TwigFunction('getLinkGiftCertificateOnlineOrder', [GetLinkOnlineOrderRuntime::class, 'getLinkGiftCertificateOnlineOrder']),
  205.             new TwigFunction('getLinkGiftCertificateOnlineOrderByOnlyCode', [GetLinkOnlineOrderRuntime::class, 'getLinkGiftCertificateOnlineOrderByOnlyCode']),
  206.             new TwigFunction('getLinkTireOnlineOrder', [GetLinkOnlineOrderRuntime::class, 'getLinkTireOnlineOrder']),
  207.             new TwigFunction('isSubscriber', [SubscriptionTwigRuntime::class, 'isSubscriber']),
  208.             new TwigFunction('getSubscription', [SubscriptionTwigRuntime::class, 'getSubscription']),
  209.             new TwigFunction('getOfferCommentsCount', [GetCommentsCountTwigRuntime::class, 'getOfferCommentsCount']),
  210.             new TwigFunction('getDeliveryTimeText', [DeliveryTimeTextTwigRuntime::class, 'getDeliveryTimeText']),
  211.             new TwigFunction('getCertificateAddresses', [GiftCertificateTwigRuntime::class, 'getCertificateAddresses']),
  212.             new TwigFunction('isServerFeatureEnabled', [ServerFeatureStateTwigRuntime::class, 'isServerFeatureEnabled']),
  213.             new TwigFunction('getDishNutrients', [GetDishNutrientsTwigRuntime::class, 'getDishNutrients']),
  214.             new TwigFunction('getUserAverageCommentRating', [GetUserAverageCommentRatingTwigRuntime::class, 'getUserAverageCommentRating']),
  215.             new TwigFunction('getGiftSubscription', [GiftSubscriptionRuntime::class, 'getSubscription']),
  216.             new TwigFunction('getVoiceMessage', [VoiceMessageTwigRuntime::class, 'getVoiceMessage']),
  217.             new TwigFunction('getPhoneOrEmailForActivity', [UserInfoForTransferActivityTwigRuntime::class, 'getPhoneOrEmailForActivity']),
  218.             new TwigFunction('isPartnerGiftOfferAvailable', [PartnerGiftOfferTwigRuntime::class, 'isPartnerGiftOfferAvailable'])
  219.         );
  220.     }
  221.     public function getTests() {
  222.         return [
  223.             new TwigTest('instanceof', [$this'isInstanceof']),
  224.             new TwigTest('object', [$this'isObject'])
  225.         ];
  226.     }
  227.     public function getTeaserWatermark($offerID) {
  228.         return $this->entityManager->getRepository(Offer::class)->getTeaserWatermark($offerID);
  229.     }
  230.     public function addSchemeAndHttpHostToImageSrc($text) {
  231.         $schemeAndHttpHost $this->request->getSchemeAndHttpHost();
  232.         return preg_replace('/(<img.*src=")(?!http)[\/]{0,1}([^"]*)/'"$1".$schemeAndHttpHost."/$2"$text);
  233.     }
  234.     public function getBrandingBanner(Environment $twig$user$categoryIDs = [], $offerID null) {
  235.         // TODO:: REFACTORING AND CACHING
  236.         $brandingBannerList = [];
  237.         if ($user && $user->getEmail() == 'kristina@slivki.by') {
  238.             $dql "select brandingBanner from Slivki:BrandingBanner brandingBanner where brandingBanner.test = :test order by brandingBanner.ID desc";
  239.             $brandingBannerList $this->entityManager->createQuery($dql)->setParameter('test'true)->getResult();
  240.         }
  241.         if (empty($brandingBannerList)) {
  242.             $dql "select brandingBanner from Slivki:BrandingBanner brandingBanner where brandingBanner.activeSince < :timeFrom and brandingBanner.activeTill > :timeTo and brandingBanner.test = :test and brandingBanner.active = :active order by brandingBanner.ID desc";
  243.             $brandingBannerList $this->entityManager->createQuery($dql)
  244.                 ->setParameter('timeFrom', new \DateTime())
  245.                 ->setParameter('timeTo', (new \DateTime())->modify('-1 day'))
  246.                 ->setParameter('test'false)->setParameter('active'true)
  247.                 ->getResult();
  248.             $bannersForCity = [];
  249.             $currentCityID = (int) $this->getCurrentCity()->getID();
  250.             foreach ($brandingBannerList as $key => $item) {
  251.                 if (in_array($currentCityID$item->getCityIds(), true)) {
  252.                     $bannersForCity[] = $item;
  253.                 } else {
  254.                     unset($brandingBannerList[$key]);
  255.                 }
  256.             }
  257.             if (!empty($bannersForCity)) {
  258.                 $brandingBannerList $bannersForCity;
  259.             }
  260.         }
  261.         $currentBrandingBanner = [];
  262.         $refreshCookie $this->request->cookies->get('refresh');
  263.         if (empty($categoryIDs) && strpos($this->request->headers->get('referer'), 'https://www.t.dev.slivki.by') === false && !$refreshCookie) {
  264.             foreach ($brandingBannerList as $branding) {
  265.                 if ($branding->getTitle() == 'yandex') {
  266.                     $currentBrandingBanner = [$branding];
  267.                 }
  268.             }
  269.         }
  270.         if (!$currentBrandingBanner) {
  271.             $breaker false;
  272.             $currentCategory null;
  273.             $categoryBrandingBannerList = [];
  274.             /** @var  BrandingBanner $item */
  275.             foreach ($categoryIDs as $categoryID) {
  276.                 $currentCategory $this->entityManager->getRepository(Category::class)->find($categoryID);
  277.                 if ($currentCategory) {
  278.                     /** @var BrandingBanner $brandingBanner */
  279.                     foreach ($brandingBannerList as $item) {
  280.                         if ($item->isPassThrough()) {
  281.                             $categoryBrandingBannerList[] = $item;
  282.                         } else if ($item->isForCategories()) {
  283.                             foreach ($item->getCategories() as $brandingBannerCategory) {
  284.                                 if ($brandingBannerCategory->getID() == $categoryID || $currentCategory->isChildOfRecursive($brandingBannerCategory->getID())) {
  285.                                     $categoryBrandingBannerList[] = $item;
  286.                                     break;
  287.                                 }
  288.                             }
  289.                         }
  290.                     }
  291.                 }
  292.             }
  293.             if (!empty($categoryBrandingBannerList)) {
  294.                 $brandingBannerList $categoryBrandingBannerList;
  295.             } else {
  296.                 foreach ($categoryIDs as $categoryID) {
  297.                     $currentCategory $this->entityManager->getRepository(Category::class)->find($categoryID);
  298.                     if ($currentCategory) {
  299.                         foreach ($brandingBannerList as $key=>$item) {
  300.                             $removeCategoryFlag true;
  301.                             if ($item->isForCategories() && !$item->isPassThrough()) {
  302.                                 foreach ($item->getCategories() as $brandingBannerCategory) {
  303.                                     if (in_array($brandingBannerCategory->getID(), $categoryIDs) || $currentCategory->isChildOfRecursive($brandingBannerCategory->getID())) {
  304.                                         $removeCategoryFlag false;
  305.                                         break;
  306.                                     }
  307.                                 }
  308.                                 if ($removeCategoryFlag) {
  309.                                     unset($brandingBannerList[$key]);
  310.                                 }
  311.                             }
  312.                         }
  313.                     }
  314.                 }
  315.                 if (empty($categoryIDs)) {
  316.                     foreach ($brandingBannerList as $key=>$item) {
  317.                         if ($item->isForCategories() && !$item->isPassThrough()) {
  318.                             unset($brandingBannerList[$key]);
  319.                         }
  320.                     }
  321.                 }
  322.             }
  323.             $brandingBannerList array_values(array_unique($brandingBannerListSORT_REGULAR));
  324.             if (!$this->isMobileDevice()) {
  325.                 foreach ($brandingBannerList as $brandingBanner) {
  326.                     if (empty($currentBrandingBanner) or $breaker == true) {
  327.                         $currentBrandingBanner $brandingBanner;
  328.                     }
  329.                     if ($breaker) {
  330.                         break;
  331.                     }
  332.                     if ($brandingBanner->getBannerID() == $this->request->cookies->get('fullSiteBanner1')) {
  333.                         $breaker true;
  334.                     }
  335.                 }
  336.             } else {
  337.                 foreach ($brandingBannerList as $brandingBanner) {
  338.                     if (($brandingBanner->getMobileImage() || $brandingBanner->getMobileDivider()) && (empty($currentBrandingBanner) || $breaker == true)) {
  339.                         $currentBrandingBanner $brandingBanner;
  340.                     }
  341.                     if ($breaker) {
  342.                         break;
  343.                     }
  344.                     if ($brandingBanner->getBannerID() == $this->request->cookies->get('fullSiteBanner1')) {
  345.                         $breaker true;
  346.                     }
  347.                 }
  348.             }
  349.         }
  350.         if (self::isMobileDevice()) {
  351.             return empty($currentBrandingBanner) ? null $currentBrandingBanner;
  352.         }
  353.         if (empty($currentBrandingBanner)) {
  354.             return '';
  355.         } else {
  356.             if (is_array($currentBrandingBanner)) {
  357.                 $currentBrandingBanner $currentBrandingBanner[0];
  358.             }
  359.             return $twig->render('Slivki/banners/branding_banner.html.twig', ['brandingBanner' => $currentBrandingBanner]);
  360.         }
  361.     }
  362.     public function getNoticePopup(Environment $twig$user) {
  363.         $noticePopup '';
  364.         $directorPopupID null;
  365.         try {
  366.             /** @var User $user */
  367.             if (($user && $user->hasRole(UserGroup::ROLE_SUPPLIER_ID))) {
  368.                 if (!$user->hasRole(UserGroup::ROLE_DIRECTOR_A) && !$user->hasRole(UserGroup::ROLE_DIRECTOR_B)) {
  369.                     $softCache = new SoftCache('director');
  370.                     $lastDirectorGroup $softCache->get('last_director_group'0);
  371.                     $lastDirectorGroup++;
  372.                     if ($lastDirectorGroup 1) {
  373.                         $lastDirectorGroup 0;
  374.                     }
  375.                     $softCache->set('last_director_group'$lastDirectorGroup0);
  376.                     if ($lastDirectorGroup == 0) {
  377.                         $role $this->entityManager->getRepository(UserGroup::class)->find(UserGroup::ROLE_DIRECTOR_A);
  378.                         $user->addRole($role);
  379.                     } else {
  380.                         $role $this->entityManager->getRepository(UserGroup::class)->find(UserGroup::ROLE_DIRECTOR_B);
  381.                         $user->addRole($role);
  382.                     }
  383.                     $this->entityManager->flush();
  384.                 }
  385.                 if ($user->hasRole(UserGroup::ROLE_DIRECTOR_A)) {
  386.                     $directorPopupID NoticePopup::ID_DIRECTOR_A;
  387.                 } else {
  388.                     $directorPopupID NoticePopup::ID_DIRECTOR_B;
  389.                 }
  390.                 $noticePopup $this->getNoticePopupByID($twig$user$directorPopupID);
  391.             }
  392.             if ($noticePopup == '') {
  393.                 $noticePopup $this->getNoticePopupByID($twig$userNoticePopup::ID_USER);
  394.             }
  395.             if ($user && $user->getEmail() == 'volga@slivki.by') {
  396.                 $testPopups $this->entityManager->getRepository(NoticePopup::class)->findBy(['test' => true'type' => $directorPopupID]);
  397.                 if (isset($testPopups[0])) {
  398.                     $noticePopup $this->getNoticePopupByID($twig$user$testPopups[0]->getType());
  399.                 }
  400.             }
  401.         } catch (\Exception $e) {
  402.             Logger::instance('Notice popup error')->info($e->getMessage());
  403.             return '';
  404.         }
  405.         return $noticePopup;
  406.     }
  407.     private function getNoticePopupByID(Environment $twig$user$type) {
  408.         if ($this->isMobileDevice()) {
  409.             return '';
  410.         }
  411.         $dql 'select noticePopup from Slivki:NoticePopup noticePopup where noticePopup.active = true and noticePopup.type = :type and current_timestamp() between noticePopup.activeFrom and noticePopup.activeTill';
  412.         $noticePopupList $this->entityManager->createQuery($dql)->setParameter('type'$type)->getResult();
  413.         if (!$noticePopupList || count($noticePopupList) == 0) {
  414.             return '';
  415.         }
  416.         $noticePopup $noticePopupList[0];
  417.         if ($noticePopup->isTest() && $user && $user->getEmail() == 'volga@slivki.by') {
  418.             $noticePopupHtml $this->getNoticePopupHtmlView($twig$noticePopup);
  419.             return $noticePopupHtml == SoftCache::EMPTY_VALUE '' $noticePopupHtml;
  420.         }
  421.         if ($noticePopup) {
  422.             if ($this->isNoticePopupShown($noticePopup->getCookieName(), $user$noticePopup->getID())) {
  423.                 return '';
  424.             }
  425.         }
  426.         $noticePopupHtml $this->getNoticePopupHtmlView($twig$noticePopup);
  427.         return $noticePopupHtml == SoftCache::EMPTY_VALUE '' $noticePopupHtml;
  428.     }
  429.     private function getNoticePopupHtmlView(Environment $twig$noticePopup) {
  430.         $noticePopupID $noticePopup->getID();
  431.         $image $this->entityManager->getRepository(Media::class)
  432.             ->findOneBy(['entityID' => $noticePopupID'mediaType' => MediaType::TYPE_NOTICE_POPUP_IMAGE_ID], ['ID' => 'desc']);
  433.         $imageMobile $this->entityManager->getRepository(Media::class)
  434.             ->findOneBy(['entityID' => $noticePopupID'mediaType' => MediaType::TYPE_NOTICE_POPUP_IMAGE_ID_MOBILE], ['ID' => 'desc']);
  435.         return $twig->render('Slivki/popups/notice_popup.html.twig',
  436.             ['id'=> 'notice_popup''noticePopup' => $noticePopup'image' => $image'imageMobile' => $imageMobile]);
  437.     }
  438.     private function isNoticePopupShown($popupID$user$sessionKeySuffix) {
  439.         if (!$user) {
  440.             return false;
  441.         }
  442.         $session = new Session();
  443.         $sessionKey 'noticePopup' $sessionKeySuffix;
  444.         $noticePopupID $session->get($sessionKey);
  445.         if (!$noticePopupID) {
  446.             $popupView $this->entityManager->getRepository(NoticePopupView::class)->findBy(['popupID' => $popupID'userID' => $user->getID()]);
  447.             if (!$popupView) {
  448.                 $popupView = new NoticePopupView();
  449.                 $popupView->setCreatedOn(new \DateTime());
  450.                 $popupView->setPopupID($popupID);
  451.                 $popupView->setUserID($user->getID());
  452.                 $this->entityManager->persist($popupView);
  453.                 $this->entityManager->flush($popupView);
  454.                 return false;
  455.             }
  456.             $session->set($sessionKey$popupID);
  457.             return true;
  458.         }
  459.         if ($noticePopupID != $popupID) {
  460.             $session->set($sessionKey$popupID);
  461.             return false;
  462.         }
  463.         return true;
  464.     }
  465.     public function isMobileDevice() {
  466.         return CommonUtil::isMobileDevice($this->request);
  467.     }
  468.     public function getInfoPages($type) {
  469.         return $this->entityManager->getRepository(InfoPage::class)->getInfoPagesFromCache($type);
  470.     }
  471.     public function getTopSiteBanner($categoryIDs = [], $mobile$mobileCache false) {
  472.         return $this->bannerService->getHeadBannerCached($this->getCurrentCity()->getID(), $categoryIDs$mobile$mobileCache);
  473.     }
  474.     public function getCategoryBanner($categoryID) {
  475.         $categoryRepository $this->entityManager->getRepository(Category::class);
  476.         $category $categoryRepository->findCached($categoryID);
  477.         if (isset($category['category']) && $category['category'] instanceof Category) {
  478.             return $this->bannerService->getCategoryBannerCached($category['category'], self::isMobileDevice());
  479.         }
  480.         return '';
  481.     }
  482.     public function getGoogleBanner(Environment $twig) {
  483.         return $twig->render('Slivki/banners/head_banner_admixer.html.twig');
  484.     }
  485.     public function getCategoryTypeList() {
  486.         return $this->entityManager->getRepository(CategoryType::class)->findAll();
  487.     }
  488.     public function getLastComments() {
  489.         $dql 'select comment from Slivki:Comment comment where comment.hidden = false and comment.confirmedPhone = true order by comment.createdOn desc';
  490.         $commentQuery $this->entityManager->createQuery($dql);
  491.         $commentQuery->setMaxResults(3);
  492.         return $commentQuery->getResult();
  493.     }
  494.     public function getTopCityList() {
  495.         return $this->entityManager->getRepository(City::class)->findBy(['parent' => null], ['ID' => 'ASC']);
  496.     }
  497.     public function getCityList() {
  498.         return $this->entityManager->getRepository(City::class)->getCitiesSorted();
  499.     }
  500.     public function getCategoriesList($domainObjectID$cityID City::DEFAULT_CITY_ID) {
  501.         return $this->entityManager->getRepository(Category::class)->getCategoryTree($domainObjectID0$cityID);
  502.     }
  503.     public function getTopLevelCategories() {
  504.         $categoryRepository $this->entityManager->getRepository(Category::class);
  505.         return $categoryRepository->getTopLevelOfferCategories($this->request->getSession()->get(City::CITY_ID_SESSION_KEYCity::DEFAULT_CITY_ID));
  506.     }
  507.     public function getURL($action$entityID$withDomain false) {
  508.         $url "";
  509.         $seoRepository $this->entityManager->getRepository(Seo::class);
  510.         $seo $seoRepository->getByEntity($action$entityID);
  511.         if ($seo) {
  512.             $url $seo->getMainAlias();
  513.             if ($withDomain) {
  514.                 $url 'https://' $seo->getDomain() . '.slivki.by' $url;
  515.             }
  516.         }
  517.         return $url;
  518.     }
  519.     public function getCategoryURL(Category $category) {
  520.         return $this->entityManager->getRepository(Seo::class)->getCategoryURL($category);
  521.     }
  522.     public function getImageURL($media null$width$height) {
  523.         $imageUrl ImageService::FALLBACK_IMAGE;
  524.         if (!$media) {
  525.             return $imageUrl;
  526.         }
  527.         try {
  528.             $imageUrl $this->imageService->getImageURLCached($media$width$height);
  529.         } catch (Exception $exception) {
  530.             $logger Logger::instance('TwigExtension');
  531.             $logger->info('Media ID = ' $media->getID() . ', file = ' $media->getPath() . $media->getName() . ' not found in getImageURL!');
  532.         }
  533.         return $imageUrl;
  534.     }
  535.     public function getProfileImageURL($media$width$height) {
  536.         if (!$media) {
  537.             return ImageService::DEFAULT_AVATAR;
  538.         }
  539.         return $this->imageService->getImageURL($media$width$height);
  540.     }
  541.     public function getMetaInfo() {
  542.         $metaInfo $this->request->attributes->get(SiteController::PARAMETER_META_INFO);
  543.         if ($metaInfo) {
  544.             $metaInfo = array(
  545.                 "title" => $metaInfo->getTitle(),
  546.                 "metaTitle" => $metaInfo->getMetaTitle(),
  547.                 "metaDescription" => $metaInfo->getMetaDescription(),
  548.                 "metaKeywords" => $metaInfo->getMetaKeywords()
  549.             );
  550.         } else {
  551.             $metaInfo = array(
  552.                 "title" => '',
  553.                 "metaTitle" => "Скидки в Минске! Акции и распродажи на Slivki.by!",
  554.                 "metaDescription" => "Грандиозные скидки и акции в Минске: рестораны, салоны красоты, клубы, спорт, досуг... Лучшие цены в популярных заведениях и магазинах Минска на Slivki.by!",
  555.                 "metaKeywords" => "скидки, распродажи, рекламные акции, Минск, кредит, дисконтная карта, карточка, новогодние скидки, Сезонные Распродажи и скидки, праздничная распродажа, распродажа одежды, приз, подарок, сюрприз, сувенир, РБ, Республика Беларусь, единая дисконтная система Беларуси, выгода, товары и услуги выгодно, экономия, промо, Акция, магазин, успеть купить, Модная одежда, фирмы, компании, рестораны, Минск, сезон потребитель дешево качественно быстро удобно продавец Новый товар"
  556.             );
  557.         }
  558.         return $metaInfo;
  559.     }
  560.     public function getSidebar(Environment $environment$categoryID null): string
  561.     {
  562.         if (!$this->serverFeatureStateChecker->isServerFeatureEnabled(SwitcherFeatures::SALES())) {
  563.             return $environment->render('Slivki/uz/sidebar.html.twig');
  564.         }
  565.         $sidebarCached $this->sidebarCacheService->getSidebarCached();
  566.         if (null === $sidebarCached) {
  567.             return '';
  568.         }
  569.         return $sidebarCached->getFirstPage();
  570.     }
  571.     public function getBannerCodeFromFile($banner) {
  572.         $filePath realpath($this->kernel->getProjectDir() . '/public') . $banner->getCodeFilePath();
  573.         $fileContent = @file_get_contents($filePath);
  574.         return $fileContent;
  575.     }
  576.     /**
  577.      * @return \Slivki\Entity\SiteSettings
  578.      */
  579.     public function getSiteSettings() {
  580.         $softCache = new SoftCache("");
  581.         $cacheKey SiteSettingsRepository::CACHE_NAME;
  582.         $settingsCached $softCache->get($cacheKey);
  583.         if ($settingsCached) {
  584.             return $settingsCached;
  585.         }
  586.         $settingsCached $this->entityManager->getRepository(SiteSettings::class)->findAll();
  587.         $settingsCached $settingsCached[0];
  588.         $softCache->set($cacheKey$settingsCached60 60);
  589.         return $settingsCached;
  590.     }
  591.     public function staticCall($function$arguments = []) {
  592.         return call_user_func($function$arguments);
  593.     }
  594.     public function getStatVisitCount($entityID$entityType$dateFrom$dateTo) {
  595.         $entityManager $this->entityManager;
  596.         switch ($entityType) {
  597.             case Visit::TYPE_MALL_ALL_PAGES:
  598.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_MALL_MAIN_PAGE.",".Visit::TYPE_MALL_DETAILS.",".Visit::TYPE_MALL_BRAND.")";
  599.                 break;
  600.             case Visit::TYPE_SLIVKI_TV_ALL:
  601.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_SLIVKI_TV_GUIDES.")";
  602.                 break;
  603.             case Visit::TYPE_FLIER_ALL:
  604.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_FLIER_DETAILS.")";
  605.                 break;
  606.             case Visit::TYPE_OFFERS_ALL:
  607.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_OFFER.")";
  608.                 break;
  609.             case Visit::TYPE_OFFER_CATEGORIES_ALL:
  610.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_OFFER_CATEGORY.")";
  611.                 break;
  612.             case Visit::TYPE_SALE_ALL:
  613.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_SALE.")";
  614.                 break;
  615.             case Visit::TYPE_SALE_CATEGORIES_ALL: {
  616.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_SALE_CATEGORY.")";
  617.                 break;
  618.             }
  619.             case Visit::TYPE_OFFER_BY_CATEGORY:
  620.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_OFFER.") and entity_id in (select entity_id from category2entity where category_id = ".$entityID.")";
  621.                 break;
  622.             case Visit::TYPE_OFFER_BY_CATEGORY_REF:
  623.                 $sql "select sum(visit_count_ref) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_OFFER.") and entity_id in (select entity_id from category2entity where category_id = ".$entityID.")";
  624.                 break;
  625.             case Visit::TYPE_FLIER_CATEGORIES_ALL: {
  626.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_FLIER_CATEGORY.")";
  627.                 break;
  628.             }
  629.             case Visit::TYPE_SLIVKI_TV_CATEGORIES_ALL: {
  630.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_type_id in (".Visit::TYPE_SLIVKI_TV_CATEGORIES_ALL.")";
  631.                 break;
  632.             }
  633.             default:
  634.                 $sql "select sum(visit_count) from stat_visit_count_aggregated where visit_date >= '$dateFrom' and visit_date <= '$dateTo' and entity_id = $entityID and entity_type_id = $entityType";
  635.                 break;
  636.         }
  637.         $count $entityManager->getConnection()->executeQuery($sql)->fetchColumn();
  638.         return $count $count 0;
  639.     }
  640.     public function getVisitCount($id$type$increment true) {
  641.         return $this->entityManager->getRepository(Visit::class)->getVisitCount($id$type$increment);
  642.     }
  643.     public function getOfferVisitCount($offer$today false) {
  644.         if ($today) {
  645.             return $this->entityManager->getRepository(Offer::class)->getVisitCount($offertrue);
  646.         }
  647.         return $this->entityManager->getRepository(Visit::class)->getVisitCount($offer->getID(), Visit::TYPE_OFFER30);
  648.     }
  649.     public function getOfferConversion(int $offerID): string
  650.     {
  651.         $visitCount $this->entityManager
  652.             ->getRepository(Visit::class)
  653.             ->getVisitCount($offerIDVisit::TYPE_OFFER30);
  654.         $purchaseCount $this->entityManager
  655.             ->getRepository(PurchaseCount::class)
  656.             ->findOneBy(['entityID' => $offerID]);
  657.         if (
  658.             $visitCount === ||
  659.             !$purchaseCount ||
  660.             !$purchaseCount->getPurchaseCountLastMonthWithCorrection()
  661.         ) {
  662.             return 0;
  663.         }
  664.         $purchases $purchaseCount->getPurchaseCountLastMonthWithCorrection();
  665.         $conversion = ($purchases $visitCount) * 100;
  666.         return round($conversion 10);
  667.     }
  668.     public function getSaleVisitCount($saleID$daysCount 30$reload false) { //TODO: Use one function for all types
  669.         return $this->entityManager->getRepository(Visit::class)->getVisitCount($saleIDCategory::SALE_CATEGORY_ID$daysCount$reload);
  670.     }
  671.     public function getVideoGuideVisitCount($saleID) {
  672.         return $this->entityManager->getRepository(VisitCounter::class)->getVisitCount($saleIDVisitCounter::TYPE_SALEfalse);
  673.     }
  674.     public function getOfferMonthlyPurchaseCount(int $offerId): int
  675.     {
  676.         return $this->purchaseCountDao->getLastMonthWithCorrectionByOfferId($offerId);
  677.     }
  678.     public function getBankCurrencyList() {
  679.         return $this->entityManager->getRepository(BankCurrency::class)->findBy([], ['ID' => 'ASC']);
  680.     }
  681.     public function getCategoryBreadcrumbs(Category $category) {
  682.         return $this->entityManager->getRepository(Category::class)->getCategoryBreadcrumbs($category);
  683.     }
  684.     public function getCommentEntityByType($entityID$typeID) {
  685.         return $this->entityManager->getRepository(Comment::class)->getCommentEntity($entityID$typeID);
  686.     }
  687.     public function getCommentsCountByUserID($userID$entityID$typeID) {
  688.         return $this->entityManager->getRepository(Comment::class)->getCommentsCountByUserID($userID$entityID$typeID);
  689.     }
  690.     public function getMainMenu(Environment $twig$showStatistics$oldTemplate true) { //TODO: Pass all data to view directly
  691.         $mobileDevice $this->isMobileDevice();
  692.         $cityID $this->request->getSession()->get(City::CITY_ID_SESSION_KEYCity::DEFAULT_CITY_ID);
  693.         $type null;
  694.         if ($showStatistics) {
  695.             $type TextCacheService::MAIN_MENU_STATISTICS_TYPE;
  696.         } else  {
  697.             $type $mobileDevice TextCacheService::MAIN_MENU_MOBILE_TYPE TextCacheService::MAIN_MENU_TYPE;
  698.         }
  699.         if ($mobileDevice && $oldTemplate) {
  700.             $type TextCacheService::MAIN_MENU_OLD_MOBILE_TYPE;
  701.         }
  702.         $textCacheService $this->textCacheService;
  703.         $mainMenu $textCacheService->getText($cityID$typetrue);
  704.         if ($mainMenu) {
  705.             if (!$mobileDevice) {
  706.                 $mainMenuBanner '';
  707.                 $mainMenuBanners $this->bannerService->getMainMenuBannerCached();
  708.                 if ($mainMenuBanners) {
  709.                     $mainMenuBannersCount count($mainMenuBanners);
  710.                     $currentMainMenuBannerIndex $this->request->cookies->get('mainMenuBanner', -1);
  711.                     if ($currentMainMenuBannerIndex == $mainMenuBannersCount 1) {
  712.                         $currentMainMenuBannerIndex 0;
  713.                     } else {
  714.                         $currentMainMenuBannerIndex++;
  715.                     }
  716.                     if (isset($mainMenuBanners[$currentMainMenuBannerIndex])) {
  717.                         $mainMenuBanner $mainMenuBanners[$currentMainMenuBannerIndex];
  718.                     }
  719.                 }
  720.                 $mainMenu[3] = str_replace('||mainMenuBannerPlaceholder||'$mainMenuBanner$mainMenu[3]);
  721.             }
  722.             return $mainMenu[3];
  723.         }
  724.         return '';
  725.     }
  726.     public function getCommentsMenuItems() {
  727.         return $this->entityManager->getRepository(Comment::class)->getTopMenu();
  728.     }
  729.     public function getActiveSubCategories($categoryID) {
  730.         $city $this->getCurrentCity();
  731.         return $this->entityManager->getRepository(Category::class)->getActiveCategories(
  732.             $categoryID, [Category::DEFAULT_CATEGORY_TYPECategory::SERVICE_CATEGORY_TYPE], Category::OFFER_CATEGORY_ID$city->getID());
  733.     }
  734.     public function getTestMenuItem($itemID)
  735.     {
  736.         $keySuffix self::isMobileDevice() ? 'Mobile' '';
  737.         switch ($itemID) {
  738.             case 0:
  739.                 if (!self::$photoGuideMenuItem) {
  740.                     $url $this->getURL(SeoRepository::RESOURCE_URL_SALE_CATEGORYCategory::PHOTOGUIDE_SALE_CATEGORY_ID);
  741.                     self::$photoGuideMenuItem = ['name' => 'Фотогиды''url' => $url];
  742.                 }
  743.                 return self::$photoGuideMenuItem;
  744.             case 1:
  745.                 return ['name' => 'Новости скидок''url' => '/skidki-i-rasprodazhi'];
  746.             case 2:
  747.                 return ['name' => 'e-Товары''abKey' => 'e-tovary' $keySuffix];
  748.         }
  749.         return null;
  750.     }
  751.     public function getActiveSalesCount() {
  752.         return $this->entityManager->getRepository(Sale::class)->getActiveSalesCountCached();
  753.     }
  754.     public function getActiveOffersCount($cityID City::DEFAULT_CITY_ID) {
  755.         return $this->entityManager->getRepository(Offer::class)->getActiveOffersCountCached($cityID);
  756.     }
  757.     public function getPartnerLogoURL($partnerID) {
  758.         if ($this->partnerLogoURL) {
  759.             //return $this->partnerLogoURL;
  760.         }
  761.         $media $this->entityManager->getRepository(Media::class)->getMedia($partnerIDMediaType::TYPE_PARTNER_LOGO_ID);
  762.         if ($media) {
  763.             $this->partnerLogoURL $this->imageService->getImageURLCached($media[0], 860);
  764.         } else {
  765.             $this->partnerLogoURL ImageService::FALLBACK_IMAGE;;
  766.         }
  767.         return $this->partnerLogoURL;
  768.     }
  769.     /**
  770.      * Detect & return the ending for the plural word
  771.      *
  772.      * @param  integer $endings  nouns or endings words for (1, 4, 5)
  773.      * @param  array   $number   number rows to ending determine
  774.      *
  775.      * @return string
  776.      *
  777.      * @example:
  778.      * {{ ['Остался %d час', 'Осталось %d часа', 'Осталось %d часов']|plural(11) }}
  779.      * {{ count }} стат{{ ['ья','ьи','ей']|plural(count)
  780.      */
  781.     function pluralFilter($endings$number) {
  782.         return CommonUtil::plural($endings$number);
  783.     }
  784.     function pregReplaceFilter($str$pattern$replacement) {
  785.         return preg_replace($pattern$replacement$str);
  786.     }
  787.     public function localizeDateFilter(DateTimeInterface $dateTime$format$locale 'ru_Ru'): string
  788.     {
  789.         return (new IntlDateFormatter(
  790.             $locale,
  791.             IntlDateFormatter::NONE,
  792.             IntlDateFormatter::NONE,
  793.             date_default_timezone_get(),
  794.             IntlDateFormatter::GREGORIAN,
  795.             $format
  796.         ))->format($dateTime);
  797.     }
  798.     public function localizeDateTimestampFilter(string $timestampstring $formatstring $locale 'ru_Ru'): string
  799.     {
  800.         return (new \IntlDateFormatter(
  801.             $locale,
  802.             \IntlDateFormatter::NONE,
  803.             \IntlDateFormatter::NONE,
  804.             date_default_timezone_get(),
  805.             \IntlDateFormatter::GREGORIAN,
  806.             $format
  807.         ))->format((new \DateTimeImmutable())->setTimestamp($timestamp));
  808.     }
  809.     function phoneFilter($number) {
  810.         if ($number != (int)$number || strlen((string)$number) != 12) {
  811.             return $number;
  812.         }
  813.         return substr($number,5,3) . ' ' .  substr($number,8,2) . ' '
  814.             substr($number,10,2);
  815.     }
  816.     function jsonDecodeFilter($json) {
  817.         return json_decode($json);
  818.     }
  819.     /**
  820.      * @param $var
  821.      * @param $instance
  822.      * @return bool
  823.      */
  824.     public function isInstanceof($var$instance) {
  825.         return  $var instanceof $instance;
  826.     }
  827.     public function isObject($var) {
  828.         return is_object($var);
  829.     }
  830.     public function getName() {
  831.         return "slivki_extension";
  832.     }
  833.     public function getSaleShortDescription(Sale $sale) {
  834.         $saleDescriptions $sale->getDescriptions();
  835.         if (!$saleDescriptions || $saleDescriptions->count() == 0) {
  836.             return '';
  837.         }
  838.         $description $sale->getDescriptions()->first()->getDescription();
  839.         $crawler = new Crawler();
  840.         $crawler->addHtmlContent($description);
  841.         $pList $crawler->filter('p');
  842.         $i 0;
  843.         $shortDescription '';
  844.         foreach ($pList as $domElement) {
  845.             $p htmlentities($domElement->textContentnull'utf-8');
  846.             $p trim(str_replace("&nbsp;"" "$p));
  847.             $p html_entity_decode($p);
  848.             if (strlen($p) > 0) {
  849.                 $i++;
  850.                 if($i == 2) {
  851.                     $shortDescription $p;
  852.                     break;
  853.                 }
  854.             }
  855.         }
  856.         $shortDescription strip_tags($shortDescription);
  857.         return $shortDescription;
  858.     }
  859.     public function getSupplierOfferPhotoBlockByOfferID(Environment $twig$offerID) {
  860.         $perPage 20;
  861.         $mediaList $this->cacheService->getMediaList($offerID,OfferSupplierPhotoMedia::TYPE00$perPage);
  862.         if (empty($mediaList)) {
  863.             return '';
  864.         }
  865.         $data = [
  866.             'offerID' => $offerID,
  867.             'supplierOfferPhotoList' => $mediaList
  868.         ];
  869.         $data['supplierOfferPhotoList'] = array_slice($data['supplierOfferPhotoList'], 020);
  870.         return $twig->render('Slivki/comments/offer_supplier_photo_block.html.twig'$data);
  871.     }
  872.     public function getUserCommentsMediaBlockByEntityID(Environment $twig$entityID$entityType) {
  873.         $data['commentAndMediaList'] = [];
  874.         switch ($entityType) {
  875.             case 'category':
  876.                 $data['commentAndMediaList'] = $this->entityManager->getRepository(Media::class)->getOfferCommentMediaListByCategoryID($entityID);
  877.                 break;
  878.             case 'offer':
  879.                 $data['commentAndMediaList'] = $this->entityManager->getRepository(Media::class)->getOfferCommentMediaListByOfferID($entityID);
  880.                 break;
  881.             case 'all':
  882.                 $data['commentAndMediaList'] = $this->entityManager->getRepository(Media::class)->getOfferCommentMediaList();
  883.                 break;
  884.         }
  885.         $data['entityID'] = $entityID;
  886.         $data['entityType'] = $entityType;
  887.         if(empty($data['commentAndMediaList'])) {
  888.             return '';
  889.         }
  890.         $html $twig->render('Slivki/comments/media_block.html.twig'$data);
  891.         return $html;
  892.     }
  893.     public function getEntityRatingWithCount($entityType$entityID$dateFrom false$dateTo false) {
  894.         return $this->entityManager->getRepository(Comment::class)->getEntityRatingWithCount($entityType$entityID$dateFrom$dateTo);
  895.     }
  896.     public function getCompaniesRatingBlock(Environment $twig$categoryID$isMobile false) {
  897.         $type $isMobile TextCacheService::COMPANIES_RATING_MOBILE_TYPE TextCacheService::COMPANIES_RATING_TYPE;
  898.         $html $this->textCacheService->getText($categoryID$type);
  899.         if (!$html || $html == '') { //TODO: remove
  900.             $softCache = new SoftCache(OfferRepository::CACHE_NAME);
  901.             $mobileCacheName $isMobile 'mobile-' '';
  902.             $html $softCache->get('company-rating-data-' '-' $mobileCacheName $categoryID);
  903.         }
  904.         return $html $html '';
  905.     }
  906.     public function getMailingCampaignEntityPositionByEntityID($mailingCampaignID$entityID$entityType) {
  907.         $mailingCampaign $this->entityManager->getRepository(MailingCampaign::class)->find($mailingCampaignID);
  908.         return $mailingCampaign->getEntityPositionByEntityID($entityID$entityType);
  909.     }
  910.     public function getActiveCityList() {
  911.         return $this->entityManager->getRepository(City::class)->getActiveCitiesCached();
  912.     }
  913.     public function getActiveSortedCityList() {
  914.         return $this->entityManager->getRepository(City::class)->getActiveSortedCitiesCached();
  915.     }
  916.     public function getCurrentCity() {
  917.         return $this->entityManager->getRepository(City::class)->findCached($this->request->getSession()->get(City::CITY_ID_SESSION_KEYCity::DEFAULT_CITY_ID));
  918.     }
  919.     public function setSeenMicrophoneTooltip(User $user) {
  920.         if (!$user->isSeenMicrophoneTooltip()) {
  921.             $user $this->entityManager->merge($user);
  922.             $user->setSeenMicrophoneTooltip();
  923.             $this->entityManager->flush($user);
  924.         }
  925.     }
  926.     public function getFlierProductCategories(Environment $twig) {
  927.         $dql "select category from Slivki:ProductCategory category where category.parents is empty order by category.name ASC";
  928.         $categories $this->entityManager->createQuery($dql)->getResult();
  929.         return $twig->render('Slivki/admin/sales/products/category_list.html.twig', ['categories' => $categories]);
  930.     }
  931.     public function getFlierProductSubCategories(Environment $twig$categoryID) {
  932.         $categories $this->entityManager->getRepository(ProductCategory::class)->find($categoryID);
  933.         $subCategories $categories->getSubCategories();
  934.         return  $twig->render('Slivki/admin/sales/products/sub_category_list.html.twig', ['categories' => $subCategories]);
  935.     }
  936.     public function getIPLocationData() {
  937.         $defaultLocation = [53.90225027.561889];
  938.         return $defaultLocation;
  939.         $data $this->entityManager->getRepository(City::class)->getIPLocationData($this->request);
  940.         if (!$data) {
  941.             return $defaultLocation;
  942.         }
  943.         return [$data['latitude'], $data['longitude']];
  944.     }
  945.     public function isInDefaultCity() {
  946.         return City::DEFAULT_CITY_ID == $this->entityManager->getRepository(City::class)->getCityIDByGeoIP($this->request);
  947.     }
  948.     public function isTireDirector($userID) {
  949.         return $this->entityManager->getRepository(Director::class)->isTireDirector($userID);
  950.     }
  951.     public function getCurrentCityURL() {
  952.         $cityID $this->request->getSession()->get(City::CITY_ID_SESSION_KEYCity::DEFAULT_CITY_ID);
  953.         if ($cityID == City::DEFAULT_CITY_ID) {
  954.             return '/';
  955.         }
  956.         return $this->entityManager->getRepository(Seo::class)->getCityURL($cityID);
  957.     }
  958.     public function showMyPromocodesMenuItem(User $user) {
  959.         $userRepository $this->entityManager->getRepository(User::class);
  960.         $lastBuyDate $userRepository->getLastBuyDate($user);
  961.         if (!$lastBuyDate) {
  962.             return false;
  963.         }
  964.         return time() - $lastBuyDate->format('U') < 30 24 3600;
  965.     }
  966.     public function getFooter(Environment $twig): string
  967.     {
  968.         $mobile $this->isMobileDevice();
  969.         $footerVersion rand(01);
  970.         $cacheKey 'footer-2-' $this->getCurrentCity()->getID() . '-'  . ($mobile '-mobile' '') . '-' $footerVersion;
  971.         $softCache = new SoftCache('');
  972.         $footer $softCache->getDataWithLock($cacheKey);
  973.         if (!$footer) {
  974.             $view sprintf(
  975.                 'Slivki%s/footer%s.html.twig',
  976.                 $this->parameterBag->get('regional_template_path'),
  977.                 $mobile '_mobile' '',
  978.             );
  979.             $footer $twig->render($view, ['footerVersion' => $footerVersion]);
  980.             $data = ['data' => $footer'expDate' => time() + 60 60];
  981.             $softCache->set($cacheKey$data0);
  982.         }
  983.         return $footer;
  984.     }
  985.     public function getSaleCategoriesSortedBySaleVisits() {
  986.         return $this->entityManager->getRepository(Category::class)->getSaleCategoriesSortedBySaleVisits();
  987.     }
  988.     public function addLazyAndLightboxImagesInDescription($description) {
  989.         if (trim($description) == '') {
  990.             return '';
  991.         }
  992.         $html = new Crawler();
  993.         $html->addHtmlContent($description);
  994.         $nodeList $html->filter('img');
  995.         if (!empty($nodeList)) {
  996.             $nodeList->each(function (Crawler $node) {
  997.                 $nodeElem $node->getNode(0);
  998.                 $source $nodeElem->getAttribute('src');
  999.                 $dataOriginal $nodeElem->getAttribute('data-original');
  1000.                 if ($source and !$dataOriginal) {
  1001.                     $nodeElem->setAttribute('src''/common-img/d.gif');
  1002.                     $nodeElem->setAttribute('data-original'$source);
  1003.                     $nodeElem->setAttribute('class''sale-lazy-spin');
  1004.                     $parentElem $node->parents()->first()->getNode(0);
  1005.                     $ratio $nodeElem->getAttribute('data-ratio');
  1006.                     if ($ratio != '') {
  1007.                         $newParentNode = new \DOMElement('div');
  1008.                         $parentElem->replaceChild($newParentNode$nodeElem);
  1009.                         $newParentNode->setAttribute('class''sale-lazy-wrap');
  1010.                         $width $nodeElem->getAttribute('width');
  1011.                         $style $nodeElem->getAttribute('style');
  1012.                         $newParentNode->setAttribute('style''max-width:' $width'px;' $style);
  1013.                         $nodeForPadding =  new \DOMElement('div');
  1014.                         $newParentNode->appendChild($nodeForPadding);
  1015.                         $nodeForPadding->setAttribute('style''padding-bottom:' $ratio'%');
  1016.                         $newParentNode->appendChild($nodeElem);
  1017.                     }
  1018.                 }
  1019.             });
  1020.         }
  1021.         if (self::isMobileDevice()) {
  1022.             $nodeList $html->filter('iframe');
  1023.             if (!empty($nodeList)) {
  1024.                 $nodeList->each(function (Crawler $node) {
  1025.                     $nodeElem $node->getNode(0);
  1026.                     $source $nodeElem->getAttribute('src');
  1027.                     if (strpos($source'youtube') !== false) {
  1028.                         $parentElem $node->parents()->first()->getNode(0);
  1029.                         $newParentNode = new \DOMElement('div');
  1030.                         $parentElem->replaceChild($newParentNode$nodeElem);
  1031.                         $newParentNode->setAttribute('class''embed-responsive embed-responsive-16by9');
  1032.                         $nodeElem->setAttribute('class''embed-responsive-item');
  1033.                         $newParentNode->appendChild($nodeElem);
  1034.                     }
  1035.                 });
  1036.             }
  1037.         }
  1038.         $result str_replace('<body>'''$html->html());
  1039.         $result str_replace('</body>'''$result);
  1040.         return $result;
  1041.     }
  1042.     public function getMedia($entityID$type) {
  1043.         $mediaList $this->entityManager->getRepository(Media::class)->getMedia($entityID$type);
  1044.         return $mediaList $mediaList[0] : null;
  1045.     }
  1046.     public function logWrite($data) {
  1047.         Logger::instance('TWIG LOG WRITER')->info(print_r($datatrue));
  1048.     }
  1049.     public function getManagerPhoneNumber($offset 0) {
  1050.         switch ($this->getCurrentCity()->getID()) {
  1051.             case 5:
  1052.                 return '+375 29 380 03 33';
  1053.             case 2:
  1054.                 return '+375 29 678 53 32';
  1055.             default:
  1056.                 return '+375 29 508 44 44';
  1057.         }
  1058.     }
  1059.     public function calcDeliveryPriceOffer(
  1060.         $extension,
  1061.         $pickupDeliveryType,
  1062.         $regularPrice,
  1063.         $priceOffer,
  1064.         ?OfferExtensionVariant $extensionVariant
  1065.     )
  1066.     {
  1067.         return PriceDeliveryType::calcDeliveryPickupPrice(
  1068.             $extension,
  1069.             $pickupDeliveryType,
  1070.             $regularPrice,
  1071.             $priceOffer,
  1072.             $extensionVariant
  1073.         );
  1074.     }
  1075.     public function getSocialProviderLoginUrl($socialNetwork$goto) {
  1076.         $className 'Slivki\Util\OAuth2Client\Provider\\' ucfirst($socialNetwork) . 'Client';
  1077.         /** @var AbstractOAuth2Client $oAuthProvider */
  1078.         $oAuthProvider = new $className();
  1079.         return $oAuthProvider->getLoginUrl($goto);
  1080.     }
  1081.     public function isProductFastDelivery($director): bool
  1082.     {
  1083.         if (null === $director) {
  1084.             return false;
  1085.         }
  1086.         $offers $director->getOffers();
  1087.         /** @var ProductFastDeliveryRepository $deliveryFastRepository */
  1088.         $deliveryFastRepository $this->entityManager->getRepository(ProductFastDelivery::class);
  1089.         /** @var Offer $offerItem */
  1090.         foreach ($offers as $offerItem) {
  1091.             if ($offerItem->isActive()) {
  1092.                 $times $deliveryFastRepository->getFastDeliveryProductsByOffer($offerItem);
  1093.                 if ($times) {
  1094.                     return true;
  1095.                 }
  1096.             }
  1097.         }
  1098.         return false;
  1099.     }
  1100.     public function calcDishDiscount($regularPrice$offerPrice) {
  1101.         $offerPrice = (float)$offerPrice;
  1102.         $regularPrice = (float)$regularPrice;
  1103.         if ($regularPrice == 0) {
  1104.             return 0;
  1105.         }
  1106.         return \round(100 - ($offerPrice $regularPrice 100));
  1107.     }
  1108.     public function getRTBHouseUID(User $user null) {
  1109.         return $this->RTBHouseService->getUID($user);
  1110.     }
  1111.     public function getVimeoEmbedPreview($videoId)
  1112.     {
  1113.         $config $this->videoConfigService->config($videoId);
  1114.         if (isset($config['video']['thumbs'])
  1115.             && \is_array($config['video']['thumbs'])
  1116.             && \count($config['video']['thumbs']) > 0
  1117.         ) {
  1118.             return \reset($config['video']['thumbs']);
  1119.         }
  1120.         return '';
  1121.     }
  1122.     public function getUserBalanceCodesCount(User $user$cityID) {
  1123.         return $this->entityManager->getRepository(User::class)->getUserBalanceCodesCount($user$cityID);
  1124.     }
  1125.     public function showAppInviteModal(User $user null) : bool {
  1126.         if (!$user) {
  1127.             return true;
  1128.         }
  1129.         $sql 'select max(created_on) from offer_order'
  1130.             .' where status > 0 and device_type = ' SiteController::DEVICE_TYPE_MOBILE_APP
  1131.             ' and user_id = ' $user->getID();
  1132.         $userLastAppPurchaseDate $this->entityManager->getConnection()->executeQuery($sql)->fetchColumn();
  1133.         if (!$userLastAppPurchaseDate) {
  1134.             return true;
  1135.         }
  1136.         return (new \DateTime($userLastAppPurchaseDate) < (new \DateTime())->modify('-1 month'));
  1137.     }
  1138. }