function cscircles_students_page()
{
    echo "<div class='wrap'>\n<h2>Manage CS Circles Students</h2>";
    if (isSoft($_REQUEST, 'submitted', 'true')) {
        $d = 0;
        $h = 0;
        $newhide = "";
        $newnicks = array();
        $newgroups = array();
        foreach ($_REQUEST as $key => $val) {
            if (substr($key, 0, 1) == 's' && is_numeric(substr($key, 1))) {
                $id = substr($key, 1);
                if ($val == 'hide') {
                    if ($newhide != '') {
                        $newhide .= ",";
                    }
                    $newhide .= substr($key, 1);
                    $h++;
                } else {
                    if ($val == 'remove') {
                        if (strtolower(get_user_meta(substr($key, 1), 'pbguru', true)) == strtolower(wp_get_current_user()->user_login)) {
                            update_user_meta(substr($key, 1), 'pbguru', '');
                        }
                        $d++;
                    }
                }
            }
            if (substr($key, 0, 1) == 'n' && is_numeric(substr($key, 1))) {
                $id = substr($key, 1);
                $nick = $val;
                $nick = preg_replace('_[<>&\\"\\\\]_', "", trim($nick));
                if ($nick != '') {
                    $newnicks[$id] = $nick;
                }
            }
            if (substr($key, 0, 1) == 'g' && is_numeric(substr($key, 1))) {
                $id = substr($key, 1);
                $group = $val;
                $group = preg_replace('_[<>&\\"\\\\]_', "", trim($group));
                if ($group != '') {
                    $newgroups[$id] = $group;
                }
            }
        }
        update_user_meta(wp_get_current_user()->ID, 'pb_hidestudents', $newhide);
        update_user_meta(wp_get_current_user()->ID, 'pb_studentnicks', json_encode($newnicks));
        update_user_meta(wp_get_current_user()->ID, 'pb_studentgroups', json_encode($newgroups));
        echo "<div class='updated'>Deleted {$d} students. You have {$h} hidden students.</div>";
    }
    echo "<script type='text/javascript'>classlist = function() {\n var classes={};\n jQuery('.grouplabel').each(function(i, elt) {classes[elt.value] = true;});\n var keys = [];\n for(var key in classes) {\n   if (classes.hasOwnProperty(key)) {\n     keys.push(key);\n   }\n }\n keys.sort();\n jQuery('#classlist-select').html('');\n jQuery('#classlist-select').append(new Option('', ''));\n jQuery('#classlist-select').append(new Option('<show all students>', '<show all students>'));\n for (var i = 0; i < keys.length; i++) {\n   if (keys[i] != '')\n     jQuery('#classlist-select').append(new Option(keys[i], keys[i]));\n } \n}\nselectClass = function() {\n  theclass = jQuery('#classlist-select')[0].value;\n  jQuery('.student-row').each(function(i, row){\n    if (jQuery(row).find('.grouplabel')[0].value == theclass || theclass == '<show all students>')\n     jQuery(jQuery(row).find('.unhide')[0]).prop('checked',true);\n    else\n     jQuery(jQuery(row).find('.hide')[0]).prop('checked',true);\n  });\n}\njQuery(classlist); // call once on load\n</script>";
    echo "<p>Students who are hidden or removed won't show up\non the Progress page. You still have access to \nmessages between you and them in the Mail page histories.</p>\n\n<p>If you hide a student, you can unhide them later.</p>\n<p>If you remove a student, their guru is cleared. They would have to re-add you if you want them back.</p>\n<p>Students are listed in order of registration on CS Circles (earliest first).</p>\n<!--<p>Nicknames are optional. Otherwise, students appear by their e-mail and/or userid.</p>-->\n";
    if (count(getStudents(true)) == 0) {
        echo "<div class='error'>You have no students. This page will disappear, and reappear if you get more students.</div>";
    } else {
        echo "<form method='get' action='users.php'>\n   <input type='hidden' name='page' value='cscircles-students'>\n   <input type='hidden' name='submitted' value='true'>\n<table style='text-align:center'><tr><th>Unhidden</th><th>Hidden</th><th>Remove</th><th>Username</th><th>Name</th><th>E-mail</th><th>Optional nickname <br>(appears just to you, throughout site)</th><th>Optional class/section label</th></tr>";
        $hidden = get_user_meta(wp_get_current_user()->ID, 'pb_hidestudents', true);
        $nicks = json_decode(get_user_meta(wp_get_current_user()->ID, 'pb_studentnicks', true), true);
        $groups = json_decode(get_user_meta(wp_get_current_user()->ID, 'pb_studentgroups', true), true);
        if (!is_array($nicks)) {
            $nicks = array();
        }
        if (!is_array($groups)) {
            $groups = array();
        }
        if ($hidden == '') {
            $hidden = '';
        }
        $hidden = explode(",", $hidden);
        foreach (getStudents(true) as $index => $id) {
            $user = get_userdata($id);
            $hid = in_array($id, $hidden);
            $c1 = $hid ? "" : "checked='true'";
            $c2 = $hid ? "checked='true'" : "";
            $nick = getSoft($nicks, $id, '');
            $group = getSoft($groups, $id, '');
            echo "<tr class='student-row'>\n<td><input type='radio' class='unhide' name='s{$id}' {$c1} value='unhide'/></td>\n<td><input type='radio' class='hide' name='s{$id}' {$c2} value='hide'/></td>\n<td><input type='radio' name='s{$id}' value='remove'/></td>\n<td>{$user->user_login}</td>\n<td>{$user->user_firstname} {$user->user_lastname}</td>\n<td>{$user->user_email}</td>\n<td><input style='width:100%' type='text' name = 'n{$id}' value=\"{$nick}\"></td>\n<td><input style='width:100%' type='text' class = 'grouplabel' name = 'g{$id}' value=\"{$group}\" onkeyup=\"javascript:classlist()\" onchange=\"javascript:classlist()\"></td>\n</tr>";
        }
        echo "<tr id='classlist-tr'>\n   <td colspan='7' style='text-align:right'>\n   <div id='classlist-label'>Show a class and hide all other students? (Select and press <b>Submit</b> to activate.)</div>\n   </td>\n   <td style='text-align:left'><select id='classlist-select' onchange='javascript:selectClass()'></select></td></tr>";
        echo "</table>\n   <button class='button-primary' id='submit'>Submit</button></form>";
        echo "</div>";
    }
}
if ($problem_info === NULL) {
    header('HTTP/1.1 404 Not Found');
    return;
}
$message = stripcslashes($_POST["message"]);
$noreply = getSoft($_POST, 'noreply', 'false');
if ($source == 1) {
    //inline help form
    $guru_login = get_the_author_meta('pbguru', get_current_user_id());
    // '' if does not exist
    $guru = get_user_by('login', $guru_login);
    // FALSE if does not exist
    $code = stripcslashes($_POST["code"]);
    $message .= "\n===\n" . __t("The user sent this code with the message:") . "\n===\n" . $code;
    echo send($problem_info, getUserID(), isSoft($_POST, 'recipient', '1') ? $guru->ID : 0, getUserID(), $slug, $message, $noreply);
} elseif ($source == 2) {
    //mail page
    $id = $_POST['id'];
    $guru_login = get_the_author_meta('pbguru', $id);
    // '' if does not exist
    $guru = get_user_by('login', $guru_login);
    // FALSE if does not exist
    if (userIsAdmin() || userIsAssistant() || getUserID() == $guru->ID) {
        // from {guru or CSC Asst.} to student
        echo send($problem_info, userIsAdmin() ? 0 : getUserID(), $id, $id, $slug, $message, $noreply);
    } elseif ($id == getUserID()) {
        // from student to {guru or CSC Asst.}
        echo send($problem_info, $id, isSoft($_POST, 'recipient', '1') ? $guru->ID : 0, $id, $slug, $message, $noreply);
    }
}
// end of file!
$cached_result = NULL;
$basehash = md5($_REQUEST['user_script'] . "\t\t\t" . $_REQUEST['raw_input_json']) . '-viz';
$versionedhash = $basehash . VIZ_VERSION;
$dontCache = strstr($_REQUEST['user_script'], 'random') !== FALSE;
if (!$dontCache) {
    // don't cache if randomized
    $the_count = $wpdb->get_var($wpdb->prepare("SELECT count(1) FROM {$wpdb->prefix}pb_submissions\nWHERE hash LIKE %s LIMIT 4", $basehash . '%'));
    $cached_result = $wpdb->get_var($wpdb->prepare("SELECT result FROM {$wpdb->prefix}pb_submissions\nWHERE hash = %s AND result is NOT NULL LIMIT 1", $versionedhash));
    // if cached_result is NULL, we still have to compute it anyway
}
/************* do logging *************/
// save things to build up the cache count
// save things sent to the real visualizer (not the iframe)
// but once something is iframed 5 times we don't need to log it any more
if ($cached_result === NULL || !isSoft($_REQUEST, "iframe_mode", "Y")) {
    $logRow = array('beginstamp' => date('Y-m-d H:i:s', time()), 'usercode' => $_REQUEST['user_script'], 'userinput' => $_REQUEST['raw_input_json'], 'hash' => $versionedhash, 'problem' => isSoft($_REQUEST, "iframe_mode", "Y") ? 'visualizer-iframe' : 'visualizer', 'ipaddress' => $_SERVER['REMOTE_ADDR'], 'referer' => $_SERVER['HTTP_REFERER'], 'userid' => is_user_logged_in() ? wp_get_current_user()->ID : -1);
    $table_name = $wpdb->prefix . "pb_submissions";
    $wpdb->insert($table_name, $logRow);
    $logid = $wpdb->insert_id;
}
/************* actually execute the visualizer if necessary *************/
if ($cached_result !== NULL) {
    echo $cached_result;
    exit;
}
$descriptorspec = array(0 => array("pipe", "r"), 1 => array("pipe", "w"), 2 => array("pipe", "w"));
$command = PSAFEEXEC . " --env_vars PY --gid 1000 --uidplus 10000 --cpu 5 --mem 100000 --clock 7 --chroot_dir " . PJAIL . " --exec_dir /static/OnlinePythonTutor3-cemc/ --exec /bin/python3 -S -u csc_exec.py -";
$process = proc_open($command, $descriptorspec, $pipes);
if (is_resource($process)) {
    fwrite($pipes[0], json_encode($_REQUEST));
    fclose($pipes[0]);
function list_pybox_pages($options, $content)
{
    $out = get_pages(array('parent' => 0, 'status' => 'publish'));
    $links = array();
    global $polylang;
    foreach ($out as $page) {
        if ($polylang->model->get_translation('post', $page->ID, 'en') == $page->ID) {
            //english only
            $links[] = array("url" => get_page_link($page->ID), "title" => $page->post_title);
            if (isSoft($_GET, 'export', 'Y')) {
                $p = get_page($page->ID);
                $slug = $p->post_name;
                $f = fopen(PEXPORT . $slug . ".txt", 'w');
                fwrite($f, 'Title: ' . $p->post_title . "\n\nContent:\n\n");
                fwrite($f, translator_template($p->post_content));
                fclose($f);
            } elseif (isSoft($_GET, 'export', 'raw')) {
                $p = get_page($page->ID);
                $slug = $p->post_name;
                $f = fopen(PEXPORT . $slug . ".txt", 'w');
                fwrite($f, 'Title: ' . $p->post_title . "\n\nContent:\n\n");
                fwrite($f, $p->post_content);
                fclose($f);
            }
        }
    }
    $user_id = get_current_user_id();
    $current_user = wp_get_current_user();
    $profile_url = get_edit_profile_url($user_id);
    if ($user_id) {
        $links[] = array("url" => $profile_url, "title" => __t('Edit my profile'));
        $links[] = array("url" => wp_logout_url(), 'title' => __t('Log out now'));
    } else {
        $links[] = array("url" => wp_logout_url(), 'title' => __t('Log in or create a new account'));
    }
    foreach ($links as $i => $page) {
        $s = $page['title'];
        $m = preg_match('/^([0-9]+)([A-Z])?\\: (.*)$/', $s, $matches);
        if ($m <= 0) {
            $k = '9999---' . $s;
        } else {
            $sub = $matches[2];
            if ($sub == "") {
                $sub = "-";
            }
            $k = sprintf("%04d", $matches[1]) . '-' . $sub . '-' . $matches[3];
        }
        $links[$i]['key'] = $k;
    }
    //  print_r($links);
    function cmp($p1, $p2)
    {
        return strcmp($p1['key'], $p2['key']);
    }
    usort($links, 'cmp');
    echo '<ul>';
    foreach ($links as $page) {
        echo "<li><a class='open-same-window' href='" . $page['url'] . "'>" . $page['title'] . "</a></li>";
    }
    echo '</ul>';
}
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);
}
$r .= '[';
if ($row['type'] == 'short answer') {
    $codename = 'pyShort';
} else {
    if ($row['type'] == 'multiple choice') {
        $codename = 'pyMulti';
    } else {
        if ($row['type'] == 'multichoice scramble') {
            $codename = 'pyMultiScramble';
        } else {
            if ($row['type'] == 'scramble') {
                $codename = 'pyScramble';
                unset($args['scramble']);
            } else {
                if ($row['type'] == 'code') {
                    if (isSoft($args, 'pyexample', 'Y')) {
                        $codename = 'pyExample';
                        unset($args['pyexample']);
                    } else {
                        $codename = 'pyBox';
                    }
                } else {
                    $codename = 'py??' . $row['type'] . '??';
                }
            }
        }
    }
}
$r .= $codename;
foreach ($args as $field => $value) {
    $r .= "\n";
    }
    //$errnorm = normalize($errmsg);
    global $hintage;
    foreach ($hintage as $patt => $repl) {
        if (preg_match("" . $patt . "", $errmsg, $matches)) {
            return preg_replace("" . $patt . "", $repl, $errmsg);
        }
    }
    return NULL;
}
function hintable($errmsg)
{
    $hintables = array("RuntimeError", "ValueError", "ZeroDivisionError", "AttributeError", "TabError", "IndentationError", "IndexError", "SyntaxError", "UnboundLocalError", "ImportError", "OverflowError", "TypeError", "NameError", "EOFError");
    $t = substr($errmsg, 0, strpos($errmsg, ":"));
    return in_array($t, $hintables);
}
function normalize($errmsg)
{
    $errmsg = preg_replace("_ '.*'( |\$)_", " '' ", $errmsg);
    $errmsg = preg_replace("_\".*\"_", "\"\"", $errmsg);
    $errmsg = preg_replace("_\\d+_", "0", $errmsg);
    $errmsg = preg_replace("_\\b\\w+\\(\\)_", "func()", $errmsg);
    $errmsg = preg_replace("_^TypeError: unsupported operand type\\(s\\) .+\$_", "TypeError: unsupported operand type(s)", $errmsg);
    return $errmsg;
}
// only called from ajax to action-submit, so has $REQUEST["lang"]
if (isSoft($_REQUEST, "lang", "lt_LT")) {
    require_once "plugin-errorhint-lt_LT.php";
} else {
    require_once "plugin-errorhint-en_US.php";
}
function pyUser($options, $content)
{
    if (!is_user_logged_in()) {
        return __t("You must login to view your user page.");
    }
    global $wpdb;
    $user = wp_get_current_user();
    $uid = $user->ID;
    $students = getStudents();
    $cstudents = count($students);
    $problem_table = $wpdb->prefix . "pb_problems";
    $problems = $wpdb->get_results("SELECT * FROM {$problem_table} WHERE facultative = 0 AND lang = '" . currLang2() . "' AND lesson IS NOT NULL ORDER BY lesson ASC, boxid ASC", ARRAY_A);
    $problemsByNumber = array();
    foreach ($problems as $prow) {
        $problemsByNumber[$prow['slug']] = $prow;
    }
    $gp = getSoft($_GET, "problem", "");
    if ($gp != "" && $gp != "console" && !array_key_exists($gp, $problemsByNumber)) {
        echo sprintf(__t("Problem %s not found (at least in current language)"), $gp);
        return;
    }
    if (userIsAdmin() || userIsAssistant() || $cstudents > 0) {
        $preamble = "<div class='progress-selector'>\n       <form method='get'><table style='border:none'><tr><td>" . sprintf(__t("View one of your students? (you have %s)"), $cstudents) . '</td><td>';
        $options = array();
        $options[''] = __t('Show only me');
        $options['all'] = __t('Summary of all my students');
        if (!userIsAdmin()) {
            foreach ($students as $student) {
                $info = get_userdata($student);
                $options[$info->ID] = userString($info->ID);
            }
        }
        if (userIsAdmin()) {
            $preamble .= 'blank: you; "all": all; id#: user (<a href="' . cscurl('allusers') . '">list</a>) <input style = "padding:0px;width:60px" type="text" name="user" value="' . getSoft($_REQUEST, 'user', '') . '">';
        } else {
            $preamble .= optionsHelper($options, 'user');
        }
        $preamble .= '</td></tr><tr><td>';
        $preamble .= __t("Just show submissions for one problem?");
        $options = array();
        $options[''] = __t('Show all');
        $options['console'] = __t('Console');
        foreach ($problems as $problem) {
            if ($problem['type'] == 'code') {
                $options[$problem['slug']] = $problem['publicname'];
            }
        }
        $preamble .= '</td><td>';
        $preamble .= optionsHelper($options, 'problem');
        $preamble .= "</td></tr><tr><td colspan='2' style='text-align:center'><input style='width: 25%' type='submit' value='" . __t('Submit') . "'/></tr></td></table></form></div>";
        echo $preamble;
    }
    $allStudents = isSoft($_GET, 'user', 'all');
    $viewingAsStudent = '' == getSoft($_GET, 'user', '');
    $allProblems = $gp == "";
    if (!$viewingAsStudent) {
        if ($allProblems) {
            $problem_html = "all problems";
        } else {
            if ($gp == 'console') {
                $problem_html = "Console";
            } else {
                $problem_html = "<a href='" . $problemsByNumber[$gp]['url'] . "'>" . $problemsByNumber[$gp]['publicname'] . "</a>";
            }
        }
    }
    if (!$allStudents && array_key_exists('user', $_GET) && $_GET['user'] != '') {
        if (!is_numeric($_GET['user'])) {
            return __t("User id must be numeric.");
        }
        $getuid = (int) $_GET['user'];
        if (userIsAdmin() || userIsAssistant()) {
            if (get_userdata($getuid) === FALSE) {
                return __t("Invalid user id.");
            }
        } else {
            if (!in_array($getuid, $students)) {
                return __t("Invalid user id.");
            }
        }
        $uid = $getuid;
        $user = get_userdata($uid);
        echo "<div class='history-prenote'>" . sprintf(__t("Now viewing %s for "), $problem_html) . userString($uid) . '</div>';
    }
    if ($allStudents) {
        echo "<div class='history-prenote'>" . sprintf(__t("Now viewing %s for all of your students"), $problem_html) . "</div>";
    }
    /***************** end of header ***************/
    $flexigrids = "";
    $completed_table = $wpdb->prefix . "pb_completed";
    if ($allStudents && !$allProblems && $gp != "console") {
        $flexigrids .= niceFlex('perstudent', sprintf(__t("Solutions by my students for %s"), $problemsByNumber[$_GET['problem']]['publicname']), 'problem-summary', 'dbProblemSummary', array('p' => $_GET['problem']));
    }
    $dbparams = array();
    if (getSoft($_GET, 'user', '') != '') {
        $dbparams['user'] = $_GET['user'];
    }
    if (getSoft($_GET, 'problem', '') != '') {
        $dbparams['problemhash'] = $_GET['problem'];
    }
    $flexigrids .= niceFlex('submittedcode', $allProblems ? __t("Submitted code") : sprintf(__t("Submitted code for %s"), $_GET['problem'] == 'console' ? 'Console' : $problemsByNumber[$_GET['problem']]['publicname']), 'entire-history', 'dbEntireHistory', $dbparams);
    $recent = "";
    if (!$allStudents) {
        // queries more than 6 in order to fill out progress table of all problems
        $completed = $wpdb->get_results("SELECT * FROM {$completed_table} WHERE userid = {$uid} ORDER BY time DESC", ARRAY_A);
        $recent .= '<div class="recent"><span class="latest-title">' . __t("Latest problems completed") . ":</span>";
        // but for now we only use 6 entries for "most recently completed" section
        for ($i = 0; $i < count($completed) && $i < 6; $i++) {
            $p = getSoft($problemsByNumber, $completed[$i]['problem'], FALSE);
            if ($p !== FALSE) {
                if (getSoft($_GET, 'user', '') != '') {
                    if ($problemsByNumber[$p['slug']]['type'] == 'code') {
                        $url = '.?user='******'user'] . '&problem=' . $p['slug'];
                    } else {
                        $url = null;
                    }
                } else {
                    $url = $p['url'];
                }
                $recent .= ' <a class="open-same-window problem-completed" ';
                if ($url != null) {
                    $recent .= ' href="' . $url . '" ';
                }
                $recent .= ' title="' . $completed[$i]['time'] . '">' . $p['publicname'] . '</a>';
            } else {
                $recent .= '[' . $completed[$i]['problem'] . ']';
            }
        }
        $recent .= '</div>';
    }
    $submissions_table = $wpdb->prefix . "pb_submissions";
    $studentTable = '';
    if ($allStudents && !userIsAdmin()) {
        $studentList = getStudentList();
        $where = "WHERE userid in {$studentList}";
        if (!$allProblems) {
            $where .= $wpdb->prepare("and problem LIKE %s", $gp);
        }
        // show number of problems each student completed
        $scompleted = $wpdb->get_results("SELECT userid, count(1) as comps from {$completed_table} {$where} GROUP BY userid", OBJECT_K);
        // show number of submissions by each student for this problem
        $ssubmissions = $wpdb->get_results("SELECT userid, count(1) as subs from {$submissions_table} {$where} GROUP BY userid", OBJECT_K);
        $studentTable .= '<div class="history-note">Student listing (click name to drill down)</div>';
        $studentTable .= '<table>';
        foreach (getStudents() as $stu) {
            $studentTable .= '<tr>';
            $studentTable .= '<td>';
            $studentTable .= '<a class="open-same-window" href="?user='******'&problem=' . $gp . '">';
            $studentTable .= userString($stu);
            $studentTable .= '</a></td>';
            $studentTable .= '<td>';
            if ($allProblems) {
                $studentTable .= (array_key_exists($stu, $scompleted) ? $scompleted[$stu]->comps : 0) . ' completed';
            } else {
                $studentTable .= '<img src="' . UFILES . (array_key_exists($stu, $scompleted) ? 'checked' : 'icon') . '.png"/>';
            }
            $studentTable .= '</td>';
            $studentTable .= '<td>';
            $studentTable .= (array_key_exists($stu, $ssubmissions) ? $ssubmissions[$stu]->subs : 0) . ' submissions';
            $studentTable .= '</td>';
            $studentTable .= '</tr>';
        }
        $studentTable .= '</table>';
    }
    $lessons_table = $wpdb->prefix . "pb_lessons";
    $lessons = $wpdb->get_results("SELECT * FROM {$lessons_table} WHERE lang = '" . currLang2() . "'", ARRAY_A);
    $lessonsByNumber = array();
    foreach ($lessons as $lrow) {
        $lessonsByNumber[$lrow['ordering']] = $lrow;
    }
    $overview = '';
    if ($allProblems || !$allStudents) {
        $overview = '<h2 style="margin-top:5px;text-align:center">' . __t('List of all problems') . ' ' . ($allStudents ? __t('(with #completed)') : __t('(with #submissions)')) . '</h2>';
        if (!$viewingAsStudent) {
            $overview .= "<div style='text-align:center'>Click on the <img style='height:1em,width:1em' src='" . UFILES . "/icon.png'> to drill down.</div>";
        }
        $checkIt = array();
        //array from slug to boolean, whether to check the icon
        $showNum = array();
        //array from slug to number, number to display beside each
        if ($allStudents) {
            if (userIsAdmin() || userIsAssistant()) {
                $completed = $wpdb->get_results("SELECT count(userid), problem from {$completed_table} GROUP BY problem", ARRAY_A);
            } else {
                $studentList = getStudentList();
                $completed = $wpdb->get_results("SELECT count(userid), problem from {$completed_table} WHERE userid in {$studentList} GROUP BY problem", ARRAY_A);
            }
            foreach ($completed as $crow) {
                $showNum[$crow['problem']] = $crow['count(userid)'];
            }
        } else {
            $submissions = $wpdb->get_results("SELECT count(1), problem from {$submissions_table} WHERE userid = {$uid} GROUP BY problem", ARRAY_A);
            foreach ($submissions as $srow) {
                $showNum[$srow['problem']] = $srow['count(1)'];
            }
            foreach ($completed as $crow) {
                // this was queried earlier
                $checkIt[$crow['problem']] = TRUE;
            }
        }
        $overview .= '<table style="width:auto;border:none;margin:0px auto;">';
        $lesson = -1;
        $lrow = NULL;
        $llink = "";
        $firstloop = true;
        foreach ($problems as $prow) {
            if ($prow['lesson'] != $lesson) {
                if (!$firstloop) {
                    $overview .= "</td></tr>\n";
                }
                $firstloop = false;
                $overview .= "<tr><td class='lessoninfo'>";
                $lesson = $prow['lesson'];
                $lrow = $lessonsByNumber[$lesson];
                $overview .= '<a class="open-same-window" href="';
                $llink = get_page_link($lrow['id']);
                $overview .= $llink;
                $overview .= '">';
                $overview .= $lrow['number'] . ": " . $lrow['title'];
                $overview .= '</a></td><td>';
            }
            if (!$viewingAsStudent) {
                // drill-down link
                $url = '.?user='******'user'] . '&problem=' . $prow['slug'];
            } else {
                $url = $prow['url'];
            }
            $overview .= '<a class="open-same-window" ';
            if ($url != null) {
                $overview .= ' href="' . $url . '" ';
            }
            $overview .= '>';
            $overview .= '<table class="history-tablette" ><tr class="history-tablette-top"><td>';
            $overview .= '<img style="margin:-10px 0px" title="' . $prow['publicname'] . '" src="' . UFILES . (isSoft($checkIt, $prow['slug'], TRUE) ? 'checked' : 'icon') . '.png"/>';
            $overview .= '</a></td></tr><tr class="history-tablette-bottom"><td>';
            /*      $overview .= '<a class="open-same-window" ';
                  if ($url != null) $overview .= ' href="' . $url . '" ';
                  $overview .= '>';*/
            $overview .= array_key_exists($prow['slug'], $showNum) ? $showNum[$prow['slug']] : '&nbsp;';
            $overview .= '</td></tr></table></a>';
        }
        $overview .= '</table>';
    }
    return "<div class='userpage'>{$flexigrids} {$recent} {$studentTable} {$overview}</div>";
}