3 # FILE: SavedSearch.php 5 # Part of the Collection Workflow Integration System (CWIS) 6 # Copyright 2011-2015 Edward Almasy and Internet Scout Research Group 7 # http://scout.wisc.edu/cwis/ 13 # ---- PUBLIC INTERFACE -------------------------------------------------- 15 # search frequency mnemonics 34 public function __construct($SearchId, $SearchName = NULL, $UserId = NULL,
35 $Frequency = NULL, $SearchParameters = NULL)
37 # get our own database handle 40 # if search ID was provided 41 if ($SearchId !== NULL)
44 $this->SearchId = intval($SearchId);
46 # initialize our local copies of data 48 "SELECT * FROM SavedSearches " 49 .
"WHERE SearchId = ".$this->SearchId);
51 if ($this->DB->NumRowsSelected() == 0)
53 throw new Exception (
"Specified SearchId does not exist");
56 $this->Record = $this->DB->FetchRow();
58 # get our Search Parameters 59 if (isset($this->Record[
"SearchData"]))
62 $this->Record[
"SearchData"]);
64 # remove now redundant 'Data' from our record 65 unset ($this->Record[
"SearchData"]);
72 # update search details where provided 90 # add new saved search to database 91 $this->DB->Query(
"INSERT INTO SavedSearches" 92 .
" (SearchName, UserId, Frequency) VALUES (" 93 .
"'".addslashes($SearchName).
"', " 95 .intval($Frequency).
")");
97 # retrieve and save ID of new search locally 98 $this->SearchId = $this->DB->LastInsertId();
100 # populate our local data for this record 102 "SELECT * FROM SavedSearches " 103 .
"WHERE SearchId = ".$this->SearchId);
104 $this->Record = $this->DB->FetchRow();
106 $this->InitializeSearchParameters($SearchParameters);
107 $this->InitializeLastMatches();
121 if (!is_null($NewSearchGroups))
124 $Params->SetFromLegacyArray($NewSearchGroups);
128 # return search parameters to caller 139 if (!is_null($NewParams))
144 $Data = $NewParams->Data();
147 "UPDATE SavedSearches SET SearchData = '". addslashes($Data).
"'" 148 .
" WHERE SearchId = ".$this->SearchId);
154 throw new Exception(
"NewParams must be a SearchParameterSet");
158 return clone $this->SearchParameters;
168 return $this->UpdateValue(
"SearchName", $NewValue);
177 return $this->SearchId;
187 return $this->UpdateValue(
"UserId", $NewValue);
197 return $this->UpdateValue(
"Frequency", $NewValue);
207 "UPDATE SavedSearches SET DateLastRun = NOW() " 208 .
"WHERE SearchId = ".$this->SearchId);
218 return $this->UpdateValue(
"DateLastRun", $NewValue);
227 $NewValue = implode(
",", $ArrayofMatchingIds);
228 $this->UpdateValue(
"LastMatchingIds", $NewValue);
237 return explode(
",", $this->DB->Query(
238 "SELECT LastMatchingIds FROM SavedSearches " 239 .
"WHERE SearchId = ".$this->SearchId,
"LastMatchingIds"));
248 return self::TranslateSearchGroupsToUrlParameters($this->
SearchGroups());
290 # assume that no parameters will be found 293 # for each group in parameters 295 foreach ($SearchGroups as $GroupIndex => $Group)
297 # if group holds single parameters 298 if ($GroupIndex ==
"MAIN")
300 # for each field within group 301 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
303 # add segment to URL for this field 304 if ($FieldName ==
"XXXKeywordXXX")
310 $Field = $Schema->GetFieldByName($FieldName);
311 $FieldId = $Field->Id();
313 if (is_array($Value))
315 $UrlPortion .=
"&F".$FieldId.
"=";
317 foreach ($Value as $SingleValue)
319 $ValueString .= $SingleValue.
" ";
321 $UrlPortion .= urlencode(trim($ValueString));
325 $UrlPortion .=
"&F".$FieldId.
"=".urlencode($Value);
331 # convert value based on field type 332 $FieldId = ($GroupIndex[0] ==
"X")
333 ? substr($GroupIndex, 1)
335 $Field = $Schema->GetField($FieldId);
336 $FieldName = $Field->Name();
337 $Values = self::TranslateValues($Field,
338 $Group[
"SearchStrings"][$FieldName],
339 "SearchGroup to Database");
343 foreach ($Values as $Value)
348 $UrlPortion .=
"&G".$FieldId.
"=".$Value;
352 $UrlPortion .=
"-".$Value;
358 # trim off any leading "&" 359 if (strlen($UrlPortion)) { $UrlPortion = substr($UrlPortion, 1); }
361 # return URL portion to caller 372 return self::TranslateSearchGroupsToUrlParameters($this->
SearchGroups());
383 # assume that no parameters will be found 384 $UrlPortion = array();
386 # for each group in parameters 388 foreach ($SearchGroups as $GroupIndex => $Group)
390 # if group holds single parameters 391 if ($GroupIndex ==
"MAIN")
393 # for each field within group 394 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
396 # add segment to URL for this field 397 if ($FieldName ==
"XXXKeywordXXX")
403 $Field = $Schema->GetFieldByName($FieldName);
404 $FieldId = $Field->Id();
406 if (is_array($Value))
409 foreach ($Value as $SingleValue)
411 $ValueString .= $SingleValue.
" ";
414 $UrlPortion[
"F".$FieldId] = urlencode(trim($ValueString));
418 $UrlPortion[
"F".$FieldId] = urlencode($Value);
424 # convert value based on field type 425 $FieldId = ($GroupIndex[0] ==
"X")
426 ? substr($GroupIndex, 1)
428 $Field = $Schema->GetField($FieldId);
429 $FieldName = $Field->Name();
430 $Values = self::TranslateValues($Field,
431 $Group[
"SearchStrings"][$FieldName],
432 "SearchGroup to Database");
438 foreach ($Values as $Value)
443 $UrlPortion[$LeadChar.$FieldId] = $Value;
447 $UrlPortion[$LeadChar.$FieldId] .=
"-".$Value;
453 # return URL portion to caller 464 # if URL segment was passed in instead of GET var array 465 if (is_string($GetVars))
467 $GetVars = ParseQueryString($GetVars);
470 # start with empty list of parameters 471 $SearchGroups = array();
474 $AllFields = $Schema->GetFields(NULL, NULL, TRUE);
476 foreach ($AllFields as $Field)
478 $FieldId = $Field->Id();
479 $FieldName = $Field->Name();
481 # if URL included literal value for this field 482 if (isset($GetVars[
"F".$FieldId]))
484 # retrieve value and add to search parameters 485 $SearchGroups[
"MAIN"][
"SearchStrings"][$FieldName] =
486 $GetVars[
"F".$FieldId];
489 # if URL included group value for this field 490 if (isset($GetVars[
"G".$FieldId]))
492 # retrieve and parse out values 493 $Values = explode(
"-", $GetVars[
"G".$FieldId]);
496 $Values = self::TranslateValues($Field, $Values,
497 "Database to SearchGroup");
499 # add values to searchgroups 500 $SearchGroups[$FieldId][
"SearchStrings"][$FieldName] = $Values;
503 # if URL included group value for this field 504 if (isset($GetVars[
"H".$FieldId]))
506 # retrieve and parse out values 507 $Values = explode(
"-", $GetVars[
"H".$FieldId]);
510 $Values = self::TranslateValues($Field, $Values,
511 "Database to SearchGroup");
513 # add values to searchgroups 514 $SearchGroups[
"X".$FieldId][
"SearchStrings"][$FieldName] = $Values;
518 # if keyword pseudo-field was included in URL 519 if (isset($GetVars[
"FK"]))
521 # retrieve value and add to search parameters 522 $SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"] = $GetVars[
"FK"];
526 foreach ($SearchGroups as $GroupIndex => $Group)
528 $SearchGroups[$GroupIndex][
"Logic"] = ($GroupIndex ==
"MAIN")
530 : (($GroupIndex[0] ==
"X")
534 # return parameters to caller 535 return $SearchGroups;
549 $IncludeHtml = TRUE, $StartWithBreak = TRUE, $TruncateLongWordsTo = 0)
552 $IncludeHtml, $StartWithBreak, $TruncateLongWordsTo);
567 $IncludeHtml = TRUE, $StartWithBreak = TRUE, $TruncateLongWordsTo = 0)
571 # start with empty description 574 # set characters used to indicate literal strings 575 $LiteralStart = $IncludeHtml ?
"<i>" :
"\"";
576 $LiteralEnd = $IncludeHtml ?
"</i>" :
"\"";
577 $LiteralBreak = $IncludeHtml ?
"<br>\n" :
"\n";
579 # if this is a simple keyword search 580 if (isset($SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"])
581 && (count($SearchGroups) == 1)
582 && (count($SearchGroups[
"MAIN"][
"SearchStrings"]) == 1))
584 # just use the search string 585 $Descrip .= $LiteralStart;
586 $Descrip .= defaulthtmlentities(
587 $SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"]);
588 $Descrip .= $LiteralEnd . $LiteralBreak;
592 # start description on a new line (if requested) 595 $Descrip .= $LiteralBreak;
598 # define list of phrases used to represent logical operators 599 $WordsForOperators = array(
601 ">" =>
"is greater than",
602 "<" =>
"is less than",
603 ">=" =>
"is at least",
604 "<=" =>
"is no more than",
608 # for each search group 609 foreach ($SearchGroups as $GroupIndex => $Group)
612 if ($GroupIndex ==
"MAIN")
614 # for each field in group 615 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
617 # determine wording based on operator 618 preg_match(
"/^[=><!]+/", $Value, $Matches);
619 if (count($Matches) && isset($WordsForOperators[$Matches[0]]))
621 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
622 $Wording = $WordsForOperators[$Matches[0]];
626 $Wording =
"contains";
629 # if field is psuedo-field 630 if ($FieldName ==
"XXXKeywordXXX")
632 # add criteria for psuedo-field 633 $Descrip .=
"Keyword ".$Wording.
" " 634 .$LiteralStart.htmlspecialchars($Value)
635 .$LiteralEnd.$LiteralBreak;
640 $Field = $Schema->GetFieldByName($FieldName);
643 # add criteria for field 644 $Descrip .= $Field->GetDisplayName().
" ".$Wording.
" " 645 .$LiteralStart.htmlspecialchars($Value)
646 .$LiteralEnd.$LiteralBreak;
653 # for each field in group 656 foreach ($Group[
"SearchStrings"] as $FieldName => $Values)
659 $Values = self::TranslateValues(
660 $FieldName, $Values,
"SearchGroup to Display");
664 foreach ($Values as $Value)
666 # determine wording based on operator 667 preg_match(
"/^[=><!]+/", $Value, $Matches);
668 $Operator = $Matches[0];
669 $Wording = $WordsForOperators[$Operator];
672 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
674 # add text to description 677 $Descrip .= $FieldName.
" ".$Wording.
" " 678 .$LiteralStart.htmlspecialchars($Value)
679 .$LiteralEnd.$LiteralBreak;
684 $Descrip .= ($IncludeHtml ?
685 " " :
" ")
686 .$LogicTerm.$Wording.
" ".$LiteralStart
687 .htmlspecialchars($Value).$LiteralEnd
696 # if caller requested that long words be truncated 697 if ($TruncateLongWordsTo > 4)
699 # break description into words 700 $Words = explode(
" ", $Descrip);
704 foreach ($Words as $Word)
706 # if word is longer than specified length 707 if (strlen(strip_tags($Word)) > $TruncateLongWordsTo)
709 # truncate word and add ellipsis 713 # add word to new description 714 $NewDescrip .=
" ".$Word;
717 # set description to new description 718 $Descrip = $NewDescrip;
721 # return description to caller 741 # start out assuming no fields are being searched 742 $FieldNames = array();
744 # for each search group defined 745 foreach ($SearchGroups as $GroupIndex => $Group)
747 # for each field in group 748 foreach ($Group[
"SearchStrings"] as $FieldName => $Values)
750 # add field name to list of fields being searched 751 $FieldNames[] = $FieldName;
755 # return list of fields being searched to caller 766 # define list with descriptions 768 self::SEARCHFREQ_NEVER =>
"Never",
769 self::SEARCHFREQ_HOURLY =>
"Hourly",
770 self::SEARCHFREQ_DAILY =>
"Daily",
771 self::SEARCHFREQ_WEEKLY =>
"Weekly",
772 self::SEARCHFREQ_BIWEEKLY =>
"Biweekly",
773 self::SEARCHFREQ_MONTHLY =>
"Monthly",
774 self::SEARCHFREQ_QUARTERLY =>
"Quarterly",
775 self::SEARCHFREQ_YEARLY =>
"Yearly",
778 # for each argument passed in 779 $Args = func_get_args();
780 foreach ($Args as $Arg)
782 # remove value from list 783 $FreqDescr = array_diff_key($FreqDescr, array($Arg =>
""));
786 # return list to caller 795 $this->DB->Query(
"DELETE FROM SavedSearches" 796 .
" WHERE SearchId = ".intval($this->SearchId));
800 # ---- PRIVATE INTERFACE ------------------------------------------------- 804 private $SearchGroups;
806 private $SearchParameters;
822 private static function TranslateValues($FieldOrFieldName, $Values, $TranslationType)
824 # start out assuming we won't find any values to translate 825 $ReturnValues = array();
827 # convert field name to field object if necessary 828 if (is_object($FieldOrFieldName))
830 $Field = $FieldOrFieldName;
836 $Field = $Schema->GetFieldByName($FieldOrFieldName);
839 # if incoming value is not an array 840 if (!is_array($Values))
842 # convert incoming value to an array 843 $Values = array($Values);
846 # for each incoming value 847 foreach ($Values as $Value)
849 switch ($TranslationType)
851 case "SearchGroup to Display":
852 # if field is Flag field 855 # translate value to true/false label and add leading operator 856 $ReturnValues[] = ($Value ==
"=1") ?
857 "=".$Field->FlagOnLabel() :
"=".$Field->FlagOffLabel();
859 elseif ($Field->Name() ==
"Cumulative Rating")
861 # translate numeric value to stars 862 $StarStrings = array(
869 preg_match(
"/[0-9]+$/", $Value, $Matches);
870 $Number = $Matches[0];
871 preg_match(
"/^[=><!]+/", $Value, $Matches);
872 $Operator = $Matches[0];
873 $ReturnValues[] = $Operator.$StarStrings[$Number];
878 $ReturnValues[] = $Value;
882 case "SearchGroup to Database":
883 # strip off leading operator on value 884 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
886 # look up index for value 890 # (for flag or number fields the value index is already 891 # what is used in SearchGroups) 894 $ReturnValues[] = $Value;
899 # (for user fields the value index is the user ID) 900 $User =
new CWUser(strval($Value));
903 $ReturnValues[] = $User->Id();
908 if (!isset($PossibleFieldValues))
910 $PossibleFieldValues = $Field->GetPossibleValues();
912 $NewValue = array_search($Value, $PossibleFieldValues);
913 if ($NewValue !== FALSE)
915 $ReturnValues[] = $NewValue;
920 $NewValue = $Field->GetIdForValue($Value);
921 if ($NewValue !== NULL)
923 $ReturnValues[] = $NewValue;
928 case "Database to SearchGroup":
929 # look up value for index 932 # (for flag fields the value index (0 or 1) is already 933 # what is used in Database) 936 $ReturnValues[] =
"=".$Value;
941 # (for flag fields the value index (0 or 1) is already 942 # what is used in Database) 946 $ReturnValues[] =
">=".$Value;
951 $User =
new CWUser(intval($Value));
954 $ReturnValues[] =
"=".$User->Get(
"UserName");
959 if (!isset($PossibleFieldValues))
961 $PossibleFieldValues = $Field->GetPossibleValues();
964 if (isset($PossibleFieldValues[$Value]))
966 $ReturnValues[] =
"=".$PossibleFieldValues[$Value];
971 $NewValue = $Field->GetValueForId($Value);
972 if ($NewValue !== NULL)
974 $ReturnValues[] =
"=".$NewValue;
981 # return array of translated values to caller 982 return $ReturnValues;
987 # utility function for updating values in database 994 private function UpdateValue($FieldName, $NewValue)
996 return $this->DB->UpdateValue(
"SavedSearches", $FieldName, $NewValue,
997 "SearchId = ".$this->SearchId, $this->Record);
1016 private function InitializeSearchParameters($SearchParameters)
1018 # if given legacy data, modernize it 1019 if (!is_null($SearchParameters) && is_array($SearchParameters))
1022 $Params->SetFromLegacyArray($SearchParameters);
1023 $SearchParameters = $Params;
1028 # signal event to allow modification of search parameters 1029 $SignalResult = $GLOBALS[
"AF"]->SignalEvent(
1030 "EVENT_FIELDED_SEARCH", array(
1031 "SearchParameters" => $SearchParameters,
1033 "SavedSearch" => $this));
1035 # save possibly modified values 1042 private function InitializeLastMatches()
1048 # build the list of results the user can see 1051 foreach ($SearchResults as $SchemaId => $SchemaResults)
1054 $SchemaItemIds = $RFactory->FilterNonViewableResources(
1055 array_keys($SchemaResults), $EndUser);
1056 $NewItemIds = array_merge(
1057 $NewItemIds, $SchemaItemIds);
1060 # if visible search results were found, save them 1061 if (count($NewItemIds))
static TranslateUrlParametersToSearchGroups($GetVars)
Translate URL parameters to legacy search group array.
Set of parameters used to perform a search.
UpdateDateLastRun()
Update date this search was last run.
SQL database abstraction object with smart query caching.
GetSearchId()
Get search id.
static TranslateSearchGroupsToUrlParameterArray($SearchGroups)
Translate a search group array to an URL parameter array.
const SEARCHFREQ_QUARTERLY
UserId($NewValue=DB_NOVALUE)
Get/set user ID.
Frequency($NewValue=DB_NOVALUE)
Get/set search frequency.
const SEARCHFREQ_BIWEEKLY
static TranslateSearchGroupsToTextDescription($SearchGroups, $IncludeHtml=TRUE, $StartWithBreak=TRUE, $TruncateLongWordsTo=0)
Translate search group array into multi-line string describing search criteria.
DateLastRun($NewValue=DB_NOVALUE)
Get/set the date this search was last run.
static TranslateSearchGroupsToUrlParameters($SearchGroups)
Translate search group array into URL parameters (e.g.
GetSearchGroupsAsTextDescription($IncludeHtml=TRUE, $StartWithBreak=TRUE, $TruncateLongWordsTo=0)
Get multi-line string describing search criteria.
static NeatlyTruncateString($String, $MaxLength, $BreakAnywhere=FALSE)
Attempt to truncate a string as neatly as possible with respect to word breaks, punctuation, and HTML tags.
SearchGroups($NewSearchGroups=NULL)
Get/set search parameters from legacy array.
GetSearchFieldNames()
Get list of fields to be searched.
__construct($SearchId, $SearchName=NULL, $UserId=NULL, $Frequency=NULL, $SearchParameters=NULL)
Object constructor.
Delete()
Delete saved search.
SearchName($NewValue=DB_NOVALUE)
Get/set name of search.
GetSearchGroupsAsUrlParameterArray()
Get search groups as an URL parameter array.
GetSearchGroupsAsUrlParameters()
Get search groups as URL parameters (e.g.
LastMatches()
Return array of most recently matched ResourceIds for a search.
Factory for Resource objects.
CWIS-specific user class.
SearchParameters($NewParams=NULL)
Get/set search parameters.
static GetSearchFrequencyList()
Get array of possible search frequency descriptions.
static TranslateSearchGroupsToSearchFieldNames($SearchGroups)
Extract list of fields to be searched from search group array.
SaveLastMatches($ArrayofMatchingIds)
Save array of last matches.