public ApiResult AvailableWidgets(int siteId, bool isStage = true)
        {
            try
            {
                var content = _metaInfoRepository.GetContent("QPDiscriminator", siteId);
                if (content == null)
                {
                    return(ApiResult.Error(Response, $"Not found QPDiscriminator content in site {siteId}"));
                }

                var cacheTag = new string[1] {
                    _qpContentCacheTagNamingProvider.Get(content.ContentName, siteId, isStage)
                };

                var widgetDefinitions = _cacheProvider.GetOrAdd($"AvailableWidgets_{siteId}_{isStage}", cacheTag, TimeSpan.FromSeconds(30), () => {
                    var result = _itemDefinitionRepository
                                 .GetAllItemDefinitions(siteId, isStage)
                                 .Where(d => !d.IsPage)
                                 .ToList();

                    var baseIconUrl = _qpUrlResolver.UrlForImage(siteId, content.ContentId, "IconUrl");

                    foreach (var w in result)
                    {
                        w.IconUrl = (baseIconUrl ?? "") + "/" + w.IconUrl;
                    }

                    return(result);
                });

                return(ApiResult.Success <IEnumerable <ItemDefinitionPersistentData> >(widgetDefinitions));
            }
            catch (Exception ex)
            {
                return(ApiResult.Error(Response, ex.Message));
            }
        }
Exemplo n.º 2
0
        public AbstractItemStorage Build()
        {
            var logBuildId = Guid.NewGuid();

            _logger.LogDebug("AbstractItemStorage build via QP started. Build id: {0}, SiteId: {0}, IsStage: {1}", logBuildId, _buildSettings.SiteId, _buildSettings.IsStage);

            var          plainList = _abstractItemRepository.GetPlainAllAbstractItems(_buildSettings.SiteId, _buildSettings.IsStage).ToList();//плоский список dto
            var          activated = new Dictionary <int, AbstractItem>();
            AbstractItem root      = null;

            _logger.LogDebug("Found abstract items: {0}. Build id: {1}", plainList.Count(), logBuildId);

            //первый проход списка - активируем, т.е. создаём AbsractItem-ы с правильным типом и набором заполненных полей, запоминаем root
            foreach (var persistentItem in plainList)
            {
                var activatedItem = _itemFactory.Create(persistentItem.Discriminator);
                if (activatedItem == null)
                {
                    continue;
                }

                activatedItem.MapPersistent(persistentItem);
                activated.Add(persistentItem.Id, activatedItem);

                if (persistentItem.Discriminator == _buildSettings.RootPageDiscriminator)
                {
                    root = activatedItem;
                }
            }

            _logger.LogDebug("Activated abstract items: {0}. Build id: {1}", activated.Count, logBuildId);

            if (root != null)
            {
                //сгруппируем AbsractItem-ы по extensionId
                //все элементы с пустым extensionId будут в одной группе
                var groupsByExtensions = activated
                                         .GroupBy(_ => _.Value.ExtensionId.GetValueOrDefault(0), _ => _.Value)
                                         .ToList();

                //словарь, каким элементам нужна загрузка связи m2m
                var needLoadM2mInExtensionDict = new Dictionary <int, AbstractItem>();

                //получим инфу об основном контенте (AbstractItem), она нам пригодится. Мы зашиваемся на netName контента - это легально
                var baseContent = _metaInfoRepository.GetContent(KnownNetNames.AbstractItem, _buildSettings.SiteId);
                if (baseContent == null)
                {
                    _logger.LogWarning($"Failed to obtain content definition for {1}. Build id: {0}", logBuildId, KnownNetNames.AbstractItem);
                }

                //получим инфу обо всех контентах-расширениях, которые используются
                var extensionContents = _metaInfoRepository
                                        .GetContentsById(groupsByExtensions
                                                         .Select(g => g.Key)
                                                         .Where(extId => extId > 0)
                                                         .ToArray(), _buildSettings.SiteId)
                                        .ToDictionary(c => c.ContentId);

                //догрузим нетипизированные поля в коллекцию Details из контентов-расширений и основного контента(AbstractItem)
                foreach (var group in groupsByExtensions)
                {
                    var extensionId = group.Key;
                    if (extensionId == 0 && !_buildSettings.LoadAbstractItemFieldsToDetailsCollection)
                    {
                        _logger.LogDebug("Skip load data for extension-less elements (LoadAbstractItemFieldsToDetailsCollection = false). Build id: {1}", logBuildId);
                        continue;
                    }

                    var ids = group.Select(_ => _.Id).ToArray();

                    _logger.LogDebug("Load data from extension table {0}. Build id: {1}", extensionId, logBuildId);

                    var extensionData = extensionId == 0 ?
                                        _abstractItemRepository.GetAbstractItemExtensionlessData(ids, baseContent, _buildSettings.IsStage) :
                                        _abstractItemRepository.GetAbstractItemExtensionData(extensionId, ids, baseContent,
                                                                                             _buildSettings.LoadAbstractItemFieldsToDetailsCollection, _buildSettings.IsStage);
                    if (extensionData == null)
                    {
                        _logger.LogDebug("Not found data for extension {0}. Build id: {1}", extensionId, logBuildId);
                        continue;
                    }

                    var extensionContent = extensionContents.ContainsKey(extensionId) ? extensionContents[extensionId] : null;

                    var m2mFieldNames = new List <string>();;
                    var fileFields    = new List <ContentAttributePersistentData>();
                    if (extensionContent?.ContentAttributes != null)
                    {
                        m2mFieldNames = extensionContent.ContentAttributes.Where(ca => ca.IsManyToManyField).Select(ca => ca.ColumnName).ToList();
                        fileFields    = extensionContent.ContentAttributes.Where(ca => ca.IsFileField).ToList();
                    }
                    if (_buildSettings.LoadAbstractItemFieldsToDetailsCollection)
                    {
                        m2mFieldNames = m2mFieldNames.Union(baseContent.ContentAttributes.Where(ca => ca.IsManyToManyField).Select(ca => ca.ColumnName)).ToList();
                        fileFields    = fileFields.Union(baseContent.ContentAttributes.Where(ca => ca.IsFileField)).ToList();
                    }

                    foreach (var item in group)
                    {
                        if (extensionData.ContainsKey(item.Id))
                        {
                            item.M2mFieldNames.AddRange(m2mFieldNames);

                            var details = extensionData[item.Id];

                            //проведём замены в некоторых значениях доп полей
                            foreach (var key in details.Keys)
                            {
                                if (m2mFieldNames.Any() && key == "CONTENT_ITEM_ID")
                                {
                                    needLoadM2mInExtensionDict[Convert.ToInt32(details[key])] = item;
                                }

                                if (details[key] is string stringValue)
                                {
                                    //1) надо заменить плейсхолдер <%=upload_url%> на реальный урл
                                    details.Set(key, stringValue.Replace(_buildSettings.UploadUrlPlaceholder, _qpUrlResolver.UploadUrl(_buildSettings.SiteId)));

                                    //2) проверим, является ли это поле ссылкой на файл, тогда нужно преобразовать его в полный урл
                                    var fileField = fileFields.FirstOrDefault(f => f.ColumnName.Equals(key, StringComparison.InvariantCultureIgnoreCase));
                                    if (fileField != null)
                                    {
                                        var baseUrl = _qpUrlResolver.UrlForImage(_buildSettings.SiteId, fileField);
                                        if (!String.IsNullOrEmpty(baseUrl))
                                        {
                                            details.Set(key, baseUrl + "/" + stringValue);
                                        }
                                    }
                                }
                            }

                            item.Details = details;
                        }
                    }
                }

                //догрузим связи m2m в контентах расширений, в которых они есть
                if (needLoadM2mInExtensionDict.Any())
                {
                    _logger.LogDebug("Load data for many-to-many fields in extensions. Build id: {1}", logBuildId);
                    var m2mData = _abstractItemRepository.GetManyToManyData(needLoadM2mInExtensionDict.Keys.ToArray(), _buildSettings.IsStage);
                    foreach (var key in m2mData.Keys)
                    {
                        if (!needLoadM2mInExtensionDict.ContainsKey(key))
                        {
                            continue;
                        }

                        needLoadM2mInExtensionDict[key].M2mRelations.Merge(m2mData[key]);
                    }
                }

                //догрузим связи m2m в основном контенте, если это нужно
                var needLoadM2mInAbstractItem = _buildSettings.LoadAbstractItemFieldsToDetailsCollection &&
                                                baseContent.ContentAttributes.Any(ca => ca.IsManyToManyField);
                if (needLoadM2mInAbstractItem)
                {
                    _logger.LogDebug("Load data for many-to-many fields in main content (QPAbstractItem). Build id: {1}", logBuildId);
                    var m2mData = _abstractItemRepository.GetManyToManyData(activated.Keys.ToArray(), _buildSettings.IsStage);
                    foreach (var key in m2mData.Keys)
                    {
                        activated[key].M2mRelations.Merge(m2mData[key]);
                    }
                }

                //второй проход списка: заполняем поля иерархии Parent-Children, на основании ParentId. Заполняем VersionOf
                foreach (var item in activated.Values)
                {
                    if (item.VersionOfId.HasValue && activated.ContainsKey(item.VersionOfId.Value))
                    {
                        var main = activated[item.VersionOfId.Value];
                        item.MapVersionOf(main);

                        if (main.ParentId.HasValue && activated.ContainsKey(main.ParentId.Value))
                        {
                            activated[main.ParentId.Value].AddChild(item);
                        }
                    }
                    else if (item.ParentId.HasValue && activated.ContainsKey(item.ParentId.Value))
                    {
                        activated[item.ParentId.Value].AddChild(item);
                    }
                }

                return(new AbstractItemStorage(root));
            }
            else
            {
                _logger.LogWarning(@"Root was not found! Possible causes: 1) not found item with discriminator = {0} 2) not found item definition for root discriminator 3) not found .net class matching with root page item definition. Build id: {1}", _buildSettings.RootPageDiscriminator, logBuildId);
                return(null);
            }
        }