Example #1
0
 public function testGets()
 {
     $vm_ret = $this->opt_object->get('ca_objects.type_id', array('convertCodesToDisplayText' => true));
     $this->assertEquals('Moving Image', $vm_ret);
     $vm_ret = $this->opt_object->get('ca_objects.preferred_labels');
     $this->assertEquals('My test moving image', $vm_ret);
     $vm_ret = $this->opt_object->get('ca_objects.duration');
     $this->assertEquals('0:23:28', $vm_ret);
     $vm_ret = $this->opt_object->get('ca_objects.access');
     $this->assertEquals('0', $vm_ret);
     $vm_ret = $this->opt_object->get('ca_objects.access', array('convertCodesToDisplayText' => true));
     $this->assertEquals('not accessible to public', $vm_ret);
     $o_tep = new TimeExpressionParser();
     $vn_now = time();
     $vm_ret = $this->opt_object->get('ca_objects.lastModified');
     $this->assertTrue($o_tep->parse($vm_ret));
     $va_modified_unix = $o_tep->getUnixTimestamps();
     //$this->assertEquals($vn_now, $va_modified_unix['start'], 'lastModified timestamp cannot be more than 1 minute off', 60);
 }
 public function testGets()
 {
     $vm_ret = $this->opt_object->get('ca_objects.type_id', array('convertCodesToDisplayText' => true));
     $this->assertEquals('Moving Image', $vm_ret);
     $vm_ret = $this->opt_object->get('ca_objects.preferred_labels');
     $this->assertEquals('My test moving image', $vm_ret);
     $vm_ret = $this->opt_object->get('ca_objects.duration');
     $this->assertEquals('0:23:28', $vm_ret);
     $vm_ret = $this->opt_object->get('ca_item_comments.comment');
     $this->assertEquals('I like this very much.', $vm_ret);
     $this->assertTrue(!is_numeric($this->opt_object->get('ca_item_comments.created_on')));
     // should always be current date/time as text
     $o_tep = new TimeExpressionParser();
     $vn_now = time();
     $vm_ret = $this->opt_object->get('ca_objects.lastModified');
     $this->assertTrue($o_tep->parse($vm_ret));
     $va_modified_unix = $o_tep->getUnixTimestamps();
     //$this->assertEquals($vn_now, $va_modified_unix['start'], 'lastModified timestamp cannot be more than 1 minute off', 60);
 }
Example #3
0
 /** 
  *
  */
 public function search($ps_datetime_expression, $ps_code = null)
 {
     $o_tep = new TimeExpressionParser();
     if ($o_tep->parse($ps_datetime_expression)) {
         list($vn_period_start, $vn_period_end) = $o_tep->getUnixTimestamps();
         if ($vn_period_start && $vn_period_end) {
             $o_db = new Db();
             $qr_log = $o_db->query("\n\t\t\t\t\tSELECT sl.*, u.fname, u.lname, u.email, sfl.name form\n\t\t\t\t\tFROM ca_search_log sl\n\t\t\t\t\tLEFT JOIN ca_users AS u ON sl.user_id = u.user_id\n\t\t\t\t\tLEFT JOIN ca_search_form_labels AS sfl ON sl.form_id = sfl.form_id\n\t\t\t\t\tWHERE \n\t\t\t\t\t\t(log_datetime BETWEEN {$vn_period_start} AND {$vn_period_end})\n\t\t\t\t\tORDER BY log_datetime DESC\n\t\t\t\t");
             $o_dm = Datamodel::load();
             $va_rows = array();
             while ($qr_log->nextRow()) {
                 $va_row = $qr_log->getRow();
                 $t_table = $o_dm->getInstanceByTableNum($va_row['table_num'], true);
                 $va_row['table_name'] = $t_table->getProperty('NAME_PLURAL');
                 $va_row['user_name'] = $va_row['fname'] . ' ' . $va_row['lname'];
                 $va_rows[$va_row['search_id']] = $va_row;
             }
             return $va_rows;
         }
     }
     return null;
 }
 public function parseValue($ps_value, $pa_element_info, $pa_options = null)
 {
     $o_date_config = Configuration::load(__CA_CONF_DIR__ . '/datetime.conf');
     $show_Undated = $o_date_config->get('showUndated');
     $ps_value = trim($ps_value);
     $va_settings = $this->getSettingValuesFromElementArray($pa_element_info, array('dateRangeBoundaries', 'mustNotBeBlank'));
     if ($ps_value) {
         if (!DateRangeAttributeValue::$o_tep->parse($ps_value)) {
             // invalid date
             $this->postError(1970, _t('%1 is invalid', $pa_element_info['displayLabel']), 'DateRangeAttributeValue->parseValue()');
             return false;
         }
         $va_dates = DateRangeAttributeValue::$o_tep->getHistoricTimestamps();
         if ($va_settings['dateRangeBoundaries']) {
             if (DateRangeAttributeValue::$o_tep->parse($va_settings['dateRangeBoundaries'])) {
                 $va_boundary_dates = DateRangeAttributeValue::$o_tep->getHistoricTimestamps();
                 if ($va_dates[0] < $va_boundary_dates[0] || $va_dates[0] > $va_boundary_dates[1] || $va_dates[1] < $va_boundary_dates[0] || $va_dates[1] > $va_boundary_dates[1]) {
                     // date is out of bounds
                     $this->postError(1970, _t('%1 must be within %2', $pa_element_info['displayLabel'], $va_settings['dateRangeBoundaries']), 'DateRangeAttributeValue->parseValue()');
                     return false;
                 }
             }
         }
     } else {
         if ((bool) $va_settings['mustNotBeBlank']) {
             $this->postError(1970, _t('%1 must not be empty', $pa_element_info['displayLabel']), 'DateRangeAttributeValue->parseValue()');
             return false;
         } else {
             $o_date_config = Configuration::load(__CA_CONF_DIR__ . '/datetime.conf');
             // Default to "undated" date for blanks
             $vs_undated_date = '';
             if ((bool) $o_date_config->get('showUndated')) {
                 $o_lang_config = DateRangeAttributeValue::$o_tep->getLanguageSettings();
                 $vs_undated_date = array_shift($o_lang_config->getList('undatedDate'));
             }
             return array('value_longtext1' => $vs_undated_date, 'value_decimal1' => null, 'value_decimal2' => null);
         }
     }
     return array('value_longtext1' => $ps_value, 'value_decimal1' => $va_dates[0], 'value_decimal2' => $va_dates[1]);
 }
 /**
  * Returns array of users associated with the currently loaded row. The array
  * is key'ed on user user user_id; each value is an  array containing information about the user. Array keys are:
  *			user_id			[user_id for user]
  *			user_name	[name of user]
  *			code				[short alphanumeric code identifying the group]
  *			description	[text description of group]
  *
  * @return array List of groups associated with the currently loaded row
  */
 public function getUsers($pa_options = null)
 {
     if (!($vn_id = (int) $this->getPrimaryKey())) {
         return null;
     }
     if (!($vs_user_rel_table = $this->getProperty('USERS_RELATIONSHIP_TABLE'))) {
         return null;
     }
     $vs_pk = $this->primaryKey();
     if (!is_array($pa_options)) {
         $pa_options = array();
     }
     $vb_return_for_bundle = isset($pa_options['returnAsInitialValuesForBundle']) && $pa_options['returnAsInitialValuesForBundle'] ? true : false;
     $o_dm = Datamodel::load();
     $t_rel = $o_dm->getInstanceByTableName($vs_user_rel_table);
     $vb_supports_date_restrictions = (bool) $t_rel->hasField('effective_date');
     $o_tep = new TimeExpressionParser();
     $o_db = $this->getDb();
     $qr_res = $o_db->query("\n\t\t\t\tSELECT u.*, r.*\n\t\t\t\tFROM {$vs_user_rel_table} r\n\t\t\t\tINNER JOIN ca_users AS u ON u.user_id = r.user_id\n\t\t\t\tWHERE\n\t\t\t\t\tr.{$vs_pk} = ?\n\t\t\t", $vn_id);
     $va_users = array();
     $va_user_ids = $qr_res->getAllFieldValues("user_id");
     if ($qr_users = $this->makeSearchResult('ca_users', $va_user_ids)) {
         $va_initial_values = caProcessRelationshipLookupLabel($qr_users, new ca_users(), array('stripTags' => true));
     } else {
         $va_initial_values = array();
     }
     $qr_res->seek(0);
     while ($qr_res->nextRow()) {
         $va_row = array();
         foreach (array('user_id', 'user_name', 'fname', 'lname', 'email', 'sdatetime', 'edatetime', 'access') as $vs_f) {
             $va_row[$vs_f] = $qr_res->get($vs_f);
         }
         if ($vb_supports_date_restrictions) {
             $o_tep->init();
             $o_tep->setUnixTimestamps($qr_res->get('sdatetime'), $qr_res->get('edatetime'));
             $va_row['effective_date'] = $o_tep->getText();
         }
         if ($vb_return_for_bundle) {
             $va_row['label'] = $va_initial_values[$va_row['user_id']]['label'];
             $va_row['id'] = $va_row['user_id'];
             $va_users[(int) $qr_res->get('relation_id')] = $va_row;
         } else {
             $va_users[(int) $qr_res->get('user_id')] = $va_row;
         }
     }
     return $va_users;
 }
Example #6
0
 private function _doQueriesForSqlSearch($po_rewritten_query, $pn_subject_tablenum, $ps_dest_table, $pn_level = 0)
 {
     // query is always of type Zend_Search_Lucene_Search_Query_Boolean
     $vn_i = 0;
     $va_old_signs = $po_rewritten_query->getSigns();
     foreach ($po_rewritten_query->getSubqueries() as $o_lucene_query_element) {
         $vb_is_blank_search = false;
         if (is_null($va_old_signs)) {
             // if array is null then according to Zend Lucene all subqueries should be "are required"... so we AND them
             $vs_op = "AND";
         } else {
             if (is_null($va_old_signs[$vn_i])) {
                 // is the sign for a particular query is null then OR is (it is "neither required nor prohibited")
                 $vs_op = 'OR';
             } else {
                 $vs_op = $va_old_signs[$vn_i] === false ? 'NOT' : 'AND';
                 // true sign indicated "required" (AND) operation, false indicated "prohibited" (NOT) operation
             }
         }
         if ($vn_i == 0) {
             $vs_op = 'OR';
         }
         $va_direct_query_temp_tables = array();
         // List of temporary tables created by direct search queries; tables listed here are dropped at the end of processing for the query element
         switch (get_class($o_lucene_query_element)) {
             case 'Zend_Search_Lucene_Search_Query_Boolean':
                 $this->_createTempTable('ca_sql_search_temp_' . $pn_level);
                 $this->_doQueriesForSqlSearch($o_lucene_query_element, $pn_subject_tablenum, 'ca_sql_search_temp_' . $pn_level, $pn_level + 1);
                 // merge with current destination
                 switch ($vs_op) {
                     case 'AND':
                         // and
                         $this->_createTempTable($ps_dest_table . '_acc');
                         if ($vn_i == 0) {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\t\tSELECT DISTINCT row_id, boost\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_temp_{$pn_level}\n\t\t\t\t\t\t\t\t";
                             //print "$vs_sql<hr>";
                             $qr_res = $this->opo_db->query($vs_sql);
                         } else {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}_acc\n\t\t\t\t\t\t\t\t\tSELECT mfs.row_id, SUM(mfs.boost)\n\t\t\t\t\t\t\t\t\tFROM {$ps_dest_table} mfs\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_temp_{$pn_level} AS ftmp1 ON ftmp1.row_id = mfs.row_id\n\t\t\t\t\t\t\t\t\tGROUP BY mfs.row_id\n\t\t\t\t\t\t\t\t";
                             //print "$vs_sql<hr>";
                             $qr_res = $this->opo_db->query($vs_sql);
                             $qr_res = $this->opo_db->query("TRUNCATE TABLE {$ps_dest_table}");
                             $qr_res = $this->opo_db->query("INSERT INTO {$ps_dest_table} SELECT row_id, boost FROM {$ps_dest_table}_acc");
                         }
                         $this->_dropTempTable($ps_dest_table . '_acc');
                         break;
                     case 'NOT':
                         $vs_sql = "\n\t\t\t\t\t\t\t\tDELETE FROM {$ps_dest_table} WHERE row_id IN\n\t\t\t\t\t\t\t\t(SELECT row_id FROM ca_sql_search_temp_{$pn_level})\n\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         break;
                     default:
                     case 'OR':
                         // or
                         $vs_sql = "\n\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\tSELECT row_id, SUM(boost)\n\t\t\t\t\t\t\t\tFROM ca_sql_search_temp_{$pn_level}\n\t\t\t\t\t\t\t\tGROUP BY row_id\n\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         break;
                 }
                 $this->_dropTempTable('ca_sql_search_temp_' . $pn_level);
                 break;
             case 'Zend_Search_Lucene_Search_Query_Term':
             case 'Zend_Search_Lucene_Search_Query_MultiTerm':
             case 'Zend_Search_Lucene_Search_Query_Phrase':
             case 'Zend_Search_Lucene_Search_Query_Range':
                 $va_ft_terms = array();
                 $va_ft_like_terms = array();
                 $va_ft_stem_terms = array();
                 $vs_direct_sql_query = null;
                 $pa_direct_sql_query_params = null;
                 // set to array with values to use with direct SQL query placeholders or null to pass single standard table_num value as param (most queries just need this single value)
                 $va_tmp = array();
                 $vs_access_point = '';
                 $va_raw_terms = array();
                 switch (get_class($o_lucene_query_element)) {
                     case 'Zend_Search_Lucene_Search_Query_Range':
                         $va_lower_term = $o_lucene_query_element->getLowerTerm();
                         $va_upper_term = $o_lucene_query_element->getUpperTerm();
                         $va_element = $this->_getElementIDForAccessPoint($va_lower_term->field);
                         switch ($va_element['datatype']) {
                             case 4:
                                 // geocode
                                 $t_geocode = new GeocodeAttributeValue();
                                 $va_parsed_value = $t_geocode->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vs_lower_lat = $va_parsed_value['value_decimal1'];
                                 $vs_lower_long = $va_parsed_value['value_decimal2'];
                                 $va_parsed_value = $t_geocode->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vs_upper_lat = $va_parsed_value['value_decimal1'];
                                 $vs_upper_long = $va_parsed_value['value_decimal2'];
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vs_lower_lat) . " AND " . floatval($vs_upper_lat) . ")\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($vs_lower_long) . " AND " . floatval($vs_upper_long) . ")\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 6:
                                 // currency
                                 $t_cur = new CurrencyAttributeValue();
                                 $va_parsed_value = $t_cur->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_cur->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_longtext1 = '" . $this->opo_db->escape($vs_currency) . "')\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 10:
                                 // timecode
                                 $t_timecode = new TimecodeAttributeValue();
                                 $va_parsed_value = $t_timecode->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_timecode->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case 8:
                                 // length
                                 $t_len = new LengthAttributeValue();
                                 $va_parsed_value = $t_len->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_len->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case 9:
                                 // weight
                                 $t_weight = new WeightAttributeValue();
                                 $va_parsed_value = $t_weight->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_weight->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case 11:
                                 // integer
                                 $vn_lower_val = intval($va_lower_term->text);
                                 $vn_upper_val = intval($va_upper_term->text);
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_integer1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 12:
                                 // decimal
                                 $vn_lower_val = floatval($va_lower_term->text);
                                 $vn_upper_val = floatval($va_upper_term->text);
                                 break;
                         }
                         if (!$vs_direct_sql_query) {
                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t";
                         }
                         break;
                     case 'Zend_Search_Lucene_Search_Query_Phrase':
                         $va_words = array();
                         foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                             if (!$vs_access_point && ($vs_field = $o_term->field)) {
                                 $vs_access_point = $vs_field;
                             }
                             $va_raw_terms[] = $vs_text = (string) $o_term->text;
                             if (strlen($vs_escaped_text = $this->opo_db->escape($vs_text))) {
                                 $va_words[] = $vs_escaped_text;
                             }
                         }
                         if (!sizeof($va_words)) {
                             continue 3;
                         }
                         $va_ap_tmp = explode(".", $vs_access_point);
                         $vn_fld_table = $vn_fld_num = null;
                         if (sizeof($va_ap_tmp) == 2) {
                             $va_element = $this->_getElementIDForAccessPoint($vs_access_point);
                             if ($va_element) {
                                 $vs_fld_num = $va_element['field_num'];
                                 $vs_fld_table_num = $va_element['table_num'];
                                 $vs_fld_limit_sql = " AND (swi.field_table_num = {$vs_fld_table_num} AND swi.field_num = '{$vs_fld_num}')";
                             }
                         }
                         $va_temp_tables = array();
                         $vn_w = 0;
                         foreach ($va_words as $vs_word) {
                             $vn_w++;
                             $vs_temp_table = 'ca_sql_search_phrase_' . md5($pn_subject_tablenum . "/" . $vs_word . "/" . $vn_w);
                             $this->_createTempTable($vs_temp_table);
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT INTO {$vs_temp_table}\n\t\t\t\t\t\t\t\t\tSELECT swi.index_id + 1, 1\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_words sw \n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON sw.word_id = swi.word_id \n\t\t\t\t\t\t\t\t\t" . (sizeof($va_temp_tables) ? " INNER JOIN " . $va_temp_tables[sizeof($va_temp_tables) - 1] . " AS tt ON swi.index_id = tt.row_id" : "") . "\n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\tsw.word = ? AND swi.table_num = ? {$vs_fld_limit_sql}\n \t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql, $vs_word, (int) $pn_subject_tablenum);
                             $qr_count = $this->opo_db->query("SELECT count(*) c FROM {$vs_temp_table}");
                             if (!$qr_count->nextRow() || !(int) $qr_count->get('c')) {
                                 foreach ($va_temp_tables as $vs_temp_table) {
                                     $this->_dropTempTable($vs_temp_table);
                                 }
                                 break 2;
                             }
                             $va_temp_tables[] = $vs_temp_table;
                         }
                         $vs_results_temp_table = array_pop($va_temp_tables);
                         $this->opo_db->query("UPDATE {$vs_results_temp_table} SET row_id = row_id - 1");
                         $va_direct_query_temp_tables[$vs_results_temp_table] = true;
                         $vs_direct_sql_query = "SELECT swi.row_id, ca.boost \n\t\t\t\t\t\t\t\t\t\t\t\t\tFROM {$vs_results_temp_table} ca\n\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON swi.index_id = ca.row_id \n\t\t\t\t\t\t\t";
                         $pa_direct_sql_query_params = array();
                         // don't pass any params
                         foreach ($va_temp_tables as $vs_temp_table) {
                             $this->_dropTempTable($vs_temp_table);
                         }
                         break;
                     case 'Zend_Search_Lucene_Search_Query_MultiTerm':
                         $va_ft_like_term_list = array();
                         foreach ($o_lucene_query_element->getTerms() as $o_term) {
                             $va_raw_terms[] = $vs_term = (string) (method_exists($o_term, "getTerm") ? $o_term->getTerm()->text : $o_term->text);
                             if (!$vs_access_point && ($vs_field = method_exists($o_term, "getTerm") ? $o_term->getTerm()->field : $o_term->field)) {
                                 $vs_access_point = $vs_field;
                             }
                             $vs_stripped_term = preg_replace('!\\*+$!u', '', $vs_term);
                             $va_ft_like_terms[] = $vs_stripped_term . ($vb_had_wildcard ? '%' : '');
                         }
                         break;
                     default:
                         $vs_access_point = $o_lucene_query_element->getTerm()->field;
                         $vs_term = $o_lucene_query_element->getTerm()->text;
                         if ($vs_access_point && mb_strtoupper($vs_term) == _t('[BLANK]')) {
                             $vb_is_blank_search = true;
                             break;
                         }
                         $va_terms = $this->_tokenize($vs_term, true, $vn_i);
                         $vb_output_term = false;
                         foreach ($va_terms as $vs_term) {
                             if (in_array(trim(mb_strtolower($vs_term, 'UTF-8')), WLPlugSearchEngineSqlSearch::$s_stop_words)) {
                                 continue;
                             }
                             if (get_class($o_lucene_query_element) != 'Zend_Search_Lucene_Search_Query_MultiTerm') {
                                 $vs_stripped_term = preg_replace('!\\*+$!u', '', $vs_term);
                                 // do stemming
                                 if ($this->opb_do_stemming) {
                                     $vs_to_stem = preg_replace('!\\*$!u', '', $vs_term);
                                     if (!preg_match('!y$!u', $vs_to_stem) && !preg_match('![0-9]+!', $vs_to_stem)) {
                                         // don't stem things ending in 'y' as that can cause problems (eg "Bowery" becomes "Boweri")
                                         if (!($vs_stem = trim($this->opo_stemmer->stem($vs_to_stem)))) {
                                             $vs_stem = (string) $vs_term;
                                         }
                                         $va_ft_stem_terms[] = "'" . $this->opo_db->escape($vs_stem) . "'";
                                     } else {
                                         $va_ft_terms[] = '"' . $this->opo_db->escape($vs_term) . '"';
                                     }
                                 } else {
                                     $va_ft_terms[] = '"' . $this->opo_db->escape($vs_term) . '"';
                                 }
                                 $vb_output_term = true;
                             }
                         }
                         if ($vb_output_term) {
                             $va_raw_terms[] = $vs_term;
                         } else {
                             $vn_i--;
                         }
                         break;
                 }
                 $vs_fld_num = $vs_table_num = $t_table = null;
                 $vb_ft_bit_optimization = false;
                 if ($vs_access_point) {
                     list($vs_table, $vs_field, $vs_sub_field) = explode('.', $vs_access_point);
                     if (in_array($vs_table, array('created', 'modified'))) {
                         $o_tep = new TimeExpressionParser();
                         $vs_date = join(' ', $va_raw_terms);
                         if (!$o_tep->parse($vs_date)) {
                             break;
                         }
                         $va_range = $o_tep->getUnixTimestamps();
                         $vn_user_id = null;
                         if ($vs_field = trim($vs_field)) {
                             if (!is_int($vs_field)) {
                                 $t_user = new ca_users();
                                 if ($t_user->load(array("user_name" => $vs_field))) {
                                     $vn_user_id = (int) $t_user->getPrimaryKey();
                                 }
                             } else {
                                 $vn_user_id = (int) $vs_field;
                             }
                         }
                         $vs_user_sql = $vn_user_id ? " AND (ccl.user_id = " . (int) $vn_user_id . ")" : "";
                         switch ($vs_table) {
                             case 'created':
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccl.logged_row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.logged_table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.changetype = 'I')\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 'modified':
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccl.logged_row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.logged_table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.changetype = 'U')\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\tUNION\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccls.subject_row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_change_log_subjects AS ccls ON ccls.log_id = ccl.log_id\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccls.subject_table_num = {$pn_subject_tablenum})\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\t";
                                 break;
                         }
                     } else {
                         if ($vs_table && $vs_field) {
                             $t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true);
                             if ($t_table) {
                                 $vs_table_num = $t_table->tableNum();
                                 if (is_numeric($vs_field)) {
                                     $vs_fld_num = 'I' . $vs_field;
                                     $vn_fld_num = (int) $vs_field;
                                 } else {
                                     $vn_fld_num = $this->getFieldNum($vs_table, $vs_field);
                                     $vs_fld_num = 'I' . $vn_fld_num;
                                     if (!strlen($vn_fld_num)) {
                                         $t_element = new ca_metadata_elements();
                                         if ($t_element->load(array('element_code' => $vs_sub_field ? $vs_sub_field : $vs_field))) {
                                             $vn_fld_num = $t_element->getPrimaryKey();
                                             $vs_fld_num = 'A' . $vn_fld_num;
                                             if (!$vb_is_blank_search) {
                                                 //
                                                 // For certain types of attributes we can directly query the
                                                 // attributes in the database rather than using the full text index
                                                 // This allows us to do "intelligent" querying... for example on date ranges
                                                 // parsed from natural language input and for length dimensions using unit conversion
                                                 //
                                                 switch ($t_element->get('datatype')) {
                                                     case 2:
                                                         // dates
                                                         $vb_all_numbers = true;
                                                         foreach ($va_raw_terms as $vs_term) {
                                                             if (!is_numeric($vs_term)) {
                                                                 $vb_all_numbers = false;
                                                                 break;
                                                             }
                                                         }
                                                         $vs_raw_term = join(' ', $va_raw_terms);
                                                         $vb_exact = $vs_raw_term[0] == "#" ? true : false;
                                                         // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                                         if ($vb_exact) {
                                                             $vs_raw_term = substr($vs_raw_term, 1);
                                                             if ($this->opo_tep->parse($vs_raw_term)) {
                                                                 $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                             }
                                                         } else {
                                                             if ($this->opo_tep->parse($vs_raw_term)) {
                                                                 $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 <= " . floatval($va_dates['start']) . " AND cav.value_decimal2 >= " . floatval($va_dates['end']) . ")\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                             }
                                                         }
                                                         break;
                                                     case 4:
                                                         // geocode
                                                         $t_geocode = new GeocodeAttributeValue();
                                                         // If it looks like a lat/long pair that has been tokenized by Lucene
                                                         // into oblivion rehydrate it here.
                                                         if ($va_coords = caParseGISSearch(join(' ', $va_raw_terms))) {
                                                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN {$va_coords['min_latitude']} AND {$va_coords['max_latitude']})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN {$va_coords['min_longitude']} AND {$va_coords['max_longitude']})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         }
                                                         break;
                                                     case 6:
                                                         // currency
                                                         $t_cur = new CurrencyAttributeValue();
                                                         $va_parsed_value = $t_cur->parseValue(join(' ', $va_raw_terms), $t_element->getFieldValuesArray());
                                                         $vn_amount = $va_parsed_value['value_decimal1'];
                                                         $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_amount) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_longtext1 = '" . $this->opo_db->escape($vs_currency) . "')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 8:
                                                         // length
                                                         $t_len = new LengthAttributeValue();
                                                         $va_parsed_value = $t_len->parseValue(array_shift($va_raw_terms), $t_element->getFieldValuesArray());
                                                         $vn_len = $va_parsed_value['value_decimal1'];
                                                         // this is always in meters so we can compare this value to the one in the database
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_len) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 9:
                                                         // weight
                                                         $t_weight = new WeightAttributeValue();
                                                         $va_parsed_value = $t_weight->parseValue(array_shift($va_raw_terms), $t_element->getFieldValuesArray());
                                                         $vn_weight = $va_parsed_value['value_decimal1'];
                                                         // this is always in kilograms so we can compare this value to the one in the database
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_weight) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 10:
                                                         // timecode
                                                         $t_timecode = new TimecodeAttributeValue();
                                                         $va_parsed_value = $t_timecode->parseValue(join(' ', $va_raw_terms), $t_element->getFieldValuesArray());
                                                         $vn_timecode = $va_parsed_value['value_decimal1'];
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_timecode) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 11:
                                                         // integer
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_integer1 = " . intval(array_shift($va_raw_terms)) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                     case 12:
                                                         // decimal
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval(array_shift($va_raw_terms)) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         break;
                                                 }
                                             }
                                         } else {
                                             // neither table fields nor elements, i.e. 'virtual' fields like _count should
                                             $vn_fld_num = false;
                                             $vs_fld_num = $vs_field;
                                         }
                                     }
                                 }
                                 if ($t_table->getFieldInfo($t_table->fieldName($vn_fld_num), 'FIELD_TYPE') == FT_BIT) {
                                     $vb_ft_bit_optimization = true;
                                 }
                             }
                         }
                     }
                 }
                 //
                 // If we're querying on the fulltext index then we need to construct
                 // the query here... if we already have a direct SQL query to run then we can skip this
                 //
                 if ($vb_is_blank_search) {
                     $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (swi.word_id = 0))";
                     if (!sizeof($va_sql_where)) {
                         continue;
                     }
                     $vs_sql_where = join(' OR ', $va_sql_where);
                 } elseif (!$vs_direct_sql_query) {
                     $va_sql_where = array();
                     if (sizeof($va_ft_terms)) {
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (sw.word IN (" . join(',', $va_ft_terms) . ")))";
                         } else {
                             if (sizeof($va_ft_terms) == 1) {
                                 $va_sql_where[] = "(sw.word = " . $va_ft_terms[0] . ")";
                             } else {
                                 $va_sql_where[] = "(sw.word IN (" . join(',', $va_ft_terms) . "))";
                             }
                         }
                     }
                     if (sizeof($va_ft_like_terms)) {
                         $va_tmp = array();
                         foreach ($va_ft_like_terms as $vs_term) {
                             if ($vb_ft_bit_optimization) {
                                 $va_tmp[] = '(sw.word = \' ' . $this->opo_db->escape(trim($vs_term)) . ' \')';
                             } else {
                                 $va_tmp[] = '(sw.word LIKE \'' . $this->opo_db->escape(trim($vs_term)) . '%\')';
                             }
                         }
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (" . join(' AND ', $va_tmp) . "))";
                         } else {
                             $va_sql_where[] = "(" . join(' AND ', $va_tmp) . ")";
                         }
                     }
                     if (sizeof($va_ft_stem_terms)) {
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (sw.stem IN (" . join(',', $va_ft_stem_terms) . ")))";
                         } else {
                             $va_sql_where[] = "(sw.stem IN (" . join(',', $va_ft_stem_terms) . "))";
                         }
                     }
                     if (!sizeof($va_sql_where)) {
                         continue;
                     }
                     $vs_sql_where = join(' OR ', $va_sql_where);
                 } else {
                     $va_ft_terms = $va_ft_like_terms = $va_ft_like_terms = array();
                 }
                 //print "OP=$vs_op<br>";
                 if ($vn_i == 0) {
                     if ($vs_direct_sql_query) {
                         $vs_direct_sql_query = str_replace('^JOIN', "", $vs_direct_sql_query);
                     }
                     $vs_sql = $vs_direct_sql_query ? "INSERT IGNORE INTO {$ps_dest_table} {$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t" . (!$vb_is_blank_search ? "INNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id" : '') . "\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\tGROUP BY swi.row_id \n\t\t\t\t\t\t";
                     if (($vn_num_terms = sizeof($va_ft_terms) + sizeof($va_ft_like_terms) + sizeof($va_ft_stem_terms)) > 1 && !$vs_direct_sql_query) {
                         $vs_sql .= " HAVING count(distinct sw.word_id) = {$vn_num_terms}";
                     }
                     if ($this->debug) {
                         print 'FIRST: ' . $vs_sql . " [{$pn_subject_tablenum}]<hr>\n";
                     }
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql, is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum));
                 } else {
                     switch ($vs_op) {
                         case 'AND':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', "INNER JOIN {$ps_dest_table} AS ftmp1 ON ftmp1.row_id = ca.row_id", $vs_direct_sql_query);
                             }
                             $this->_createTempTable($ps_dest_table . '_acc');
                             $vs_sql = $vs_direct_sql_query ? "INSERT IGNORE INTO {$ps_dest_table}_acc {$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}_acc\n\t\t\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tINNER JOIN {$ps_dest_table} AS ftmp1 ON ftmp1.row_id = swi.row_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t\tGROUP BY\n\t\t\t\t\t\t\t\t\t\tswi.row_id\n\t\t\t\t\t\t\t\t";
                             if (($vn_num_terms = sizeof($va_ft_terms) + sizeof($va_ft_like_terms) + sizeof($va_ft_stem_terms)) > 1) {
                                 $vs_sql .= " HAVING count(distinct sw.word_id) = {$vn_num_terms}";
                             }
                             if ($this->debug) {
                                 print 'AND:' . $vs_sql . "<hr>\n";
                             }
                             $qr_res = $this->opo_db->query($vs_sql, is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum));
                             $qr_res = $this->opo_db->query("TRUNCATE TABLE {$ps_dest_table}");
                             $qr_res = $this->opo_db->query("INSERT INTO {$ps_dest_table} SELECT row_id, boost FROM {$ps_dest_table}_acc");
                             //$qr_res = $this->opo_db->query("TRUNCATE TABLE ca_sql_search_temp_2");
                             $this->_dropTempTable($ps_dest_table . '_acc');
                             break;
                         case 'NOT':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', "", $vs_direct_sql_query);
                             }
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT row_id\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_words sw\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\t" . ($vs_sql_where ? "{$vs_sql_where} AND " : "") . " swi.table_num = ? \n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '');
                             //print "$vs_sql<hr>";
                             $qr_res = $this->opo_db->query($vs_sql, is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum));
                             $va_ids = $qr_res->getAllFieldValues("row_id");
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tDELETE FROM {$ps_dest_table} \n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\trow_id IN (?)\n\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql, array($va_ids));
                             //print "$vs_sql<hr>";
                             break;
                         default:
                         case 'OR':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', "", $vs_direct_sql_query);
                             }
                             $vs_sql = $vs_direct_sql_query ? "INSERT IGNORE INTO {$ps_dest_table} {$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t\tGROUP BY\n\t\t\t\t\t\t\t\t\t\tswi.row_id\n\t\t\t\t\t\t\t\t";
                             if ($this->debug) {
                                 print 'OR' . $vs_sql . "<hr>\n";
                             }
                             $qr_res = $this->opo_db->query($vs_sql, is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum));
                             break;
                     }
                 }
                 // Drop any temporary tables created by direct search queries
                 foreach (array_keys($va_direct_query_temp_tables) as $vs_temp_table_to_drop) {
                     $this->_dropTempTable($vs_temp_table_to_drop);
                 }
                 break;
             default:
                 //print get_class($o_lucene_query_element);
                 break;
         }
         $vn_i++;
     }
 }
Example #7
0
/**
 * Convert text into string suitable for sorting, by moving articles to end of string, etc.
 *
 * @param string $ps_text Text to convert to sortable value
 * @param array $pa_options Options include:
 *		locale = Locale settings to use. If omitted current default locale is used. [Default is current locale]
 *		omitArticle = Omit leading definite and indefinited articles, rather than moving them to the end of the text [Default is true]
 *
 * @return string Converted text. If locale cannot be found $ps_text is returned unchanged.
 */
function caSortableValue($ps_text, $pa_options = null)
{
    global $g_ui_locale;
    $ps_locale = caGetOption('locale', $pa_options, $g_ui_locale);
    if (!$ps_locale) {
        return $ps_text;
    }
    $pb_omit_article = caGetOption('omitArticle', $pa_options, true);
    $o_locale_settings = TimeExpressionParser::getSettingsForLanguage($ps_locale);
    $vs_display_value = trim(preg_replace('![^\\p{L}0-9 ]+!u', ' ', $ps_text));
    // Move articles to end of string
    $va_definite_articles = $o_locale_settings ? $o_locale_settings->get('definiteArticles') : array();
    $va_indefinite_articles = $o_locale_settings ? $o_locale_settings->get('indefiniteArticles') : array();
    foreach (array($va_definite_articles, $va_indefinite_articles) as $va_articles) {
        if (is_array($va_articles)) {
            foreach ($va_articles as $vs_article) {
                if (preg_match('!^(' . $vs_article . ')[ ]+!i', $vs_display_value, $va_matches)) {
                    $vs_display_value = trim(str_replace($va_matches[1], '', $vs_display_value) . ($pb_omit_article ? '' : ', ' . $va_matches[1]));
                    break 2;
                }
            }
        }
    }
    // Left-pad numbers
    if (preg_match("![\\d]+!", $vs_display_value, $va_matches)) {
        for ($i = 0; $i < sizeof($va_matches); $i++) {
            $vs_padded = str_pad($va_matches[$i], 15, 0, STR_PAD_LEFT);
            $vs_display_value = str_replace($va_matches[$i], $vs_padded, $vs_display_value);
        }
    }
    return $vs_display_value;
}
Example #8
0
/**
 *
 *
 * @param string $ps_date_expression Start of date range, as Unix timestamp
 * @param array $pa_options All options supported by TimeExpressionParser::getText() are supported
 *
 * @return array
 */
function caGetISODates($ps_date_expression, $pa_options = null)
{
    if (!is_array($pa_options)) {
        $pa_options = array();
    }
    $o_tep = new TimeExpressionParser();
    if (!$o_tep->parse($ps_date_expression)) {
        return null;
    }
    return array('start' => $o_tep->getText(array_merge($pa_options, array('start_as_iso8601' => true))), 'end' => $o_tep->getText(array_merge($pa_options, array('end_as_iso8601' => true))));
}
 /**
  * Return current date/time 
  *
  * @param array $pa_options Options include:
  *		format = format of return value. Options are:
  *				unix = Unix-timestamp
  *				historic = Historic timestamp 
  *				[Default is historic]
  *				
  */
 public static function now($pa_options = null)
 {
     $ps_format = caGetOption('format', $pa_options, null, ['toLowerCase' => true]);
     $o_tep = new TimeExpressionParser();
     $o_tep->parse(__TEP_NOW__);
     switch ($ps_format) {
         case 'unix':
             return array_shift($o_tep->getUnixTimestamps());
             break;
         case 'historic':
         default:
             return array_shift($o_tep->getHistoricTimestamps());
             break;
     }
     return null;
 }
Example #10
0
 private function _doQueriesForSqlSearch($po_rewritten_query, $pn_subject_tablenum, $ps_dest_table, $pn_level = 0, $pa_options = null)
 {
     // query is always of type Zend_Search_Lucene_Search_Query_Boolean
     $vn_i = 0;
     switch (get_class($po_rewritten_query)) {
         case 'Zend_Search_Lucene_Search_Query_MultiTerm':
             $va_elements = $po_rewritten_query->getTerms();
             break;
         default:
             $va_elements = $po_rewritten_query->getSubqueries();
             break;
     }
     $o_base = new SearchBase();
     $va_old_signs = $po_rewritten_query->getSigns();
     foreach ($va_elements as $o_lucene_query_element) {
         $vb_is_blank_search = false;
         if (is_null($va_old_signs)) {
             // if array is null then according to Zend Lucene all subqueries should be "are required"... so we AND them
             $vs_op = "AND";
         } else {
             if (is_null($va_old_signs[$vn_i])) {
                 // is the sign for a particular query is null then OR is (it is "neither required nor prohibited")
                 $vs_op = 'OR';
             } else {
                 $vs_op = $va_old_signs[$vn_i] === false ? 'NOT' : 'AND';
                 // true sign indicated "required" (AND) operation, false indicated "prohibited" (NOT) operation
             }
         }
         if ($vn_i == 0) {
             $vs_op = 'OR';
         }
         $va_direct_query_temp_tables = array();
         // List of temporary tables created by direct search queries; tables listed here are dropped at the end of processing for the query element
         $pa_direct_sql_query_params = null;
         // set to array with values to use with direct SQL query placeholders or null to pass single standard table_num value as param (most queries just need this single value)
         $vs_direct_sql_query = null;
         $vn_direct_sql_target_table_num = $pn_subject_tablenum;
         switch ($vs_class = get_class($o_lucene_query_element)) {
             case 'Zend_Search_Lucene_Search_Query_Boolean':
             case 'Zend_Search_Lucene_Search_Query_MultiTerm':
                 $this->_createTempTable('ca_sql_search_temp_' . $pn_level);
                 if ($vs_op == 'AND' && $vn_i == 0) {
                     $this->_doQueriesForSqlSearch($o_lucene_query_element, $pn_subject_tablenum, $ps_dest_table, $pn_level + 1);
                 } else {
                     $this->_doQueriesForSqlSearch($o_lucene_query_element, $pn_subject_tablenum, 'ca_sql_search_temp_' . $pn_level, $pn_level + 1);
                 }
                 // merge with current destination
                 switch ($vs_op) {
                     case 'AND':
                         if ($vn_i > 0) {
                             $this->_createTempTable("{$ps_dest_table}_acc");
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}_acc\n\t\t\t\t\t\t\t\t\tSELECT mfs.row_id, SUM(mfs.boost)\n\t\t\t\t\t\t\t\t\tFROM {$ps_dest_table} mfs\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_temp_{$pn_level} AS ftmp1 ON ftmp1.row_id = mfs.row_id\n\t\t\t\t\t\t\t\t\tGROUP BY mfs.row_id\n\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql);
                             $qr_res = $this->opo_db->query("TRUNCATE TABLE {$ps_dest_table}");
                             $qr_res = $this->opo_db->query("INSERT INTO {$ps_dest_table} SELECT row_id, boost FROM {$ps_dest_table}_acc");
                             $this->_dropTempTable("{$ps_dest_table}_acc");
                         }
                         break;
                     case 'NOT':
                         $qr_res = $this->opo_db->query("SELECT row_id FROM ca_sql_search_temp_{$pn_level}");
                         if (is_array($va_ids = $qr_res->getAllFieldValues()) && sizeof($va_ids)) {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tDELETE FROM {$ps_dest_table} WHERE row_id IN (?)\n\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql, array($va_ids));
                         }
                         break;
                     default:
                     case 'OR':
                         $vs_sql = "\n\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\tSELECT row_id, SUM(boost)\n\t\t\t\t\t\t\t\tFROM ca_sql_search_temp_{$pn_level}\n\t\t\t\t\t\t\t\tGROUP BY row_id\n\t\t\t\t\t\t\t";
                         $qr_res = $this->opo_db->query($vs_sql);
                         break;
                 }
                 $vn_i++;
                 $this->_dropTempTable('ca_sql_search_temp_' . $pn_level);
                 break;
             case 'Zend_Search_Lucene_Search_Query_Term':
             case 'Zend_Search_Lucene_Index_Term':
             case 'Zend_Search_Lucene_Search_Query_Phrase':
             case 'Zend_Search_Lucene_Search_Query_Range':
                 $va_ft_terms = array();
                 $va_ft_like_terms = array();
                 $va_ft_stem_terms = array();
                 $vs_access_point = '';
                 $va_raw_terms = array();
                 switch (get_class($o_lucene_query_element)) {
                     case 'Zend_Search_Lucene_Search_Query_Range':
                         $va_lower_term = $o_lucene_query_element->getLowerTerm();
                         $va_upper_term = $o_lucene_query_element->getUpperTerm();
                         $va_element = $this->_getElementIDForAccessPoint($pn_subject_tablenum, $va_lower_term->field);
                         $vn_direct_sql_target_table_num = $va_element['table_num'];
                         $va_indexed_fields = $o_base->getFieldsToIndex($pn_subject_tablenum, $vn_direct_sql_target_table_num);
                         $vn_root_element_id = $va_element['element_info']['hier_element_id'];
                         if (!isset($va_indexed_fields['_ca_attribute_' . $va_element['element_id']]) && (!$vn_root_element_id || $vn_root_element_id && !isset($va_indexed_fields['_ca_attribute_' . $vn_root_element_id]))) {
                             break 2;
                         }
                         // skip if not indexed
                         switch ($va_element['datatype']) {
                             case __CA_ATTRIBUTE_VALUE_GEOCODE__:
                                 $t_geocode = new GeocodeAttributeValue();
                                 $va_parsed_value = $t_geocode->parseValue('[' . $va_lower_term->text . ']', $va_element['element_info']);
                                 $vs_lower_lat = $va_parsed_value['value_decimal1'];
                                 $vs_lower_long = $va_parsed_value['value_decimal2'];
                                 $va_parsed_value = $t_geocode->parseValue('[' . $va_upper_term->text . ']', $va_element['element_info']);
                                 $vs_upper_lat = $va_parsed_value['value_decimal1'];
                                 $vs_upper_long = $va_parsed_value['value_decimal2'];
                                 // mysql BETWEEN always wants the lower value first ... BETWEEN 5 AND 3 wouldn't match 4 ... So we swap the values if necessary
                                 if ($vs_upper_lat < $vs_lower_lat) {
                                     $tmp = $vs_upper_lat;
                                     $vs_upper_lat = $vs_lower_lat;
                                     $vs_lower_lat = $tmp;
                                 }
                                 if ($vs_upper_long < $vs_lower_long) {
                                     $tmp = $vs_upper_long;
                                     $vs_upper_long = $vs_lower_long;
                                     $vs_lower_long = $tmp;
                                 }
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vs_lower_lat) . " AND " . floatval($vs_upper_lat) . ")\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($vs_lower_long) . " AND " . floatval($vs_upper_long) . ")\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case __CA_ATTRIBUTE_VALUE_CURRENCY__:
                                 $t_cur = new CurrencyAttributeValue();
                                 $va_parsed_value = $t_cur->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_cur->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_longtext1 = '" . $this->opo_db->escape($vs_currency) . "')\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case __CA_ATTRIBUTE_VALUE_TIMECODE__:
                                 $t_timecode = new TimecodeAttributeValue();
                                 $va_parsed_value = $t_timecode->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_timecode->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case __CA_ATTRIBUTE_VALUE_LENGTH__:
                                 $t_len = new LengthAttributeValue();
                                 $va_parsed_value = $t_len->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_len->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case __CA_ATTRIBUTE_VALUE_WEIGHT__:
                                 $t_weight = new WeightAttributeValue();
                                 $va_parsed_value = $t_weight->parseValue($va_lower_term->text, $va_element['element_info']);
                                 $vn_lower_val = $va_parsed_value['value_decimal1'];
                                 $va_parsed_value = $t_weight->parseValue($va_upper_term->text, $va_element['element_info']);
                                 $vn_upper_val = $va_parsed_value['value_decimal1'];
                                 break;
                             case __CA_ATTRIBUTE_VALUE_INTEGER__:
                                 $vn_lower_val = intval($va_lower_term->text);
                                 $vn_upper_val = intval($va_upper_term->text);
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t(cav.value_integer1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t";
                                 break;
                             case __CA_ATTRIBUTE_VALUE_NUMERIC__:
                                 $vn_lower_val = floatval($va_lower_term->text);
                                 $vn_upper_val = floatval($va_upper_term->text);
                                 break;
                         }
                         if (!$vs_direct_sql_query) {
                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t(cav.element_id = " . intval($va_element['element_id']) . ") AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($vn_lower_val) . " AND " . floatval($vn_upper_val) . ")\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t";
                         }
                         break;
                     case 'Zend_Search_Lucene_Search_Query_Phrase':
                         if ($this->getOption('strictPhraseSearching')) {
                             $vs_search_tokenizer_regex = $this->opo_search_config->get('search_tokenizer_regex');
                             $va_words = array();
                             foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                                 if (!$vs_access_point && ($vs_field = $o_term->field)) {
                                     $vs_access_point = $vs_field;
                                 }
                                 $va_terms = preg_split("![{$vs_search_tokenizer_regex}]+!u", (string) $o_term->text);
                                 $va_raw_terms[] = (string) $o_term->text;
                                 foreach ($va_terms as $vs_term) {
                                     if (strlen($vs_escaped_text = $this->opo_db->escape($vs_term))) {
                                         $va_words[] = $vs_escaped_text;
                                     }
                                 }
                             }
                             if (!sizeof($va_words)) {
                                 continue 3;
                             }
                             $va_ap_tmp = explode(".", $vs_access_point);
                             $vn_fld_table = $vn_fld_num = null;
                             if (sizeof($va_ap_tmp) >= 2) {
                                 $va_element = $this->_getElementIDForAccessPoint($pn_subject_tablenum, $vs_access_point);
                                 if ($va_element) {
                                     $vs_fld_num = $va_element['field_num'];
                                     $vs_fld_table_num = $va_element['table_num'];
                                     $vs_fld_limit_sql = " AND (swi.field_table_num = {$vs_fld_table_num} AND swi.field_num = '{$vs_fld_num}')";
                                     if (is_array($va_element['relationship_type_ids']) && sizeof($va_element['relationship_type_ids'])) {
                                         $vs_fld_limit_sql .= " AND (swi.rel_type_id IN (" . join(",", $va_element['relationship_type_ids']) . "))";
                                     }
                                 }
                             }
                             $va_temp_tables = array();
                             $vn_w = 0;
                             foreach ($va_words as $vs_word) {
                                 $vn_w++;
                                 $vs_temp_table = 'ca_sql_search_phrase_' . md5($pn_subject_tablenum . "/" . $vs_word . "/" . $vn_w);
                                 $this->_createTempTable($vs_temp_table);
                                 $vs_sql = "\n\t\t\t\t\t\t\t\t\tINSERT INTO {$vs_temp_table}\n\t\t\t\t\t\t\t\t\tSELECT swi.index_id + 1, 1\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_words sw \n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON sw.word_id = swi.word_id \n\t\t\t\t\t\t\t\t\t" . (sizeof($va_temp_tables) ? " INNER JOIN " . $va_temp_tables[sizeof($va_temp_tables) - 1] . " AS tt ON swi.index_id = tt.row_id" : "") . "\n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\tsw.word = ? AND swi.table_num = ? {$vs_fld_limit_sql}\n \t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t";
                                 $qr_res = $this->opo_db->query($vs_sql, $vs_word, (int) $pn_subject_tablenum);
                                 $qr_count = $this->opo_db->query("SELECT count(*) c FROM {$vs_temp_table}");
                                 if (!$qr_count->nextRow() || !(int) $qr_count->get('c')) {
                                     foreach ($va_temp_tables as $vs_temp_table) {
                                         $this->_dropTempTable($vs_temp_table);
                                     }
                                     break 2;
                                 }
                                 $va_temp_tables[] = $vs_temp_table;
                             }
                             $vs_results_temp_table = array_pop($va_temp_tables);
                             $this->opo_db->query("UPDATE {$vs_results_temp_table} SET row_id = row_id - 1");
                             $va_direct_query_temp_tables[$vs_results_temp_table] = true;
                             $vs_direct_sql_query = "SELECT swi.row_id, ca.boost \n\t\t\t\t\t\t\t\t\t\t\t\t\tFROM {$vs_results_temp_table} ca\n\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON swi.index_id = ca.row_id \n\t\t\t\t\t\t\t";
                             $pa_direct_sql_query_params = array();
                             // don't pass any params
                             foreach ($va_temp_tables as $vs_temp_table) {
                                 $this->_dropTempTable($vs_temp_table);
                             }
                             break;
                         }
                     default:
                         switch ($vs_class) {
                             case 'Zend_Search_Lucene_Search_Query_Phrase':
                                 $va_term_objs = $o_lucene_query_element->getQueryTerms();
                                 break;
                             case 'Zend_Search_Lucene_Index_Term':
                                 $va_term_objs = array($o_lucene_query_element);
                                 break;
                             default:
                                 $va_term_objs = array($o_lucene_query_element->getTerm());
                                 break;
                         }
                         foreach ($va_term_objs as $o_term) {
                             $va_access_point_info = $this->_getElementIDForAccessPoint($pn_subject_tablenum, $o_term->field);
                             $vs_access_point = $va_access_point_info['access_point'];
                             $vs_term = $o_term->text;
                             if ($vs_access_point && mb_strtoupper($vs_term) == _t('[BLANK]')) {
                                 $t_ap = $this->opo_datamodel->getInstanceByTableNum($va_access_point_info['table_num'], true);
                                 if (is_a($t_ap, 'BaseLabel')) {
                                     // labels have the literal text "[Blank]" indexed to "blank" to indicate blank-ness
                                     $vb_is_blank_search = false;
                                     $vs_term = _t('blank');
                                 } else {
                                     $vb_is_blank_search = true;
                                     break;
                                 }
                             }
                             $va_terms = array($vs_term);
                             //$this->_tokenize($vs_term, true, $vn_i);
                             $vb_has_wildcard = (bool) preg_match('!\\*$!', $vs_term);
                             $vb_output_term = false;
                             foreach ($va_terms as $vs_term) {
                                 if ($vb_has_wildcard) {
                                     $vs_term .= '*';
                                 }
                                 if (in_array(trim(mb_strtolower($vs_term, 'UTF-8')), WLPlugSearchEngineSqlSearch::$s_stop_words)) {
                                     continue;
                                 }
                                 $vs_stripped_term = preg_replace('!\\*+$!u', '', $vs_term);
                                 if ($vb_has_wildcard) {
                                     $va_ft_like_terms[] = $vs_stripped_term;
                                 } else {
                                     // do stemming
                                     $vb_do_stemming = $this->opb_do_stemming;
                                     if (mb_substr($vs_term, -1) == '|') {
                                         $vs_term = mb_substr($vs_term, 0, mb_strlen($vs_term) - 1);
                                         $vb_do_stemming = false;
                                     }
                                     if ($vb_do_stemming) {
                                         $vs_to_stem = preg_replace('!\\*$!u', '', $vs_term);
                                         if (!preg_match('!y$!u', $vs_to_stem) && !preg_match('![0-9]+!', $vs_to_stem)) {
                                             // don't stem things ending in 'y' as that can cause problems (eg "Bowery" becomes "Boweri")
                                             if (!($vs_stem = trim($this->opo_stemmer->stem($vs_to_stem)))) {
                                                 $vs_stem = (string) $vs_term;
                                             }
                                             $va_ft_stem_terms[] = "'" . $this->opo_db->escape($vs_stem) . "'";
                                         } else {
                                             $va_ft_terms[] = '"' . $this->opo_db->escape($vs_term) . '"';
                                         }
                                     } else {
                                         $va_ft_terms[] = '"' . $this->opo_db->escape($vs_term) . '"';
                                     }
                                 }
                                 $vb_output_term = true;
                             }
                             if ($vb_output_term) {
                                 $va_raw_terms[] = $vs_term;
                             }
                         }
                         break;
                 }
                 $vs_fld_num = $vs_table_num = $t_table = null;
                 $vb_ft_bit_optimization = false;
                 if ($vs_access_point) {
                     list($vs_table, $vs_field, $vs_sub_field) = explode('.', $vs_access_point);
                     if (in_array($vs_table, array('created', 'modified'))) {
                         $o_tep = new TimeExpressionParser();
                         $vs_date = join(' ', $va_raw_terms);
                         if (!$o_tep->parse($vs_date)) {
                             break;
                         }
                         $va_range = $o_tep->getUnixTimestamps();
                         $vn_user_id = null;
                         if ($vs_field = trim($vs_field)) {
                             if (!is_int($vs_field)) {
                                 $t_user = new ca_users();
                                 if ($t_user->load(array("user_name" => $vs_field))) {
                                     $vn_user_id = (int) $t_user->getPrimaryKey();
                                 }
                             } else {
                                 $vn_user_id = (int) $vs_field;
                             }
                         }
                         $vs_user_sql = $vn_user_id ? " AND (ccl.user_id = " . (int) $vn_user_id . ")" : "";
                         switch ($vs_table) {
                             case 'created':
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccl.logged_row_id row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.logged_table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.changetype = 'I')\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\t";
                                 break;
                             case 'modified':
                                 $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccl.logged_row_id row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.logged_table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.changetype = 'U')\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\tUNION\n\t\t\t\t\t\t\t\t\t\t\tSELECT ccls.subject_row_id row_id, 1\n\t\t\t\t\t\t\t\t\t\t\tFROM ca_change_log ccl\n\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_change_log_subjects AS ccls ON ccls.log_id = ccl.log_id\n\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t(ccl.log_datetime BETWEEN " . (int) $va_range['start'] . " AND " . (int) $va_range['end'] . ")\n\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t(ccls.subject_table_num = {$pn_subject_tablenum})\n\t\t\t\t\t\t\t\t\t\t\t\t{$vs_user_sql}\n\t\t\t\t\t\t\t\t\t\t";
                                 break;
                         }
                     } else {
                         if ($vs_table && $vs_field && ($t_table = $this->opo_datamodel->getInstanceByTableName($vs_table, true))) {
                             $vs_table_num = $t_table->tableNum();
                             if (is_numeric($vs_field)) {
                                 $vs_fld_num = 'I' . $vs_field;
                                 $vn_fld_num = (int) $vs_field;
                             } else {
                                 $vn_fld_num = $this->getFieldNum($vs_table, $vs_field);
                                 $vs_fld_num = 'I' . $vn_fld_num;
                                 $vn_direct_sql_target_table_num = $vs_table_num;
                                 if (!strlen($vn_fld_num)) {
                                     $t_element = new ca_metadata_elements();
                                     if ($t_element->load(array('element_code' => $vs_sub_field ? $vs_sub_field : $vs_field))) {
                                         $va_indexed_fields = $o_base->getFieldsToIndex($pn_subject_tablenum, $vn_direct_sql_target_table_num);
                                         $vn_fld_num = $t_element->getPrimaryKey();
                                         $vn_root_element_id = $t_element->get('hier_element_id');
                                         if (!isset($va_indexed_fields['_ca_attribute_' . $vn_fld_num]) && (!$vn_root_element_id || $vn_root_element_id && !isset($va_indexed_fields['_ca_attribute_' . $vn_root_element_id]))) {
                                             break 2;
                                         }
                                         // skip if not indexed
                                         $vs_fld_num = 'A' . $vn_fld_num;
                                         if (!$vb_is_blank_search) {
                                             //
                                             // For certain types of attributes we can directly query the
                                             // attributes in the database rather than using the full text index
                                             // This allows us to do "intelligent" querying... for example on date ranges
                                             // parsed from natural language input and for length dimensions using unit conversion
                                             //
                                             switch ($t_element->get('datatype')) {
                                                 case __CA_ATTRIBUTE_VALUE_DATERANGE__:
                                                     $vb_all_numbers = true;
                                                     foreach ($va_raw_terms as $vs_term) {
                                                         if (!is_numeric($vs_term)) {
                                                             $vb_all_numbers = false;
                                                             break;
                                                         }
                                                     }
                                                     $vs_raw_term = join(' ', $va_raw_terms);
                                                     $vb_exact = $vs_raw_term[0] == "#" ? true : false;
                                                     // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                                     if ($vb_exact) {
                                                         $vs_raw_term = substr($vs_raw_term, 1);
                                                         if ($this->opo_tep->parse($vs_raw_term)) {
                                                             $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         }
                                                     } else {
                                                         if ($this->opo_tep->parse($vs_raw_term)) {
                                                             $va_dates = $this->opo_tep->getHistoricTimestamps();
                                                             $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 <= " . floatval($va_dates['start']) . " AND cav.value_decimal2 >= " . floatval($va_dates['end']) . ")\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                         }
                                                     }
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_GEOCODE__:
                                                     // At this point $va_raw_terms has been tokenized by Lucene into oblivion
                                                     // and is also dependent on the search_tokenizer_regex so we can't really do anything with it.
                                                     // We now build our own un-tokenized term array instead. caParseGISSearch() can handle it.
                                                     $va_gis_terms = array();
                                                     foreach ($o_lucene_query_element->getQueryTerms() as $o_term) {
                                                         $va_gis_terms[] = trim((string) $o_term->text);
                                                     }
                                                     if ($va_coords = caParseGISSearch(join(' ', $va_gis_terms))) {
                                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 BETWEEN {$va_coords['min_latitude']} AND {$va_coords['max_latitude']})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal2 BETWEEN {$va_coords['min_longitude']} AND {$va_coords['max_longitude']})\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     }
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_CURRENCY__:
                                                     $t_cur = new CurrencyAttributeValue();
                                                     $va_parsed_value = $t_cur->parseValue(join(' ', $va_raw_terms), $t_element->getFieldValuesArray());
                                                     $vn_amount = $va_parsed_value['value_decimal1'];
                                                     $vs_currency = preg_replace('![^A-Z0-9]+!', '', $va_parsed_value['value_longtext1']);
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_amount) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_longtext1 = '" . $this->opo_db->escape($vs_currency) . "')\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_LENGTH__:
                                                     // If it looks like a dimension that has been tokenized by Lucene
                                                     // into oblivion rehydrate it here.
                                                     try {
                                                         switch (sizeof($va_raw_terms)) {
                                                             case 2:
                                                                 $vs_dimension = $va_raw_terms[0] . caGetDecimalSeparator() . $va_raw_terms[1];
                                                                 break;
                                                             case 3:
                                                                 $vs_dimension = $va_raw_terms[0] . caGetDecimalSeparator() . $va_raw_terms[1] . " " . $va_raw_terms[2];
                                                                 break;
                                                             default:
                                                                 $vs_dimension = join(' ', $va_raw_terms);
                                                         }
                                                         $vo_parsed_measurement = caParseLengthDimension($vs_dimension);
                                                         $vn_len = $vo_parsed_measurement->convertTo('METER', 6, 'en_US');
                                                     } catch (Exception $e) {
                                                         $vs_direct_sql_query = null;
                                                         break;
                                                     }
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_len) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_WEIGHT__:
                                                     // If it looks like a weight that has been tokenized by Lucene
                                                     // into oblivion rehydrate it here.
                                                     try {
                                                         switch (sizeof($va_raw_terms)) {
                                                             case 2:
                                                                 $vs_dimension = $va_raw_terms[0] . caGetDecimalSeparator() . $va_raw_terms[1];
                                                                 break;
                                                             case 3:
                                                                 $vs_dimension = $va_raw_terms[0] . caGetDecimalSeparator() . $va_raw_terms[1] . " " . $va_raw_terms[2];
                                                                 break;
                                                             default:
                                                                 $vs_dimension = join(' ', $va_raw_terms);
                                                         }
                                                         $vo_parsed_measurement = caParseWeightDimension($vs_dimension);
                                                         $vn_weight = $vo_parsed_measurement->convertTo('KILOGRAM', 6, 'en_US');
                                                     } catch (Exception $e) {
                                                         $vs_direct_sql_query = null;
                                                         break;
                                                     }
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_weight) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_TIMECODE__:
                                                     $t_timecode = new TimecodeAttributeValue();
                                                     $va_parsed_value = $t_timecode->parseValue(join(' ', $va_raw_terms), $t_element->getFieldValuesArray());
                                                     $vn_timecode = $va_parsed_value['value_decimal1'];
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval($vn_timecode) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_INTEGER__:
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_integer1 = " . intval(array_shift($va_raw_terms)) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                                 case __CA_ATTRIBUTE_VALUE_NUMERIC__:
                                                     $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tSELECT ca.row_id, 1\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tFROM ca_attribute_values cav\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tINNER JOIN ca_attributes AS ca ON ca.attribute_id = cav.attribute_id\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.element_id = {$vn_fld_num}) AND (ca.table_num = ?)\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t(cav.value_decimal1 = " . floatval(array_shift($va_raw_terms)) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t";
                                                     break;
                                             }
                                         }
                                     } else {
                                         // neither table fields nor elements, i.e. 'virtual' fields like _count should
                                         $vn_fld_num = false;
                                         $vs_fld_num = $vs_field;
                                     }
                                 }
                             }
                             if (($vs_intrinsic_field_name = $t_table->fieldName($vn_fld_num)) && ($vn_intrinsic_type = $t_table->getFieldInfo($vs_intrinsic_field_name, 'FIELD_TYPE')) == FT_BIT) {
                                 $vb_ft_bit_optimization = true;
                             } elseif ($vn_intrinsic_type == FT_HISTORIC_DATERANGE) {
                                 $vb_all_numbers = true;
                                 foreach ($va_raw_terms as $vs_term) {
                                     if (!is_numeric($vs_term)) {
                                         $vb_all_numbers = false;
                                         break;
                                     }
                                 }
                                 $vs_date_start_fld = $t_table->getFieldInfo($vs_intrinsic_field_name, 'START');
                                 $vs_date_end_fld = $t_table->getFieldInfo($vs_intrinsic_field_name, 'END');
                                 $vs_raw_term = join(' ', $va_raw_terms);
                                 $vb_exact = $vs_raw_term[0] == "#" ? true : false;
                                 // dates prepended by "#" are considered "exact" or "contained - the matched dates must be wholly contained by the search term
                                 if ($vb_exact) {
                                     $vs_raw_term = substr($vs_raw_term, 1);
                                     if ($this->opo_tep->parse($vs_raw_term)) {
                                         $va_dates = $this->opo_tep->getHistoricTimestamps();
                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\tSELECT " . $t_table->primaryKey() . ", 1\n\t\t\t\t\t\t\t\t\t\t\t\tFROM " . $t_table->tableName() . "\n\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_start_fld} BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_end_fld} BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t";
                                     }
                                 } else {
                                     if ($this->opo_tep->parse($vs_raw_term)) {
                                         $va_dates = $this->opo_tep->getHistoricTimestamps();
                                         $vs_direct_sql_query = "\n\t\t\t\t\t\t\t\t\t\t\t\tSELECT " . $t_table->primaryKey() . ", 1\n\t\t\t\t\t\t\t\t\t\t\t\tFROM " . $t_table->tableName() . "\n\t\t\t\t\t\t\t\t\t\t\t\t^JOIN\n\t\t\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_start_fld} BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_end_fld} BETWEEN " . floatval($va_dates['start']) . " AND " . floatval($va_dates['end']) . ")\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t\t\t({$vs_date_start_fld} <= " . floatval($va_dates['start']) . " AND {$vs_date_end_fld} >= " . floatval($va_dates['end']) . ")\t\n\t\t\t\t\t\t\t\t\t\t\t\t\t)\n\t\t\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t\t";
                                     }
                                 }
                                 $pa_direct_sql_query_params = array();
                             }
                         }
                     }
                 }
                 //
                 // If we're querying on the fulltext index then we need to construct
                 // the query here... if we already have a direct SQL query to run then we can skip this
                 //
                 if ($vb_is_blank_search) {
                     $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (swi.word_id = 0))";
                     if (!sizeof($va_sql_where)) {
                         continue;
                     }
                     $vs_sql_where = join(' OR ', $va_sql_where);
                 } elseif (!$vs_direct_sql_query) {
                     $va_sql_where = array();
                     if (sizeof($va_ft_terms)) {
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (sw.word IN (" . join(',', $va_ft_terms) . ")))";
                         } else {
                             if (sizeof($va_ft_terms) == 1) {
                                 $va_sql_where[] = "(sw.word = " . $va_ft_terms[0] . ")";
                             } else {
                                 $va_sql_where[] = "(sw.word IN (" . join(',', $va_ft_terms) . "))";
                             }
                         }
                     }
                     if (sizeof($va_ft_like_terms)) {
                         $va_tmp = array();
                         foreach ($va_ft_like_terms as $vs_term) {
                             if ($vb_ft_bit_optimization) {
                                 $va_tmp[] = '(sw.word = \' ' . $this->opo_db->escape(trim($vs_term)) . ' \')';
                             } else {
                                 $va_tmp[] = '(sw.word LIKE \'' . $this->opo_db->escape(trim($vs_term)) . '%\')';
                             }
                         }
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (" . join(' AND ', $va_tmp) . "))";
                         } else {
                             $va_sql_where[] = "(" . join(' AND ', $va_tmp) . ")";
                         }
                     }
                     if (sizeof($va_ft_stem_terms)) {
                         if ($t_table && strlen($vs_fld_num) > 1) {
                             $va_sql_where[] = "((swi.field_table_num = " . intval($vs_table_num) . ") AND (swi.field_num = '{$vs_fld_num}') AND (sw.stem IN (" . join(',', $va_ft_stem_terms) . ")))";
                         } else {
                             $va_sql_where[] = "(sw.stem IN (" . join(',', $va_ft_stem_terms) . "))";
                         }
                     }
                     if (!sizeof($va_sql_where)) {
                         continue;
                     }
                     $vs_sql_where = join(' OR ', $va_sql_where);
                 } else {
                     $va_ft_terms = $va_ft_like_terms = $va_ft_like_terms = array();
                 }
                 $vs_rel_type_id_sql = null;
                 if (is_array($va_access_point_info['relationship_type_ids']) && sizeof($va_access_point_info['relationship_type_ids'])) {
                     $vs_rel_type_id_sql = " AND (swi.rel_type_id IN (" . join(",", $va_access_point_info['relationship_type_ids']) . "))";
                 }
                 if (!$vs_fld_num && is_array($va_restrict_to_fields = caGetOption('restrictSearchToFields', $pa_options, null)) && sizeof($va_restrict_to_fields)) {
                     $va_field_restrict_sql = array();
                     foreach ($va_restrict_to_fields as $va_restrict) {
                         $va_field_restrict_sql[] = "((swi.field_table_num = " . intval($va_restrict['table_num']) . ") AND (swi.field_num = '" . $va_restrict['field_num'] . "'))";
                     }
                     $vs_sql_where .= " AND (" . join(" OR ", $va_field_restrict_sql) . ")";
                 }
                 $va_join = array();
                 if ($vn_direct_sql_target_table_num != $pn_subject_tablenum) {
                     // We're doing direct queries on metadata in a related table, fun!
                     // Now let's rewrite the direct query to work...
                     if ($t_target = $this->opo_datamodel->getInstanceByTableNum($vn_direct_sql_target_table_num, true)) {
                         // First we create the join from the related table to our subject
                         $vs_target_table_name = $t_target->tableName();
                         $va_path = array_keys($this->opo_datamodel->getPath($vn_direct_sql_target_table_num, $pn_subject_tablenum));
                         $vs_left_table = array_shift($va_path);
                         $vn_cj = 0;
                         foreach ($va_path as $vs_right_table) {
                             if (sizeof($va_rels = $this->opo_datamodel->getRelationships($vs_left_table, $vs_right_table)) > 0) {
                                 $va_join[] = "INNER JOIN {$vs_right_table} ON {$vs_right_table}." . $va_rels[$vs_left_table][$vs_right_table][0][1] . " = " . ($vn_cj == 0 ? 'ca.row_id' : "{$vs_left_table}." . $va_rels[$vs_left_table][$vs_right_table][0][0]);
                             }
                             $vs_left_table = $vs_right_table;
                             $vn_cj++;
                         }
                         // Next we rewrite the key we're pulling to be from our subject
                         $vs_direct_sql_query = str_replace("SELECT ca.row_id", "SELECT " . $this->opo_datamodel->primaryKey($pn_subject_tablenum, true), $vs_direct_sql_query);
                         // Finally we pray
                     }
                 }
                 if ($vn_i == 0) {
                     if ($vs_direct_sql_query) {
                         $vs_direct_sql_query = str_replace('^JOIN', join("\n", $va_join), $vs_direct_sql_query);
                         $vs_sql = "INSERT IGNORE INTO {$ps_dest_table} {$vs_direct_sql_query}";
                         if (strpos($vs_sql, '?') !== false && (!is_array($pa_direct_sql_query_params) || sizeof($pa_direct_sql_query_params) == 0)) {
                             $pa_direct_sql_query_params = array($vn_direct_sql_target_table_num != $pn_subject_tablenum ? $vn_direct_sql_target_table_num : (int) $pn_subject_tablenum);
                         }
                     } else {
                         $vs_sql = "\n\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t" . (!$vb_is_blank_search ? "INNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id" : '') . "\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t{$vs_rel_type_id_sql}\n\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\tGROUP BY swi.row_id\n\t\t\t\t\t\t\t";
                         $pa_direct_sql_query_params = array((int) $pn_subject_tablenum);
                     }
                     if (($vn_num_terms = sizeof($va_ft_terms) + sizeof($va_ft_like_terms) + sizeof($va_ft_stem_terms)) > 1 && !$vs_direct_sql_query) {
                         $vs_sql .= " HAVING count(distinct sw.word_id) = {$vn_num_terms}";
                     }
                     $t = new Timer();
                     $pa_direct_sql_query_params = is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array();
                     if (strpos($vs_sql, '?') === false) {
                         $pa_direct_sql_query_params = array();
                     }
                     $this->opo_db->query($vs_sql, $pa_direct_sql_query_params);
                     $vn_i++;
                     if ($this->debug) {
                         Debug::msg('FIRST: ' . $vs_sql . " [{$pn_subject_tablenum}] " . $t->GetTime(4));
                     }
                 } else {
                     switch ($vs_op) {
                         case 'AND':
                             if ($vs_direct_sql_query) {
                                 if ($vn_direct_sql_target_table_num != $pn_subject_tablenum) {
                                     array_push($va_join, "INNER JOIN {$ps_dest_table} AS ftmp1 ON ftmp1.row_id = " . $this->opo_datamodel->primaryKey($pn_subject_tablenum, true));
                                 } else {
                                     array_unshift($va_join, "INNER JOIN {$ps_dest_table} AS ftmp1 ON ftmp1.row_id = ca.row_id");
                                 }
                                 $vs_direct_sql_query = str_replace('^JOIN', join("\n", $va_join), $vs_direct_sql_query);
                                 $pa_direct_sql_query_params = array($vn_direct_sql_target_table_num != $pn_subject_tablenum ? $vn_direct_sql_target_table_num : (int) $pn_subject_tablenum);
                             }
                             $vs_sql = $vs_direct_sql_query ? "{$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\t\t\tSELECT swi.row_id\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t\t{$vs_rel_type_id_sql}\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t\tGROUP BY swi.row_id\n\t\t\t\t\t\t\t\t";
                             if (($vn_num_terms = sizeof($va_ft_terms) + sizeof($va_ft_like_terms) + sizeof($va_ft_stem_terms)) > 1) {
                                 $vs_sql .= " HAVING count(distinct sw.word_id) = {$vn_num_terms}";
                             }
                             $t = new Timer();
                             $pa_direct_sql_query_params = is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum);
                             if (strpos($vs_sql, '?') === false) {
                                 $pa_direct_sql_query_params = array();
                             }
                             $qr_res = $this->opo_db->query($vs_sql, $pa_direct_sql_query_params);
                             if ($this->debug) {
                                 Debug::msg('AND: ' . $vs_sql . ' ' . $t->GetTime(4) . ' ' . $qr_res->numRows());
                             }
                             if (is_array($va_ids = $qr_res->getAllFieldValues($vs_direct_sql_query && $vn_direct_sql_target_table_num != $pn_subject_tablenum ? $this->opo_datamodel->primaryKey($pn_subject_tablenum) : 'row_id')) && sizeof($va_ids)) {
                                 $vs_sql = "DELETE FROM {$ps_dest_table} WHERE row_id NOT IN (?)";
                                 $qr_res = $this->opo_db->query($vs_sql, array($va_ids));
                                 if ($this->debug) {
                                     Debug::msg('AND DELETE: ' . $vs_sql . ' ' . $t->GetTime(4));
                                 }
                             } else {
                                 // we don't have any results left, ie. our AND query should yield an empty result
                                 $this->opo_db->query("DELETE FROM {$ps_dest_table}");
                             }
                             $vn_i++;
                             break;
                         case 'NOT':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', join("\n", $va_join), $vs_direct_sql_query);
                                 $pa_direct_sql_query_params = array($vn_direct_sql_target_table_num != $pn_subject_tablenum ? $vn_direct_sql_target_table_num : (int) $pn_subject_tablenum);
                             }
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT row_id\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_words sw\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_word_index AS swi ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\t" . ($vs_sql_where ? "{$vs_sql_where} AND " : "") . " swi.table_num = ? \n\t\t\t\t\t\t\t\t\t\t{$vs_rel_type_id_sql}\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '');
                             $pa_direct_sql_query_params = is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum);
                             if (strpos($vs_sql, '?') === false) {
                                 $pa_direct_sql_query_params = array();
                             }
                             $qr_res = $this->opo_db->query($vs_sql, $pa_direct_sql_query_params);
                             $va_ids = $qr_res->getAllFieldValues($vs_direct_sql_query && $vn_direct_sql_target_table_num != $pn_subject_tablenum ? $this->opo_datamodel->primaryKey($pn_subject_tablenum) : 'row_id');
                             if (sizeof($va_ids) > 0) {
                                 $vs_sql = "\n\t\t\t\t\t\t\t\t\t\tDELETE FROM {$ps_dest_table} \n\t\t\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\t\t\trow_id IN (?)\n\t\t\t\t\t\t\t\t\t";
                                 if ($this->debug) {
                                     Debug::msg('NOT ' . $vs_sql);
                                 }
                                 $qr_res = $this->opo_db->query($vs_sql, array($va_ids));
                             }
                             $vn_i++;
                             break;
                         default:
                         case 'OR':
                             if ($vs_direct_sql_query) {
                                 $vs_direct_sql_query = str_replace('^JOIN', join("\n", $va_join), $vs_direct_sql_query);
                                 $pa_direct_sql_query_params = array($vn_direct_sql_target_table_num != $pn_subject_tablenum ? $vn_direct_sql_target_table_num : (int) $pn_subject_tablenum);
                             }
                             $vs_sql = $vs_direct_sql_query ? "INSERT IGNORE INTO {$ps_dest_table} {$vs_direct_sql_query}" : "\n\t\t\t\t\t\t\t\t\tINSERT IGNORE INTO {$ps_dest_table}\n\t\t\t\t\t\t\t\t\tSELECT swi.row_id, SUM(swi.boost)\n\t\t\t\t\t\t\t\t\tFROM ca_sql_search_word_index swi\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_sql_search_words AS sw ON sw.word_id = swi.word_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_sql_where}\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tswi.table_num = ?\n\t\t\t\t\t\t\t\t\t\t{$vs_rel_type_id_sql}\n\t\t\t\t\t\t\t\t\t\t" . ($this->getOption('omitPrivateIndexing') ? " AND swi.access = 0" : '') . "\n\t\t\t\t\t\t\t\t\tGROUP BY\n\t\t\t\t\t\t\t\t\t\tswi.row_id\n\t\t\t\t\t\t\t\t";
                             if ($this->debug) {
                                 Debug::msg('OR ' . $vs_sql);
                             }
                             $vn_i++;
                             $pa_direct_sql_query_params = is_array($pa_direct_sql_query_params) ? $pa_direct_sql_query_params : array((int) $pn_subject_tablenum);
                             if (strpos($vs_sql, '?') === false) {
                                 $pa_direct_sql_query_params = array();
                             }
                             $qr_res = $this->opo_db->query($vs_sql, $pa_direct_sql_query_params);
                             break;
                     }
                 }
                 // Drop any temporary tables created by direct search queries
                 foreach (array_keys($va_direct_query_temp_tables) as $vs_temp_table_to_drop) {
                     $this->_dropTempTable($vs_temp_table_to_drop);
                 }
                 break;
             default:
                 //print get_class($o_lucene_query_element);
                 break;
         }
     }
 }
/**
 * Convert text into string suitable for sorting, by moving articles to end of string, etc.
 *
 * @param string $ps_text Text to convert to sortable value
 * @param array $pa_options Options include:
 *		locale = Locale settings to use. If omitted current default locale is used. [Default is current locale]
 *
 * @return string Converted text. If locale cannot be found $ps_text is returned unchanged.
 */
function caSortableValue($ps_text, $pa_options = null)
{
    global $g_ui_locale;
    $ps_locale = caGetOption('locale', $pa_options, $g_ui_locale);
    if (!$ps_locale) {
        return $ps_text;
    }
    $o_locale_settings = TimeExpressionParser::getSettingsForLanguage($ps_locale);
    $vs_display_value = trim(preg_replace('![^\\p{L}0-9 ]+!u', ' ', $ps_text));
    $va_definite_articles = $o_locale_settings->get('definiteArticles');
    $va_indefinite_articles = $o_locale_settings->get('indefiniteArticles');
    foreach (array($va_definite_articles, $va_indefinite_articles) as $va_articles) {
        if (is_array($va_articles)) {
            foreach ($va_articles as $vs_article) {
                if (preg_match('!^(' . $vs_article . ')[ ]+!i', $vs_display_value, $va_matches)) {
                    $vs_display_value = trim(str_replace($va_matches[1], '', $vs_display_value) . ', ' . $va_matches[1]);
                    break 2;
                }
            }
        }
    }
    return $vs_display_value;
}
 /**
  * Returns list of items in the specified table related to the currently loaded row or rows specified in options.
  * 
  * @param $pm_rel_table_name_or_num - the table name or table number of the item type you want to get a list of (eg. if you are calling this on an ca_objects instance passing 'ca_entities' here will get you a list of entities related to the object)
  * @param $pa_options - array of options. Supported options are:
  *
  *		[Options controlling rows for which data is returned]
  *			row_ids = Array of primary key values to use when fetching related items. If omitted or set to a null value the 'row_id' option will be used. [Default is null]
  *			row_id = Primary key value to use when fetching related items. If omitted or set to a false value (null, false, 0) then the primary key value of the currently loaded row is used. [Default is currently loaded row]
  *			start = Zero-based index to begin return set at. [Default is 0]
  *			limit = Maximum number of related items to return. [Default is 1000]
  *			showDeleted = Return related items that have been deleted. [Default is false]
  *			primaryIDs = array of primary keys in related table to exclude from returned list of items. Array is keyed on table name for compatibility with the parameter as used in the caProcessTemplateForIDs() helper [Default is null - nothing is excluded].
  *			restrictToBundleValues = Restrict returned items to those with specified bundle values. Specify an associative array with keys set to bundle names and key values set to arrays of values to filter on (eg. [bundle_name1 => [value1, value2, ...]]). [Default is null]
  *			where = Restrict returned items to specified field values. The fields must be intrinsic and in the related table. This option can be useful when you want to efficiently fetch specific rows from a related table. Note that multiple fields/values are logically AND'ed together – all must match for a row to be returned - and that only equivalence is supported. [Default is null]			
  *			criteria = Restrict returned items using SQL criteria appended directly onto the query. Criteria is used as-is and must be compatible with the generated SQL query. [Default is null]
  *			showCurrentOnly = Returns only relationships with the latest effective date for their row_id that is not greater than the current date. Note that effective dates are treated as point dates, not ranges, when analyzed for "current-ness". That is, the more recent dates in the past, even if the "end" date is in the past are treated as current. This option is only supported for standard many-many non-self relations and is ignored for all other kinds of relationships. [Default is false]
  *			currentOnly = Synonym for showCurrentOnly
  *		
  *		[Options controlling scope of data in return value]
  *			restrictToTypes = Restrict returned items to those of the specified types. An array of list item idnos and/or item_ids may be specified. [Default is null]
  *			restrictToRelationshipTypes =  Restrict returned items to those related using the specified relationship types. An array of relationship type idnos and/or type_ids may be specified. [Default is null]
  *			excludeTypes = Restrict returned items to those *not* of the specified types. An array of list item idnos and/or item_ids may be specified. [Default is null]
  *			excludeRelationshipTypes = Restrict returned items to those *not* related using the specified relationship types. An array of relationship type idnos and/or type_ids may be specified. [Default is null]
  *			restrictToType = Synonym for restrictToTypes. [Default is null]
  *			restrictToRelationshipType = Synonym for restrictToRelationshipTypes. [Default is null]
  *			excludeType = Synonym for excludeTypes. [Default is null]
  *			excludeRelationshipType = Synonym for excludeRelationshipTypes. [Default is null]
  *			restrictToLists = Restrict returned items to those that are in one or more specified lists. This option is only relevant when fetching related ca_list_items. An array of list list_codes or list_ids may be specified. [Default is null]
  * 			fields = array of fields (in table.fieldname format) to include in returned data. [Default is null]
  *			returnNonPreferredLabels = Return non-preferred labels in returned data. [Default is false]
  *			returnLabelsAsArray = Return all labels associated with row in an array, rather than as a text value in the current locale. [Default is false]
  *			dontReturnLabels = Don't include labels in returned data. [Default is false]
  *			idsOnly = Return one-dimensional array of related primary key values only. [Default is false]
  *
  *		[Options controlling format of data in return value]
  *			useLocaleCodes = Return locale values as codes (Ex. en_US) rather than numeric database-specific locale_ids. [Default is false]
  *			sort = Array list of bundles to sort returned values on. The sortable bundle specifiers are fields with or without tablename. Only those fields returned for the related table (intrinsics, attributes and label fields) are sortable. [Default is null]
  *			sortDirection = Direction of sort. Use "asc" (ascending) or "desc" (descending). [Default is asc]
  *			groupFields = Groups together fields in an arrangement that is easier for import to another system. Used by the ItemInfo web service when in "import" mode. [Default is false]
  *
  *		[Front-end access control]	
  *			checkAccess = Array of access values to filter returned values on. Available for any related table with an "access" field (ca_objects, ca_entities, etc.). If omitted no filtering is performed. [Default is null]
  *			user_id = Perform item level access control relative to specified user_id rather than currently logged in user. [Default is user_id for currently logged in user]
  *
  *		[Options controlling format of data in return value]
  *			returnAs = format of return value; possible values are:
  *				data					= return array of data about each related item [default]
  *				searchResult			= a search result instance (aka. a subclass of BaseSearchResult) 
  *				ids						= an array of ids (aka. primary keys); same as setting the 'idsOnly' option
  *				modelInstances			= an array of instances, one for each match. Each instance is the  class of the related item, a subclass of BaseModel 
  *				firstId					= the id (primary key) of the first match. This is the same as the first item in the array returned by 'ids'
  *				firstModelInstance		= the instance of the first match. This is the same as the first instance in the array returned by 'modelInstances'
  *				count					= the number of related items
  *
  *					Default is "data" - returns a list of arrays with data about each related item
  *
  * @return array List of related items
  */
 public function getRelatedItems($pm_rel_table_name_or_num, $pa_options = null)
 {
     global $AUTH_CURRENT_USER_ID;
     $vn_user_id = isset($pa_options['user_id']) && $pa_options['user_id'] ? $pa_options['user_id'] : (int) $AUTH_CURRENT_USER_ID;
     $vb_show_if_no_acl = (bool) ($this->getAppConfig()->get('default_item_access_level') > __CA_ACL_NO_ACCESS__);
     if (caGetOption('idsOnly', $pa_options, false)) {
         $pa_options['returnAs'] = 'ids';
     }
     // 'idsOnly' is synonym for returnAs => 'ids'
     $ps_return_as = caGetOption('returnAs', $pa_options, 'data', array('forceLowercase' => true, 'validValues' => array('data', 'searchResult', 'ids', 'modelInstances', 'firstId', 'firstModelInstance', 'count')));
     // convert options
     if (($pa_options['restrictToTypes'] = caGetOption(array('restrictToTypes', 'restrict_to_types', 'restrictToType', 'restrict_to_type'), $pa_options, null)) && !is_array($pa_options['restrictToTypes'])) {
         $pa_options['restrictToTypes'] = array($pa_options['restrictToTypes']);
     }
     if (($pa_options['restrictToRelationshipTypes'] = caGetOption(array('restrictToRelationshipTypes', 'restrict_to_relationship_types', 'restrictToRelationshipType', 'restrict_to_relationship_type'), $pa_options, null)) && !is_array($pa_options['restrictToRelationshipTypes'])) {
         $pa_options['restrictToRelationshipTypes'] = array($pa_options['restrictToRelationshipTypes']);
     }
     if (($pa_options['excludeTypes'] = caGetOption(array('excludeTypes', 'exclude_types', 'excludeType', 'exclude_type'), $pa_options, null)) && !is_array($pa_options['excludeTypes'])) {
         $pa_options['excludeTypes'] = array($pa_options['excludeTypes']);
     }
     if (($pa_options['excludeRelationshipTypes'] = caGetOption(array('excludeRelationshipTypes', 'exclude_relationship_types', 'excludeRelationshipType', 'exclude_relationship_type'), $pa_options, null)) && !is_array($pa_options['excludeRelationshipTypes'])) {
         $pa_options['excludeRelationshipTypes'] = array($pa_options['excludeRelationshipTypes']);
     }
     if (!isset($pa_options['dontIncludeSubtypesInTypeRestriction']) && (isset($pa_options['dont_include_subtypes_in_type_restriction']) && $pa_options['dont_include_subtypes_in_type_restriction'])) {
         $pa_options['dontIncludeSubtypesInTypeRestriction'] = $pa_options['dont_include_subtypes_in_type_restriction'];
     }
     if (!isset($pa_options['returnNonPreferredLabels']) && (isset($pa_options['restrict_to_type']) && $pa_options['restrict_to_type'])) {
         $pa_options['returnNonPreferredLabels'] = $pa_options['restrict_to_type'];
     }
     if (!isset($pa_options['returnLabelsAsArray']) && (isset($pa_options['return_labels_as_array']) && $pa_options['return_labels_as_array'])) {
         $pa_options['returnLabelsAsArray'] = $pa_options['return_labels_as_array'];
     }
     if (!isset($pa_options['restrictToLists']) && (isset($pa_options['restrict_to_lists']) && $pa_options['restrict_to_lists'])) {
         $pa_options['restrictToLists'] = $pa_options['restrict_to_lists'];
     }
     $pb_group_fields = isset($pa_options['groupFields']) ? $pa_options['groupFields'] : false;
     $pa_primary_ids = isset($pa_options['primaryIDs']) && is_array($pa_options['primaryIDs']) ? $pa_options['primaryIDs'] : null;
     $pb_show_current_only = caGetOption('showCurrentOnly', $pa_options, caGetOption('currentOnly', $pa_options, false));
     if (!isset($pa_options['useLocaleCodes']) && (isset($pa_options['returnLocaleCodes']) && $pa_options['returnLocaleCodes'])) {
         $pa_options['useLocaleCodes'] = $pa_options['returnLocaleCodes'];
     }
     $pb_use_locale_codes = isset($pa_options['useLocaleCodes']) ? $pa_options['useLocaleCodes'] : false;
     $pa_get_where = isset($pa_options['where']) && is_array($pa_options['where']) && sizeof($pa_options['where']) ? $pa_options['where'] : null;
     $pa_row_ids = isset($pa_options['row_ids']) && is_array($pa_options['row_ids']) ? $pa_options['row_ids'] : null;
     $pn_row_id = isset($pa_options['row_id']) && $pa_options['row_id'] ? $pa_options['row_id'] : $this->getPrimaryKey();
     $o_db = $this->getDb();
     $t_locale = $this->getLocaleInstance();
     $o_tep = $this->getTimeExpressionParser();
     $vb_uses_effective_dates = false;
     if (isset($pa_options['sort']) && !is_array($pa_options['sort'])) {
         $pa_options['sort'] = array($pa_options['sort']);
     }
     $pa_sort_fields = isset($pa_options['sort']) && is_array($pa_options['sort']) ? array_filter($pa_options['sort'], "strlen") : null;
     $ps_sort_direction = isset($pa_options['sortDirection']) && $pa_options['sortDirection'] ? $pa_options['sortDirection'] : null;
     if (!$pa_row_ids && $pn_row_id > 0) {
         $pa_row_ids = array($pn_row_id);
     }
     if (!$pa_row_ids || !is_array($pa_row_ids) || !sizeof($pa_row_ids)) {
         return array();
     }
     $pb_return_labels_as_array = isset($pa_options['returnLabelsAsArray']) && $pa_options['returnLabelsAsArray'] ? true : false;
     $pn_limit = isset($pa_options['limit']) && (int) $pa_options['limit'] > 0 ? (int) $pa_options['limit'] : 1000;
     $pn_start = isset($pa_options['start']) && (int) $pa_options['start'] > 0 ? (int) $pa_options['start'] : 0;
     if (is_numeric($pm_rel_table_name_or_num)) {
         if (!($vs_related_table_name = $this->getAppDatamodel()->getTableName($pm_rel_table_name_or_num))) {
             return null;
         }
     } else {
         if (sizeof($va_tmp = explode(".", $pm_rel_table_name_or_num)) > 1) {
             $pm_rel_table_name_or_num = array_shift($va_tmp);
         }
         if (!($o_instance = $this->getAppDatamodel()->getInstanceByTableName($pm_rel_table_name_or_num, true))) {
             return null;
         }
         $vs_related_table_name = $pm_rel_table_name_or_num;
     }
     if (!is_array($pa_options)) {
         $pa_options = array();
     }
     $vb_is_combo_key_relation = false;
     // indicates relation is via table_num/row_id combination key
     $vs_subject_table_name = $this->tableName();
     $vs_item_rel_table_name = $vs_rel_item_table_name = null;
     switch (sizeof($va_path = array_keys($this->getAppDatamodel()->getPath($vs_subject_table_name, $vs_related_table_name)))) {
         case 3:
             $t_item_rel = $this->getAppDatamodel()->getTableInstance($va_path[1]);
             $vs_item_rel_table_name = $t_item_rel->tableName();
             $t_rel_item = $this->getAppDatamodel()->getTableInstance($va_path[2]);
             $vs_rel_item_table_name = $t_rel_item->tableName();
             $vs_key = $t_item_rel->primaryKey();
             //'relation_id';
             break;
         case 2:
             $t_item_rel = $this->isRelationship() ? $this : null;
             $vs_item_rel_table_name = $t_item_rel ? $t_item_rel->tableName() : null;
             $t_rel_item = $this->getAppDatamodel()->getTableInstance($va_path[1]);
             $vs_rel_item_table_name = $t_rel_item->tableName();
             $vs_key = $t_rel_item->primaryKey();
             break;
         default:
             // is this related with row_id/table_num combo?
             if (($t_rel_item = $this->getAppDatamodel()->getTableInstance($vs_related_table_name)) && $t_rel_item->hasField('table_num') && $t_rel_item->hasField('row_id')) {
                 $vs_key = $t_rel_item->primaryKey();
                 $vs_rel_item_table_name = $t_rel_item->tableName();
                 $vb_is_combo_key_relation = true;
                 $va_path = array($vs_subject_table_name, $vs_rel_item_table_name);
             } else {
                 // bad related table
                 return null;
             }
             break;
     }
     // check for self relationship
     $vb_self_relationship = false;
     if ($vs_subject_table_name == $vs_related_table_name) {
         $vb_self_relationship = true;
         $t_item_rel = $this->getAppDatamodel()->getTableInstance($va_path[1]);
         $vs_item_rel_table_name = $t_item_rel->tableName();
         $t_rel_item = $this->getAppDatamodel()->getTableInstance($va_path[0]);
         $vs_rel_item_table_name = $t_rel_item->tableName();
     }
     $va_wheres = array();
     $va_selects = array();
     $va_joins_post_add = array();
     $vs_related_table = $vs_rel_item_table_name;
     if ($t_rel_item->hasField('type_id')) {
         $va_selects[] = "{$vs_related_table}.type_id item_type_id";
     }
     if ($t_rel_item->hasField('source_id')) {
         $va_selects[] = "{$vs_related_table}.source_id item_source_id";
     }
     // TODO: get these field names from models
     if (($t_tmp = $t_item_rel) || $t_rel_item->isRelationship() && ($t_tmp = $t_rel_item)) {
         //define table names
         $vs_linking_table = $t_tmp->tableName();
         $va_selects[] = "{$vs_related_table}." . $t_rel_item->primaryKey();
         // include dates in returned data
         if ($t_tmp->hasField('effective_date')) {
             $va_selects[] = $vs_linking_table . '.sdatetime';
             $va_selects[] = $vs_linking_table . '.edatetime';
             $vb_uses_effective_dates = true;
         }
         if ($t_rel_item->hasField('is_enabled')) {
             $va_selects[] = "{$vs_related_table}.is_enabled";
         }
         if ($t_tmp->hasField('type_id')) {
             $va_selects[] = $vs_linking_table . '.type_id relationship_type_id';
             require_once __CA_MODELS_DIR__ . '/ca_relationship_types.php';
             $t_rel = new ca_relationship_types();
             $vb_uses_relationship_types = true;
         }
         // limit related items to a specific type
         if ($vb_uses_relationship_types && isset($pa_options['restrictToRelationshipTypes']) && $pa_options['restrictToRelationshipTypes']) {
             if (!is_array($pa_options['restrictToRelationshipTypes'])) {
                 $pa_options['restrictToRelationshipTypes'] = array($pa_options['restrictToRelationshipTypes']);
             }
             if (sizeof($pa_options['restrictToRelationshipTypes'])) {
                 $va_rel_types = array();
                 foreach ($pa_options['restrictToRelationshipTypes'] as $vm_type) {
                     if (!$vm_type) {
                         continue;
                     }
                     if (!($vn_type_id = $t_rel->getRelationshipTypeID($vs_linking_table, $vm_type))) {
                         $vn_type_id = (int) $vm_type;
                     }
                     if ($vn_type_id > 0) {
                         $va_rel_types[] = $vn_type_id;
                         if (is_array($va_children = $t_rel->getHierarchyChildren($vn_type_id, array('idsOnly' => true)))) {
                             $va_rel_types = array_merge($va_rel_types, $va_children);
                         }
                     }
                 }
                 if (sizeof($va_rel_types)) {
                     $va_wheres[] = '(' . $vs_linking_table . '.type_id IN (' . join(',', $va_rel_types) . '))';
                 }
             }
         }
         if ($vb_uses_relationship_types && isset($pa_options['excludeRelationshipTypes']) && $pa_options['excludeRelationshipTypes']) {
             if (!is_array($pa_options['excludeRelationshipTypes'])) {
                 $pa_options['excludeRelationshipTypes'] = array($pa_options['excludeRelationshipTypes']);
             }
             if (sizeof($pa_options['excludeRelationshipTypes'])) {
                 $va_rel_types = array();
                 foreach ($pa_options['excludeRelationshipTypes'] as $vm_type) {
                     if ($vn_type_id = $t_rel->getRelationshipTypeID($vs_linking_table, $vm_type)) {
                         $va_rel_types[] = $vn_type_id;
                         if (is_array($va_children = $t_rel->getHierarchyChildren($vn_type_id, array('idsOnly' => true)))) {
                             $va_rel_types = array_merge($va_rel_types, $va_children);
                         }
                     }
                 }
                 if (sizeof($va_rel_types)) {
                     $va_wheres[] = '(' . $vs_linking_table . '.type_id NOT IN (' . join(',', $va_rel_types) . '))';
                 }
             }
         }
     }
     // limit related items to a specific type
     $va_type_ids = caMergeTypeRestrictionLists($t_rel_item, $pa_options);
     if (is_array($va_type_ids) && sizeof($va_type_ids) > 0) {
         $va_wheres[] = "({$vs_related_table}.type_id IN (" . join(',', $va_type_ids) . ')' . ($t_rel_item->getFieldInfo('type_id', 'IS_NULL') ? " OR ({$vs_related_table}.type_id IS NULL)" : '') . ')';
     }
     $va_source_ids = caMergeSourceRestrictionLists($t_rel_item, $pa_options);
     if (method_exists($t_rel_item, "getSourceFieldName") && ($vs_source_id_fld = $t_rel_item->getSourceFieldName()) && is_array($va_source_ids) && sizeof($va_source_ids) > 0) {
         $va_wheres[] = "({$vs_related_table}.{$vs_source_id_fld} IN (" . join(',', $va_source_ids) . "))";
     }
     if (isset($pa_options['excludeType']) && $pa_options['excludeType']) {
         if (!isset($pa_options['excludeTypes']) || !is_array($pa_options['excludeTypes'])) {
             $pa_options['excludeTypes'] = array();
         }
         $pa_options['excludeTypes'][] = $pa_options['excludeType'];
     }
     if (isset($pa_options['excludeTypes']) && is_array($pa_options['excludeTypes'])) {
         $va_type_ids = caMakeTypeIDList($vs_related_table, $pa_options['excludeTypes']);
         if (is_array($va_type_ids) && sizeof($va_type_ids) > 0) {
             $va_wheres[] = "({$vs_related_table}.type_id NOT IN (" . join(',', $va_type_ids) . "))";
         }
     }
     if ($this->getAppConfig()->get('perform_item_level_access_checking')) {
         $t_user = new ca_users($vn_user_id, true);
         if (is_array($va_groups = $t_user->getUserGroups()) && sizeof($va_groups)) {
             $va_group_ids = array_keys($va_groups);
         } else {
             $va_group_ids = array();
         }
         // Join to limit what browse table items are used to generate facet
         $va_joins_post_add[] = 'LEFT JOIN ca_acl ON ' . $vs_related_table_name . '.' . $t_rel_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_rel_item->tableNum() . "\n";
         $va_wheres[] = "(\n\t\t\t\t((\n\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\tOR\n\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t)";
     }
     if (is_array($pa_get_where)) {
         foreach ($pa_get_where as $vs_fld => $vm_val) {
             if ($t_rel_item->hasField($vs_fld)) {
                 $va_wheres[] = "({$vs_related_table_name}.{$vs_fld} = " . (!is_numeric($vm_val) ? "'" . $this->getDb()->escape($vm_val) . "'" : $vm_val) . ")";
             }
         }
     }
     if ($vs_idno_fld = $t_rel_item->getProperty('ID_NUMBERING_ID_FIELD')) {
         $va_selects[] = "{$vs_related_table}.{$vs_idno_fld}";
     }
     if ($vs_idno_sort_fld = $t_rel_item->getProperty('ID_NUMBERING_SORT_FIELD')) {
         $va_selects[] = "{$vs_related_table}.{$vs_idno_sort_fld}";
     }
     $va_selects[] = $va_path[1] . '.' . $vs_key;
     if (isset($pa_options['fields']) && is_array($pa_options['fields'])) {
         $va_selects = array_merge($va_selects, $pa_options['fields']);
     }
     // if related item is labelable then include the label table in the query as well
     $vs_label_display_field = null;
     if (method_exists($t_rel_item, "getLabelTableName") && (!isset($pa_options['dontReturnLabels']) || !$pa_options['dontReturnLabels'])) {
         if ($vs_label_table_name = $t_rel_item->getLabelTableName()) {
             // make sure it actually has a label table...
             $va_path[] = $vs_label_table_name;
             $t_rel_item_label = $this->getAppDatamodel()->getTableInstance($vs_label_table_name);
             $vs_label_display_field = $t_rel_item_label->getDisplayField();
             if ($pb_return_labels_as_array || is_array($pa_sort_fields) && sizeof($pa_sort_fields)) {
                 $va_selects[] = $vs_label_table_name . '.*';
             } else {
                 $va_selects[] = $vs_label_table_name . '.' . $vs_label_display_field;
                 $va_selects[] = $vs_label_table_name . '.locale_id';
                 if ($t_rel_item_label->hasField('surname')) {
                     // hack to include fields we need to sort entity labels properly
                     $va_selects[] = $vs_label_table_name . '.surname';
                     $va_selects[] = $vs_label_table_name . '.forename';
                 }
             }
             if ($t_rel_item_label->hasField('is_preferred') && (!isset($pa_options['returnNonPreferredLabels']) || !$pa_options['returnNonPreferredLabels'])) {
                 $va_wheres[] = "(" . $vs_label_table_name . '.is_preferred = 1)';
             }
         }
     }
     // return source info in returned data
     if ($t_item_rel && $t_item_rel->hasField('source_info')) {
         $va_selects[] = $vs_linking_table . '.source_info';
     }
     if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access')) {
         $va_wheres[] = "({$vs_related_table}.access IN (" . join(',', $pa_options['checkAccess']) . "))";
     }
     if ((!isset($pa_options['showDeleted']) || !$pa_options['showDeleted']) && $t_rel_item->hasField('deleted')) {
         $va_wheres[] = "({$vs_related_table}.deleted = 0)";
     }
     if (($va_criteria = isset($pa_options['criteria']) ? $pa_options['criteria'] : null) && is_array($va_criteria) && sizeof($va_criteria)) {
         $va_wheres[] = "(" . join(" AND ", $va_criteria) . ")";
     }
     if ($vb_self_relationship) {
         //
         // START - traverse self relation
         //
         $va_rel_info = $this->getAppDatamodel()->getRelationships($va_path[0], $va_path[1]);
         if ($vs_label_table_name) {
             $va_label_rel_info = $this->getAppDatamodel()->getRelationships($va_path[0], $vs_label_table_name);
         }
         $va_rels = array();
         $vn_i = 0;
         foreach ($va_rel_info[$va_path[0]][$va_path[1]] as $va_possible_keys) {
             $va_joins = array();
             $va_joins[] = "INNER JOIN " . $va_path[1] . " ON " . $va_path[1] . '.' . $va_possible_keys[1] . ' = ' . $va_path[0] . '.' . $va_possible_keys[0] . "\n";
             if ($vs_label_table_name) {
                 $va_joins[] = "INNER JOIN " . $vs_label_table_name . " ON " . $vs_label_table_name . '.' . $va_label_rel_info[$va_path[0]][$vs_label_table_name][0][1] . ' = ' . $va_path[0] . '.' . $va_label_rel_info[$va_path[0]][$vs_label_table_name][0][0] . "\n";
             }
             $vs_other_field = $vn_i == 0 ? $va_rel_info[$va_path[0]][$va_path[1]][1][1] : $va_rel_info[$va_path[0]][$va_path[1]][0][1];
             $vs_direction = preg_match('!left!', $vs_other_field) ? 'ltor' : 'rtol';
             $va_selects['row_id'] = $va_path[1] . '.' . $vs_other_field . ' AS row_id';
             $vs_order_by = '';
             $vs_sort_fld = '';
             if ($t_item_rel && $t_item_rel->hasField('rank')) {
                 $vs_order_by = " ORDER BY {$vs_item_rel_table_name}.rank";
                 $vs_sort_fld = 'rank';
                 $va_selects[] = "{$vs_item_rel_table_name}.rank";
             } else {
                 if ($t_rel_item && ($vs_sort = $t_rel_item->getProperty('ID_NUMBERING_SORT_FIELD'))) {
                     $vs_order_by = " ORDER BY {$vs_related_table}.{$vs_sort}";
                     $vs_sort_fld = $vs_sort;
                     $va_selects[] = "{$vs_related_table}.{$vs_sort}";
                 }
             }
             $vs_sql = "\n\t\t\t\t\tSELECT " . join(', ', $va_selects) . "\n\t\t\t\t\tFROM " . $va_path[0] . "\n\t\t\t\t\t" . join("\n", array_merge($va_joins, $va_joins_post_add)) . "\n\t\t\t\t\tWHERE\n\t\t\t\t\t\t" . join(' AND ', array_merge($va_wheres, array('(' . $va_path[1] . '.' . $vs_other_field . ' IN (' . join(',', $pa_row_ids) . '))'))) . "\n\t\t\t\t\t{$vs_order_by}";
             $qr_res = $o_db->query($vs_sql);
             if ($vb_uses_relationship_types) {
                 $va_rel_types = $t_rel->getRelationshipInfo($va_path[1]);
             }
             $vn_c = 0;
             if ($pn_start > 0) {
                 $qr_res->seek($pn_start);
             }
             while ($qr_res->nextRow()) {
                 if ($vn_c >= $pn_limit) {
                     break;
                 }
                 if (is_array($pa_primary_ids) && is_array($pa_primary_ids[$vs_related_table])) {
                     if (in_array($qr_res->get($vs_key), $pa_primary_ids[$vs_related_table])) {
                         continue;
                     }
                 }
                 if ($ps_return_as !== 'data') {
                     $va_rels[] = $qr_res->get($t_rel_item->primaryKey());
                     continue;
                 }
                 $va_row = $qr_res->getRow();
                 $vn_id = $va_row[$vs_key] . '/' . $va_row['row_id'];
                 $vs_sort_key = $qr_res->get($vs_sort_fld);
                 $vs_display_label = $va_row[$vs_label_display_field];
                 if (!$va_rels[$vs_sort_key][$vn_id]) {
                     $va_rels[$vs_sort_key][$vn_id] = $qr_res->getRow();
                 }
                 if ($vb_uses_effective_dates) {
                     // return effective dates as display/parse-able text
                     if ($va_rels[$vs_sort_key][$vn_id]['sdatetime'] || $va_rels[$vs_sort_key][$vn_id]['edatetime']) {
                         $o_tep->setHistoricTimestamps($va_rels[$vs_sort_key][$vn_id]['sdatetime'], $va_rels[$vs_sort_key][$vn_id]['edatetime']);
                         $va_rels[$vs_sort_key][$vn_id]['effective_date'] = $o_tep->getText();
                     }
                 }
                 $vn_locale_id = $qr_res->get('locale_id');
                 if ($pb_use_locale_codes) {
                     $va_rels[$vs_v]['locale_id'] = $vn_locale_id = $t_locale->localeIDToCode($vn_locale_id);
                 }
                 $va_rels[$vs_sort_key][$vn_id]['labels'][$vn_locale_id] = $pb_return_labels_as_array ? $va_row : $vs_display_label;
                 $va_rels[$vs_sort_key][$vn_id]['_key'] = $vs_key;
                 $va_rels[$vs_sort_key][$vn_id]['direction'] = $vs_direction;
                 $vn_c++;
                 if ($vb_uses_relationship_types) {
                     $va_rels[$vs_sort_key][$vn_id]['relationship_typename'] = $vs_direction == 'ltor' ? $va_rel_types[$va_row['relationship_type_id']]['typename'] : $va_rel_types[$va_row['relationship_type_id']]['typename_reverse'];
                     $va_rels[$vs_sort_key][$vn_id]['relationship_type_code'] = $va_rel_types[$va_row['relationship_type_id']]['type_code'];
                 }
                 //
                 // Return data in an arrangement more convenient for the data importer
                 //
                 if ($pb_group_fields) {
                     $vs_rel_pk = $t_rel_item->primaryKey();
                     if ($t_rel_item_label) {
                         foreach ($t_rel_item_label->getFormFields() as $vs_field => $va_field_info) {
                             if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                                 continue;
                             }
                             $va_rels[$vs_v]['preferred_labels'][$vs_field] = $va_rels[$vs_v][$vs_field];
                             unset($va_rels[$vs_v][$vs_field]);
                         }
                     }
                     foreach ($t_rel_item->getFormFields() as $vs_field => $va_field_info) {
                         if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                             continue;
                         }
                         $va_rels[$vs_v]['intrinsic'][$vs_field] = $va_rels[$vs_v][$vs_field];
                         unset($va_rels[$vs_v][$vs_field]);
                     }
                     unset($va_rels[$vs_v]['_key']);
                     unset($va_rels[$vs_v]['row_id']);
                 }
             }
             $vn_i++;
         }
         ksort($va_rels);
         // sort by sort key... we'll remove the sort key in the next loop while we add the labels
         // Set 'label' entry - display label in current user's locale
         $va_sorted_rels = array();
         foreach ($va_rels as $vs_sort_key => $va_rels_by_sort_key) {
             foreach ($va_rels_by_sort_key as $vn_id => $va_rel) {
                 $va_tmp = array(0 => $va_rel['labels']);
                 $va_sorted_rels[$vn_id] = $va_rel;
                 $va_values_filtered_by_locale = caExtractValuesByUserLocale($va_tmp);
                 $va_sorted_rels[$vn_id]['label'] = array_shift($va_values_filtered_by_locale);
             }
         }
         $va_rels = $va_sorted_rels;
         //
         // END - traverse self relation
         //
     } else {
         if (method_exists($this, 'isSelfRelationship') && $this->isSelfRelationship()) {
             //
             // START - from self relation itself (Eg. get related ca_objects from ca_objects_x_objects); in this case there are two possible paths (keys) to check, "left" and "right"
             //
             $va_wheres[] = "({$vs_subject_table_name}." . $this->primaryKey() . " IN (" . join(",", $pa_row_ids) . "))";
             $vs_cur_table = array_shift($va_path);
             $vs_rel_table = array_shift($va_path);
             $va_rel_info = $this->getAppDatamodel()->getRelationships($vs_cur_table, $vs_rel_table);
             $va_rels = array();
             foreach ($va_rel_info[$vs_cur_table][$vs_rel_table] as $vn_i => $va_rel) {
                 $va_joins = array('INNER JOIN ' . $vs_rel_table . ' ON ' . $vs_cur_table . '.' . $va_rel[0] . ' = ' . $vs_rel_table . '.' . $va_rel[1] . "\n");
                 $vs_base_table = $vs_rel_table;
                 foreach ($va_path as $vs_join_table) {
                     $va_label_rel_info = $this->getAppDatamodel()->getRelationships($vs_base_table, $vs_join_table);
                     $va_joins[] = 'INNER JOIN ' . $vs_join_table . ' ON ' . $vs_base_table . '.' . $va_label_rel_info[$vs_base_table][$vs_join_table][0][0] . ' = ' . $vs_join_table . '.' . $va_label_rel_info[$vs_base_table][$vs_join_table][0][1] . "\n";
                     $vs_base_table = $vs_join_table;
                 }
                 $va_selects[] = $vs_subject_table_name . '.' . $this->primaryKey() . ' AS row_id';
                 $vs_order_by = '';
                 if ($t_item_rel && $t_item_rel->hasField('rank')) {
                     $vs_order_by = " ORDER BY {$vs_item_rel_table_name}.rank";
                     $va_selects[] = $t_item_rel->tableName() . '.rank';
                 } else {
                     if ($t_rel_item && ($vs_sort = $t_rel_item->getProperty('ID_NUMBERING_SORT_FIELD'))) {
                         $vs_order_by = " ORDER BY {$vs_related_table}.{$vs_sort}";
                         $va_selects[] = "{$vs_related_table}.{$vs_sort}";
                     }
                 }
                 $vs_sql = "\n\t\t\t\t\tSELECT DISTINCT " . join(', ', $va_selects) . "\n\t\t\t\t\tFROM {$vs_subject_table_name}\n\t\t\t\t\t" . join("\n", array_merge($va_joins, $va_joins_post_add)) . "\n\t\t\t\t\tWHERE\n\t\t\t\t\t\t" . join(' AND ', $va_wheres) . "\n\t\t\t\t\t{$vs_order_by}\n\t\t\t\t";
                 //print "<pre>$vs_sql</pre>\n";
                 $qr_res = $o_db->query($vs_sql);
                 if ($vb_uses_relationship_types) {
                     $va_rel_types = $t_rel->getRelationshipInfo($vs_item_rel_table_name);
                     $vs_left_table = $t_item_rel->getLeftTableName();
                     $vs_direction = $vs_left_table == $vs_subject_table_name ? 'ltor' : 'rtol';
                 }
                 $vn_c = 0;
                 if ($pn_start > 0) {
                     $qr_res->seek($pn_start);
                 }
                 while ($qr_res->nextRow()) {
                     if ($vn_c >= $pn_limit) {
                         break;
                     }
                     if (is_array($pa_primary_ids) && is_array($pa_primary_ids[$vs_related_table])) {
                         if (in_array($qr_res->get($vs_key), $pa_primary_ids[$vs_related_table])) {
                             continue;
                         }
                     }
                     if ($ps_return_as !== 'data') {
                         $va_rels[] = $qr_res->get($t_rel_item->primaryKey());
                         continue;
                     }
                     $va_row = $qr_res->getRow();
                     $vs_v = $va_row['row_id'] . '/' . $va_row[$vs_key];
                     $vs_display_label = $va_row[$vs_label_display_field];
                     if (!isset($va_rels[$vs_v]) || !$va_rels[$vs_v]) {
                         $va_rels[$vs_v] = $va_row;
                     }
                     if ($vb_uses_effective_dates) {
                         // return effective dates as display/parse-able text
                         if ($va_rels[$vs_v]['sdatetime'] || $va_rels[$vs_v]['edatetime']) {
                             $o_tep->setHistoricTimestamps($va_rels[$vs_v]['sdatetime'], $va_rels[$vs_v]['edatetime']);
                             $va_rels[$vs_v]['effective_date'] = $o_tep->getText();
                         }
                     }
                     $vn_locale_id = $qr_res->get('locale_id');
                     if ($pb_use_locale_codes) {
                         $va_rels[$vs_v]['locale_id'] = $vn_locale_id = $t_locale->localeIDToCode($vn_locale_id);
                     }
                     $va_rels[$vs_v]['labels'][$vn_locale_id] = $pb_return_labels_as_array ? $va_row : $vs_display_label;
                     $va_rels[$vs_v]['_key'] = $vs_key;
                     $va_rels[$vs_v]['direction'] = $vs_direction;
                     $vn_c++;
                     if ($vb_uses_relationship_types) {
                         $va_rels[$vs_v]['relationship_typename'] = $vs_direction == 'ltor' ? $va_rel_types[$va_row['relationship_type_id']]['typename'] : $va_rel_types[$va_row['relationship_type_id']]['typename_reverse'];
                         $va_rels[$vs_v]['relationship_type_code'] = $va_rel_types[$va_row['relationship_type_id']]['type_code'];
                     }
                     if ($pb_group_fields) {
                         $vs_rel_pk = $t_rel_item->primaryKey();
                         if ($t_rel_item_label) {
                             foreach ($t_rel_item_label->getFormFields() as $vs_field => $va_field_info) {
                                 if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                                     continue;
                                 }
                                 $va_rels[$vs_v]['preferred_labels'][$vs_field] = $va_rels[$vs_v][$vs_field];
                                 unset($va_rels[$vs_v][$vs_field]);
                             }
                         }
                         foreach ($t_rel_item->getFormFields() as $vs_field => $va_field_info) {
                             if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                                 continue;
                             }
                             $va_rels[$vs_v]['intrinsic'][$vs_field] = $va_rels[$vs_v][$vs_field];
                             unset($va_rels[$vs_v][$vs_field]);
                         }
                         unset($va_rels[$vs_v]['_key']);
                         unset($va_rels[$vs_v]['row_id']);
                     }
                 }
                 //if (!isset($pa_options['idsOnly']) || !$pa_options['idsOnly']) {
                 if ($ps_return_as === 'data') {
                     // Set 'label' entry - display label in current user's locale
                     foreach ($va_rels as $vs_v => $va_rel) {
                         $va_tmp = array(0 => $va_rel['labels']);
                         $va_tmp2 = caExtractValuesByUserLocale($va_tmp);
                         $va_rels[$vs_v]['label'] = array_shift($va_tmp2);
                     }
                 }
             }
             //
             // END - from self relation itself
             //
         } else {
             //
             // BEGIN - non-self relation
             //
             $va_wheres[] = "({$vs_subject_table_name}." . $this->primaryKey() . " IN (" . join(",", $pa_row_ids) . "))";
             $vs_cur_table = array_shift($va_path);
             $va_joins = array();
             // Enforce restrict_to_lists for related list items
             if ($vs_related_table_name == 'ca_list_items' && is_array($pa_options['restrictToLists'])) {
                 $va_list_ids = array();
                 foreach ($pa_options['restrictToLists'] as $vm_list) {
                     if ($vn_list_id = ca_lists::getListID($vm_list)) {
                         $va_list_ids[] = $vn_list_id;
                     }
                 }
                 if (sizeof($va_list_ids)) {
                     $va_wheres[] = "(ca_list_items.list_id IN (" . join(",", $va_list_ids) . "))";
                 }
             }
             if ($vb_is_combo_key_relation) {
                 $va_joins = array("INNER JOIN {$vs_related_table_name} ON {$vs_related_table_name}.row_id = " . $this->primaryKey(true) . " AND {$vs_related_table_name}.table_num = " . $this->tableNum());
             } else {
                 foreach ($va_path as $vs_join_table) {
                     $va_rel_info = $this->getAppDatamodel()->getRelationships($vs_cur_table, $vs_join_table);
                     $vs_join = 'INNER JOIN ' . $vs_join_table . ' ON ';
                     $va_tmp = array();
                     foreach ($va_rel_info[$vs_cur_table][$vs_join_table] as $vn_i => $va_rel) {
                         $va_tmp[] = $vs_cur_table . "." . $va_rel_info[$vs_cur_table][$vs_join_table][$vn_i][0] . ' = ' . $vs_join_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][$vn_i][1] . "\n";
                     }
                     $va_joins[] = $vs_join . join(' OR ', $va_tmp);
                     $vs_cur_table = $vs_join_table;
                 }
             }
             // If we're getting ca_set_items, we have to rename the intrinsic row_id field because the pk is named row_id below. Hence, this hack.
             if ($vs_related_table_name == 'ca_set_items') {
                 $va_selects[] = 'ca_set_items.row_id AS record_id';
             }
             $va_selects[] = $vs_subject_table_name . '.' . $this->primaryKey() . ' AS row_id';
             //
             // Filter to only current relationships
             //
             if ($pb_show_current_only && $vb_uses_effective_dates) {
                 // _filter_current.edatetime IS NULL criteria allows undated relationships to be considered "current"; if current filtering is done it
                 // is assumed that all relationships are dated and that undated relations are legacy and therefore should be considered potentially current
                 $vs_filter_to_current_join = "\n\t\t\t\t\tINNER JOIN (\n\t\t\t\t\t\tSELECT {$vs_item_rel_table_name}." . $this->primaryKey() . ", max({$vs_item_rel_table_name}.edatetime) edatetime\n\t\t\t\t\t\tFROM {$vs_subject_table_name}\n\t\t\t\t\t\t" . join("\n", array_merge($va_joins, $va_joins_post_add)) . "\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t(" . join(' AND ', array_merge($va_wheres, array("({$vs_item_rel_table_name}.sdatetime <= " . TimeExpressionParser::now() . ")"))) . ")\n\t\t\t\t\t\t\tOR ({$vs_item_rel_table_name}.sdatetime IS NULL)\n\t\t\t\t\t\tGROUP BY \n\t\t\t\t\t\t\t{$vs_item_rel_table_name}." . $this->primaryKey() . "\n\t\t\t\t\t) AS _filter_current ON _filter_current." . $this->primaryKey() . " = {$vs_item_rel_table_name}." . $this->primaryKey() . " AND (_filter_current.edatetime = {$vs_item_rel_table_name}.edatetime OR _filter_current.edatetime IS NULL)\n\t\t\t\t";
                 $va_joins[] = $vs_filter_to_current_join;
             }
             $vs_order_by = '';
             if ($t_item_rel && $t_item_rel->hasField('rank')) {
                 $vs_order_by = " ORDER BY {$vs_item_rel_table_name}.rank";
                 $va_selects[] = $t_item_rel->tableName() . '.rank';
             } else {
                 if ($t_rel_item && ($vs_sort = $t_rel_item->getProperty('ID_NUMBERING_SORT_FIELD'))) {
                     $vs_order_by = " ORDER BY {$vs_related_table}.{$vs_sort}";
                     $va_selects[] = "{$vs_related_table}.{$vs_sort}";
                 }
             }
             $vs_sql = "\n\t\t\t\tSELECT DISTINCT " . join(', ', $va_selects) . "\n\t\t\t\tFROM {$vs_subject_table_name}\n\t\t\t\t" . join("\n", array_merge($va_joins, $va_joins_post_add)) . "\n\t\t\t\tWHERE\n\t\t\t\t\t" . join(' AND ', $va_wheres) . "\n\t\t\t\t{$vs_order_by}\n\t\t\t";
             $qr_res = $o_db->query($vs_sql);
             if ($vb_uses_relationship_types) {
                 $va_rel_types = $t_rel->getRelationshipInfo($t_tmp->tableName());
                 if (method_exists($t_tmp, 'getLeftTableName')) {
                     $vs_left_table = $t_tmp->getLeftTableName();
                     $vs_direction = $vs_left_table == $vs_subject_table_name ? 'ltor' : 'rtol';
                 }
             }
             $va_rels = array();
             $vn_c = 0;
             if ($pn_start > 0) {
                 $qr_res->seek($pn_start);
             }
             $va_seen_row_ids = array();
             while ($qr_res->nextRow()) {
                 if ($vn_c >= $pn_limit) {
                     break;
                 }
                 if (is_array($pa_primary_ids) && is_array($pa_primary_ids[$vs_related_table])) {
                     if (in_array($qr_res->get($vs_key), $pa_primary_ids[$vs_related_table])) {
                         continue;
                     }
                 }
                 //if (isset($pa_options['idsOnly']) && $pa_options['idsOnly']) {
                 if ($ps_return_as !== 'data') {
                     $va_rels[] = $qr_res->get($t_rel_item->primaryKey());
                     continue;
                 }
                 $va_row = $qr_res->getRow();
                 $vs_v = sizeof($va_path) <= 2 ? $va_row['row_id'] . '/' . $va_row[$vs_key] : $va_row[$vs_key];
                 $vs_display_label = $va_row[$vs_label_display_field];
                 if (!isset($va_rels[$vs_v]) || !$va_rels[$vs_v]) {
                     $va_rels[$vs_v] = $va_row;
                 }
                 if ($vb_uses_effective_dates) {
                     // return effective dates as display/parse-able text
                     if ($va_rels[$vs_v]['sdatetime'] || $va_rels[$vs_v]['edatetime']) {
                         $o_tep->setHistoricTimestamps($va_rels[$vs_v]['sdatetime'], $va_rels[$vs_v]['edatetime']);
                         $va_rels[$vs_v]['effective_date'] = $o_tep->getText();
                     }
                     // Only allow one current item per row_id
                     //if ($pb_show_current_only && isset($va_seen_row_ids[$va_row['row_id']])) {
                     //	unset($va_rels[$vs_v]);
                     //	continue;
                     //}
                 }
                 $vn_locale_id = $qr_res->get('locale_id');
                 if ($pb_use_locale_codes) {
                     $va_rels[$vs_v]['locale_id'] = $vn_locale_id = $t_locale->localeIDToCode($vn_locale_id);
                 }
                 $va_rels[$vs_v]['labels'][$vn_locale_id] = $pb_return_labels_as_array ? $va_row : $vs_display_label;
                 $va_rels[$vs_v]['_key'] = $vs_key;
                 $va_rels[$vs_v]['direction'] = $vs_direction;
                 $vn_c++;
                 if ($vb_uses_relationship_types) {
                     $va_rels[$vs_v]['relationship_typename'] = $vs_direction == 'ltor' ? $va_rel_types[$va_row['relationship_type_id']]['typename'] : $va_rel_types[$va_row['relationship_type_id']]['typename_reverse'];
                     $va_rels[$vs_v]['relationship_type_code'] = $va_rel_types[$va_row['relationship_type_id']]['type_code'];
                 }
                 if ($pb_group_fields) {
                     $vs_rel_pk = $t_rel_item->primaryKey();
                     if ($t_rel_item_label) {
                         foreach ($t_rel_item_label->getFormFields() as $vs_field => $va_field_info) {
                             if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                                 continue;
                             }
                             $va_rels[$vs_v]['preferred_labels'][$vs_field] = $va_rels[$vs_v][$vs_field];
                             unset($va_rels[$vs_v][$vs_field]);
                         }
                     }
                     foreach ($t_rel_item->getFormFields() as $vs_field => $va_field_info) {
                         if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                             continue;
                         }
                         $va_rels[$vs_v]['intrinsic'][$vs_field] = $va_rels[$vs_v][$vs_field];
                         unset($va_rels[$vs_v][$vs_field]);
                     }
                     unset($va_rels[$vs_v]['_key']);
                     unset($va_rels[$vs_v]['row_id']);
                 }
                 $va_seen_row_ids[$va_row['row_id']] = true;
             }
             //if (!isset($pa_options['idsOnly']) || !$pa_options['idsOnly']) {
             if ($ps_return_as === 'data') {
                 // Set 'label' entry - display label in current user's locale
                 foreach ($va_rels as $vs_v => $va_rel) {
                     $va_tmp = array(0 => $va_rel['labels']);
                     $va_tmp2 = caExtractValuesByUserLocale($va_tmp);
                     $va_rels[$vs_v]['label'] = array_shift($va_tmp2);
                 }
             }
             //
             // END - non-self relation
             //
         }
     }
     // Apply restrictToBundleValues
     $va_filters = isset($pa_options['restrictToBundleValues']) ? $pa_options['restrictToBundleValues'] : null;
     if (is_array($va_filters) && sizeof($va_filters) > 0) {
         foreach ($va_rels as $vn_pk => $va_related_item) {
             foreach ($va_filters as $vs_filter => $va_filter_vals) {
                 if (!$vs_filter) {
                     continue;
                 }
                 if (!is_array($va_filter_vals)) {
                     $va_filter_vals = array($va_filter_vals);
                 }
                 foreach ($va_filter_vals as $vn_index => $vs_filter_val) {
                     // is value a list attribute idno?
                     $va_tmp = explode('.', $vs_filter);
                     $vs_element = array_pop($va_tmp);
                     if (!is_numeric($vs_filter_val) && (($t_element = $t_rel_item->_getElementInstance($vs_element)) && $t_element->get('datatype') == 3)) {
                         $va_filter_vals[$vn_index] = caGetListItemID($t_element->get('list_id'), $vs_filter_val);
                     }
                 }
                 $t_rel_item->load($va_related_item[$t_rel_item->primaryKey()]);
                 $va_filter_values = $t_rel_item->get($vs_filter, array('returnAsArray' => true, 'alwaysReturnItemID' => true));
                 $vb_keep = false;
                 if (is_array($va_filter_values)) {
                     foreach ($va_filter_values as $vm_filtered_val) {
                         if (!is_array($vm_filtered_val)) {
                             $vm_filtered_val = array($vm_filtered_val);
                         }
                         foreach ($vm_filtered_val as $vs_val) {
                             if (in_array($vs_val, $va_filter_vals)) {
                                 // one match is enough to keep it
                                 $vb_keep = true;
                             }
                         }
                     }
                 }
                 if (!$vb_keep) {
                     unset($va_rels[$vn_pk]);
                 }
             }
         }
     }
     //
     // Sort on fields if specified
     //
     if (is_array($pa_sort_fields) && sizeof($pa_sort_fields) && sizeof($va_rels)) {
         $va_ids = $va_ids_to_rel_ids = array();
         $vs_rel_pk = $t_rel_item->primaryKey();
         foreach ($va_rels as $vn_i => $va_rel) {
             $va_ids[$vn_i] = $va_rel[$vs_rel_pk];
             $va_ids_to_rel_ids[$va_rel[$vs_rel_pk]][] = $vn_i;
         }
         if (sizeof($va_ids) > 0) {
             $qr_sort = caMakeSearchResult($vs_related_table_name, array_values($va_ids), array('sort' => $pa_sort_fields, 'sortDirection' => $ps_sort_direction));
             $va_rels_sorted = array();
             $vs_rel_pk_full = $t_rel_item->primaryKey(true);
             while ($qr_sort->nextHit()) {
                 foreach ($va_ids_to_rel_ids[$qr_sort->get($vs_rel_pk_full)] as $vn_rel_id) {
                     $va_rels_sorted[$vn_rel_id] = $va_rels[$vn_rel_id];
                 }
             }
             $va_rels = $va_rels_sorted;
         }
     }
     switch ($ps_return_as) {
         case 'firstmodelinstance':
             foreach ($va_rels as $vn_id) {
                 $o_instance = new $vs_related_table_name();
                 if ($o_instance->load($vn_id)) {
                     return $o_instance;
                 }
             }
             return null;
             break;
         case 'modelinstances':
             $va_instances = array();
             foreach ($va_rels as $vn_id) {
                 $o_instance = new $vs_related_table_name();
                 if ($o_instance->load($vn_id)) {
                     $va_instances[] = $o_instance;
                 }
             }
             return $va_instances;
             break;
         case 'firstid':
             if (sizeof($va_rels)) {
                 return array_shift($va_rels);
             }
             return null;
             break;
         case 'count':
             return sizeof($va_rels);
             break;
         case 'searchresult':
             if (sizeof($va_rels) > 0) {
                 return caMakeSearchResult($vs_related_table_name, $va_rels);
             }
             return null;
             break;
         default:
         case 'ids':
             return $va_rels;
             break;
     }
 }
Example #13
0
 /**
  * Responds to the two main List verbs, includes resumption and limiting.
  *
  * @param string $verb OAI-PMH verb for the request
  * @param string $metadataPrefix Metadata prefix
  * @param int $cursor Offset in response to begin output at
  * @param mixed $set Optional set argument
  * @param string $from Optional from date argument
  * @param string $until Optional until date argument
  * @uses createResumptionToken()
  */
 private function listResponse($oaiData, $verb, $metadataPrefix, $cursor, $set, $from, $until)
 {
     $listLimit = $this->_listLimit;
     $o_dm = Datamodel::load();
     // by this point, the mapping code was checked to be valid
     $t_instance = $o_dm->getInstanceByTableName($this->table, true);
     $vs_pk = $t_instance->primaryKey();
     $va_access_values = caGetUserAccessValues($this->opo_request, $this->opa_provider_info);
     $vb_show_deleted = (bool) $this->opa_provider_info['show_deleted'];
     $vb_dont_enforce_access_settings = (bool) $this->opa_provider_info['dont_enforce_access_settings'];
     $vb_dont_cache = (bool) $this->opa_provider_info['dont_cache'];
     $vs_table = $t_instance->tableName();
     if (!($o_search = caGetSearchInstance($vs_table))) {
         $this->throwError(self::OAI_ERR_BAD_ARGUMENT);
         return;
     }
     // Construct date range for from/until if defined
     $o_tep = new TimeExpressionParser();
     $o_lang_settings = $o_tep->getLanguageSettings();
     $vs_conj = array_shift($o_lang_settings->getList("rangeConjunctions"));
     $vs_range = $from && $until ? "{$from} {$vs_conj} {$until}" : '';
     if ($set && $this->opa_provider_info['setFacet']) {
         $o_browse = caGetBrowseInstance($this->table);
         if (($vs_query = $this->opa_provider_info['query']) && $vs_query != "*") {
             $o_browse->addCriteria("_search", $vs_query);
         }
         $o_browse->addCriteria($this->opa_provider_info['setFacet'], $set);
         $o_browse->execute(array('showDeleted' => $vb_show_deleted, 'no_cache' => $vb_dont_cache, 'limitToModifiedOn' => $vs_range, 'checkAccess' => $vb_dont_enforce_access_settings ? null : $va_access_values));
         $qr_res = $o_browse->getResults();
     } else {
         $qr_res = $o_search->search(strlen($this->opa_provider_info['query']) ? $this->opa_provider_info['query'] : "*", array('no_cache' => $vb_dont_cache, 'limitToModifiedOn' => $vs_range, 'showDeleted' => $vb_show_deleted, 'checkAccess' => $vb_dont_enforce_access_settings ? null : $va_access_values));
     }
     if (!$qr_res) {
         $this->throwError(self::OAI_ERR_NO_RECORDS_MATCH, _t('Query failed'));
         return;
     }
     $rows = $qr_res->numHits();
     if (count($qr_res->numHits()) == 0) {
         $this->throwError(self::OAI_ERR_NO_RECORDS_MATCH, _t('No records match the given criteria'));
     } else {
         $verbElement = $oaiData->createElement($verb);
         $oaiData->documentElement->appendChild($verbElement);
         $t_change_log = new ApplicationChangeLog();
         if ($vb_show_deleted) {
             // get list of deleted records
             $va_deleted_items = array();
             $qr_res->seek($cursor);
             $vn_c = 0;
             $va_get_deleted_timestamps_for = array();
             while ($qr_res->nextHit()) {
                 if ((bool) $qr_res->get("{$vs_table}.deleted")) {
                     $va_deleted_items[$vs_pk_val = (int) $qr_res->get("{$vs_table}.{$vs_pk}")] = true;
                     $va_get_deleted_timestamps_for[$vs_pk_val] = true;
                 } else {
                     $vn_access = (int) $qr_res->get("{$vs_table}.access");
                     if (!in_array($vn_access, $va_access_values)) {
                         $va_deleted_items[(int) $qr_res->get("{$vs_table}.{$vs_pk}")] = true;
                     }
                 }
                 $vn_c++;
                 if ($vn_c >= $listLimit) {
                     break;
                 }
             }
             $qr_res->seek(0);
             $va_deleted_timestamps = $t_change_log->getDeleteOnTimestampsForIDs($vs_table, array_keys($va_get_deleted_timestamps_for));
         }
         // Export data using metadata mapping
         $va_items = ca_data_exporters::exportRecordsFromSearchResultToArray($this->getMappingCode(), $qr_res, array('start' => $cursor, 'limit' => $listLimit));
         if (is_array($va_items) && sizeof($va_items)) {
             $va_timestamps = $t_change_log->getLastChangeTimestampsForIDs($vs_table, array_keys($va_items));
             foreach ($va_items as $vn_id => $vs_item_xml) {
                 if ($vb_show_deleted && $va_deleted_items[$vn_id]) {
                     $headerData = array('identifier' => OaiIdentifier::itemToOaiId($vn_id), 'datestamp' => self::unixToUtc($va_deleted_timestamps[$vn_id]['timestamp'] ? $va_deleted_timestamps[$vn_id]['timestamp'] : $va_timestamps[$vn_id]['timestamp']));
                     if ($verb == 'ListIdentifiers') {
                         $header = $this->createElementWithChildren($oaiData, $verbElement, 'header', $headerData);
                         $header->setAttribute("status", "deleted");
                     } else {
                         $recordElement = $verbElement->appendChild($oaiData->createElement('record'));
                         $header = $this->createElementWithChildren($oaiData, $recordElement, 'header', $headerData);
                         $header->setAttribute("status", "deleted");
                     }
                 } else {
                     $headerData = array('identifier' => OaiIdentifier::itemToOaiId($vn_id), 'datestamp' => self::unixToUtc($va_timestamps[$vn_id]['timestamp']));
                     if ($verb == 'ListIdentifiers') {
                         $this->createElementWithChildren($oaiData, $verbElement, 'header', $headerData);
                     } else {
                         $recordElement = $verbElement->appendChild($oaiData->createElement('record'));
                         $this->createElementWithChildren($oaiData, $recordElement, 'header', $headerData);
                         $metadataElement = $oaiData->createElement('metadata');
                         $o_doc_src = DomDocument::loadXML($vs_item_xml);
                         if ($o_doc_src) {
                             // just in case the xml fails to load through DomDocument for some reason (e.g. a bad mapping or very weird characters)
                             $metadataElement->appendChild($oaiData->importNode($o_doc_src->documentElement, true));
                         }
                         $recordElement->appendChild($metadataElement);
                     }
                 }
             }
         }
         if ($rows > $cursor + $listLimit) {
             $token = $this->createResumptionToken($verb, $metadataPrefix, $cursor + $listLimit, $set, $from, $until);
             $tokenElement = $oaiData->createElement('resumptionToken', $token['key']);
             $tokenElement->setAttribute('expirationDate', self::unixToUtc($token['expiration']));
             $tokenElement->setAttribute('completeListSize', $rows);
             $tokenElement->setAttribute('cursor', $cursor);
             $verbElement->appendChild($tokenElement);
         } else {
             if ($cursor != 0) {
                 $tokenElement = $this->oaiData->createElement('resumptionToken');
                 $verbElement->appendChild($tokenElement);
             }
         }
     }
 }
 function testNormalizationDecades()
 {
     $o_tep = new TimeExpressionParser('16th century', 'en_US');
     $va_historic = $o_tep->getHistoricTimestamps();
     $va_decades_expected = array(1500 => '1500s', 1510 => '1510s', 1520 => '1520s', 1530 => '1530s', 1540 => '1540s', 1550 => '1550s', 1560 => '1560s', 1570 => '1570s', 1580 => '1580s', 1590 => '1590s');
     $va_decades = $o_tep->normalizeDateRange($va_historic['start'], $va_historic['end'], 'decades');
     $this->assertEquals(10, sizeof($va_decades));
     $this->assertEquals($va_decades_expected, $va_decades);
 }
 /**
  *
  */
 public function getComments($ps_mode = null, $pn_limit = null, $pa_options = null)
 {
     $o_db = $this->getDb();
     $vs_where = '';
     switch ($ps_mode) {
         case 'moderated':
             $vs_where = "WHERE cic.moderated_on IS NOT NULL";
             break;
         case 'unmoderated':
             $vs_where = "WHERE cic.moderated_on IS NULL";
             break;
     }
     if (intval($pn_limit) > 0) {
         $vs_limit = " LIMIT " . intval($pn_limit);
     }
     $o_tep = new TimeExpressionParser();
     $qr_res = $o_db->query("\n\t\t\tSELECT cic.*, u.user_id, u.fname, u.lname, u.email user_email\n\t\t\tFROM ca_item_comments cic\n\t\t\tLEFT JOIN ca_users AS u ON u.user_id = cic.user_id\n\t\t\t{$vs_where} ORDER BY cic.created_on DESC {$vs_limit}\n\t\t");
     $o_datamodel = $this->getAppDatamodel();
     $va_comments = array();
     while ($qr_res->nextRow()) {
         $vn_datetime = $qr_res->get('created_on');
         $o_tep->setUnixTimestamps($vn_datetime, $vn_datetime);
         $va_row = $qr_res->getRow();
         $va_row['created_on'] = $o_tep->getText();
         $t_table = $o_datamodel->getInstanceByTableNum($qr_res->get('table_num'), true);
         if ($t_table->load($qr_res->get('row_id'))) {
             $va_row['commented_on'] = $t_table->getLabelForDisplay(false);
             if ($vs_idno = $t_table->get('idno')) {
                 $va_row['commented_on'] .= ' [' . $vs_idno . ']';
             }
         }
         foreach (array("media1", "media2", "media3", "media4") as $vs_media_field) {
             $va_media_versions = array();
             $va_media_versions = $qr_res->getMediaVersions($vs_media_field);
             $va_media = array();
             if (is_array($va_media_versions) && sizeof($va_media_versions) > 0) {
                 foreach ($va_media_versions as $vs_version) {
                     $va_image_info = array();
                     $va_image_info = $qr_res->getMediaInfo($vs_media_field, $vs_version);
                     $va_image_info["TAG"] = $qr_res->getMediaTag($vs_media_field, $vs_version);
                     $va_image_info["URL"] = $qr_res->getMediaUrl($vs_media_field, $vs_version);
                     $va_media[$vs_version] = $va_image_info;
                 }
                 $va_row[$vs_media_field] = $va_media;
             }
         }
         $va_comments[] = $va_row;
     }
     return $va_comments;
 }
 /**
  * Returns list of items in the specified table related to the currently loaded row.
  * 
  * @param $pm_rel_table_name_or_num - the table name or table number of the item type you want to get a list of (eg. if you are calling this on an ca_objects instance passing 'ca_entities' here will get you a list of entities related to the object)
  * @param $pa_options - array of options. Supported options are:
  *
  * 		restrict_to_type = restricts returned items to those of the specified type; only supports a single type which can be specified as a list item_code or item_id
  *		restrictToType = synonym for restrict_to_type
  *		restrict_to_types = restricts returned items to those of the specified types; pass an array of list item_codes or item_ids
  *		restrictToTypes = synonym for restrict_to_types
  *		dont_include_subtypes_in_type_restriction = if set subtypes are not included when enforcing restrict_to_types. Note that restrict_to_relationship_types always includes subtypes in its restriction.
  *		dontIncludeSubtypesInTypeRestriction = synonym for dont_include_subtypes_in_type_restriction
  *		restrict_to_relationship_types = restricts returned items to those related to the current row by the specified relationship type(s). You can pass either an array of types or a single type. The types can be relationship type_code's or type_id's.
  *		restrictToRelationshipTypes = synonym for restrict_to_relationship_types
  *
  * 		restrictToBundleValues = restricts returned items to bundle values in related table. pass an associative array with elements like this: bundle_name => list of values
  *
  *		exclude_relationship_types = omits any items related to the current row with any of the specified types from the returned set of ids. You can pass either an array of types or a single type. The types can be relationship type_code's or type_id's.
  *		excludeRelationshipTypes = synonym for exclude_relationship_types
  * 		exclude_type = excludes returned items of the specified type; only supports a single type which can be specified as a list item_code or item_id
  *		excludeType = synonym for exclude_type
  *		exclude_types = omits any items related to the current row that are of any of the specified types from the returned set of ids. You can pass either an array of types or a single type. The types can be type_code's or type_id's.
  *		excludeTypes = synonym for exclude_types
  *
  *		restrict_to_lists = when fetching related ca_list_items restricts returned items to those that are in the specified lists; pass an array of list list_codes or list_ids
  *		restrictToLists = synonym for restrict_to_lists
  *
  *		fields = array of fields (in table.fieldname format) to include in returned data
  *		return_non_preferred_labels = if set to true, non-preferred labels are included in returned data
  *		returnNonPreferredLabels = synonym for return_non_preferred_labels
  *		checkAccess = array of access values to filter results by; if defined only items with the specified access code(s) are returned
  *		return_labels_as_array = if set to true then all labels associated with row are returned in an array, otherwise only a text value in the current locale is returned; default is false - return single label in current locale
  *		returnLabelsAsArray = synonym for return_labels_as_array
  * 		row_ids = array of primary key values to use when fetching related items; if omitted or set to a null value the 'row_id' option (single value) will be used; if row_id is also not set then the currently loaded primary key value will be used
  *		row_id = primary key value to use when fetching related items; if omitted or set to a false value (eg. null, false, 0) then the currently loaded primary key value is used [default]
  *		start = item to start return set at; first item is numbered zero; default is 0
  *		limit = number of items to limit return set to; default is 1000
  *		sort = optional array of bundles to sort returned values on. Currently only supported when getting related values via simple related <table_name> and <table_name>.related invokations. Eg. from a ca_objects results you can use the 'sort' option got get('ca_entities'), get('ca_entities.related') or get('ca_objects.related'). The bundle specifiers are fields with or without tablename. Only those fields returned for the related tables (intrinsics, label fields and attributes) are sortable.
  *		sortDirection = direction of sort. Valid values as "ASC" (ascending) and "DESC" (descending). Default is ASC.
  *		showDeleted = if set to true, related items that have been deleted are returned. Default is false.
  *		where = optional array of fields and field values to filter returned values on. The fields must be intrinsic and in the same table as the field being "get()'ed" Can be used to filter returned values from primary and related tables. This option can be useful when you want to fetch certain values from a related table. For example, you want to get the relationship source_info values, but only for relationships going to a specific related record. Note that multiple fields/values are effectively AND'ed together - all must match for a row to be returned - and that only equivalence is supported (eg. field equals value).
  *		user_id = If set item level access control is performed relative to specified user_id, otherwise defaults to logged in user
  *		groupFields = Groups together fields in an arrangement that is easier for import to another system. Used by the ItemInfo web service when in "import" mode. Default is false.
  *		returnLocaleCodes = Return locale values as codes (Ex. en_US) rather than numeric database-specific locale_ids. Default is false.
  * @return array - list of related items
  */
 public function getRelatedItems($pm_rel_table_name_or_num, $pa_options = null)
 {
     global $AUTH_CURRENT_USER_ID;
     $vn_user_id = isset($pa_options['user_id']) && $pa_options['user_id'] ? $pa_options['user_id'] : (int) $AUTH_CURRENT_USER_ID;
     $vb_show_if_no_acl = (bool) ($this->getAppConfig()->get('default_item_access_level') > __CA_ACL_NO_ACCESS__);
     // convert options
     if (isset($pa_options['restrictToType']) && (!isset($pa_options['restrict_to_type']) || !$pa_options['restrict_to_type'])) {
         $pa_options['restrict_to_type'] = $pa_options['restrictToType'];
     }
     if (isset($pa_options['restrictToTypes']) && (!isset($pa_options['restrict_to_types']) || !$pa_options['restrict_to_types'])) {
         $pa_options['restrict_to_types'] = $pa_options['restrictToTypes'];
     }
     if (isset($pa_options['restrictToRelationshipTypes']) && (!isset($pa_options['restrict_to_relationship_types']) || !$pa_options['restrict_to_relationship_types'])) {
         $pa_options['restrict_to_relationship_types'] = $pa_options['restrictToRelationshipTypes'];
     }
     if (isset($pa_options['excludeType']) && (!isset($pa_options['exclude_type']) || !$pa_options['exclude_type'])) {
         $pa_options['exclude_type'] = $pa_options['excludeType'];
     }
     if (isset($pa_options['excludeTypes']) && (!isset($pa_options['exclude_types']) || !$pa_options['exclude_types'])) {
         $pa_options['exclude_types'] = $pa_options['excludeTypes'];
     }
     if (isset($pa_options['excludeRelationshipTypes']) && (!isset($pa_options['exclude_relationship_types']) || !$pa_options['exclude_relationship_types'])) {
         $pa_options['exclude_relationship_types'] = $pa_options['excludeRelationshipTypes'];
     }
     if (isset($pa_options['dontIncludeSubtypesInTypeRestriction']) && (!isset($pa_options['dont_include_subtypes_in_type_restriction']) || !$pa_options['dont_include_subtypes_in_type_restriction'])) {
         $pa_options['dont_include_subtypes_in_type_restriction'] = $pa_options['dontIncludeSubtypesInTypeRestriction'];
     }
     if (isset($pa_options['returnNonPreferredLabels']) && (!isset($pa_options['return_non_preferred_labels']) || !$pa_options['return_non_preferred_labels'])) {
         $pa_options['return_non_preferred_labels'] = $pa_options['returnNonPreferredLabels'];
     }
     if (isset($pa_options['returnLabelsAsArray']) && (!isset($pa_options['return_labels_as_array']) || !$pa_options['return_labels_as_array'])) {
         $pa_options['return_labels_as_array'] = $pa_options['returnLabelsAsArray'];
     }
     if (isset($pa_options['restrictToLists']) && (!isset($pa_options['restrict_to_lists']) || !$pa_options['restrict_to_lists'])) {
         $pa_options['restrict_to_lists'] = $pa_options['restrictToLists'];
     }
     if (isset($pa_options['groupFields'])) {
         $pa_options['groupFields'] = (bool) $pa_options['groupFields'];
     } else {
         $pa_options['groupFields'] = false;
     }
     $o_db = $this->getDb();
     $t_locale = new ca_locales();
     $o_tep = new TimeExpressionParser();
     $vb_uses_effective_dates = false;
     $va_get_where = isset($pa_options['where']) && is_array($pa_options['where']) && sizeof($pa_options['where']) ? $pa_options['where'] : null;
     $va_row_ids = isset($pa_options['row_ids']) && is_array($pa_options['row_ids']) ? $pa_options['row_ids'] : null;
     $vn_row_id = isset($pa_options['row_id']) && $pa_options['row_id'] ? $pa_options['row_id'] : $this->getPrimaryKey();
     if (isset($pa_options['sort']) && !is_array($pa_options['sort'])) {
         $pa_options['sort'] = array($pa_options['sort']);
     }
     $va_sort_fields = isset($pa_options['sort']) && is_array($pa_options['sort']) ? $pa_options['sort'] : null;
     $vs_sort_direction = isset($pa_options['sortDirection']) && $pa_options['sortDirection'] ? $pa_options['sortDirection'] : null;
     if (!$va_row_ids && $vn_row_id > 0) {
         $va_row_ids = array($vn_row_id);
     }
     if (!$va_row_ids || !is_array($va_row_ids) || !sizeof($va_row_ids)) {
         return array();
     }
     $vb_return_labels_as_array = isset($pa_options['return_labels_as_array']) && $pa_options['return_labels_as_array'] ? true : false;
     $vn_limit = isset($pa_options['limit']) && (int) $pa_options['limit'] > 0 ? (int) $pa_options['limit'] : 1000;
     $vn_start = isset($pa_options['start']) && (int) $pa_options['start'] > 0 ? (int) $pa_options['start'] : 0;
     if (is_numeric($pm_rel_table_name_or_num)) {
         if (!($vs_related_table_name = $this->getAppDataModel()->getTableName($pm_rel_table_name_or_num))) {
             return null;
         }
     } else {
         if (sizeof($va_tmp = explode(".", $pm_rel_table_name_or_num)) > 1) {
             $pm_rel_table_name_or_num = array_shift($va_tmp);
         }
         if (!($o_instance = $this->getAppDataModel()->getInstanceByTableName($pm_rel_table_name_or_num, true))) {
             return null;
         }
         $vs_related_table_name = $pm_rel_table_name_or_num;
     }
     if (!is_array($pa_options)) {
         $pa_options = array();
     }
     switch (sizeof($va_path = array_keys($this->getAppDatamodel()->getPath($this->tableName(), $vs_related_table_name)))) {
         case 3:
             $t_item_rel = $this->getAppDatamodel()->getTableInstance($va_path[1]);
             $t_rel_item = $this->getAppDatamodel()->getTableInstance($va_path[2]);
             $vs_key = $t_item_rel->primaryKey();
             //'relation_id';
             break;
         case 2:
             $t_item_rel = null;
             $t_rel_item = $this->getAppDatamodel()->getTableInstance($va_path[1]);
             $vs_key = $t_rel_item->primaryKey();
             break;
         default:
             // bad related table
             return null;
             break;
     }
     // check for self relationship
     $vb_self_relationship = false;
     if ($this->tableName() == $vs_related_table_name) {
         $vb_self_relationship = true;
         $t_rel_item = $this->getAppDatamodel()->getTableInstance($va_path[0]);
         $t_item_rel = $this->getAppDatamodel()->getTableInstance($va_path[1]);
     }
     $va_wheres = array();
     $va_selects = array();
     $va_joins_post_add = array();
     $vs_related_table = $t_rel_item->tableName();
     if ($t_rel_item->hasField('type_id')) {
         $va_selects[] = $vs_related_table . '.type_id item_type_id';
     }
     if ($t_rel_item->hasField('source_id')) {
         $va_selects[] = $vs_related_table . '.source_id item_source_id';
     }
     // TODO: get these field names from models
     if ($t_item_rel) {
         //define table names
         $vs_linking_table = $t_item_rel->tableName();
         $va_selects[] = $vs_related_table . '.' . $t_rel_item->primaryKey();
         // include dates in returned data
         if ($t_item_rel->hasField('effective_date')) {
             $va_selects[] = $vs_linking_table . '.sdatetime';
             $va_selects[] = $vs_linking_table . '.edatetime';
             $vb_uses_effective_dates = true;
         }
         if ($t_rel_item->hasField('is_enabled')) {
             $va_selects[] = $vs_related_table . '.is_enabled';
         }
         if ($t_item_rel->hasField('type_id')) {
             $va_selects[] = $vs_linking_table . '.type_id relationship_type_id';
             require_once __CA_MODELS_DIR__ . '/ca_relationship_types.php';
             $t_rel = new ca_relationship_types();
             $vb_uses_relationship_types = true;
         }
         // limit related items to a specific type
         if ($vb_uses_relationship_types && isset($pa_options['restrict_to_relationship_types']) && $pa_options['restrict_to_relationship_types']) {
             if (!is_array($pa_options['restrict_to_relationship_types'])) {
                 $pa_options['restrict_to_relationship_types'] = array($pa_options['restrict_to_relationship_types']);
             }
             if (sizeof($pa_options['restrict_to_relationship_types'])) {
                 $va_rel_types = array();
                 foreach ($pa_options['restrict_to_relationship_types'] as $vm_type) {
                     if (!$vm_type) {
                         continue;
                     }
                     if (!($vn_type_id = $t_rel->getRelationshipTypeID($vs_linking_table, $vm_type))) {
                         $vn_type_id = (int) $vm_type;
                     }
                     if ($vn_type_id > 0) {
                         $va_rel_types[] = $vn_type_id;
                         if (is_array($va_children = $t_rel->getHierarchyChildren($vn_type_id, array('idsOnly' => true)))) {
                             $va_rel_types = array_merge($va_rel_types, $va_children);
                         }
                     }
                 }
                 if (sizeof($va_rel_types)) {
                     $va_wheres[] = '(' . $vs_linking_table . '.type_id IN (' . join(',', $va_rel_types) . '))';
                 }
             }
         }
         if ($vb_uses_relationship_types && isset($pa_options['exclude_relationship_types']) && $pa_options['exclude_relationship_types']) {
             if (!is_array($pa_options['exclude_relationship_types'])) {
                 $pa_options['exclude_relationship_types'] = array($pa_options['exclude_relationship_types']);
             }
             if (sizeof($pa_options['exclude_relationship_types'])) {
                 $va_rel_types = array();
                 foreach ($pa_options['exclude_relationship_types'] as $vm_type) {
                     if ($vn_type_id = $t_rel->getRelationshipTypeID($vs_linking_table, $vm_type)) {
                         $va_rel_types[] = $vn_type_id;
                         if (is_array($va_children = $t_rel->getHierarchyChildren($vn_type_id, array('idsOnly' => true)))) {
                             $va_rel_types = array_merge($va_rel_types, $va_children);
                         }
                     }
                 }
                 if (sizeof($va_rel_types)) {
                     $va_wheres[] = '(' . $vs_linking_table . '.type_id NOT IN (' . join(',', $va_rel_types) . '))';
                 }
             }
         }
     }
     // limit related items to a specific type
     if (isset($pa_options['restrict_to_type']) && $pa_options['restrict_to_type']) {
         if (!isset($pa_options['restrict_to_types']) || !is_array($pa_options['restrict_to_types'])) {
             $pa_options['restrict_to_types'] = array();
         }
         $pa_options['restrict_to_types'][] = $pa_options['restrict_to_type'];
     }
     $va_type_ids = caMergeTypeRestrictionLists($t_rel_item, $pa_options);
     if (is_array($va_type_ids) && sizeof($va_type_ids) > 0) {
         $va_wheres[] = "({$vs_related_table}.type_id IN (" . join(',', $va_type_ids) . ')' . ($t_rel_item->getFieldInfo('type_id', 'IS_NULL') ? " OR ({$vs_related_table}.type_id IS NULL)" : '') . ')';
     }
     $va_source_ids = caMergeSourceRestrictionLists($t_rel_item, $pa_options);
     if (method_exists($t_rel_item, "getSourceFieldName") && ($vs_source_id_fld = $t_rel_item->getSourceFieldName()) && is_array($va_source_ids) && sizeof($va_source_ids) > 0) {
         $va_wheres[] = "({$vs_related_table}.{$vs_source_id_fld} IN (" . join(',', $va_source_ids) . "))";
     }
     if (isset($pa_options['exclude_type']) && $pa_options['exclude_type']) {
         if (!isset($pa_options['exclude_types']) || !is_array($pa_options['exclude_types'])) {
             $pa_options['exclude_types'] = array();
         }
         $pa_options['exclude_types'][] = $pa_options['exclude_type'];
     }
     if (isset($pa_options['exclude_types']) && is_array($pa_options['exclude_types'])) {
         $va_type_ids = caMakeTypeIDList($t_rel_item->tableName(), $pa_options['exclude_types']);
         if (is_array($va_type_ids) && sizeof($va_type_ids) > 0) {
             $va_wheres[] = '(' . $vs_related_table . '.type_id NOT IN (' . join(',', $va_type_ids) . '))';
         }
     }
     if ($this->getAppConfig()->get('perform_item_level_access_checking')) {
         $t_user = new ca_users($vn_user_id, true);
         if (is_array($va_groups = $t_user->getUserGroups()) && sizeof($va_groups)) {
             $va_group_ids = array_keys($va_groups);
         } else {
             $va_group_ids = array();
         }
         // Join to limit what browse table items are used to generate facet
         $va_joins_post_add[] = 'LEFT JOIN ca_acl ON ' . $t_rel_item->tableName() . '.' . $t_rel_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_rel_item->tableNum() . "\n";
         $va_wheres[] = "(\n\t\t\t\t((\n\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\tOR\n\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t)";
     }
     if (is_array($va_get_where)) {
         foreach ($va_get_where as $vs_fld => $vm_val) {
             if ($t_rel_item->hasField($vs_fld)) {
                 $va_wheres[] = "({$vs_related_table_name}.{$vs_fld} = " . (!is_numeric($vm_val) ? "'" . $this->getDb()->escape($vm_val) . "'" : $vm_val) . ")";
             }
         }
     }
     if ($vs_idno_fld = $t_rel_item->getProperty('ID_NUMBERING_ID_FIELD')) {
         $va_selects[] = $t_rel_item->tableName() . '.' . $vs_idno_fld;
     }
     if ($vs_idno_sort_fld = $t_rel_item->getProperty('ID_NUMBERING_SORT_FIELD')) {
         $va_selects[] = $t_rel_item->tableName() . '.' . $vs_idno_sort_fld;
     }
     $va_selects[] = $va_path[1] . '.' . $vs_key;
     if (isset($pa_options['fields']) && is_array($pa_options['fields'])) {
         $va_selects = array_merge($va_selects, $pa_options['fields']);
     }
     // if related item is labelable then include the label table in the query as well
     $vs_label_display_field = null;
     if (method_exists($t_rel_item, "getLabelTableName")) {
         if ($vs_label_table_name = $t_rel_item->getLabelTableName()) {
             // make sure it actually has a label table...
             $va_path[] = $vs_label_table_name;
             $t_rel_item_label = $this->getAppDatamodel()->getTableInstance($vs_label_table_name);
             $vs_label_display_field = $t_rel_item_label->getDisplayField();
             if ($vb_return_labels_as_array || is_array($va_sort_fields) && sizeof($va_sort_fields)) {
                 $va_selects[] = $vs_label_table_name . '.*';
             } else {
                 $va_selects[] = $vs_label_table_name . '.' . $vs_label_display_field;
                 $va_selects[] = $vs_label_table_name . '.locale_id';
                 if ($t_rel_item_label->hasField('surname')) {
                     // hack to include fields we need to sort entity labels properly
                     $va_selects[] = $vs_label_table_name . '.surname';
                     $va_selects[] = $vs_label_table_name . '.forename';
                 }
             }
             if ($t_rel_item_label->hasField('is_preferred') && (!isset($pa_options['return_non_preferred_labels']) || !$pa_options['return_non_preferred_labels'])) {
                 $va_wheres[] = "(" . $vs_label_table_name . '.is_preferred = 1)';
             }
         }
     }
     // return source info in returned data
     if ($t_item_rel && $t_item_rel->hasField('source_info')) {
         $va_selects[] = $vs_linking_table . '.source_info';
     }
     if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access')) {
         $va_wheres[] = "(" . $t_rel_item->tableName() . '.access IN (' . join(',', $pa_options['checkAccess']) . '))';
     }
     if ((!isset($pa_options['showDeleted']) || !$pa_options['showDeleted']) && $t_rel_item->hasField('deleted')) {
         $va_wheres[] = "(" . $t_rel_item->tableName() . '.deleted = 0)';
     }
     if ($vb_self_relationship) {
         //
         // START - self relation
         //
         $va_rel_info = $this->getAppDatamodel()->getRelationships($va_path[0], $va_path[1]);
         if ($vs_label_table_name) {
             $va_label_rel_info = $this->getAppDatamodel()->getRelationships($va_path[0], $vs_label_table_name);
         }
         $va_rels = array();
         $vn_i = 0;
         foreach ($va_rel_info[$va_path[0]][$va_path[1]] as $va_possible_keys) {
             $va_joins = array();
             $va_joins[] = "INNER JOIN " . $va_path[1] . " ON " . $va_path[1] . '.' . $va_possible_keys[1] . ' = ' . $va_path[0] . '.' . $va_possible_keys[0] . "\n";
             if ($vs_label_table_name) {
                 $va_joins[] = "INNER JOIN " . $vs_label_table_name . " ON " . $vs_label_table_name . '.' . $va_label_rel_info[$va_path[0]][$vs_label_table_name][0][1] . ' = ' . $va_path[0] . '.' . $va_label_rel_info[$va_path[0]][$vs_label_table_name][0][0] . "\n";
             }
             $vs_other_field = $vn_i == 0 ? $va_rel_info[$va_path[0]][$va_path[1]][1][1] : $va_rel_info[$va_path[0]][$va_path[1]][0][1];
             $vs_direction = preg_match('!left!', $vs_other_field) ? 'ltor' : 'rtol';
             $va_selects['row_id'] = $va_path[1] . '.' . $vs_other_field . ' AS row_id';
             $vs_order_by = '';
             $vs_sort_fld = '';
             if ($t_item_rel && $t_item_rel->hasField('rank')) {
                 $vs_order_by = ' ORDER BY ' . $t_item_rel->tableName() . '.rank';
                 $vs_sort_fld = 'rank';
                 $va_selects[] = $t_item_rel->tableName() . ".rank";
             } else {
                 if ($t_rel_item && ($vs_sort = $t_rel_item->getProperty('ID_NUMBERING_SORT_FIELD'))) {
                     $vs_order_by = " ORDER BY " . $t_rel_item->tableName() . ".{$vs_sort}";
                     $vs_sort_fld = $vs_sort;
                     $va_selects[] = $t_rel_item->tableName() . ".{$vs_sort}";
                 }
             }
             $vs_sql = "\n\t\t\t\t\tSELECT " . join(', ', $va_selects) . "\n\t\t\t\t\tFROM " . $va_path[0] . "\n\t\t\t\t\t" . join("\n", array_merge($va_joins, $va_joins_post_add)) . "\n\t\t\t\t\tWHERE\n\t\t\t\t\t\t" . join(' AND ', array_merge($va_wheres, array('(' . $va_path[1] . '.' . $vs_other_field . ' IN (' . join(',', $va_row_ids) . '))'))) . "\n\t\t\t\t\t{$vs_order_by}";
             //print "<pre>$vs_sql</pre>\n";
             $qr_res = $o_db->query($vs_sql);
             if ($vb_uses_relationship_types) {
                 $va_rel_types = $t_rel->getRelationshipInfo($va_path[1]);
             }
             $vn_c = 0;
             if ($vn_start > 0) {
                 $qr_res->seek($vn_start);
             }
             while ($qr_res->nextRow()) {
                 if ($vn_c >= $vn_limit) {
                     break;
                 }
                 $va_row = $qr_res->getRow();
                 $vn_id = $va_row[$vs_key] . '/' . $va_row['row_id'];
                 $vs_sort_key = $qr_res->get($vs_sort_fld);
                 $vs_display_label = $va_row[$vs_label_display_field];
                 //unset($va_row[$vs_label_display_field]);
                 if (!$va_rels[$vs_sort_key][$vn_id]) {
                     $va_rels[$vs_sort_key][$vn_id] = $qr_res->getRow();
                 }
                 if ($vb_uses_effective_dates) {
                     // return effective dates as display/parse-able text
                     if ($va_rels[$vs_sort_key][$vn_id]['sdatetime'] || $va_rels[$vs_sort_key][$vn_id]['edatetime']) {
                         $o_tep->setHistoricTimestamps($va_rels[$vs_sort_key][$vn_id]['sdatetime'], $va_rels[$vs_sort_key][$vn_id]['edatetime']);
                         $va_rels[$vs_sort_key][$vn_id]['effective_date'] = $o_tep->getText();
                     }
                 }
                 $vn_locale_id = $qr_res->get('locale_id');
                 if (isset($pa_options['returnLocaleCodes']) && $pa_options['returnLocaleCodes']) {
                     $va_rels[$vs_v]['locale_id'] = $vn_locale_id = $t_locale->localeIDToCode($vn_locale_id);
                 }
                 $va_rels[$vs_sort_key][$vn_id]['labels'][$vn_locale_id] = $vb_return_labels_as_array ? $va_row : $vs_display_label;
                 $va_rels[$vs_sort_key][$vn_id]['_key'] = $vs_key;
                 $va_rels[$vs_sort_key][$vn_id]['direction'] = $vs_direction;
                 $vn_c++;
                 if ($vb_uses_relationship_types) {
                     $va_rels[$vs_sort_key][$vn_id]['relationship_typename'] = $vs_direction == 'ltor' ? $va_rel_types[$va_row['relationship_type_id']]['typename'] : $va_rel_types[$va_row['relationship_type_id']]['typename_reverse'];
                     $va_rels[$vs_sort_key][$vn_id]['relationship_type_code'] = $va_rel_types[$va_row['relationship_type_id']]['type_code'];
                 }
                 //
                 // Return data in an arrangement more convenient for the data importer
                 //
                 if ($pa_options['groupFields']) {
                     $vs_rel_pk = $t_rel_item->primaryKey();
                     if ($t_rel_item_label) {
                         foreach ($t_rel_item_label->getFormFields() as $vs_field => $va_field_info) {
                             if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                                 continue;
                             }
                             $va_rels[$vs_v]['preferred_labels'][$vs_field] = $va_rels[$vs_v][$vs_field];
                             unset($va_rels[$vs_v][$vs_field]);
                         }
                     }
                     foreach ($t_rel_item->getFormFields() as $vs_field => $va_field_info) {
                         if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                             continue;
                         }
                         $va_rels[$vs_v]['intrinsic'][$vs_field] = $va_rels[$vs_v][$vs_field];
                         unset($va_rels[$vs_v][$vs_field]);
                     }
                     unset($va_rels[$vs_v]['_key']);
                     unset($va_rels[$vs_v]['row_id']);
                 }
             }
             $vn_i++;
         }
         ksort($va_rels);
         // sort by sort key... we'll remove the sort key in the next loop while we add the labels
         // Set 'label' entry - display label in current user's locale
         $va_sorted_rels = array();
         foreach ($va_rels as $vs_sort_key => $va_rels_by_sort_key) {
             foreach ($va_rels_by_sort_key as $vn_id => $va_rel) {
                 $va_tmp = array(0 => $va_rel['labels']);
                 $va_sorted_rels[$vn_id] = $va_rel;
                 $va_values_filtered_by_locale = caExtractValuesByUserLocale($va_tmp);
                 $va_sorted_rels[$vn_id]['label'] = array_shift($va_values_filtered_by_locale);
             }
         }
         $va_rels = $va_sorted_rels;
         //
         // END - self relation
         //
     } else {
         //
         // BEGIN - non-self relation
         //
         $va_wheres[] = "(" . $this->tableName() . '.' . $this->primaryKey() . " IN (" . join(",", $va_row_ids) . "))";
         $vs_cur_table = array_shift($va_path);
         $va_joins = array();
         // Enforce restrict_to_lists for related list items
         if ($vs_related_table_name == 'ca_list_items' && is_array($pa_options['restrict_to_lists'])) {
             $va_list_ids = array();
             foreach ($pa_options['restrict_to_lists'] as $vm_list) {
                 if ($vn_list_id = ca_lists::getListID($vm_list)) {
                     $va_list_ids[] = $vn_list_id;
                 }
             }
             if (sizeof($va_list_ids)) {
                 $va_wheres[] = "(ca_list_items.list_id IN (" . join(",", $va_list_ids) . "))";
             }
         }
         foreach ($va_path as $vs_join_table) {
             $va_rel_info = $this->getAppDatamodel()->getRelationships($vs_cur_table, $vs_join_table);
             $va_joins[] = 'INNER JOIN ' . $vs_join_table . ' ON ' . $vs_cur_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][0] . ' = ' . $vs_join_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][1] . "\n";
             $vs_cur_table = $vs_join_table;
         }
         // If we're getting ca_set_items, we can't rename our primary key "row_id" because that table
         // has a field called row_id and it's important and should be in the selected fields. Hence, this hack.
         if ($vs_related_table_name == 'ca_set_items') {
             $va_selects[] = $this->tableName() . '.' . $this->primaryKey();
             $va_selects[] = 'ca_set_items.row_id';
         } else {
             $va_selects[] = $this->tableName() . '.' . $this->primaryKey() . ' AS row_id';
         }
         $vs_order_by = '';
         if ($t_item_rel && $t_item_rel->hasField('rank')) {
             $vs_order_by = ' ORDER BY ' . $t_item_rel->tableName() . '.rank';
         } else {
             if ($t_rel_item && ($vs_sort = $t_rel_item->getProperty('ID_NUMBERING_SORT_FIELD'))) {
                 $vs_order_by = " ORDER BY " . $t_rel_item->tableName() . ".{$vs_sort}";
             }
         }
         $vs_sql = "\n\t\t\t\tSELECT DISTINCT " . join(', ', $va_selects) . "\n\t\t\t\tFROM " . $this->tableName() . "\n\t\t\t\t" . join("\n", array_merge($va_joins, $va_joins_post_add)) . "\n\t\t\t\tWHERE\n\t\t\t\t\t" . join(' AND ', $va_wheres) . "\n\t\t\t\t{$vs_order_by}\n\t\t\t";
         //print "<pre>$vs_sql</pre>\n";
         $qr_res = $o_db->query($vs_sql);
         if ($vb_uses_relationship_types) {
             $va_rel_types = $t_rel->getRelationshipInfo($t_item_rel->tableName());
             $vs_left_table = $t_item_rel->getLeftTableName();
             $vs_direction = $vs_left_table == $this->tableName() ? 'ltor' : 'rtol';
         }
         $va_rels = array();
         $vn_c = 0;
         if ($vn_start > 0) {
             $qr_res->seek($vn_start);
         }
         while ($qr_res->nextRow()) {
             if ($vn_c >= $vn_limit) {
                 break;
             }
             if (isset($pa_options['returnAsSearchResult']) && $pa_options['returnAsSearchResult']) {
                 $va_rels[] = $qr_res->get($t_rel_item->primaryKey());
                 continue;
             }
             $va_row = $qr_res->getRow();
             $vs_v = $va_row[$vs_key];
             $vs_display_label = $va_row[$vs_label_display_field];
             //unset($va_row[$vs_label_display_field]);
             if (!isset($va_rels[$vs_v]) || !$va_rels[$vs_v]) {
                 $va_rels[$vs_v] = $va_row;
             }
             if ($vb_uses_effective_dates) {
                 // return effective dates as display/parse-able text
                 if ($va_rels[$vs_v]['sdatetime'] || $va_rels[$vs_v]['edatetime']) {
                     $o_tep->setHistoricTimestamps($va_rels[$vs_v]['sdatetime'], $va_rels[$vs_v]['edatetime']);
                     $va_rels[$vs_v]['effective_date'] = $o_tep->getText();
                 }
             }
             $vn_locale_id = $qr_res->get('locale_id');
             if (isset($pa_options['returnLocaleCodes']) && $pa_options['returnLocaleCodes']) {
                 $va_rels[$vs_v]['locale_id'] = $vn_locale_id = $t_locale->localeIDToCode($vn_locale_id);
             }
             $va_rels[$vs_v]['labels'][$vn_locale_id] = $vb_return_labels_as_array ? $va_row : $vs_display_label;
             $va_rels[$vs_v]['_key'] = $vs_key;
             $va_rels[$vs_v]['direction'] = $vs_direction;
             $vn_c++;
             if ($vb_uses_relationship_types) {
                 $va_rels[$vs_v]['relationship_typename'] = $vs_direction == 'ltor' ? $va_rel_types[$va_row['relationship_type_id']]['typename'] : $va_rel_types[$va_row['relationship_type_id']]['typename_reverse'];
                 $va_rels[$vs_v]['relationship_type_code'] = $va_rel_types[$va_row['relationship_type_id']]['type_code'];
             }
             if ($pa_options['groupFields']) {
                 $vs_rel_pk = $t_rel_item->primaryKey();
                 if ($t_rel_item_label) {
                     foreach ($t_rel_item_label->getFormFields() as $vs_field => $va_field_info) {
                         if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                             continue;
                         }
                         $va_rels[$vs_v]['preferred_labels'][$vs_field] = $va_rels[$vs_v][$vs_field];
                         unset($va_rels[$vs_v][$vs_field]);
                     }
                 }
                 foreach ($t_rel_item->getFormFields() as $vs_field => $va_field_info) {
                     if (!isset($va_rels[$vs_v][$vs_field]) || $vs_field == $vs_rel_pk) {
                         continue;
                     }
                     $va_rels[$vs_v]['intrinsic'][$vs_field] = $va_rels[$vs_v][$vs_field];
                     unset($va_rels[$vs_v][$vs_field]);
                 }
                 unset($va_rels[$vs_v]['_key']);
                 unset($va_rels[$vs_v]['row_id']);
             }
         }
         if (!isset($pa_options['returnAsSearchResult']) || !$pa_options['returnAsSearchResult']) {
             // Set 'label' entry - display label in current user's locale
             foreach ($va_rels as $vs_v => $va_rel) {
                 $va_tmp = array(0 => $va_rel['labels']);
                 $va_tmp2 = caExtractValuesByUserLocale($va_tmp);
                 $va_rels[$vs_v]['label'] = array_shift($va_tmp2);
             }
         }
         //
         // END - non-self relation
         //
     }
     // Apply restrictToBundleValues
     $va_filters = caGetOption('restrictToBundleValues', $pa_options, array());
     if (is_array($va_filters) && sizeof($va_filters) > 0) {
         foreach ($va_rels as $vn_pk => $va_related_item) {
             foreach ($va_filters as $vs_filter => $va_filter_vals) {
                 if (!$vs_filter) {
                     continue;
                 }
                 if (!is_array($va_filter_vals)) {
                     $va_filter_vals = array($va_filter_vals);
                 }
                 foreach ($va_filter_vals as $vn_index => $vs_filter_val) {
                     // is value a list attribute idno?
                     $va_tmp = explode('.', $vs_filter);
                     $vs_element = array_pop($va_tmp);
                     if (!is_numeric($vs_filter_val) && (($t_element = $t_rel_item->_getElementInstance($vs_element)) && $t_element->get('datatype') == 3)) {
                         $va_filter_vals[$vn_index] = caGetListItemID($t_element->get('list_id'), $vs_filter_val);
                     }
                 }
                 $t_rel_item->load($va_related_item[$t_rel_item->primaryKey()]);
                 $va_filter_values = $t_rel_item->get($vs_filter, array('returnAsArray' => true, 'alwaysReturnItemID' => true));
                 $vb_keep = false;
                 if (is_array($va_filter_values)) {
                     foreach ($va_filter_values as $vm_filtered_val) {
                         if (!is_array($vm_filtered_val)) {
                             $vm_filtered_val = array($vm_filtered_val);
                         }
                         foreach ($vm_filtered_val as $vs_val) {
                             if (in_array($vs_val, $va_filter_vals)) {
                                 // one match is enough to keep it
                                 $vb_keep = true;
                             }
                         }
                     }
                 }
                 if (!$vb_keep) {
                     unset($va_rels[$vn_pk]);
                 }
             }
         }
     }
     //
     // Sort on fields if specified
     //
     if (is_array($va_sort_fields) && sizeof($va_rels)) {
         $va_ids = array();
         $vs_rel_pk = $t_rel_item->primaryKey();
         foreach ($va_rels as $vn_i => $va_rel) {
             $va_ids[] = $va_rel[$vs_rel_pk];
         }
         // Handle sorting on attribute values
         $vs_rel_pk = $t_rel_item->primaryKey();
         foreach ($va_sort_fields as $vn_x => $vs_sort_field) {
             if ($vs_sort_field == 'relation_id') {
                 // sort by relationship primary key
                 if ($t_item_rel) {
                     $va_sort_fields[$vn_x] = $vs_sort_field = $t_item_rel->tableName() . '.' . $t_item_rel->primaryKey();
                 }
                 continue;
             }
             $va_tmp = explode('.', $vs_sort_field);
             if ($va_tmp[0] == $vs_related_table_name) {
                 $qr_rel = $t_rel_item->makeSearchResult($va_tmp[0], $va_ids);
                 $vs_table = array_shift($va_tmp);
                 $vs_key = join(".", $va_tmp);
                 while ($qr_rel->nextHit()) {
                     $vn_pk_val = $qr_rel->get($vs_table . "." . $vs_rel_pk);
                     foreach ($va_rels as $vn_rel_id => $va_rel) {
                         if ($va_rel[$vs_rel_pk] == $vn_pk_val) {
                             $va_rels[$vn_rel_id][$vs_key] = $qr_rel->get($vs_sort_field, array("delimiter" => ";", 'sortable' => 1));
                             break;
                         }
                     }
                 }
             }
         }
         // Perform sort
         $va_rels = caSortArrayByKeyInValue($va_rels, $va_sort_fields, $vs_sort_direction);
     }
     return $va_rels;
 }
Example #17
0
					<th class="list-header-unsorted">
						<?php 
print _t('Active?');
?>
					</th>
					<th class="list-header-unsorted">
						<?php 
print _t('Last login');
?>
					</th>
					<th class="{sorter: false} list-header-nosort">&nbsp;</th>
				</tr>
			</thead>
			<tbody>
<?php 
$o_tep = new TimeExpressionParser();
foreach ($va_user_list as $va_user) {
    if ($va_user['last_login'] > 0) {
        $o_tep->setUnixTimestamps($va_user['last_login'], $va_user['last_login']);
    }
    ?>
			<tr>
				<td>
					<?php 
    print $va_user['user_name'];
    ?>
				</td>
				<td>
					<?php 
    print $va_user['lname'] . ', ' . $va_user['fname'];
    ?>
 /**
  * Gets list of messages conforming to specified options.
  * @param array $pa_options
  *		readOnly =
  *		unreadOnly = 
  *		user_id = 
  *		created_on = 
  *		transaction_id = 
  *		type = 
  * @return array
  */
 public function getMessages($pn_user_id, $pa_options = null)
 {
     $o_db = $this->getDb();
     $pb_read_only = (bool) (isset($pa_options['readOnly']) && $pa_options['readOnly']);
     $pb_unread_only = (bool) (isset($pa_options['unreadOnly']) && $pa_options['unreadOnly']);
     $pn_restrict_to_transaction_user_id = isset($pa_options['user_id']) && (int) $pa_options['user_id'] ? (int) $pa_options['user_id'] : null;
     $ps_created_on = isset($pa_options['created_on']) && (string) $pa_options['created_on'] ? (string) $pa_options['created_on'] : null;
     $va_sql_wheres = array();
     $va_sql_params = array();
     if ($pn_restrict_to_transaction_user_id) {
         $va_sql_wheres[] = "tra.user_id = ?";
         $va_sql_params[] = $pn_restrict_to_transaction_user_id;
     }
     if (is_array($pa_options) && array_key_exists('transaction_id', $pa_options)) {
         $va_sql_wheres[] = "tra.transaction_id = ?";
         $va_sql_params[] = (int) $pa_options['transaction_id'];
     }
     if ($ps_created_on) {
         $o_tep = new TimeExpressionParser();
         if ($o_tep->parse($ps_created_on) && ($va_dates = $o_tep->getUnixTimestamps())) {
             $va_sql_wheres[] = "(comm.created_on BETWEEN ? AND ?)";
             $va_sql_params[] = $va_dates['start'];
             $va_sql_params[] = $va_dates['end'];
         }
     }
     if (isset($pa_options['type']) && in_array($pa_options['type'], array('O', 'L'))) {
         $va_sql_wheres[] = "(comm.communication_type = ?)";
         $va_sql_params[] = (string) $pa_options['type'];
     }
     if (isset($pa_options['search']) && strlen($pa_options['search'])) {
         $o_search = new CommerceCommunicationSearch();
         if ($qr_hits = $o_search->search($pa_options['search'])) {
             $va_ids = array();
             while ($qr_hits->nextHit()) {
                 $va_ids[] = $qr_hits->get('communication_id');
             }
             if (sizeof($va_ids)) {
                 $va_sql_wheres[] = "(comm.communication_id IN (?))";
                 $va_sql_params[] = $va_ids;
             } else {
                 $va_sql_wheres[] = "(comm.communication_id = 0)";
             }
         }
     }
     if ($pb_read_only) {
         $va_sql_wheres[] = "(comm.read_on IS NOT NULL) AND (comm.from_user_id <> " . (int) $pn_user_id . ")";
     }
     if ($pb_unread_only) {
         $va_sql_wheres[] = "(comm.read_on IS NULL) AND (comm.from_user_id <> " . (int) $pn_user_id . ")";
     }
     $qr_res = $o_db->query($vs_sql = "\n\t \t\tSELECT comm.*, tra.short_description, tra.transaction_id, tra.created_on transaction_created_on, tra.set_id\n\t \t\tFROM ca_commerce_communications comm\n\t \t\tINNER JOIN ca_commerce_transactions AS tra ON tra.transaction_id = comm.transaction_id\n\t \t\t" . (sizeof($va_sql_wheres) ? " WHERE " . join(" AND ", $va_sql_wheres) : '') . "\n\t \t\tORDER BY\n\t \t\t\tcomm.created_on DESC\n\t \t", $va_sql_params);
     //print $vs_sql;
     $va_messages = array();
     while ($qr_res->nextRow()) {
         $va_messages[$qr_res->get('transaction_id')][] = $qr_res->getRow();
     }
     return $va_messages;
 }
Example #19
0
 public function DownloadUserReport()
 {
     $vs_download_format = $this->request->getParameter("download_format", pString);
     if (!$vs_download_format) {
         $vs_download_format = "tab";
     }
     $this->view->setVar("download_format", $vs_download_format);
     switch ($vs_download_format) {
         default:
         case "tab":
             $this->view->setVar("file_extension", "txt");
             $this->view->setVar("mimetype", "text/plain");
             $vs_delimiter_col = "\t";
             $vs_delimiter_row = "\n";
             break;
             # -----------------------------------
         # -----------------------------------
         case "csv":
             $this->view->setVar("file_extension", "txt");
             $this->view->setVar("mimetype", "text/plain");
             $vs_delimiter_col = ",";
             $vs_delimiter_row = "\n";
             break;
             # -----------------------------------
     }
     $o_db = new Db();
     $t_user = new ca_users();
     $va_fields = array("lname", "fname", "email", "user_name", "userclass", "active", "last_login");
     $va_profile_prefs = $t_user->getValidPreferences('profile');
     $va_profile_prefs_labels = array();
     foreach ($va_profile_prefs as $vs_pref) {
         $va_pref_info = $t_user->getPreferenceInfo($vs_pref);
         $va_profile_prefs_labels[$vs_pref] = $va_pref_info["label"];
     }
     $qr_users = $o_db->query("SELECT * FROM ca_users ORDER BY user_id DESC");
     if ($qr_users->numRows()) {
         $va_rows = array();
         # --- headings
         $va_row = array();
         # --- headings for field values
         foreach ($va_fields as $vs_field) {
             switch ($vs_field) {
                 case "last_login":
                     $va_row[] = _t("Last login");
                     break;
                     # --------------------
                 # --------------------
                 default:
                     $va_row[] = $t_user->getDisplayLabel("ca_users." . $vs_field);
                     break;
                     # --------------------
             }
         }
         # --- headings for profile prefs
         foreach ($va_profile_prefs_labels as $vs_pref => $vs_pref_label) {
             $va_row[] = $vs_pref_label;
         }
         $va_rows[] = join($vs_delimiter_col, $va_row);
         reset($va_fields);
         reset($va_profile_prefs_labels);
         $o_tep = new TimeExpressionParser();
         while ($qr_users->nextRow()) {
             $va_row = array();
             # --- fields
             foreach ($va_fields as $vs_field) {
                 switch ($vs_field) {
                     case "userclass":
                         $va_row[] = $t_user->getChoiceListValue($vs_field, $qr_users->get("ca_users." . $vs_field));
                         break;
                         # -----------------------
                     # -----------------------
                     case "active":
                         $va_row[] = $qr_users->get("ca_users." . $vs_field) == 1 ? _t("active") : _t("not active");
                         break;
                         # -----------------------
                     # -----------------------
                     case "last_login":
                         //if (!is_array($va_vars = $qr_users->getVars('vars'))) { $va_vars = array(); }
                         if (!is_array($va_vars = $qr_users->getVars('volatile_vars'))) {
                             $va_vars = array();
                         }
                         if ($va_vars['last_login'] > 0) {
                             $o_tep->setUnixTimestamps($va_vars['last_login'], $va_vars['last_login']);
                             $va_row[] = $o_tep->getText();
                         } else {
                             $va_row[] = "-";
                         }
                         break;
                         # -----------------------
                     # -----------------------
                     default:
                         if ($vs_download_format == "csv") {
                             $va_row[] = str_replace(",", "-", $qr_users->get("ca_users." . $vs_field));
                         } else {
                             $va_row[] = $qr_users->get("ca_users." . $vs_field);
                         }
                         break;
                         # -----------------------
                 }
             }
             # --- profile prefs
             foreach ($va_profile_prefs_labels as $vs_pref => $vs_pref_label) {
                 $t_user->load($qr_users->get("ca_users.user_id"));
                 $va_row[] = $t_user->getPreference($vs_pref);
             }
             $va_rows[] = join($vs_delimiter_col, $va_row);
         }
         $vs_file_contents = join($vs_delimiter_row, $va_rows);
         $this->view->setVar("file_contents", $vs_file_contents);
         return $this->render('user_report.php');
     } else {
         $this->notification->addNotification(_t("There are no users"), __NOTIFICATION_TYPE_INFO__);
         $this->ListUsers();
         return;
     }
 }
 public function parseValue($ps_value, $pa_element_info, $pa_options = null)
 {
     $ps_value = trim($ps_value);
     $va_settings = $this->getSettingValuesFromElementArray($pa_element_info, array('dateRangeBoundaries', 'mustNotBeBlank'));
     $o_tep = new TimeExpressionParser();
     if ($ps_value) {
         if (!$o_tep->parse($ps_value)) {
             // invalid date
             $this->postError(1970, _t('%1 is invalid', $pa_element_info['displayLabel']), 'DateRangeAttributeValue->parseValue()');
             return false;
         }
         $va_dates = $o_tep->getHistoricTimestamps();
         if ($va_settings['dateRangeBoundaries']) {
             if ($o_tep->parse($va_settings['dateRangeBoundaries'])) {
                 $va_boundary_dates = $o_tep->getHistoricTimestamps();
                 if ($va_dates[0] < $va_boundary_dates[0] || $va_dates[0] > $va_boundary_dates[1] || $va_dates[1] < $va_boundary_dates[0] || $va_dates[1] > $va_boundary_dates[1]) {
                     // date is out of bounds
                     $this->postError(1970, _t('%1 must be within %2', $pa_element_info['displayLabel'], $va_settings['dateRangeBoundaries']), 'DateRangeAttributeValue->parseValue()');
                     return false;
                 }
             }
         }
     } else {
         if ((bool) $va_settings['mustNotBeBlank']) {
             $this->postError(1970, _t('%1 must not be empty', $pa_element_info['displayLabel']), 'DateRangeAttributeValue->parseValue()');
             return false;
         } else {
             // Default to "undated" date for blanks
             $o_config = $o_tep->getLanguageSettings();
             $va_undated_dates = $o_config->getList('undatedDate');
             return array('value_longtext1' => $va_undated_dates[0], 'value_decimal1' => null, 'value_decimal2' => null);
         }
     }
     return array('value_longtext1' => $ps_value, 'value_decimal1' => $va_dates[0], 'value_decimal2' => $va_dates[1]);
 }
Example #21
0
 /**
  * Returns version of label 'display' field value suitable for sorting
  * The sortable value is the same as the display value except when the display value
  * starts with a definite article ('the' in English) or indefinite article ('a' or 'an' in English)
  * in the locale of the label, in which case the article is moved to the end of the sortable value.
  * 
  * What constitutes an article is defined in the TimeExpressionParser localization files. So if the
  * locale of the label doesn't correspond to an existing TimeExpressionParser localization, then
  * the users' current locale setting is used.
  */
 private function _generateSortableValue()
 {
     if ($vs_sort_field = $this->getProperty('LABEL_SORT_FIELD')) {
         $vs_display_field = $this->getProperty('LABEL_DISPLAY_FIELD');
         $o_tep = new TimeExpressionParser();
         $t_locale = new ca_locales();
         $o_tep->setLanguage($t_locale->localeIDToCode($this->get('locale_id')));
         $o_lang_settings = $o_tep->getLanguageSettings();
         $vs_display_value = trim(preg_replace('![^\\p{L}0-9 ]+!u', ' ', $this->get($vs_display_field)));
         $va_definite_articles = $o_lang_settings->get('definiteArticles');
         $va_indefinite_articles = $o_lang_settings->get('indefiniteArticles');
         foreach (array($o_lang_settings->get('definiteArticles'), $o_lang_settings->get('indefiniteArticles')) as $va_articles) {
             if (is_array($va_articles)) {
                 foreach ($va_articles as $vs_article) {
                     if (preg_match('!^(' . $vs_article . ')[ ]+!i', $vs_display_value, $va_matches)) {
                         $vs_display_value = trim(str_replace($va_matches[1], '', $vs_display_value) . ', ' . $va_matches[1]);
                         break 2;
                     }
                 }
             }
         }
         $this->set($vs_sort_field, $vs_display_value);
     }
 }
Example #22
0
 /**
  * 
  *
  * @param int $pn_user_id user_id of user to check set access for
  * @param int $pn_access type of access required. Use __CA_SET_READ_ACCESS__ for read-only access or __CA_SET_EDIT_ACCESS__ for editing (full) access
  * @param int $pa_set_ids The ids of the sets to check. If omitted then currently loaded set will be checked. Can also be set to a single integer.
  * @return int
  */
 public function getAccessExpirationDates($pn_user_id, $pn_access, $pa_set_ids = null, $pa_options = null)
 {
     if (!is_array($pa_set_ids)) {
         if (!$pa_set_ids) {
             $pa_set_ids = $this->get('set_id');
         }
         if (!$pa_set_ids) {
             return null;
         }
         $pa_set_ids = array($pa_set_ids);
     }
     foreach ($pa_set_ids as $vn_i => $vn_set_id) {
         $pa_set_ids[$vn_i] = (int) $vn_set_id;
     }
     $o_db = $this->getDb();
     $qr_res = $o_db->query("\n\t\t\tSELECT sxg.set_id, sxg.sdatetime, sxg.edatetime, s.user_id\n\t\t\tFROM ca_sets_x_user_groups sxg\n\t\t\tINNER JOIN ca_user_groups AS ug ON sxg.group_id = ug.group_id\n\t\t\tINNER JOIN ca_users_x_groups AS uxg ON uxg.group_id = ug.group_id\n\t\t\tINNER JOIN ca_sets AS s ON s.set_id = sxg.set_id\n\t\t\tWHERE \n\t\t\t\t(sxg.access >= ?) AND (uxg.user_id = ?) AND (sxg.set_id IN (?)) AND (s.deleted = 0)\n\t\t\t\tAND\n\t\t\t\t(\n\t\t\t\t\tsxg.sdatetime <= " . time() . " AND sxg.edatetime >= " . time() . "\n\t\t\t\t)\n\t\t", (int) $pn_access, (int) $pn_user_id, $pa_set_ids);
     $o_tep = new TimeExpressionParser();
     $va_expiration_times = array();
     while ($qr_res->nextRow()) {
         if ($qr_res->get('user_id') == $pn_user_id) {
             $va_expiration_times[$vn_set_id] = -1;
             continue;
         }
         $vn_set_id = $qr_res->get('set_id');
         $vn_exp = $qr_res->get('edatetime');
         if (!isset($va_expiration_times[$vn_set_id]) || $va_expiration_times[$vn_set_id] < $vn_exp) {
             $o_tep->setUnixTimestamps($vn_exp, $vn_exp);
             $vs_text = $o_tep->getText();
             $va_expiration_times[$vn_set_id] = $vs_text;
         }
     }
     $qr_res = $o_db->query("\n\t\t\tSELECT sxu.set_id, sxu.sdatetime, sxu.edatetime, s.user_id\n\t\t\tFROM ca_sets_x_users sxu\n\t\t\tINNER JOIN ca_users AS u ON sxu.user_id = u.user_id\n\t\t\tINNER JOIN ca_sets AS s ON s.set_id = sxu.set_id\n\t\t\tWHERE \n\t\t\t\t(sxu.access >= ?) AND (u.user_id = ?) AND (sxu.set_id IN (?)) AND (s.deleted = 0)\n\t\t\t\tAND\n\t\t\t\t(\n\t\t\t\t\tsxu.sdatetime <= " . time() . " AND sxu.edatetime >= " . time() . "\n\t\t\t\t)\n\t\t", (int) $pn_access, (int) $pn_user_id, $pa_set_ids);
     while ($qr_res->nextRow()) {
         if ($qr_res->get('user_id') == $pn_user_id) {
             $va_expiration_times[$vn_set_id] = -1;
             continue;
         }
         $vn_set_id = $qr_res->get('set_id');
         $vn_exp = $qr_res->get('edatetime');
         if (!isset($va_expiration_times[$vn_set_id]) || $va_expiration_times[$vn_set_id] < $vn_exp) {
             $o_tep->setUnixTimestamps($vn_exp, $vn_exp);
             $vs_text = $o_tep->getText();
             $va_expiration_times[$vn_set_id] = $vs_text;
         }
     }
     return $va_expiration_times;
 }
Example #23
0
 /**
  * Return list of items from the specified table that are related to the current browse set. This is the method that actually
  * pulls the facet content, regardless of whether the facet is cached yet or not. If you want to use the facet cache, call
  * BrowseEngine::getFacet()
  *
  * @see BrowseEngine::getFacet()
  * Options:
  *		checkAccess = array of access values to filter facets that have an 'access' field by
  *		checkAvailabilityOnly = if true then content is not actually fetch - only the availablility of content is verified
  *		user_id = If set item level access control is performed relative to specified user_id, otherwise defaults to logged in user
  */
 public function getFacetContent($ps_facet_name, $pa_options = null)
 {
     global $AUTH_CURRENT_USER_ID;
     $vs_browse_table_name = $this->ops_browse_table_name;
     $vs_browse_table_num = $this->opn_browse_table_num;
     $vn_user_id = isset($pa_options['user_id']) && (int) $pa_options['user_id'] ? (int) $pa_options['user_id'] : (int) $AUTH_CURRENT_USER_ID;
     $vb_show_if_no_acl = (bool) ($this->opo_config->get('default_item_access_level') > __CA_ACL_NO_ACCESS__);
     $t_user = new ca_users($vn_user_id);
     if (is_array($va_groups = $t_user->getUserGroups()) && sizeof($va_groups)) {
         $va_group_ids = array_keys($va_groups);
     } else {
         $va_group_ids = array();
     }
     if (!is_array($this->opa_browse_settings)) {
         return null;
     }
     if (!isset($this->opa_browse_settings['facets'][$ps_facet_name])) {
         return null;
     }
     if (!is_array($pa_options)) {
         $pa_options = array();
     }
     $vb_check_availability_only = isset($pa_options['checkAvailabilityOnly']) ? (bool) $pa_options['checkAvailabilityOnly'] : false;
     $va_all_criteria = $this->getCriteria();
     $va_criteria = $this->getCriteria($ps_facet_name);
     $va_facet_info = $this->opa_browse_settings['facets'][$ps_facet_name];
     $t_subject = $this->getSubjectInstance();
     if ($va_facet_info['relative_to']) {
         $vs_browse_table_name = $va_facet_info['relative_to'];
         $vs_browse_table_num = $this->opo_datamodel->getTableNum($vs_browse_table_name);
     }
     $vs_browse_type_limit_sql = '';
     if (($va_browse_type_ids = $this->getTypeRestrictionList()) && sizeof($va_browse_type_ids)) {
         // type restrictions
         $vs_browse_type_limit_sql = '(' . $t_subject->tableName() . '.' . $t_subject->getTypeFieldName() . ' IN (' . join(', ', $va_browse_type_ids) . ')' . ($t_subject->getFieldInfo('type_id', 'IS_NULL') ? " OR (" . $this->ops_browse_table_name . '.' . $t_subject->getTypeFieldName() . " IS NULL)" : '') . ')';
         if (is_array($va_facet_info['type_restrictions'])) {
             // facet type restrictions bind a facet to specific types; we check them here
             $va_restrict_to_types = $this->_convertTypeCodesToIDs($va_facet_info['type_restrictions']);
             $vb_is_ok_to_browse = false;
             foreach ($va_browse_type_ids as $vn_type_id) {
                 if (in_array($vn_type_id, $va_restrict_to_types)) {
                     $vb_is_ok_to_browse = true;
                     break;
                 }
             }
             if (!$vb_is_ok_to_browse) {
                 return array();
             }
         }
     }
     // Values to exclude from list attributes and authorities; can be idnos or ids
     $va_exclude_values = caGetOption('exclude_values', $va_facet_info, array(), array('castTo' => 'array'));
     $va_results = $this->opo_ca_browse_cache->getResults();
     $vb_single_value_is_present = false;
     $vs_single_value = isset($va_facet_info['single_value']) ? $va_facet_info['single_value'] : null;
     $va_wheres = array();
     switch ($va_facet_info['type']) {
         # -----------------------------------------------------
         case 'has':
             $vn_state = null;
             if (isset($va_all_criteria[$ps_facet_name])) {
                 break;
             }
             // only one instance of this facet allowed per browse
             if (!($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true))) {
                 break;
             }
             $vs_yes_text = isset($va_facet_info['label_yes']) && $va_facet_info['label_yes'] ? $va_facet_info['label_yes'] : _t('Yes');
             $vs_no_text = isset($va_facet_info['label_no']) && $va_facet_info['label_no'] ? $va_facet_info['label_no'] : _t('No');
             $va_facet_values = array('yes' => array('id' => 1, 'label' => $vs_yes_text), 'no' => array('id' => 0, 'label' => $vs_no_text));
             // Actually check that both yes and no values will result in something
             if ($va_facet_info['element_code']) {
                 $t_element = new ca_metadata_elements();
                 if (!$t_element->load(array('element_code' => $va_facet_info['element_code']))) {
                     break;
                 }
                 $vs_element_code = $va_facet_info['element_code'];
                 $va_facet = array();
                 $va_counts = array();
                 foreach ($va_facet_values as $vs_state_name => $va_state_info) {
                     $va_wheres = array();
                     $va_joins = array();
                     if (!(bool) $va_state_info['id']) {
                         // no option
                         $va_wheres[] = $this->ops_browse_table_name . '.' . $t_item->primaryKey() . " NOT IN (select row_id from ca_attributes where table_num = " . $t_item->tableNum() . " AND element_id = " . $t_element->getPrimaryKey() . ")";
                     } else {
                         // yes option
                         $va_joins[] = "LEFT JOIN ca_attributes AS caa ON  " . $this->ops_browse_table_name . '.' . $t_item->primaryKey() . " = caa.row_id AND " . $t_item->tableNum() . " = caa.table_num";
                         $va_wheres[] = "caa.element_id = " . $t_element->getPrimaryKey();
                     }
                     if ($t_item->hasField('deleted')) {
                         $va_wheres[] = "(" . $t_item->tableName() . ".deleted = 0)";
                     }
                     if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                         $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                     }
                     if (sizeof($va_results)) {
                         $va_wheres[] = $vs_browse_table_name . "." . $t_item->primaryKey() . " IN (" . join(",", $va_results) . ")";
                     }
                     if ($va_facet_info['relative_to']) {
                         if ($t_subject->hasField('deleted')) {
                             $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                         }
                         if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                             $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                             $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                         }
                     }
                     if ($this->opo_config->get('perform_item_level_access_checking')) {
                         if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                             // Join to limit what browse table items are used to generate facet
                             $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                             $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t\t)";
                         }
                     }
                     $vs_join_sql = join("\n", $va_joins);
                     $vs_where_sql = '';
                     if (sizeof($va_wheres) > 0) {
                         $vs_where_sql = ' WHERE ' . join(' AND ', $va_wheres);
                     }
                     if ($vb_check_availability_only) {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 2\n\t\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         if ($qr_res->nextRow()) {
                             $va_counts[$vs_state_name] = (int) $qr_res->numRows();
                         }
                     } else {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT " . $vs_browse_table_name . '.' . $t_item->primaryKey() . "\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         if ($qr_res->numRows() > 0) {
                             $va_facet[$vs_state_name] = $va_state_info;
                         } else {
                             return array();
                             // if either option in a "has" facet fails then don't show the facet
                         }
                     }
                 }
             } else {
                 $vs_rel_table_name = $va_facet_info['table'];
                 if (!is_array($va_restrict_to_relationship_types = $va_facet_info['restrict_to_relationship_types'])) {
                     $va_restrict_to_relationship_types = array();
                 }
                 $va_restrict_to_relationship_types = $this->_getRelationshipTypeIDs($va_restrict_to_relationship_types, $va_facet_info['relationship_table']);
                 if (!is_array($va_exclude_relationship_types = $va_facet_info['exclude_relationship_types'])) {
                     $va_exclude_relationship_types = array();
                 }
                 $va_exclude_relationship_types = $this->_getRelationshipTypeIDs($va_exclude_relationship_types, $va_facet_info['relationship_table']);
                 $vn_table_num = $this->opo_datamodel->getTableNum($vs_rel_table_name);
                 $vs_rel_table_pk = $this->opo_datamodel->getTablePrimaryKeyName($vn_table_num);
                 switch (sizeof($va_path = array_keys($this->opo_datamodel->getPath($vs_browse_table_name, $vs_rel_table_name)))) {
                     case 3:
                         $t_item_rel = $this->opo_datamodel->getInstanceByTableName($va_path[1], true);
                         $t_rel_item = $this->opo_datamodel->getInstanceByTableName($va_path[2], true);
                         $vs_key = 'relation_id';
                         break;
                     case 2:
                         $t_item_rel = null;
                         $t_rel_item = $this->opo_datamodel->getInstanceByTableName($va_path[1], true);
                         $vs_key = $t_rel_item->primaryKey();
                         break;
                     default:
                         // bad related table
                         return null;
                         break;
                 }
                 $vs_cur_table = array_shift($va_path);
                 $va_joins_init = array();
                 foreach ($va_path as $vs_join_table) {
                     $va_rel_info = $this->opo_datamodel->getRelationships($vs_cur_table, $vs_join_table);
                     $va_joins_init[] = ($vn_state ? 'INNER' : 'LEFT') . ' JOIN ' . $vs_join_table . ' ON ' . $vs_cur_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][0] . ' = ' . $vs_join_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][1] . "\n";
                     $vs_cur_table = $vs_join_table;
                 }
                 $va_facet = array();
                 $va_counts = array();
                 foreach ($va_facet_values as $vs_state_name => $va_state_info) {
                     $va_wheres = array();
                     $va_joins = $va_joins_init;
                     if (!(bool) $va_state_info['id']) {
                         // no option
                         $va_wheres[] = "(" . $t_rel_item->tableName() . "." . $t_rel_item->primaryKey() . " IS NULL)";
                         if ($t_rel_item->hasField('deleted')) {
                             $va_wheres[] = "((" . $t_rel_item->tableName() . ".deleted = 0) OR (" . $t_rel_item->tableName() . ".deleted IS NULL))";
                         }
                         if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access')) {
                             $va_wheres[] = "((" . $t_rel_item->tableName() . ".access NOT IN (" . join(',', $pa_options['checkAccess']) . ")) OR (" . $t_rel_item->tableName() . ".access IS NULL))";
                         }
                         if (sizeof($va_restrict_to_relationship_types) > 0 && is_object($t_item_rel)) {
                             $va_wheres[] = "((" . $t_item_rel->tableName() . ".type_id NOT IN (" . join(',', $va_restrict_to_relationship_types) . ")) OR (" . $t_item_rel->tableName() . ".type_id IS NULL))";
                         }
                         if (sizeof($va_exclude_relationship_types) > 0 && is_object($t_item_rel)) {
                             $va_wheres[] = "(" . $t_item_rel->tableName() . ".type_id IN (" . join(',', $va_exclude_relationship_types) . "))";
                         }
                     } else {
                         // yes option
                         $va_wheres[] = "(" . $t_rel_item->tableName() . "." . $t_rel_item->primaryKey() . " IS NOT NULL)";
                         if ($t_rel_item->hasField('deleted')) {
                             $va_wheres[] = "(" . $t_rel_item->tableName() . ".deleted = 0)";
                         }
                         if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access')) {
                             $va_wheres[] = "(" . $t_rel_item->tableName() . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                         }
                         if (sizeof($va_restrict_to_relationship_types) > 0 && is_object($t_item_rel)) {
                             $va_wheres[] = "(" . $t_item_rel->tableName() . ".type_id IN (" . join(',', $va_restrict_to_relationship_types) . "))";
                         }
                         if (sizeof($va_exclude_relationship_types) > 0 && is_object($t_item_rel)) {
                             $va_wheres[] = "(" . $t_item_rel->tableName() . ".type_id NOT IN (" . join(',', $va_exclude_relationship_types) . "))";
                         }
                     }
                     if ($t_item->hasField('deleted')) {
                         $va_wheres[] = "(" . $t_item->tableName() . ".deleted = 0)";
                     }
                     if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                         $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                     }
                     if (sizeof($va_results)) {
                         $va_wheres[] = $vs_browse_table_name . "." . $t_item->primaryKey() . " IN (" . join(",", $va_results) . ")";
                     }
                     if ($va_facet_info['relative_to']) {
                         if ($t_subject->hasField('deleted')) {
                             $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                         }
                         if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                             $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                             $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                         }
                     }
                     if ($this->opo_config->get('perform_item_level_access_checking')) {
                         if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                             // Join to limit what browse table items are used to generate facet
                             $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                             $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t\t)";
                         }
                     }
                     $vs_join_sql = join("\n", $va_joins);
                     $vs_where_sql = '';
                     if (sizeof($va_wheres) > 0) {
                         $vs_where_sql = ' WHERE ' . join(' AND ', $va_wheres);
                     }
                     if ($vb_check_availability_only) {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 2\n\t\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         if ($qr_res->nextRow()) {
                             $va_counts[$vs_state_name] = (int) $qr_res->numRows();
                         }
                     } else {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT " . $vs_browse_table_name . '.' . $t_item->primaryKey() . "\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t";
                         //print "$vs_sql<hr>";
                         $qr_res = $this->opo_db->query($vs_sql);
                         if ($qr_res->numRows() > 0) {
                             $va_facet[$vs_state_name] = $va_state_info;
                         } else {
                             return array();
                             // if either option in a "has" facet fails then don't show the facet
                         }
                     }
                 }
             }
             if ($vb_check_availability_only) {
                 return sizeof($va_counts) > 1 ? true : false;
             }
             return $va_facet;
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'label':
             if (!($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true))) {
                 break;
             }
             if (!($t_label = $t_item->getLabelTableInstance())) {
                 break;
             }
             if (!is_array($va_restrict_to_types = $va_facet_info['restrict_to_types'])) {
                 $va_restrict_to_types = array();
             }
             $vs_item_pk = $t_item->primaryKey();
             $vs_label_table_name = $t_label->tableName();
             $vs_label_pk = $t_label->primaryKey();
             $vs_label_display_field = $t_item->getLabelDisplayField();
             $vs_label_sort_field = $t_item->getLabelSortField();
             $vs_where_sql = $vs_join_sql = '';
             $vb_needs_join = false;
             $va_where_sql = array();
             $va_joins = array();
             if ($vs_browse_type_limit_sql) {
                 $va_where_sql[] = $vs_browse_type_limit_sql;
             }
             if (isset($va_facet_info['preferred_labels_only']) && $va_facet_info['preferred_labels_only'] && $t_label->hasField('is_preferred')) {
                 $va_where_sql[] = "l.is_preferred = 1";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_where_sql[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($t_item->hasField('deleted')) {
                 $va_where_sql[] = "(" . $vs_browse_table_name . ".deleted = 0)";
                 $vb_needs_join = true;
             }
             if (sizeof($va_restrict_to_types)) {
                 $va_restrict_to_type_ids = caMakeTypeIDList($vs_browse_table_name, $va_restrict_to_types, array('dont_include_subtypes_in_type_restriction' => true));
                 if (sizeof($va_restrict_to_type_ids)) {
                     $va_where_sql[] = "(" . $vs_browse_table_name . "." . $t_item->getTypeFieldName() . " IN (" . join(", ", $va_restrict_to_type_ids) . ")" . ($t_item->getFieldInfo('type_id', 'IS_NULL') ? " OR (" . $vs_browse_table_name . '.' . $t_item->getTypeFieldName() . " IS NULL)" : '') . ")";
                     $vb_needs_join = true;
                 }
             }
             if (sizeof($va_exclude_types)) {
                 $va_exclude_type_ids = caMakeTypeIDList($vs_browse_table_name, $va_exclude_types, array('dont_include_subtypes_in_type_restriction' => true));
                 if (sizeof($va_exclude_type_ids)) {
                     $va_where_sql[] = "(" . $vs_browse_table_name . "." . $t_item->getTypeFieldName() . " NOT IN (" . join(", ", $va_exclude_type_ids) . ")" . ($t_item->getFieldInfo('type_id', 'IS_NULL') ? " OR (" . $vs_browse_table_name . '.' . $t_item->getTypeFieldName() . " IS NULL)" : '') . ")";
                     $vb_needs_join = true;
                 }
             }
             if ($vb_needs_join) {
                 $va_joins[] = "INNER JOIN " . $vs_browse_table_name . " ON " . $vs_browse_table_name . "." . $t_item->primaryKey() . " = l." . $t_item->primaryKey();
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_where_sql[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_where_sql = array_merge($va_where_sql, $va_relative_sql_data['wheres']);
                 }
             }
             if (sizeof($va_results)) {
                 if ($va_facet_info['relative_to']) {
                     $va_where_sql[] = $this->ops_browse_table_name . "." . $t_subject->primaryKey() . " IN (" . join(",", $va_results) . ")";
                 } else {
                     $va_where_sql[] = "l.{$vs_item_pk} IN (" . join(",", $va_results) . ")";
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_where_sql[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (sizeof($va_where_sql)) {
                 $vs_where_sql = "WHERE " . join(" AND ", $va_where_sql);
             }
             if ($vb_check_availability_only) {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM {$vs_label_table_name} l\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 1\n\t\t\t\t\t\t";
                 $qr_res = $this->opo_db->query($vs_sql);
                 return (int) $qr_res->numRows() > 0 ? true : false;
             } else {
                 $vs_parent_fld = $t_item->getProperty('HIERARCHY_PARENT_ID_FLD');
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT  l.* " . ($vs_parent_fld ? ", " . $vs_browse_table_name . "." . $vs_parent_fld : '') . " \n\t\t\t\t\t\t\tFROM {$vs_label_table_name} l\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t";
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_values = array();
                 $va_child_counts = array();
                 $vn_parent_id = null;
                 while ($qr_res->nextRow()) {
                     $vn_id = $qr_res->get($t_item->primaryKey());
                     if ($vs_parent_fld) {
                         $vn_parent_id = $qr_res->get($vs_parent_fld);
                         if ($vn_parent_id) {
                             $va_child_counts[$vn_parent_id]++;
                         }
                     }
                     $va_values[$vn_id][$qr_res->get('locale_id')] = array_merge($qr_res->getRow(), array('id' => $vn_id, 'parent_id' => $vn_parent_id, 'label' => $qr_res->get($vs_label_display_field)));
                     if (!is_null($vs_single_value) && $vn_id == $vs_single_value) {
                         $vb_single_value_is_present = true;
                     }
                 }
                 if ($vs_parent_fld) {
                     foreach ($va_values as $vn_id => $va_values_by_locale) {
                         foreach ($va_values_by_locale as $vn_locale_id => $va_value) {
                             $va_values[$vn_id][$vn_locale_id]['child_count'] = (int) $va_child_counts[$vn_id];
                         }
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 $va_values = caExtractValuesByUserLocale($va_values);
                 return $va_values;
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'attribute':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $t_element = new ca_metadata_elements();
             if (!$t_element->load(array('element_code' => $va_facet_info['element_code']))) {
                 return array();
             }
             $vn_element_type = $t_element->get('datatype');
             $vn_element_id = $t_element->getPrimaryKey();
             $va_joins = array('INNER JOIN ca_attribute_values ON ca_attributes.attribute_id = ca_attribute_values.attribute_id', 'INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_attributes.row_id AND ca_attributes.table_num = ' . intval($vs_browse_table_num));
             $va_wheres = array();
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = ' AND (' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 // exclude criteria values
                 $vs_criteria_exclude_sql = '';
                 if (is_array($va_criteria) && sizeof($va_criteria)) {
                     $vs_criteria_exclude_sql = ' AND (ca_attribute_values.value_longtext1 NOT IN (' . join(", ", caQuoteList(array_keys($va_criteria))) . ')) ';
                 }
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t(ca_attribute_values.element_id = ?) {$vs_criteria_exclude_sql} {$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 2";
                 //print $vs_sql;
                 $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                 return (int) $qr_res->numRows() > 1 ? true : false;
             } else {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT value_longtext1, value_decimal1, value_longtext2, value_integer1\n\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? {$vs_where_sql}";
                 $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                 $va_values = array();
                 $va_list_items = null;
                 $va_suppress_values = null;
                 if ($va_facet_info['suppress'] && !is_array($va_facet_info['suppress'])) {
                     $va_facet_info['suppress'] = array($va_facet_info['suppress']);
                 }
                 if (!is_array($va_suppress_values = caGetOption('suppress', $va_facet_info, null))) {
                     $va_suppress_values = caGetOption('exclude_values', $va_facet_info, null);
                 }
                 switch ($vn_element_type) {
                     case __CA_ATTRIBUTE_VALUE_LIST__:
                         $va_values = $qr_res->getAllFieldValues('value_longtext1');
                         $qr_res->seek(0);
                         $t_list_item = new ca_list_items();
                         $va_list_item_cache = $t_list_item->getFieldValuesForIDs($va_values, array('idno', 'item_value', 'parent_id', 'access'));
                         $va_list_child_count_cache = array();
                         if (is_array($va_list_item_cache)) {
                             foreach ($va_list_item_cache as $vn_id => $va_item) {
                                 if (!($vn_parent_id = $va_item['parent_id'])) {
                                     continue;
                                 }
                                 if (is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && !in_array($va_item['access'], $pa_options['checkAccess'])) {
                                     continue;
                                 }
                                 $va_list_child_count_cache[$vn_parent_id]++;
                             }
                         }
                         $va_list_label_cache = $t_list_item->getPreferredDisplayLabelsForIDs($va_values);
                         // Translate value idnos to ids
                         if (is_array($va_suppress_values)) {
                             $va_suppress_values = ca_lists::getItemIDsFromList($t_element->get('list_id'), $va_suppress_values);
                         }
                         $va_facet_list = array();
                         foreach ($va_values as $vn_val) {
                             if (!$vn_val) {
                                 continue;
                             }
                             if (is_array($va_suppress_values) && in_array($vn_val, $va_suppress_values)) {
                                 continue;
                             }
                             if (is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && !in_array($va_item['access'], $pa_options['checkAccess'])) {
                                 continue;
                             }
                             if ($va_criteria[$vn_val]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             $vn_child_count = isset($va_list_child_count_cache[$vn_val]) ? $va_list_child_count_cache[$vn_val] : 0;
                             $va_facet_list[$vn_val] = array('id' => $vn_val, 'label' => html_entity_decode($va_list_label_cache[$vn_val]), 'parent_id' => isset($va_list_item_cache[$vn_val]['parent_id']) ? $va_list_item_cache[$vn_val]['parent_id'] : null, 'child_count' => $vn_child_count);
                         }
                         // preserve order of list
                         $va_values_sorted_by_list_order = array();
                         if (is_array($va_list_item_cache)) {
                             foreach ($va_list_item_cache as $vn_item_id => $va_item) {
                                 if (isset($va_facet_list[$vn_item_id])) {
                                     $va_values_sorted_by_list_order[$vn_item_id] = $va_facet_list[$vn_item_id];
                                 }
                             }
                         }
                         return caSortArrayByKeyInValue($va_values_sorted_by_list_order, array('label'));
                         break;
                     case __CA_ATTRIBUTE_VALUE_OBJECTS__:
                     case __CA_ATTRIBUTE_VALUE_ENTITIES__:
                     case __CA_ATTRIBUTE_VALUE_PLACES__:
                     case __CA_ATTRIBUTE_VALUE_OCCURRENCES__:
                     case __CA_ATTRIBUTE_VALUE_COLLECTIONS__:
                     case __CA_ATTRIBUTE_VALUE_LOANS__:
                     case __CA_ATTRIBUTE_VALUE_MOVEMENTS__:
                     case __CA_ATTRIBUTE_VALUE_STORAGELOCATIONS__:
                     case __CA_ATTRIBUTE_VALUE_OBJECTLOTS__:
                         if ($t_rel_item = AuthorityAttributeValue::elementTypeToInstance($vn_element_type)) {
                             $va_ids = $qr_res->getAllFieldValues('value_integer1');
                             $va_auth_items = $t_rel_item->getPreferredDisplayLabelsForIDs($va_ids);
                             $qr_res->seek(0);
                         }
                         break;
                     default:
                         if (isset($va_facet_info['suppress']) && is_array($va_facet_info['suppress'])) {
                             $va_suppress_values = $va_facet_info['suppress'];
                         }
                         break;
                 }
                 while ($qr_res->nextRow()) {
                     $o_attr = Attribute::getValueInstance($vn_element_type, $qr_res->getRow(), true);
                     if (!($vs_val = trim($o_attr->getDisplayValue()))) {
                         continue;
                     }
                     if (is_array($va_suppress_values) && in_array($vs_val, $va_suppress_values)) {
                         continue;
                     }
                     if ($va_criteria[$vs_val]) {
                         continue;
                     }
                     // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                     switch ($vn_element_type) {
                         case __CA_ATTRIBUTE_VALUE_LIST__:
                             $vn_child_count = 0;
                             if ($va_list_parent_ids[$vs_val]) {
                                 $vn_child_count++;
                             }
                             $va_values[$vs_val] = array('id' => str_replace('/', '&#47;', $vs_val), 'label' => html_entity_decode($va_list_items[$vs_val]['name_plural'] ? $va_list_items[$vs_val]['name_plural'] : $va_list_items[$vs_val]['item_value']), 'parent_id' => $va_list_items[$vs_val]['parent_id'], 'child_count' => $vn_child_count);
                             break;
                         case __CA_ATTRIBUTE_VALUE_OBJECTS__:
                         case __CA_ATTRIBUTE_VALUE_ENTITIES__:
                         case __CA_ATTRIBUTE_VALUE_PLACES__:
                         case __CA_ATTRIBUTE_VALUE_OCCURRENCES__:
                         case __CA_ATTRIBUTE_VALUE_COLLECTIONS__:
                         case __CA_ATTRIBUTE_VALUE_LOANS__:
                         case __CA_ATTRIBUTE_VALUE_MOVEMENTS__:
                         case __CA_ATTRIBUTE_VALUE_STORAGELOCATIONS__:
                         case __CA_ATTRIBUTE_VALUE_OBJECTLOTS__:
                             $va_values[$vs_val] = array('id' => $vn_id, 'label' => html_entity_decode($va_auth_items[$vn_id] ? $va_auth_items[$vn_id] : $vs_val));
                             break;
                         case __CA_ATTRIBUTE_VALUE_CURRENCY__:
                             $va_values[sprintf("%014.2f", preg_replace("![\\D]+!", "", $vs_val))] = array('id' => str_replace('/', '&#47;', $vs_val), 'label' => $vs_val);
                             break;
                         default:
                             $va_values[$vs_val] = array('id' => str_replace('/', '&#47;', $vs_val), 'label' => $vs_val);
                             break;
                     }
                     if (!is_null($vs_single_value) && $vs_val == $vs_single_value) {
                         $vb_single_value_is_present = true;
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 ksort($va_values);
                 return $va_values;
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'location':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $vs_sort_field = null;
             if ($t_item->getProperty('ID_NUMBERING_ID_FIELD') == $vs_field_name) {
                 $vs_sort_field = $t_item->getProperty('ID_NUMBERING_SORT_FIELD');
             }
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             $va_wheres[] = "({$vs_browse_table_name}.current_loc_class IS NOT NULL)";
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = '(' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 if (sizeof($va_criteria) > 0) {
                     return false;
                 }
                 // only one current location criteria allowed
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 2";
                 $qr_res = $this->opo_db->query($vs_sql);
                 if ($qr_res->nextRow()) {
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 }
                 return false;
             } else {
                 if (sizeof($va_criteria) > 0) {
                     return array();
                 }
                 // only one current location criteria allowed
                 $vs_pk = $t_item->primaryKey();
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT {$vs_browse_table_name}.current_loc_class, {$vs_browse_table_name}.current_loc_subclass, {$vs_browse_table_name}.current_loc_id\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}";
                 if ($vs_sort_field) {
                     $vs_sql .= " ORDER BY {$vs_sort_field}";
                 }
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_collapse_map = $this->getCollapseMapForLocationFacet($va_facet_info);
                 $va_values = $va_values_by_table = array();
                 while ($qr_res->nextRow()) {
                     if (!($vs_loc_class = trim($qr_res->get('current_loc_class')))) {
                         continue;
                     }
                     if (!($vs_loc_subclass = trim($qr_res->get('current_loc_subclass')))) {
                         continue;
                     }
                     if (!($vs_loc_id = trim($qr_res->get('current_loc_id')))) {
                         continue;
                     }
                     $vs_val = "{$vs_loc_class}:{$vs_loc_subclass}:{$vs_loc_id}";
                     if ($va_criteria[$vs_val]) {
                         continue;
                     }
                     // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                     $va_values_by_table[$vs_loc_class][$vs_loc_subclass][$vs_loc_id] = true;
                 }
                 foreach ($va_values_by_table as $vs_loc_class => $va_loc_id_by_subclass) {
                     foreach ($va_loc_id_by_subclass as $vs_loc_subclass => $va_loc_ids) {
                         if (sizeof($va_tmp = array_keys($va_loc_ids))) {
                             $vs_loc_table_name = $this->opo_datamodel->getTableName($vs_loc_class);
                             if (($vs_table_name = $vs_loc_table_name) == 'ca_objects_x_storage_locations') {
                                 $vs_table_name = 'ca_storage_locations';
                             }
                             $qr_res = caMakeSearchResult($vs_table_name, $va_tmp);
                             if (isset($va_collapse_map[$vs_table_name]) && isset($va_collapse_map[$vs_table_name]['*']) && $va_collapse_map[$vs_table_name]['*']) {
                                 $va_values[$vs_id = "{$vs_loc_class}"] = array('id' => $vs_id, 'label' => $va_collapse_map[$vs_table_name]['*']);
                                 continue;
                             }
                             while ($qr_res->nextHit()) {
                                 $vn_id = $qr_res->getPrimaryKey();
                                 $va_config = ca_objects::getConfigurationForCurrentLocationType($vs_table_name, $vs_loc_subclass, array('facet' => isset($va_facet_info['display']) ? $va_facet_info['display'] : null));
                                 $vs_template = isset($va_config['template']) ? $va_config['template'] : "^{$vs_table_name}.preferred_labels";
                                 if (isset($va_collapse_map[$vs_table_name]) && isset($va_collapse_map[$vs_table_name][$vs_loc_subclass]) && $va_collapse_map[$vs_table_name][$vs_loc_subclass]) {
                                     $va_values[$vs_id = "{$vs_loc_class}:{$vs_loc_subclass}"] = array('id' => $vs_id, 'label' => $va_collapse_map[$vs_table_name][$vs_loc_subclass]);
                                     continue;
                                 }
                                 $va_values[$vs_id = "{$vs_loc_class}:{$vs_loc_subclass}:{$vn_id}"] = array('id' => $vs_id, 'label' => $qr_res->getWithTemplate($vs_template));
                             }
                         }
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return caSortArrayByKeyInValue($va_values, array('label'));
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'fieldList':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $vs_field_name = $va_facet_info['field'];
             $va_field_info = $t_item->getFieldInfo($vs_field_name);
             $t_list = new ca_lists();
             $t_list_item = new ca_list_items();
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             if (isset($va_field_info['LIST_CODE']) && ($vs_list_name = $va_field_info['LIST_CODE'])) {
                 // Handle fields containing ca_list_item.item_id's
                 $va_joins = array('INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $vs_field_name . ' = li.item_id', 'INNER JOIN ca_lists ON ca_lists.list_id = li.list_id');
                 if (sizeof($va_results) && $this->numCriteria() > 0) {
                     $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
                 }
                 if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                     $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                     $va_wheres[] = "(li.access IN (" . join(',', $pa_options['checkAccess']) . "))";
                 }
                 if ($vs_browse_type_limit_sql) {
                     $va_wheres[] = $vs_browse_type_limit_sql;
                 }
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_facet_info['relative_to']) {
                     if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                         $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                         $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                     }
                 }
                 if (is_array($va_criteria) && sizeof($va_criteria)) {
                     $va_wheres[] = "(li.item_id NOT IN (" . join(",", array_keys($va_criteria)) . "))";
                 }
                 if ($this->opo_config->get('perform_item_level_access_checking')) {
                     if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                         // Join to limit what browse table items are used to generate facet
                         $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                         $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t)";
                     }
                 }
                 $vs_join_sql = join("\n", $va_joins);
                 if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                     $vs_where_sql = ' AND (' . $vs_where_sql . ')';
                 }
                 if ($vb_check_availability_only) {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\tFROM ca_list_items li\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\tca_lists.list_code = ? {$vs_where_sql}\n\t\t\t\t\t\t\t\tLIMIT 2";
                     $qr_res = $this->opo_db->query($vs_sql, $vs_list_name);
                     return (int) $qr_res->numRows() > 1 ? true : false;
                 } else {
                     // Get label ordering fields
                     $va_ordering_fields_to_fetch = isset($va_facet_info['order_by_label_fields']) && is_array($va_facet_info['order_by_label_fields']) ? $va_facet_info['order_by_label_fields'] : array();
                     $va_orderbys = array();
                     $t_rel_item_label = new ca_list_item_labels();
                     foreach ($va_ordering_fields_to_fetch as $vs_sort_by_field) {
                         if (!$t_rel_item_label->hasField($vs_sort_by_field)) {
                             continue;
                         }
                         $va_orderbys[] = $va_label_selects[] = 'lil.' . $vs_sort_by_field;
                     }
                     $vs_order_by = sizeof($va_orderbys) ? "ORDER BY " . join(', ', $va_orderbys) : '';
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT DISTINCT lil.item_id, lil.name_singular, lil.name_plural, lil.locale_id\n\t\t\t\t\t\t\t\tFROM ca_list_items li\n\t\t\t\t\t\t\t\tINNER JOIN ca_list_item_labels AS lil ON lil.item_id = li.item_id\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\tca_lists.list_code = ?  AND lil.is_preferred = 1 {$vs_where_sql} {$vs_order_by}";
                     //print $vs_sql." [$vs_list_name]";
                     $qr_res = $this->opo_db->query($vs_sql, $vs_list_name);
                     $va_values = array();
                     while ($qr_res->nextRow()) {
                         $vn_id = $qr_res->get('item_id');
                         if ($va_criteria[$vn_id]) {
                             continue;
                         }
                         // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                         $va_values[$vn_id][$qr_res->get('locale_id')] = array('id' => $vn_id, 'label' => $qr_res->get('name_plural'));
                         if (!is_null($vs_single_value) && $vn_id == $vs_single_value) {
                             $vb_single_value_is_present = true;
                         }
                     }
                     if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                         return array();
                     }
                     return caExtractValuesByUserLocale($va_values);
                 }
             } else {
                 if ($vs_list_name = $va_field_info['LIST']) {
                     $va_list_items_by_value = array();
                     // fields with values set according to ca_list_items (not a foreign key ref)
                     if ($va_list_items = caExtractValuesByUserLocale($t_list->getItemsForList($vs_list_name))) {
                         foreach ($va_list_items as $vn_id => $va_list_item) {
                             $va_list_items_by_value[$va_list_item['item_value']] = $va_list_item['name_plural'];
                         }
                     } else {
                         foreach ($va_field_info['BOUNDS_CHOICE_LIST'] as $vs_val => $vn_id) {
                             $va_list_items_by_value[$vn_id] = $vs_val;
                         }
                     }
                     if (sizeof($va_results) && $this->numCriteria() > 0) {
                         $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
                     }
                     if ($vs_browse_type_limit_sql) {
                         $va_wheres[] = $vs_browse_type_limit_sql;
                     }
                     if ($t_subject->hasField('deleted')) {
                         $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                     }
                     if ($va_facet_info['relative_to']) {
                         if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                             $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                             $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                         }
                     }
                     if ($this->opo_config->get('perform_item_level_access_checking')) {
                         if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                             // Join to limit what browse table items are used to generate facet
                             $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                             $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t\t)";
                         }
                     }
                     if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                         $vs_where_sql = '(' . $vs_where_sql . ')';
                     }
                     $vs_join_sql = join("\n", $va_joins);
                     if ($vb_check_availability_only) {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t" . ($vs_where_sql ? 'WHERE' : '') . "\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 2";
                         $qr_res = $this->opo_db->query($vs_sql);
                         return (int) $qr_res->numRows() > 1 ? true : false;
                     } else {
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT DISTINCT " . $vs_browse_table_name . '.' . $vs_field_name . "\n\t\t\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t" . ($vs_where_sql ? 'WHERE' : '') . "\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql}";
                         //print $vs_sql." [$vs_list_name]";
                         $qr_res = $this->opo_db->query($vs_sql);
                         $va_values = array();
                         while ($qr_res->nextRow()) {
                             $vn_id = $qr_res->get($vs_field_name);
                             if ($va_criteria[$vn_id]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             if (isset($va_list_items_by_value[$vn_id])) {
                                 $va_values[$vn_id] = array('id' => $vn_id, 'label' => $va_list_items_by_value[$vn_id]);
                                 if (!is_null($vs_single_value) && $vn_id == $vs_single_value) {
                                     $vb_single_value_is_present = true;
                                 }
                             }
                         }
                         if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                             return array();
                         }
                         return $va_values;
                     }
                 } else {
                     if ($t_browse_table = $this->opo_datamodel->getInstanceByTableName($vs_facet_table = $va_facet_info['table'], true)) {
                         // Handle fields containing ca_list_item.item_id's
                         $va_joins = array('INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $vs_field_name . ' = ' . $vs_facet_table . '.' . $t_browse_table->primaryKey());
                         $vs_display_field_name = null;
                         if (method_exists($t_browse_table, 'getLabelTableInstance')) {
                             $t_label_instance = $t_browse_table->getLabelTableInstance();
                             $vs_display_field_name = isset($va_facet_info['display']) && $va_facet_info['display'] ? $va_facet_info['display'] : $t_label_instance->getDisplayField();
                             $va_joins[] = 'INNER JOIN ' . $t_label_instance->tableName() . " AS lab ON lab." . $t_browse_table->primaryKey() . ' = ' . $t_browse_table->tableName() . '.' . $t_browse_table->primaryKey();
                         }
                         if (sizeof($va_results) && $this->numCriteria() > 0) {
                             $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
                         }
                         if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                             $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                         }
                         if ($vs_browse_type_limit_sql) {
                             $va_wheres[] = $vs_browse_type_limit_sql;
                         }
                         if ($va_facet_info['relative_to']) {
                             if ($t_subject->hasField('deleted')) {
                                 $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                             }
                             if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                                 $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                                 $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                             }
                         }
                         if ($this->opo_config->get('perform_item_level_access_checking')) {
                             if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                                 // Join to limit what browse table items are used to generate facet
                                 $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                                 $va_wheres[] = "(\n\t\t\t\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t\t\t\t)";
                             }
                         }
                         $vs_join_sql = join("\n", $va_joins);
                         if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                             $vs_where_sql = 'WHERE (' . $vs_where_sql . ')';
                         }
                         if ($vb_check_availability_only) {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\t\tFROM {$vs_facet_table}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\t\tLIMIT 1";
                             $qr_res = $this->opo_db->query($vs_sql);
                             return (int) $qr_res->numRows() > 0 ? true : false;
                         } else {
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\t\tSELECT DISTINCT *\n\t\t\t\t\t\t\t\t\t\tFROM {$vs_facet_table}\n\t\t\t\t\t\t\t\t\t\t\n\t\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql}";
                             //print $vs_sql;
                             $qr_res = $this->opo_db->query($vs_sql);
                             $va_values = array();
                             $vs_pk = $t_browse_table->primaryKey();
                             while ($qr_res->nextRow()) {
                                 $vn_id = $qr_res->get($vs_pk);
                                 if ($va_criteria[$vn_id]) {
                                     continue;
                                 }
                                 // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                                 $va_values[$vn_id][$qr_res->get('locale_id')] = array('id' => $vn_id, 'label' => $qr_res->get($vs_display_field_name));
                                 if (!is_null($vs_single_value) && $vn_id == $vs_single_value) {
                                     $vb_single_value_is_present = true;
                                 }
                             }
                             if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                                 return array();
                             }
                             return caExtractValuesByUserLocale($va_values);
                         }
                     }
                 }
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'field':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             if (!is_array($va_restrict_to_types = $va_facet_info['restrict_to_types'])) {
                 $va_restrict_to_types = array();
             }
             if (!is_array($va_restrict_to_types = $this->_convertTypeCodesToIDs($va_restrict_to_types, array('instance' => $t_item, 'dontExpandHierarchically' => true)))) {
                 $va_restrict_to_types = array();
             }
             $va_restrict_to_types_expanded = $this->_convertTypeCodesToIDs($va_restrict_to_types, array('instance' => $t_item));
             $vs_field_name = $va_facet_info['field'];
             $va_field_info = $t_item->getFieldInfo($vs_field_name);
             $vs_sort_field = null;
             if ($t_item->getProperty('ID_NUMBERING_ID_FIELD') == $vs_field_name) {
                 $vs_sort_field = $vs_browse_table_name . '.' . $t_item->getProperty('ID_NUMBERING_SORT_FIELD');
             }
             $t_list = new ca_lists();
             $t_list_item = new ca_list_items();
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             $va_facet_values = null;
             if ($vb_is_bit = $va_field_info['FIELD_TYPE'] == FT_BIT) {
                 $vs_yes_text = caGetOption('label_yes', $va_facet_info, _t('Yes'));
                 $vs_no_text = caGetOption('label_no', $va_facet_info, _t('No'));
                 $va_facet_values = array(1 => array('id' => 1, 'label' => $vs_yes_text), 0 => array('id' => 0, 'label' => $vs_no_text));
             }
             if (is_array($va_restrict_to_types) && sizeof($va_restrict_to_types) > 0 && method_exists($t_rel_item, "getTypeList")) {
                 $va_wheres[] = "{$va_restrict_to_types_expanded}.type_id IN (" . join(',', caGetOption('dont_include_subtypes', $va_facet_info, false) ? $va_restrict_to_types : $va_restrict_to_types_expanded) . ")";
                 $va_selects[] = "{$va_restrict_to_types_expanded}.type_id";
             }
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = '(' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT {$vs_browse_table_name}.{$vs_field_name}\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 2";
                 $qr_res = $this->opo_db->query($vs_sql);
                 if ($qr_res->numRows() > 1) {
                     return true;
                 }
                 return false;
             } else {
                 $vs_pk = $t_item->primaryKey();
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT {$vs_browse_table_name}.{$vs_field_name}\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}";
                 if ($vs_sort_field) {
                     $vs_sql .= " ORDER BY {$vs_sort_field}";
                 }
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_values = array();
                 while ($qr_res->nextRow()) {
                     if (!($vs_val = trim($qr_res->get($vs_field_name))) && !$vb_is_bit) {
                         continue;
                     }
                     if ($va_criteria[$vs_val]) {
                         continue;
                     }
                     // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                     if ($vb_is_bit && isset($va_facet_values[$vs_val])) {
                         $va_values[$vs_val] = $va_facet_values[$vs_val];
                     } else {
                         $va_values[$vs_val] = array('id' => str_replace('/', '&#47;', $vs_val), 'label' => $vs_val);
                     }
                     if (!is_null($vs_single_value) && $vs_val == $vs_single_value) {
                         $vb_single_value_is_present = true;
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return $va_values;
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'violations':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $vs_field_name = $va_facet_info['field'];
             $va_field_info = $t_item->getFieldInfo($vs_field_name);
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             $va_facet_values = null;
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = '(' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\tINNER JOIN ca_metadata_dictionary_rule_violations ON ca_metadata_dictionary_rule_violations.row_id = {$vs_browse_table_name}." . $t_item->primaryKey() . " AND ca_metadata_dictionary_rule_violations.table_num = {$vs_browse_table_num}\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\tLIMIT 2";
                 $qr_res = $this->opo_db->query($vs_sql);
                 if ($qr_res->nextRow()) {
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 }
                 return false;
             } else {
                 $vs_pk = $t_item->primaryKey();
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT ca_metadata_dictionary_rules.rule_id\n\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\tINNER JOIN ca_metadata_dictionary_rule_violations ON ca_metadata_dictionary_rule_violations.row_id = {$vs_browse_table_name}." . $t_item->primaryKey() . " AND ca_metadata_dictionary_rule_violations.table_num = {$vs_browse_table_num}\n\t\t\t\t\t\t\tINNER JOIN ca_metadata_dictionary_rules ON ca_metadata_dictionary_rules.rule_id = ca_metadata_dictionary_rule_violations.rule_id\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t{$vs_where_sql}";
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_values = array();
                 $t_rule = new ca_metadata_dictionary_rules();
                 while ($qr_res->nextRow()) {
                     if ($t_rule->load($qr_res->get('rule_id'))) {
                         if (!($vs_val = trim($t_rule->getSetting('label')))) {
                             continue;
                         }
                         $vs_code = $t_rule->get('rule_code');
                         if ($va_criteria[$vs_val]) {
                             continue;
                         }
                         // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                         if (isset($va_facet_values[$vs_code])) {
                             $va_values[$vs_code] = $va_facet_values[$vs_code];
                         } else {
                             $va_values[$vs_code] = array('id' => $vs_code, 'label' => $vs_val);
                         }
                         if (!is_null($vs_single_value) && $vs_code == $vs_single_value) {
                             $vb_single_value_is_present = true;
                         }
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return $va_values;
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'checkouts':
             if ($vs_browse_table_name != 'ca_objects') {
                 return array();
             }
             $t_item = new ca_objects();
             $va_joins = array();
             $va_wheres = array();
             $vs_where_sql = '';
             $va_facet_values = null;
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             $vs_checkout_join_sql = "INNER JOIN ca_object_checkouts ON ca_object_checkouts.object_id = ca_objects.object_id";
             $vn_current_time = time();
             switch ($va_facet_info['status']) {
                 case 'overdue':
                     $va_wheres[] = "((ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL) AND (ca_object_checkouts.due_date <= {$vn_current_time}))";
                     break;
                 case 'reserved':
                     $va_wheres[] = "((ca_object_checkouts.checkout_date IS NULL) AND (ca_object_checkouts.return_date IS NULL))";
                     break;
                 case 'available':
                     $vs_checkout_join_sql = '';
                     $va_wheres[] = "(ca_objects.object_id NOT IN (SELECT object_id FROM ca_object_checkouts WHERE (ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL)))";
                     break;
                 default:
                 case 'out':
                     $va_wheres[] = "((ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL))";
                     break;
             }
             if ($vs_checkout_join_sql) {
                 $va_joins[] = $vs_checkout_join_sql;
                 $va_joins[] = "INNER JOIN ca_users ON ca_object_checkouts.user_id = ca_users.user_id";
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = '(' . $vs_where_sql . ')';
             }
             if ($vb_check_availability_only) {
                 switch ($va_facet_info['mode']) {
                     case 'user':
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM ca_objects\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql} AND ca_objects.deleted = 0\n\t\t\t\t\t\t\t\t\tLIMIT 2";
                         break;
                     default:
                     case 'all':
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\t\tFROM ca_objects\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\tca_objects.deleted = 0 " . (sizeof($va_results) ? "AND (" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tLIMIT 2";
                         break;
                 }
                 $qr_res = $this->opo_db->query($vs_sql);
                 if ($qr_res->nextRow()) {
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 }
                 return false;
             } else {
                 $va_values = array();
                 $vs_pk = $t_item->primaryKey();
                 switch ($va_facet_info['mode']) {
                     case 'user':
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT DISTINCT ca_object_checkouts.user_id, ca_users.fname, ca_users.lname, ca_users.email\n\t\t\t\t\t\t\t\t\tFROM ca_objects\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql} " . (sizeof($va_results) ? " AND (" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))" : "");
                         $qr_res = $this->opo_db->query($vs_sql);
                         while ($qr_res->nextRow()) {
                             $vn_user_id = $qr_res->get('user_id');
                             $vs_val = $qr_res->get('fname') . ' ' . $qr_res->get('lname') . (($vs_email = $qr_res->get('email')) ? "({$vs_email})" : '');
                             if ($va_criteria[$vs_val]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             if (isset($va_facet_values[$vn_user_id])) {
                                 $va_values[$vn_user_id] = $va_facet_values[$vn_user_id];
                             } else {
                                 $va_values[$vn_user_id] = array('id' => $vn_user_id, 'label' => $vs_val);
                             }
                             if (!is_null($vs_single_value) && $vn_user_id == $vs_single_value) {
                                 $vb_single_value_is_present = true;
                             }
                         }
                         break;
                     case 'all':
                     default:
                         foreach (array(_t('Available') => 'available', _t('Out') => 'out', _t('Reserved') => 'reserved', _t('Overdue') => 'overdue') as $vs_status_text => $vs_status) {
                             $vs_join_sql = "INNER JOIN ca_object_checkouts ON ca_object_checkouts.object_id = ca_objects.object_id";
                             switch ($vs_status) {
                                 case 'overdue':
                                     $vs_where = "((ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL) AND (ca_object_checkouts.due_date <= {$vn_current_time}))";
                                     break;
                                 case 'reserved':
                                     $vs_where = "((ca_object_checkouts.checkout_date IS NULL) AND (ca_object_checkouts.return_date IS NULL))";
                                     break;
                                 case 'available':
                                     $vs_join_sql = '';
                                     $vs_where = "(ca_objects.object_id NOT IN (SELECT object_id FROM ca_object_checkouts WHERE (ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL)))";
                                     break;
                                 default:
                                 case 'out':
                                     $vs_where = "((ca_object_checkouts.checkout_date <= {$vn_current_time}) AND (ca_object_checkouts.return_date IS NULL))";
                                     break;
                             }
                             if (sizeof($va_results) && $this->numCriteria() > 0) {
                                 $vs_where .= " AND (" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
                             }
                             $vs_sql = "\n\t\t\t\t\t\t\t\t\t\tSELECT count(*) c\n\t\t\t\t\t\t\t\t\t\tFROM ca_objects\n\t\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\t\t{$vs_where}\n\t\t\t\t\t\t\t\t\t";
                             $qr_res = $this->opo_db->query($vs_sql);
                             $qr_res->nextRow();
                             if (!$qr_res->get('c')) {
                                 continue;
                             }
                             $va_values[$vs_status] = array('id' => $vs_status, 'label' => $vs_status_text);
                             if (!is_null($vs_single_value) && $vs_status == $vs_single_value) {
                                 $vb_single_value_is_present = true;
                             }
                         }
                         break;
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return $va_values;
             }
             return array();
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'normalizedDates':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $t_element = new ca_metadata_elements();
             $vb_is_element = $vb_is_field = false;
             if (!($vb_is_element = $t_element->load(array('element_code' => $va_facet_info['element_code']))) && !($vb_is_field = $t_item->hasField($va_facet_info['element_code']) && $t_item->getFieldInfo($va_facet_info['element_code'], 'FIELD_TYPE') === FT_HISTORIC_DATERANGE)) {
                 return array();
             }
             if ($vb_is_element) {
                 $va_joins = array('INNER JOIN ca_attribute_values ON ca_attributes.attribute_id = ca_attribute_values.attribute_id', 'INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_attributes.row_id AND ca_attributes.table_num = ' . intval($vs_browse_table_num));
             } else {
                 $va_joins = array();
             }
             $va_wheres = array();
             $vs_normalization = $va_facet_info['normalization'];
             // how do we construct the date ranges presented to uses. In other words - how do we want to allow users to browse dates? By year, decade, century?
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_where_sql = '';
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = ' AND (' . $vs_where_sql . ')';
             }
             $vs_join_sql = join("\n", $va_joins);
             if ($vb_is_element) {
                 $vn_element_id = $t_element->getPrimaryKey();
                 $vs_dir = strtoupper($va_facet_info['sort']) === 'DESC' ? "DESC" : "ASC";
                 $o_tep = new TimeExpressionParser();
                 $vn_min_date = $vn_max_date = null;
                 $vs_min_sql = $vs_max_sql = '';
                 if (isset($va_facet_info['minimum_date'])) {
                     if ($o_tep->parse($va_facet_info['minimum_date'])) {
                         $va_tmp = $o_tep->getHistoricTimestamps();
                         $vn_min_date = (double) $va_tmp['start'];
                         $vs_min_sql = " AND (ca_attribute_values.value_decimal1 >= {$vn_min_date})";
                     }
                 }
                 if (isset($va_facet_info['maximum_date'])) {
                     if ($o_tep->parse($va_facet_info['maximum_date'])) {
                         $va_tmp = $o_tep->getHistoricTimestamps();
                         $vn_max_date = (double) $va_tmp['end'];
                         $vs_max_sql = " AND (ca_attribute_values.value_decimal2 <= {$vn_max_date})";
                     }
                 }
                 if ($vb_check_availability_only) {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 1";
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 } else {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT DISTINCT ca_attribute_values.value_decimal1, ca_attribute_values.value_decimal2\n\t\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t";
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                     $vn_current_year = (int) date("Y");
                     $va_values = array();
                     $vb_include_unknown = (bool) caGetOption('include_unknown', $va_facet_info, false);
                     $vb_unknown_is_set = false;
                     while ($qr_res->nextRow()) {
                         $vn_start = $qr_res->get('value_decimal1');
                         $vn_end = $qr_res->get('value_decimal2');
                         if (!($vn_start && $vn_end)) {
                             if ($vb_include_unknown) {
                                 $vb_unknown_is_set = true;
                             }
                             continue;
                         }
                         if ($vn_end > $vn_current_year + 50) {
                             continue;
                         }
                         // bad years can make for large facets that cause timeouts so cut it off 50 years into the future
                         $va_normalized_values = $o_tep->normalizeDateRange($vn_start, $vn_end, $vs_normalization);
                         foreach ($va_normalized_values as $vn_sort_value => $vs_normalized_value) {
                             if ($va_criteria[$vs_normalized_value]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             if (is_numeric($vs_normalized_value) && (int) $vs_normalized_value === 0) {
                                 continue;
                             }
                             // don't include year=0
                             $va_values[$vn_sort_value][$vs_normalized_value] = array('id' => $vs_normalized_value, 'label' => $vs_normalized_value);
                             if (!is_null($vs_single_value) && $vs_normalized_value == $vs_single_value) {
                                 $vb_single_value_is_present = true;
                             }
                         }
                     }
                     if ($vb_include_unknown && !$vb_unknown_is_set) {
                         // Check for rows where no data is set at all as opposed to null dates
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT DISTINCT ca_attributes.row_id\n\t\t\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t";
                         //print $vs_sql;
                         $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                         if ($qr_res->numRows() < sizeof($va_results)) {
                             $vb_unknown_is_set = true;
                         }
                     }
                     if ($vb_unknown_is_set && sizeof($va_values) > 0) {
                         $va_values['999999999'][_t('Date unknown')] = array('id' => 'null', 'label' => _t('Date unknown'));
                     }
                     if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                         return array();
                     }
                     ksort($va_values);
                     if ($vs_dir == 'DESC') {
                         $va_values = array_reverse($va_values);
                     }
                     $va_sorted_values = array();
                     foreach ($va_values as $vn_sort_value => $va_values_for_sort_value) {
                         $va_sorted_values = array_merge($va_sorted_values, $va_values_for_sort_value);
                     }
                     return $va_sorted_values;
                 }
             } else {
                 // is intrinsic
                 $vs_dir = strtoupper($va_facet_info['sort']) === 'DESC' ? "DESC" : "ASC";
                 $vs_browse_start_fld = $t_item->getFieldInfo($va_facet_info['element_code'], 'START');
                 $vs_browse_end_fld = $t_item->getFieldInfo($va_facet_info['element_code'], 'END');
                 $o_tep = new TimeExpressionParser();
                 $vn_min_date = $vn_max_date = null;
                 $vs_min_sql = $vs_max_sql = '';
                 if (isset($va_facet_info['minimum_date'])) {
                     if ($o_tep->parse($va_facet_info['minimum_date'])) {
                         $va_tmp = $o_tep->getHistoricTimestamps();
                         $vn_min_date = (double) $va_tmp['start'];
                         $vs_min_sql = " AND ({$vs_browse_table_name}.{$vs_browse_start_fld} >= {$vn_min_date})";
                     }
                 }
                 if (isset($va_facet_info['maximum_date'])) {
                     if ($o_tep->parse($va_facet_info['maximum_date'])) {
                         $va_tmp = $o_tep->getHistoricTimestamps();
                         $vn_max_date = (double) $va_tmp['end'];
                         $vs_max_sql = " AND ({$vs_browse_table_name}.{$vs_browse_end_fld} <= {$vn_max_date})";
                     }
                 }
                 if ($vb_check_availability_only) {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t1 = 1\n\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\t\tLIMIT 1";
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql);
                     return (int) $qr_res->numRows() > 0 ? true : false;
                 } else {
                     $vs_sql = "\n\t\t\t\t\t\t\t\tSELECT DISTINCT {$vs_browse_table_name}.{$vs_browse_start_fld}, {$vs_browse_table_name}.{$vs_browse_end_fld}\n\t\t\t\t\t\t\t\tFROM {$vs_browse_table_name}\n\t\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t1 = 1\n\t\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t";
                     //print $vs_sql;
                     $qr_res = $this->opo_db->query($vs_sql);
                     $va_values = array();
                     while ($qr_res->nextRow()) {
                         $vn_start = $qr_res->get($vs_browse_start_fld);
                         $vn_end = $qr_res->get($vs_browse_end_fld);
                         if (!($vn_start && $vn_end)) {
                             continue;
                         }
                         $va_normalized_values = $o_tep->normalizeDateRange($vn_start, $vn_end, $vs_normalization);
                         foreach ($va_normalized_values as $vn_sort_value => $vs_normalized_value) {
                             if ($va_criteria[$vs_normalized_value]) {
                                 continue;
                             }
                             // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                             if (is_numeric($vs_normalized_value) && (int) $vs_normalized_value === 0) {
                                 continue;
                             }
                             // don't include year=0
                             $va_values[$vn_sort_value][$vs_normalized_value] = array('id' => $vs_normalized_value, 'label' => $vs_normalized_value);
                             if (!is_null($vs_single_value) && $vs_normalized_value == $vs_single_value) {
                                 $vb_single_value_is_present = true;
                             }
                         }
                     }
                     if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                         return array();
                     }
                     ksort($va_values);
                     if ($vs_dir == 'DESC') {
                         $va_values = array_reverse($va_values);
                     }
                     $va_sorted_values = array();
                     foreach ($va_values as $vn_sort_value => $va_values_for_sort_value) {
                         $va_sorted_values = array_merge($va_sorted_values, $va_values_for_sort_value);
                     }
                     return $va_sorted_values;
                 }
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'normalizedLength':
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             $t_element = new ca_metadata_elements();
             $vb_is_element = $vb_is_field = false;
             if (!($vb_is_element = $t_element->load(array('element_code' => $va_facet_info['element_code']))) && !($vb_is_field = $t_item->hasField($va_facet_info['element_code']) && $t_item->getFieldInfo($va_facet_info['element_code'], 'FIELD_TYPE') === FT_HISTORIC_DATERANGE)) {
                 return array();
             }
             if ($vb_is_element) {
                 $va_joins = array('INNER JOIN ca_attribute_values ON ca_attributes.attribute_id = ca_attribute_values.attribute_id', 'INNER JOIN ' . $vs_browse_table_name . ' ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_attributes.row_id AND ca_attributes.table_num = ' . intval($vs_browse_table_num));
             } else {
                 $va_joins = array();
             }
             $va_wheres = array();
             $vs_normalization = $va_facet_info['normalization'];
             // how do we construct the dimensions ranges presented to users. In other words - what increments do we can to use to  browse measurments?
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_item->hasField('access')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($t_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $vs_browse_table_name . ".deleted = 0)";
             }
             if ($va_facet_info['relative_to']) {
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_where_sql = '';
             if (is_array($va_wheres) && sizeof($va_wheres) && ($vs_where_sql = join(' AND ', $va_wheres))) {
                 $vs_where_sql = ' AND (' . $vs_where_sql . ')';
             }
             $vs_join_sql = join("\n", $va_joins);
             $vn_element_id = $t_element->getPrimaryKey();
             $vs_dir = strtoupper($va_facet_info['sort']) === 'DESC' ? "DESC" : "ASC";
             $vs_min_sql = $vs_max_sql = '';
             $vo_minimum_dimension = caParseLengthDimension(caGetOption('minimum_dimension', $va_facet_info, "0 in"));
             $vo_maximum_dimension = caParseLengthDimension(caGetOption('maximum_dimension', $va_facet_info, "0 in"));
             if ($vo_minimum_dimension) {
                 $vn_tmp = (double) $vo_minimum_dimension->convertTo('METER', 6, 'en_US');
                 $vs_min_sql = " AND (ca_attribute_values.value_decimal1 >= {$vn_tmp})";
             }
             if (caGetOption('maximum_dimension', $va_facet_info, null) && $vo_maximum_dimension) {
                 $vn_tmp = (double) $vo_maximum_dimension->convertTo('METER', 6, 'en_US');
                 $vs_max_sql = " AND (ca_attribute_values.value_decimal1 <= {$vn_tmp})";
             }
             if ($vb_check_availability_only) {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t\t\tLIMIT 1";
                 //print $vs_sql;
                 $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                 return (int) $qr_res->numRows() > 0 ? true : false;
             } else {
                 $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT ca_attribute_values.value_decimal1, ca_attribute_values.value_decimal2, ca_attribute_values.value_longtext1, ca_attribute_values.value_longtext2\n\t\t\t\t\t\t\tFROM ca_attributes\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\tca_attribute_values.element_id = ? \n\t\t\t\t\t\t\t\t{$vs_min_sql}\n\t\t\t\t\t\t\t\t{$vs_max_sql}\n\t\t\t\t\t\t\t\t{$vs_where_sql}\n\t\t\t\t\t\t";
                 //print $vs_sql;
                 $qr_res = $this->opo_db->query($vs_sql, $vn_element_id);
                 $va_values = array();
                 if (!($vs_output_units = caGetLengthUnitType($vs_units = caGetOption('units', $va_facet_info, 'm')))) {
                     $vs_output_units = Zend_Measure_Length::METER;
                 }
                 $vs_increment = caGetOption('increment', $va_facet_info, '1 m');
                 $vo_increment = caParseLengthDimension($vs_increment);
                 $vn_increment_in_current_units = (double) $vo_increment->convertTo($vs_output_units, 6, 'en_US');
                 while ($qr_res->nextRow()) {
                     $vn_meters = $qr_res->get('value_decimal1');
                     // measurement in meters
                     // convert to target dimensions
                     // normalize
                     $vo_dim = new Zend_Measure_Length($vn_meters, Zend_Measure_Length::METER, 'en_US');
                     $vs_dim = $vo_dim->convertTo($vs_output_units, 6, 'en_US');
                     $vn_dim = (double) $vs_dim;
                     $vn_normalized = floor($vn_dim / $vn_increment_in_current_units) * $vn_increment_in_current_units;
                     if (isset($va_criteria[$vn_normalized])) {
                         continue;
                     }
                     $vs_normalized_range_with_units = "{$vn_normalized} {$vs_units} - " . ($vn_normalized + $vn_increment_in_current_units) . " {$vs_units}";
                     $va_values[$vn_normalized][$vn_normalized] = array('id' => $vn_normalized, 'label' => $vs_normalized_range_with_units);
                     if (!is_null($vs_single_value) && $vn_normalized == $vs_single_value) {
                         $vb_single_value_is_present = true;
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 ksort($va_values);
                 if ($vs_dir == 'DESC') {
                     $va_values = array_reverse($va_values);
                 }
                 $va_sorted_values = array();
                 foreach ($va_values as $vn_sort_value => $va_values_for_sort_value) {
                     $va_sorted_values = array_merge($va_sorted_values, $va_values_for_sort_value);
                 }
                 return $va_sorted_values;
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         case 'authority':
             $vs_rel_table_name = $va_facet_info['table'];
             $va_params = $this->opo_ca_browse_cache->getParameters();
             // Make sure we honor type restrictions for the related authority
             $va_user_type_restrictions = caGetTypeRestrictionsForUser($vs_rel_table_name);
             $va_restrict_to_types = $va_facet_info['restrict_to_types'];
             if (is_array($va_user_type_restrictions)) {
                 if (!is_array($va_restrict_to_types)) {
                     $va_restrict_to_types = $va_user_type_restrictions;
                 } else {
                     $va_restrict_to_types = array_intersect($va_restrict_to_types, $va_user_type_restrictions);
                 }
             }
             if (!is_array($va_exclude_types = $va_facet_info['exclude_types'])) {
                 $va_exclude_types = array();
             }
             if (!is_array($va_restrict_to_relationship_types = $va_facet_info['restrict_to_relationship_types'])) {
                 $va_restrict_to_relationship_types = array();
             }
             if (!is_array($va_exclude_relationship_types = $va_facet_info['exclude_relationship_types'])) {
                 $va_exclude_relationship_types = array();
             }
             $t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true);
             if ($vs_browse_table_name == $vs_rel_table_name) {
                 // browsing on self-relations not supported
                 break;
             } else {
                 switch (sizeof($va_path = array_keys($this->opo_datamodel->getPath($vs_browse_table_name, $vs_rel_table_name)))) {
                     case __CA_ATTRIBUTE_VALUE_LIST__:
                         $t_item_rel = $this->opo_datamodel->getInstanceByTableName($va_path[1], true);
                         $t_rel_item = $this->opo_datamodel->getInstanceByTableName($va_path[2], true);
                         $vs_key = 'relation_id';
                         break;
                     case __CA_ATTRIBUTE_VALUE_DATERANGE__:
                         $t_item_rel = null;
                         $t_rel_item = $this->opo_datamodel->getInstanceByTableName($va_path[1], true);
                         $vs_key = $t_rel_item->primaryKey();
                         break;
                     default:
                         // bad related table
                         return null;
                         break;
                 }
             }
             $vb_rel_is_hierarchical = (bool) $t_rel_item->isHierarchical();
             //
             // Convert related item type_code specs in restrict_to_types and exclude_types lists to numeric type_ids we need for the query
             //
             if (!is_array($va_restrict_to_types = $this->_convertTypeCodesToIDs($va_restrict_to_types, array('instance' => $t_rel_item, 'dontExpandHierarchically' => true)))) {
                 $va_restrict_to_types = array();
             }
             if (!is_array($va_exclude_types = $this->_convertTypeCodesToIDs($va_exclude_types, array('instance' => $t_rel_item, 'dontExpandHierarchically' => true)))) {
                 $va_exclude_types = array();
             }
             $va_restrict_to_types_expanded = $this->_convertTypeCodesToIDs($va_restrict_to_types, array('instance' => $t_rel_item));
             $va_exclude_types_expanded = $this->_convertTypeCodesToIDs($va_exclude_types, array('instance' => $t_rel_item));
             // look up relationship type restrictions
             $va_restrict_to_relationship_types = $this->_getRelationshipTypeIDs($va_restrict_to_relationship_types, $va_facet_info['relationship_table']);
             $va_exclude_relationship_types = $this->_getRelationshipTypeIDs($va_exclude_relationship_types, $va_facet_info['relationship_table']);
             $va_joins = array();
             $va_selects = array();
             $va_wheres = array();
             $va_orderbys = array();
             if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                 $vs_cur_table = array_shift($va_path);
                 foreach ($va_path as $vs_join_table) {
                     $va_rel_info = $this->opo_datamodel->getRelationships($vs_cur_table, $vs_join_table);
                     $va_joins[] = 'INNER JOIN ' . $vs_join_table . ' ON ' . $vs_cur_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][0] . ' = ' . $vs_join_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][1] . "\n";
                     $vs_cur_table = $vs_join_table;
                 }
             } else {
                 if ($va_facet_info['show_all_when_first_facet']) {
                     $va_path = array_reverse($va_path);
                     // in "show_all" mode we turn the browse on it's head and grab records by the "subject" table, rather than the browse table
                     $vs_cur_table = array_shift($va_path);
                     $vs_join_table = $va_path[0];
                     $va_rel_info = $this->opo_datamodel->getRelationships($vs_cur_table, $vs_join_table);
                     $va_joins[] = 'LEFT JOIN ' . $vs_join_table . ' ON ' . $vs_cur_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][0] . ' = ' . $vs_join_table . '.' . $va_rel_info[$vs_cur_table][$vs_join_table][0][1] . "\n";
                 }
             }
             if (sizeof($va_results) && $this->numCriteria() > 0) {
                 $va_wheres[] = "(" . $t_subject->tableName() . '.' . $t_subject->primaryKey() . " IN (" . join(',', $va_results) . "))";
             }
             if (!is_array($va_restrict_to_lists = $va_facet_info['restrict_to_lists'])) {
                 $va_restrict_to_lists = array();
             }
             if (is_array($va_restrict_to_lists) && sizeof($va_restrict_to_lists) > 0 && $t_rel_item->tableName() == 'ca_list_items') {
                 $va_list_ids = array();
                 foreach ($va_restrict_to_lists as $vm_list) {
                     if (is_numeric($vm_list)) {
                         $vn_list_id = (int) $vm_list;
                     } else {
                         $vn_list_id = (int) ca_lists::getListID($vm_list);
                     }
                     if ($vn_list_id) {
                         $va_list_ids[] = $vn_list_id;
                     }
                 }
                 if (sizeof($va_list_ids) > 0) {
                     $va_wheres[] = "{$vs_rel_table_name}.list_id IN (" . join(',', $va_list_ids) . ")";
                 }
             }
             if (is_array($va_restrict_to_types) && sizeof($va_restrict_to_types) > 0 && method_exists($t_rel_item, "getTypeList")) {
                 $va_wheres[] = "{$vs_rel_table_name}.type_id IN (" . join(',', caGetOption('dont_include_subtypes', $va_facet_info, false) ? $va_restrict_to_types : $va_restrict_to_types_expanded) . ")" . ($t_rel_item->getFieldInfo('type_id', 'IS_NULL') ? " OR ({$vs_rel_table_name}.type_id IS NULL)" : '');
                 $va_selects[] = "{$vs_rel_table_name}.type_id";
             }
             if (is_array($va_exclude_types) && sizeof($va_exclude_types) > 0 && method_exists($t_rel_item, "getTypeList")) {
                 $va_wheres[] = "{$vs_rel_table_name}.type_id NOT IN (" . join(',', caGetOption('dont_include_subtypes', $va_facet_info, false) ? $va_exclude_types : $va_exclude_types_expanded) . ")";
             }
             if (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access')) {
                 $va_wheres[] = "(" . $t_rel_item->tableName() . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                 // exclude non-accessible authority items
                 if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                     $va_wheres[] = "(" . $vs_browse_table_name . ".access IN (" . join(',', $pa_options['checkAccess']) . "))";
                     // exclude non-accessible browse items
                 }
             }
             if ($t_item->hasField('deleted') && !$va_facet_info['show_all_when_first_facet']) {
                 $va_wheres[] = "(" . $t_item->tableName() . ".deleted = 0)";
             }
             if ($t_rel_item->hasField('deleted')) {
                 $va_wheres[] = "(" . $t_rel_item->tableName() . ".deleted = 0)";
             }
             $vs_rel_pk = $t_rel_item->primaryKey();
             $va_rel_attr_elements = $t_rel_item->getApplicableElementCodes(null, true, false);
             $va_attrs_to_fetch = array();
             if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                 //$va_selects[] = $t_item->tableName().'.'.$t_item->primaryKey();			// get primary key of subject
             }
             $va_selects[] = $t_rel_item->tableName() . '.' . $vs_rel_pk;
             // get primary key of related
             $vs_hier_parent_id_fld = $vs_hier_id_fld = null;
             if ($vb_rel_is_hierarchical) {
                 $vs_hier_parent_id_fld = $t_rel_item->getProperty('HIERARCHY_PARENT_ID_FLD');
                 $va_selects[] = $t_rel_item->tableName() . '.' . $vs_hier_parent_id_fld;
                 if ($vs_hier_id_fld = $t_rel_item->getProperty('HIERARCHY_ID_FLD')) {
                     $va_selects[] = $t_rel_item->tableName() . '.' . $vs_hier_id_fld;
                 }
             }
             // analyze group_fields (if defined) and add them to the query
             $va_groupings_to_fetch = array();
             if (isset($va_facet_info['groupings']) && is_array($va_facet_info['groupings']) && sizeof($va_facet_info['groupings'])) {
                 foreach ($va_facet_info['groupings'] as $vs_grouping => $vs_grouping_name) {
                     // is grouping type_id?
                     if ($vs_grouping === 'type' && $t_rel_item->hasField('type_id')) {
                         $va_selects[] = $t_rel_item->tableName() . '.type_id';
                         $va_groupings_to_fetch[] = 'type_id';
                     }
                     // is group field a relationship type?
                     if ($vs_grouping === 'relationship_types') {
                         $va_selects[] = $va_facet_info['relationship_table'] . '.type_id rel_type_id';
                         $va_groupings_to_fetch[] = 'rel_type_id';
                     }
                     // is group field an attribute?
                     if (preg_match('!^ca_attribute_([^:]*)!', $vs_grouping, $va_matches)) {
                         if ($vn_element_id = array_search($va_matches[1], $va_rel_attr_elements)) {
                             $va_attrs_to_fetch[] = $vn_element_id;
                         }
                     }
                 }
             }
             if ($va_facet_info['relative_to']) {
                 // TODO: do this everywhere
                 $va_restrict_to_relationship_types = array();
                 $vs_browse_type_limit_sql = '';
                 if ($t_subject->hasField('deleted')) {
                     $va_wheres[] = "(" . $t_subject->tableName() . ".deleted = 0)";
                 }
                 if ($va_relative_sql_data = $this->_getRelativeFacetSQLData($va_facet_info['relative_to'], $pa_options)) {
                     $va_joins = array_merge($va_joins, $va_relative_sql_data['joins']);
                     $va_wheres = array_merge($va_wheres, $va_relative_sql_data['wheres']);
                 }
             }
             if (sizeof($va_restrict_to_relationship_types) > 0 && is_object($t_item_rel)) {
                 $va_wheres[] = $t_item_rel->tableName() . ".type_id IN (" . join(',', $va_restrict_to_relationship_types) . ")";
             }
             if (sizeof($va_exclude_relationship_types) > 0 && is_object($t_item_rel)) {
                 $va_wheres[] = $t_item_rel->tableName() . ".type_id NOT IN (" . join(',', $va_exclude_relationship_types) . ")";
             }
             if ($vs_browse_type_limit_sql) {
                 $va_wheres[] = $vs_browse_type_limit_sql;
             }
             if ($this->opo_config->get('perform_item_level_access_checking')) {
                 if ($t_item = $this->opo_datamodel->getInstanceByTableName($vs_browse_table_name, true)) {
                     // Join to limit what browse table items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl ON ' . $vs_browse_table_name . '.' . $t_item->primaryKey() . ' = ca_acl.row_id AND ca_acl.table_num = ' . $t_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(ca_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(ca_acl.user_id IS NULL and ca_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND ca_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR ca_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                     // Join to limit what related items are used to generate facet
                     $va_joins[] = 'LEFT JOIN ca_acl AS rel_acl ON ' . $t_rel_item->tableName() . '.' . $t_rel_item->primaryKey() . ' = rel_acl.row_id AND rel_acl.table_num = ' . $t_rel_item->tableNum() . "\n";
                     $va_wheres[] = "(\n\t\t\t\t\t\t\t\t((\n\t\t\t\t\t\t\t\t\t(rel_acl.user_id = " . (int) $vn_user_id . ")\n\t\t\t\t\t\t\t\t\t" . (sizeof($va_group_ids) > 0 ? "OR\n\t\t\t\t\t\t\t\t\t(rel_acl.group_id IN (" . join(",", $va_group_ids) . "))" : "") . "\n\t\t\t\t\t\t\t\t\tOR\n\t\t\t\t\t\t\t\t\t(rel_acl.user_id IS NULL and rel_acl.group_id IS NULL)\n\t\t\t\t\t\t\t\t) AND rel_acl.access >= " . __CA_ACL_READONLY_ACCESS__ . ")\n\t\t\t\t\t\t\t\t" . ($vb_show_if_no_acl ? "OR rel_acl.acl_id IS NULL" : "") . "\n\t\t\t\t\t\t\t)";
                 }
             }
             $vs_join_sql = join("\n", $va_joins);
             if ($vb_check_availability_only) {
                 if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                     $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t" . (sizeof($va_wheres) ? ' WHERE ' : '') . join(" AND ", $va_wheres) . " LIMIT 1";
                 } else {
                     $vs_sql = "\n\t\t\t\t\t\t\tSELECT 1\n\t\t\t\t\t\t\tFROM " . $t_rel_item->tableName() . "\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t" . (sizeof($va_wheres) ? ' WHERE ' : '') . join(" AND ", $va_wheres) . " LIMIT 1";
                 }
                 $qr_res = $this->opo_db->query($vs_sql);
                 //print "<hr>$vs_sql<hr>\n";
                 return (int) $qr_res->numRows() > 0 ? true : false;
             } else {
                 if (!$va_facet_info['show_all_when_first_facet'] || $this->numCriteria() > 0) {
                     $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT " . join(', ', $va_selects) . "\n\t\t\t\t\t\t\tFROM " . $vs_browse_table_name . "\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t" . (sizeof($va_wheres) ? ' WHERE ' : '') . join(" AND ", $va_wheres) . "\n\t\t\t\t\t\t\t\t" . (sizeof($va_orderbys) ? "ORDER BY " . join(', ', $va_orderbys) : '');
                 } else {
                     $vs_sql = "\n\t\t\t\t\t\t\tSELECT DISTINCT " . join(', ', $va_selects) . "\n\t\t\t\t\t\t\tFROM " . $t_rel_item->tableName() . "\n\t\t\t\t\t\t\t{$vs_join_sql}\n\t\t\t\t\t\t\t\t" . (sizeof($va_wheres) ? ' WHERE ' : '') . join(" AND ", $va_wheres) . "\n\t\t\t\t\t\t\t\t" . (sizeof($va_orderbys) ? "ORDER BY " . join(', ', $va_orderbys) : '');
                 }
                 //print "<hr>$vs_sql<hr>\n";
                 $qr_res = $this->opo_db->query($vs_sql);
                 $va_facet = $va_facet_items = array();
                 $vs_rel_pk = $t_rel_item->primaryKey();
                 // First get related ids with type and relationship type values
                 // (You could get all of the data we need for the facet in a single query but it turns out to be faster for very large facets to
                 // do it in separate queries, one for the primary ids and another for the labels; a third is done if attributes need to be fetched.
                 // There appears to be a significant [~10%] performance for smaller facets and a larger one [~20-25%] for very large facets)
                 $vn_max_level = caGetOption('maximum_levels', $va_facet_info, null);
                 while ($qr_res->nextRow()) {
                     $va_fetched_row = $qr_res->getRow();
                     $vn_id = $va_fetched_row[$vs_rel_pk];
                     //if (isset($va_facet_items[$vn_id])) { continue; } --- we can't do this as then we don't detect items that have multiple rel_type_ids... argh.
                     if (isset($va_criteria[$vn_id])) {
                         continue;
                     }
                     // skip items that are used as browse critera - don't want to browse on something you're already browsing on
                     if (!$va_facet_items[$va_fetched_row[$vs_rel_pk]]) {
                         // if(!is_null($vn_max_level)) {
                         // 									if (sizeof($va_ancestors) + 1 > $vn_max_level) {
                         // 										if ($va_tmp = $va_ancestors[sizeof($va_ancestors) - $vn_max_level]) {
                         // 											$va_ancestors = array();
                         // 											$va_fetched_row = $va_tmp['NODE'];
                         // 										}
                         // 									}
                         // 								}
                         if (is_array($va_restrict_to_types) && sizeof($va_restrict_to_types) && $va_fetched_row['type_id'] && !in_array($va_fetched_row['type_id'], $va_restrict_to_types)) {
                             continue;
                         }
                         $va_facet_items[$va_fetched_row[$vs_rel_pk]] = array('id' => $va_fetched_row[$vs_rel_pk], 'type_id' => array(), 'parent_id' => $vb_rel_is_hierarchical ? $va_fetched_row[$vs_hier_parent_id_fld] : null, 'hierarchy_id' => $vb_rel_is_hierarchical ? $va_fetched_row[$vs_hier_id_fld] : null, 'rel_type_id' => array(), 'child_count' => 0);
                         if (!is_null($vs_single_value) && $va_fetched_row[$vs_rel_pk] == $vs_single_value) {
                             $vb_single_value_is_present = true;
                         }
                     }
                     if ($va_fetched_row['type_id']) {
                         $va_facet_items[$va_fetched_row[$vs_rel_pk]]['type_id'][] = $va_fetched_row['type_id'];
                     }
                     if ($va_fetched_row['rel_type_id']) {
                         $va_facet_items[$va_fetched_row[$vs_rel_pk]]['rel_type_id'][] = $va_fetched_row['rel_type_id'];
                     }
                 }
                 if (!isset($va_facet_info['dont_expand_hierarchically']) || !$va_facet_info['dont_expand_hierarchically']) {
                     $qr_res->seek(0);
                     $va_ids = $qr_res->getAllFieldValues($vs_rel_pk);
                     $qr_ancestors = call_user_func($t_rel_item->tableName() . '::getHierarchyAncestorsForIDs', $va_ids, array('returnAs' => 'SearchResult'));
                     $vs_rel_table = $t_rel_item->tableName();
                     $vs_rel_pk = $t_rel_item->primaryKey();
                     $vb_check_ancestor_access = (bool) (isset($pa_options['checkAccess']) && is_array($pa_options['checkAccess']) && sizeof($pa_options['checkAccess']) && $t_rel_item->hasField('access'));
                     if ($qr_ancestors) {
                         while ($qr_ancestors->nextHit()) {
                             $vn_parent_type_id = $qr_ancestors->get('type_id');
                             if (sizeof($va_exclude_types) > 0 && in_array($vn_parent_type_id, $va_exclude_types)) {
                                 continue;
                             }
                             if (sizeof($va_restrict_to_types) > 0 && !in_array($vn_parent_type_id, $va_restrict_to_types)) {
                                 continue;
                             }
                             if ($vb_check_ancestor_access && !in_array($qr_ancestors->get('access'), $pa_options['checkAccess'])) {
                                 continue;
                             }
                             $va_facet_items[$vn_ancestor_id = (int) $qr_ancestors->get("{$vs_rel_pk}")] = array('id' => $vn_ancestor_id, 'type_id' => array(), 'parent_id' => $vb_rel_is_hierarchical ? $qr_ancestors->get("{$vs_hier_parent_id_fld}") : null, 'hierarchy_id' => $vb_rel_is_hierarchical && $vs_hier_id_fld ? $qr_ancestors->get($vs_hier_id_fld) : null, 'rel_type_id' => array(), 'child_count' => 0);
                         }
                     }
                 }
                 // Set child counts
                 foreach ($va_facet_items as $vn_i => $va_item) {
                     if ($va_item['parent_id'] && isset($va_facet_items[$va_item['parent_id']])) {
                         $va_facet_items[$va_item['parent_id']]['child_count']++;
                     }
                 }
                 // Get labels for facet items
                 if (sizeof($va_row_ids = array_keys($va_facet_items))) {
                     if ($vs_label_table_name = $t_rel_item->getLabelTableName()) {
                         $t_rel_item_label = $this->opo_datamodel->getInstanceByTableName($vs_label_table_name, true);
                         $vs_label_display_field = $t_rel_item_label->getDisplayField();
                         $vs_rel_pk = $t_rel_item->primaryKey();
                         $va_label_wheres = array();
                         if ($t_rel_item_label->hasField('is_preferred')) {
                             $va_label_wheres[] = "({$vs_label_table_name}.is_preferred = 1)";
                         }
                         $va_label_wheres[] = "({$vs_label_table_name}.{$vs_rel_pk} IN (" . join(",", $va_row_ids) . "))";
                         $va_label_selects[] = "{$vs_label_table_name}.{$vs_rel_pk}";
                         $va_label_selects[] = "{$vs_label_table_name}.locale_id";
                         $va_label_fields = $t_rel_item->getLabelUIFields();
                         foreach ($va_label_fields as $vs_label_field) {
                             $va_label_selects[] = "{$vs_label_table_name}.{$vs_label_field}";
                         }
                         // Get label ordering fields
                         $va_ordering_fields_to_fetch = isset($va_facet_info['order_by_label_fields']) && is_array($va_facet_info['order_by_label_fields']) ? $va_facet_info['order_by_label_fields'] : array();
                         $va_orderbys = array();
                         foreach ($va_ordering_fields_to_fetch as $vs_sort_by_field) {
                             if (!$t_rel_item_label->hasField($vs_sort_by_field)) {
                                 continue;
                             }
                             $va_orderbys[] = $va_label_selects[] = $vs_label_table_name . '.' . $vs_sort_by_field;
                         }
                         // get labels
                         $vs_sql = "\n\t\t\t\t\t\t\t\t\tSELECT " . join(', ', $va_label_selects) . "\n\t\t\t\t\t\t\t\t\tFROM " . $vs_label_table_name . "\n\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_label_wheres) ? ' WHERE ' : '') . join(" AND ", $va_label_wheres) . "\n\t\t\t\t\t\t\t\t\t\t" . (sizeof($va_orderbys) ? "ORDER BY " . join(', ', $va_orderbys) : '') . "";
                         //print $vs_sql;
                         $qr_labels = $this->opo_db->query($vs_sql);
                         while ($qr_labels->nextRow()) {
                             $va_fetched_row = $qr_labels->getRow();
                             $va_facet_item = array_merge($va_facet_items[$va_fetched_row[$vs_rel_pk]], array('label' => $va_fetched_row[$vs_label_display_field]));
                             foreach ($va_ordering_fields_to_fetch as $vs_to_fetch) {
                                 $va_facet_item[$vs_to_fetch] = $va_fetched_row[$vs_to_fetch];
                             }
                             $va_facet[$va_fetched_row[$vs_rel_pk]][$va_fetched_row['locale_id']] = $va_facet_item;
                         }
                     }
                     // get attributes for facet items
                     if (sizeof($va_attrs_to_fetch)) {
                         $qr_attrs = $this->opo_db->query("\n\t\t\t\t\t\t\t\t\tSELECT c_av.*, c_a.locale_id, c_a.row_id\n\t\t\t\t\t\t\t\t\tFROM ca_attributes c_a\n\t\t\t\t\t\t\t\t\tINNER JOIN ca_attribute_values c_av ON c_a.attribute_id = c_av.attribute_id\n\t\t\t\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\t\t\t\tc_av.element_id IN (" . join(',', $va_attrs_to_fetch) . ")\n\t\t\t\t\t\t\t\t\t\tAND\n\t\t\t\t\t\t\t\t\t\tc_a.table_num = ? \n\t\t\t\t\t\t\t\t\t\tAND \n\t\t\t\t\t\t\t\t\t\tc_a.row_id IN (" . join(',', $va_row_ids) . ")\n\t\t\t\t\t\t\t\t", $t_rel_item->tableNum());
                         while ($qr_attrs->nextRow()) {
                             $va_fetched_row = $qr_attrs->getRow();
                             $vn_id = $va_fetched_row['row_id'];
                             // if no locale is set for the attribute default it to whatever the locale for the item is
                             if (!($vn_locale_id = $va_fetched_row['locale_id'])) {
                                 $va_tmp = array_keys($va_facet[$vn_id]);
                                 $vn_locale_id = $va_tmp[0];
                             }
                             $va_facet[$vn_id][$vn_locale_id]['ca_attribute_' . $va_fetched_row['element_id']][] = $va_fetched_row;
                         }
                     }
                 }
                 if (!is_null($vs_single_value) && !$vb_single_value_is_present) {
                     return array();
                 }
                 return caExtractValuesByUserLocale($va_facet);
             }
             break;
             # -----------------------------------------------------
         # -----------------------------------------------------
         default:
             return null;
             break;
             # -----------------------------------------------------
     }
 }
 public function Payment()
 {
     if (!$this->opt_order->getPrimaryKey()) {
         $this->Edit();
         return;
     }
     $va_months = array();
     $o_tep = new TimeExpressionParser();
     foreach ($o_tep->getMonthList() as $vn_m => $vs_month) {
         $va_months[$vs_month] = $vn_m + 1;
     }
     $this->view->setVar('credit_card_exp_month_list', $va_months);
     $va_tmp = getDate();
     $vn_current_year = $va_tmp['year'];
     $vn_i = 0;
     $va_credit_card_exp_year_list = array();
     while ($vn_i < 8) {
         $va_credit_card_exp_year_list[$vn_current_year + $vn_i] = $vn_current_year + $vn_i;
         $vn_i++;
     }
     $this->view->setVar('credit_card_exp_year_list', $va_credit_card_exp_year_list);
     $this->render('order_payment_html.php');
 }
        ?>
	<div class="listItems">
<?php 
        $va_row = array();
        foreach ($va_facet as $vn_i => $va_item) {
            print "<div class='item'>" . caNavLink($this->request, $va_item['label'], 'browseSelectPanelLink', $this->request->getModulePath(), $this->request->getController(), strlen($vm_modify_id) ? 'modifyCriteria' : 'addCriteria', array_merge(array('facet' => $vs_facet_name, 'id' => $va_item['id'], 'mod_id' => $vm_modify_id), $va_other_params)) . "</div>";
        }
        ?>
	</div>
<?php 
        break;
        # ------------------------------------------------------------
    # ------------------------------------------------------------
    case 'alphabetical':
    default:
        $o_tep = new TimeExpressionParser();
        // TODO: how do we handle non-latin characters?
        $va_label_order_by_fields = isset($va_facet_info['order_by_label_fields']) ? $va_facet_info['order_by_label_fields'] : array('label');
        foreach ($va_facet as $vn_i => $va_item) {
            $va_groups = array();
            $vn_show_jump_bar = 0;
            switch ($vs_grouping_field) {
                case 'label':
                    $va_groups[] = mb_substr($va_item[$va_label_order_by_fields[0]], 0, 1);
                    $vn_show_jump_bar = 1;
                    break;
                case 'relationship_types':
                    foreach ($va_item['rel_type_id'] as $vs_g) {
                        if (isset($va_relationship_types[$vs_g]['typename'])) {
                            $va_groups[] = $va_relationship_types[$vs_g]['typename'];
                        } else {
Example #26
0
/**
 * Returns 
 *
 * @param int $pn_start_timestamp Start of date range, as Unix timestamp
 * @param array $pa_options All options supported by TimeExpressionParser::getText() are supported
 *
 * @return string Localized date range expression
 */
function caGetDateRangeForTimelineJS($pa_historic_timestamps, $pa_options = null)
{
    $o_tep = new TimeExpressionParser();
    $va_start = $o_tep->getHistoricDateParts($pa_historic_timestamps[0]);
    $va_end = $o_tep->getHistoricDateParts($pa_historic_timestamps[1]);
    return array('start' => $va_start['year'] . ',' . $va_start['month'] . ',' . $va_start['day'], 'end' => $va_end['year'] . ',' . $va_end['month'] . ',' . $va_end['day']);
}
Example #27
0
 /**
  * Set field value(s) for the table row represented by this object
  *
  * @param string|array string $pa_fields representation of a field name
  * or array of string representations of field names
  * @param mixed $pm_value value to set the given field(s) to
  * @param array $pa_options associative array of options
  * possible options (keys):
  * when dealing with date/time fields:
  * - SET_DIRECT_DATE
  * - SET_DIRECT_TIME
  * - SET_DIRECT_TIMES
  *
  * for media/files fields:
  * - original_filename : (note that it is lower case) optional parameter which enables you to pass the original filename of a file, in addition to the representation in the temporary, global _FILES array;
  *
  * for text fields:
  *	- purify : if set then text input is run through HTML Purifier before being set
  *
  * for parent_id field:
  *	- treatParentIDAsIdno: force parent_id value to be used as idno lookup rather than a primary key value
  */
 public function set($pa_fields, $pm_value = "", $pa_options = null)
 {
     $this->errors = array();
     if (!is_array($pa_fields)) {
         $pa_fields = array($pa_fields => $pm_value);
     }
     foreach ($pa_fields as $vs_field => $vm_value) {
         if (array_key_exists($vs_field, $this->FIELDS)) {
             $pa_fields_type = $this->getFieldInfo($vs_field, "FIELD_TYPE");
             $pb_need_reload = false;
             if (!$this->verifyFieldValue($vs_field, $vm_value, $pb_need_reload)) {
                 return false;
             }
             if ($pb_need_reload) {
                 return true;
             }
             // was set to default
             if ($vs_field == $this->primaryKey()) {
                 $vm_value = preg_replace("/[\"']/", "", $vm_value);
             }
             $vs_cur_value = isset($this->_FIELD_VALUES[$vs_field]) ? $this->_FIELD_VALUES[$vs_field] : null;
             switch ($pa_fields_type) {
                 case FT_NUMBER:
                     if ($vs_cur_value != $vm_value) {
                         $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                     }
                     if ($vs_field == $this->HIERARCHY_PARENT_ID_FLD && (strlen($vm_value) > 0 && (!is_numeric($vm_value) || caGetOption('treatParentIDAsIdno', $pa_options, false)))) {
                         if (is_array($va_ids = call_user_func_array($this->tableName() . "::find", array(array('idno' => $vm_value, 'deleted' => 0), array('returnAs' => 'ids', 'transaction' => $this->getTransaction()))))) {
                             $vm_value = array_shift($va_ids);
                         }
                     }
                     if ($vm_value !== "" || $this->getFieldInfo($vs_field, "IS_NULL") && $vm_value == "") {
                         if ($vm_value) {
                             if (($vs_list_code = $this->getFieldInfo($vs_field, "LIST_CODE")) && !is_numeric($vm_value)) {
                                 // translate ca_list_item idno's into item_ids if necessary
                                 if ($vn_id = ca_lists::getItemID($vs_list_code, $vm_value)) {
                                     $vm_value = $vn_id;
                                 } else {
                                     $this->postError(1103, _t('Value %1 is not in list %2', $vm_value, $vs_list_code), 'BaseModel->set()', $this->tableName() . '.' . $vs_field);
                                     return false;
                                 }
                             } else {
                                 $vm_orig_value = $vm_value;
                                 $vm_value = preg_replace("/[^\\d-.]+/", "", $vm_value);
                                 # strip non-numeric characters
                                 if (!preg_match("/^[\\-]{0,1}[\\d.]+\$/", $vm_value)) {
                                     $this->postError(1100, _t("'%1' for %2 is not numeric", $vm_orig_value, $vs_field), "BaseModel->set()", $this->tableName() . '.' . $vs_field);
                                     return false;
                                 }
                             }
                         }
                         $this->_FIELD_VALUES[$vs_field] = $vm_value;
                     }
                     break;
                 case FT_BIT:
                     if ($vs_cur_value != $vm_value) {
                         $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                     }
                     $this->_FIELD_VALUES[$vs_field] = $vm_value ? 1 : 0;
                     break;
                 case FT_DATETIME:
                 case FT_HISTORIC_DATETIME:
                 case FT_DATE:
                 case FT_HISTORIC_DATE:
                     if ($this->DIRECT_DATETIMES || $pa_options["SET_DIRECT_DATE"]) {
                         $this->_FIELD_VALUES[$vs_field] = $vm_value;
                         $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                     } else {
                         if (!$vm_value && $this->FIELDS[$vs_field]["IS_NULL"]) {
                             if ($vs_cur_value) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                             }
                             $this->_FIELD_VALUES[$vs_field] = null;
                         } else {
                             $o_tep = new TimeExpressionParser();
                             if ($pa_fields_type == FT_DATE || $pa_fields_type == FT_HISTORIC_DATE) {
                                 $va_timestamps = $o_tep->parseDate($vm_value);
                             } else {
                                 $va_timestamps = $o_tep->parseDatetime($vm_value);
                             }
                             if (!$va_timestamps) {
                                 $this->postError(1805, $o_tep->getParseErrorMessage(), 'BaseModel->set()', $this->tableName() . '.' . $vs_field);
                                 return false;
                             }
                             if ($pa_fields_type == FT_HISTORIC_DATETIME || $pa_fields_type == FT_HISTORIC_DATE) {
                                 if ($vs_cur_value != $va_timestamps["start"]) {
                                     $this->_FIELD_VALUES[$vs_field] = $va_timestamps["start"];
                                     $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 }
                             } else {
                                 $va_timestamps = $o_tep->getUnixTimestamps();
                                 if ($va_timestamps[0] == -1) {
                                     $this->postError(1830, $o_tep->getParseErrorMessage(), 'BaseModel->set()', $this->tableName() . '.' . $vs_field);
                                     return false;
                                 }
                                 if ($vs_cur_value != $va_timestamps["start"]) {
                                     $this->_FIELD_VALUES[$vs_field] = $va_timestamps["start"];
                                     $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 }
                             }
                         }
                     }
                     break;
                 case FT_TIME:
                     if ($this->DIRECT_TIMES || $pa_options["SET_DIRECT_TIME"]) {
                         $this->_FIELD_VALUES[$vs_field] = $vm_value;
                     } else {
                         if (!$vm_value && $this->FIELDS[$vs_field]["IS_NULL"]) {
                             if ($vs_cur_value) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                             }
                             $this->_FIELD_VALUES[$vs_field] = null;
                         } else {
                             $o_tep = new TimeExpressionParser();
                             if (!$o_tep->parseTime($vm_value)) {
                                 $this->postError(1805, $o_tep->getParseErrorMessage(), 'BaseModel->set()', $this->tableName() . '.' . $vs_field);
                                 return false;
                             }
                             $va_times = $o_tep->getTimes();
                             if ($vs_cur_value != $va_times['start']) {
                                 $this->_FIELD_VALUES[$vs_field] = $va_times['start'];
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                             }
                         }
                     }
                     break;
                 case FT_TIMESTAMP:
                     # can't set timestamp
                     break;
                 case FT_DATERANGE:
                 case FT_HISTORIC_DATERANGE:
                     $vs_start_field_name = $this->getFieldInfo($vs_field, "START");
                     $vs_end_field_name = $this->getFieldInfo($vs_field, "END");
                     $vn_start_date = isset($this->_FIELD_VALUES[$vs_start_field_name]) ? $this->_FIELD_VALUES[$vs_start_field_name] : null;
                     $vn_end_date = isset($this->_FIELD_VALUES[$vs_end_field_name]) ? $this->_FIELD_VALUES[$vs_end_field_name] : null;
                     if ($this->DIRECT_DATETIMES || $pa_options["SET_DIRECT_DATE"]) {
                         if (is_array($vm_value) && sizeof($vm_value) == 2 && $vm_value[0] <= $vm_value[1]) {
                             if ($vn_start_date != $vm_value[0]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 $this->_FIELD_VALUES[$vs_start_field_name] = $vm_value[0];
                             }
                             if ($vn_end_date != $vm_value[1]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 $this->_FIELD_VALUES[$vs_end_field_name] = $vm_value[1];
                             }
                         } else {
                             $this->postError(1100, _t("Invalid direct date values"), "BaseModel->set()", $this->tableName() . '.' . $vs_field);
                         }
                     } else {
                         if (!$vm_value && $this->FIELDS[$vs_field]["IS_NULL"]) {
                             if ($vn_start_date || $vn_end_date) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                             }
                             $this->_FIELD_VALUES[$vs_start_field_name] = null;
                             $this->_FIELD_VALUES[$vs_end_field_name] = null;
                         } else {
                             $o_tep = new TimeExpressionParser();
                             if (!$o_tep->parseDatetime($vm_value)) {
                                 $this->postError(1805, $o_tep->getParseErrorMessage(), 'BaseModel->set()', $this->tableName() . '.' . $vs_field);
                                 return false;
                             }
                             if ($pa_fields_type == FT_HISTORIC_DATERANGE) {
                                 $va_timestamps = $o_tep->getHistoricTimestamps();
                             } else {
                                 $va_timestamps = $o_tep->getUnixTimestamps();
                                 if ($va_timestamps[0] == -1) {
                                     $this->postError(1830, $o_tep->getParseErrorMessage(), 'BaseModel->set()', $this->tableName() . '.' . $vs_field);
                                     return false;
                                 }
                             }
                             if ($vn_start_date != $va_timestamps["start"]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 $this->_FIELD_VALUES[$vs_start_field_name] = $va_timestamps["start"];
                             }
                             if ($vn_end_date != $va_timestamps["end"]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 $this->_FIELD_VALUES[$vs_end_field_name] = $va_timestamps["end"];
                             }
                         }
                     }
                     break;
                 case FT_TIMERANGE:
                     $vs_start_field_name = $this->getFieldInfo($vs_field, "START");
                     $vs_end_field_name = $this->getFieldInfo($vs_field, "END");
                     if ($this->DIRECT_TIMES || $pa_options["SET_DIRECT_TIMES"]) {
                         if (is_array($vm_value) && sizeof($vm_value) == 2 && $vm_value[0] <= $vm_value[1]) {
                             if ($this->_FIELD_VALUES[$vs_start_field_name] != $vm_value[0]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 $this->_FIELD_VALUES[$vs_start_field_name] = $vm_value[0];
                             }
                             if ($this->_FIELD_VALUES[$vs_end_field_name] != $vm_value[1]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 $this->_FIELD_VALUES[$vs_end_field_name] = $vm_value[1];
                             }
                         } else {
                             $this->postError(1100, _t("Invalid direct time values"), "BaseModel->set()", $this->tableName() . '.' . $vs_field);
                         }
                     } else {
                         if (!$vm_value && $this->FIELDS[$vs_field]["IS_NULL"]) {
                             if ($this->_FIELD_VALUES[$vs_start_field_name] || $this->_FIELD_VALUES[$vs_end_field_name]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                             }
                             $this->_FIELD_VALUES[$vs_start_field_name] = null;
                             $this->_FIELD_VALUES[$vs_end_field_name] = null;
                         } else {
                             $o_tep = new TimeExpressionParser();
                             if (!$o_tep->parseTime($vm_value)) {
                                 $this->postError(1805, $o_tep->getParseErrorMessage(), 'BaseModel->set()', $this->tableName() . '.' . $vs_field);
                                 return false;
                             }
                             $va_timestamps = $o_tep->getTimes();
                             if ($this->_FIELD_VALUES[$vs_start_field_name] != $va_timestamps["start"]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 $this->_FIELD_VALUES[$vs_start_field_name] = $va_timestamps["start"];
                             }
                             if ($this->_FIELD_VALUES[$vs_end_field_name] != $va_timestamps["end"]) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 $this->_FIELD_VALUES[$vs_end_field_name] = $va_timestamps["end"];
                             }
                         }
                     }
                     break;
                 case FT_TIMECODE:
                     $o_tp = new TimecodeParser();
                     if ($o_tp->parse($vm_value)) {
                         if ($o_tp->getParsedValueInSeconds() != $vs_cur_value) {
                             $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                             $this->_FIELD_VALUES[$vs_field] = $o_tp->getParsedValueInSeconds();
                         }
                     }
                     break;
                 case FT_TEXT:
                     $vm_value = (string) $vm_value;
                     if (is_string($vm_value)) {
                         $vm_value = stripSlashes($vm_value);
                     }
                     if (isset($pa_options['purify']) && $pa_options['purify'] || (bool) $this->opb_purify_input || $this->getFieldInfo($vs_field, "PURIFY")) {
                         $vm_value = BaseModel::getPurifier()->purify((string) $vm_value);
                     }
                     if ($this->getFieldInfo($vs_field, "DISPLAY_TYPE") == DT_LIST_MULTIPLE) {
                         if (is_array($vm_value)) {
                             if (!($vs_list_multiple_delimiter = $this->getFieldInfo($vs_field, 'LIST_MULTIPLE_DELIMITER'))) {
                                 $vs_list_multiple_delimiter = ';';
                             }
                             $vs_string_value = join($vs_list_multiple_delimiter, $vm_value);
                             $vs_string_value = str_replace("", '', $vs_string_value);
                             if ($vs_cur_value !== $vs_string_value) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                             }
                             $this->_FIELD_VALUES[$vs_field] = $vs_string_value;
                         }
                     } else {
                         $vm_value = str_replace("", '', $vm_value);
                         if ($this->getFieldInfo($vs_field, "ENTITY_ENCODE_INPUT")) {
                             $vs_value_entity_encoded = htmlentities(html_entity_decode($vm_value));
                             if ($vs_cur_value !== $vs_value_entity_encoded) {
                                 $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                             }
                             $this->_FIELD_VALUES[$vs_field] = $vs_value_entity_encoded;
                         } else {
                             if ($this->getFieldInfo($vs_field, "URL_ENCODE_INPUT")) {
                                 $vs_value_url_encoded = urlencode($vm_value);
                                 if ($vs_cur_value !== $vs_value_url_encoded) {
                                     $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 }
                                 $this->_FIELD_VALUES[$vs_field] = $vs_value_url_encoded;
                             } else {
                                 if ($vs_cur_value !== $vm_value) {
                                     $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                                 }
                                 $this->_FIELD_VALUES[$vs_field] = $vm_value;
                             }
                         }
                     }
                     break;
                 case FT_PASSWORD:
                     if (isset($pa_options['purify']) && $pa_options['purify'] || (bool) $this->opb_purify_input || $this->getFieldInfo($vs_field, "PURIFY")) {
                         $vm_value = BaseModel::getPurifier()->purify((string) $vm_value);
                     }
                     if (!$vm_value) {
                         // store blank passwords as blank,
                         $this->_FIELD_VALUES[$vs_field] = "";
                         $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                     } else {
                         // leave the treatment of the password to the AuthAdapter, i.e. don't do hashing here
                         if ($vs_cur_value != $vm_value) {
                             $this->_FIELD_VALUES[$vs_field] = $vm_value;
                             $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                         }
                     }
                     break;
                 case FT_VARS:
                     if (md5(print_r($vs_cur_value, true)) != md5(print_r($vm_value, true))) {
                         $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                     }
                     $this->_FIELD_VALUES[$vs_field] = $vm_value;
                     break;
                 case FT_MEDIA:
                 case FT_FILE:
                     $vb_allow_fetching_of_urls = (bool) $this->_CONFIG->get('allow_fetching_of_media_from_remote_urls');
                     # if there's a tmp_name is the global _FILES array
                     # then we'll process it in insert()/update()...
                     $this->_SET_FILES[$vs_field]['options'] = $pa_options;
                     if (caGetOSFamily() == OS_WIN32) {
                         // fix for paths using backslashes on Windows failing in processing
                         $vm_value = str_replace('\\', '/', $vm_value);
                     }
                     if (isset($pa_options['purify']) && $pa_options['purify'] || (bool) $this->opb_purify_input || $this->getFieldInfo($vs_field, "PURIFY")) {
                         $pa_options["original_filename"] = BaseModel::getPurifier()->purify((string) $pa_options["original_filename"]);
                         $vm_value = BaseModel::getPurifier()->purify((string) $vm_value);
                     }
                     $va_matches = null;
                     if (is_string($vm_value) && (file_exists($vm_value) || $vb_allow_fetching_of_urls && isURL($vm_value) || preg_match("!^userMedia[\\d]+/!", $vm_value))) {
                         $this->_SET_FILES[$vs_field]['original_filename'] = $pa_options["original_filename"];
                         $this->_SET_FILES[$vs_field]['tmp_name'] = $vm_value;
                         $this->_FIELD_VALUE_CHANGED[$vs_field] = true;
                     } else {
                         # only return error when file name is not 'none'
                         # 'none' is PHP's stupid way of telling you there
                         # isn't a file...
                         if ($vm_value != "none" && $vm_value) {
                             //$this->postError(1500,_t("%1 does not exist", $vm_value),"BaseModel->set()", $this->tableName().'.'.$vs_field);
                         }
                         return false;
                     }
                     break;
                 default:
                     die("Invalid field type in BaseModel->set()");
                     break;
             }
         } else {
             $this->postError(710, _t("'%1' does not exist in this object", $vs_field), "BaseModel->set()", $this->tableName() . '.' . $vs_field);
             return false;
         }
     }
     return true;
 }
 public function renderWidget($ps_widget_id, &$pa_settings)
 {
     parent::renderWidget($ps_widget_id, $pa_settings);
     global $g_ui_locale_id;
     if (!in_array($pa_settings['display_type'], BaseWidget::$s_widget_settings['recentlyCreatedWidget']["display_type"]["options"])) {
         return "";
     }
     if ($t_table = $this->opo_datamodel->getInstanceByTableName($pa_settings['display_type'], true)) {
         $vo_db = new Db();
         $o_tep = new TimeExpressionParser();
         if ($pa_settings["display_limit"] && intval($pa_settings["display_limit"]) > 0 && intval($pa_settings["display_limit"]) < 1000) {
             $vn_limit = intval($pa_settings["display_limit"]);
         } else {
             $vn_limit = 11;
         }
         $vs_deleted_sql = '';
         if ($t_table->hasField('deleted')) {
             $vs_deleted_sql = " AND (t.deleted = 0) ";
         }
         $vs_idno_sql = '';
         if ($t_table->hasField('idno')) {
             $vs_idno_sql = "t.idno,";
         } elseif ($t_table->hasField('idno_stub')) {
             $vs_idno_sql = "t.idno_stub,";
         }
         $vs_sql = "\n\t\t\t\t\tSELECT\n\t\t\t\t\t\tt.{$t_table->primaryKey()},\n\t\t\t\t\t\tlt.{$t_table->getLabelDisplayField()},\n\t\t\t\t\t\t{$vs_idno_sql}\n\t\t\t\t\t\tlt.locale_id\n\t\t\t\t\tFROM\n\t\t\t\t\t\t{$t_table->tableName()} t\n\t\t\t\t\tINNER JOIN\n\t\t\t\t\t\t{$t_table->getLabelTableName()} AS lt ON t.{$t_table->primaryKey()} = lt.{$t_table->primaryKey()}\n\t\t\t\t\tWHERE\n\t\t\t\t\t\t(lt.is_preferred = 1) \n\t\t\t\t\t\t{$vs_deleted_sql}\n\t\t\t\t\tORDER BY\n\t\t\t\t\t\tt.{$t_table->primaryKey()} DESC\n\t\t\t\t\tLIMIT\n\t\t\t\t\t\t{$vn_limit}\n\t\t\t\t";
         $qr_records = $vo_db->query($vs_sql);
         $va_item_list = array();
         while ($qr_records->nextRow() && sizeof($va_item_list) < intval($pa_settings["display_limit"])) {
             if (isset($va_item_list[$qr_records->get($t_table->primaryKey())])) {
                 // we only overwrite if we hit one with UI locale (i.e. the first hit wins if there is no label in UI locale)
                 if (!(intval($qr_records->get($t_table->getLabelTableName() . ".locale_id")) == intval($g_ui_locale_id))) {
                     continue;
                 }
             }
             $vs_sql = "\n\t\t\t\t\t\tSELECT\n\t\t\t\t\t\t\tlog_datetime\n\t\t\t\t\t\tFROM\n\t\t\t\t\t\t\tca_change_log\n\t\t\t\t\t\tWHERE\n\t\t\t\t\t\t\tlogged_table_num = " . (int) $t_table->tableNum() . "\n\t\t\t\t\t\tAND\n\t\t\t\t\t\t\tchangetype = 'I'\n\t\t\t\t\t\tAND\n\t\t\t\t\t\t\tlogged_row_id = " . (int) $qr_records->get($t_table->primaryKey()) . "\n\t\t\t\t\t";
             $qr_create_date = $vo_db->query($vs_sql);
             if ($qr_create_date->nextRow()) {
                 $vn_time_created = intval($qr_create_date->get("log_datetime"));
                 $o_tep->setUnixTimestamps($vn_time_created, $vn_time_created);
                 $va_options = $this->opb_dont_show_timestamp ? array('timeOmit' => true) : null;
                 $vs_datetime_text = $o_tep->getText($va_options);
             } else {
                 $vs_datetime_text = '';
             }
             $va_item_list[$qr_records->get($t_table->primaryKey())] = array("display" => $qr_records->get($t_table->getLabelTableName() . "." . $t_table->getLabelDisplayField()), "idno" => $qr_records->get("idno"), "idno_stub" => $qr_records->get("idno_stub"), "locale_id" => $qr_records->get($t_table->getLabelTableName() . ".locale_id"), "datetime" => $vs_datetime_text);
         }
         if (!(intval($pa_settings["height_px"]) > 30 && intval($pa_settings["height_px"]) < 1000)) {
             // if value is not within reasonable boundaries, set it to default
             $this->opo_view->setVar("height_px", 270);
         } else {
             $this->opo_view->setVar("height_px", intval($pa_settings["height_px"]));
         }
         $this->opo_view->setVar('item_list', $va_item_list);
         $this->opo_view->setVar('table_num', $this->opo_datamodel->getTableNum($t_table->tableName()));
         $this->opo_view->setVar('request', $this->getRequest());
         $this->opo_view->setVar('table_display', $this->opa_table_display_names[$t_table->tableName()]);
         $this->opo_view->setVar('idno_display', isset($pa_settings["display_idno"]) ? $pa_settings["display_idno"] : FALSE);
         return $this->opo_view->render('main_html.php');
     }
 }
Example #29
0
/**
 * Returns date range for calendar display
 *
 * @param int $pn_start_timestamp Start of date range, as Unix timestamp
 * @param array $pa_options All options supported by TimeExpressionParser::getText() are supported
 *
 * @return string Localized date range expression
 */
function caGetDateRangeForCalendar($pa_historic_timestamps, $pa_options = null)
{
    $o_tep = new TimeExpressionParser();
    $va_start = $o_tep->getHistoricDateParts($pa_historic_timestamps[0]);
    $va_end = $o_tep->getHistoricDateParts($pa_historic_timestamps[1]);
    if ($va_start['year'] < 0) {
        $va_start['year'] = 1900;
    }
    if ($va_end['year'] >= 2000000) {
        $va_end['year'] = date("Y");
    }
    return array('start' => $va_start, 'end' => $va_end, 'start_iso' => $o_tep->getISODateTime($va_start, 'FULL'), 'end_iso' => $o_tep->getISODateTime($va_end, 'FULL'));
}
Example #30
0
 /** 
  *
  */
 public function search($ps_datetime_expression, $ps_code = null)
 {
     $o_tep = new TimeExpressionParser();
     if ($o_tep->parse($ps_datetime_expression)) {
         list($vn_period_start, $vn_period_end) = $o_tep->getUnixTimestamps();
         if ($vn_period_start && $vn_period_end) {
             $o_db = new Db();
             $qr_codes = $o_db->query("SELECT DISTINCT code FROM ca_eventlog ORDER BY code");
             $va_codes = array();
             while ($qr_codes->nextRow()) {
                 $va_codes[] = $qr_codes->get("code");
             }
             if (in_array($ps_code, $va_codes)) {
                 $qr_log = $o_db->query("\n\t\t\t\t\t\tSELECT *\n\t\t\t\t\t\tFROM ca_eventlog\n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t(date_time BETWEEN {$vn_period_start} AND {$vn_period_end}) \n\t\t\t\t\t\t\tAND (code = ?)\n\t\t\t\t\t\tORDER BY date_time DESC\n\t\t\t\t\t", $ps_code);
             } else {
                 $qr_log = $o_db->query("\n\t\t\t\t\t\tSELECT *\n\t\t\t\t\t\tFROM ca_eventlog\n\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t(date_time BETWEEN {$vn_period_start} AND {$vn_period_end})\n\t\t\t\t\t\tORDER BY date_time DESC\n\t\t\t\t\t");
             }
             return $qr_log->getAllRows();
         }
     }
     return null;
 }