function dbFlexigrid($innerFunction, $headers = TRUE)
{
    $userid = getUserID();
    $profilingID = beginProfilingEntry(array("activity" => "database", "userid" => $userid));
    $page = getSoft($_REQUEST, "page", -1);
    //flexigrid required
    $rp = getSoft($_REQUEST, "rp", -1);
    //flexigrid required: results per page
    if (!is_numeric($page) || $page <= 0) {
        $page = 1;
    }
    if (!is_numeric($rp) || $rp < 0) {
        $rp = 1;
    }
    // $rp == 0 means you just want the count, it is not a real interface with flexigrid
    $sortname = trim(getSoft($_REQUEST, "sortname", NULL));
    if ($sortname == "undefined") {
        $sortname = NULL;
    }
    $sortorder = trim(getSoft($_REQUEST, "sortorder", ""));
    // not yet utilized: sortname, sortorder, qtype, query
    if (strtoupper($sortorder) != "ASC") {
        $sortorder = "DESC";
    }
    global $db_query_info;
    $result = $innerFunction(" LIMIT " . ($page - 1) * $rp . ", " . $rp . " ", $sortname, $sortorder);
    if ($headers) {
        header("Expires: Mon, 26 Jul 1997 05:00:00 GMT");
        header("Last-Modified: " . gmdate("D, d M Y H:i:s") . "GMT");
        header("Cache-Control: no-cache, must-revalidate");
        header("Pragma: no-cache");
        header("Content-type: text/x-json");
    }
    $activity = "database-";
    if (array_key_exists('type', $db_query_info)) {
        $activity .= $db_query_info['type'];
        unset($db_query_info['type']);
    }
    if (is_array($result)) {
        // success case
        $result['page'] = $page;
        $db_query_info['result'] = 'success';
        endProfilingEntry($profilingID, array("activity" => $activity, "meta" => $db_query_info));
        return json_encode($result);
    } else {
        $db_query_info['result'] = 'error';
        endProfilingEntry($profilingID, array("activity" => $activity, "meta" => $db_query_info));
        return json_encode("Error: " . $result);
        // failure case; just sending a string.
    }
}
function run_submission($post)
{
    /**************************************************
        part 0 : initialization and checking that a valid problem is selected 
      ************************************************/
    global $logRow, $beginstamp, $userid, $userinput, $meta, $wpdb, $inputInUse, $facultative, $usertni, $mainProfilingID, $slug, $log_it, $appendix;
    $beginstamp = time();
    $logRow = FALSE;
    $meta = array();
    if ($log_it) {
        $mainProfilingID = beginProfilingEntry(array("activity" => "submit-code"));
    }
    /*if ($_SERVER['REQUEST_METHOD'] != 'POST')
      return merror('', 'HTTP mangling: method "' . $_SERVER['REQUEST_METHOD'] .
      '" was requested instead of "POST"', 'suppress');*/
    if (count($post) == 0) {
        return merror('', 'HTTP mangling: request contained no data', 'suppress');
    }
    if (strlen(print_r($post, TRUE)) > POSTLIMIT) {
        pyboxlog("submit.php got too many bytes of data:" . strlen(print_r($post, TRUE)));
        return mfail(sprintf(__t('Submitted data (program and/or test input) ' . 'too large. Reduce size or <a href = "%s">' . 'run at home</a>.'), cscurl('install')));
    }
    $id = getSoft($post, "pyId", "EMPTY");
    $usercode = tabs_to_spaces(3, getSoft($post, "usercode" . $id, -1));
    if (!is_string($usercode)) {
        return merror("", "No usercode" . $id . "!" . print_r($post, TRUE));
    }
    $usercode = preg_replace('|\\xc2\\xa0|', ' ', $usercode);
    // nbsp
    $userinput = getSoft($post, "userinput", "");
    $userinput = preg_replace('|\\xc2\\xa0|', ' ', $userinput);
    //nbsp
    $hash = $post["hash"];
    //$graderArgsString = safeDereference("@file:" . $hash, 'hashes');
    //if (!is_string($graderArgsString))
    //  return merror("", "PyBox error: problem hash " . $hash . " not found.");
    /**************************************************
        part 1 : set global variables, build skeleton log row; quit upon justsave.
      ************************************************/
    //$problemArgs = multilineToAssociative($graderArgsString);
    //  foreach ($problemArgs as $key=>$value)
    //  $problemArgs[$key] = stripcslashes($value);
    $problemArgs = $wpdb->get_var($wpdb->prepare("\nSELECT graderArgs from " . $wpdb->prefix . "pb_problems WHERE hash = %s", $hash));
    if ($problemArgs === NULL) {
        return merror("", sprintf(__t("Pybox error: problem hash %s not found. " . "Try reloading the page."), $hash));
    }
    $problemArgs = json_decode($problemArgs, TRUE);
    //  if ($problemArgs != $problemArgsNew)
    //  pyboxlog("different: " . var_export($problemArgs, TRUE)
    // . var_export($problemArgsNew, TRUE));
    //else
    //  pyboxlog("same", TRUE);
    $re = '/^(' . implode("|", array_keys(optionsAndDefaults())) . ')([0-9]*)$/';
    $problemOptions = optionsAndDefaults();
    $subproblemOptions = array();
    foreach ($problemArgs as $key => $value) {
        $match = preg_match($re, $key, $matches);
        if ($match == 0) {
            return merror("", "PyBox error: unknown option " . $key);
        }
        if ($matches[2] == "") {
            $problemOptions[$matches[1]] = $value;
        } else {
            if (!array_key_exists($matches[2], $subproblemOptions)) {
                $subproblemOptions[$matches[2]] = array();
            }
            $subproblemOptions[$matches[2]][$matches[1]] = $value;
        }
    }
    foreach ($subproblemOptions as $index => $spo) {
        foreach ($problemOptions as $option => $value) {
            if (!array_key_exists($option, $spo)) {
                $subproblemOptions[$index][$option] = $value;
            }
        }
    }
    $inputInUse = isSoft($post, "inputInUse", "Y");
    /*  var_dump( $post, TRUE);
      echo "eq: " .(($post["inputInUse"] === "Y") ? "T":"F");
      echo "inputinuse: " . ($inputInUse ? "T":"F");*/
    if ($inputInUse && !isSoft($problemOptions, "allowinput", "Y")) {
        return merror("", "Pybox error: input not actually allowed");
    }
    $facultative = isSoft($problemOptions, "facultative", "Y") || $inputInUse;
    $usertni = isSoft($problemOptions, "usertni", "Y");
    $userid = is_user_logged_in() ? wp_get_current_user()->ID : -1;
    $meta['userid'] = $userid;
    $meta['problem'] = getSoft($problemArgs, 'slug', $hash);
    $slug = getSoft($problemArgs, 'slug', NULL);
    //most of submit logging preparation. quitting earlier => not logged in DB
    if ($log_it and !isSoft($problemOptions, "nolog", "Y")) {
        $postmisc = $post;
        unset($postmisc['usercode' . $id]);
        unset($postmisc['userinput']);
        unset($postmisc['hash']);
        $logRow = array('beginstamp' => date('Y-m-d H:i:s', $beginstamp), 'usercode' => $usercode, 'hash' => $hash, 'postmisc' => print_r($postmisc, TRUE), 'problem' => $slug, 'ipaddress' => $_SERVER['REMOTE_ADDR'], 'referer' => $_SERVER['HTTP_REFERER']);
        if ($inputInUse) {
            $logRow['userinput'] = $userinput;
        }
        $logRow['userid'] = $userid;
        //if ($logRow['problem']===NULL)
        //pyboxlog('nameless problem that is not read-only!', TRUE);
    }
    // old feature:
    //  $justsave = array_key_exists('justsave', $post);
    //if ($justsave)
    //  return msave();
    /**************************************************
        part 2 : grading
      ************************************************/
    if ($problemOptions['taboo'] != FALSE) {
        $taboo = explode(",", $problemOptions['taboo']);
        foreach ($taboo as $t) {
            $p = strpos($t, "|");
            if ($p === FALSE) {
                $regex = $t;
                $display = $t;
            } else {
                $display = substr($t, 0, $p);
                $regex = substr($t, $p + 1);
            }
            $match = preg_match("#.*" . trim($regex) . ".*#", $usercode);
            if ($match != 0) {
                return mfail(sprintf(__t("You cannot use %s in this exercise."), "<code>" . trim($display) . "</code>"));
            }
        }
    }
    if ($problemOptions["maxeditdistance"] != FALSE) {
        $k = $problemOptions["maxeditdistance"];
        $S = preg_replace('/\\s+/', '', $usercode);
        $T = preg_replace('/\\s+/', '', $problemOptions["originalcode"]);
        $s = strlen($S);
        $t = strlen($T);
        $msg = sprintf(__t("You are only allowed to change at most %s " . "characters compared to the original version " . "of the code."), $k);
        if (abs($s - $t) > 2 * $k + 5) {
            return mfail($msg) . " " . sprintf(__t("You changed %s or more."), 2 * $k + 5);
        } else {
            $DP = array_fill(0, $s + 1, NULL);
            for ($i = 0; $i <= $s; $i++) {
                $DP[$i] = array_fill(0, $t + 1, NULL);
            }
            for ($i = 0; $i <= $s; $i++) {
                for ($j = 0; $j <= $t; $j++) {
                    if ($i == 0 || $j == 0) {
                        $DP[$i][$j] = $i + $j;
                    } else {
                        $DP[$i][$j] = $DP[$i - 1][$j - 1];
                        if ($S[$i - 1] != $T[$j - 1]) {
                            $DP[$i][$j]++;
                        }
                        $DP[$i][$j] = min($DP[$i][$j], 1 + min($DP[$i][$j - 1], $DP[$i - 1][$j]));
                    }
                }
            }
            if ($DP[$s][$t] > 0 + $k) {
                return mfail($msg . " " . sprintf(__t("You changed %s."), $DP[$s][$t]));
            }
        }
    }
    /*************** done preprocessing source code,
       time to execute some things ************************/
    if ($inputInUse && $slug != 'console') {
        global $usertni;
        $appendix = '<div class="testing-warning">';
        if ($usertni) {
            $appendix .= __t('Note: ran with user tests.');
        } else {
            $appendix .= __t('Note: ran with user inputs.');
        }
        if (!isSoft($problemOptions, "facultative", "Y")) {
            $appendix .= ' ' . __t(' Click "Go back to grading" to switch back.');
        } else {
            $appendix .= ' ' . __t(' Click "Hide input box" to switch back.');
        }
    } else {
        $appendix = '';
    }
    if (count($subproblemOptions) == 0) {
        //    if ($problemOptions['grader'] == '*nograder*')
        $subproblemOptions["1"] = $problemOptions;
        //else
        //return merror("", "No test cases found!");
    }
    // $spo: subproblemOptions for the current subproblem
    $tcTotal = 0;
    foreach ($subproblemOptions as $N => $spo) {
        $tcTotal += $spo["repeats"];
    }
    $m = '';
    //the result string, built a bit at a time.
    ksort($subproblemOptions);
    //test case 1, then 2, ...
    $tcCurrent = 0;
    $allCorrect = TRUE;
    ///*********************************************** main grading loop ***/
    foreach ($subproblemOptions as $N => $spo) {
        // spo: subproblemOptions for current subproblem
        for ($i = 0; $i < $spo["repeats"]; $i++) {
            $tcCurrent++;
            if (!$inputInUse && $tcTotal > 1) {
                $m .= "<b>" . sprintf(__t('Results for test case %1$s out of %2$s'), $tcCurrent, $tcTotal) . "</b><br/>";
            }
            try {
                $GLOBALS['pb_translation'] = getSoft($spo, 'translate', NULL);
                $tcOutcome = doGrading($usercode, $spo);
                $GLOBALS['pb_translation'] = NULL;
                $m .= $tcOutcome["message"];
                if ($tcOutcome["result"] == "error") {
                    return merror($m, $tcOutcome["errmsg"]);
                }
                if ($tcOutcome["result"] == "fail" && $spo["haltonwrong"] == "Y") {
                    return mfail($m);
                }
                if ($tcOutcome["result"] == "fail") {
                    $allCorrect = FALSE;
                }
            } catch (PyboxException $e) {
                return merror($m, $e->getMessage());
            }
            if ($inputInUse) {
                break;
            }
        }
        if ($inputInUse) {
            break;
        }
    }
    return $allCorrect ? mpass($m) : mfail($m);
}