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 WHERE SearchId = ".$this->SearchId);
154 while ($Record = $this->DB->FetchRow())
156 # add parameter to search criteria
157 if ($Record[
"FieldId"] == -101)
159 $SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"] =
160 $Record[
"SearchText"];
164 $Field = $Schema->GetField($Record[
"FieldId"]);
165 $SearchGroups[
"MAIN"][
"SearchStrings"][$Field->Name()] =
166 $Record[
"SearchText"];
170 # for each value ID search parameter
171 $this->DB->Query(
"SELECT * FROM SavedSearchIdParameters WHERE SearchId = ".$this->SearchId);
172 while ($Record = $this->DB->FetchRow())
174 # translate value based on field type
175 $FieldId = $Record[
"FieldId"];
176 if (!isset($Fields[$FieldId])) { $Fields[$FieldId] =
new MetadataField($FieldId); }
177 $Values = SavedSearch::TranslateValues($Fields[$FieldId],
178 $Record[
"SearchValueId"],
"Database to SearchGroup");
180 # add parameter to search criteria
181 foreach ($Values as $Value)
183 $SearchGroups[$FieldId][
"SearchStrings"][$Fields[$FieldId]->Name()][] = $Value;
187 # set appropriate logic in search parameters
188 foreach ($SearchGroups as $GroupIndex => $Group)
190 $SearchGroups[$GroupIndex][
"Logic"] =
195 # save search parameters locally
200 # return search parameters to caller
201 return $this->SearchGroups;
210 {
return $this->UpdateValue(
"SearchName", $NewValue); }
216 function Id() {
return $this->SearchId; }
224 {
return $this->UpdateValue(
"UserId", $NewValue); }
232 {
return $this->UpdateValue(
"Frequency", $NewValue); }
234 # set date search was last run to current date/time
237 $this->DB->Query(
"UPDATE SavedSearches SET DateLastRun = NOW() WHERE SearchId = ".$this->SearchId);
240 # get/set date search was last run
242 {
return $this->UpdateValue(
"DateLastRun", $NewValue); }
251 return self::TranslateSearchGroupsToUrlParameters($this->
SearchGroups());
262 # assume that no parameters will be found
265 # for each group in parameters
267 foreach ($SearchGroups as $GroupIndex => $Group)
269 # if group holds single parameters
270 if ($GroupIndex ==
"MAIN")
272 # for each field within group
273 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
275 # add segment to URL for this field
276 if ($FieldName ==
"XXXKeywordXXX")
282 $Field = $Schema->GetFieldByName($FieldName);
283 $FieldId = $Field->Id();
285 if (is_array($Value))
287 $UrlPortion .=
"&F".$FieldId.
"=";
289 foreach ($Value as $SingleValue)
291 $ValueString .= $SingleValue.
" ";
293 $UrlPortion .= urlencode(trim($ValueString));
297 $UrlPortion .=
"&F".$FieldId.
"=".urlencode($Value);
303 # convert value based on field type
304 $FieldId = $GroupIndex;
305 $Field = $Schema->GetField($FieldId);
306 $FieldName = $Field->Name();
307 $Values = SavedSearch::TranslateValues($Field, $Group[
"SearchStrings"][$FieldName],
"SearchGroup to Database");
311 foreach ($Values as $Value)
316 $UrlPortion .=
"&G".$FieldId.
"=".$Value;
320 $UrlPortion .=
"-".$Value;
326 # trim off any leading "&"
327 if (strlen($UrlPortion)) { $UrlPortion = substr($UrlPortion, 1); }
329 # return URL portion to caller
340 return self::TranslateSearchGroupsToUrlParameters($this->
SearchGroups());
351 # assume that no parameters will be found
352 $UrlPortion = array();
354 # for each group in parameters
356 foreach ($SearchGroups as $GroupIndex => $Group)
358 # if group holds single parameters
359 if ($GroupIndex ==
"MAIN")
361 # for each field within group
362 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
364 # add segment to URL for this field
365 if ($FieldName ==
"XXXKeywordXXX")
371 $Field = $Schema->GetFieldByName($FieldName);
372 $FieldId = $Field->Id();
374 if (is_array($Value))
377 foreach ($Value as $SingleValue)
379 $ValueString .= $SingleValue.
" ";
382 $UrlPortion[
"F".$FieldId] = urlencode(trim($ValueString));
386 $UrlPortion[
"F".$FieldId] = urlencode($Value);
392 # convert value based on field type
393 $FieldId = $GroupIndex;
394 $Field = $Schema->GetField($FieldId);
395 $FieldName = $Field->Name();
396 $Values = SavedSearch::TranslateValues($Field, $Group[
"SearchStrings"][$FieldName],
"SearchGroup to Database");
400 foreach ($Values as $Value)
405 $UrlPortion[
"G".$FieldId] = $Value;
409 $UrlPortion[
"G".$FieldId] .=
"-".$Value;
415 # return URL portion to caller
419 # set search groups from URL (GET method) parameters
420 # (returns search group array)
423 # if URL segment was passed in instead of GET var array
424 if (is_string($GetVars))
426 parse_str($GetVars, $GetVars);
429 # start with empty list of parameters
430 $SearchGroups = array();
433 $AllFields = $Schema->GetFields(NULL, NULL, TRUE);
435 foreach ($AllFields as $Field)
437 $FieldId = $Field->Id();
438 $FieldName = $Field->Name();
440 # if URL included literal value for this field
441 if (isset($GetVars[
"F".$FieldId]))
443 # retrieve value and add to search parameters
444 $SearchGroups[
"MAIN"][
"SearchStrings"][$FieldName] = $GetVars[
"F".$FieldId];
447 # if URL included group value for this field
448 if (isset($GetVars[
"G".$FieldId]))
450 # retrieve and parse out values
451 $Values = explode(
"-", $GetVars[
"G".$FieldId]);
454 $Values = SavedSearch::TranslateValues($Field, $Values,
"Database to SearchGroup");
456 # add values to searchgroups
457 $SearchGroups[$FieldId][
"SearchStrings"][$FieldName] = $Values;
461 # if keyword pseudo-field was included in URL
462 if (isset($GetVars[
"FK"]))
464 # retrieve value and add to search parameters
465 $SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"] = $GetVars[
"FK"];
469 foreach ($SearchGroups as $GroupIndex => $Group)
471 $SearchGroups[$GroupIndex][
"Logic"] = ($GroupIndex ==
"MAIN")
475 # return parameters to caller
476 return $SearchGroups;
490 $IncludeHtml = TRUE, $StartWithBreak = TRUE, $TruncateLongWordsTo = 0)
492 return self::TranslateSearchGroupsToTextDescription($this->
SearchGroups(),
493 $IncludeHtml, $StartWithBreak, $TruncateLongWordsTo);
508 $IncludeHtml = TRUE, $StartWithBreak = TRUE, $TruncateLongWordsTo = 0)
512 # start with empty description
515 # set characters used to indicate literal strings
516 $LiteralStart = $IncludeHtml ?
"<i>" :
"\"";
517 $LiteralEnd = $IncludeHtml ?
"</i>" :
"\"";
518 $LiteralBreak = $IncludeHtml ?
"<br>\n" :
"\n";
520 # if this is a simple keyword search
521 if (isset($SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"])
522 && (count($SearchGroups) == 1)
523 && (count($SearchGroups[
"MAIN"][
"SearchStrings"]) == 1))
525 # just use the search string
526 $Descrip .= $LiteralStart;
527 $Descrip .= defaulthtmlentities($SearchGroups[
"MAIN"][
"SearchStrings"][
"XXXKeywordXXX"]);
528 $Descrip .= $LiteralEnd . $LiteralBreak;
532 # start description on a new line (if requested)
535 $Descrip .= $LiteralBreak;
538 # define list of phrases used to represent logical operators
539 $WordsForOperators = array(
541 ">" =>
"is greater than",
542 "<" =>
"is less than",
543 ">=" =>
"is at least",
544 "<=" =>
"is no more than",
548 # for each search group
549 foreach ($SearchGroups as $GroupIndex => $Group)
552 if ($GroupIndex ==
"MAIN")
554 # for each field in group
555 foreach ($Group[
"SearchStrings"] as $FieldName => $Value)
557 # convert keyword pseudo-field name if necessary
558 if ($FieldName ==
"XXXKeywordXXX") { $FieldName =
"Keyword"; }
560 # determine wording based on operator
561 preg_match(
"/^[=><!]+/", $Value, $Matches);
562 if (count($Matches) && isset($WordsForOperators[$Matches[0]]))
564 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
565 $Wording = $WordsForOperators[$Matches[0]];
569 $Wording =
"contains";
572 $Field = $Schema->GetFieldByName($FieldName);
574 # get display name for field
577 $FieldName = $Field->GetDisplayName();
580 # add criteria for field
581 $Descrip .= $FieldName.
" ".$Wording.
" "
582 .$LiteralStart.htmlspecialchars($Value)
583 .$LiteralEnd.$LiteralBreak;
588 # for each field in group
589 foreach ($Group[
"SearchStrings"] as $FieldName => $Values)
592 $Values = SavedSearch::TranslateValues($FieldName, $Values,
"SearchGroup to Display");
596 foreach ($Values as $Value)
598 # determine wording based on operator
599 preg_match(
"/^[=><!]+/", $Value, $Matches);
600 $Operator = $Matches[0];
601 $Wording = $WordsForOperators[$Operator];
604 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
606 # add text to description
609 $Descrip .= $FieldName.
" ".$Wording.
" ".$LiteralStart.htmlspecialchars($Value).$LiteralEnd.$LiteralBreak;
614 $Descrip .= ($IncludeHtml ?
" " :
" ")
615 .
"or ".$Wording.
" ".$LiteralStart
616 .htmlspecialchars($Value).$LiteralEnd
625 # if caller requested that long words be truncated
626 if ($TruncateLongWordsTo > 4)
628 # break description into words
629 $Words = explode(
" ", $Descrip);
633 foreach ($Words as $Word)
635 # if word is longer than specified length
636 if (strlen(strip_tags($Word)) > $TruncateLongWordsTo)
638 # truncate word and add ellipsis
639 $Word = NeatlyTruncateString($Word, $TruncateLongWordsTo - 3);
642 # add word to new description
643 $NewDescrip .=
" ".$Word;
646 # set description to new description
647 $Descrip = $NewDescrip;
650 # return description to caller
660 return self::TranslateSearchGroupsToSearchFieldNames($this->
SearchGroups());
670 # start out assuming no fields are being searched
671 $FieldNames = array();
673 # for each search group defined
674 foreach ($SearchGroups as $GroupIndex => $Group)
676 # for each field in group
677 foreach ($Group[
"SearchStrings"] as $FieldName => $Values)
679 # add field name to list of fields being searched
680 $FieldNames[] = $FieldName;
684 # return list of fields being searched to caller
695 # define list with descriptions
697 self::SEARCHFREQ_NEVER =>
"Never",
698 self::SEARCHFREQ_HOURLY =>
"Hourly",
699 self::SEARCHFREQ_DAILY =>
"Daily",
700 self::SEARCHFREQ_WEEKLY =>
"Weekly",
701 self::SEARCHFREQ_BIWEEKLY =>
"Bi-Weekly",
702 self::SEARCHFREQ_MONTHLY =>
"Monthly",
703 self::SEARCHFREQ_QUARTERLY =>
"Quarterly",
704 self::SEARCHFREQ_YEARLY =>
"Yearly",
707 # for each argument passed in
708 $Args = func_get_args();
709 foreach ($Args as $Arg)
711 # remove value from list
712 $FreqDescr = array_diff_key($FreqDescr, array($Arg =>
""));
715 # return list to caller
724 $this->DB->Query(
"DELETE FROM SavedSearches"
725 .
" WHERE SearchId = ".intval($this->SearchId));
726 $this->DB->Query(
"DELETE FROM SavedSearchTextParameters"
727 .
" WHERE SearchId = ".intval($this->SearchId));
728 $this->DB->Query(
"DELETE FROM SavedSearchIdParameters"
729 .
" WHERE SearchId = ".intval($this->SearchId));
733 # ---- PRIVATE INTERFACE -------------------------------------------------
737 private $SearchGroups;
739 # utility function to convert between value representations
740 # (method accepts a value or array and always return an array)
741 # (this is needed because values are represented differently:
743 # in DB / in URL / in forms 0/1 123 456
744 # used in SearchGroups 0/1 jdoe cname
745 # displayed to user On/Off jdoe cname
746 # where "123" and "456" are option or controlled name IDs)
747 private static function TranslateValues($FieldOrFieldName, $Values, $TranslationType)
749 # start out assuming we won't find any values to translate
750 $ReturnValues = array();
752 # convert field name to field object if necessary
753 if (is_object($FieldOrFieldName))
755 $Field = $FieldOrFieldName;
761 $Field = $Schema->GetFieldByName($FieldOrFieldName);
764 # if incoming value is not an array
765 if (!is_array($Values))
767 # convert incoming value to an array
768 $Values = array($Values);
771 # for each incoming value
772 foreach ($Values as $Value)
774 switch ($TranslationType)
776 case "SearchGroup to Display":
777 # if field is Flag field
780 # translate value to true/false label and add leading operator
781 $ReturnValues[] = ($Value ==
"=1") ?
"=".$Field->FlagOnLabel() :
"=".$Field->FlagOffLabel();
783 elseif ($Field->Name() ==
"Cumulative Rating")
785 # translate numeric value to stars
786 $StarStrings = array(
793 preg_match(
"/[0-9]+$/", $Value, $Matches);
794 $Number = $Matches[0];
795 preg_match(
"/^[=><!]+/", $Value, $Matches);
796 $Operator = $Matches[0];
797 $ReturnValues[] = $Operator.$StarStrings[$Number];
802 $ReturnValues[] = $Value;
806 case "SearchGroup to Database":
807 # strip off leading operator on value
808 $Value = preg_replace(
"/^[=><!]+/",
"", $Value);
810 # look up index for value
813 # (for flag or number fields the value index is already what is used in SearchGroups)
816 $ReturnValues[] = $Value;
821 # (for user fields the value index is the user ID)
822 $User =
new SPTUser(strval($Value));
825 $ReturnValues[] = $User->Id();
830 if (!isset($PossibleFieldValues))
832 $PossibleFieldValues = $Field->GetPossibleValues();
834 $NewValue = array_search($Value, $PossibleFieldValues);
835 if ($NewValue !== FALSE)
837 $ReturnValues[] = $NewValue;
842 $NewValue = $Field->GetIdForValue($Value);
843 if ($NewValue !== NULL)
845 $ReturnValues[] = $NewValue;
850 case "Database to SearchGroup":
851 # look up value for index
854 # (for flag fields the value index (0 or 1) is already what is used in Database)
857 $ReturnValues[] =
"=".$Value;
862 # (for flag fields the value index (0 or 1) is already what is used in Database)
865 $ReturnValues[] =
">=".$Value;
870 $User =
new SPTUser(intval($Value));
873 $ReturnValues[] =
"=".$User->Get(
"UserName");
878 if (!isset($PossibleFieldValues))
880 $PossibleFieldValues = $Field->GetPossibleValues();
883 if (isset($PossibleFieldValues[$Value]))
885 $ReturnValues[] =
"=".$PossibleFieldValues[$Value];
890 $NewValue = $Field->GetValueForId($Value);
891 if ($NewValue !== NULL)
893 $ReturnValues[] =
"=".$NewValue;
900 # return array of translated values to caller
901 return $ReturnValues;
906 # utility function for updating values in database
907 private function UpdateValue($FieldName, $NewValue)
909 return $this->DB->UpdateValue(
"SavedSearches", $FieldName, $NewValue,
910 "SearchId = ".$this->SearchId, $this->Record);
913 # legacy methods for backward compatibility