/**
 *
 * exporting attachment files related to item.
 *
 * @param string $export_path storing directory name
 * @param resource $fhdl file handle that items are exported to.
 * @param int $item_id id of item with attachment files to export.
 * @return array( 'path' => $export_path,
 *                   'attachments' => array( file path of attachment1, file path of attachment2, ... ) )
 *            (relative path of $export_filepath)
 *         returns false if it failed
 */
function xnpExportFile($export_path, $fhdl, $item_id)
{
    $file = xnpGetFileInfo('t_file.file_id, t_file_type.name, t_file.original_file_name, t_file.file_size, t_file.mime_type, t_file.thumbnail_file, t_file.caption', "item_id = {$item_id} and is_deleted=0", $item_id);
    if (!$fhdl) {
        return false;
    }
    // create files directory under $export_path.
    $dir = $export_path . '/files';
    if (!file_exists($dir)) {
        if (!mkdir($dir)) {
            xoonips_error("can't make directory '{$dir}'");
            return false;
        }
    }
    // for absolete path of attachment file
    $files = array();
    foreach ($file as $f) {
        $file = array();
        list($file['file_id'], $file['file_type_name'], $file['original_file_name'], $file['file_size'], $file['mime_type'], $file['thumbnail_file'], $file['caption']) = $f;
        // copy atatchment file $file['file_id'] to $dir and renamed to original file name
        // output <file> to file handle $fhdl
        $hdl = fopen(xnpGetUploadFilePath($file['file_id']), 'rb');
        if (file_exists(xnpGetUploadFilePath($file['file_id']))) {
            if (!copy(xnpGetUploadFilePath($file['file_id']), $dir . '/' . $file['file_id'])) {
                xoonips_error('can\'t write a file \'' . $dir . '/' . $file['file_id'] . "' of the item(ID={$item_id})");
                return false;
            }
            if (!fwrite($fhdl, '<file' . " item_id=\"{$item_id}\"" . " file_type_name=\"{$file['file_type_name']}\"" . " original_file_name=\"{$file['original_file_name']}\"" . " file_name=\"files/{$file['file_id']}\"" . " file_size=\"{$file['file_size']}\"" . " mime_type=\"{$file['mime_type']}\"" . ">\n" . (isset($file['thumbnail_file']) ? '<thumbnail>' . base64_encode($file['thumbnail_file']) . "</thumbnail>\n" : '') . '<caption>' . $file['caption'] . "</caption>\n" . "</file>\n")) {
                fclose($hdl);
                xoonips_error("can't export <file> of the item(ID={$item_id})");
                return false;
            }
            $files[] = "files/{$file['file_id']}";
        }
    }
    return true;
}
/**
 *
 * @param op 'quicksearch' 'advancedsearch' 'itemsubtypesearch' 'itemtypesearch'
 * @param keyword search keyword
 * @param search_itemtype how to search ('all', 'basic' or name of itemtype (ex.xnppaper) )
 * @param private_flag true if search private indexes.
 * @param msg reference to variables that receive  error message
 * @param iids reference to array that receive item id that match query condition
 * @param search_cache_id search cache id(in/out)
 * @param search_tab 'item'/'metadata'/'file' (it regards illegal value as 'item')
 * @param file_or_item_metadata  'file'=search_text table only, 'item_metadata'=other than search_text table, 'all'=all. effective only if op==quicksearch && search_itemtype!=basic
 * @return true search succeed.
 * @return false search failed. make sure $msg for detail.
 * this function needs $xoopsDB, $xoopsUser, $_SESSION.
 *
 */
function xnpSearchExec($op, $keyword, $search_itemtype, $private_flag, &$msg, &$iids, &$search_var, &$search_cache_id, $search_tab, $file_or_item_metadata = 'all')
{
    global $xoopsDB, $xoopsUser;
    $xnpsid = $_SESSION['XNPSID'];
    if (!xnp_is_valid_session_id($xnpsid)) {
        // guest access is forbidden
        return array();
    } else {
        if ($xoopsUser) {
            // identified user
            $uid = $xoopsUser->getVar('uid');
        } else {
            // guest access is permitted
            $uid = 0;
        }
    }
    $cache_table = $xoopsDB->prefix('xoonips_search_cache');
    $cache_item_table = $xoopsDB->prefix('xoonips_search_cache_item');
    $cache_file_table = $xoopsDB->prefix('xoonips_search_cache_file');
    $cache_meta_table = $xoopsDB->prefix('xoonips_search_cache_metadata');
    $meta_table = $xoopsDB->prefix('xoonips_oaipmh_metadata');
    $repo_table = $xoopsDB->prefix('xoonips_oaipmh_repositories');
    $basic_table = $xoopsDB->prefix('xoonips_item_basic');
    $title_table = $xoopsDB->prefix('xoonips_item_title');
    $keyword_table = $xoopsDB->prefix('xoonips_item_keyword');
    $file_table = $xoopsDB->prefix('xoonips_file');
    $xlink_table = $xoopsDB->prefix('xoonips_index_item_link');
    $index_table = $xoopsDB->prefix('xoonips_index');
    $glink_table = $xoopsDB->prefix('xoonips_groups_users_link');
    $search_text_table = $xoopsDB->prefix('xoonips_search_text');
    $user_table = $xoopsDB->prefix('users');
    $event_log_table = $xoopsDB->prefix('xoonips_event_log');
    // search_cache_idがあるなら、search_cacheから取得.
    if ($search_cache_id) {
        $search_cache_id = (int) $search_cache_id;
        $sql = "select unix_timestamp(timestamp) from {$cache_table} where search_cache_id={$search_cache_id} and sess_id='" . session_id() . "'";
        $result = $xoopsDB->query($sql);
        if ($xoopsDB->getRowsNum($result) == 0) {
            //todo: session timeoutのためにsearch_cacheから消されたのかもしれない(普通は起こらないのだが)。どのようなメッセージを出すべきか?
            $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
            return false;
            // bad search_cache_id
        }
        list($timestamp) = $xoopsDB->fetchRow($result);
        $event_type_ids = array(ETID_INSERT_ITEM, ETID_UPDATE_ITEM, ETID_DELETE_ITEM, ETID_DELETE_GROUP, ETID_INSERT_GROUP_MEMBER, ETID_DELETE_GROUP_MEMBER, ETID_DELETE_INDEX, ETID_CERTIFY_ITEM, ETID_REJECT_ITEM, ETID_TRANSFER_ITEM);
        $sql = "select count(*) from {$event_log_table} where event_type_id in (" . implode(',', $event_type_ids) . ") and timestamp >= {$timestamp}";
        $result = $xoopsDB->query($sql);
        if ($result == false) {
            $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
            return false;
        }
        list($count) = $xoopsDB->fetchRow($result);
        if ($count == 0) {
            if ($search_tab == 'metadata') {
                $sql = "select identifier from {$cache_meta_table} where search_cache_id={$search_cache_id}";
            } else {
                if ($search_tab == 'file') {
                    $sql = "select tf.item_id    from {$cache_file_table} as tcf\n                  left join {$file_table} as tf on tcf.file_id = tf.file_id\n                  left join {$basic_table} as tb on tb.item_id = tf.item_id\n                  left join {$search_text_table} as tst on tf.file_id=tst.file_id\n                  where search_cache_id={$search_cache_id} and tb.item_id is not null and tf.file_id is not null and tf.is_deleted=0";
                } else {
                    $sql = "select tci.item_id    from {$cache_item_table} as tci\n                  left join {$basic_table} as tb on tb.item_id = tci.item_id\n                  where search_cache_id={$search_cache_id} and tb.item_id is not null";
                }
            }
            $result = $xoopsDB->query($sql);
            while (list($iid) = $xoopsDB->fetchRow($result)) {
                $iids[] = $iid;
            }
            return true;
        }
    }
    $cachable = $op == 'quicksearch' || $op == 'advancedsearch' || $op == 'itemtypesearch' || $op == 'itemsubtypesearch';
    $search_cache_id = 0;
    if ($cachable) {
        // search_cache_idを発行する
        $sql = "insert into {$cache_table} ( sess_id ) values ( '" . session_id() . "' )";
        $result = $xoopsDB->queryF($sql);
        if ($result == false) {
            $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
            return false;
        }
        $search_cache_id = $xoopsDB->getInsertId();
    }
    $itemtypes = array();
    $itemtype_names = array();
    $tmp = array();
    if (($res = xnp_get_item_types($tmp)) != RES_OK) {
        $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
        return false;
    } else {
        foreach ($tmp as $i) {
            $itemtypes[$i['item_type_id']] = $i;
            $itemtype_names[$i['name']] = $i;
        }
    }
    $join1 = " left join {$xlink_table} on {$xlink_table}.item_id  = {$basic_table}.item_id " . " left join {$index_table} on {$index_table}.index_id = {$xlink_table}.index_id " . " left join {$glink_table} on {$glink_table}.gid      = {$index_table}.gid " . " left join {$user_table}  on {$user_table}.uid       = {$basic_table}.uid ";
    $iids = array();
    if ($private_flag) {
        // operation to add item into index. search for only the user's item.
        $privilege = "( {$index_table}.open_level = " . OL_PRIVATE . " and {$index_table}.uid={$uid} )";
    } else {
        // search for readable items.
        $xmember_handler =& xoonips_gethandler('xoonips', 'member');
        if ($xmember_handler->isAdmin($uid) || xnp_is_moderator($xnpsid, $uid)) {
            $privilege = " 1 ";
        } else {
            $privilege = " ({$index_table}.open_level = " . OL_PUBLIC . " or \n" . "  {$index_table}.open_level = " . OL_PRIVATE . " and {$index_table}.uid={$uid} or \n" . "  {$index_table}.open_level = " . OL_GROUP_ONLY . " and {$glink_table}.uid={$uid} ) \n";
        }
    }
    if ($op == 'advancedsearch' || $op == 'itemsubtypesearch') {
        // advanced では、結果をタブ表示しない。$search_tab を無視する。
        // advanced では、fileがヒットした場合でも search_cache_fileではなくsearch_cache_itemのほうに書く
        $formdata =& xoonips_getutility('formdata');
        foreach ($itemtypes as $itemtype_id => $itemtype) {
            $wheres = array(' 0 ');
            $module_name = $itemtype['name'];
            if ($formdata->getValue('post', $module_name, 'n', false)) {
                include_once XOOPS_ROOT_PATH . '/modules/' . $itemtype['viewphp'];
                $f = $module_name . 'GetAdvancedSearchQuery';
                $table = $xoopsDB->prefix("{$module_name}_item_detail");
                $key_name = "{$table}." . substr($module_name, 3) . '_id';
                // xnppaper -> paper_id
                $where = "";
                $join = "";
                $f($where, $join);
                // require retrieve additional query string to item type module
                if ($where != "") {
                    $sql = "select {$basic_table}.item_id, {$search_cache_id} from {$basic_table} " . $join1 . " left join {$file_table}    on {$file_table}.item_id    = {$basic_table}.item_id " . " left join {$title_table}   on {$title_table}.item_id   = {$basic_table}.item_id " . " left join {$keyword_table} on {$keyword_table}.item_id = {$basic_table}.item_id " . " left join {$table} on {$key_name} = {$basic_table}.item_id " . " left join {$search_text_table} on {$search_text_table}.file_id    = {$file_table}.file_id " . $join . " where  {$key_name} is not NULL and ( {$where} ) and {$privilege} \n" . " group by {$basic_table}.item_id  \n";
                    if ($cachable) {
                        // write to cache at once
                        $result = $xoopsDB->queryF("insert ignore into {$cache_item_table} ( item_id, search_cache_id ) " . $sql);
                        $sql = "select item_id from {$cache_item_table} where search_cache_id = {$search_cache_id}";
                    }
                    $result = $xoopsDB->query($sql);
                    if ($result == false) {
                        $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
                        xoonips_error($xoopsDB->error() . " at " . __LINE__ . " in " . __FILE__);
                        return false;
                    }
                    while (list($iid) = $xoopsDB->fetchRow($result)) {
                        $iids[] = $iid;
                    }
                }
            }
        }
    } else {
        if ($op == 'itemtypesearch') {
            // top画面から。$search_itemtypeに一致するアイテムを列挙
            $itemtype_id = $itemtype_names[$search_itemtype]['item_type_id'];
            $sql = "select {$basic_table}.item_id, {$search_cache_id} from {$basic_table} \n" . $join1 . " where {$privilege} and {$basic_table}.item_type_id={$itemtype_id} \n" . " group by {$basic_table}.item_id  ";
            // inserting results to cache
            $result = $xoopsDB->queryF("insert ignore into {$cache_item_table} ( item_id, search_cache_id ) " . $sql);
            $sql = "select item_id from {$cache_item_table} where search_cache_id = {$search_cache_id}";
            $result = $xoopsDB->query($sql);
            if ($result == false) {
                $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
                return false;
            }
            while (list($iid) = $xoopsDB->fetchRow($result)) {
                $iids[] = $iid;
            }
        } else {
            if ($op == 'quicksearch' && trim($keyword) != '') {
                $search_var[] = 'keyword';
                $search_var[] = 'search_itemtype';
                list($elements, $keywords, $errorMessage) = xnpSplitKeywords2($keyword);
                $keywordsLen = count($keywords);
                if ($errorMessage) {
                    $msg = $errorMessage;
                    return false;
                }
                if ($search_itemtype == 'basic') {
                    // search titles and keywords
                    $wheres_title_keyword = xnpGetKeywordsQueries(array($title_table . '.title', $keyword_table . '.keyword'), $keywords);
                    $where = " {$basic_table}.item_type_id != " . ITID_INDEX . " and  " . xnpUnsplitKeywords2($elements, $wheres_title_keyword);
                    $sql = "select {$basic_table}.item_id, {$search_cache_id} from {$basic_table} \n" . $join1 . " left join {$title_table}   on {$basic_table}.item_id = {$title_table}.item_id " . " left join {$keyword_table} on {$basic_table}.item_id = {$keyword_table}.item_id " . " where {$where} and {$privilege} \n" . " group by {$basic_table}.item_id  \n";
                    // inserting results to cache
                    $result = $xoopsDB->queryF("insert ignore into {$cache_item_table} ( item_id, search_cache_id ) " . $sql);
                    $sql = "select item_id from {$cache_item_table} where search_cache_id = {$search_cache_id}";
                    $result = $xoopsDB->query($sql);
                    if ($result == false) {
                        $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
                        return false;
                    }
                    while (list($iid) = $xoopsDB->fetchRow($result)) {
                        $iids[] = $iid;
                    }
                }
                if ($search_itemtype == 'metadata' || $search_itemtype == 'all') {
                    // if 'metadata' then set result of search to cache and $iids
                    // if 'all' then write to cache
                    $searchutil =& xoonips_getutility('search');
                    $encoding = mb_detect_encoding($keyword);
                    $fulltext_criteria =& $searchutil->getFulltextSearchCriteria('search_text', $keyword, $encoding);
                    $sql = "select identifier, {$search_cache_id}\n             from {$meta_table} as data, {$repo_table} as repo\n             where repo.enabled=1 AND repo.deleted!=1 AND repo.repository_id=data.repository_id\n              AND " . $fulltext_criteria->render() . " order by identifier, data.repository_id";
                    // inserting results to cache
                    $result = $xoopsDB->queryF("insert into {$cache_meta_table} ( identifier, search_cache_id ) " . $sql);
                    $sql = "select item_id from {$cache_item_table} where search_cache_id = {$search_cache_id}";
                    $result = $xoopsDB->query($sql);
                    if ($result == false) {
                        $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
                        return false;
                    }
                    while (list($iid) = $xoopsDB->fetchRow($result)) {
                        $iids[] = $iid;
                    }
                }
                if (isset($itemtype_names[$search_itemtype]) || $search_itemtype == 'all') {
                    /* where_condition[item_type] = "item_type_id=$itemtype_id and " ( query that combines 'wheres2' and 'and or ( )' ).
                          wheres2[keyword] = ( where_basic[keyword] or where_detail[keyword] )
                       */
                    // search_itemtype == (itemtype)の場合もファイル内部を検索する必要がある.
                    // ここでファイル検索を行って、結果を xoonips_search_cache_file に書く
                    $wheres_basic = xnpGetKeywordsQueries(array($title_table . '.title', $keyword_table . '.keyword', $basic_table . '.description', $basic_table . '.doi', $user_table . '.uname', $user_table . '.name'), $keywords);
                    foreach ($itemtypes as $itemtype_id => $itemtype) {
                        if ($itemtype['item_type_id'] == ITID_INDEX) {
                            continue;
                        }
                        $module_name = $itemtype['name'];
                        //echo "$search_itemtype / $module_name <br />\n";
                        if ($search_itemtype == $module_name || $search_itemtype == 'all') {
                            $itemtype_id = $itemtype['item_type_id'];
                            if ($file_or_item_metadata == 'all' || $file_or_item_metadata == 'item_metadata') {
                                include_once XOOPS_ROOT_PATH . '/modules/' . $itemtype['viewphp'];
                                $f = $module_name . 'GetDetailInformationQuickSearchQuery';
                                if (!function_exists($f)) {
                                    continue;
                                }
                                $table = $xoopsDB->prefix("{$module_name}_item_detail");
                                $wheres_detail = array();
                                $f($wheres_detail, $join, $keywords);
                                $wheres2 = array();
                                for ($i = 0; $i < $keywordsLen; $i++) {
                                    // search by item_id
                                    $where3 = sprintf(' OR `%s`.`item_id`=%s', $basic_table, $xoopsDB->quoteString($keywords[$i]));
                                    if (empty($wheres_detail[$i])) {
                                        $wheres_detail[$i] = '0';
                                    }
                                    $wheres2[] = $wheres_basic[$i] . ' or ' . $wheres_detail[$i] . $where3;
                                }
                                $where = " {$basic_table}.item_type_id={$itemtype_id} and " . xnpUnsplitKeywords2($elements, $wheres2);
                                $key_name = "{$table}." . substr($module_name, 3) . '_id';
                                // xnppaper -> paper_id
                                $sql = "select {$basic_table}.item_id, {$search_cache_id} from {$basic_table} " . $join1 . " left join {$file_table}  on {$file_table}.item_id   = {$basic_table}.item_id " . " left join {$title_table}   on {$basic_table}.item_id = {$title_table}.item_id " . " left join {$keyword_table} on {$basic_table}.item_id = {$keyword_table}.item_id " . " left join {$table} on {$key_name} = {$basic_table}.item_id " . $join . " where {$where} and {$privilege} \n" . " group by {$basic_table}.item_id  \n";
                                $result = $xoopsDB->queryF("insert ignore into {$cache_item_table} ( item_id, search_cache_id ) " . $sql);
                                if ($result == false) {
                                    $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
                                    return false;
                                }
                            }
                            if ($file_or_item_metadata == 'all' || $file_or_item_metadata == 'file') {
                                $searchutil =& xoonips_getutility('search');
                                $encoding = mb_detect_encoding($keyword);
                                $fulltext_criteria =& $searchutil->getFulltextSearchCriteria('search_text', $keyword, $encoding);
                                // search inside files
                                $sql = "insert ignore into {$cache_file_table} ( file_id, search_cache_id )\n                          select {$file_table}.file_id, {$search_cache_id} from {$file_table}\n                          left join {$basic_table} on {$file_table}.item_id={$basic_table}.item_id\n                          left join {$search_text_table} on {$file_table}.file_id = {$search_text_table}.file_id\n                          where item_type_id={$itemtype_id} and " . $fulltext_criteria->render() . " and {$file_table}.is_deleted=0";
                                // write to cache at once
                                $result = $xoopsDB->queryF($sql);
                                if ($result == false) {
                                    $msg = _MD_XOONIPS_ITEM_SEARCH_ERROR;
                                    return false;
                                }
                            }
                        }
                    }
                    switch ($search_tab) {
                        case 'metadata':
                            $result = $xoopsDB->query("select item_id from {$cache_meta_table} where search_cache_id={$search_cache_id}");
                            break;
                        case 'file':
                            $result = $xoopsDB->query("select item_id from {$cache_file_table} where search_cache_id={$search_cache_id}");
                            break;
                        case 'item':
                        default:
                            $result = $xoopsDB->query("select item_id from {$cache_item_table} where search_cache_id={$search_cache_id}");
                            break;
                    }
                    while (list($iid) = $xoopsDB->fetchRow($result)) {
                        $iids[] = $iid;
                    }
                }
            }
        }
    }
    return true;
}