$usrID = get_user_id();
$bkm_ID = intval($_POST["bkmk_id"]);
if ($bkm_ID && $_POST["save-mode"] == "edit") {
    if (array_key_exists("tagString", $_POST)) {
        $tagString = doTagInsertion($bkm_ID);
    } else {
        $tagString = NULL;
    }
    $updatable = array("bkm_PwdReminder" => array("password-reminder", "passwordReminder"), "bkm_Rating" => array("overall-rating", "rating"));
    $updates = array();
    foreach ($updatable as $colName => $varNames) {
        if (array_key_exists($varNames[0], $_POST)) {
            $updates[$colName] = $_POST[$varNames[0]];
        }
    }
    mysql__update("usrBookmarks", "bkm_ID={$bkm_ID} and bkm_UGrpID={$usrID}", $updates);
    $res = mysql_query("select " . join(", ", array_keys($updates)) . " from usrBookmarks where bkm_ID={$bkm_ID} and bkm_UGrpID={$usrID}");
    if (mysql_num_rows($res) == 1) {
        $dbVals = mysql_fetch_assoc($res);
        $hVals = array();
        foreach ($dbVals as $colName => $val) {
            $hVals[$updatable[$colName][1]] = $val;
        }
        if ($tagString !== NULL) {
            $hVals["tagString"] = $tagString;
        }
        print "(" . json_format($hVals) . ")";
    } else {
        if ($tagString !== NULL) {
            print "({tagString: \"" . slash($tagString) . "\"})";
        }
/**
 * register external URL (see saveRecordDetails.php)
 * $filejson - either url or json string with file data array
 *
 * returns ulf_ID
 */
function register_external($filejson)
{
    $filedata = json_decode($filejson, true);
    //DEBUG
    /*****DEBUG****/
    //error_log("1.>>>>>".$filedata);
    /*****DEBUG****/
    //error_log("2.>>>>>".print_r($filedata, true));
    if (!is_array($filedata)) {
        //can't parse - assume this is URL - old way
        $filedata = array();
        $url = $filejson;
        //1. get url, source and type
        $acfg = explode('|', $url);
        $filedata['remoteURL'] = $acfg[0];
        $filedata['ext'] = NULL;
        if (count($acfg) < 3) {
            $oType = detectSourceAndType($url);
            $filedata['remoteSource'] = $oType[0];
            $filedata['mediaType'] = $oType[1];
            $filedata['ext'] = $oType[2];
        } else {
            $filedata['remoteSource'] = $acfg[1];
            $filedata['mediaType'] = $acfg[2];
            if (count($acfg) == 4) {
                $filedata['ext'] = $acfg[3];
            }
        }
    }
    if (@$filedata['ext'] == null && $filedata['mediaType'] == "xml") {
        $filedata['ext'] = "xml";
    }
    //*****DEBUG****/// error_log("reg remote file data ".print_r($filedata,true));
    $fileparameters = @$filedata['params'] ? $filedata['params'] : "mediatype=" . $filedata['mediaType'];
    if (@$filedata['remoteSource'] && $filedata['remoteSource'] != 'heurist') {
        // && $filedata['remoteSource']!='generic'){
        $fileparameters = $fileparameters . "|source=" . $filedata['remoteSource'];
    }
    //if id is defined
    if (array_key_exists('id', $filedata) && intval($filedata['id']) > 0) {
        //update
        $file_id = $filedata['id'];
        //ignore registration for already uploaded file
        if (array_key_exists('remoteSource', $filedata) && $filedata['remoteSource'] != 'heurist') {
            mysql__update('recUploadedFiles', 'ulf_ID=' . $file_id, array('ulf_Modified' => date('Y-m-d H:i:s'), 'ulf_MimeExt ' => $filedata['ext'], 'ulf_ExternalFileReference' => $filedata['remoteURL'], 'ulf_Parameters' => $fileparameters));
        }
    } else {
        if (!array_key_exists('remoteURL', $filedata) || $filedata['remoteURL'] == null || $filedata['remoteURL'] == "") {
            return null;
        }
        //2. find duplication (the same url)
        if (array_key_exists('remoteSource', $filedata) && $filedata['remoteSource'] != 'heurist') {
            $res = mysql_query('select ulf_ID from recUploadedFiles ' . 'where ulf_ExternalFileReference = "' . addslashes($filedata['remoteURL']) . '"');
            if (mysql_num_rows($res) == 1) {
                $row = mysql_fetch_assoc($res);
                $file_id = $row['ulf_ID'];
                mysql__update('recUploadedFiles', 'ulf_ID=' . $file_id, array('ulf_Modified' => date('Y-m-d H:i:s'), 'ulf_MimeExt ' => $filedata['ext'], 'ulf_Parameters' => $fileparameters));
                return $file_id;
            }
        }
        //3. save into  recUploadedFiles
        $res = mysql__insert('recUploadedFiles', array('ulf_OrigFileName' => '_remote', 'ulf_UploaderUGrpID' => get_user_id(), 'ulf_Added' => date('Y-m-d H:i:s'), 'ulf_MimeExt ' => array_key_exists('ext', $filedata) ? $filedata['ext'] : NULL, 'ulf_FileSizeKB' => 0, 'ulf_Description' => NULL, 'ulf_ExternalFileReference' => array_key_exists('remoteURL', $filedata) ? $filedata['remoteURL'] : NULL, 'ulf_Parameters' => $fileparameters));
        if (!$res) {
            /*****DEBUG****/
            //error_log("ERROR Insert record: ".mysql_error());
            return null;
            //"Error registration remote source  $url into database";
        }
        $file_id = mysql_insert_id();
        mysql_query('update recUploadedFiles set ulf_ObfuscatedFileID = "' . addslashes(sha1($file_id . '.' . rand())) . '" where ulf_ID = ' . $file_id);
    }
    //4. returns ulf_ID
    return $file_id;
}
Exemple #3
0
function do_file_update(&$file, $type, $condition, $new_filename)
{
    global $FILES, $MAINTABLES;
    global $MYSQL_ERRORS;
    $updates = array();
    if (!@$FILES[$type]) {
        fatal("{$type} objects aren't associated with a file upload");
    }
    if (!@$file['filename']) {
        fatal("non-file passed through to do_file_update");
    }
    if (@$FILES[$type]['name']) {
        $updates[$FILES[$type]['name']] = $file['filename'];
    }
    if (@$FILES[$type]['size']) {
        $updates[$FILES[$type]['size']] = $file['size'];
    }
    if (@$FILES[$type]['type']) {
        $updates[$FILES[$type]['type']] = $file['filetype'];
    }
    $updates[$FILES[$type]['path']] = $new_filename;
    if (!move_uploaded_file($file['localpath'], HEURIST_FILESTORE_DIR . '/' . $new_filename)) {
        fatal("desperate failure while handling uploaded file");
    }
    if (!mysql__update($MAINTABLES[$type], $condition, $updates)) {
        array_push($MYSQL_ERRORS, mysql_error());
    }
}
Exemple #4
0
function saveWoot($args)
{
    if (!is_logged_in()) {
        return array("success" => false, "errorType" => "no logged-in user");
    }
    mysql_connection_overwrite(DATABASE);
    $wootId = intval(@$args["id"]);
    $wootTitle = mysql_real_escape_string(@$args["title"]);
    mysql_query("start transaction");
    if (!$wootId || $wootId === "new") {
        /* This is a new WOOT that hasn't been saved yet */
        if (!$wootTitle) {
            return array("success" => false, "errorType" => "missing title");
        }
        mysql__insert(WOOT_TABLE, array("woot_Title" => $wootTitle, "woot_Created" => array("now()"), "woot_Modified" => array("now()"), "woot_Version" => 0, "woot_CreatorID" => get_user_id()));
        $wootId = mysql_insert_id();
        if (!$wootId) {
            return array("success" => false, "errorType" => "a woot with the given title already exists");
        }
        $woot = mysql_fetch_assoc(mysql_query("select * from " . WOOT_TABLE . " where woot_ID={$wootId}"));
        $woot["permissions"] = $args["permissions"];
        $result = insertWootPermissions($wootId, $woot);
        if ($result["success"] != true) {
            return $result;
        }
    } else {
        /* We are saving the WOOT -- get a new version number, commit, and then do chunk-wise operations.
         * Other people can operate on a separate version at the same time.
         */
        if (!hasWootWritePermission($wootId)) {
            return array("success" => false, "errorType" => "woot doesn't exist, or insufficient permissions on woot");
        }
        mysql_query("update " . WOOT_TABLE . " set woot_Version=woot_Version+1 where woot_ID={$wootId}");
    }
    $res = mysql_query("select * from " . WOOT_TABLE . " where woot_ID={$wootId}");
    mysql_query("commit and chain");
    $woot = mysql_fetch_assoc($res);
    $version = intval($woot["woot_Version"]);
    $chunkIds = getReadableChunks($wootId, true);
    $res = mysql_query("select * from " . CHUNK_TABLE . "\n\t\t\t\t\t\t\t where chunk_WootID={$wootId} and chunk_IsLatest and !chunk_Deleted and chunk_ID in (" . join(",", $chunkIds) . ")\n\t\t\t\t\t\t  order by chunk_DisplayOrder");
    $existingVisibleChunks = array();
    while ($chunk = @mysql_fetch_assoc($res)) {
        /* The @ takes care of the possibility that there are no chunks in this woot */
        $existingVisibleChunks[$chunk["chunk_InsertOrder"]] = $chunk;
    }
    $incomingChunks = $args["chunks"];
    // Get the current chunk ordering (including the chunks the current user can't actually see)
    $existingChunkOrder = mysql__select_array(CHUNK_TABLE, "chunk_InsertOrder", "chunk_WootID={$wootId} and chunk_IsLatest and ! chunk_Deleted order by chunk_DisplayOrder");
    reset($existingChunkOrder);
    // Check that the incoming chunks are in the same order as the existing chunks, otherwise raise an error
    if (count($existingChunkOrder)) {
        foreach ($incomingChunks as $chunk) {
            if (!@$chunk["number"]) {
                continue;
            }
            // new chunk, doesn't have an ordering yet
            while (current($existingChunkOrder) != $chunk["number"]) {
                if (next($existingChunkOrder) === FALSE) {
                    // Ran out of existing chunks
                    // The incoming chunk is out of order (you're out of order, the whole court's out of order)
                    return array("success" => false, "errorType" => "invalid chunk ordering", "chunkNonce" => $chunk["nonce"]);
                }
            }
        }
    }
    $chunkNonceToNumber = array();
    $newChunks = array(NULL => array());
    $newChunkCount = 0;
    $firstExistingChunk = NULL;
    $lastExistingChunk = NULL;
    foreach ($incomingChunks as $chunk) {
        $prevChunkId = NULL;
        if (@$chunk["number"]) {
            // If the incoming chunk has a number which doesn't correspond to an existing chunk,
            // then the user has had permissions pulled out from under them (or they're playing funny buggers)
            // Either way, raise an error
            if (!@$existingVisibleChunks[$chunk["number"]]) {
                return array("success" => false, "errorType" => "chunk permissions have changed", "chunkNonce" => $chunk["nonce"]);
            }
            $chunkNumber = intval($chunk["number"]);
            // Keep track of the position of this (existing) chunk.
            // Any new chunks that occur before the next (existing) chunk will be stored in $newChunks[$lastExistingChunk]
            if (!$firstExistingChunk) {
                $firstExistingChunk = $chunkNumber;
            }
            $lastExistingChunk = $chunkNumber;
            $newChunks[$lastExistingChunk] = array();
            if (!@$chunk["unmodified"]) {
                // Chunk exists, and is reported as modified.  Make a new version of it.
                $res = mysql_query("select chunk_ID, chunk_DisplayOrder, chunk_OwnerID from " . CHUNK_TABLE . " where chunk_WootID={$wootId} and chunk_InsertOrder={$chunkNumber} and chunk_IsLatest");
                if (mysql_num_rows($res) != 1) {
                    /* should do something ... do we care? */
                }
                $prevChunk = mysql_fetch_assoc($res);
                $prevChunkId = $prevChunk["chunk_ID"];
                $chunkOrder = $prevChunk["chunk_DisplayOrder"];
                $chunkOwner = $prevChunk["chunk_OwnerID"];
                mysql__update(CHUNK_TABLE, "chunk_WootID={$wootId} and chunk_InsertOrder={$chunkNumber}", array("chunk_IsLatest" => 0));
            } else {
                // Chunk exists, but is not modified.  Nothing more to do.
                continue;
            }
        } else {
            $res = mysql_query("select max(chunk_InsertOrder) from " . CHUNK_TABLE . " where chunk_WootID={$wootId}");
            $chunkNumber = @mysql_fetch_row($res);
            $chunkNumber = intval(@$chunkNumber[0]) + 1;
            $chunkOrder = 0;
            // chunk order will be overridden anyway since there is a new chunk to take care of
            $chunkOwner = get_user_id();
            array_push($newChunks[$lastExistingChunk], $chunkNumber);
            ++$newChunkCount;
        }
        $chunkDeleted = preg_match('/^\\s*$/', $chunk["text"]);
        mysql__insert(CHUNK_TABLE, array("chunk_WootID" => $wootId, "chunk_InsertOrder" => $chunkNumber, "chunk_Version" => $version, "chunk_Text" => $chunk["text"], "chunk_IsLatest" => 1, "chunk_DisplayOrder" => $chunkOrder, "chunk_Modified" => array("now()"), "chunk_OwnerID" => $chunkOwner, "chunk_EditorID" => get_user_id(), "chunk_Deleted" => $chunkDeleted));
        $chunkId = mysql_insert_id();
        if (!$chunkDeleted) {
            if ($chunkOwner == get_user_id() || is_admin()) {
                // only the owner (or an admin) can change the permissions
                $result = insertPermissions($chunkId, $chunk, $woot["woot_CreatorID"]);
                if ($result["success"] != true) {
                    return $result;
                }
            } else {
                // copy the permissions from the previous version of the chunk
                mysql_query("insert into " . PERMISSION_TABLE . "\n\t\t\t\t\t\t\t\t (wprm_ChunkID, wprm_UGrpID, wprm_GroupID, wprm_Type, wprm_CreatorID, wprm_Created)\n\t\t\t\t\t\t   select distinct {$chunkId}, wprm_UGrpID, wprm_GroupID, wprm_Type, wprm_CreatorID, wprm_Created\n\t\t\t\t\t\t\t from " . PERMISSION_TABLE . " where wprm_ChunkID={$prevChunkId}");
            }
            if (@$chunk["nonce"]) {
                // if the client hasn't specified a nonce they're obviously not interested in the resulting chunk number
                $chunkNonceToNumber[$chunk["nonce"]] = $chunkNumber;
            }
        } else {
            if ($chunk["nonce"]) {
                $chunkNonceToNumber[$chunk["nonce"]] = NULL;
                // blast away the existing number for this chunk
            }
        }
    }
    if ($newChunkCount) {
        // New chunks have been inserted.
        // Make a merged list of existing chunks and newly inserted chunks, then update their ordering
        $allChunks = array();
        foreach ($existingChunkOrder as $existingChunkNumber) {
            // Consider chunks (A, B*, C*, D, E*) where B*, C* and E* are new chunks, and A and D are existing chunks.
            // In the merged list, B* and C* will directly follow A, and E* will directly follow D.
            // So, given existingChunkOrder (X, A, Y, D, Z) and chunkNonceToNumber (A, B*, C*, D, E*),
            // allChunks becomes (X, A, B*, C*, Y, D, E*, Z)
            if ($existingChunkNumber == $firstExistingChunk && count($newChunks[NULL])) {
                // This is the first chunk that the user can see, and there are new chunks to add before it.
                $allChunks = array_merge($allChunks, $newChunks[NULL]);
            }
            array_push($allChunks, $existingChunkNumber);
            if (count(@$newChunks[$existingChunkNumber])) {
                // There are new chunks to add directly after this chunk
                $allChunks = array_merge($allChunks, $newChunks[$existingChunkNumber]);
            }
        }
        if (!$firstExistingChunk && count($newChunks[NULL])) {
            // Okay, there were no existing chunks that the user could see ... add any new chunks at the end
            $allChunks = array_merge($allChunks, $newChunks[NULL]);
        }
        for ($i = 0; $i < count($allChunks); ++$i) {
            $order = $i + 1;
            $chunkNumber = $allChunks[$i];
            mysql_query("update " . CHUNK_TABLE . " set chunk_DisplayOrder={$order}\n\t\t\t\t\t\t\t  where chunk_WootID={$wootId} and chunk_InsertOrder={$chunkNumber} and chunk_IsLatest");
        }
    }
    mysql_query("commit");
    return array("success" => true, "id" => $wootId, "version" => $version, "chunks" => $chunkNonceToNumber);
}
/**
 * Main method that parses POST and update details for given record ID
 *
 * @param int $recID
 */
function updateRecord($recID, $rtyID = null)
{
    // Update the given record.
    // This is non-trivial: so that the versioning stuff (achive_*) works properly
    // we need to separate this into updates, inserts and deletes.
    // We get the currect record details and compare them against the post
    // if the details id is in the post[dtyID][dtlID] then compare the values
    $recID = intval($recID);
    // Check that the user has permissions to edit it.
    $res = mysql_query("select * from Records" . " left join sysUsrGrpLinks on ugl_GroupID=rec_OwnerUGrpID" . " left join defRecTypes on rty_ID=rec_RecTypeID" . " where rec_ID={$recID} and (! rec_OwnerUGrpID or rec_OwnerUGrpID=" . get_user_id() . " or ugl_UserID=" . get_user_id() . ")");
    if (mysql_num_rows($res) == 0) {
        $res = mysql_query("select grp.ugr_Name from Records, " . USERS_DATABASE . ".sysUGrps grp where rec_ID={$recID} and grp.ugr_ID=rec_OwnerUGrpID");
        $grpName = mysql_fetch_row($res);
        $grpName = $grpName[0];
        print '({ error: "\\nSorry - you can\'t edit this record.\\nYou aren\'t in the ' . slash($grpName) . ' workgroup" })';
        return;
    }
    $record = mysql_fetch_assoc($res);
    /*****DEBUG****/
    error_log("save record dtls POST " . print_r($_POST, true));
    // Upload any files submitted ... (doesn't have to take place right now, but may as well)
    uploadFiles();
    //Artem: it does not work here - since we uploaded files at once
    // Get the existing records details and compare them to the incoming data
    $recDetails = getRecordDetails($recID);
    // find UPDATES - everything that is in current record and has a post value is treated as an update
    $recDetailUpdates = array();
    /*****DEBUG****/
    //error_log("save record dtls ".print_r($recDetails,true));
    foreach ($recDetails as $dtyID => $dtlIDs) {
        $eltName = "type:" . $dtyID;
        if (!(@$_POST[$eltName] && is_array($_POST[$eltName]))) {
            // element wasn't in POST: ignore it -this could be a non-rectype detail
            unset($recDetails[$dtyID]);
            // remove from details so it's not deleted
            continue;
        }
        if (count($_POST[$eltName]) == 0) {
            // element was in POST but without content: values have been deleted client-side (need to be deleted in DB so leave POST)
            continue;
        }
        $bdInputHandler = getInputHandlerForType($dtyID);
        //returns the particular handler (processor) for given field type
        foreach ($dtlIDs as $dtlID => $val) {
            /*****DEBUG****/
            //error_log(" in saveRecord details loop  $dtyID,  $dtlID, ".print_r($val,true));
            $eltID = "bd:" . $dtlID;
            $val = @$_POST[$eltName][$eltID];
            if (!$bdInputHandler->inputOK($val, $dtyID, $rtyID)) {
                /*****DEBUG****/
                //error_log(" in saveRecord update details value check error  $dtyID,  $dtlID, ".print_r($val,true));
                continue;
                // faulty input ... ignore
            }
            $toadd = $bdInputHandler->convertPostToMysql($val);
            /*****DEBUG****/
            //error_log(" in saveRecord update details value converted from $val to $toadd");
            if ($toadd == null) {
                continue;
            }
            $recDetailUpdates[$dtlID] = $toadd;
            $recDetailUpdates[$dtlID]["dtl_DetailTypeID"] = $dtyID;
            /*
            @TODO Since this function is utilized in (email)import we need to add verification of values according to detail type
            at the first for terms (enumeration field type)
            */
            unset($_POST[$eltName][$eltID]);
            // remove data from post submission
            if (count($_POST[$eltName]) == 0) {
                // if nothing left in post dtyID then remove it also
                unset($_POST[$eltName]);
            }
            unset($recDetails[$dtyID][$dtlID]);
            // remove data from local reflection of the database
        }
    }
    /*****DEBUG****/
    //error_log("save record dtls POST after updates removed ".print_r($_POST,true));
    /*****DEBUG****/
    //error_log("save record dtls after updates removed ".print_r($recDetails,true));
    // find DELETES
    // Anything left in recDetails now represents recDetails rows that need to be deleted
    $bibDetailDeletes = array();
    foreach ($recDetails as $dtyID => $dtlIDs) {
        foreach ($dtlIDs as $dtlID => $val) {
            array_push($bibDetailDeletes, $dtlID);
        }
    }
    // find INSERTS
    // Try to insert anything left in POST as new recDetails rows
    $bibDetailInserts = array();
    /*****DEBUG****/
    error_log(" in saveRecord checking for inserts  _POST =" . print_r($_POST, true));
    foreach ($_POST as $eltName => $bds) {
        // if not properly formatted or empty or an empty array then skip it
        if (!preg_match("/^type:\\d+\$/", $eltName) || !$_POST[$eltName] || count($_POST[$eltName]) == 0) {
            continue;
        }
        $dtyID = substr($eltName, 5);
        $bdInputHandler = getInputHandlerForType($dtyID);
        foreach ($bds as $eltID => $val) {
            if (!$bdInputHandler->inputOK($val, $dtyID, $rtyID)) {
                /*****DEBUG****/
                //error_log(" in saveRecord insert details value check error for $eltName,  $eltID, ".print_r($val,true));
                continue;
                // faulty input ... ignore
            }
            $newBibDetail = $bdInputHandler->convertPostToMysql($val);
            $newBibDetail["dtl_DetailTypeID"] = $dtyID;
            $newBibDetail["dtl_RecID"] = $recID;
            /*****DEBUG****/
            //error_log("new detail ".print_r($newBibDetail,true));
            array_push($bibDetailInserts, $newBibDetail);
            unset($_POST[$eltName][$eltID]);
            // remove data from post submission
        }
    }
    // Anything left in POST now is stuff that we have no intention of inserting ... ignore it
    // We now have:
    //  - $recDetailUpdates: an assoc. array of dtl_ID => column values to be updated in recDetails
    //  - $bibDetailInserts: an array of column values to be inserted into recDetails
    //  - $bibDetailDeletes: an array of dtl_ID values corresponding to rows to be deleted from recDetails
    // Commence versioning ...
    mysql_query("start transaction");
    $recUpdates = array("rec_Modified" => array("now()"), "rec_FlagTemporary" => 0);
    $recUpdates["rec_ScratchPad"] = $_POST["notes"];
    if (intval(@$_POST["rectype"])) {
        $recUpdates["rec_RecTypeID"] = intval($_POST["rectype"]);
    }
    if (array_key_exists("rec_url", $_POST)) {
        $recUpdates["rec_URL"] = $_POST["rec_url"];
    }
    $owner = $record['rec_OwnerUGrpID'];
    if (is_admin() || is_admin('group', $owner) || $owner == get_user_id()) {
        // must be grpAdmin or record owner to changes ownership or visibility
        if (array_key_exists("rec_owner", $_POST)) {
            $recUpdates["rec_OwnerUGrpID"] = $_POST["rec_owner"];
        }
        if (array_key_exists("rec_visibility", $_POST)) {
            $recUpdates["rec_NonOwnerVisibility"] = $_POST["rec_visibility"];
        } else {
            if ($record['rec_NonOwnerVisibility'] == 'public' && HEURIST_PUBLIC_TO_PENDING) {
                $recUpdates["rec_NonOwnerVisibility"] = 'pending';
            }
        }
    }
    /*****DEBUG****/
    error_log(" in saveRecord update recUpdates = " . print_r($recUpdates, true));
    mysql__update("Records", "rec_ID={$recID}", $recUpdates);
    $biblioUpdated = mysql_affected_rows() > 0 ? true : false;
    if (mysql_error()) {
        error_log("error rec update" . mysql_error());
    }
    $updatedRowCount = 0;
    foreach ($recDetailUpdates as $bdID => $vals) {
        /*****DEBUG****/
        error_log(" in saveRecord update details dtl_ID = {$bdID} value =" . print_r($vals, true));
        mysql__update("recDetails", "dtl_ID={$bdID} and dtl_RecID={$recID}", $vals);
        if (mysql_affected_rows() > 0) {
            ++$updatedRowCount;
        }
    }
    if (mysql_error()) {
        error_log("error detail updates" . mysql_error());
    }
    $insertedRowCount = 0;
    foreach ($bibDetailInserts as $vals) {
        /*****DEBUG****/
        error_log(" in saveRecord insert details detail =" . print_r($vals, true));
        mysql__insert("recDetails", $vals);
        if (mysql_affected_rows() > 0) {
            ++$insertedRowCount;
        }
    }
    if (mysql_error()) {
        error_log("error detail inserts" . mysql_error());
    }
    $deletedRowCount = 0;
    if ($bibDetailDeletes) {
        /*****DEBUG****/
        error_log(" in saveRecord delete details " . print_r($bibDetailDeletes, true));
        mysql_query("delete from recDetails where dtl_ID in (" . join($bibDetailDeletes, ",") . ") and dtl_RecID={$recID}");
        if (mysql_affected_rows() > 0) {
            $deletedRowCount = mysql_affected_rows();
        }
    }
    if (mysql_error()) {
        error_log("error detail deletes" . mysql_error());
    }
    // eliminate any duplicated lines
    $notesIn = explode("\n", str_replace("\r", "", $_POST["notes"]));
    $notesOut = "";
    $notesMap = array();
    for ($i = 0; $i < count($notesIn); ++$i) {
        if (!@$notesMap[$notesIn[$i]] || !$notesIn[$i]) {
            // preserve blank lines
            $notesOut .= $notesIn[$i] . "\n";
            $notesMap[$notesIn[$i]] = true;
        }
    }
    $_POST["notes"] = preg_replace("/\n\n+/", "\n", $notesOut);
    if ($updatedRowCount > 0 || $insertedRowCount > 0 || $deletedRowCount > 0 || $biblioUpdated) {
        /* something changed: update the records title and commit all changes */
        $title_check = check_title_mask2($record["rty_TitleMask"], $record["rec_RecTypeID"], true);
        if ($title_check != '') {
            $new_title = "Please go to Designer View > Essentials > Record types/fields and edit the title mask for this record type";
        } else {
            $new_title = fill_title_mask($record["rty_TitleMask"], $record["rec_ID"], $record["rec_RecTypeID"]);
        }
        mysql_query("update Records\n                set rec_Title = '" . addslashes($new_title) . "'\n                where rec_ID = {$recID}");
        mysql_query("commit");
        // Update memcached's copy of record (if it is cached)
        updateCachedRecord($recID);
        return true;
    } else {
        /* nothing changed: rollback the transaction so we don't get false versioning */
        mysql_query("rollback");
        return false;
    }
}
function handleComments($recordID, $removals, $modifications, $additions)
{
    // removals are encoded as just the comments ID# ... easy.
    if ($removals) {
        $removals = array_map("intval", $removals);
        mysql_query("update recThreadedComments set cmt_Deleted=1\n                where cmt_OwnerUGrpID=" . get_user_id() . " and cmt_RecID={$recordID} and cmt_ID in (" . join(",", $removals) . ")");
    }
    // modifications have the values
    // .id, .parentComment, .text
    foreach ($modifications as $modification) {
        // note that parentComment (of course) cannot be modified
        mysql__update("recThreadedComments", "cmt_ID=" . intval($modification["id"]) . " and cmt_OwnerUGrpID=" . get_user_id(), array("cmt_Text" => $modification["text"], "cmt_Modified" => date('Y-m-d H:i:s')));
    }
    // additions are the same as modifications, except that the COMMENT-ID is blank (of course!)
    $newIDs = array();
    foreach ($additions as $addition) {
        $parentID = intval($addition["parentComment"]);
        // do a sanity check first: does this reply make sense?
        $parentTest = $parentID ? "cmt_ID={$parentID}" : "cmt_ID is null";
        if (!mysql__select_array("Records left join recThreadedComments on rec_ID=cmt_RecID and {$parentTest}", "rec_ID", "rec_ID={$recordID} and {$parentTest}")) {
            array_push($newIDs, array("error" => "invalid parent comments"));
            continue;
        }
        if (!$parentID || intval($parentID) === 0) {
            $parentID = null;
        }
        mysql__insert("recThreadedComments", array("cmt_Text" => $addition["text"], "cmt_Added" => date('Y-m-d H:i:s'), "cmt_OwnerUGrpID" => get_user_id(), "cmt_ParentCmtID" => $parentID, "cmt_RecID" => $recordID));
        array_push($newIDs, array("id" => mysql_insert_id()));
    }
    return $newIDs;
}
<div id="page-inner" style="overflow:auto;padding-left: 20px;">
<div>This function removes invalid characters in the data fields in the database records<br />&nbsp;<hr /></div>
<table>
<?php 
mysql_connection_overwrite(DATABASE);
$prevInvalidRecId = 0;
foreach ($textDetails as $textDetail) {
    if (!check($textDetail['dtl_Value'])) {
        if ($prevInvalidRecId < $textDetail['dtl_RecID']) {
            print "<tr><td><a target=_blank href='" . HEURIST_BASE_URL . "records/edit/editRecord.html?recID=" . $textDetail['dtl_RecID'] . "&db=" . HEURIST_DBNAME . "'> " . $textDetail['dtl_RecID'] . "</a></td></tr>\n";
            $prevInvalidRecId = $textDetail['dtl_RecID'];
            mysql__update("Records", "rec_ID=" . $textDetail['dtl_RecID'], array("rec_Modified" => $now));
        }
        print "<tr><td><pre>" . "Invalid characters found in " . $textDetail['dty_Name'] . " field :</pre></td></tr>\n";
        $newText = str_replace($invalidChars, $replacements, $textDetail['dtl_Value']);
        mysql__update("recDetails", "dtl_ID=" . $textDetail['dtl_ID'], array("dtl_Value" => $newText));
        if (mysql_error()) {
            print "<tr><td><pre>" . "Error " . mysql_error() . "while updating to : " . htmlspecialchars($newText) . "</pre></td></tr>\n";
        } else {
            print "<tr><td><pre>" . "Updated to : " . htmlspecialchars($newText) . "</pre></td></tr>\n";
        }
    }
}
function check($text)
{
    global $invalidChars;
    foreach ($invalidChars as $charCode) {
        if (strpos($text, $charCode)) {
            error_log("found invalid char ");
            return false;
        }
}
mysql_connection_overwrite(DATABASE);
header("Content-type: text/javascript");
$cmt_id = intval(@$_POST["cmt_ID"]);
$rec_id = intval(@$_POST["recID"]);
$owner = intval(@$_POST["owner"]);
if ($cmt_id) {
    // UPDATE COMMENT
    $updates = array("cmt_Modified" => array("now()"));
    if (@$_POST["text"]) {
        $updates["cmt_Text"] = $_POST["text"];
    }
    if (array_key_exists("delete", $_POST)) {
        $updates["cmt_Deleted"] = true;
    }
    mysql__update("recThreadedComments", "cmt_ID={$cmt_id} and cmt_OwnerUgrpID=" . get_user_id(), $updates);
    if (mysql_error()) {
        $error = mysql_error();
    }
    $res = mysql_query("select * from recThreadedComments left join " . USERS_DATABASE . ".sysUGrps usr on cmt_OwnerUgrpID=usr.ugr_ID where cmt_ID={$cmt_id} and ! cmt_Deleted");
    $cmt = mysql_fetch_assoc($res);
} else {
    if ($rec_id) {
        // ADD NEW COMMENT TO RECORD
        $inserts = array("cmt_Text" => $_POST["text"], "cmt_Added" => array("now()"), "cmt_OwnerUgrpID" => get_user_id(), "cmt_RecID" => $rec_id);
        if ($owner) {
            $inserts["cmt_ParentCmtID"] = $owner;
        }
        mysql__insert("recThreadedComments", $inserts);
        if (mysql_error()) {
            $error = mysql_error();
            if (executeScript($dir . $filename)) {
                $src_min++;
                print "<p>Upgraded to " . $src_maj . "." . $src_min . ".0</p>";
            } else {
                $upgrade_success = false;
            }
        } else {
            print "<p style='font-weight:bold'>Cannot find the database upgrade script '" . $filename . "'. Please contact Heurist support</p>";
            $upgrade_success = false;
            break;
        }
    }
    if ($src_min > $keep_minver) {
        //update database
        mysql_connection_overwrite(DATABASE);
        mysql__update("sysIdentification", "1", array("sys_dbSubVersion" => $src_min, "sys_dbSubSubVersion" => "0"));
        print "<br>";
    }
    if ($upgrade_success) {
        print "<p>Upgrade was successeful.&nbsp;&nbsp;<a href='" . HEURIST_BASE_URL . "?db=" . HEURIST_DBNAME . "'>Return to main page</a></p>";
    }
} else {
    ?>

                    <p>Your database <b> <?php 
    echo HEURIST_DBNAME;
    ?>
 </b> currently uses database format version <b><?php 
    echo HEURIST_DBVERSION;
    ?>
 </b>
/**
* save_search : save the current search criteria as a saved search in the saved searches table
*
* @param mixed $data
*/
function save_search($data)
{
    $result = array();
    $wg = intval(@$data['svs_UGrpID']);
    $sID = @$data['svs_ID'];
    //$publish = $data['publish'];
    $label = @$data['svs_Name'];
    $now = date('Y-m-d');
    $cmb = array('svs_Name' => $label, 'svs_Query' => @$data['svs_Query'], 'svs_UGrpID' => $wg > 0 ? $wg : get_user_id(), 'svs_Added' => $now, 'svs_Modified' => $now);
    /* overwrites saved search with same name
       $res = mysql_query('select svs_ID, svs_UGrpID from usrSavedSearches where svs_Name="'.slash($_REQUEST['svs_Name']).'"'.
       ' and svs_UGrpID='.$cmb['svs_UGrpID']);
       $row = mysql_fetch_row($res);*/
    mysql_connection_overwrite(DATABASE);
    if ($sID) {
        /*$row ||  if ($row ) {
          $ss = intval($row[0]);
          }*/
        mysql__update('usrSavedSearches', 'svs_ID=' . $sID, $cmb);
    } else {
        mysql__insert('usrSavedSearches', $cmb);
        $sID = mysql_insert_id();
    }
    if (mysql_error()) {
        $result['problem'] = 'MySQL error: ' . addslashes(mysql_error()) . ' : search not saved';
    } else {
        // execute function in calling context insertSavedSearch(ssName, ssQuery, wg, ssID)
        $result['execute'] = array('insertSavedSearch', $data['svs_Name'], $data['svs_Query'], $wg, $sID);
        //$onload = "location.replace('actionHandler.php?db=".HEURIST_DBNAME."'); top.HEURIST.search.insertSavedSearch('".slash($data['svs_Name'])."', '".slash($data['svs_Query'])."', ".$wg.", ".$sID.");";
        /*if ($publish) {
          $onload .= " top.location.href = top.location.href + (top.location.href.match(/\?/) ? '&' : '?') + 'pub=1&label=".$label."&sid=".$ss."'+(top.location.href.match(/db=/) ? '' : '&db=".HEURIST_DBNAME."');";
          }else{
          $onload .= ' top.location.href = top.location.href + (top.location.href.match(/\?/) ? \'&\' : \'?\') + \'label='.$label.'&sid='.$ss.'\'+(top.location.href.match(/db=/) ? \'\' : \'&db='.HEURIST_DBNAME.'\');';
          }*/
    }
    return $result;
}
function do_fix_dupe()
{
    $master_rec_id = $_SESSION['master_rec_id'];
    $master_details = $_SESSION['master_details'];
    unset($_SESSION['master_details']);
    //clear master_details so we don't re-enter this code
    unset($_SESSION['master_rec_id']);
    $_SESSION['finished_merge'] = 1;
    // set state variable for next loop
    $dup_rec_ids = array();
    if (in_array($master_rec_id, explode(',', $_REQUEST['bib_ids']))) {
        $dup_rec_ids = array_diff(explode(',', $_REQUEST['bib_ids']), array($master_rec_id));
    }
    $dup_rec_list = '(' . join(',', $dup_rec_ids) . ')';
    $add_dt_ids = array();
    // array of detail ids to insert for the master record grouped by detail type is
    $update_dt_ids = array();
    // array of detail ids to get value for updating the master record
    $keep_dt_ids = array();
    // array of master record repeatable detail ids to keep grouped by detail type id- used to find master details to remove
    //parse form data
    foreach ($_REQUEST as $key => $value) {
        preg_match('/(add|update|keep)(\\d+)/', $key, $matches);
        if (!$matches) {
            continue;
        }
        switch (strtolower($matches[1])) {
            case 'add':
                $add_dt_ids[$matches[2]] = $value;
                break;
            case 'update':
                if ($value != "master") {
                    $update_dt_ids[$matches[2]] = $value;
                }
                break;
            case 'keep':
                $keep_dt_ids[$matches[2]] = $value;
                break;
        }
    }
    //   mysql_connection_overwrite("`heuristdb-nyirti`");   //for debug
    mysql_connection_overwrite(DATABASE);
    //    mysql_query('set @suppress_update_trigger:=1'); // shut off update triggers to let us munge the records with out worrying about the archive.
    // set modified on master so the changes will stick  aslo update url if there is one.
    $now = date('Y-m-d H:i:s');
    $pairs = @$_REQUEST['URL'] ? array("rec_URL" => $_REQUEST['URL'], "rec_Modified" => $now) : array("rec_Modified" => $now);
    mysql__update("Records", "rec_ID={$master_rec_id}", $pairs);
    //process keeps - which means find repeatables in master record to delete  all_details - keeps = deletes
    //get array of repeatable detail ids for master
    $master_rep_dt_ids = array();
    $res = mysql_query('select rst_DetailTypeID from defRecStructure where rst_MaxValues != 1 and rst_RecTypeID = ' . $_SESSION['rty_ID']);
    while ($row = mysql_fetch_array($res)) {
        array_push($master_rep_dt_ids, $row[0]);
    }
    $master_rep_detail_ids = array();
    foreach ($master_rep_dt_ids as $rep_dt_id) {
        if (array_key_exists($rep_dt_id, $master_details)) {
            foreach ($master_details[$rep_dt_id] as $detail) {
                array_push($master_rep_detail_ids, $detail['dtl_ID']);
            }
        }
    }
    //get flat array of keep detail ids
    if ($keep_dt_ids && count($keep_dt_ids)) {
        $master_keep_ids = array();
        foreach ($keep_dt_ids as $dt_id => $details) {
            foreach ($details as $detail) {
                array_push($master_keep_ids, $detail);
            }
        }
    }
    //diff the arrays  don't delet yet as the user might be adding an existing value
    $master_delete_dt_ids = array();
    if ($master_rep_detail_ids) {
        $master_delete_dt_ids = array_diff($master_rep_detail_ids, $master_keep_ids);
    }
    //ART HERE   $master_keep_ids
    //FIXME add code to remove any none repeatable extra details
    //for each update
    if ($update_dt_ids) {
        $update_detail = array();
        foreach ($update_dt_ids as $rdt_id => $rd_id) {
            //look up data for detail and
            $update_detail = mysql_fetch_assoc(mysql_query('select * from recDetails where dtl_ID=' . $rd_id));
            // if exist in master details  update val
            if (in_array($rdt_id, array_keys($master_details))) {
                mysql__update("recDetails", "dtl_ID=" . $master_details[$rdt_id][0]['dtl_ID'], array("dtl_Value" => $update_detail['dtl_Value']));
                // else  insert the data as detail for master record
            } else {
                unset($update_detail['dtl_ID']);
                //get rid of the detail id the insert will create a new one.
                $update_detail['dtl_RecID'] = $master_rec_id;
                // set this as a detail of the master record
                mysql__insert('recDetails', $update_detail);
            }
        }
    }
    //process adds
    if ($add_dt_ids) {
        $add_details = array();
        // for each add detail
        foreach ($add_dt_ids as $key => $detail_ids) {
            foreach ($detail_ids as $detail_id) {
                // since adds are only for repeatables check if it exist in delete array ?yes - remove from delete list if there
                if ($key_remove = array_search($detail_id, $master_delete_dt_ids) !== FALSE) {
                    //FIXME need to compare teh value not the dtl_ID (they will always be diff)
                    //remove from array
                    unset($master_delete_dt_ids[$key_remove]);
                } else {
                    //no  then lookup data for detail and insert the data as detail under the master rec id
                    $add_detail = mysql_fetch_assoc(mysql_query('select * from recDetails where dtl_ID=' . $detail_id));
                    unset($add_detail['dtl_ID']);
                    //the id is auto set during insert
                    $add_detail['dtl_RecID'] = $master_rec_id;
                    mysql__insert('recDetails', $add_detail);
                }
            }
        }
    }
    foreach ($dup_rec_ids as $dup_rec_id) {
        //saw FIXME we should be updating the chain of links
        mysql_query('insert into recForwarding (rfw_OldRecID, rfw_NewRecID) values (' . $dup_rec_id . ', ' . $master_rec_id . ')');
        //saw FIXME  we should update the relationship table on both rr_rec_idxxx  fields
    }
    // move dup bookmarks and tags to master unless they are already there
    //get bookmarkid =>userid for bookmarks of master record
    $master_bkm_UGrpIDs = mysql__select_assoc('usrBookmarks', 'bkm_ID', 'bkm_UGrpID', 'bkm_recID = ' . $master_rec_id);
    //get kwd_ids for  all bookmarks of master record
    $master_tag_ids = mysql__select_array('usrRecTagLinks', 'rtl_TagID', 'rtl_RecID = ' . $master_rec_id);
    //get bookmarkid => userid of bookmarks for dup records
    $dup_bkm_UGrpIDs = mysql__select_assoc('usrBookmarks', 'bkm_ID', 'bkm_UGrpID', 'bkm_recID in' . $dup_rec_list);
    // if dup userid already has a bookmark on master record then add dup bkm_ID to delete_bkm_IDs_list else add to  update_bkm_IDs
    $update_bkm_IDs = array();
    $delete_bkm_IDs = array();
    $dup_delete_bkm_ID_to_master_bkm_id = array();
    //for every user or group that bookmarks a dup record if it already bookmarks the master then mark it for deletion
    // otherwise mark it for update to point to the master record
    foreach ($dup_bkm_UGrpIDs as $dup_bkm_ID => $dup_bkm_UGrpID) {
        if (count(@$master_bkm_UGrpIDs) && ($matching_master_bkm_ID = array_search($dup_bkm_UGrpID, $master_bkm_UGrpIDs))) {
            array_push($delete_bkm_IDs, $dup_bkm_ID);
            $dup_delete_bkm_ID_to_master_bkm_id[$dup_bkm_ID] = $matching_master_bkm_ID;
        } else {
            array_push($update_bkm_IDs, $dup_bkm_ID);
            $master_bkm_UGrpIDs[$dup_bkm_ID] = $dup_bkm_UGrpID;
        }
    }
    //move duplicate record bookmarks for users without bookmarks on the master record
    $update_bkm_IDs_list = '(' . join(',', $update_bkm_IDs) . ")";
    $delete_bkm_IDs_list = '(' . join(',', $delete_bkm_IDs) . ")";
    if (strlen($update_bkm_IDs_list) > 2) {
        // update the bookmarks and tags that are not in the master
        mysql_query('update usrBookmarks set bkm_recID=' . $master_rec_id . ' where bkm_ID in ' . $update_bkm_IDs_list);
        //        mysql_query('update usrRecTagLinks set rtl_RecID='.$master_rec_id.' where kwl_pers_id in '.$update_bkm_IDs_list);
    }
    // process to be deleted dup bookmarks
    foreach ($delete_bkm_IDs as $delete_dup_bkm_ID) {
        //copy soon to be deleted dup bookmark data to master record bookmark  by concat notes and pwd_reminder, max of ratings and copy zotero if non existant
        $master_bkm_ID = @$dup_delete_bkm_ID_to_master_bkm_id[$delete_dup_bkm_ID];
        $res1 = mysql_query('select * from usrBookmarks where bkm_ID=' . $master_bkm_ID);
        $res2 = mysql_query('select * from usrBookmarks where bkm_ID=' . $delete_dup_bkm_ID);
        if (!($res1 && $res2)) {
            continue;
        }
        $master_pers_record = mysql_fetch_assoc($res1);
        $delete_dup_pers_record = mysql_fetch_assoc($res2);
        //        $master_pers_record['pers_notes'] .= $delete_dup_pers_record['pers_notes'];
        $master_pers_record['bkm_PwdReminder'] .= "; " . $delete_dup_pers_record['bkm_PwdReminder'];
        $master_pers_record['bkm_Rating'] = max($master_pers_record['bkm_Rating'], $delete_dup_pers_record['bkm_Rating']);
        if (!$master_pers_record['bkm_ZoteroID']) {
            $master_pers_record['bkm_ZoteroID'] = $delete_dup_pers_record['bkm_ZoteroID'];
        }
        unset($master_pers_record['bkm_ID']);
        mysql__update('usrBookmarks', 'bkm_ID=' . $master_bkm_ID, $master_pers_record);
    }
    //for every delete dup tag link whoses tag id is not already linked to the master record change the record id to master
    //get tag links for the soon to be deleted dup records
    $delete_dup_rtl_ids = mysql__select_assoc('usrRecTagLinks', 'rtl_ID', 'rtl_TagID', 'rtl_RecID in' . $dup_rec_list);
    foreach ($delete_dup_rtl_ids as $rtl_ID => $tag_id) {
        if (count($master_tag_ids) && array_search($tag_id, $master_tag_ids)) {
            //if it's already linked to the master delete it
            mysql_query('delete from usrRecTagLinks where rtl_ID = ' . $rtl_ID);
            //FIXME add error code
        } else {
            // otherwise point it to the master record
            mysql_query('update usrRecTagLinks set rtl_RecID=' . $master_rec_id . ', where rtl_ID = ' . $rtl_ID);
            array_push($master_tag_ids, $tag_id);
            // add to the array of tagids already on the master record
        }
    }
    // move reminders to master
    mysql_query('update usrReminders set rem_RecID=' . $master_rec_id . ' where rem_RecID in ' . $dup_rec_list);
    //?FIXME  do we need to check reminders like we checked usrBookmarks
    //delete master details
    if ($master_delete_dt_ids && count($master_delete_dt_ids)) {
        $master_detail_delete_list = '(' . join(',', $master_delete_dt_ids) . ')';
        mysql_query('delete from recDetails where dtl_ID in ' . $master_detail_delete_list);
        //FIXME add error code
    }
    //delete dup details
    mysql_query('delete from recDetails where dtl_RecID in ' . $dup_rec_list);
    //delete dup usrBookmarks
    if (strlen($delete_bkm_IDs_list) > 2) {
        mysql_query('delete from usrBookmarks where bkm_ID in ' . $delete_bkm_IDs_list);
    }
    // move dup record pointers to master record
    mysql_query('update recDetails left join defDetailTypes on dty_ID=dtl_DetailTypeID set dtl_Value=' . $master_rec_id . ' where dtl_Value in ' . $dup_rec_list . ' and dty_Type="resource"');
    //delete dups
    mysql_query('delete from Records where rec_ID in ' . $dup_rec_list);
    //delete unwanted details in master
    //if ($master_delete_dt_ids && $master_delete_dt_ids[0]){
    //    $master_delete_dt_ids_list = '('.join(',',$master_delete_dt_ids). ')' ;
    //    mysql_query('delete from recDetails where dtl_ID in '.$master_delete_dt_ids_list);
    // }
    //try to get the record to update title and hash
    // calculate title, do an update
    $type = $_SESSION['rty_ID'];
    $mask = mysql__select_array("defRecTypes", "rty_TitleMask", "rty_ID=" . $type);
    if ($mask && count($mask) > 0) {
        $mask = $mask[0];
        $title = fill_title_mask($mask, $master_rec_id, $type);
        if ($title) {
            mysql_query("update Records set rec_Title = '" . mysql_real_escape_string($title) . "' where rec_ID = {$master_rec_id}");
        }
    }
    mysql_query('update Records set rec_Hash = hhash(rec_ID) where rec_ID=' . $master_rec_id);
    header('Location: combineDuplicateRecords.php?db=' . HEURIST_DBNAME . '&bib_ids=' . $_REQUEST['bib_ids']);
}