+1-888-365-2779
Try Now
More in this section

Forums / Developing with Sitefinity / Custom Search Index Breaks Generic Content Module

Custom Search Index Breaks Generic Content Module

4 posts, 0 answered
  1. Paulo Gomes
    Paulo Gomes avatar
    4 posts
    Registered:
    30 Jul 2008
    25 Feb 2011
    Link to this post
    Hi All,

    I have created a custom search index that works beautifully. This search index is based on
    IIndexingServiceClient. The initial code is as follow:

        public class FileIndexProvider : IIndexingServiceClient
        {
            public string Description {
                get { return "This is the index of all files on the Sitefinity's libraries"; }
            }

            public IIndexerInfo[] GetContentToIndex() {
                LibraryManager manager = new LibraryManager();
                IList list = manager.GetContent();
                List<FileIndexerInfo> info = new List<FileIndexerInfo>();

                foreach (CmsContentBase content in list) {
                    FileIndexerInfo fileInfo = new FileIndexerInfo();
                    fileInfo.Culture = CultureInfo.CurrentCulture.Name;
                    fileInfo.Encoding = Encoding.Default;
                    fileInfo.ItemID = content.ID;
                    fileInfo.MimeType = "";
                    fileInfo.Tags = GetContentTags(manager, content.ID);
                    fileInfo.Path = content.ID.ToString();
                    fileInfo.Name = content.GetMetaData("Name").ToString();
                    fileInfo.Size = (long)content.GetMetaData("Size");
                    fileInfo.Extension = content.GetMetaData("Extension").ToString();
                    fileInfo.Author = content.GetMetaData("Author").ToString();
                    fileInfo.Description = content.GetMetaData("Description").ToString();
                    fileInfo.Category = content.GetMetaData("Category").ToString();
                    info.Add(fileInfo);
                }
                return info.ToArray();
            }

            private string GetContentTags(LibraryManager manager, Guid contentId)
            {
              IList tags = manager.GetTags(contentId);
              StringBuilder contentTags = new StringBuilder();
              foreach(Tag tag in tags)
              {
                if(contentTags.Length > 0)
                  contentTags.Append(", ");

                contentTags.Append(tag.TagName);
              }
              return contentTags.ToString();
            }

            public string[] GetUrlsToIndex() {
                return new string[0];
            }

            public event EventHandler<IndexEventArgs> Index;

            public void Initialize(IDictionary<stringstring> settings) {
            }

            public string Name {
                get { return "FileIndex"; }
            }
        }

    However, I had to make it autoindex new content and due to that I had to make a few amendments to it:

      public class FileIndexProvider : ContentIndexProvider
      {
        public override string Description
        {
          get { return "This is the index of all files on the Sitefinity's libraries"; }
        }

        public override IIndexerInfo[] GetContentToIndex()
        {
          LibraryManager manager = new LibraryManager();
          IList list = manager.GetContent();
          List<FileIndexerInfo> info = new List<FileIndexerInfo>();

          foreach(CmsContentBase content in list)
          {
            FileIndexerInfo fileInfo = new FileIndexerInfo();
            fileInfo.Culture = CultureInfo.CurrentCulture.Name;
            fileInfo.Encoding = Encoding.Default;
            fileInfo.ItemID = content.ID;
            fileInfo.MimeType = "";
            fileInfo.Tags = GetContentTags(manager, content.ID);
            fileInfo.Path = content.ID.ToString();
            fileInfo.Name = content.GetMetaData("Name").ToString();
            fileInfo.Size = (long)content.GetMetaData("Size");
            fileInfo.Extension = content.GetMetaData("Extension").ToString();
            fileInfo.Author = content.GetMetaData("Author").ToString();
            fileInfo.Description = content.GetMetaData("Description").ToString();
            fileInfo.Category = content.GetMetaData("Category").ToString();
            info.Add(fileInfo);
          }
          return info.ToArray();
        }

        private string GetContentTags(LibraryManager manager, Guid contentId)
        {
          IList tags = manager.GetTags(contentId);
          StringBuilder contentTags = new StringBuilder();
          foreach(Tag tag in tags)
          {
            if(contentTags.Length > 0)
              contentTags.Append(", ");
            contentTags.Append(tag.TagName);
          }
          return contentTags.ToString();
        }

        public override string[] GetUrlsToIndex()
        {
          return new string[0];
        }

        public override void Initialize(IDictionary<stringstring> settings)
        {
          Telerik.Cms.Engine.ContentManager.Executed += new EventHandler<Telerik.ExecutedEventArgs>(ContentManager_Executed);
          base.Initialize(settings);
        }

        protected void ContentManager_Executed(object sender, Telerik.ExecutedEventArgs e)
        {
          IContent content = e.Data as IContent;
          if(content == null || string.Compare(content.ProviderName, "Libraries"true) != 0)
            return;

          switch(e.CommandName)
          {
            case "UpdateContent":
            case "CreateContent":
            case "DeleteContent":
              IndexDataManager dataManager = new IndexDataManager();
              IList indexes = dataManager.GetServices();
              if (indexes.Count > 0)
              {
                var service = new IndexingService((IIndexingServiceInfo)indexes[0]);
                HttpContext.Current.Server.ScriptTimeout = 4800;
                service.Index(false);
              }
            break;
          }
        }

        public override string Name
        {
          get
          {
            return "FileIndex";
          }
        }
      }


    The problem is, with this changes the search index and auto index works as well. But every time I try to update or add ANY generic content through Sitefinity web pages I get an Object reference not set to an instance to an object:

    [NullReferenceException: Object reference not set to an instance of an object.]
       Telerik.Cms.Engine.ContentIndexProvider.GetItemUrl(IContent contentItem, String singleItemUrl, CultureInfo culture) +462
       Telerik.Cms.Engine.ContentIndexProvider.ContentProvider_Executed(Object sender, ExecutedEventArgs e) +189
       System.EventHandler`1.Invoke(Object sender, TEventArgs e) +0
       Telerik.Cms.Engine.Data.Providers.DefaultProvider.OnExecuted(ExecutedEventArgs args, Boolean fireEvents) +39
       Telerik.Cms.Engine.Data.EventExecuteController.tran_EndCommit(Object sender, EventArgs e) +25
       Nolics.ORMapper.Base.Transaction.OnEndCommit() +27
       Nolics.ORMapper.Base.Transaction.Commit(Boolean fCleanIfSuccess) +1253
       Nolics.ORMapper.Base.Transaction.Commit() +10
       Telerik.Cms.Engine.Data.Providers.DefaultProvider.SaveContent(IContent content, Boolean fireEvents) +403
       Telerik.Cms.Engine.ContentProviderBase.SaveContent(IContent content) +12
       Telerik.Cms.Engine.ContentManager.SavedStagedContent(StagedContent staged, ContentStatus status) +257
       Telerik.Cms.Engine.ContentManager.SaveContent(IContent content, ContentStatus status) +1267
       Telerik.Cms.Engine.WebControls.Admin.ContentItemEdit`1.SaveContent() +1211
       Telerik.Cms.Engine.WebControls.Admin.ContentItemEdit`1.Button_Command(Object sender, CommandEventArgs e) +62
       System.Web.UI.WebControls.LinkButton.OnCommand(CommandEventArgs e) +108
       System.Web.UI.WebControls.LinkButton.RaisePostBackEvent(String eventArgument) +135
       System.Web.UI.WebControls.LinkButton.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10
       System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13
       System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +175
       System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1565


    What could be leading to this problem?

    Cheers,
    Paulo Gomes
  2. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    25 Nov 2016
    25 Feb 2011
    Link to this post
    Hi Paulo,

    Your index inherits from ContentIndexProvider and actually you are getting null reference exception when you try to save an item. You are indexing the entire content inside ContentManager_Executed which will break the website performance. To add or delete a single item from your index you have to create a method where you call the Index event handler and passing arguments to it

    protected virtual void ContentProvider_Executed(object sender, ExecutedEventArgs e)
        {
            switch (e.CommandName)
            {
                case "UpdateContent":
                case "CreateContent":
       
                    IContent content = (IContent)e.Data;
                 
                        IIndexerInfo[] info;
                        string path = this.GetItemUrl(content, this.baseUrl, null);
                        info = new IIndexerInfo[] { this.GetIndexerInfo(path, content) ;       
                        this.OnIndex(new IndexEventArgs(info));
           
                    break;
       
                case "DeleteContent":
                  
                        this.OnIndex(new IndexEventArgs("DeleteIndex", new Guid[] { itemId }));
                     break;
              ....
                     ....


    protected virtual void OnIndex(IndexEventArgs args)
        {
            if (this.Index != null)
                this.Index(this, args);
        }
     
     public event EventHandler<IndexEventArgs> Index;

    Also in is not necessary to subscribe for Executed event inside the Initialize of your provider. This should be handled by the provider.

    Also make sure that you have registered the module for the correct provider which in your case seems to be Libraries

    All the best,
    Ivan Dimitrov
    the Telerik team
    Registration for Q1 2011 What’s New Webinar Week is now open. Mark your calendar for the week starting March 21st and book your seat for a walk through all the exciting stuff we ship with the new release!
  3. Paulo Gomes
    Paulo Gomes avatar
    4 posts
    Registered:
    30 Jul 2008
    25 Feb 2011
    Link to this post
    Hi Ivan,

    Thanks for the quick reply, however I still get the same issue even with the changes you suggested. Here's how it is looking like now:

      public class FileIndexProvider : ContentIndexProvider
      {
        public override string Description
        {
          get { return "This is the index of all files on the Sitefinity's libraries"; }
        }

        public override IIndexerInfo[] GetContentToIndex()
        {
          LibraryManager manager = new LibraryManager();
          IList list = manager.GetContent();
          List<FileIndexerInfo> info = new List<FileIndexerInfo>();

          foreach(CmsContentBase content in list)
          {
            FileIndexerInfo fileInfo = new FileIndexerInfo();
            fileInfo.Culture = CultureInfo.CurrentCulture.Name;
            fileInfo.Encoding = Encoding.Default;
            fileInfo.ItemID = content.ID;
            fileInfo.MimeType = "";
            fileInfo.Tags = GetContentTags(manager, content.ID);
            fileInfo.Path = content.ID.ToString();
            fileInfo.Name = content.GetMetaData("Name").ToString();
            fileInfo.Size = (long)content.GetMetaData("Size");
            fileInfo.Extension = content.GetMetaData("Extension").ToString();
            fileInfo.Author = content.GetMetaData("Author").ToString();
            fileInfo.Description = content.GetMetaData("Description").ToString();
            fileInfo.Category = content.GetMetaData("Category").ToString();
            info.Add(fileInfo);
          }
          return info.ToArray();
        }

        private string GetContentTags(LibraryManager manager, Guid contentId)
        {
          IList tags = manager.GetTags(contentId);
          StringBuilder contentTags = new StringBuilder();
          foreach(Tag tag in tags)
          {
            if(contentTags.Length > 0)
              contentTags.Append(", ");

            contentTags.Append(tag.TagName);
          }
          return contentTags.ToString();
        }


        public override string[] GetUrlsToIndex()
        {
          return new string[0];
        }

        public override void Initialize(IDictionary<stringstring> settings)
        {
          Telerik.Cms.Engine.ContentManager.Executed += new EventHandler<Telerik.ExecutedEventArgs>(ContentManager_Executed);
          base.Initialize(settings);
        }

        protected void ContentManager_Executed(object sender, Telerik.ExecutedEventArgs e)
        {
          IContent content = e.Data as IContent;
          if(content == null || string.Compare(content.ProviderName, "Libraries"true) != 0)
            return;

          switch(e.CommandName)
          {
            case "UpdateContent":
            case "CreateContent":
              string path = this.GetItemUrl(content, this.baseUrl, null);
              IIndexerInfo[] info = new IIndexerInfo[] { this.GetIndexerInfo(path, content) };
              this.OnIndex(new IndexEventArgs(info));
              break;

            case "DeleteContent":
              this.OnIndex(new IndexEventArgs("DeleteIndex"new Guid[] { content.ID }));
              break;
          }
        }

        public event EventHandler<IndexEventArgs> Index;

        protected virtual void OnIndex(IndexEventArgs args)
        {
          if(this.Index != null)
            this.Index(this, args);
        }

        public override string Name
        {
          get
          {
            return "FileIndex";
          }
        }
      }
    I am not sure what you mean by "make sure that you have registered the module for the correct provider", at the moment the only registration I made on web.config was the following: 

    <indexClients>
    <add name="FileIndex" type="Framework.Sitefinity.Search.FileIndexProvider, App_Code" settingsControl="Framework.Sitefinity.Search.FileIndexSettings, App_Code" viewSettingsControl="Framework.Sitefinity.Search.FileIndexViewControl, App_Code" description="Here is the description"/>
    </indexClients>

    Which sure looks wrong now that I am not implementing the IIndexingServiceClient interface. How should I register this then?

    Cheers,
    Paulo Gomes
  4. Ivan Dimitrov
    Ivan Dimitrov avatar
    16072 posts
    Registered:
    25 Nov 2016
    01 Mar 2011
    Link to this post
    Hi Paulo,

    Here is the way that the provider should be implemented

    1. You need a custom IIndexerInfo class

    public class DocumentLibraryIndexerInfo : IIndexerInfo
    {
       
      
        public DocumentLibraryIndexerInfo(string url, string[] metaFields, string cnt, IContent content)
        {
            this._url = url;
            this.metaFields = metaFields;
            this._content = cnt;
            this.contentItem = content;
        }
      
        protected virtual string GetMetaData()
        {
            StringBuilder sb = new StringBuilder();
            foreach (string key in this.metaFields)
            {
                try
                {
                    string keyValue = Convert.ToString(this.contentItem.GetMetaData(key));
      
                    if (!string.IsNullOrEmpty(keyValue))
                    {
                        sb.AppendLine();
                        sb.Append("<");
                        sb.Append(key.Replace("_", ""));
                        sb.Append(">");
                        sb.Append(keyValue);
                        sb.Append("</");
                        sb.Append(key.Replace("_", ""));
                        sb.Append(">");
                    }
      
                }
                catch (Exception ex)
                {
                    Telerik.Utilities.Log.Exception(ex);
                }
      
            }
      
            return sb.ToString();
        }
        #region IIndexerInfo Members
      
        public string Culture
        {
            get { return string.Empty; }
        }
      
        public Guid ItemID
        {
            get { return Guid.Empty; }
        }
      
        public Encoding Encoding
        {
            get { return Encoding.UTF8; }
        }
      
        public byte[] GetData()
        {
            string text = this._content;
             
            if (this.metaFields.Length > 0)
            {
                text += this.GetMetaData();
            }
            return this.Encoding.GetBytes(text);
        }
      
      
      
        public string MimeType
        {
            get { return "text/html"; }
        }
      
        public string Path
        {
            get { return this._url; }
        }
      
        public string ResolveIndexPath()
        {
            return this.Path;
        }
      
        protected string[] metaFields;
        protected string _url;
        protected string _content;
        protected IContent contentItem;
      
        #endregion
    }

    2. In your custom index provider you should use the custom IIndexerInfo class

    public class LibraryIndexProviderCustom : IIndexingServiceClient
    {
        public LibraryIndexProviderCustom()
        {
        }
      
        public virtual string DataProviderName
        {
            get
            {
                if (this.manager != null)
                    return this.manager.Provider.Name;
                return String.Empty;
            }
        }
      
        public virtual string FilterExpression
        {
            get
            {
                return this.filterExprssion;
            }
        }
      
        public virtual Guid[] ParentIDs
        {
            get
            {
                return this.parentIDs;
            }
        }
      
        public virtual string Name
        {
            get
            {
                return "Documents";
            }
        }
      
        public virtual string Description
        {
            get
            {
                return "Some Description";
            }
        }
      
        protected virtual LibraryManager Manager
        {
            get
            {
                return this.manager;
            }
        }
      
        protected virtual string[] MetaFields
        {
            get
            {
                return this.metaFields;
            }
        }
      
      
        public virtual IDictionary<string, string> AdditionalFields
        {
            get
            {
                return this.additionalFields;
            }
        }
      
      
      
        protected virtual string ContentItemKeyImpl
        {
            get
            {
                return LibraryManager.ContentItemKey;
            }
        }
      
      
        protected virtual string ContentProviderKeyImpl
        {
            get
            {
                return LibraryManager.ContentProviderKey;
            }
        }
      
      
        protected virtual DocumentLibraryIndexerInfo GetIndexerInfo(string path, IContent content)
        {
              
                return new DocumentLibraryIndexerInfo(path, this.MetaFields, content.Content.ToString(), content);
        }
      
        public virtual IIndexerInfo[] GetContentToIndex()
        {
            if (this.retrievalMethod != RetrievalMethod.Direct)
                return new IIndexerInfo[0];
      
            if (this.manager == null)
                throw new InvalidOperationException("NotInitialized");
      
            IList list = this.manager.GetContent(0, 0, String.Empty, ContentStatus.Published, this.ParentIDs, this.FilterExpression);
            List<IContent> lst = new List<IContent>();
            foreach (IContent cnt in list)
            {
                if (!lst.Contains(cnt))
                    lst.Add(cnt);
            }
            List<DocumentLibraryIndexerInfo> data = new List<DocumentLibraryIndexerInfo>(lst.Count);
            foreach (IContent content in lst)
            {
                if (content != null)
                {
                    if (this.manager.Provider.AllowLocalization)
                    {
                        foreach (string lng in content.Languages)
                        {
                            CultureInfo cult = CultureInfo.GetCultureInfo(lng);
                            string path = this.GetItemUrl(content, this.baseUrl, cult);
                            if (content.Content != null)
                            {
                                data.Add(this.GetIndexerInfo(path, content));
                            }
                        }
                    }
                    else
                    {
                        string path = this.GetItemUrl(content, this.baseUrl, null);
                        if (content.Content != null)
                        {
                            data.Add(this.GetIndexerInfo(path, content));
                        }
                    }
                }
            }
            return data.ToArray();
        }
      
        public virtual string[] GetUrlsToIndex()
        {
            if (this.retrievalMethod != RetrievalMethod.WebRequest)
                return new string[0];
      
            if (this.manager == null)
                throw new InvalidOperationException("NotInitialized");
      
            IList data = this.manager.GetContent(0, 0, String.Empty, ContentStatus.Published, this.ParentIDs, this.FilterExpression);
            List<string> urls = new List<string>(data.Count);
            for (int i = 0; i < data.Count; i++)
            {
                if (this.manager.Provider.AllowLocalization)
                {
                    IContent content = (IContent)data[i];
                    foreach (string lng in content.Languages)
                    {
                        content.Language = lng;
                        urls.Add(UrlPath.ResolveAbsoluteUrl(this.GetItemUrl(content, this.baseUrl, CultureInfo.GetCultureInfo(lng))));
                    }
                }
                else
                {
                    urls.Add(UrlPath.ResolveAbsoluteUrl(this.GetItemUrl((IContent)data[i], this.baseUrl, null)));
                }
            }
            return urls.ToArray();
        }
      
        public virtual void Initialize(IDictionary<string, string> settings)
        {
            string sVal;
            if (!settings.TryGetValue("DataProviderName", out sVal))
                sVal = LibraryManager.DefaultContentProvider;
            this.manager = this.CreateManager(sVal);
            settings.TryGetValue("FilterExpression", out this.filterExprssion);
            if (settings.TryGetValue("ParentIDs", out sVal) && !String.IsNullOrEmpty(sVal))
            {
                GuidArrayConverter converter = new GuidArrayConverter();
                this.parentIDs = (Guid[])converter.ConvertFrom(sVal);
            }
            else
            {
                this.parentIDs = new Guid[0];
            }
            if (settings.TryGetValue("BaseUrl", out this.baseUrl) && !String.IsNullOrEmpty(this.baseUrl))
            {
                this.baseUrl = UrlPath.ResolveUrl(this.baseUrl);
                int idx = this.baseUrl.LastIndexOf('.');
                if (idx != -1)
                {
                    this.baseUrl = this.baseUrl.Substring(0, idx);
                }
                this.baseUrl = VirtualPathUtility.AppendTrailingSlash(this.baseUrl);
            }
            if (settings.TryGetValue("RetrievalMethod", out sVal))
                this.retrievalMethod = (RetrievalMethod)Enum.Parse(typeof(RetrievalMethod), sVal);
            else
                this.retrievalMethod = RetrievalMethod.Direct;
      
            ((IEventExecute)this.manager.Provider).Executed += new EventHandler<ExecutedEventArgs>(ContentProvider_Executed);
      
            
            List<string> lstFields = new List<string>(this.manager.Provider.MetaKeys.Count);
      
            foreach (MetaInfo info in this.manager.Provider.MetaKeys.Values)
            {
                if (info.Searchable)
                    lstFields.Add(info.Key);
            }
      
            this.metaFields = lstFields.ToArray();
        }
      
      
        protected virtual string GetItemUrl(IContent contentItem, string singleItemUrl, CultureInfo culture)
        {
            string sep = String.Empty;
            string url = singleItemUrl;
            if (String.IsNullOrEmpty(contentItem.Url))
            {
                int idx = url.IndexOf('?');
                if (idx != -1)
                    url = url.Substring(0, idx);
                url = String.Concat(url, "?",
                    this.ContentProviderKeyImpl, "=", contentItem.ProviderName, "&",
                    this.ContentItemKeyImpl, "=", contentItem.ID);
            }
            else
            {
                string itemUrl = String.Concat(contentItem.Url, this.manager.Provider.ContentExtension);
                if (this.manager.Provider.UrlFormatQueryStringIndex != -1)
                {
                    itemUrl += "?" + ContentUrlRewriterService.FormatURL(contentItem, this.manager.Provider
                        , ContentUrlRewriterService.FormatOptions.QueryString);
                }
                else if (sep.Length > 0)
                {
                    sep = "?";
                }
                if (!itemUrl.StartsWith("~"))
                {
                    int idx = url.LastIndexOf('.');
                    if (idx != -1)
                    {
                        url = url.Substring(0, idx);
                    }
                    else
                    {
                        idx = url.IndexOf('?');
                        if (idx != -1)
                            url = url.Substring(0, idx);
                    }
      
                    itemUrl = String.Concat(VirtualPathUtility.RemoveTrailingSlash(url), itemUrl);
                }
                url = itemUrl;
            }
      
            return url;
        }
      
        
      
        protected virtual LibraryManager CreateManager(string providerName)
        {
            return new LibraryManager(providerName);
        }
      
        protected virtual void ContentProvider_Executed(object sender, ExecutedEventArgs e)
        {
            switch (e.CommandName)
            {
                case "UpdateContent":
                case "CreateContent":
      
                    IContent content = (IContent)e.Data;
                    if (content.Status != ContentStatus.Published)
                        return;
                    if (this.retrievalMethod == RetrievalMethod.Direct)
                    {
                        IIndexerInfo[] info;
                        if (this.manager.Provider.AllowLocalization)
                        {
                            CultureInfo cult = CultureInfo.GetCultureInfo(content.Language);
                            string path = this.GetItemUrl(content, this.baseUrl, cult);
                            info = new IIndexerInfo[] { this.GetIndexerInfo(path, content) };
                        }
                        else
                        {
                            string path = this.GetItemUrl(content, this.baseUrl, null);
                            info = new IIndexerInfo[] { this.GetIndexerInfo(path, content) };
                        }
                        this.OnIndex(new IndexEventArgs(info));
                    }
                    else
                    {
                        
                     
                    }
      
                    break;
      
                case "DeleteContent":
                    Guid itemId = e.ItemID;
      
                    if (itemId != Guid.Empty)
                        this.OnIndex(new IndexEventArgs("DeleteIndex", new Guid[] { itemId }));
      
                    break;
      
                default:
                    break;
            }
      
        }
      
        protected virtual void OnIndex(IndexEventArgs args)
        {
            if (this.Index != null)
                this.Index(this, args);
        }
      
        public event EventHandler<IndexEventArgs> Index;
      
        protected IDictionary<string, string> additionalFields;
        protected string baseUrl;
        protected LibraryManager manager;
        protected string filterExprssion;
        protected Guid[] parentIDs;
        protected RetrievalMethod retrievalMethod;
        protected string[] metaFields;
     
       
    }


    Regards,
    Ivan Dimitrov
    the Telerik team
    Registration for Q1 2011 What’s New Webinar Week is now open. Mark your calendar for the week starting March 21st and book your seat for a walk through all the exciting stuff we ship with the new release!
Register for webinar
4 posts, 0 answered