function get_packages()
{
    $files = array();
    $handler = opendir(ASSET_FOLDER);
    while ($file = readdir($handler)) {
        if ($file != "." && $file != ".." && pathinfo($file, PATHINFO_EXTENSION) == 'json') {
            $files[] = ASSET_FOLDER . '/' . $file;
        }
    }
    closedir($handler);
    ksort($files);
    $packages = array();
    foreach ($files as $file) {
        $data = file_get_contents($file);
        $data = json_decode($data);
        if (is_a($data, 'StdClass')) {
            $package = object2array($data);
            $packages = array_merge_unique($packages, $package);
        }
    }
    return $packages;
}
Exemplo n.º 2
0
function action_update($i, $action)
{
    global $db, $where_types, $where_connect_types;
    // Cache the tables for table checks
    $tables = cache_tables();
    // Check for the 'table' paramater
    if (!array_key_exists('table', $action)) {
        return param_error("update action", "table", "action {$i}");
    }
    // To make life easier
    $table = $action['table'];
    if (!in_array($table, $tables)) {
        return herror("update action", "table '{$table}' for " . "action {$i} does not exist");
    }
    $status = cache_table_status($table);
    // Check the table supports transactions
    if ($status['Engine'] != 'InnoDB') {
        return herror("update action", "table '{$table}' for " . "action {$i} does not support transactions");
    }
    // Check for the 'rows' paramater
    if (!array_key_exists('rows', $action)) {
        return param_error("update action", "rows", "action {$i}");
    }
    // Check for the 'rows' paramater
    if (!count($action['rows'])) {
        return herror("update action", "no rows to update for action {$i}");
    }
    $column_cache[$table] = cache_columns($table);
    $relation_aliases = array();
    if (array_key_exists('relations', $action)) {
        if (!count($action['relations'])) {
            return herror("update action", "paramater 'relations' for action {$i} " . "exists but contains no relations");
        }
        foreach ($action['relations'] as $foreign_table => $relation) {
            if (!in_array($foreign_table, $tables)) {
                return herror("update action", "table '{$foreign_table}' for " . "relation '{$foreign_table}' in action {$i} does not exist");
            }
            if (!array_key_exists('id', $relation)) {
                return param_error("update action", "id", "relation " . "'{$foreign_table}' in action {$i}");
            }
            if (!in_array($relation['id'], $column_cache[$table])) {
                return herror("update action", "column '{$relation['id']}' " . "on table '{$table}' for relation '{$foreign_table}' in " . "action {$i} does not exist");
            }
            if (!array_key_exists('foreign_id', $relation)) {
                return param_error("update action", "foreign_id", "relation " . "'{$foreign_table}' in action {$i}");
            }
            $column_cache[$foreign_table] = cache_columns($foreign_table);
            if (!in_array($relation['foreign_id'], $column_cache[$foreign_table])) {
                return herror("update action", "column '{$relation['foreign_id']}' " . "on table '{$foreign_table}' for relation '{$foreign_table}' in " . "action {$i} does not exist");
            }
            foreach ($relation['columns'] as $relation_column => $alias) {
                if (in_array($alias, $column_cache[$table]) || array_key_exists($alias, $relation_aliases)) {
                    return herror("update action", "alias '{$alias}' of column" . "'{$relation_column}' for relation '{$foreign_table}' in " . "action {$i} conflicts with an existing column or alias");
                }
                $relation_aliases[] = $alias;
            }
        }
    }
    $columns = array();
    foreach ($action['rows'] as $row) {
        $columns = array_merge_unique($columns, array_keys($row));
    }
    // Strip all columns that are for aliases
    $columns = array_values(array_diff($columns, $relation_aliases));
    // Check all columns
    foreach ($columns as $column) {
        if (!in_array($column, $column_cache[$table])) {
            return herror("update action", "column '{$column}' for  " . "action {$i} does not exist");
        }
    }
    $rows = $action['rows'];
    $row_count = count($rows);
    // Check all the columns are there
    for ($j = 0; $j < $row_count; $j++) {
        $row_columns = array_keys($rows[$j]);
        $missing = array_values(array_diff($relation_aliases, $row_columns));
        if (count($missing)) {
            return herror("update action", "column '{$missing[0]}' for row {$j} in " . "action {$i} does not exist");
        }
        $missing = array_values(array_diff($columns, $row_columns));
        if (count($missing)) {
            return herror("update action", "column '{$missing[0]}' for row {$j} in " . "action {$i} does not exist");
        }
    }
    if (!auth_validate_request($_POST['email'], $action)) {
        return herror("update action", "authentication error for action {$i}");
    }
    // Only commit the changes if there are no errors
    if (!$db->beginTransaction()) {
        herror_sql("update action", "failed to start transaction for action {$i}");
    }
    if (array_key_exists('relations', $action)) {
        foreach ($action['relations'] as $foreign_table => $relation) {
            $where_columns = array();
            // Prepare the bind names
            foreach ($relation['columns'] as $relation_column => $alias) {
                $where_columns[] = "{$relation_column} = :{$alias}";
            }
            $where_columns_expr = implode(' AND ', $where_columns);
            $sql = "SELECT {$relation['foreign_id']} FROM {$foreign_table} WHERE " . "{$where_columns_expr} LIMIT 1";
            $stmt = $db->prepare($sql);
            if (!$stmt) {
                return herror_stmt($stmt, "update action", "MySQL error for " . "relation '{$foreign_table}' in action {$i}");
            }
            for ($j = 0; $j < $row_count; $j++) {
                $where_values = array();
                foreach ($relation['columns'] as $relation_column => $alias) {
                    $where_values[$alias] = $rows[$j][$alias];
                }
                if (!$stmt->execute($where_values)) {
                    return herror_stmt($stmt, "update action", "MySQL error for " . "relation '{$foreign_table}' in action {$i}");
                }
                if ($id = $stmt->fetchColumn()) {
                    $stmt->closeCursor();
                    $rows[$j][$relation['id']] = $id;
                } else {
                    $stmt->closeCursor();
                    // If we are allowed to insert the relations, do so
                    if (array_key_exists('insert', $relation) && mb_strtolower($relation['insert']) == 'true') {
                        $where_columns_set = implode(', ', $where_columns);
                        $sql = "INSERT INTO {$foreign_table} SET {$where_columns_set}";
                        $stmt = $db->prepare($sql);
                        if (!$stmt) {
                            return herror_stmt($stmt, "update action", "MySQL " . "error for relation '{$foreign_table}' in action {$i}");
                        }
                        if (!$stmt->execute($where_values)) {
                            return herror_stmt($stmt, "update action", "MySQL " . "error for relation '{$foreign_table}' in action {$i}");
                        }
                        $rows[$j][$relation['id']] = $db->lastInsertId();
                    } else {
                        return herror("update action", "unable to get id for relation" . "'{$foreign_table}' in action {$i}");
                    }
                }
                // Remove all the relation rows
                foreach ($relation['columns'] as $relation_column => $alias) {
                    unset($rows[$j][$alias]);
                }
            }
        }
    }
    $columns = array_keys($rows[0]);
    // WHERE, ORDER BY, LIMIT
    $where = array();
    $where_values = array();
    $where_type = ' AND ';
    if (array_key_exists('where_type', $action)) {
        if (!array_key_exists($action['where_type'], $where_connect_types)) {
            return herror("update action", "invalid where type " . "'{$action['where_type']}' for action {$i}");
        }
        $where_type = $where_connect_types[$action['where_type']];
    }
    if (array_key_exists('where', $action)) {
        if (!is_array($action['where'])) {
            return herror("update action", "paramater 'where' for action {$i} " . "exists but is not an object");
        }
        if (!count($action['where'])) {
            return herror("update action", "paramater 'where' for action {$i} " . "exists but contains no where expressions");
        }
        $j = 0;
        foreach ($action['where'] as $expr) {
            if (!array_key_exists('type', $expr)) {
                return param_error("update action", "type", "where " . "expression {$j} in action {$i}");
            }
            if (!array_key_exists($expr['type'], $where_types)) {
                return herror("update action", "where expression type {$expr['type']}" . " does not exists for where expression {$j} in action {$i}");
            }
            if (!array_key_exists('column', $expr)) {
                return param_error("update action", "column", "where " . "expression {$j} in action {$i}");
            }
            $column = $expr['column'];
            // Check all tables, including relation tables
            if (!check_column($column, $table, $column_cache)) {
                return herror("update action", "invalid column " . "'{$column}' for where expression {$j} in action {$i}");
            }
            if (!array_key_exists('value', $expr)) {
                return param_error("update action", "value", "where " . "expression {$j} in action {$i}");
            }
            $where[] = "{$column} {$where_types[$expr['type']]} :where_{$column}";
            $where_values[":where_{$column}"] = $expr['value'];
            $j++;
        }
        $where = " WHERE " . implode($where_type, $where);
    } else {
        $where = '';
    }
    $order_by = array();
    if (array_key_exists('order_by', $action)) {
        if (!count($action['order_by'])) {
            return herror("update action", "paramater 'order_by' for action {$i} " . "exists but contains no order expressions");
        }
        $j = 0;
        foreach ($action['order_by'] as $order) {
            if (!array_key_exists('column', $order)) {
                return param_error("update action", "column", "order " . "expression {$j} in action {$i}");
            }
            $column = $order['column'];
            // Check all tables, including relation tables
            if (!check_column($column, $table, $column_cache)) {
                return herror("update action", "invalid column " . "'{$column}' for order expression {$j} in action {$i}");
            }
            $direction = 'ASC';
            if (array_key_exists('direction', $order)) {
                $direction = mb_strtoupper($order['direction']);
                if ($direction != 'ASC' && $direction != 'DESC') {
                    return herror("update action", "invalid direction " . "'{$direction}' for order expression {$j} in action {$i}");
                }
            }
            $order_by[] = "{$column} {$direction}";
            $j++;
        }
        $order_by = " ORDER BY " . implode(', ', $order_by);
    } else {
        $order_by = '';
    }
    $limit = '';
    if (array_key_exists('limit', $action)) {
        if (!is_int($action['limit']) || $action['limit'] < 0) {
            return herror("update action", "limit for in action {$i} must be a " . "positive integer");
        }
        $limit = " LIMIT {$action['limit']}";
    }
    $value_holders = array();
    foreach ($columns as $alias) {
        $value_holders[] = "{$alias} = :{$alias}";
    }
    $value_holders = implode(', ', $value_holders);
    $sql = "UPDATE {$table} SET {$value_holders}{$where}{$order_by}{$limit}";
    $stmt = $db->prepare($sql);
    if (!$stmt) {
        return herror_sql("insert action", "MySQL error for row action {$i}");
    }
    for ($j = 0; $j < $row_count; $j++) {
        if (!$stmt->execute(array_merge($rows[$j], $where_values))) {
            $err = $stmt->errorInfo();
            return herror_stmt($stmt, "insert action", "MySQL error for row " . "{$j} in action {$i}: " . $err[2]);
        }
        $stmt->closeCursor();
    }
    $db->commit();
}
Exemplo n.º 3
0
function GetFlistIds($flistString, $allowPages = false, $allowMultipleMatches = true, $failOnEmpty = true)
{
    global $fbcmdPrefs;
    global $flistMatchArray;
    global $flistMatchIdString;
    $unknownNames = array();
    $flistMatchArray = array();
    $flistFQL = array('FriendId', 'FriendBaseInfo');
    $flistItems = explode(',', $flistString);
    // Pre-process to see if Friend Lists or Pages or Groups are required
    foreach ($flistItems as $item) {
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_friendlist']) {
            array_push_unique($flistFQL, 'FriendListNames');
            array_push_unique($flistFQL, 'FriendListMembers');
        }
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_page'] || strtoupper($item) == '=PAGES') {
            array_push_unique($flistFQL, 'PageId');
            array_push_unique($flistFQL, 'PageNames');
        }
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_group']) {
            array_push_unique($flistFQL, 'GroupNames');
        }
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_tag']) {
            array_push_unique($flistFQL, 'PageId');
            array_push_unique($flistFQL, 'PageNames');
            array_push_unique($flistFQL, 'GroupNames');
        }
    }
    MultiFQL($flistFQL);
    global $dataFriendId;
    global $dataFriendBaseInfo;
    global $indexFriendBaseInfo;
    global $fbUser;
    global $flistChunkCounter;
    $flistChunkCounter = 0;
    foreach ($flistItems as $item) {
        $itemUC = strtoupper($item);
        // =KEYWORDS /////////////////////////////////////////////////////////////
        if (substr($item, 0, 1) == '=') {
            if ($itemUC == '=ME') {
                array_push_unique($flistMatchArray, $fbUser);
                continue;
            }
            if ($itemUC == '=ALL') {
                foreach ($dataFriendId as $fid) {
                    array_push_unique($flistMatchArray, $fid['uid2']);
                }
                continue;
            }
            if (substr($itemUC, 0, 5) == '=BDAY') {
                $matchTime = time();
                if (preg_match("/=BDAY\\+(\\d+)?\$/", $itemUC, $matches)) {
                    if (isset($matches[1])) {
                        $matchTime += 24 * 60 * 60 * $matches[1];
                    } else {
                        $matchTime += 24 * 60 * 60;
                    }
                }
                if (preg_match("/=BDAY-(\\d+)?\$/", $itemUC, $matches)) {
                    if (isset($matches[1])) {
                        $matchTime -= 24 * 60 * 60 * $matches[1];
                    } else {
                        $matchTime -= 24 * 60 * 60;
                    }
                }
                if (preg_match("/=BDAY=(.+)\$/", $itemUC, $matches)) {
                    $matchTime = strtotime($matches[1]);
                    if (!$matchTime) {
                        FbcmdWarning("Bad BDAY Syntax: [{$item}] using today");
                        $matchTime = time();
                    }
                }
                $matchDate = date('m/d', $matchTime);
                foreach ($dataFriendBaseInfo as $fbi) {
                    if (substr($fbi['birthday_date'], 0, 5) == $matchDate) {
                        array_push_unique($flistMatchArray, $fbi['uid']);
                    }
                }
                continue;
            }
            if ($itemUC == '=ONLINE') {
                foreach ($dataFriendBaseInfo as $fbi) {
                    if ($fbi['online_presence'] == 'active' || $fbi['online_presence'] == 'idle' && $fbcmdPrefs['online_idle']) {
                        array_push_unique($flistMatchArray, $fbi['uid']);
                    }
                }
                continue;
            }
            if ($itemUC == '=PAGES') {
                if (!$allowPages) {
                    global $fbcmdCommand;
                    FbcmdWarning("{$fbcmdCommand} does not support pages: {$item} ignored");
                } else {
                    global $dataPageId;
                    foreach ($dataPageId as $page_id) {
                        array_push_unique($flistMatchArray, $page_id['page_id']);
                    }
                }
                continue;
            }
            FbcmdWarning("Unknown flist entry: {$item}");
            continue;
        }
        // _FRIEND LIST //////////////////////////////////////////////////////////
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_friendlist']) {
            global $dataFriendListNames;
            global $dataFriendListMembers;
            $flidMatches = FlistMatch($item, true, $dataFriendListNames, 'flid', 'name', $allowMultipleMatches);
            if (count($flidMatches)) {
                foreach ($dataFriendListMembers as $flm) {
                    // http://bugs.developers.facebook.com/show_bug.cgi?id=5977
                    // if (in_array($flm[0],$flidMatches)) {
                    // array_push_unique($flistMatchArray,$flm[1]);
                    // }
                    if (in_array($flm['flid'], $flidMatches)) {
                        array_push_unique($flistMatchArray, $flm['uid']);
                    }
                }
            }
            continue;
        }
        // !USERNAME /////////////////////////////////////////////////////////////
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_username']) {
            $uidMatches = FlistMatch($item, true, $dataFriendBaseInfo, 'uid', 'username', $allowMultipleMatches);
            array_merge_unique($flistMatchArray, $uidMatches);
            continue;
        }
        // +PAGES ////////////////////////////////////////////////////////////////
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_page']) {
            if (!$allowPages) {
                global $fbcmdCommand;
                FbcmdWarning("{$fbcmdCommand} does not support pages: {$item} ignored");
            } else {
                global $dataPageNames;
                $pidMatches = FlistMatch($item, true, $dataPageNames, 'page_id', 'name', $allowMultipleMatches);
                array_merge_unique($flistMatchArray, $pidMatches);
            }
            continue;
        }
        // ~GROUPS ///////////////////////////////////////////////////////////////
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_group']) {
            global $dataGroupNames;
            global $fbObject;
            $gidMatches = FlistMatch($item, true, $dataGroupNames, 'gid', 'name', false);
            if (isset($gidMatches[0])) {
                $fql = "SELECT uid FROM group_member WHERE gid={$gidMatches[0]}";
                try {
                    $fbReturn = $fbObject->api_client->fql_query($fql);
                    TraceReturn($fbReturn);
                } catch (Exception $e) {
                    FbcmdException($e);
                }
                if (!empty($fbReturn)) {
                    foreach ($fbReturn as $u) {
                        $flistMatchArray[] = $u['uid'];
                    }
                } else {
                    FbcmdWarning("Could Not get Group Members for GROUP {$gidMatches[0]}");
                }
            }
            continue;
        }
        // @TAG FORMAT ///////////////////////////////////////////////////////////
        if (substr($item, 0, 1) == $fbcmdPrefs['prefix_tag']) {
            $tagList = MatchTag(substr($item, 1), $allowPages, false);
            if ($tagList) {
                array_merge_unique($flistMatchArray, array($tagList[0][0]));
            }
            continue;
        }
        // REGULAR NAMES /////////////////////////////////////////////////////////
        $uidMatches = FlistMatch($item, false, $dataFriendBaseInfo, 'uid', 'name', $allowMultipleMatches);
        array_merge_unique($flistMatchArray, $uidMatches);
    }
    if (count($flistMatchArray) == 0) {
        if ($failOnEmpty) {
            if (substr(strtoupper($flistString), 0, 5) == '=BDAY') {
                print "No Friends With Birthday Matches\n";
                exit;
            } else {
                FbcmdFatalError("Empty flist: {$flistString}");
            }
        } else {
            $flistMatchIdString = '';
        }
    } else {
        $flistMatchIdString = implode(',', $flistMatchArray);
    }
    foreach ($flistMatchArray as $id) {
        if (ProfileName($id) == 'unknown') {
            $unknownNames[] = $id;
        }
    }
    if (count($unknownNames) > 0) {
        global $fqlFlistNames;
        global $keyFlistNames;
        $fqlFlistNames = 'SELECT id,name FROM profile WHERE id IN (' . implode(',', $unknownNames) . ')';
        $keyFlistNames = 'id';
        MultiFQL(array('FlistNames'));
    }
    return;
}
Exemplo n.º 4
0
/**
 * put your comment there...
 *
 * @param mixed $system
 * @param mixed $params
 *
 *       FOR RULES
 *       rules - rules queries - to search related records on server side
 *       getrelrecs (=1) - search relationship records (along with related) on server side
 *       topids - list of records ids, it is used to compose 'parentquery' parameter to use in rules (@todo - replace with new rules algorithm)
 *
 *       INTERNAL/recursive
 *       parentquery - sql expression to substiture in rule query
 *       sql - sql expression to execute (used as recursive parameters to search relationship records)
 *
 *       SEARCH parameters that are used to compose sql expression
 *       q - query string (old mode) or json array (new mode)
 *       w (=all|bookmark a|b) - search among all or bookmarked records
 *       limit  - limit for sql query is set explicitely on client side
 *       offset - offset parameter value for sql query
 *       s - sort order
 *
 *       OUTPUT parameters
 *       vo (=h3) - output format in h3 for backward capability (for detail=ids only)
 *       needall (=1) - by default it returns only first 3000, to return all set it to 1,
 *                      it is set to 1 for server-side rules searches
 *       publiconly (=1) - ignore current user and returns only public records
 *
 *       detail (former 'f') - ids       - only record ids
 *                             header    - record header
 *                             timemap   - record header + timemap details
 *                             detail    - record header + all details
 *                             structure - record header + all details + record type structure (for editing) - NOT USED
 *
 *       CLIENT SIDE
 *       id - unque id to sync with client side
 *       source - id of html element that is originator of this search
 *       qname - original name of saved search (for messaging)
 */
function recordSearch($system, $params)
{
    //for error message
    $savedSearchName = @$params['qname'] ? "Saved search: " . $params['qname'] . "<br>" : "";
    if (!@$params['detail']) {
        $params['detail'] = @$params['f'];
        //backward capability
    }
    $istimemap_request = @$params['detail'] == 'timemap';
    $istimemap_counter = 0;
    //total records with timemap data
    $fieldtypes_ids = null;
    if ($istimemap_request) {
        //get date,year and geo fields from structure
        $fieldtypes_ids = dbs_GetDetailTypes($system, array('date', 'year', 'geo'), 3);
        if ($fieldtypes_ids == null || count($fieldtypes_ids) == 0) {
            $fieldtypes_ids = array(DT_GEO_OBJECT, DT_DATE, DT_START_DATE, DT_END_DATE);
            //9,10,11,28';
        }
        $fieldtypes_ids = implode(',', $fieldtypes_ids);
    } else {
        if (!in_array(@$params['detail'], array('header', 'timemap', 'detail', 'structure'))) {
            //specific set of detail fields
            if (is_array($params['detail'])) {
                $fieldtypes_ids = $params['detail'];
            } else {
                $fieldtypes_ids = explode(',', $params['detail']);
            }
            if (is_array($fieldtypes_ids) && (count($fieldtypes_ids) > 1 || is_numeric($fieldtypes_ids[0]))) {
                $fieldtypes_ids = implode(',', $fieldtypes_ids);
                $params['detail'] = 'detail';
            } else {
                $fieldtypes_ids = null;
                $params['detail'] = 'ids';
            }
        }
    }
    $is_ids_only = 'ids' == $params['detail'];
    $return_h3_format = @$params['vo'] == 'h3' && $is_ids_only;
    if (null == $system) {
        $system = new System();
        if (!$system->init(@$_REQUEST['db'])) {
            $response = $system->getError();
            if ($return_h3_format) {
                $response['error'] = $response['message'];
            }
            return $response;
        }
    }
    $mysqli = $system->get_mysqli();
    $currentUser = $system->getCurrentUser();
    if ($system->get_user_id() < 1) {
        $params['w'] = 'all';
        //does not allow to search bookmarks if not logged in
    }
    if ($is_ids_only) {
        $select_clause = 'select SQL_CALC_FOUND_ROWS DISTINCT rec_ID ';
    } else {
        $select_clause = 'select SQL_CALC_FOUND_ROWS DISTINCT ' . 'bkm_ID,' . 'bkm_UGrpID,' . 'rec_ID,' . 'rec_URL,' . 'rec_RecTypeID,' . 'rec_Title,' . 'rec_OwnerUGrpID,' . 'rec_NonOwnerVisibility,' . 'bkm_PwdReminder ';
        /*.'rec_URLLastVerified,'
          .'rec_URLErrorMessage,'
          .'bkm_PwdReminder ';*/
    }
    if ($currentUser && @$currentUser['ugr_ID'] > 0) {
        $currUserID = $currentUser['ugr_ID'];
    } else {
        $currUserID = 0;
        $params['w'] = 'all';
    }
    if (@$params['topids']) {
        //if topids are defined we use them as starting point for following rule query
        // it is used for incremental client side only
        //@todo - implement it in different way - substitute topids to query json as predicate ids:
        $query_top = array();
        if (strcasecmp(@$params['w'], 'B') == 0 || strcasecmp(@$params['w'], 'bookmark') == 0) {
            $query_top['from'] = 'FROM usrBookmarks TOPBKMK LEFT JOIN Records TOPBIBLIO ON bkm_recID=rec_ID ';
        } else {
            $query_top['from'] = 'FROM Records TOPBIBLIO LEFT JOIN usrBookmarks TOPBKMK ON bkm_recID=rec_ID and bkm_UGrpID=' . $currUserID . ' ';
        }
        $query_top['where'] = "(TOPBIBLIO.rec_ID in (" . $params['topids'] . "))";
        $query_top['sort'] = '';
        $query_top['limit'] = '';
        $query_top['offset'] = '';
        $params['parentquery'] = $query_top;
        //parentquery parameter is used in  get_sql_query_clauses
    } else {
        if (@$params['rules']) {
            //special case - server side operation
            // rules - JSON array the same as stored in saved searches table
            if (is_array(@$params['rules'])) {
                $rules_tree = $params['rules'];
            } else {
                $rules_tree = json_decode($params['rules'], true);
            }
            $flat_rules = array();
            $flat_rules[0] = array();
            //create flat rule array
            $rules = _createFlatRule($flat_rules, $rules_tree, 0);
            //find result for main query
            unset($params['rules']);
            if (@$params['limit']) {
                unset($params['limit']);
            }
            if (@$params['offset']) {
                unset($params['offset']);
            }
            if (@$params['vo']) {
                unset($params['vo']);
            }
            $params['needall'] = 1;
            //return all records
            $resSearch = recordSearch($system, $params);
            $keepMainSet = true;
            if ($keepMainSet) {
                //find main query results
                $fin_result = $resSearch;
                //main result set
                $flat_rules[0]['results'] = $is_ids_only ? $fin_result['data']['records'] : array_keys($fin_result['data']['records']);
                //get ids
            } else {
                //empty main result set
                //remove from $fin_result! but keep in $flat_rules[0]['results']?
            }
            $is_get_relation_records = @$params['getrelrecs'] == 1;
            //get all related and relationship records
            foreach ($flat_rules as $idx => $rule) {
                if ($idx == 0) {
                    continue;
                }
                $is_last = @$rule['islast'] == 1;
                //create request
                $params['q'] = $rule['query'];
                $parent_ids = $flat_rules[$rule['parent']]['results'];
                //list of record ids of parent resultset
                $rule['results'] = array();
                //reset
                //split by 3000 - search based on parent ids (max 3000)
                $k = 0;
                while ($k < count($parent_ids)) {
                    //$need_details2 = $need_details && ($is_get_relation_records || $is_last);
                    $params3 = $params;
                    $params3['topids'] = implode(",", array_slice($parent_ids, $k, 3000));
                    if (!$is_last) {
                        //($is_get_relation_records ||
                        //$params3['detail'] = 'ids';  //no need in details for preliminary results  ???????
                    }
                    $response = recordSearch($system, $params3);
                    if ($response['status'] == HEURIST_OK) {
                        //merge with final results
                        if ($is_ids_only) {
                            $fin_result['data']['records'] = array_merge_unique($fin_result['data']['records'], $response['data']['records']);
                        } else {
                            $fin_result['data']['records'] = mergeRecordSets($fin_result['data']['records'], $response['data']['records']);
                            $fin_result['data']['order'] = array_merge($fin_result['data']['order'], array_keys($response['data']['records']));
                            foreach (array_keys($response['data']['records']) as $rt) {
                                $rectype_id = @$rt['4'];
                                if ($rectype_id) {
                                    /*if(@$fin_result['data']['rectypes'][$rectype_id]){
                                          $fin_result['data']['rectypes'][$rectype_id]++;
                                      }else{
                                          $fin_result['data']['rectypes'][$rectype_id]=1;
                                      }*/
                                    if (!array_key_exists($rectype_id, $fin_result['data']['rectypes'])) {
                                        $fin_result['data']['rectypes'][$rectype_id] = 1;
                                    }
                                }
                            }
                        }
                        if (!$is_last) {
                            //add top ids for next level
                            $flat_rules[$idx]['results'] = array_merge_unique($flat_rules[$idx]['results'], $is_ids_only ? $response['data']['records'] : array_keys($response['data']['records']));
                        }
                        if ($is_get_relation_records && (strpos($params3['q'], "related_to") > 0 || strpos($params3['q'], "relatedfrom") > 0)) {
                            //find relation records (recType=1)
                            //create query to search related records
                            if (strcasecmp(@$params3['w'], 'B') == 0 || strcasecmp(@$params3['w'], 'bookmark') == 0) {
                                $from = 'FROM usrBookmarks TOPBKMK LEFT JOIN Records TOPBIBLIO ON bkm_recID=rec_ID ';
                            } else {
                                $from = 'FROM Records TOPBIBLIO LEFT JOIN usrBookmarks TOPBKMK ON bkm_recID=rec_ID and bkm_UGrpID=' . $currUserID . ' ';
                            }
                            if (strpos($params3['q'], "related_to") > 0) {
                                $fld2 = "rl_SourceID";
                                $fld1 = "rl_TargetID";
                            } else {
                                $fld1 = "rl_SourceID";
                                $fld2 = "rl_TargetID";
                            }
                            $where = "WHERE (TOPBIBLIO.rec_ID in (select rl_RelationID from recLinks where (rl_RelationID is not null) and {$fld1} in (" . $params3['topids'] . ") and {$fld2} in (" . implode(",", $is_ids_only ? $response['data']['records'] : array_keys($response['data']['records'])) . ")))";
                            $params2 = $params3;
                            unset($params2['topids']);
                            unset($params2['q']);
                            $params2['sql'] = $select_clause . $from . $where;
                            $response = recordSearch($system, $params2);
                            //search for relationship records
                            if ($response['status'] == HEURIST_OK) {
                                if (!@$fin_result['data']['relationship']) {
                                    $fin_result['data']['relationship'] = array();
                                }
                                if ($is_ids_only) {
                                    $fin_result['data']['relationship'] = array_merge_unique($fin_result['data']['relationship'], $response['data']['records']);
                                } else {
                                    $fin_result['data']['relationship'] = mergeRecordSets($fin_result['data']['relationship'], $response['data']['records']);
                                }
                                /*merge with final results
                                  if($is_ids_only){
                                      $fin_result['data']['records'] = array_merge($fin_result['data']['records'], $response['data']['records']);
                                  }else{
                                      $fin_result['data']['records'] = mergeRecordSets($fin_result['data']['records'], $response['data']['records']);
                                      $fin_result['data']['order'] = array_merge($fin_result['data']['order'], array_keys($response['data']['records']));
                                      $fin_result['data']['rectypes'][1] = 1;
                                  }
                                  */
                            }
                        }
                        //$is_get_relation_records
                    } else {
                        //@todo terminate execution and return error
                    }
                    $k = $k + 3000;
                }
                //while chunks
            }
            //for rules
            if ($is_ids_only) {
                //$fin_result['data']['records'] = array_unique($fin_result['data']['records']);
            }
            $fin_result['data']['count'] = count($fin_result['data']['records']);
            if ($return_h3_format) {
                $fin_result = array("resultCount" => $fin_result['data']['count'], "recordCount" => $fin_result['data']['count'], "recIDs" => implode(",", $fin_result['data']['records']));
            }
            //@todo - assign if size less than 3000? only
            $fin_result['data']['mainset'] = $flat_rules[0]['results'];
            return $fin_result;
        }
    }
    //END RULES
    $chunk_size = PHP_INT_MAX;
    if (@$params['sql']) {
        $query = $params['sql'];
    } else {
        $is_mode_json = false;
        if (@$params['q']) {
            if (is_array(@$params['q'])) {
                $query_json = $params['q'];
                //DEBUG error_log('Q='.print_r($params['q'],true));
            } else {
                $query_json = json_decode(@$params['q'], true);
            }
            if (is_array($query_json) && count($query_json) > 0) {
                $params['q'] = $query_json;
                $is_mode_json = true;
            }
        } else {
            return $system->addError(HEURIST_INVALID_REQUEST, $savedSearchName . "Invalid search request. Missed query parameter 'q'");
        }
        if ($is_mode_json) {
            $aquery = get_sql_query_clauses_NEW($mysqli, $params, $currentUser);
        } else {
            $aquery = get_sql_query_clauses($mysqli, $params, $currentUser);
            //!!!! IMPORTANT CALL OR compose_sql_query at once
        }
        if ($is_ids_only && @$params['needall']) {
            $chunk_size = PHP_INT_MAX;
            $aquery["limit"] = '';
        } else {
            $chunk_size = $system->user_GetPreference('search_detail_limit');
            //limit for map/timemap output
        }
        if (!isset($aquery["where"]) || trim($aquery["where"]) === '') {
            return $system->addError(HEURIST_DB_ERROR, "Invalid search request; unable to construct valid SQL query", null);
        }
        $query = $select_clause . $aquery["from"] . " WHERE " . $aquery["where"] . $aquery["sort"] . $aquery["limit"] . $aquery["offset"];
        //error_log($is_mode_json.' '.$query);
        /* DEBUG
                    if($params['q']=='doerror'){ //force error
                        $query ='abracadabra'; 
                    }
        */
    }
    $res = $mysqli->query($query);
    if (!$res) {
        $response = $system->addError(HEURIST_DB_ERROR, $savedSearchName . 'Search query error', $mysqli->error);
    } else {
        $fres = $mysqli->query('select found_rows()');
        if (!$fres) {
            $response = $system->addError(HEURIST_DB_ERROR, $savedSearchName . 'Search query error (retrieving number of records)', $mysqli->error);
        } else {
            $total_count_rows = $fres->fetch_row();
            $total_count_rows = $total_count_rows[0];
            $fres->close();
            if ($is_ids_only) {
                //------------------------  LOAD and RETURN only IDS
                $records = array();
                while ($row = $res->fetch_row()) {
                    array_push($records, (int) $row[0]);
                }
                $res->close();
                if (@$params['vo'] == 'h3') {
                    //output version
                    $response = array('resultCount' => $total_count_rows, 'recordCount' => count($records), 'recIDs' => implode(',', $records));
                } else {
                    $response = array('status' => HEURIST_OK, 'data' => array('queryid' => @$params['id'], 'count' => $total_count_rows, 'offset' => get_offset($params), 'reccount' => count($records), 'records' => $records));
                }
            } else {
                //----------------------------------
                // read all field names
                $_flds = $res->fetch_fields();
                $fields = array();
                foreach ($_flds as $fld) {
                    array_push($fields, $fld->name);
                }
                array_push($fields, 'rec_ThumbnailURL');
                //array_push($fields, 'rec_Icon'); //last one -icon ID
                $rectype_structures = array();
                $rectypes = array();
                $records = array();
                $order = array();
                // load all records
                while ($row = $res->fetch_row()) {
                    //3000 maximal allowed chunk
                    array_push($row, $fieldtypes_ids ? '' : fileGetThumbnailURL($system, $row[2]));
                    //array_push( $row, $row[4] ); //by default icon if record type ID
                    $records[$row[2]] = $row;
                    array_push($order, $row[2]);
                    if (!@$rectypes[$row[4]]) {
                        $rectypes[$row[4]] = 1;
                    }
                }
                $res->close();
                if (($istimemap_request || $params['detail'] == 'detail' || $params['detail'] == 'structure') && count($records) > 0) {
                    //search for specific details
                    if (!$fieldtypes_ids && $fieldtypes_ids != '') {
                        $detail_query = 'select dtl_RecID,' . 'dtl_DetailTypeID,' . 'dtl_Value,' . 'AsWKT(dtl_Geo), 0, 0, 0 ' . 'from recDetails
                                where dtl_RecID in (' . join(',', array_keys($records)) . ') ' . ' and dtl_DetailTypeID in (' . $fieldtypes_ids . ')';
                    } else {
                        $detail_query = 'select dtl_RecID,' . 'dtl_DetailTypeID,' . 'dtl_Value,' . 'AsWKT(dtl_Geo),' . 'dtl_UploadedFileID,' . 'recUploadedFiles.ulf_ObfuscatedFileID,' . 'recUploadedFiles.ulf_Parameters ' . 'from recDetails
                                  left join recUploadedFiles on ulf_ID = dtl_UploadedFileID
                                where dtl_RecID in (' . join(',', array_keys($records)) . ')';
                    }
                    // @todo - we may use getAllRecordDetails
                    $res_det = $mysqli->query($detail_query);
                    if (!$res_det) {
                        $response = $system->addError(HEURIST_DB_ERROR, $savedSearchName . 'Search query error (retrieving details)', $mysqli->error);
                        return $response;
                    } else {
                        while ($row = $res_det->fetch_row()) {
                            $recID = array_shift($row);
                            if (!array_key_exists('d', $records[$recID])) {
                                $records[$recID]['d'] = array();
                            }
                            $dtyID = $row[0];
                            $val = null;
                            if ($row[2]) {
                                $val = $row[1] . ' ' . $row[2];
                                //dtl_Geo @todo convert to JSON
                            } else {
                                if ($row[3]) {
                                    $val = array($row[4], $row[5]);
                                    //obfuscated value for fileid
                                } else {
                                    if (@$row[1]) {
                                        $val = $row[1];
                                    }
                                }
                            }
                            if ($val) {
                                if (!array_key_exists($dtyID, $records[$recID]['d'])) {
                                    $records[$recID]['d'][$dtyID] = array();
                                }
                                array_push($records[$recID]['d'][$dtyID], $val);
                            }
                        }
                        //while
                        $res_det->close();
                        ///@todo
                        // 1. optimize loop - include into main detail loop
                        // 2. exit loop if more than 5000 geo enabled
                        // 3. return geojson and timeline items
                        //additional loop for timemap request
                        //1. exclude records without timemap data
                        //2. limit to $chunk_size
                        if ($istimemap_request) {
                            $tm_records = array();
                            $order = array();
                            $rectypes = array();
                            foreach ($records as $recID => $record) {
                                if (is_array(@$record['d']) && count($record['d']) > 0) {
                                    //this record is time enabled
                                    if ($istimemap_counter < $chunk_size) {
                                        $tm_records[$recID] = $record;
                                        array_push($order, $recID);
                                        $rectypes[$record[4]] = 1;
                                    }
                                    $istimemap_counter++;
                                }
                            }
                            $records = $tm_records;
                            $total_count_rows = $istimemap_counter;
                        }
                        //$istimemap_request
                    }
                }
                //$need_details
                $rectypes = array_keys($rectypes);
                if ($params['detail'] == 'structure' && count($rectypes) > 0) {
                    //rarely used in editing.js
                    //description of recordtype and used detail types
                    $rectype_structures = dbs_GetRectypeStructures($system, $rectypes, 1);
                    //no groups
                }
                //"query"=>$query,
                $response = array('status' => HEURIST_OK, 'data' => array('queryid' => @$params['id'], 'count' => $total_count_rows, 'offset' => get_offset($params), 'reccount' => count($records), 'fields' => $fields, 'records' => $records, 'order' => $order, 'rectypes' => $rectypes, 'structures' => $rectype_structures));
                if ($fieldtypes_ids) {
                    $response['data']['fields_detail'] = explode(',', $fieldtypes_ids);
                }
            }
            //$is_ids_only
        }
    }
    return $response;
}