public function search_content($key, $opts, $admin = false)
 {
     PerchUtil::debug('Search term: ' . $key, 'success');
     $this->mb_fallback();
     $search_method = 'get_search_sql';
     $format_method = 'format_result';
     if ($admin) {
         $search_method = 'get_admin_search_sql';
         $format_method = 'format_admin_result';
     }
     $search_handlers = PerchSystem::get_registered_search_handlers();
     $search_content = true;
     if (PerchUtil::count($opts['apps'])) {
         $search_content = in_array('PerchContent', $opts['apps']);
         if (PerchUtil::count($search_handlers)) {
             $new_handlers = array();
             foreach ($search_handlers as $handler) {
                 $short = str_replace('_SearchHandler', '', $handler);
                 if (in_array($short, $opts['apps'])) {
                     $new_handlers[] = $handler;
                 }
             }
             $search_handlers = $new_handlers;
         }
     }
     $out = array();
     if ($key != '') {
         if (!$this->api) {
             $this->api = new PerchAPI(1.0, 'content');
         }
         $encoded_key = str_replace('"', '', PerchUtil::json_safe_encode($key));
         $Paging = $this->api->get('Paging');
         if (isset($opts['count'])) {
             $Paging->set_per_page($opts['count']);
             if (isset($opts['start']) && $opts['start'] != '') {
                 $Paging->set_start_position($opts['start']);
             }
         } else {
             $Paging->disable();
         }
         // Proper query using FULLTEXT
         $sql = $Paging->select_sql();
         if (!$search_content) {
             $sql .= ' \'PerchContent_SearchHandler\' AS source, \'\' AS score, \'\' AS col1, \'\' AS col2, \'\' AS col3, \'\' AS col4, \'\' AS col5, \'\' AS col6, \'\' AS col7, \'\' AS col8 FROM ' . $this->table . ' WHERE 1=0 UNION ';
         }
         if (PerchUtil::count($search_handlers)) {
             $first = true;
             foreach ($search_handlers as $handler) {
                 $handler_sql = false;
                 if (method_exists($handler, $search_method)) {
                     $handler_sql = call_user_func(array($handler, $search_method), $key, $opts);
                 }
                 if ($handler_sql) {
                     if ($first) {
                         $sql .= ' ' . $handler_sql . ' ';
                         $first = false;
                     } else {
                         $sql .= ' 
                         UNION 
                         ' . $handler_sql . ' ';
                     }
                 }
                 $handler_sql = false;
             }
         }
         $sql .= ' ORDER BY score DESC';
         if ($Paging->enabled()) {
             $sql .= ' ' . $Paging->limit_sql();
         }
         $rows = $this->db->get_rows($sql);
         if (PerchUtil::count($rows) == 0) {
             if ($search_content) {
                 $sql = $Paging->select_sql();
             } else {
                 $sql = $Paging->select_sql() . ' \'PerchContent_SearchHandler\' AS source, \'\' AS score, \'\' AS col1, \'\' AS col2, \'\' AS col3, \'\' AS col4, \'\' AS col5, \'\' AS col6, \'\' AS col7, \'\' AS col8 FROM ' . $this->table . ' WHERE 1=0 UNION ';
             }
             if (PerchUtil::count($search_handlers)) {
                 $first = true;
                 foreach ($search_handlers as $handler) {
                     $handler_sql = call_user_func(array($handler, 'get_backup_search_sql'), $key, $opts);
                     if ($handler_sql) {
                         if ($first) {
                             $sql .= ' ' . $handler_sql . ' ';
                             $first = false;
                         } else {
                             $sql .= ' 
                             UNION 
                             ' . $handler_sql . ' ';
                         }
                     }
                     $handler_sql = false;
                 }
             }
             $sql .= ' ORDER BY score ASC ';
             if ($Paging->enabled()) {
                 $sql .= ' ' . $Paging->limit_sql();
             }
             $rows = $this->db->get_rows($sql);
         }
         if ($Paging->enabled()) {
             $Paging->set_total($this->db->get_count($Paging->total_count_sql()));
         }
         if (PerchUtil::count($rows)) {
             foreach ($rows as $row) {
                 $className = $row['source'];
                 if (method_exists($className, $format_method)) {
                     $r = call_user_func(array($className, $format_method), $key, $opts, $row);
                 } else {
                     $r = false;
                 }
                 if ($r) {
                     $r['source'] = str_replace('_SearchHandler', '', $row['source']);
                     // duplicate vals
                     foreach ($r as $k => $val) {
                         $r['result_' . $k] = $val;
                         if ($opts['no-conflict']) {
                             //unset($r[$k]);
                         }
                     }
                     $r['search_key'] = $key;
                     if (!$opts['no-conflict']) {
                         $r['key'] = $key;
                     }
                     $out[] = $r;
                 }
             }
         }
     }
     if (isset($opts['skip-template']) && $opts['skip-template']) {
         return $out;
     }
     $Template = new PerchTemplate('search/' . $opts['template'], 'search');
     $Template->enable_encoding();
     if (PerchUtil::count($out)) {
         foreach ($out as &$row) {
             // compat
             if (!$opts['no-conflict']) {
                 $row['url'] = $row['result_url'];
                 if (isset($row['result_result_url'])) {
                     $row['result_url'] = $row['result_result_url'];
                 }
             }
             // hide default doc
             if ($opts['hide-default-doc']) {
                 $row['result_url'] = preg_replace('/' . preg_quote(PERCH_DEFAULT_DOC) . '$/', '', $row['result_url']);
             }
             if ($opts['hide-extensions'] && strpos($row['result_url'], '.')) {
                 $parts = explode('.', $row['result_url']);
                 $ext = array_pop($parts);
                 $query = '';
                 if (strpos($ext, '?') !== false) {
                     $qparts = explode('?', $ext);
                     array_shift($qparts);
                     if (PerchUtil::count($qparts)) {
                         $query = '?' . implode('?', $qparts);
                     }
                 }
                 $row['result_url'] = implode('.', $parts) . $query;
             }
             // trailing slash
             if ($opts['add-trailing-slash']) {
                 $row['result_url'] = rtrim($row['result_url'], '/') . '/';
             }
         }
         if (isset($Paging) && $Paging->enabled()) {
             $paging_array = $Paging->to_array($opts);
             // merge in paging vars
             foreach ($out as &$item) {
                 foreach ($paging_array as $key => $val) {
                     $item[$key] = $val;
                 }
             }
         }
         return $Template->render_group($out, 1);
     } else {
         $Template->use_noresults();
         return $Template->render(array('search_key' => $key, 'key' => $key));
     }
 }