/////////////////////////////////////////////////////////// // SearchResultList.cs // Implementation of Class SearchResultList // CSLA type: Read only collection // Created on: 19-April-2005 // Object design: John // Coded: 19-April-2005 /////////////////////////////////////////////////////////// using System; using System.Data; using GZTW.Data; using CSLA.Data; using CSLA; using System.Text; using System.Collections.Generic; //using log4net; namespace GZTW.AyaNova.BLL { /// /// Read only collection of objects /// [Serializable] public class SearchResultList : ReadOnlyCollectionBase { // Create a logger for use in this class //private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); //case 981 /// /// List of object id's if performing GetPickListForObjectType search /// public List ListPickListID = new List(); #region Data structure /// /// /// [Serializable] public struct SearchResultListInfo { internal Guid mRootObjectID; internal RootObjectTypes mRootObjectType; internal Guid mAncestorRootObjectID; internal RootObjectTypes mAncestorRootObjectType; internal string mExtract; internal string mDescription; internal float mRank; internal GridNameValueCellItem mSource; //Public properties /// /// Object id most closely associated with search result (may not be a directly openable object, that's what ancestor style is for) /// [Display(DisplayType.Hidden)] public Guid RootObjectID {get{return mRootObjectID;}} /// /// Object type most closely associated with search result /// [Display(DisplayType.Hidden)] public RootObjectTypes RootObjectType {get{return mRootObjectType;}} /// /// Object to open to view this search results original record /// If this object is not a child or grandchild, then this is the same as RootObjectID /// [Display(DisplayType.Hidden)] public Guid AncestorRootObjectID {get{return mAncestorRootObjectID;}} /// /// Object type to open to view this search results original record /// [Display(DisplayType.Hidden)] public RootObjectTypes AncestorRootObjectType {get{return mAncestorRootObjectType;}} /// /// A brief excerpt automatically generated that tries to show the most relevant block of text /// in the search result for the given search string /// [Display(DisplayType.Text)] public string LT_SearchResult_Label_Extract {get{return mExtract;}} /// /// Description of search result /// [Display(DisplayType.Text)] public string LT_SearchResult_Label_Description {get{return mDescription;}} /// /// Ranking of search result in order of how closely it matches the searched for string of text /// See class for an explanation of this value /// [Display(DisplayType.DecimalNumber)] public float LT_SearchResult_Label_Rank{get{return mRank;}} internal SmartDate mCreated; /// /// Date and time object found that matches was created /// [Display(DisplayType.DateTime)] public object LT_Common_Label_Created {get{return mCreated.DBValue;}} internal SmartDate mModified; /// /// Date and time object found that matches was last modified /// [Display(DisplayType.DateTime)] public object LT_Common_Label_Modified {get{return mModified.DBValue;}} //Case 194 //internal Guid mCreator; //[Display(DisplayType.ListUsers)] //public Guid LT_Common_Label_Creator {get{return mCreator;}} //internal Guid mModifier; //[Display(DisplayType.ListUsers)] //public Guid LT_Common_Label_Modifier {get{return mModifier;}} internal string mCreator; /// /// Creator of matching found object record /// [Display(DisplayType.Text)] public string LT_Common_Label_Creator { get { return mCreator; } } internal string mModifier; /// /// Modifier of matching found object record /// [Display(DisplayType.Text)] public string LT_Common_Label_Modifier { get { return mModifier; } } /// /// Descriptive text and id of openable item to view source of search result /// (used to create search result list buttons in AyaNova UI) /// [Display(DisplayType.Button, RootObjectType = RootObjectTypes.SearchResult)] public GridNameValueCellItem LT_SearchResult_Label_Source { get { return mSource; } } /// /// /// /// public bool Equals(SearchResultListInfo obj) { return this.mRootObjectID.Equals(obj.mRootObjectID); } }//end SearchResultListInfo #endregion #region Constructor /// /// /// protected SearchResultList() { ////case 1039 //log.Debug("SearchResultList()"); // AllowSort=false; // AllowFind=true; // AllowEdit=false; // AllowNew=false; // AllowRemove=false; } #endregion #region Business properties and methods /// /// Get item by index /// /// public SearchResultListInfo this[int Item] { get { return (SearchResultListInfo) List[Item]; } } /// /// Returns SearchResultListInfo item that matches passed in itemid value /// /// public SearchResultListInfo this[Guid ItemID] { get { foreach (SearchResultListInfo child in List) { if(child.mRootObjectID==ItemID) return child; } throw new ArgumentException("SearchResultList: ID not found:\r\n"+ItemID.ToString()); } } #endregion #region contains /// /// Check if item in collection /// /// public bool Contains(SearchResultListInfo obj) { foreach (SearchResultListInfo child in List) { if(child.Equals(obj)) return true; } return false; } #endregion #region Reporting and shared UI editor helpers /// /// Returns the report key which is a property of /// reports used to link all reports that can be used /// with a particular data source. /// public static string ReportKey { get { return "SearchResultList"; } } /// /// Returns the Detailed report key /// which is used to determine which reports and objects /// will be used for detailed reports /// /// If empty string then indicates there is no detailed report object or reports /// public static string DetailedReportKey { get { return ""; } } /// /// Base object that this list is reporting on /// used by shared UI editor to instantiate new objects /// when user selects new in UI elements that display this list /// /// (I.E. when user clicks on new in a read only list grid, this is the object type created) /// public static RootObjectTypes BaseObjectType { get { return RootObjectTypes.SearchResult; } } /// /// Locale key so that generic list editor /// UI code knows what title to give the list in a /// grid /// public string LocaleKey { get { return ""; } } /// /// The Type of the struct used to store list records /// Used to fetch the custom display attributes of the fields /// contained within the record to modify the grid display accordingly /// /// public static Type ListRecordType { get { return typeof(SearchResultListInfo); } } /// /// Field that contains the ID of the objects /// that are the basis of this list. /// /// Used for compiling an ID list for reporting from user /// selections in a grid. /// public static string IDField { get { return "RootObjectID"; } } /// /// Same as IDField but for detailed reports /// public static string IDFieldDetailed { get { return IDField; } } #endregion #region Static methods /// /// Internal method used by list factory /// internal static SearchResultList Get(string Filter, int MaxRecords, List IDList) { if (Filter == null) return new SearchResultList(); if (Filter == "") return new SearchResultList(); //case 2073 was stripping out apostrophes when it should because that vary's from how the //word breaker originally broked the text they are likely searching for (i.e. "Mary's place") //return (SearchResultList)DataPortal.Fetch(new Criteria(Filter.Replace("'", "").Replace("\"", ""),IDList, MaxRecords,RootObjectTypes.Nothing)); return (SearchResultList)DataPortal.Fetch(new Criteria(Filter.Replace("\"", ""), IDList, MaxRecords, RootObjectTypes.Nothing)); } /// /// Process search and return list of results /// /// collection of objects public static SearchResultList GetList(string Search) { ////case 1039 //log.Debug("GetList("+Search+")"); //if empty search return empty list if(Search==null) return new SearchResultList(); if(Search=="") return new SearchResultList(); //scrub search string for sql injection attacks //and pass to fetch through criteria return (SearchResultList) DataPortal.Fetch(new Criteria(Search.Replace("\"",""),null,-1,RootObjectTypes.Nothing)); } /// /// Return an empty list /// used for initializing grid /// /// public static SearchResultList GetEmptyList() { return new SearchResultList(); } //case 981 /// /// Special search format: /// Instead of a normal search that returns the /// object that *contains* the search text, instead, /// this search will attempt to connect the object that does contain /// the search term to the object type desired. If it can't /// then no result is returned even if the object found contains the search term. /// /// This can be used, for example, to return a list of clients who can be /// connected in any way to related objects that contain the search term. /// /// So for example if a user is looking for a client they can enter text they know appears /// somewhere on a workorder for that client, the search will find the work order /// and walk up the workorder heiarchy to retrieve the client ID /// /// This type of search is used for the "finder" feature in AyaNova /// /// This search format, does not return a regular search result list /// insead it populates the ListPickListID list. /// /// (only unique id results are returned / no duplicates and no empty id's) /// /// Search terms /// Only return results that can be linked to this object type (object types will be added as required) /// A list with RootObjectID *only* populated, all other fields are empty public static SearchResultList GetPickListForObjectType(string Search, RootObjectTypes objectType) { if (objectType != RootObjectTypes.Client) throw new System.NotSupportedException(objectType.ToString() + " is not currently supported. Object types supported are: Client"); ////case 1039 //log.Debug("GetList("+Search+")"); //if empty search return empty list if (Search == null) return new SearchResultList(); if (Search == "") return new SearchResultList(); if (objectType == RootObjectTypes.Nothing) return new SearchResultList(); //scrub search string for sql injection attacks //and pass to fetch through criteria //case 2073 return (SearchResultList)DataPortal.Fetch(new Criteria(Search.Replace("\"", ""), null, -1, objectType)); } #endregion #region DAL DATA ACCESS /// /// protected override void DataPortal_Fetch(object Criteria) { ////case 1039 //log.Debug("DataPortal_Fetch"); Criteria crit = (Criteria)Criteria; if(crit.Search=="") return; //sql ize it crit.Search=crit.Search.Replace("*","%"); StringBuilder sbSQL= new StringBuilder(); string[] saTerms=AyaBizUtils.BreakSearchPhrase(crit.Search).Split(','); if(saTerms==null || saTerms.GetLength(0)==0) return; //convert search terms to query string q="SELECT ~MAXRECS~ aSearchKey.aSourceObjectID, aSearchKey.aSourceObjectType " + "FROM aSearchDictionary INNER " + "JOIN aSearchKey ON aSearchDictionary.aID " + "= aSearchKey.aWordID WHERE "; if (crit.MaxRecords > 0) q = q.Replace("~MAXRECS~", "TOP " + crit.MaxRecords.ToString()); else q = q.Replace("~MAXRECS~", ""); sbSQL.Append(q); //case 3581 string insertNationalTypeQueryCharacter = string.Empty; if (DBUtil.DB.DBType == DataBaseType.MSSQL) { insertNationalTypeQueryCharacter = "N"; } foreach (string sTerm in saTerms) { if (sTerm.IndexOf("%") != -1) sbSQL.Append(" (aSearchDictionary.aWord Like " + insertNationalTypeQueryCharacter + "'" + sTerm + "') OR");//case 3581 else sbSQL.Append(" (aSearchDictionary.aWord = " + insertNationalTypeQueryCharacter + "'" + sTerm + "') OR");//case 3581 } //trim off the final "OR" sbSQL.Length=sbSQL.Length-2; if (crit.IDList != null) { //Case 556 System.Text.StringBuilder sbIN = new System.Text.StringBuilder(); sbIN.Append(" AND (aSearchKey.aSourceObjectID in ("); foreach (Guid gItem in crit.IDList) { sbIN.Append("'"); sbIN.Append("{"); sbIN.Append(gItem.ToString().ToUpperInvariant()); sbIN.Append("}"); sbIN.Append("',"); } sbIN.Length = sbIN.Length - 1; sbIN.Append(")) "); sbSQL.Append(sbIN.ToString()); } sbSQL.Append(" GROUP BY aSearchKey.aSourceObjectID, " + "aSearchKey.aSourceObjectType HAVING (COUNT(*) " + "= " + saTerms.GetLength(0).ToString() +")"); //------------------ //Case 194 UserPickList upl = null; if (crit.objectType == RootObjectTypes.Nothing)//case 981 upl = UserPickList.GetList(false); SafeDataReader dr = null; ListPickListID.Clear(); try { dr=DBUtil.GetReaderFromSQLString(sbSQL.ToString()); while (dr.Read()) { //standard search as before? if (crit.objectType == RootObjectTypes.Nothing)//case 981 { //standard search SearchResult sr = AyaBizUtils.GetSearchResultFor((RootObjectTypes)dr.GetInt16("aSourceObjectType"), dr.GetGuid("aSourceObjectID"), saTerms); //sr.rank will equal zero if there is no good match or //if the user has no rights to retrieve that object etc if (sr.Rank > 0) { SearchResultListInfo info = new SearchResultListInfo(); info.mRootObjectID = dr.GetGuid("aSourceObjectID"); info.mRootObjectType = (RootObjectTypes)dr.GetInt16("aSourceObjectType"); info.mAncestorRootObjectID = sr.AncestorRootObjectID; info.mAncestorRootObjectType = sr.AncestorRootObjectType; info.mDescription = sr.Description; info.mExtract = sr.Extract; info.mRank = sr.Rank; info.mModified = sr.Modified; info.mModifier = upl[sr.Modifier]; info.mCreated = sr.Created; info.mCreator = upl[sr.Creator]; info.mSource = new GridNameValueCellItem( info.mAncestorRootObjectID, EnumDescConverter.GetEnumDescription(info.mRootObjectType), info.mAncestorRootObjectType); InnerList.Add(info); } } else { //Picklist search // SearchResultListInfo info = new SearchResultListInfo(); // info.mRootObjectID = dr.GetGuid("aSourceObjectID"); // info.mRootObjectType = (RootObjectTypes)dr.GetInt16("aSourceObjectType"); // info.mAncestorRootObjectID = RelatedObjectIDFetcher.GetRelatedObjectID(new TypeAndID(info.mRootObjectType, info.mRootObjectID), crit.objectType); List items = RelatedObjectIDFetcher.GetRelatedObjectID(new TypeAndID((RootObjectTypes)dr.GetInt16("aSourceObjectType"), dr.GetGuid("aSourceObjectID")), crit.objectType); foreach (Guid id in items) { if (!(id==Guid.Empty) && !ListPickListID.Contains(id)) ListPickListID.Add(id); } //if (info.AncestorRootObjectID == Guid.Empty || ListPickListID.Contains(info.AncestorRootObjectID)) // continue; //ListPickListID.Add(info.AncestorRootObjectID); //InnerList.Add(info); } } } finally { if(dr!=null) dr.Close(); } } #endregion #region criteria /// /// Criteria for identifying existing object /// [Serializable] private class Criteria { public List IDList; public string Search; public int MaxRecords; public RootObjectTypes objectType; public Criteria(string _Search, List _IDList, int _MaxRecords, RootObjectTypes _objectType) { Search = _Search; MaxRecords = _MaxRecords; IDList = _IDList; objectType = _objectType; } } #endregion }//end SearchResultList }//end namespace GZTW.AyaNova.BLL