function translator_template_helper($matches) { $attr = shortcode_parse_atts($matches[3]); // anything which is not a slug is not an exercise and passes through unchanged. if (!array_key_exists('slug', $attr)) { return $matches[0]; } if (!array_key_exists('title', $attr)) { pyboxlog('[in translator_template_helper] Warning: ' . $attr['slug'] . ' has no title!'); $attr['title'] = ""; } $r = '[pyRecall slug="' . $attr['slug'] . '"'; $to_translate = array('title', 'epilogue', 'right', 'wrong', 'defaultcode'); foreach ($to_translate as $key) { if (array_key_exists($key, $attr)) { $r .= ' ' . $key . '="'; $value = $attr[$key]; $value = str_replace('"', '""', $value); $value = str_replace("\n", '\\n', $value); $r .= $value . '"'; } } $r .= ']' . $matches[5] . '[/pyRecall]'; return $r; }
function pybox_database_install() { pyboxlog("running pybox_database_install"); global $wpdb; // we use dbDelta since it is more compatible with upgrades require_once ABSPATH . 'wp-admin/includes/upgrade.php'; // we create indexes with direct queries since "Duplicate key name" works in our favour $table_name = $wpdb->prefix . "pb_completed"; $sql = "CREATE TABLE " . $table_name . " (\nuserid integer, \nproblem text,\ntime timestamp\n) CHARACTER SET utf8 COLLATE utf8_general_ci;"; dbDelta($sql); $wpdb->query("create index pb_index_problem on " . $wpdb->prefix . "pb_completed (problem (16));"); $wpdb->query("create index pb_index on " . $wpdb->prefix . "pb_completed (userid, problem (16));"); $table_name = $wpdb->prefix . "pb_lessons"; $sql = "CREATE TABLE " . $table_name . " (\nmajor integer,\nminor text,\nordering integer,\ntitle text,\nid integer,\nnumber text,\nlang text,\nPRIMARY KEY (id)\n) CHARACTER SET utf8 COLLATE utf8_general_ci;"; dbDelta($sql); $wpdb->query("create index pb_index on " . $wpdb->prefix . "pb_lessons (lang (2), ordering);"); $table_name = $wpdb->prefix . "pb_problems"; $sql = "CREATE TABLE " . $table_name . " (\npostid integer,\nlesson integer,\nboxid integer,\nslug text,\ntype text,\nfacultative boolean,\nshortcodeArgs text,\ngraderArgs text,\nhash text,\nurl text,\npublicname text,\ncontent text,\nlang text\n) CHARACTER SET utf8 COLLATE utf8_general_ci;"; dbDelta($sql); $wpdb->query("create index pb_index on " . $wpdb->prefix . "pb_problems (hash (32));"); $wpdb->query("create index pb_index_named on " . $wpdb->prefix . "pb_problems (lang (2), slug (16));"); $table_name = $wpdb->prefix . "pb_profiling"; $sql = "CREATE TABLE " . $table_name . " (\nID integer NOT NULL AUTO_INCREMENT,\nactivity text,\nstart datetime,\npreciseStart text,\npreciseEnd text,\nuserid integer,\nduration decimal(20, 10),\ncrossref integer,\nparent integer,\nmeta text,\nprimary key (ID)\n) CHARACTER SET utf8 COLLATE utf8_general_ci;"; dbDelta($sql); $wpdb->query("create index pb_index on " . $wpdb->prefix . "pb_profiling (activity (32), start);"); $table_name = $wpdb->prefix . "pb_mail"; $sql = "CREATE TABLE " . $table_name . " (\nID integer NOT NULL AUTO_INCREMENT,\nustudent integer,\nufrom integer,\nuto integer,\nproblem text,\nbody text,\ntime timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,\nunanswered boolean,\nprimary key (ID)\n) CHARACTER SET utf8 COLLATE utf8_general_ci;"; dbDelta($sql); $wpdb->query("create index pb_index on " . $wpdb->prefix . "pb_mail (uto, unanswered);"); $wpdb->query("create index pb_index_subject on " . $wpdb->prefix . "pb_mail (ustudent, problem (16), ID);"); $wpdb->query("create index pb_index_problem on " . $wpdb->prefix . "pb_mail (problem (16));"); $wpdb->query("create index pb_index_from on " . $wpdb->prefix . "pb_mail (ufrom);"); $table_name = $wpdb->prefix . "pb_submissions"; $sql = "CREATE TABLE " . $table_name . " (\nID INT NOT NULL AUTO_INCREMENT,\nbeginstamp datetime,\nendstamp timestamp, \nuserid integer,\nproblem text,\nhash text,\nusercode text,\nuserinput text,\nresult mediumtext,\nipaddress text,\npostmisc text,\nreferer text,\nPRIMARY KEY (ID)\n) CHARACTER SET utf8 COLLATE utf8_general_ci;"; dbDelta($sql); $wpdb->query("create index pb_index on " . $wpdb->prefix . "pb_submissions (userid, problem (16), beginstamp);"); $wpdb->query("create index pb_index_problem on " . $wpdb->prefix . "pb_submissions (problem (16));"); $wpdb->query("create index pb_hash_result on " . $wpdb->prefix . "pb_submissions (hash (40), result (2));"); }
define('MAX_VIS_CACHED_LEN', 30000); /************* preliminary stuff *************/ header("Content-type: text/plain; charset=utf8"); require_once "include-to-load-wp.php"; foreach ($_REQUEST as $k => $v) { $_REQUEST[$k] = stripslashes($v); } if (!array_key_exists('user_script', $_REQUEST)) { echo "Error, missing inputs"; return; } if (!array_key_exists('raw_input_json', $_REQUEST)) { $_REQUEST['raw_input_json'] = ''; } if (strlen(print_r($_REQUEST, TRUE)) > POSTLIMIT) { pyboxlog("action-optv3.php got too many bytes of data:" . strlen(print_r($_REQUEST, TRUE))); return sprintf(__t('Submitted data (program and/or test input) ' . 'too large. Reduce size or <a href = "%s">' . 'run at home</a>.'), cscurl('install')); } global $wpdb; /************* check for a cached version *************/ $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 *************/
<?php require_once "include-to-load-wp.php"; $message = $_REQUEST["message"]; $millis = $_REQUEST["millis"]; $user_stdin = $_REQUEST["user_stdin"]; $user_script = $_REQUEST["user_script"]; $error = getSoft($_REQUEST, "error", ""); $meta = array('message' => $message, 'user_stdin' => $user_stdin, 'user_script' => $user_script); if ($error != "") { $meta['error'] = $error; if ($message == 'failed') { pyboxlog("Notified of a visualizer error" . "\n" . $error . "\n" . $_SERVER['SERVER_PROTOCOL'] . " " . $_SERVER['HTTP_USER_AGENT'] . " " . $_SERVER["REMOTE_ADDR"] . " " . getUserID() . "\n" . $user_stdin . "\n" . $user_script); } } retroProfilingEntry($millis * 0.001, array('activity' => 'visualize', 'meta' => $meta));
function resendEmails() { global $wpdb; $return; foreach ($wpdb->get_results("SELECT * FROM " . $wpdb->prefix . "pb_mail WHERE ID >= 3818 and ID <= 3835 and uto != 0", ARRAY_A) as $r) { $problem = $r['problem']; $pname = $wpdb->get_var("SELECT publicname FROM " . $wpdb->prefix . "pb_problems WHERE slug like '{$problem}'"); $purl = $wpdb->get_var("SELECT url FROM " . $wpdb->prefix . "pb_problems WHERE slug like '{$problem}'"); $subject = "CS Circles - Message about {$pname}"; $to = get_user_by('id', $r['uto'])->user_email; if ($r['ufrom'] == 0) { $mFrom = '"' . __t("CS Circles Assistant") . '" <' . CSCIRCLES_BOUNCE_EMAIL . '>'; } else { $user = get_user_by('id', $r['ufrom']); $mFrom = '"' . $user->user_login . '" <' . $user->user_email . '>'; } $student = $r['ustudent']; $slug = $problem; $mailref = $r['ID']; $contents = "[Please accept our apologies for the delay in this message, which was caused by a mail daemon problem.]\n\n"; $contents .= $r['body'] . "\n===\n"; $contents .= __t("To send a reply message, please visit") . "\n"; $contents .= cscurl('mail') . "?who={$student}&what={$slug}&which={$mailref}#m"; $contents .= __t("Problem URL:") . " " . $purl . "\n"; $contents .= "[" . __t("Sent by CS Circles") . " " . cscurl("homepage") . "]"; // $contents .= "\n\n" . $to; pyboxlog("Trying to resend message {$mailref}:{$mFrom}|{$to}|{$subject}|{$contents}", TRUE); pb_mail($mFrom, $to, $subject, $contents); pyboxlog("Resent message {$mailref}", TRUE); } }
function pyBoxHandler($options, $content) { //echo "PB[[[".$content."]]]"; // given a shortcode, print out the html for the user, // and save the relevant grader options in a hash file. $id = generateId(); if ($options == FALSE) { $options = array(); } // wordpress does a weird thing where valueless for ($i = 0; array_key_exists($i, $options); $i++) { // attributes map like [0]=>'attname'. $options[$options[$i]] = "Y"; // these lines change it to unset($options[$i]); // 'attname'=>'Y' } $shortcodeOptions = json_encode($options); // this will be put into DB. /// do some cleaning-up and preprocessing of options, and create the problem info for grader if (array_key_exists('translate', $options)) { $GLOBALS['pb_translation'] = $options['translate']; } if (array_key_exists('pyexample', $options)) { setSoft($options, 'grader', '*nograder*'); setSoft($options, 'readonly', 'Y'); setSoft($options, 'hideemptyinput', 'Y'); unset($options['pyexample']); } if (array_key_exists('code', $options)) { // sugar: code is an alias for defaultcode $options["defaultcode"] = $options["code"]; unset($options['code']); } $richreadonly = array_key_exists('richreadonly', $options); // sugar if ($richreadonly) { $options['readonly'] = "Y"; unset($options['richreadonly']); } if (array_key_exists('nograder', $options)) { // syntactic sugar for nograder option if (array_key_exists('grader', $options)) { pyboxlog('Warning: grader overwritten with *nograder*'); } $options["grader"] = "*nograder*"; unset($options['nograder']); } foreach ($options as $optname => $optvalue) { // syntactic sugar for inplace grader if (preg_match('|tests|', $optname) > 0 || preg_match('|precode|', $optname) > 0) { $options["inplace"] = "Y"; } } global $post, $lesson_reg_info; // $lessonNumber is numeric (major) part of lesson number // if lesson_reg_info is set we don't really care about displaying the pybox, // but we'll do things for consistency anyway. NB: when displaying a problem // in a place other than its original page (e.g., mail) this needs ot be fixed $post_title = isset($lesson_reg_info) ? get_the_title($lesson_reg_info['id']) : $post->post_name; if (preg_match('|^(\\d+).*|', $post_title, $matches) == 0) { $lessonNumber = -1; } else { $lessonNumber = $matches[1]; } $inplace = booleanize(getSoft($options, 'inplace', 'N')); $scramble = booleanize(getSoft($options, 'scramble', 'N')); // important booleans used to determine $readonly = booleanize(getSoft($options, 'readonly', 'N')); // other options... get them first $showEditorToggle = booleanize(getSoft($options, 'showeditortoggle', 'N')); unset($options['scramble']); unset($options['readonly']); // don't extract() these! unset($options['inplace']); if ($inplace) { setSoft($options, 'hideemptyoutput', 'Y'); } $defaultValues = array('defaultcode' => FALSE, 'autocommentline' => $lessonNumber > 3 && !($scramble || $readonly), 'console' => 'N', 'rows' => 10, 'allowinput' => $lessonNumber > 5 && !$scramble && !$readonly, 'disablericheditor' => ($lessonNumber > -1 && $lessonNumber < 7 || $scramble || $readonly) && !$richreadonly, 'usertni' => $inplace); foreach ($defaultValues as $key => $value) { if (!array_key_exists($key, $options)) { $options[$key] = $defaultValues[$key]; } } extract($options); $allowinput = booleanize($allowinput); $disablericheditor = booleanize($disablericheditor); $console = booleanize($console); $autocommentline = booleanize($autocommentline); $usertni = booleanize($usertni); $facultative = isset($grader) && $grader == '*nograder*' || $console || $readonly; if ($scramble || $readonly) { $options['nolog'] = 'Y'; } // for grader. note that if they are absent, their default values are 'N' if ($facultative) { $options['facultative'] = 'Y'; } else { unset($options['facultative']); } if ($allowinput) { $options['allowinput'] = 'Y'; } else { unset($options['allowinput']); } if ($scramble) { $options['scramble'] = 'Y'; } // already unset if ($readonly) { $options['readonly'] = 'Y'; } if ($inplace) { $options['inplace'] = 'Y'; } if ($usertni) { $options['usertni'] = 'Y'; } else { unset($options['usertni']); } $cosmeticOptions = array('defaultcode', 'autocommentline', 'console', 'rows', 'disablericheditor', 'scramble', 'readonly', 'showeditortoggle', 'title', 'placeholder'); $copyForGrader = array(); foreach ($options as $optname => $optvalue) { if (!in_array($optname, $cosmeticOptions)) { $copyForGrader[$optname] = $optvalue; } } if (array_key_exists('maxeditdistance', $options)) { $copyForGrader['originalcode'] = $defaultcode; } $optionsJson = json_encode($copyForGrader); $hash = md5($shortcodeOptions . $optionsJson); $slug = getSoft($options, 'slug', 'NULL'); registerPybox($id, $slug, $scramble ? "scramble" : "code", $facultative, getSoft($options, 'title', NULL), $content, $shortcodeOptions, $hash, $optionsJson); if (isMakingDatabases()) { $res = do_short_and_sweetcode($content); // faster db generation with accurate count $GLOBALS['pb_translation'] = NULL; return $res; } /// we've delivered options to the grader. get on with producing html if ($defaultcode === FALSE && $scramble && $solver !== FALSE) { $lines = explode("\n", trim(softSafeDereference($solver))); shuffle($lines); $defaultcode = implode("\n", $lines); } if ($defaultcode !== FALSE) { try { $defaultcode = softSafeDereference($defaultcode); } catch (PyboxException $e) { $GLOBALS['pb_translation'] = NULL; return pberror("PyBox error: defaultcode file " . $defaultcode . " not found."); } $defaultcode = ensureNewlineTerminated($defaultcode); } /// actually start outputting here. part 1: headers and description $r = ''; $readyScripts = ''; $r .= '<form class="pbform" action="#" id="pbform' . $id . '" method="POST">' . "\n"; if ($scramble) { $c = "scramble"; } else { if (debugEnabled()) { $c = "debug"; } else { $c = ""; } } if ($facultative) { $c .= " facultative"; } $r .= "<div class='pybox modeNeutral {$c}' id='pybox{$id}'>\n"; if (!$facultative && !array_key_exists("slug", $options)) { pyboxlog("Hash " . $hash . " not read-only, but needs a slug", TRUE); $r .= slugwarn(); } if ($facultative) { if ($console) { unset($options["title"]); $r .= heading("Console", $options); } else { $r .= heading(__t('Example'), $options); } } else { $r .= checkbox($slug); $r .= heading($scramble ? __t('Scramble Exercise') : __t('Coding Exercise'), $options); //if ($scramble) // $r .= "<b>Note (Dec 13)</b>: scramble exercises are temporarily broken — sorry!<br>"; } $r .= do_short_and_sweetcode($content); //instructions, problem description. process any shortcodes inside. // part 1.5: help box if (!$facultative && !$scramble) { $r .= '<div class="helpOuter" style="display: none;"><div class="helpInner">'; if (!is_user_logged_in()) { $r .= '<div style="text-align: center">' . __t('You need to create an account and log in to ask a question.') . '</div>'; } else { global $wpdb; $guru_login = get_the_author_meta('pbguru', get_current_user_id()); if ($guru_login != '') { $guruid = $wpdb->get_var($wpdb->prepare('SELECT ID from ' . $wpdb->prefix . 'users WHERE user_login = %s', $guru_login)); } $r .= '<div style="text-align: center">'; if ($guru_login != '' and $guruid !== NULL) { $r .= __t('Send a question by e-mail to: '); $r .= "<select class='recipient'>\n<option value='1'>" . __t("My guru") . " ({$guru_login})</option>\n<option value='-1'>" . __t("CS Circles Assistant") . "</option>\n</select></div>"; } else { $r .= __t('Send a question by e-mail to: '); $r .= "<select class='recipient'>\n<option value='-1'>" . __t("CS Circles Assistant") . "</option>\n<option value='0'>" . __t("(No guru specified in your profile)") . "</option>\n</select>"; $r .= '<br/></div>'; } $r .= __t("Enter text for the message below. <i>Be sure to explain where you're stuck and what you've tried so far. Your partial solution code will be automatically included with the message.</i>"); $r .= "<textarea style='font-family: serif'></textarea>"; $r .= "<table class='helpControls'><tr class='wp-core-ui'><td style='width: 50%'><a class='button' onclick='sendMessage({$id},\"{$slug}\")'>" . __t("Send this message") . "</a></td><td style='width: 50%'>\n <a class='button' onclick='helpClick({$id})'>" . __t("Cancel") . "</a></td></tr></table>"; } $r .= '</div></div>'; } /// part 2: code input if ($readonly) { $thecode = trim($defaultcode); $rows = count(explode("\n", $thecode)); } elseif ($console == "Y" && array_key_exists("consolecode", $_GET)) { $thecode = htmlspecialchars(html_entity_decode(stripslashes($_GET["consolecode"]))); $rows = count(explode("\n", $thecode)) + 1; } else { $thecode = $defaultcode; if ($autocommentline) { $thecode .= __t('# delete this comment and enter your code here') . "\n"; } if (array_key_exists('slug', $options) && $scramble === FALSE) { $savedCode = loadMostRecent($options['slug']); if ($savedCode !== NULL) { $thecode = $savedCode; } } } if ($scramble) { $r .= '<ul class="pyscramble" name="pyscramble" id="pyscramble' . $id . '">' . "\n"; foreach (explode("\n", rtrim($thecode)) as $s) { if (strpos($s, 'delete this comment') === FALSE) { // fix an old bug -- got stuck in database $r .= ' <li class="pyscramble">' . (trim($s) == '' ? ' ' : htmlspecialchars(html_entity_decode($s))) . "</li>\n"; } } $r .= "</ul>\n"; $r .= "<input type='hidden' id='usercode{$id}' name='usercode{$id}'/>\n"; } else { // $r .= "<div class='acecontain ace_hide' id='acecontain$id' ><div class='aceinner' id='ace$id'></div></div>"; $px = $rows * 26 + 6; //+6 for border, padding in weird box model $h = $px; if (!$readonly) { $h = max(50, $h); } $h = " style='height: {$h}px;'"; $ro = $readonly ? "readonly='readonly'" : ""; $c = $readonly ? "RO" : "RW"; $p = $readonly ? " style = 'height : {$px}px;' " : ""; $s = $readonly ? "" : "resizy"; //cols=... required for valid html but width actually set by css //height is set explicitly since it's the only way for IE to render the textarea at the correct height $pl = array_key_exists("placeholder", $options) ? "placeholder='" . $options['placeholder'] . "'" : ""; $r .= "<div class='pyboxTextwrap pyboxCodewrap {$c} {$s}' {$h}><textarea wrap='off' name='usercode{$id}' id='usercode{$id}' {$pl} cols=10 rows={$rows} {$ro} {$p} class='pyboxCode {$c}'>\n"; $r .= $thecode; $r .= '</textarea></div>' . "\n"; } // part 2.5 history container $r .= "<div id='pbhistory{$id}' class='flexcontain' style='display:none;'></div>\n"; /// part 3: stdin if ($allowinput) { if ($usertni === TRUE) { $description = __t('Enter testing statements like <tt>print(myfunction("test argument"))</tt> below.'); } else { $description = __t("You may enter input for the program in the box below."); } $r .= '<div name="pyinput" id="pyinput' . $id . '">'; $r .= $description; $r .= '<div class="pyboxTextwrap resizy" style="height: 102px;" ><textarea wrap="off" name="userinput" class="pyboxInput" cols=10 rows=4></textarea></div>'; $r .= '</div>' . "\n"; //cols=10 required for valid html but width actually set by css } /// part 4: controls $tni = $usertni ? 'Y' : 'N'; $actions = array(); if ($allowinput) { $actions['switch'] = array('id' => "switch{$id}", 'value' => 'Input Switch', 'onclick' => "pbInputSwitch({$id},'{$tni}')"); } if (!$disablericheditor) { // $userLikesRich = (!is_user_logged_in()) || ("true"!==get_the_author_meta( 'pbplain', get_current_user_id())); $userLikesRich = TRUE; if ($showEditorToggle || $richreadonly) { $actions['CMtoggle'] = array('value' => __t('Rich editor'), 'id' => "toggleCM{$id}", 'onclick' => "pbToggleCodeMirror({$id})"); } if ($userLikesRich) { $readyScripts .= "jQuery(function(){pbToggleCodeMirror({$id});});"; } } if (!$scramble && !$console && ($lessonNumber >= 4 || $lessonNumber < 0)) { $actions['consolecopy'] = array('value' => __t('Open in console'), 'onclick' => "pbConsoleCopy({$id})"); } if (!$scramble && ($lessonNumber >= 4 || $lessonNumber < 0)) { $actions['visualize'] = array('value' => __t('Visualize'), 'onclick' => "pbVisualize({$id},'{$tni}')"); } if (!$readonly && !$scramble) { //$actions['save'] = array('value'=>'Save without running', 'onclick'=>"pbSave($id)"); if (array_key_exists("slug", $options)) { $historyAction = "historyClick({$id},'{$slug}')"; $actions['history'] = array('value' => __t('History'), 'onclick' => $historyAction); } if (!($readonly === "Y") && $defaultcode != '' && $defaultcode !== FALSE) { // prepare the string for javaScript enclosure // we put in single-quotes, rather than json's default double quotes, for compatibility with // our $button usage $dc = substr(json_encode(htmlspecialchars_decode($defaultcode, ENT_QUOTES), JSON_HEX_APOS), 1, -1); $r .= "<input type='hidden' id='defaultCode{$id}' value='{$dc}'></input>\n"; $actions['default'] = array('value' => __t('Reset code to default'), 'onclick' => "pbSetText({$id},descape(\$('#defaultCode{$id}').val()))"); } } if (!$facultative && !$scramble && !get_option('cscircles_hide_help')) { $actions['help'] = array('value' => __t('Help'), 'onclick' => "helpClick({$id});"); } if ($richreadonly) { $actions = array('CMtoggle' => $actions['CMtoggle']); } // get rid of all other options $r .= "<div class='pyboxbuttons'><table><tr>\n"; if (!$richreadonly) { $r .= "<td><input type='submit' name='submit' id='submit{$id}' value=' '/></td>\n"; } $mb = 3; //maximum number of buttons, not counting 'submit' $i = 0; foreach ($actions as $name => $atts) { $i++; if ($i <= $mb) { $r .= button($name, $atts); continue; } if ($i == 1 + $mb) { $r .= "</tr></table><select id='pbSelect{$id}' class='selectmore'><option name='more'>" . __t("More actions...") . "</option>\n"; } $r .= option($name, $atts); } if (count($actions) > $mb) { $r .= "</select></div>\n"; } else { $r .= "</tr></table></div>\n"; } if (isset($cpulimit) && $cpulimit != 1) { $timeout = (WALLFACTOR * $cpulimit + WALLBUFFER + 2) * 1000; // + 2 seconds for network latency each way $r .= "<input type='hidden' name='timeout' value='{$timeout}'/>\n"; } $r .= '<input type="hidden" name="lang" value="' . currLang4() . '"/>'; $r .= '<input type="hidden" id="inputInUse' . $id . '" name="inputInUse" value="Y"/>' . "\n"; $r .= '<input type="hidden" name="pyId" value="' . $id . '"/>' . "\n"; $r .= '<input type="hidden" name="hash" value="' . $hash . '"/>' . "\n"; // although inputInUse starts as Y, the next script sets it to N and fixes the button labels $readyScripts .= $allowinput ? 'pbInputSwitch(' . $id . ',"' . ($usertni ? 'Y' : 'N') . '");' : 'document.getElementById("submit' . $id . '").value = "' . __t('Run program') . '";' . 'document.getElementById("inputInUse' . $id . '").value = "N";'; /// part 5 : results area, and footers $c = count($actions) > $mb ? ' avoidline' : ''; $r .= "<div id='pbresults{$id}' class='pbresults{$c}'></div>\n"; $r .= problemSourceWidget(array('hash' => $hash), count($actions) > $mb); $r .= '</div>' . "\n"; $r .= '</form>' . "\n"; if ($readyScripts != '') { $r .= "<script type='text/javascript'>{$readyScripts}</script>\n"; } $GLOBALS['pb_translation'] = NULL; return $r; }
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); }
function cscircles_makedb_page() { echo "<div class='wrap'>\n<h2>Rebuild CS Circles Databases</h2>\n<div>This page will rebuild the lesson database and the problem database. \n(If you're writing your own lessons, it assumes a structure like 99X-lessontitle\non the lesson slugs; contact us for help if needed.)</div>"; if (!array_key_exists('submitted', $_REQUEST)) { echo "<form method='get' action='admin.php'>\n <input type='hidden' name='page' value='cscircles-makedb'>\n <input type='hidden' name='submitted' value='true'>\n <button class='button-primary' id='submit'>Rebuild Databases</button></form>"; } else { $out = get_pages(); $lessons = array(); foreach ($out as $page) { if ($page->post_status != 'publish') { continue; } $s = $page->post_title; $m = preg_match('/^([0-9]+)([A-Za-z]?)\\: (.*)$/', $s, $matches); if (class_exists('PLL_Base')) { global $polylang; $lang = $polylang->get_post_language($page->ID); if ($lang != NULL) { $lang = $lang->slug; } else { $lang = substr(get_bloginfo("language"), 0, 2); } } else { // use default language $lang = currLang2(); } if ($m >= 1) { $lessons[] = array('number' => $matches[1] . $matches[2], 'title' => $matches[3], 'major' => $matches[1], 'minor' => $matches[2], 'id' => $page->ID, 'lang' => $lang); } elseif (class_exists('PLL_Base') && get_page_by_path('console')->ID == pll_get_post($page->ID, 'en') || get_page_by_path('console')->ID == $page->ID) { $lessons[] = array('id' => $page->ID, 'number' => NULL, 'lang' => $lang); } // go through the console page too, mainly to set up the right url in history grids, // it does not get added to pb_lessons but its contents do get added to pb_problems } function cmp($l1, $l2) { $c = strcmp($l1['lang'], $l2['lang']); if ($c != 0) { if ($l1['lang'] == 'en') { return -1; } // put english first if ($l2['lang'] == 'en') { return 1; } return $c; } if ($l1['number'] == NULL ^ $l2['number'] == NULL) { return $l1['number'] == NULL ? 1 : -1; } $c = $l1['major'] - $l2['major']; if ($c != 0) { return $c; } return strcmp($l1['minor'], $l2['minor']); } usort($lessons, 'cmp'); pyboxlog('[makedb] Rebuilding problem and lesson db', TRUE); global $wpdb, $SKIP_DB_REBUILD; $SKIP_DB_REBUILD = false; // set to true to be diagnostic $lesson_table_name = $wpdb->prefix . "pb_lessons"; $problem_table_name = $wpdb->prefix . "pb_problems"; if (!$GLOBALS['SKIP_DB_REBUILD']) { echo '<b>Truncating tables: '; $wpdb->query("TRUNCATE TABLE {$problem_table_name};"); $wpdb->query("TRUNCATE TABLE {$lesson_table_name};"); echo 'done</b>'; } $currlang = 'xx'; $i = -1; $gets = array(); foreach ($lessons as $l) { echo '<pre>'; if ($currlang != $l['lang']) { $currlang = $l['lang']; $i = 0; } if ($l['number'] == NULL) { $index = -1; } else { $index = $i; $i++; } $l['ordering'] = $index; if ($l['number'] != NULL) { echo 'About to insert lesson: ' . rowSummary($l); //. json_encode($l); if (!$GLOBALS['SKIP_DB_REBUILD']) { echo $wpdb->insert($lesson_table_name, $l) != 1 ? '<br>insert bad' : ' insert ok'; } } global $lesson_reg_info, $pyRenderCount, $post; $lesson_reg_info = array('index' => $index, 'lang' => $currlang, 'fullnumber' => $l['number'], 'url' => str_replace('/dev', '', get_page_link($l['id'])), 'id' => $l['id']); // pyboxlog($currlang . ' ' . $lesson_reg_info['url'], FALSE); // was used Apr. 7 to help transl. // render! the following line is not just cosmetic as it registers the problems. echo '<br>Snippet: ' . htmlspecialchars(substr(do_sweetcode(get_page($l['id'])->post_content), 0, 50)); $pyRenderCount = 0; // to get problem links working echo '</pre>'; } $lesson_reg_info = 1; } echo '</div>'; }