function pyDebug($o, $c) { if (WP_DEBUG) { return do_short_and_sweetcode($c); } }
function print_accordion($options, $content) { return '<div class="accordion">' . do_short_and_sweetcode($content) . '</div>'; }
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; }