3 # FILE: SavedSearch.php
6 # - the "$SearchGroups" values used herein contain a multi-dimentional
7 # array in the form of:
8 # $Criteria["MAIN"]["SearchStrings"][<field names>] = <value>
9 # for fields with a single value, and:
10 # $Criteria[<field ID>]["SearchStrings"][<field name>][] = <value>
11 # for fields with multiple values
13 # Part of the Collection Workflow Integration System (CWIS)
14 # Copyright 2011 Edward Almasy and Internet Scout Project
15 # http://scout.wisc.edu/
20 # ---- PUBLIC INTERFACE --------------------------------------------------
22 # search frequency mnemonics
33 function SavedSearch($SearchId, $SearchName = NULL, $UserId = NULL,
34 $Frequency = NULL, $SearchGroups = NULL)
36 # get our own database handle
39 # if search ID was provided
40 if ($SearchId !== NULL)
43 $this->SearchId = intval($SearchId);
45 # initialize our local copies of data
46 $this->DB->Query(
"SELECT * FROM SavedSearches"
47 .
" WHERE SearchId = '".$this->SearchId.
"'");
48 $this->Record = $this->DB->FetchRow();
50 # update search details where provided
51 if ($SearchName) { $this->
SearchName($SearchName); }
52 if ($UserId) { $this->
UserId($UserId); }
53 if ($Frequency) { $this->
Frequency($Frequency); }
57 # add new saved search to database
58 $this->DB->Query(
"INSERT INTO SavedSearches"
59 .
" (SearchName, UserId, Frequency) VALUES ("
60 .
"'".addslashes($SearchName).
"', "
62 .intval($Frequency).
")");
64 # retrieve and save ID of new search locally
65 $this->SearchId = $this->DB->LastInsertId(
"SavedSearches");
67 # save frequency and user ID locally
68 $this->Record[
"SearchName"] = $SearchName;
69 $this->Record[
"UserId"] = $UserId;
70 $this->Record[
"Frequency"] = $Frequency;
73 # save search parameters if provided
74 if ($SearchGroups) { $this->
SearchGroups($SearchGroups); }
77 # get/set search parameters
82 # if new search parameters were supplied
85 # remove existing entries for this search from the database
86 $this->DB->Query(
"DELETE FROM SavedSearchTextParameters WHERE SearchId = ".$this->SearchId);
87 $this->DB->Query(
"DELETE FROM SavedSearchIdParameters WHERE SearchId = ".$this->SearchId);
89 # for each search group
90 foreach ($NewSearchGroups as $GroupIndex => $Group)
92 # if group holds single parameters
93 if ($GroupIndex ==
"MAIN")
95 # for each field within group
96 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
98 # convert value array to single value (if necessary)
101 $ConvertedValue =
"";
102 foreach ($Value as $SingleValue)
104 $ConvertedValue .= $SingleValue.
" ";
106 $Value = trim($ConvertedValue);
109 # add new text search parameter entry to database
110 if ($FieldName ==
"XXXKeywordXXX")
116 $Field = $Schema->GetFieldByName($FieldName);
117 $FieldId = $Field->Id();
119 $this->DB->Query(
"INSERT INTO SavedSearchTextParameters"
120 .
" (SearchId, FieldId, SearchText) VALUES"
121 .
" (".$this->SearchId.
", ".$FieldId.
", '".addslashes($Value).
"')");
126 # convert value(s) as appropriate for field type
127 $FieldId = $GroupIndex;
128 $Field = $Schema->GetField($FieldId);
129 $FieldName = $Field->Name();
130 $Values = SavedSearch::TranslateValues($Field, $Group[
"SearchStrings"][$FieldName],
"SearchGroup to Database");
132 # for each converted value
133 foreach ($Values as $Value)
135 # add new ID search parameter entry to database
136 $this->DB->Query(
"INSERT INTO SavedSearchIdParameters"
137 .
" (SearchId, FieldId, SearchValueId) VALUES"
138 .
" (".$this->SearchId.
", ".$FieldId.
", ".$Value.
")");
143 # save search parameters locally
148 # if search groups not already read in
151 # for each text search parameter
152 $SearchGroups = array();
153 $this->DB->Query(
"SELECT * FROM SavedSearchTextParameters"
154 .
" WHERE SearchId = ".$this->SearchId);
155 while ($Record = $this->DB->FetchRow())
157 # add parameter to search criteria
158 if ($Record[
"FieldId"] == -101)
160 $SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"] =
161 $Record[
"SearchText"];
165 $Field = $Schema->GetField($Record[
"FieldId"]);
166 $SearchGroups[
"MAIN"][
"SearchStrings"][$Field->Name()] =
167 $Record[
"SearchText"];
171 # for each value ID search parameter
172 $this->DB->Query(
"SELECT * FROM SavedSearchIdParameters"
173 .
" WHERE SearchId = ".$this->SearchId);
174 while ($Record = $this->DB->FetchRow())
176 # translate value based on field type
177 $FieldId = $Record[
"FieldId"];
178 if (!isset($Fields[$FieldId]))
180 $Fields[$FieldId] = $Schema->GetField($FieldId);
182 $Values = SavedSearch::TranslateValues($Fields[$FieldId],
183 $Record[
"SearchValueId"],
"Database to SearchGroup");
185 # add parameter to search criteria
186 foreach ($Values as $Value)
188 $SearchGroups[$FieldId][
"SearchStrings"]
189 [$Fields[$FieldId]->Name()][] = $Value;
193 # set appropriate logic in search parameters
194 foreach ($SearchGroups as $GroupIndex => $Group)
196 $SearchGroups[$GroupIndex][
"Logic"] =
201 # save search parameters locally
206 # return search parameters to caller
207 return $this->SearchGroups;
216 {
return $this->UpdateValue(
"SearchName", $NewValue); }
222 function Id() {
return $this->SearchId; }
230 {
return $this->UpdateValue(
"UserId", $NewValue); }
238 {
return $this->UpdateValue(
"Frequency", $NewValue); }
240 # set date search was last run to current date/time
243 $this->DB->Query(
"UPDATE SavedSearches SET DateLastRun = NOW() WHERE SearchId = ".$this->SearchId);
246 # get/set date search was last run
248 {
return $this->UpdateValue(
"DateLastRun", $NewValue); }
257 return self::TranslateSearchGroupsToUrlParameters($this->
SearchGroups());
268 # assume that no parameters will be found
271 # for each group in parameters
273 foreach ($SearchGroups as $GroupIndex => $Group)
275 # if group holds single parameters
276 if ($GroupIndex ==
"MAIN")
278 # for each field within group
279 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
281 # add segment to URL for this field
282 if ($FieldName ==
"XXXKeywordXXX")
288 $Field = $Schema->GetFieldByName($FieldName);
289 $FieldId = $Field->Id();
291 if (is_array($Value))
293 $UrlPortion .=
"&F".$FieldId.
"=";
295 foreach ($Value as $SingleValue)
297 $ValueString .= $SingleValue.
" ";
299 $UrlPortion .= urlencode(trim($ValueString));
303 $UrlPortion .=
"&F".$FieldId.
"=".urlencode($Value);
309 # convert value based on field type
310 $FieldId = $GroupIndex;
311 $Field = $Schema->GetField($FieldId);
312 $FieldName = $Field->Name();
313 $Values = SavedSearch::TranslateValues($Field, $Group[
"SearchStrings"][$FieldName],
"SearchGroup to Database");
317 foreach ($Values as $Value)
322 $UrlPortion .=
"&G".$FieldId.
"=".$Value;
326 $UrlPortion .=
"-".$Value;
332 # trim off any leading "&"
333 if (strlen($UrlPortion)) { $UrlPortion = substr($UrlPortion, 1); }
335 # return URL portion to caller
346 return self::TranslateSearchGroupsToUrlParameters($this->
SearchGroups());
357 # assume that no parameters will be found
358 $UrlPortion = array();
360 # for each group in parameters
362 foreach ($SearchGroups as $GroupIndex => $Group)
364 # if group holds single parameters
365 if ($GroupIndex ==
"MAIN")
367 # for each field within group
368 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
370 # add segment to URL for this field
371 if ($FieldName ==
"XXXKeywordXXX")
377 $Field = $Schema->GetFieldByName($FieldName);
378 $FieldId = $Field->Id();
380 if (is_array($Value))
383 foreach ($Value as $SingleValue)
385 $ValueString .= $SingleValue.
" ";
388 $UrlPortion[
"F".$FieldId] = urlencode(trim($ValueString));
392 $UrlPortion[
"F".$FieldId] = urlencode($Value);
398 # convert value based on field type
399 $FieldId = $GroupIndex;
400 $Field = $Schema->GetField($FieldId);
401 $FieldName = $Field->Name();
402 $Values = SavedSearch::TranslateValues($Field, $Group[
"SearchStrings"][$FieldName],
"SearchGroup to Database");
406 foreach ($Values as $Value)
411 $UrlPortion[
"G".$FieldId] = $Value;
415 $UrlPortion[
"G".$FieldId] .=
"-".$Value;
421 # return URL portion to caller
425 # set search groups from URL (GET method) parameters
426 # (returns search group array)
429 # if URL segment was passed in instead of GET var array
430 if (is_string($GetVars))
432 parse_str($GetVars, $GetVars);
435 # start with empty list of parameters
436 $SearchGroups = array();
439 $AllFields = $Schema->GetFields(NULL, NULL, TRUE);
441 foreach ($AllFields as $Field)
443 $FieldId = $Field->Id();
444 $FieldName = $Field->Name();
446 # if URL included literal value for this field
447 if (isset($GetVars[
"F".$FieldId]))
449 # retrieve value and add to search parameters
450 $SearchGroups[
"MAIN"][
"SearchStrings"][$FieldName] = $GetVars[
"F".$FieldId];
453 # if URL included group value for this field
454 if (isset($GetVars[
"G".$FieldId]))
456 # retrieve and parse out values
457 $Values = explode(
"-", $GetVars[
"G".$FieldId]);
460 $Values = SavedSearch::TranslateValues($Field, $Values,
"Database to SearchGroup");
462 # add values to searchgroups
463 $SearchGroups[$FieldId][
"SearchStrings"][$FieldName] = $Values;
467 # if keyword pseudo-field was included in URL
468 if (isset($GetVars[
"FK"]))
470 # retrieve value and add to search parameters
471 $SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"] = $GetVars[
"FK"];
475 foreach ($SearchGroups as $GroupIndex => $Group)
477 $SearchGroups[$GroupIndex][
"Logic"] = ($GroupIndex ==
"MAIN")
481 # return parameters to caller
482 return $SearchGroups;
496 $IncludeHtml = TRUE, $StartWithBreak = TRUE, $TruncateLongWordsTo = 0)
498 return self::TranslateSearchGroupsToTextDescription($this->
SearchGroups(),
499 $IncludeHtml, $StartWithBreak, $TruncateLongWordsTo);
514 $IncludeHtml = TRUE, $StartWithBreak = TRUE, $TruncateLongWordsTo = 0)
518 # start with empty description
521 # set characters used to indicate literal strings
522 $LiteralStart = $IncludeHtml ?
"<i>" :
"\"";
523 $LiteralEnd = $IncludeHtml ?
"</i>" :
"\"";
524 $LiteralBreak = $IncludeHtml ?
"<br>\n" :
"\n";
526 # if this is a simple keyword search
527 if (isset($SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"])
528 && (count($SearchGroups) == 1)
529 && (count($SearchGroups[
"MAIN"][
"SearchStrings"]) == 1))
531 # just use the search string
532 $Descrip .= $LiteralStart;
533 $Descrip .= defaulthtmlentities($SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"]);
534 $Descrip .= $LiteralEnd . $LiteralBreak;
538 # start description on a new line (if requested)
541 $Descrip .= $LiteralBreak;
544 # define list of phrases used to represent logical operators
545 $WordsForOperators = array(
547 ">" =>
"is greater than",
548 "<" =>
"is less than",
549 ">=" =>
"is at least",
550 "<=" =>
"is no more than",
554 # for each search group
555 foreach ($SearchGroups as $GroupIndex => $Group)
558 if ($GroupIndex ==
"MAIN")
560 # for each field in group
561 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
563 # convert keyword pseudo-field name if necessary
564 if ($FieldName ==
"XXXKeywordXXX") { $FieldName =
"Keyword"; }
566 # determine wording based on operator
567 preg_match(
"/^[=><!]+/", $Value, $Matches);
568 if (count($Matches) && isset($WordsForOperators[$Matches[0]]))
570 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
571 $Wording = $WordsForOperators[$Matches[0]];
575 $Wording =
"contains";
578 $Field = $Schema->GetFieldByName($FieldName);
580 # get display name for field
583 $FieldName = $Field->GetDisplayName();
586 # add criteria for field
587 $Descrip .= $FieldName.
" ".$Wording.
" "
588 .$LiteralStart.htmlspecialchars($Value)
589 .$LiteralEnd.$LiteralBreak;
594 # for each field in group
595 foreach ($Group[
"SearchStrings"] as $FieldName => $Values)
598 $Values = SavedSearch::TranslateValues($FieldName, $Values,
"SearchGroup to Display");
602 foreach ($Values as $Value)
604 # determine wording based on operator
605 preg_match(
"/^[=><!]+/", $Value, $Matches);
606 $Operator = $Matches[0];
607 $Wording = $WordsForOperators[$Operator];
610 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
612 # add text to description
615 $Descrip .= $FieldName.
" ".$Wording.
" ".$LiteralStart.htmlspecialchars($Value).$LiteralEnd.$LiteralBreak;
620 $Descrip .= ($IncludeHtml ?
" " :
" ")
621 .
"or ".$Wording.
" ".$LiteralStart
622 .htmlspecialchars($Value).$LiteralEnd
631 # if caller requested that long words be truncated
632 if ($TruncateLongWordsTo > 4)
634 # break description into words
635 $Words = explode(
" ", $Descrip);
639 foreach ($Words as $Word)
641 # if word is longer than specified length
642 if (strlen(strip_tags($Word)) > $TruncateLongWordsTo)
644 # truncate word and add ellipsis
645 $Word = NeatlyTruncateString($Word, $TruncateLongWordsTo - 3);
648 # add word to new description
649 $NewDescrip .=
" ".$Word;
652 # set description to new description
653 $Descrip = $NewDescrip;
656 # return description to caller
666 return self::TranslateSearchGroupsToSearchFieldNames($this->
SearchGroups());
676 # start out assuming no fields are being searched
677 $FieldNames = array();
679 # for each search group defined
680 foreach ($SearchGroups as $GroupIndex => $Group)
682 # for each field in group
683 foreach ($Group[
"SearchStrings"] as $FieldName => $Values)
685 # add field name to list of fields being searched
686 $FieldNames[] = $FieldName;
690 # return list of fields being searched to caller
701 # define list with descriptions
703 self::SEARCHFREQ_NEVER =>
"Never",
704 self::SEARCHFREQ_HOURLY =>
"Hourly",
705 self::SEARCHFREQ_DAILY =>
"Daily",
706 self::SEARCHFREQ_WEEKLY =>
"Weekly",
707 self::SEARCHFREQ_BIWEEKLY =>
"Bi-Weekly",
708 self::SEARCHFREQ_MONTHLY =>
"Monthly",
709 self::SEARCHFREQ_QUARTERLY =>
"Quarterly",
710 self::SEARCHFREQ_YEARLY =>
"Yearly",
713 # for each argument passed in
714 $Args = func_get_args();
715 foreach ($Args as $Arg)
717 # remove value from list
718 $FreqDescr = array_diff_key($FreqDescr, array($Arg =>
""));
721 # return list to caller
730 $this->DB->Query(
"DELETE FROM SavedSearches"
731 .
" WHERE SearchId = ".intval($this->SearchId));
732 $this->DB->Query(
"DELETE FROM SavedSearchTextParameters"
733 .
" WHERE SearchId = ".intval($this->SearchId));
734 $this->DB->Query(
"DELETE FROM SavedSearchIdParameters"
735 .
" WHERE SearchId = ".intval($this->SearchId));
739 # ---- PRIVATE INTERFACE -------------------------------------------------
743 private $SearchGroups;
745 # utility function to convert between value representations
746 # (method accepts a value or array and always return an array)
747 # (this is needed because values are represented differently:
749 # in DB / in URL / in forms 0/1 123 456
750 # used in SearchGroups 0/1 jdoe cname
751 # displayed to user On/Off jdoe cname
752 # where "123" and "456" are option or controlled name IDs)
753 private static function TranslateValues($FieldOrFieldName, $Values, $TranslationType)
755 # start out assuming we won't find any values to translate
756 $ReturnValues = array();
758 # convert field name to field object if necessary
759 if (is_object($FieldOrFieldName))
761 $Field = $FieldOrFieldName;
767 $Field = $Schema->GetFieldByName($FieldOrFieldName);
770 # if incoming value is not an array
771 if (!is_array($Values))
773 # convert incoming value to an array
774 $Values = array($Values);
777 # for each incoming value
778 foreach ($Values as $Value)
780 switch ($TranslationType)
782 case "SearchGroup to Display":
783 # if field is Flag field
786 # translate value to true/false label and add leading operator
787 $ReturnValues[] = ($Value ==
"=1") ?
"=".$Field->FlagOnLabel() :
"=".$Field->FlagOffLabel();
789 elseif ($Field->Name() ==
"Cumulative Rating")
791 # translate numeric value to stars
792 $StarStrings = array(
799 preg_match(
"/[0-9]+$/", $Value, $Matches);
800 $Number = $Matches[0];
801 preg_match(
"/^[=><!]+/", $Value, $Matches);
802 $Operator = $Matches[0];
803 $ReturnValues[] = $Operator.$StarStrings[$Number];
808 $ReturnValues[] = $Value;
812 case "SearchGroup to Database":
813 # strip off leading operator on value
814 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
816 # look up index for value
819 # (for flag or number fields the value index is already what is used in SearchGroups)
822 $ReturnValues[] = $Value;
827 # (for user fields the value index is the user ID)
828 $User =
new SPTUser(strval($Value));
831 $ReturnValues[] = $User->Id();
836 if (!isset($PossibleFieldValues))
838 $PossibleFieldValues = $Field->GetPossibleValues();
840 $NewValue = array_search($Value, $PossibleFieldValues);
841 if ($NewValue !== FALSE)
843 $ReturnValues[] = $NewValue;
848 $NewValue = $Field->GetIdForValue($Value);
849 if ($NewValue !== NULL)
851 $ReturnValues[] = $NewValue;
856 case "Database to SearchGroup":
857 # look up value for index
860 # (for flag fields the value index (0 or 1) is already what is used in Database)
863 $ReturnValues[] =
"=".$Value;
868 # (for flag fields the value index (0 or 1) is already what is used in Database)
871 $ReturnValues[] =
">=".$Value;
876 $User =
new SPTUser(intval($Value));
879 $ReturnValues[] =
"=".$User->Get(
"UserName");
884 if (!isset($PossibleFieldValues))
886 $PossibleFieldValues = $Field->GetPossibleValues();
889 if (isset($PossibleFieldValues[$Value]))
891 $ReturnValues[] =
"=".$PossibleFieldValues[$Value];
896 $NewValue = $Field->GetValueForId($Value);
897 if ($NewValue !== NULL)
899 $ReturnValues[] =
"=".$NewValue;
906 # return array of translated values to caller
907 return $ReturnValues;
912 # utility function for updating values in database
913 private function UpdateValue($FieldName, $NewValue)
915 return $this->DB->UpdateValue(
"SavedSearches", $FieldName, $NewValue,
916 "SearchId = ".$this->SearchId, $this->Record);
919 # legacy methods for backward compatibility