/// <summary>
        /// Gets a product category mapping collection
        /// </summary>
        /// <param name="productId">Product identifier</param>
        /// <param name="storeId">Store identifier (used in multi-store environment). "showHidden" parameter should also be "true"</param>
        /// <param name="showHidden"> A value indicating whether to show hidden records</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the product category mapping collection
        /// </returns>
        protected virtual async Task <IList <ProductCategory> > GetProductCategoriesByProductIdAsync(int productId, int storeId,
                                                                                                     bool showHidden = false)
        {
            if (productId == 0)
            {
                return(new List <ProductCategory>());
            }

            var customer = await _workContext.GetCurrentCustomerAsync();

            return(await _productCategoryRepository.GetAllAsync(async query =>
            {
                if (!showHidden)
                {
                    var categoriesQuery = _categoryRepository.Table.Where(c => c.Published);

                    //apply store mapping constraints
                    categoriesQuery = await _storeMappingService.ApplyStoreMapping(categoriesQuery, storeId);

                    //apply ACL constraints
                    categoriesQuery = await _aclService.ApplyAcl(categoriesQuery, customer);

                    query = query.Where(pc => categoriesQuery.Any(c => !c.Deleted && c.Id == pc.CategoryId));
                }

                return query
                .Where(pc => pc.ProductId == productId)
                .OrderBy(pc => pc.DisplayOrder)
                .ThenBy(pc => pc.Id);
            }, cache => _staticCacheManager.PrepareKeyForDefaultCache(NopCatalogDefaults.ProductCategoriesByProductCacheKey,
                                                                      productId, showHidden, customer, storeId)));
        }
Exemple #2
0
        /// <summary>
        /// Gets all news
        /// </summary>
        /// <param name="languageId">Language identifier; 0 if you want to get all records</param>
        /// <param name="storeId">Store identifier; 0 if you want to get all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <param name="title">Filter by news item title</param>
        /// <returns>News items</returns>
        public virtual async Task <IPagedList <NewsItem> > GetAllNewsAsync(int languageId = 0, int storeId  = 0,
                                                                           int pageIndex  = 0, int pageSize = int.MaxValue, bool showHidden = false, string title = null)
        {
            var news = await _newsItemRepository.GetAllPagedAsync(async query =>
            {
                if (languageId > 0)
                {
                    query = query.Where(n => languageId == n.LanguageId);
                }

                if (!string.IsNullOrEmpty(title))
                {
                    query = query.Where(n => n.Title.Contains(title));
                }

                if (!showHidden)
                {
                    var utcNow = DateTime.UtcNow;
                    query      = query.Where(n => n.Published);
                    query      = query.Where(n => !n.StartDateUtc.HasValue || n.StartDateUtc <= utcNow);
                    query      = query.Where(n => !n.EndDateUtc.HasValue || n.EndDateUtc >= utcNow);
                }

                //Store mapping
                if (!_catalogSettings.IgnoreStoreLimitations && await _storeMappingService.IsEntityMappingExistsAsync <NewsItem>(storeId))
                {
                    query = query.Where(_storeMappingService.ApplyStoreMapping <NewsItem>(storeId));
                }

                return(query.OrderByDescending(n => n.StartDateUtc ?? n.CreatedOnUtc));
            }, pageIndex, pageSize);

            return(news);
        }
        /// <summary>
        /// Gets a list of products that were never sold
        /// </summary>
        /// <param name="vendorId">Vendor identifier (filter products by a specific vendor); 0 to load all records</param>
        /// <param name="storeId">Store identifier (filter products by a specific store); 0 to load all records</param>
        /// <param name="categoryId">Category identifier; 0 to load all records</param>
        /// <param name="manufacturerId">Manufacturer identifier; 0 to load all records</param>
        /// <param name="createdFromUtc">Order created date from (UTC); null to load all records</param>
        /// <param name="createdToUtc">Order created date to (UTC); null to load all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>Products</returns>
        public virtual IPagedList <Product> ProductsNeverSold(int vendorId            = 0, int storeId        = 0,
                                                              int categoryId          = 0, int manufacturerId = 0,
                                                              DateTime?createdFromUtc = null, DateTime?createdToUtc = null,
                                                              int pageIndex           = 0, int pageSize = int.MaxValue, bool showHidden = false)
        {
            var simpleProductTypeId = (int)ProductType.SimpleProduct;

            var availableProductsQuery =
                from oi in _orderItemRepository.Table
                join o in _orderRepository.Table on oi.OrderId equals o.Id
                where (!createdFromUtc.HasValue || createdFromUtc.Value <= o.CreatedOnUtc) &&
                (!createdToUtc.HasValue || createdToUtc.Value >= o.CreatedOnUtc) &&
                !o.Deleted
                select new { ProductId = oi.ProductId };

            var query =
                from p in _productRepository.Table
                join oi in availableProductsQuery on p.Id equals oi.ProductId
                into p_oi
                from oi in p_oi.DefaultIfEmpty()
                where oi == null &&
                p.ProductTypeId == simpleProductTypeId &&
                !p.Deleted &&
                (vendorId == 0 || p.VendorId == vendorId) &&
                (showHidden || p.Published)
                select p;

            if (categoryId > 0)
            {
                query = from p in query
                        join pc in _productCategoryRepository.Table on p.Id equals pc.ProductId
                        into p_pc
                        from pc in p_pc.DefaultIfEmpty()
                        where pc.CategoryId == categoryId
                        select p;
            }

            if (manufacturerId > 0)
            {
                query = from p in query
                        join pm in _productManufacturerRepository.Table on p.Id equals pm.ProductId
                        into p_pm
                        from pm in p_pm.DefaultIfEmpty()
                        where pm.ManufacturerId == manufacturerId
                        select p;
            }

            if (!showHidden && !_catalogSettings.IgnoreStoreLimitations && _storeMappingService.IsEntityMappingExists <Product>(storeId))
            {
                query = query.Where(_storeMappingService.ApplyStoreMapping <Product>(storeId));
            }

            query = query.OrderBy(p => p.Name);

            var products = new PagedList <Product>(query, pageIndex, pageSize);

            return(products);
        }
        /// <summary>
        /// Gets all message templates
        /// </summary>
        /// <param name="storeId">Store identifier; pass 0 to load all records</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the message template list
        /// </returns>
        public virtual async Task <IList <MessageTemplate> > GetAllMessageTemplatesAsync(int storeId)
        {
            return(await _messageTemplateRepository.GetAllAsync(async query =>
            {
                //apply store mapping constraints
                query = await _storeMappingService.ApplyStoreMapping(query, storeId);

                return query.OrderBy(t => t.Name);
            }, cache => cache.PrepareKeyForDefaultCache(NopMessageDefaults.MessageTemplatesAllCacheKey, storeId)));
        }
 /// <summary>
 /// Gets all message templates
 /// </summary>
 /// <param name="storeId">Store identifier; pass 0 to load all records</param>
 /// <returns>Message template list</returns>
 public virtual IList <MessageTemplate> GetAllMessageTemplates(int storeId)
 {
     return(_messageTemplateRepository.GetAll(query =>
     {
         //store mapping
         if (!_catalogSettings.IgnoreStoreLimitations && _storeMappingService.IsEntityMappingExists <MessageTemplate>(storeId))
         {
             query = query.Where(_storeMappingService.ApplyStoreMapping <MessageTemplate>(storeId));
         }
         return query.OrderBy(t => t.Name);
     }, cache => cache.PrepareKeyForDefaultCache(NopMessageDefaults.MessageTemplatesAllCacheKey, storeId)));
 }
        /// <summary>
        /// Gets all manufacturers
        /// </summary>
        /// <param name="manufacturerName">Manufacturer name</param>
        /// <param name="storeId">Store identifier; 0 if you want to get all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <param name="overridePublished">
        /// null - process "Published" property according to "showHidden" parameter
        /// true - load only "Published" products
        /// false - load only "Unpublished" products
        /// </param>
        /// <returns>Manufacturers</returns>
        public virtual async Task <IPagedList <Manufacturer> > GetAllManufacturersAsync(string manufacturerName = "",
                                                                                        int storeId             = 0,
                                                                                        int pageIndex           = 0,
                                                                                        int pageSize            = int.MaxValue,
                                                                                        bool showHidden         = false,
                                                                                        bool?overridePublished  = null)
        {
            return(await _manufacturerRepository.GetAllPagedAsync(async query =>
            {
                if (!showHidden)
                {
                    query = query.Where(m => m.Published);
                }
                else if (overridePublished.HasValue)
                {
                    query = query.Where(m => m.Published == overridePublished.Value);
                }

                //apply store mapping constraints
                query = await _storeMappingService.ApplyStoreMapping(query, storeId);

                //apply ACL constraints
                if (!showHidden)
                {
                    var customer = await _workContext.GetCurrentCustomerAsync();
                    query = await _aclService.ApplyAcl(query, customer);
                }

                query = query.Where(m => !m.Deleted);

                if (!string.IsNullOrWhiteSpace(manufacturerName))
                {
                    query = query.Where(m => m.Name.Contains(manufacturerName));
                }

                return query.OrderBy(m => m.DisplayOrder).ThenBy(m => m.Id);
            }, pageIndex, pageSize));
        }
Exemple #7
0
        /// <summary>
        /// Gets a topic
        /// </summary>
        /// <param name="systemName">The topic system name</param>
        /// <param name="storeId">Store identifier; pass 0 to ignore filtering by store and load the first one</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>Topic</returns>
        public virtual async Task <Topic> GetTopicBySystemNameAsync(string systemName, int storeId = 0, bool showHidden = false)
        {
            if (string.IsNullOrEmpty(systemName))
            {
                return(null);
            }

            var customer = await _workContext.GetCurrentCustomerAsync();

            var customerRoleIds = await _customerService.GetCustomerRoleIdsAsync(customer);

            var cacheKey = _staticCacheManager.PrepareKeyForDefaultCache(NopTopicDefaults.TopicBySystemNameCacheKey, systemName, storeId, customerRoleIds);

            var topic = await _staticCacheManager.GetAsync(cacheKey, async() =>
            {
                var query = _topicRepository.Table;

                if (!showHidden)
                {
                    query = query.Where(t => t.Published);
                }

                //apply store mapping constraints
                query = await _storeMappingService.ApplyStoreMapping(query, storeId);

                //apply ACL constraints
                if (!showHidden)
                {
                    query = await _aclService.ApplyAcl(query, customerRoleIds);
                }

                return(query.Where(t => t.SystemName == systemName)
                       .OrderBy(t => t.Id)
                       .FirstOrDefault());
            });

            return(topic);
        }
Exemple #8
0
        /// <summary>
        /// Gets polls
        /// </summary>
        /// <param name="storeId">The store identifier; pass 0 to load all records</param>
        /// <param name="languageId">Language identifier; pass 0 to load all records</param>
        /// <param name="showHidden">Whether to show hidden records (not published, not started and expired)</param>
        /// <param name="loadShownOnHomepageOnly">Retrieve only shown on home page polls</param>
        /// <param name="systemKeyword">The poll system keyword; pass null to load all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <returns>Polls</returns>
        public virtual async Task <IPagedList <Poll> > GetPollsAsync(int storeId, int languageId  = 0, bool showHidden          = false,
                                                                     bool loadShownOnHomepageOnly = false, string systemKeyword = null,
                                                                     int pageIndex = 0, int pageSize = int.MaxValue)
        {
            var query = _pollRepository.Table;

            //whether to load not published, not started and expired polls
            if (!showHidden)
            {
                var utcNow = DateTime.UtcNow;
                query = query.Where(poll => poll.Published);
                query = query.Where(poll => !poll.StartDateUtc.HasValue || poll.StartDateUtc <= utcNow);
                query = query.Where(poll => !poll.EndDateUtc.HasValue || poll.EndDateUtc >= utcNow);

                //filter by store
                if (!_catalogSettings.IgnoreStoreLimitations && await _storeMappingService.IsEntityMappingExistsAsync <Poll>(storeId))
                {
                    query = query.Where(_storeMappingService.ApplyStoreMapping <Poll>(storeId));
                }
            }

            //load homepage polls only
            if (loadShownOnHomepageOnly)
            {
                query = query.Where(poll => poll.ShowOnHomepage);
            }

            //filter by language
            if (languageId > 0)
            {
                query = query.Where(poll => poll.LanguageId == languageId);
            }

            //filter by system keyword
            if (!string.IsNullOrEmpty(systemKeyword))
            {
                query = query.Where(poll => poll.SystemKeyword == systemKeyword);
            }

            //order records by display order
            query = query.OrderBy(poll => poll.DisplayOrder).ThenBy(poll => poll.Id);

            //return paged list of polls
            return(await query.ToPagedListAsync(pageIndex, pageSize));
        }
        /// <summary>
        /// Gets all message templates
        /// </summary>
        /// <param name="storeId">Store identifier; pass 0 to load all records</param>
        /// <param name="keywords">Keywords to search body or subject</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the message template list
        /// </returns>
        public virtual async Task <IList <MessageTemplate> > GetAllMessageTemplatesAsync(int storeId, string keywords = null)
        {
            var messageTemplates = await _messageTemplateRepository.GetAllAsync(async query =>
            {
                //apply store mapping constraints
                query = await _storeMappingService.ApplyStoreMapping(query, storeId);

                return(query.OrderBy(t => t.Name));
            }, cache => cache.PrepareKeyForDefaultCache(NopMessageDefaults.MessageTemplatesAllCacheKey, storeId));

            if (!string.IsNullOrWhiteSpace(keywords))
            {
                messageTemplates = messageTemplates.Where(x => (x.Subject?.Contains(keywords, StringComparison.InvariantCultureIgnoreCase) ?? false) ||
                                                          (x.Body?.Contains(keywords, StringComparison.InvariantCultureIgnoreCase) ?? false)).ToList();
            }

            return(messageTemplates);
        }
Exemple #10
0
        /// <summary>
        /// Gets all blog posts
        /// </summary>
        /// <param name="storeId">The store identifier; pass 0 to load all records</param>
        /// <param name="languageId">Language identifier; 0 if you want to get all records</param>
        /// <param name="dateFrom">Filter by created date; null if you want to get all records</param>
        /// <param name="dateTo">Filter by created date; null if you want to get all records</param>
        /// <param name="pageIndex">Page index</param>
        /// <param name="pageSize">Page size</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <param name="title">Filter by blog post title</param>
        /// <returns>Blog posts</returns>
        public virtual async Task <IPagedList <BlogPost> > GetAllBlogPostsAsync(int storeId       = 0, int languageId     = 0,
                                                                                DateTime?dateFrom = null, DateTime?dateTo = null,
                                                                                int pageIndex     = 0, int pageSize       = int.MaxValue, bool showHidden = false, string title = null)
        {
            return(await _blogPostRepository.GetAllPagedAsync(async query =>
            {
                if (dateFrom.HasValue)
                {
                    query = query.Where(b => dateFrom.Value <= (b.StartDateUtc ?? b.CreatedOnUtc));
                }

                if (dateTo.HasValue)
                {
                    query = query.Where(b => dateTo.Value >= (b.StartDateUtc ?? b.CreatedOnUtc));
                }

                if (languageId > 0)
                {
                    query = query.Where(b => languageId == b.LanguageId);
                }

                if (!string.IsNullOrEmpty(title))
                {
                    query = query.Where(b => b.Title.Contains(title));
                }

                if (!showHidden)
                {
                    query = query.Where(b => !b.StartDateUtc.HasValue || b.StartDateUtc <= DateTime.UtcNow);
                    query = query.Where(b => !b.EndDateUtc.HasValue || b.EndDateUtc >= DateTime.UtcNow);
                }

                //Store mapping
                if (!_catalogSettings.IgnoreStoreLimitations && await _storeMappingService.IsEntityMappingExistsAsync <BlogPost>(storeId))
                {
                    query = query.Where(_storeMappingService.ApplyStoreMapping <BlogPost>(storeId));
                }

                query = query.OrderByDescending(b => b.StartDateUtc ?? b.CreatedOnUtc);

                return query;
            }, pageIndex, pageSize));
        }
Exemple #11
0
        /// <summary>
        /// Filter hidden entries according to constraints if any
        /// </summary>
        /// <param name="query">Query to filter</param>
        /// <param name="storeId">A store identifier</param>
        /// <param name="customerRoleIds">Identifiers of customer's roles</param>
        /// <returns>Filtered query</returns>
        protected virtual IQueryable <TEntity> FilterHiddenEntries <TEntity>(IQueryable <TEntity> query, int storeId, int[] customerRoleIds)
            where TEntity : Category
        {
            //filter unpublished entries
            query = query.Where(entry => entry.Published);

            //apply store mapping constraints
            if (!_catalogSettings.IgnoreStoreLimitations && _storeMappingService.IsEntityMappingExists <TEntity>(storeId))
            {
                query = query.Where(_storeMappingService.ApplyStoreMapping <TEntity>(storeId));
            }

            //apply ACL constraints
            if (!_catalogSettings.IgnoreAcl && _aclService.IsEntityAclMappingExist <TEntity>(customerRoleIds))
            {
                query = query.Where(_aclService.ApplyAcl <TEntity>(customerRoleIds));
            }

            return(query);
        }
Exemple #12
0
        /// <summary>
        /// Filter hidden entries according to constraints if any
        /// </summary>
        /// <param name="query">Query to filter</param>
        /// <param name="storeId">A store identifier</param>
        /// <param name="customerRolesIds">Identifiers of customer's roles</param>
        /// <returns>Filtered query</returns>
        protected virtual async Task <IQueryable <TEntity> > FilterHiddenEntriesAsync <TEntity>(IQueryable <TEntity> query,
                                                                                                int storeId, int[] customerRolesIds)
            where TEntity : Manufacturer
        {
            //filter unpublished entries
            query = query.Where(entry => entry.Published);

            //apply store mapping constraints
            if (!_catalogSettings.IgnoreStoreLimitations && await _storeMappingService.IsEntityMappingExistsAsync <TEntity>(storeId))
            {
                query = query.Where(_storeMappingService.ApplyStoreMapping <TEntity>(storeId));
            }

            //apply ACL constraints
            if (!_catalogSettings.IgnoreAcl && await _aclService.IsEntityAclMappingExistAsync <TEntity>(customerRolesIds))
            {
                query = query.Where(_aclService.ApplyAcl <TEntity>(customerRolesIds));
            }

            return(query);
        }
        protected virtual async Task <IQueryable <Product> > GetAvailableProductsQueryAsync()
        {
            var productsQuery =
                from p in _productRepository.Table
                where !p.Deleted && p.Published &&
                (p.ParentGroupedProductId == 0 || p.VisibleIndividually) &&
                (!p.AvailableStartDateTimeUtc.HasValue || p.AvailableStartDateTimeUtc <= DateTime.UtcNow) &&
                (!p.AvailableEndDateTimeUtc.HasValue || p.AvailableEndDateTimeUtc >= DateTime.UtcNow)
                select p;

            var store = await _storeContext.GetCurrentStoreAsync();

            var currentCustomer = await _workContext.GetCurrentCustomerAsync();

            //apply store mapping constraints
            productsQuery = await _storeMappingService.ApplyStoreMapping(productsQuery, store.Id);

            //apply ACL constraints
            productsQuery = await _aclService.ApplyAcl(productsQuery, currentCustomer);

            return(productsQuery);
        }
Exemple #14
0
        /// <summary>
        /// Gets all countries
        /// </summary>
        /// <param name="languageId">Language identifier. It's used to sort countries by localized names (if specified); pass 0 to skip it</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>Countries</returns>
        public virtual async Task <IList <Country> > GetAllCountriesAsync(int languageId = 0, bool showHidden = false)
        {
            var store = await _storeContext.GetCurrentStoreAsync();

            var key = _staticCacheManager.PrepareKeyForDefaultCache(NopDirectoryDefaults.CountriesAllCacheKey, languageId,
                                                                    showHidden, store);

            return(await _staticCacheManager.GetAsync(key, async() =>
            {
                var countries = await _countryRepository.GetAllAsync(async query =>
                {
                    if (!showHidden)
                    {
                        query = query.Where(c => c.Published);
                    }

                    //apply store mapping constraints
                    if (!showHidden)
                    {
                        query = await _storeMappingService.ApplyStoreMapping(query, store.Id);
                    }

                    return query.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Name);
                });

                if (languageId > 0)
                {
                    //we should sort countries by localized names when they have the same display order
                    countries = await countries
                                .ToAsyncEnumerable()
                                .OrderBy(c => c.DisplayOrder)
                                .ThenByAwait(async c => await _localizationService.GetLocalizedAsync(c, x => x.Name, languageId))
                                .ToListAsync();
                }

                return countries;
            }));
        }
Exemple #15
0
        /// <summary>
        /// Get product count for every linked tag
        /// </summary>
        /// <param name="storeId">Store identifier</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>
        /// A task that represents the asynchronous operation
        /// The task result contains the dictionary of "product tag ID : product count"
        /// </returns>
        public virtual async Task <Dictionary <int, int> > GetProductCountAsync(int storeId, bool showHidden = false)
        {
            var customer = await _workContext.GetCurrentCustomerAsync();

            var customerRoleIds = await _customerService.GetCustomerRoleIdsAsync(customer);

            var key = _staticCacheManager.PrepareKeyForDefaultCache(NopCatalogDefaults.ProductTagCountCacheKey, storeId, customerRoleIds, showHidden);

            return(await _staticCacheManager.GetAsync(key, async() =>
            {
                var query = _productProductTagMappingRepository.Table;

                if (!showHidden)
                {
                    var productsQuery = _productRepository.Table.Where(p => p.Published);

                    //apply store mapping constraints
                    productsQuery = await _storeMappingService.ApplyStoreMapping(productsQuery, storeId);

                    //apply ACL constraints
                    productsQuery = await _aclService.ApplyAcl(productsQuery, customerRoleIds);

                    query = query.Where(pc => productsQuery.Any(p => !p.Deleted && pc.ProductId == p.Id));
                }

                var pTagCount = from pt in _productTagRepository.Table
                                join ptm in query on pt.Id equals ptm.ProductTagId
                                group ptm by ptm.ProductTagId into ptmGrouped
                                select new
                {
                    ProductTagId = ptmGrouped.Key,
                    ProductCount = ptmGrouped.Count()
                };

                return pTagCount.ToDictionary(item => item.ProductTagId, item => item.ProductCount);
            }));
        }
Exemple #16
0
        /// <summary>
        /// Gets all countries
        /// </summary>
        /// <param name="languageId">Language identifier. It's used to sort countries by localized names (if specified); pass 0 to skip it</param>
        /// <param name="showHidden">A value indicating whether to show hidden records</param>
        /// <returns>Countries</returns>
        public virtual IList <Country> GetAllCountries(int languageId = 0, bool showHidden = false)
        {
            var key = _staticCacheManager.PrepareKeyForDefaultCache(NopDirectoryDefaults.CountriesAllCacheKey, languageId,
                                                                    showHidden, _storeContext.CurrentStore);

            return(_staticCacheManager.Get(key, () =>
            {
                var countries = _countryRepository.GetAll(query =>
                {
                    if (!showHidden)
                    {
                        query = query.Where(c => c.Published);
                    }

                    //Store mapping
                    var storeId = _storeContext.CurrentStore.Id;

                    if (!_catalogSettings.IgnoreStoreLimitations && _storeMappingService.IsEntityMappingExists <Country>(storeId))
                    {
                        query = query.Where(_storeMappingService.ApplyStoreMapping <Country>(storeId));
                    }

                    return query.OrderBy(c => c.DisplayOrder).ThenBy(c => c.Name);
                });

                if (languageId > 0)
                {
                    //we should sort countries by localized names when they have the same display order
                    countries = countries
                                .OrderBy(c => c.DisplayOrder)
                                .ThenBy(c => _localizationService.GetLocalized(c, x => x.Name, languageId))
                                .ToList();
                }

                return countries;
            }));
        }