public function parse($ps_expression, $pa_options = null)
 {
     $ps_expression = caRemoveAccents($ps_expression);
     if (!$pa_options) {
         $pa_options = array();
     }
     $this->init();
     if ($this->tokenize($this->preprocess($ps_expression)) == 0) {
         // nothing to parse
         return false;
     }
     $va_dates = array();
     $vn_state = TEP_STATE_BEGIN;
     $vb_can_accept = false;
     $vb_circa_is_set = false;
     while ($va_token = $this->peekToken()) {
         if ($this->getParseError()) {
             break;
         }
         switch ($vn_state) {
             # -------------------------------------------------------
             case TEP_STATE_BEGIN:
                 switch ($va_token['type']) {
                     # ----------------------
                     case TEP_TOKEN_INTEGER:
                         // is this a quarter century expression?
                         if ((int) $va_token['value'] > 0 && (int) $va_token['value'] <= 21) {
                             $va_peek = $this->peekToken(2);
                             if ($va_peek['type'] == TEP_TOKEN_ALPHA) {
                                 if (preg_match('!^Q([\\d]{1})$!i', $va_peek['value'], $va_matches)) {
                                     $vn_q = (int) $va_matches[1];
                                     if ($vn_q >= 1 && $vn_q <= 4) {
                                         $vn_start_year = ((int) $va_token['value'] - 1) * 100 + ($vn_q - 1) * 25;
                                         $vn_end_year = ((int) $va_token['value'] - 1) * 100 + $vn_q * 25;
                                         $va_dates['start'] = array('month' => 1, 'day' => 1, 'year' => $vn_start_year, 'era' => TEP_ERA_AD, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => false, 'dont_window' => true);
                                         $va_dates['end'] = array('month' => 12, 'day' => 31, 'year' => $vn_end_year, 'era' => TEP_ERA_AD, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => false, 'dont_window' => true);
                                         $this->skipToken();
                                         $this->skipToken();
                                         $vn_state = TEP_STATE_ACCEPT;
                                         $vb_can_accept = true;
                                         break 2;
                                     }
                                 }
                             }
                         }
                         //
                         // Look for MYA dates
                         //
                         $va_peek = $this->peekToken(2);
                         if ($va_peek['type'] == TEP_TOKEN_MYA) {
                             $va_dates['start'] = array('month' => 1, 'day' => 1, 'year' => intval($va_token['value']) * -1000000, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => false, 'dont_window' => true);
                             $va_dates['end'] = array('month' => 12, 'day' => 31, 'year' => intval($va_token['value']) * -1000000, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => false, 'dont_window' => true);
                             $this->skipToken();
                             $this->skipToken();
                             $vn_state = TEP_STATE_ACCEPT;
                             $vb_can_accept = true;
                             break 2;
                         }
                         break;
                         # ----------------------
                 }
                 if ($va_date = $this->_parseDateExpression()) {
                     $va_dates['start'] = $va_date;
                     $vn_state = TEP_STATE_DATE_RANGE_CONJUNCTION;
                     $vb_can_accept = true;
                     break;
                 } else {
                     switch ($va_token['type']) {
                         # ----------------------
                         case TEP_TOKEN_SEASON_WINTER:
                         case TEP_TOKEN_SEASON_SPRING:
                         case TEP_TOKEN_SEASON_SUMMER:
                         case TEP_TOKEN_SEASON_AUTUMN:
                             $this->skipToken();
                             $va_peek = $this->peekToken();
                             if ($va_peek['type'] == TEP_TOKEN_INTEGER) {
                                 $vn_start_year = $va_peek['value'];
                             } else {
                                 $va_today = $this->gmgetdate();
                                 $vn_start_year = $va_today['year'];
                             }
                             $this->skipToken();
                             $vn_year_offset = 0;
                             switch ($va_token['type']) {
                                 case TEP_TOKEN_SEASON_WINTER:
                                     $vn_start_month = 12;
                                     $vn_end_month = 3;
                                     $vn_year_offset = 1;
                                     break;
                                 case TEP_TOKEN_SEASON_SPRING:
                                     $vn_start_month = 3;
                                     $vn_end_month = 6;
                                     break;
                                 case TEP_TOKEN_SEASON_SUMMER:
                                     $vn_start_month = 6;
                                     $vn_end_month = 9;
                                     break;
                                 case TEP_TOKEN_SEASON_AUTUMN:
                                     $vn_start_month = 9;
                                     $vn_end_month = 12;
                                     break;
                             }
                             $va_dates['start'] = array('month' => $vn_start_month, 'day' => 21, 'year' => $vn_start_year, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => false);
                             $va_dates['end'] = array('month' => $vn_end_month, 'day' => 20, 'year' => $vn_start_year + $vn_year_offset, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => false);
                             $vn_state = TEP_STATE_ACCEPT;
                             $vb_can_accept = true;
                             break;
                             # ----------------------
                         # ----------------------
                         case TEP_TOKEN_UNDATED:
                             $va_dates['start'] = array('month' => null, 'day' => null, 'year' => null, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 0, 'is_undated' => true);
                             $va_dates['end'] = array('month' => null, 'day' => null, 'year' => null, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 0, 'is_undated' => true);
                             $this->skipToken();
                             $vn_state = TEP_STATE_ACCEPT;
                             $vb_can_accept = true;
                             break;
                             # ----------------------
                         # ----------------------
                         case TEP_TOKEN_ALPHA:
                             #
                             # is this a decade expression?
                             #
                             $va_decade_dates = $this->_parseDecade($va_token, $vb_circa_is_set);
                             if (sizeof($va_decade_dates) > 0) {
                                 // found decade
                                 $va_next_token = $this->peekToken();
                                 if (is_array($va_next_token) && $va_next_token['type'] == TEP_TOKEN_RANGE_CONJUNCTION) {
                                     // decade is part of range
                                     $va_dates['start'] = $va_decade_dates['start'];
                                     $vn_state = TEP_STATE_DATE_RANGE_END_DATE;
                                     $this->skipToken();
                                     // skip range conjunction
                                     break;
                                 } else {
                                     $va_dates = $va_decade_dates;
                                     $vn_state = TEP_STATE_ACCEPT;
                                     $vb_can_accept = true;
                                     break;
                                 }
                             }
                             #
                             # is this a century expression?
                             #
                             $this->skipToken();
                             $va_next_token = $this->getToken();
                             $vs_next_token_lc = mb_strtolower($va_next_token['value']);
                             $vn_use_romans = $this->opo_datetime_settings->get("useRomanNumeralsForCenturies");
                             if ($vn_use_romans && in_array($vs_next_token_lc, $this->opo_language_settings->getList("centuryIndicator")) && preg_match("/^([MDCLXVI]+)(.*)\$/", $va_token['value'], $va_roman_matches) || in_array($vs_next_token_lc, $this->opo_language_settings->getList("centuryIndicator")) && preg_match("/^([\\d]+)(.*)\$/", $va_token['value'], $va_matches) || preg_match("/^([\\d]{2})[_]{2}\$/", $va_token['value'], $va_matches)) {
                                 $va_ordinals = $this->opo_language_settings->getList("ordinalSuffixes");
                                 $va_ordinals[] = $this->opo_language_settings->get("ordinalSuffixDefault");
                                 //if (in_array($va_matches[2], $va_ordinals)) {
                                 if ($vn_use_romans && caIsRomanNumerals($va_roman_matches[1])) {
                                     $vn_century = intval(caRomanArabic($va_roman_matches[1]));
                                 } else {
                                     $vn_century = intval($va_matches[1]);
                                 }
                                 if (in_array($vs_next_token_lc, $this->opo_language_settings->getList("centuryIndicator"))) {
                                     $va_next_token = null;
                                 }
                                 $vn_is_circa = 0;
                                 while ($va_modfier_token = is_array($va_next_token) ? $va_next_token : $this->getToken()) {
                                     $va_next_token = null;
                                     switch ($va_modfier_token['type']) {
                                         case TEP_TOKEN_ERA:
                                             if ($va_modfier_token['era'] == TEP_ERA_BC) {
                                                 $vn_century *= -1;
                                             }
                                             break;
                                         case TEP_TOKEN_QUESTION_MARK_UNCERTAINTY:
                                             $vn_is_circa = 1;
                                             break;
                                         default:
                                             $this->setParseError($va_modfier_token, TEP_ERROR_TRAILING_JUNK);
                                             break;
                                     }
                                 }
                                 if (preg_match("/^([\\d]{2})[_]{2}\$/", $va_token['value'])) {
                                     $vn_century += 1;
                                 }
                                 if ($vn_century > -100 && $vn_century < 100) {
                                     if ($vn_century < 0) {
                                         $vn_start_year = $vn_century * 100;
                                         $vn_end_year = $vn_century * 100 + 99;
                                     } else {
                                         $vn_start_year = ($vn_century - 1) * 100;
                                         $vn_end_year = ($vn_century - 1) * 100 + 99;
                                     }
                                     $va_dates['start'] = array('month' => 1, 'day' => 1, 'year' => $vn_start_year, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => $vn_is_circa);
                                     $va_dates['end'] = array('month' => 12, 'day' => 31, 'year' => $vn_end_year, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => $vn_is_circa);
                                     $vn_state = TEP_STATE_ACCEPT;
                                     $vb_can_accept = true;
                                     break;
                                 }
                                 //}
                             }
                             $this->setParseError($va_token, TEP_ERROR_INVALID_EXPRESSION);
                             break;
                             # ----------------------
                         # ----------------------
                         case TEP_TOKEN_INTEGER:
                             if ($va_time_element = $this->_parseTimeExpression()) {
                                 $va_dates['start'] = $va_time_element;
                                 $vn_state = TEP_STATE_TIME_RANGE_CONJUNCTION;
                                 $vb_can_accept = true;
                             } else {
                                 $this->skipToken();
                                 if ($this->tokens() == 1) {
                                     if ($va_token_mya = $this->getToken()) {
                                         if ($va_token_mya['type'] == TEP_TOKEN_MYA) {
                                             $va_dates['start'] = array('month' => null, 'day' => null, 'year' => intval($va_token['value']) * -1000000, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 0);
                                             $va_dates['end'] = array('month' => 12, 'day' => 31, 'year' => intval($va_token['value']) * -1000000, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 0);
                                             $vb_can_accept = true;
                                             break;
                                         }
                                     }
                                     $vb_can_accept = false;
                                     $this->setParseError($va_token, TEP_ERROR_INVALID_EXPRESSION);
                                 }
                             }
                             break;
                             # ----------------------
                         # ----------------------
                         case TEP_TOKEN_CIRCA:
                             $vb_circa_is_set = true;
                             $this->skipToken();
                             if ($va_date_element = $this->_parseDateElement()) {
                                 if ($va_peek = $this->peekToken()) {
                                     if ($va_peek['type'] == TEP_TOKEN_ERA) {
                                         $this->skipToken();
                                         if ($va_peek['era'] == TEP_ERA_BC) {
                                             $va_date_element['year'] *= -1;
                                         }
                                     }
                                 }
                                 $va_dates['start'] = array('month' => $va_date_element['month'], 'day' => $va_date_element['day'], 'year' => $va_date_element['year'], 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 1);
                                 $va_dates['end'] = array('month' => $va_date_element['month'], 'day' => $va_date_element['day'], 'year' => $va_date_element['year'], 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 1);
                                 $vn_state = TEP_STATE_DATE_RANGE_CONJUNCTION;
                                 $vb_can_accept = true;
                             }
                             break;
                             # ----------------------
                         # ----------------------
                         case TEP_TOKEN_TIME:
                             if ($va_time_element = $this->_parseTimeExpression()) {
                                 $va_dates['start'] = $va_time_element;
                                 $vn_state = TEP_STATE_TIME_RANGE_CONJUNCTION;
                                 $vb_can_accept = true;
                             }
                             break;
                             # ----------------------
                         # ----------------------
                         case TEP_TOKEN_PRE_RANGE_CONJUNCTION:
                             $this->skipToken();
                             break;
                             # ----------------------
                         # ----------------------
                         case TEP_TOKEN_BEFORE:
                             $this->skipToken();
                             $vn_state = TEP_STATE_BEFORE_GET_DATE;
                             break;
                             # ----------------------
                         # ----------------------
                         case TEP_TOKEN_AFTER:
                             $this->skipToken();
                             $vn_state = TEP_STATE_AFTER_GET_DATE;
                             break;
                             # ----------------------
                         # ----------------------
                         default:
                             $this->setParseError($va_token, TEP_ERROR_INVALID_EXPRESSION);
                             $vb_can_accept = false;
                             break;
                             # ----------------------
                     }
                     break;
                     # ----------------------
                 }
                 break;
                 # -------------------------------------------------------
             # -------------------------------------------------------
             case TEP_STATE_BEFORE_GET_DATE:
                 if ($va_date = $this->_parseDateExpression()) {
                     $va_dates['start'] = array('month' => null, 'day' => null, 'year' => TEP_START_OF_UNIVERSE, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 0);
                     $va_dates['end'] = $va_date;
                     $this->skipToken();
                     $vn_state = TEP_STATE_ACCEPT;
                     $vb_can_accept = true;
                 } else {
                     $this->setParseError($va_token, TEP_ERROR_INVALID_DATE);
                     $vb_can_accept = false;
                 }
                 break;
                 # -------------------------------------------------------
             # -------------------------------------------------------
             case TEP_STATE_AFTER_GET_DATE:
                 if ($va_date = $this->_parseDateExpression()) {
                     $va_dates['start'] = $va_date;
                     $va_dates['end'] = array('month' => null, 'day' => null, 'year' => TEP_END_OF_UNIVERSE, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 0);
                     $this->skipToken();
                     $vn_state = TEP_STATE_ACCEPT;
                     $vb_can_accept = true;
                 } else {
                     $this->setParseError($va_token, TEP_ERROR_INVALID_DATE);
                     $vb_can_accept = false;
                 }
                 break;
                 # -------------------------------------------------------
             # -------------------------------------------------------
             case TEP_STATE_DATE_RANGE_CONJUNCTION:
                 $vb_circa_is_set = false;
                 if ($va_token['type'] == TEP_TOKEN_RANGE_CONJUNCTION) {
                     $this->skipToken();
                     if (!$va_dates['start']['day']) {
                         $va_dates['start']['day'] = 1;
                     }
                     if (!$va_dates['start']['month']) {
                         $va_dates['start']['month'] = 1;
                     }
                     $vn_state = TEP_STATE_DATE_RANGE_END_DATE;
                 } else {
                     $this->setParseError($va_token, TEP_ERROR_INVALID_EXPRESSION);
                 }
                 $vb_can_accept = false;
                 break;
                 # -------------------------------------------------------
             # -------------------------------------------------------
             case TEP_STATE_DATE_RANGE_END_DATE:
                 $vb_circa_is_set = (bool) $va_dates['start']['is_circa'];
                 // carry over circa-ness from start
                 #
                 # is this a decade expression?
                 #
                 $va_decade_dates = $this->_parseDecade($va_token, $vb_circa_is_set);
                 if (sizeof($va_decade_dates) > 0) {
                     // found decade
                     $va_dates['end'] = $va_decade_dates['end'];
                     $vn_state = TEP_STATE_ACCEPT;
                     $vb_can_accept = true;
                     break;
                 }
                 if ($va_date = $this->_parseDateExpression(array('start' => $va_dates['start']))) {
                     $va_dates['end'] = $va_date;
                     if (isset($va_dates['start']['is_circa']) && $va_dates['start']['is_circa']) {
                         $va_dates['end']['is_circa'] = true;
                     }
                     $vn_state = TEP_STATE_ACCEPT;
                     $vb_can_accept = true;
                 } else {
                     $this->setParseError($va_token, TEP_ERROR_INVALID_EXPRESSION);
                     $vb_can_accept = false;
                 }
                 break;
                 # -------------------------------------------------------
             # -------------------------------------------------------
             case TEP_STATE_TIME_RANGE_CONJUNCTION:
                 if ($va_token['type'] == TEP_TOKEN_RANGE_CONJUNCTION) {
                     $this->skipToken();
                     $vn_state = TEP_STATE_TIME_RANGE_END_TIME;
                 } else {
                     $this->setParseError($va_token, TEP_ERROR_INVALID_EXPRESSION);
                 }
                 $vb_can_accept = false;
                 break;
                 # -------------------------------------------------------
             # -------------------------------------------------------
             case TEP_STATE_TIME_RANGE_END_TIME:
                 if ($va_time = $this->_parseTimeExpression()) {
                     $va_dates['end'] = $va_time;
                     $vn_state = TEP_STATE_ACCEPT;
                     $vb_can_accept = true;
                 } else {
                     $this->setParseError($va_token, TEP_ERROR_INVALID_TIME);
                     $vb_can_accept = false;
                 }
                 break;
                 # -------------------------------------------------------
             # -------------------------------------------------------
             case TEP_STATE_ACCEPT:
                 $this->setParseError($va_token, TEP_ERROR_TRAILING_JUNK);
                 $vb_can_accept = true;
                 break;
         }
         # -------------------------------------------------------
     }
     if ($this->getParseError()) {
         return false;
     } else {
         if (!$vb_can_accept) {
             if ($vn_state == TEP_STATE_DATE_RANGE_END_DATE) {
                 // Allow omission of end date of range to be taken as present/ongoing date
                 // This lets a user enter dates like "1971 - " instead of "after 1971"
                 $va_dates['end'] = array('month' => null, 'day' => null, 'year' => TEP_END_OF_UNIVERSE, 'hours' => null, 'minutes' => null, 'seconds' => null, 'uncertainty' => 0, 'uncertainty_units' => '', 'is_circa' => 0);
                 return $this->_processParseResults($va_dates, $pa_options);
             }
             $this->setParseError(null, TEP_ERROR_TRAILING_JUNK);
             return false;
         } else {
             return $this->_processParseResults($va_dates, $pa_options);
         }
     }
 }
 /**
  *
  *
  */
 protected function GetHierarchyLevelData($pa_ids)
 {
     $vo_dm = Datamodel::load();
     $o_config = Configuration::load();
     $t_object = new ca_objects();
     $va_level_data = array();
     foreach ($pa_ids as $pn_id) {
         $va_params = $this->getItemIDComponents($pn_id);
         $vs_table = $va_params['table'];
         $vn_id = $va_params['id'];
         $vn_start = $va_params['start'];
         $vn_item_count = 0;
         $t_item = $vo_dm->getInstanceByTableName($vs_table, true);
         $vs_label_table_name = $t_item->getLabelTableName();
         $vs_label_display_field_name = $t_item->getLabelDisplayField();
         $vs_pk = $t_item->primaryKey();
         if ($vn_start < 0) {
             $vn_start = 0;
         }
         $va_items_for_locale = array();
         if (!$vn_id && method_exists($t_item, "getHierarchyList")) {
             $vn_id = $this->request->getParameter('root_item_id', pString);
             $va_params = $this->getItemIDComponents($vn_id);
             $vs_table = $va_params['table'];
             $vn_id = $va_params['id'];
             $vn_start = $va_params['start'];
             $t_item = $vo_dm->getInstanceByTableName($vs_table, true);
             $vs_label_table_name = $t_item->getLabelTableName();
             $vs_label_display_field_name = $t_item->getLabelDisplayField();
             $vs_pk = $t_item->primaryKey();
             $t_item->load($vn_id);
             $va_tmp = array($vs_pk => $vn_id = $t_item->get($vs_table . '.' . $vs_pk), 'item_id' => $vs_table . '-' . $vn_id, 'parent_id' => $t_item->get($vs_table . '.parent_id'), 'idno' => $t_item->get($vs_table . '.idno'), $vs_label_display_field_name => $t_item->get($vs_table . '.preferred_labels.' . $vs_label_display_field_name), 'locale_id' => $t_item->get($vs_table . '.' . 'locale_id'));
             if (!$va_tmp[$vs_label_display_field_name]) {
                 $va_tmp[$vs_label_display_field_name] = $va_tmp['idno'];
             }
             if (!$va_tmp[$vs_label_display_field_name]) {
                 $va_tmp[$vs_label_display_field_name] = '???';
             }
             if (!($vs_item_template = trim($o_config->get("{$vs_table}_hierarchy_browser_display_settings")))) {
                 $vs_item_template = "^{$vs_table}.preferred_labels.{$vs_label_display_field_name}";
             }
             $va_tmp['name'] = caProcessTemplateForIDs($vs_item_template, $vs_table, array($va_tmp[$vs_pk]));
             // Child count is only valid if has_children is not null
             $va_tmp['children'] = $t_item->get('has_children') ? (int) $t_item->get('child_count') : 1;
             // TODO: fix
             $va_items[$va_tmp[$vs_pk]][$va_tmp['locale_id']] = $va_tmp;
             $va_items_for_locale = caExtractValuesByUserLocale($va_items);
         } else {
             if ($t_item->load($vn_id)) {
                 // id is the id of the parent for the level we're going to return
                 $va_additional_wheres = array();
                 $t_label_instance = $t_item->getLabelTableInstance();
                 if ($t_label_instance && $t_label_instance->hasField('is_preferred')) {
                     $va_additional_wheres[] = "(({$vs_label_table_name}.is_preferred = 1) OR ({$vs_label_table_name}.is_preferred IS NULL))";
                 }
                 if (!is_array($va_sorts = $o_config->getList($vs_table . '_hierarchy_browser_sort_values')) || !sizeof($va_sorts)) {
                     $va_sorts = null;
                 }
                 foreach ($va_sorts as $vn_i => $vs_sort_fld) {
                     $va_tmp = explode(".", $vs_sort_fld);
                     if ($va_tmp[1] == 'preferred_labels') {
                         $va_tmp[0] = $vs_label_table_name;
                         if (!($va_tmp[1] = $va_tmp[2])) {
                             $va_tmp[1] = $vs_label_display_field_name;
                         }
                         unset($va_tmp[2]);
                         $va_sorts[$vn_i] = join(".", $va_tmp);
                     }
                 }
                 if (!in_array($vs_sort_dir = strtolower($o_config->get($vs_table . '_hierarchy_browser_sort_direction')), array('asc', 'desc'))) {
                     $vs_sort_dir = 'asc';
                 }
                 $qr_children = $t_item->getHierarchyChildrenAsQuery($t_item->getPrimaryKey(), array('additionalTableToJoin' => $vs_label_table_name, 'additionalTableJoinType' => 'LEFT', 'additionalTableSelectFields' => array($vs_label_display_field_name, 'locale_id'), 'additionalTableWheres' => $va_additional_wheres, 'returnChildCounts' => true, 'sort' => $va_sorts, 'sortDirection' => $vs_sort_dir));
                 $va_items = array();
                 if (!($vs_item_template = trim($o_config->get("{$vs_table}_hierarchy_browser_display_settings")))) {
                     $vs_item_template = "^{$vs_table}.preferred_labels.{$vs_label_display_field_name}";
                 }
                 $va_child_counts = array();
                 if (($vn_max_items_per_page = $this->request->getParameter('max', pInteger)) < 1 || $vn_max_items_per_page > 1000) {
                     $vn_max_items_per_page = null;
                 }
                 $vn_c = 0;
                 $vn_item_count = $qr_children->numRows();
                 $qr_children->seek($vn_start);
                 while ($qr_children->nextRow()) {
                     $va_tmp = array($vs_pk => $vn_id = $qr_children->get($vs_table . '.' . $vs_pk), 'item_id' => $vs_table . '-' . $vn_id, 'parent_id' => $qr_children->get($vs_table . '.parent_id'), 'idno' => $qr_children->get($vs_table . '.idno'), 'locale_id' => $qr_children->get($vs_table . '.' . 'locale_id'));
                     if (!$va_tmp[$vs_label_display_field_name]) {
                         $va_tmp[$vs_label_display_field_name] = $va_tmp['idno'];
                     }
                     if (!$va_tmp[$vs_label_display_field_name]) {
                         $va_tmp[$vs_label_display_field_name] = '???';
                     }
                     $va_tmp['name'] = caProcessTemplateForIDs($vs_item_template, $vs_table, array($va_tmp[$vs_pk]));
                     // Child count is only valid if has_children is not null
                     $va_tmp['children'] = $qr_children->get('has_children') ? (int) $qr_children->get('child_count') : 0;
                     if (is_array($va_sorts)) {
                         $vs_sort_acc = array();
                         foreach ($va_sorts as $vs_sort) {
                             $vs_sort_acc[] = $qr_children->get($vs_sort);
                         }
                         $va_tmp['sort'] = join(";", $vs_sort_acc);
                     }
                     $va_items[$va_tmp['item_id']][$va_tmp['locale_id']] = $va_tmp;
                     $vn_c++;
                     if (!is_null($vn_max_items_per_page) && $vn_c >= $vn_max_items_per_page) {
                         break;
                     }
                 }
                 if ($t_item->tableName() == 'ca_collections') {
                     $vs_object_collection_rel_type = $o_config->get('ca_objects_x_collections_hierarchy_relationship_type');
                     $va_cross_table_items = $t_item->getRelatedItems('ca_objects', array('restrictToRelationshipTypes' => array($vs_object_collection_rel_type)));
                     $vn_item_count += sizeof($va_cross_table_items);
                     $va_ids = array();
                     foreach ($va_cross_table_items as $vn_x_item_id => $va_x_item) {
                         $va_items['ca_objects-' . $vn_x_item_id][$va_x_item['locale_id']] = $va_x_item;
                         $va_items['ca_objects-' . $va_x_item['object_id']][$va_x_item['locale_id']]['item_id'] = 'ca_objects-' . $va_x_item['object_id'];
                         $va_items['ca_objects-' . $va_x_item['object_id']][$va_x_item['locale_id']]['parent_id'] = $vn_id;
                         unset($va_items['ca_objects-' . $vn_x_item_id][$va_x_item['locale_id']]['labels']);
                         $va_items['ca_objects-' . $va_x_item['object_id']][$va_x_item['locale_id']]['children'] = 0;
                         $va_ids[] = $va_x_item['object_id'];
                     }
                     if (!($vs_item_template = trim($o_config->get("ca_objects_hierarchy_browser_display_settings")))) {
                         $vs_item_template = "^ca_objects.preferred_labels.name";
                     }
                     if (sizeof($va_ids)) {
                         $va_child_counts = $t_object->getHierarchyChildCountsForIDs($va_ids);
                         $va_templates = caProcessTemplateForIDs($vs_item_template, 'ca_objects', $va_ids, array('returnAsArray' => true));
                         foreach ($va_child_counts as $vn_id => $vn_c) {
                             $va_items['ca_objects-' . $vn_id][$va_x_item['locale_id']]['children'] = $vn_c;
                         }
                         foreach ($va_ids as $vn_i => $vn_id) {
                             $va_items['ca_objects-' . $vn_id][$va_x_item['locale_id']]['name'] = $va_templates[$vn_i];
                         }
                     }
                 }
                 $va_items_for_locale = caExtractValuesByUserLocale($va_items);
                 $vs_rank_fld = $t_item->getProperty('RANK');
                 $va_sorted_items = array();
                 foreach ($va_items_for_locale as $vn_id => $va_node) {
                     $vs_key = caSortableValue(mb_strtolower(preg_replace('![^A-Za-z0-9]!', '_', caRemoveAccents($va_node['name'])))) . "_" . $vn_id;
                     if (isset($va_node['sort']) && $va_node['sort']) {
                         $va_sorted_items[$va_node['sort']][$vs_key] = $va_node;
                     } else {
                         if ($vs_rank_fld && ($vs_rank = (int) sprintf("%08d", $va_node[$vs_rank_fld]))) {
                             $va_sorted_items[$vs_rank][$vs_key] = $va_node;
                         } else {
                             $va_sorted_items[$vs_key][$vs_key] = $va_node;
                         }
                     }
                 }
                 ksort($va_sorted_items);
                 if ($vs_sort_dir == 'desc') {
                     $va_sorted_items = array_reverse($va_sorted_items);
                 }
                 $va_items_for_locale = array();
                 foreach ($va_sorted_items as $vs_k => $va_v) {
                     ksort($va_v);
                     if ($vs_sort_dir == 'desc') {
                         $va_v = array_reverse($va_v);
                     }
                     $va_items_for_locale = array_merge($va_items_for_locale, $va_v);
                 }
             }
         }
         $va_items_for_locale['_sortOrder'] = array_keys($va_items_for_locale);
         $va_items_for_locale['_primaryKey'] = $t_item->primaryKey();
         // pass the name of the primary key so the hierbrowser knows where to look for item_id's
         $va_items_for_locale['_itemCount'] = $vn_item_count;
         //$qr_children ? $qr_children->numRows() : 0;
         $va_level_data[$pn_id] = $va_items_for_locale;
     }
     return $va_level_data;
 }
Esempio n. 3
0
 /**
  *  Takes a word and returns it reduced to its stem.
  *
  *  Non-alphanumerics and hyphens are removed, except for dots and
  *  apostrophes, and if the word is less than three characters in
  *  length, it will be stemmed according to the five-step
  *  Porter stemming algorithm.
  *
  *  Note special cases here: hyphenated words (such as half-life) will
  *  only have the base after the last hyphen stemmed (so half-life would
  *  only have "life" subject to stemming). Handles multi-hyphenated
  *  words, too.
  *
  *  @param string $word Word to reduce
  *  @access public
  *  @return string Stemmed word
  */
 public function stem($word, $lang = 'en')
 {
     if (empty($word)) {
         return false;
     }
     $orig_word = $word;
     if (isset($this->opa_stem_cache[$word])) {
         return $this->opa_stem_cache[$word];
     }
     // Use PECL function if it is installed
     if (function_exists('stem')) {
         $this->opa_stem_cache[$word] = stem($word, $this->lang2code($lang));
         return $this->opa_stem_cache[$word];
     }
     $result = '';
     $word = strtolower(caRemoveAccents($word));
     // Strip punctuation, etc. Keep ' and . for URLs and contractions.
     if (substr($word, -2) == "'s") {
         $word = substr($word, 0, -2);
     }
     if (function_exists('iconv')) {
         $word = iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $word);
     }
     $word = preg_replace("/[^a-z0-9'.-]/", '', $word);
     $first = '';
     if (strpos($word, '-') !== false) {
         //list($first, $word) = explode('-', $word);
         //$first .= '-';
         $first = substr($word, 0, strrpos($word, '-') + 1);
         // Grabs hyphen too
         $word = substr($word, strrpos($word, '-') + 1);
     }
     if (strlen($word) > 2) {
         $word = $this->_step_1($word);
         $word = $this->_step_2($word);
         $word = $this->_step_3($word);
         $word = $this->_step_4($word);
         $word = $this->_step_5($word);
     }
     $result = $first . $word;
     $this->opa_stem_cache[$orig_word] = $result;
     return $result;
 }
 /**
  * Given a item_id (request parameter 'id') returns a list of direct children for use in the hierarchy browser
  * Returned data is JSON format
  */
 public function GetHierarchyLevel()
 {
     header("Content-type: application/json");
     $ps_bundle = (string) $this->request->getParameter('bundle', pString);
     $pa_ids = explode(";", $ps_ids = $this->request->getParameter('id', pString));
     if (!sizeof($pa_ids)) {
         $pa_ids = array(null);
     }
     $t_item = $this->opo_item_instance;
     if (!$t_item->isHierarchical()) {
         return;
     }
     $va_level_data = array();
     foreach ($pa_ids as $pn_id) {
         $va_tmp = explode(":", $pn_id);
         $vn_id = $va_tmp[0];
         $vn_start = (int) $va_tmp[1];
         if ($vn_start < 0) {
             $vn_start = 0;
         }
         if (sizeof($va_tmp) < 2) {
             $pn_id = '0:0';
         }
         $va_items_for_locale = array();
         $vb_gen = true;
         if (!$vn_id && method_exists($t_item, "getHierarchyList")) {
             $vn_id = (int) $this->request->getParameter('root_item_id', pInteger);
             $t_item->load($vn_id);
             // no id so by default return list of available hierarchies
             $va_items_for_locale = $t_item->getHierarchyList();
             if (sizeof($va_items_for_locale) == 1 && $this->request->getAppConfig()->get($t_item->tableName() . '_hierarchy_browser_hide_root')) {
                 $va_item = array_shift($va_items_for_locale);
                 $vn_id = $va_item['item_id'];
             } else {
                 $vb_gen = false;
             }
         }
         if ($vb_gen && $t_item->load($vn_id)) {
             // id is the id of the parent for the level we're going to return
             $vs_table_name = $t_item->tableName();
             $vs_label_table_name = $this->opo_item_instance->getLabelTableName();
             $vs_label_display_field_name = $this->opo_item_instance->getLabelDisplayField();
             $vs_pk = $this->opo_item_instance->primaryKey();
             $va_additional_wheres = array();
             $t_label_instance = $this->opo_item_instance->getLabelTableInstance();
             if ($t_label_instance && $t_label_instance->hasField('is_preferred')) {
                 $va_additional_wheres[] = "(({$vs_label_table_name}.is_preferred = 1) OR ({$vs_label_table_name}.is_preferred IS NULL))";
             }
             $o_config = Configuration::load();
             if (!is_array($va_sorts = $o_config->getList($this->ops_table_name . '_hierarchy_browser_sort_values')) || !sizeof($va_sorts)) {
                 $va_sorts = array();
             }
             foreach ($va_sorts as $vn_i => $vs_sort_fld) {
                 $va_tmp = explode(".", $vs_sort_fld);
                 if ($va_tmp[1] == 'preferred_labels') {
                     $va_tmp[0] = $vs_label_table_name;
                     if (!($va_tmp[1] = $va_tmp[2])) {
                         $va_tmp[1] = $vs_label_display_field_name;
                     }
                     unset($va_tmp[2]);
                     $va_sorts[$vn_i] = join(".", $va_tmp);
                 }
             }
             if (!in_array($vs_sort_dir = strtolower($o_config->get($this->ops_table_name . '_hierarchy_browser_sort_direction')), array('asc', 'desc'))) {
                 $vs_sort_dir = 'asc';
             }
             $va_items = array();
             if (is_array($va_item_ids = $t_item->getHierarchyChildren($t_item->getPrimaryKey(), array('idsOnly' => true))) && sizeof($va_item_ids)) {
                 $qr_children = $t_item->makeSearchResult($t_item->tableName(), $va_item_ids);
                 $va_child_counts = $t_item->getHierarchyChildCountsForIDs($va_item_ids);
                 if (!($vs_item_template = trim($o_config->get("{$vs_table_name}_hierarchy_browser_display_settings")))) {
                     $vs_item_template = "^{$vs_table_name}.preferred_labels.{$vs_label_display_field_name}";
                 }
                 if (($vn_max_items_per_page = $this->request->getParameter('max', pInteger)) < 1 || $vn_max_items_per_page > 1000) {
                     $vn_max_items_per_page = 100;
                 }
                 $vn_c = 0;
                 while ($qr_children->nextHit()) {
                     $va_tmp = array($vs_pk => $vn_id = $qr_children->get($this->ops_table_name . '.' . $vs_pk), 'item_id' => $vn_id, 'parent_id' => $qr_children->get($this->ops_table_name . '.parent_id'), 'idno' => $qr_children->get($this->ops_table_name . '.idno'), $vs_label_display_field_name => $qr_children->get($this->ops_table_name . '.preferred_labels.' . $vs_label_display_field_name), 'locale_id' => $qr_children->get($this->ops_table_name . '.' . 'locale_id'));
                     if (!$va_tmp[$vs_label_display_field_name]) {
                         $va_tmp[$vs_label_display_field_name] = $va_tmp['idno'];
                     }
                     if (!$va_tmp[$vs_label_display_field_name]) {
                         $va_tmp[$vs_label_display_field_name] = '???';
                     }
                     $va_tmp['name'] = caProcessTemplateForIDs($vs_item_template, $vs_table_name, array($va_tmp[$vs_pk]), array('requireLinkTags' => true));
                     if (!$va_tmp['name']) {
                         $va_tmp['name'] = '??? ' . $va_tmp[$vs_pk];
                     }
                     // Child count is only valid if has_children is not null
                     $va_tmp['children'] = isset($va_child_counts[$vn_id]) ? (int) $va_child_counts[$vn_id] : 0;
                     if (strlen($vs_enabled = $qr_children->get('is_enabled')) > 0) {
                         $va_tmp['is_enabled'] = $vs_enabled;
                     }
                     if (is_array($va_sorts)) {
                         $vs_sort_acc = array();
                         foreach ($va_sorts as $vs_sort) {
                             $vs_sort_acc[] = $qr_children->get($vs_sort, array('sortable' => true));
                         }
                         $va_tmp['sort'] = join(";", $vs_sort_acc);
                     }
                     $va_items[$va_tmp[$vs_pk]][$va_tmp['locale_id']] = $va_tmp;
                     $vn_c++;
                 }
                 $va_items_for_locale = caExtractValuesByUserLocale($va_items);
             }
             $vs_rank_fld = $t_item->getProperty('RANK');
             $va_sorted_items = array();
             foreach ($va_items_for_locale as $vn_id => $va_node) {
                 $vs_key = caSortableValue(mb_strtolower(preg_replace('![^A-Za-z0-9]!', '_', caRemoveAccents($va_node['name'])))) . "_" . $vn_id;
                 if (isset($va_node['sort']) && $va_node['sort']) {
                     $va_sorted_items[$va_node['sort']][$vs_key] = $va_node;
                 } else {
                     if ($vs_rank_fld && ($vs_rank = (int) sprintf("%08d", $va_node[$vs_rank_fld]))) {
                         $va_sorted_items[$vs_rank][$vs_key] = $va_node;
                     } else {
                         $va_sorted_items[$vs_key][$vs_key] = $va_node;
                     }
                 }
             }
             ksort($va_sorted_items);
             if ($vs_sort_dir == 'desc') {
                 $va_sorted_items = array_reverse($va_sorted_items);
             }
             $va_items_for_locale = array();
             $va_sorted_items = array_slice($va_sorted_items, $vn_start, $vn_max_items_per_page);
             foreach ($va_sorted_items as $vs_k => $va_v) {
                 ksort($va_v);
                 if ($vs_sort_dir == 'desc') {
                     $va_v = array_reverse($va_v);
                 }
                 $va_items_for_locale = array_merge($va_items_for_locale, $va_v);
             }
         }
         $va_items_for_locale['_sortOrder'] = array_keys($va_items_for_locale);
         $va_items_for_locale['_primaryKey'] = $t_item->primaryKey();
         // pass the name of the primary key so the hierbrowser knows where to look for item_id's
         $va_items_for_locale['_itemCount'] = $qr_children ? $qr_children->numHits() : 0;
         $va_level_data[$pn_id] = $va_items_for_locale;
     }
     if (!$this->request->getParameter('init', pInteger)) {
         // only set remember "last viewed" if the load is done interactively
         // if the GetHierarchyLevel() call is part of the initialization of the hierarchy browser
         // then all levels are loaded, sometimes out-of-order; if we record these initialization loads
         // as the 'last viewed' we can end up losing the true 'last viewed' value
         //
         // ... so the hierbrowser passes an extra 'init' parameters set to 1 if the GetHierarchyLevel() call
         // is part of a browser initialization
         $this->request->session->setVar($this->ops_table_name . '_' . $ps_bundle . '_browse_last_id', array_pop($pa_ids));
     }
     $this->view->setVar(str_replace(' ', '_', $this->ops_name_singular) . '_list', $va_level_data);
     return $this->render(str_replace(' ', '_', $this->ops_name_singular) . '_hierarchy_level_json.php');
 }
Esempio n. 5
0
 /**
  *
  */
 public function __call($ps_function, $pa_args)
 {
     $o_config = caGetBrowseConfig();
     $this->view->setVar("config", $o_config);
     $ps_function = strtolower($ps_function);
     $ps_type = $this->request->getActionExtra();
     if (!($va_browse_info = caGetInfoForBrowseType($ps_function))) {
         // invalid browse type – throw error
         die("Invalid browse type");
     }
     MetaTagManager::setWindowTitle($this->request->config->get("app_display_name") . ": " . _t("Browse %1", $va_browse_info["displayName"]));
     $this->view->setVar("browse_type", $ps_function);
     $vs_class = $va_browse_info['table'];
     $va_types = caGetOption('restrictToTypes', $va_browse_info, array(), array('castTo' => 'array'));
     $this->opo_result_context = new ResultContext($this->request, $va_browse_info['table'], $this->ops_find_type);
     // Don't set last find when loading facet, as some other controllers use this action and setting
     // last find will disrupt ResultContext navigation by setting it to "browse" when in fact a search (or some other
     // context) is still in effect.
     if (!$this->request->getParameter('getFacet', pInteger)) {
         $this->opo_result_context->setAsLastFind();
     }
     $this->view->setVar('browseInfo', $va_browse_info);
     $this->view->setVar('paging', in_array(strtolower($va_browse_info['paging']), array('continous', 'nextprevious', 'letter')) ? strtolower($va_browse_info['paging']) : 'continous');
     $this->view->setVar('name', $va_browse_info['displayName']);
     $this->view->setVar('options', caGetOption('options', $va_browse_info, array(), array('castTo' => 'array')));
     $ps_view = $this->request->getParameter('view', pString);
     $va_views = caGetOption('views', $va_browse_info, array(), array('castTo' => 'array'));
     if (!is_array($va_views) || sizeof($va_views) == 0) {
         $va_views = array('list' => array(), 'images' => array(), 'timeline' => array(), 'map' => array(), 'timelineData' => array(), 'pdf' => array());
     } else {
         $va_views['pdf'] = array();
         $va_views['timelineData'] = array();
     }
     if (!in_array($ps_view, array_keys($va_views))) {
         $ps_view = array_shift(array_keys($va_views));
     }
     $vs_format = $ps_view == 'timelineData' ? 'json' : 'html';
     caAddPageCSSClasses(array($vs_class, $ps_function));
     $this->view->setVar('isNav', $vb_is_nav = (bool) $this->request->getParameter('isNav', pInteger));
     // flag for browses that originate from nav bar
     $t_instance = $this->getAppDatamodel()->getInstanceByTableName($vs_class, true);
     $vn_type_id = $t_instance->getTypeIDForCode($ps_type);
     $this->view->setVar('t_instance', $t_instance);
     $this->view->setVar('table', $va_browse_info['table']);
     $this->view->setVar('primaryKey', $t_instance->primaryKey());
     $this->view->setVar('browse', $o_browse = caGetBrowseInstance($vs_class));
     $this->view->setVar('views', caGetOption('views', $va_browse_info, array(), array('castTo' => 'array')));
     $this->view->setVar('view', $ps_view);
     $this->view->setVar('viewIcons', $o_config->getAssoc("views"));
     //
     // Load existing browse if key is specified
     //
     if ($ps_cache_key = $this->request->getParameter('key', pString)) {
         $o_browse->reload($ps_cache_key);
     }
     if (is_array($va_types) && sizeof($va_types)) {
         $o_browse->setTypeRestrictions($va_types, array('dontExpandHierarchically' => caGetOption('dontExpandTypesHierarchically', $va_browse_info, false)));
     }
     //
     // Clear criteria if required
     //
     if ($vs_remove_criterion = $this->request->getParameter('removeCriterion', pString)) {
         $o_browse->removeCriteria($vs_remove_criterion, array($this->request->getParameter('removeID', pString)));
     }
     if ((bool) $this->request->getParameter('clear', pInteger)) {
         $o_browse->removeAllCriteria();
     }
     //
     // Return facet content
     //
     if ($this->request->getParameter('getFacet', pInteger)) {
         $vs_facet = $this->request->getParameter('facet', pString);
         $vn_s = $vb_is_nav ? $this->request->getParameter('s', pInteger) : 0;
         // start menu-based browse menu facet data at page boundary; all others get the full facet
         $this->view->setVar('start', $vn_s);
         $this->view->setVar('limit', $vn_limit = $vb_is_nav ? 500 : null);
         // break facet into pages for menu-based browse menu
         $this->view->setVar('facet_name', $vs_facet);
         $this->view->setVar('key', $o_browse->getBrowseID());
         $this->view->setVar('facet_info', $va_facet_info = $o_browse->getInfoForFacet($vs_facet));
         # --- pull in different views based on format for facet - alphabetical, list, hierarchy
         switch ($va_facet_info["group_mode"]) {
             case "alphabetical":
             case "list":
             default:
                 $this->view->setVar('facet_content', $o_browse->getFacet($vs_facet, array("checkAccess" => $this->opa_access_values, 'start' => $vn_s, 'limit' => $vn_limit)));
                 $this->render("Browse/list_facet_html.php");
                 break;
             case "hierarchical":
                 $this->render("Browse/hierarchy_facet_html.php");
                 break;
         }
         return;
     }
     //
     // Add criteria and execute
     //
     // Get any preset-criteria
     $va_base_criteria = caGetOption('baseCriteria', $va_browse_info, null);
     if ($vs_facet = $this->request->getParameter('facet', pString)) {
         $o_browse->addCriteria($vs_facet, array($this->request->getParameter('id', pString)));
     } else {
         if ($o_browse->numCriteria() == 0) {
             if (is_array($va_base_criteria)) {
                 foreach ($va_base_criteria as $vs_facet => $vs_value) {
                     $o_browse->addCriteria($vs_facet, $vs_value);
                 }
             } else {
                 $o_browse->addCriteria("_search", array("*"));
             }
         }
     }
     //
     // Sorting
     //
     $vb_sort_changed = false;
     if (!($ps_sort = $this->request->getParameter("sort", pString))) {
         if (!($ps_sort = $this->opo_result_context->getCurrentSort())) {
             if (is_array($va_sorts = caGetOption('sortBy', $va_browse_info, null))) {
                 $ps_sort = array_shift(array_keys($va_sorts));
                 $vb_sort_changed = true;
             }
         }
     } else {
         $vb_sort_changed = true;
     }
     if ($vb_sort_changed) {
         # --- set the default sortDirection if available
         $va_sort_direction = caGetOption('sortDirection', $va_browse_info, null);
         if ($ps_sort_direction = $va_sort_direction[$ps_sort]) {
             $this->opo_result_context->setCurrentSortDirection($ps_sort_direction);
         }
     }
     if (!($ps_sort_direction = $this->request->getParameter("direction", pString))) {
         if (!($ps_sort_direction = $this->opo_result_context->getCurrentSortDirection())) {
             $ps_sort_direction = 'asc';
         }
     }
     $this->opo_result_context->setCurrentSort($ps_sort);
     $this->opo_result_context->setCurrentSortDirection($ps_sort_direction);
     $va_sort_by = caGetOption('sortBy', $va_browse_info, null);
     $this->view->setVar('sortBy', is_array($va_sort_by) ? $va_sort_by : null);
     $this->view->setVar('sortBySelect', $vs_sort_by_select = is_array($va_sort_by) ? caHTMLSelect("sort", $va_sort_by, array('id' => "sort"), array("value" => $ps_sort)) : '');
     $this->view->setVar('sortControl', $vs_sort_by_select ? _t('Sort with %1', $vs_sort_by_select) : '');
     $this->view->setVar('sort', $ps_sort);
     $this->view->setVar('sort_direction', $ps_sort_direction);
     if (caGetOption('dontShowChildren', $va_browse_info, false)) {
         $o_browse->addResultFilter('ca_objects.parent_id', 'is', 'null');
     }
     //
     // Current criteria
     //
     $va_criteria = $o_browse->getCriteriaWithLabels();
     if (isset($va_criteria['_search']) && isset($va_criteria['_search']['*'])) {
         unset($va_criteria['_search']);
     }
     $o_browse->execute(array('checkAccess' => $this->opa_access_values, 'showAllForNoCriteriaBrowse' => true));
     //
     // Facets
     //
     if ($vs_facet_group = caGetOption('facetGroup', $va_browse_info, null)) {
         $o_browse->setFacetGroup($vs_facet_group);
     }
     $va_available_facet_list = caGetOption('availableFacets', $va_browse_info, null);
     $va_facets = $o_browse->getInfoForAvailableFacets();
     foreach ($va_facets as $vs_facet_name => $va_facet_info) {
         if (isset($va_base_criteria[$vs_facet_name])) {
             continue;
         }
         // skip base criteria
         $va_facets[$vs_facet_name]['content'] = $o_browse->getFacet($vs_facet_name, array("checkAccess" => $this->opa_access_values, 'checkAvailabilityOnly' => caGetOption('deferred_load', $va_facet_info, false, array('castTo' => 'bool'))));
     }
     $this->view->setVar('facets', $va_facets);
     $this->view->setVar('key', $vs_key = $o_browse->getBrowseID());
     $this->request->session->setVar($ps_function . '_last_browse_id', $vs_key);
     // remove base criteria from display list
     if (is_array($va_base_criteria)) {
         foreach ($va_base_criteria as $vs_base_facet => $vs_criteria_value) {
             unset($va_criteria[$vs_base_facet]);
         }
     }
     $va_criteria_for_display = array();
     foreach ($va_criteria as $vs_facet_name => $va_criterion) {
         $va_facet_info = $o_browse->getInfoForFacet($vs_facet_name);
         foreach ($va_criterion as $vn_criterion_id => $vs_criterion) {
             $va_criteria_for_display[] = array('facet' => $va_facet_info['label_singular'], 'facet_name' => $vs_facet_name, 'value' => $vs_criterion, 'id' => $vn_criterion_id);
         }
     }
     $this->view->setVar('criteria', $va_criteria_for_display);
     //
     // Results
     //
     $qr_res = $o_browse->getResults(array('sort' => $va_sort_by[$ps_sort], 'sort_direction' => $ps_sort_direction));
     if ($vs_letter_bar_field = caGetOption('showLetterBarFrom', $va_browse_info, null)) {
         // generate letter bar
         $va_letters = array();
         while ($qr_res->nextHit()) {
             $va_letters[caRemoveAccents(mb_strtolower(mb_substr($qr_res->get($vs_letter_bar_field), 0, 1)))]++;
         }
         $this->view->setVar('letterBar', $va_letters);
         $qr_res->seek(0);
     }
     $this->view->setVar('showLetterBar', (bool) $vs_letter_bar_field);
     if ($vs_letter_bar_field && ($vs_l = mb_strtolower($this->request->getParameter('l', pString)))) {
         $va_filtered_ids = array();
         while ($qr_res->nextHit()) {
             if (caRemoveAccents(mb_strtolower(mb_substr($qr_res->get($vs_letter_bar_field), 0, 1))) == $vs_l) {
                 $va_filtered_ids[] = $qr_res->getPrimaryKey();
             }
         }
         if (sizeof($va_filtered_ids) > 0) {
             $qr_res = caMakeSearchResult($vs_class, $va_filtered_ids);
         }
     }
     $this->view->setVar('letter', $vs_l);
     $this->view->setVar('result', $qr_res);
     if (!($pn_hits_per_block = $this->request->getParameter("n", pString))) {
         if (!($pn_hits_per_block = $this->opo_result_context->getItemsPerPage())) {
             $pn_hits_per_block = $o_config->get("defaultHitsPerBlock");
         }
     }
     $this->opo_result_context->getItemsPerPage($pn_hits_per_block);
     $this->view->setVar('hits_per_block', $pn_hits_per_block);
     $this->view->setVar('start', $vn_start = $this->request->getParameter('s', pInteger));
     $this->opo_result_context->setParameter('key', $vs_key);
     if (!$this->request->isAjax()) {
         if (($vn_key_start = $vn_start - 500) < 0) {
             $vn_key_start = 0;
         }
         $qr_res->seek($vn_key_start);
         $this->opo_result_context->setResultList($qr_res->getPrimaryKeyValues(500));
         $qr_res->seek($vn_start);
     }
     $this->opo_result_context->saveContext();
     if ($ps_type) {
         if ($this->render("Browse/{$vs_class}_{$ps_type}_{$ps_view}_{$vs_format}.php")) {
             return;
         }
     }
     switch ($ps_view) {
         case 'pdf':
             $this->_genExport($qr_res, $this->request->getParameter("export_format", pString), $vs_search_expression, $this->getCriteriaForDisplay($o_browse));
             break;
         case 'timelineData':
             $this->view->setVar('view', 'timeline');
             $this->render("Browse/browse_results_timelineData_json.php");
             break;
         default:
             $this->opo_result_context->setCurrentView($ps_view);
             $this->render("Browse/browse_results_html.php");
             break;
     }
 }
 /**
  * Given a item_id (request parameter 'id') returns a list of direct children for use in the hierarchy browser
  * Returned data is JSON format
  */
 public function GetHierarchyLevel()
 {
     $ps_bundle = (string) $this->request->getParameter('bundle', pString);
     $pa_ids = explode(";", $ps_ids = $this->request->getParameter('id', pString));
     if (!sizeof($pa_ids)) {
         $pa_ids = array(null);
     }
     $t_item = $this->opo_item_instance;
     $vs_template = $t_item->getAppConfig()->get('ca_list_items_hierarchy_browser_display_settings');
     $va_lists = array();
     if ($ps_lists = $this->request->getParameter('lists', pString)) {
         $va_lists = explode(";", $ps_lists);
     }
     if (($vn_max_items_per_page = $this->request->getParameter('max', pInteger)) < 1 || $vn_max_items_per_page > 1000) {
         $vn_max_items_per_page = 100;
     }
     $va_level_data = array();
     foreach ($pa_ids as $pn_id) {
         $va_tmp = explode(":", $pn_id);
         $vn_id = $va_tmp[0];
         $vn_start = (int) $va_tmp[1];
         if ($vn_start < 0) {
             $vn_start = 0;
         }
         if (!$vn_id && method_exists($t_item, "getHierarchyList")) {
             if (!($pn_list_id = $this->request->getParameter('list_id', pInteger))) {
                 // no id so by default return list of available hierarchies
                 $va_list_items = $t_item->getHierarchyList();
                 if (sizeof($va_lists)) {
                     // filter out lists that weren't specified
                     foreach ($va_list_items as $vn_list_id => $va_list) {
                         if (!in_array($vn_list_id, $va_lists) && !in_array($va_list['list_code'], $va_lists)) {
                             unset($va_list_items[$vn_list_id]);
                         }
                     }
                 } else {
                     if ($this->request->getParameter('voc', pInteger)) {
                         // Only show vocabularies
                         foreach ($va_list_items as $vn_list_id => $va_list) {
                             if (!$va_list['use_as_vocabulary']) {
                                 unset($va_list_items[$vn_list_id]);
                             }
                         }
                     }
                 }
             }
         } else {
             if ($t_item->load($vn_id)) {
                 // id is the id of the parent for the level we're going to return
                 $t_list = new ca_lists($vn_list_id = $t_item->get('list_id'));
                 $vs_label_table_name = $this->opo_item_instance->getLabelTableName();
                 $vs_label_display_field_name = $this->opo_item_instance->getLabelDisplayField();
                 $va_list_items = $t_list->getItemsForList($vn_list_id, array('returnHierarchyLevels' => false, 'item_id' => $vn_id, 'extractValuesByUserLocale' => true, 'sort' => $t_list->get('sort_type'), 'directChildrenOnly' => true, 'limit' => $vn_max_items_per_page, 'start' => $vn_start));
                 // output
                 $va_display_values = caProcessTemplateForIDs($vs_template, 'ca_list_items', array_keys($va_list_items), array('requireLinkTags' => true, 'returnAsArray' => true));
                 $vn_c = 0;
                 foreach ($va_list_items as $vn_item_id => $va_item) {
                     if ($vn_c >= $vn_start) {
                         unset($va_item['description']);
                         unset($va_item['icon']);
                         if (!$va_item[$vs_label_display_field_name]) {
                             $va_item[$vs_label_display_field_name] = $va_item['idno'];
                         }
                         if (!$va_item[$vs_label_display_field_name]) {
                             $va_item[$vs_label_display_field_name] = '???';
                         }
                         $va_item['name'] = $va_display_values[$vn_c];
                         if (!$va_item['name']) {
                             $va_item['name'] = '??? ' . $vn_item_id;
                         }
                         $va_item['table'] = 'ca_list_items';
                         // Child count is only valid if has_children is not null
                         $va_item['children'] = 0;
                         $va_list_items[$vn_item_id] = $va_item;
                     }
                     $vn_c++;
                     if ($vn_c > $vn_start + $vn_max_items_per_page) {
                         break;
                     }
                 }
                 if (sizeof($va_list_items)) {
                     $o_db = new Db();
                     $qr_res = $o_db->query("\n\t\t\t\t\t\t\t\tSELECT count(*) c, parent_id\n\t\t\t\t\t\t\t\tFROM ca_list_items\n\t\t\t\t\t\t\t\tWHERE \n\t\t\t\t\t\t\t\t\tparent_id IN (" . join(",", array_keys($va_list_items)) . ") AND deleted = 0\n\t\t\t\t\t\t\t\tGROUP BY parent_id\n\t\t\t\t\t\t\t");
                     while ($qr_res->nextRow()) {
                         $va_list_items[$qr_res->get('parent_id')]['children'] = $qr_res->get('c');
                     }
                 }
             }
         }
         $va_list_items_sortable = [];
         foreach ($va_list_items as $vn_item_id => $va_item) {
             $va_list_items_sortable[caSortableValue(mb_strtolower(preg_replace('![^A-Za-z0-9]!', '_', caRemoveAccents($va_item['name_plural'])))) . '_' . $vn_item_id] = $va_item;
         }
         $va_list_items = $va_list_items_sortable;
         $va_list_items['_sortOrder'] = array_keys($va_list_items);
         $va_list_items['_primaryKey'] = $t_item->primaryKey();
         // pass the name of the primary key so the hierbrowser knows where to look for item_id's
         $va_list_items['_itemCount'] = sizeof($va_list_items);
         //$t_list ? $t_list->numItemsInList() : ($qr_res ? $qr_res->numRows() : 0);
         $va_level_data[$pn_id] = $va_list_items;
     }
     if (!$this->request->getParameter('init', pInteger)) {
         // only set remember "last viewed" if the load is done interactively
         // if the GetHierarchyLevel() call is part of the initialization of the hierarchy browser
         // then all levels are loaded, sometimes out-of-order; if we record these initialization loads
         // as the 'last viewed' we can end up losing the true 'last viewed' value
         //
         // ... so the hierbrowser passes an extra 'init' parameters set to 1 if the GetHierarchyLevel() call
         // is part of a browser initialization
         $this->request->session->setVar($this->ops_table_name . '_' . $ps_bundle . '_browse_last_id', $pn_id);
     }
     $this->view->setVar('dontShowSymbols', (bool) $this->request->getParameter('noSymbols', pString));
     $this->view->setVar('list_item_list', $va_level_data);
     return $this->render('list_item_hierarchy_level_json.php');
 }