1<?php 2/** 3 * Syntax Plugin: Displays judge forms, submissions and scoreboards 4 * 5 * @license GPL 3 (http://www.gnu.org/licenses/gpl.html) 6 * @author Masoud Sadrnezhaad <masoud@sadrnezhaad.ir> 7 * @author Hamid Zarabi-Zadeh <zarrabi@sharif.edu> 8 */ 9 10// must be run within Dokuwiki 11if (!defined('DOKU_INC')) { 12 die(); 13} 14 15class syntax_plugin_judge extends DokuWiki_Syntax_Plugin 16{ 17 /** 18 * @return string Syntax mode type. container indicates that this plugin tag can be nested in listblock, table, quote, hr tags. 19 */ 20 public function getType() 21 { 22 return 'container'; 23 } 24 25 /** 26 * @return block indicates that open paragraphs need to be closed before plugin output. it can't be used inside open paragraph. 27 */ 28 public function getPType() 29 { 30 return 'block'; 31 } 32 33 /** 34 * @return int Sort order - Low numbers go before high numbers 35 */ 36 public function getSort() 37 { 38 return 10; 39 } 40 41 /** 42 * Connect lookup pattern to lexer. SpecialPattern indicates that this pattern don't have exit pattern and pattern data is not to be processed. 43 * 44 * @param string $mode Parser mode 45 */ 46 public function connectTo($mode) 47 { 48 $this->Lexer->addSpecialPattern('\{\{[judge|داوری|scoreboard][ ]*[^}]*\}\}', $mode, 'plugin_judge'); 49 } 50 51 /** 52 * Handle matches of the judge syntax 53 * 54 * @param string $match The text matched by the patterns 55 * @param int $state Type of pattern which triggered this call to handle() 56 * @param int $pos The character position of the matched text. 57 * @param Doku_Handler $handler The handler 58 * @return array Data for the renderer 59 */ 60 61 public function handle($match, $state, $pos, Doku_Handler $handler) 62 { 63 64 if (substr($match, 2, 10) == "scoreboard") { 65 /** 66 * Strip markup from start and end $match 67 */ 68 $match = substr($match, 13, -2); 69 return $this->scoreboard_handle($match); 70 } elseif (substr($match, 2, 5) == "judge") { 71 /** 72 * Strip markup from start and end $match 73 */ 74 $match = substr($match, 8, -2); 75 return $this->judge_handle($match); 76 } elseif (substr($match, 2, 10) == "داوری") { 77 /** 78 * Strip markup from start and end $match 79 */ 80 $match = substr($match, 13, -2); 81 return $this->judge_handle($match); 82 } 83 84 } 85 86 public function scoreboard_handle($match) 87 { 88 89 /** 90 * Extract problem names into array 91 */ 92 $parameters = explode(' ', $match); 93 94 return array('mode' => "scoreboard", 'questions' => $parameters); 95 96 } 97 98 public function judge_handle($match) 99 { 100 global $ID, $conf; 101 102 /** 103 * Extract problem parameters into array 104 */ 105 $parameters = explode(' ', $match); 106 107 /** 108 * Set problem parameters 109 */ 110 foreach ($parameters as $parameter) { 111 if (strpos($parameter, '=') == false) { 112 113 /** 114 * Find variable type and set it 115 */ 116 switch ($parameter) { 117 case "test-case": 118 $type = $parameter; 119 break; 120 case "output-only": 121 $type = $parameter; 122 break; 123 case "tester.cpp": 124 $method = $parameter; 125 break; 126 case "tester.py": 127 $method = $parameter; 128 break; 129 case "diff": 130 $method = $parameter; 131 break; 132 case "برنامه-نویسی": 133 $type = "test-case"; 134 break; 135 case "فقط-خروجی": 136 $type = "output-only"; 137 break; 138 case "java": 139 $language = "Java"; 140 break; 141 case "Java": 142 $language = "Java"; 143 break; 144 case "python2": 145 $language = "Python 2"; 146 break; 147 case "python3": 148 $language = "Python 3"; 149 break; 150 case "cpp": 151 $language = "C++"; 152 break; 153 case "c++": 154 $language = "C++"; 155 break; 156 case "C++": 157 $language = "C++"; 158 break; 159 case "c": 160 $language = "C"; 161 break; 162 case "C": 163 $language = "C"; 164 break; 165 default: 166 if (is_numeric($parameter)) { 167 $runtime = $parameter; 168 } else { 169 $problem_name = $parameter; 170 } 171 } 172 } else { 173 174 list($key, $value) = explode('=', $parameter); 175 176 switch ($key) { 177 178 /** 179 * Remove key string if included 180 */ 181 case "problem": 182 $problem_name = $value; 183 break; 184 case "type": 185 $type = $value; 186 break; 187 case "time": 188 $runtime = $value; 189 break; 190 case "method": 191 switch ($value) { 192 case "tester.cpp": 193 $method = $value; 194 break; 195 case "tester.py": 196 $method = $value; 197 break; 198 case "diff": 199 $method = $value; 200 } 201 case "language": 202 switch ($value) { 203 case "java": 204 $language = "Java"; 205 break; 206 case "Java": 207 $language = "Java"; 208 break; 209 case "python2": 210 $language = "Python 2"; 211 break; 212 case "python3": 213 $language = "Python 3"; 214 break; 215 case "cpp": 216 $language = "C++"; 217 break; 218 case "c++": 219 $language = "C++"; 220 break; 221 case "C++": 222 $language = "C++"; 223 break; 224 case "c": 225 $language = "C"; 226 break; 227 case "C": 228 $language = "C"; 229 break; 230 } 231 } 232 } 233 } 234 235 /** 236 * Set default value for parameters 237 */ 238 if ($problem_name == null) { 239 $problem_name = $ID; 240 } 241 242 $judge = "داوری:" . $problem_name; 243 244 if ($type === null) { 245 $type = "test-case"; 246 } 247 248 if ($runtime === null) { 249 $runtime = 0; 250 } 251 252 if ($language === null) { 253 $language = "all"; 254 } 255 256 if ($method === null) { 257 $method = "diff"; 258 } 259 260 if ($conf['useslash']) { 261 $problem_name = str_replace('/', ':', $problem_name); 262 } 263 264 return array('mode' => "judge", 'problem_name' => $problem_name, 'type' => $type, 'runtime' => $runtime, 'language' => $language, 'judge' => $judge, 'method' => $method); 265 266 } 267 268 /** 269 * Render xhtml output or metadata 270 * 271 * @param string $mode Renderer mode (supported modes: xhtml) 272 * @param Doku_Renderer $renderer The renderer 273 * @param array $data The data from the handler() function 274 * @return bool If rendering was successful. 275 */ 276 public 277 function render($mode, Doku_Renderer $renderer, $data) 278 { 279 if ($mode != 'xhtml') { 280 return false; 281 } 282 283 if ($data["mode"] == "judge") { 284 $renderer->doc .= $this->render_judge($data); 285 } elseif ($data["mode"] == "scoreboard") { 286 $renderer->doc .= $this->render_scoreboard($data); 287 } 288 289 return true; 290 } 291 292 public function render_judge($data) 293 { 294 global $conf, $USERINFO; 295 296 $html = ''; 297 298 /** 299 * Show plugin to logged in user 300 */ 301 if ($_SERVER['REMOTE_USER']) { 302 303 /** 304 * Show plugin if problem_name page exists 305 */ 306 if (page_exists($data['problem_name'])) { 307 $page_answer_exist = page_exists($data['judge']); 308 $media_files = array_filter(glob($conf['mediadir'] . "/" . str_replace("%3A", "/", urlencode($data['judge'])) . "/" . "*")); 309 $media_answer_exist = !empty($media_files); 310 if (($page_answer_exist || $media_answer_exist) && in_array("user", $USERINFO['grps'])) { 311 $html .= ' 312 <div class="judge"> 313 <p onclick="jQuery(this).next().slideToggle();">' . $this->getLang('btn_submit_answer') . '</p> 314 <div> 315 <form onsubmit="return false;" name="judge-submit-' . $data['problem_name'] . '" method="post">'; 316 if ($data['type'] === "output-only") { 317 $html .= ' 318 <div> 319 <label class="block"> 320 <input type="text" style="margin-left: 2px;" id="user-output-' . $data['problem_name'] . '" size="25" tabindex="1" value="">'; 321 $html .= 322 ' 323 <input class="button" type="submit" onclick="submitKey(' . "'" . $data['problem_name'] . "','" . $_SERVER['REMOTE_USER'] . "','" . $data['language'] . "','" . $data['type'] . "','" . $data['runtime'] . "','" . "','" . $this->getConf('upload_path') . "'" . '); return false;" value="' . $this->getLang('btn_submit') . '" /> 324 </label> 325 </div> 326 </form> 327 '; 328 } elseif ($data['type'] === "test-case") { 329 $html .= ' 330 <label class="block"> 331 <input id="code-file-' . $data['problem_name'] . '"' . ' onclick="inputFileKey(' . "'" . $data['problem_name'] . "'" . '); return false;"> 332 <input class="button" onclick="inputFileKey(' . "'" . $data['problem_name'] . "'" . '); return false;" type="reset" value="' . $this->getLang('btn_choose_file') . '"> 333 <input onchange="changeFilePath(' . "'" . $data['problem_name'] . "'" . ');" style="display: none;" name="code-' . $data['problem_name'] . '" id="code-' . $data['problem_name'] . '" type=file> 334 '; 335 336 if ($data['language'] === "all") { 337 $html .= ' 338 </label> 339 <label class="block" style="margin-top: 5px;"> 340 <span>' . $this->getLang('choose_language') . '</span> 341 <select style="width: auto;" id="language-' . $data['problem_name'] . '"> 342 <option value="Java">Java</option> 343 <option value="Python 2">Python 2</option> 344 <option value="Python 3">Python 3</option> 345 <option value="C++">C++</option> 346 <option value="C">C</option> 347 </select> 348 '; 349 } 350 351 $html .= ' 352 <input class="button" type="submit" onclick="submitKey(' . "'" . $data['problem_name'] . "','" . $_SERVER['REMOTE_USER'] . "','" . $data['language'] . "','" . $data['type'] . "','" . $data['runtime'] . "','" . $this->getConf('upload_path') . "'" . '); return false;" value="' . $this->getLang('btn_submit') . '" tabindex="4" /> 353 </label> 354 </form> 355 '; 356 } 357 358 $html .= ' 359 <div> 360 <label class="block"> 361 <span id="result-label-' . $data['problem_name'] . '"></span> 362 <span id="result-' . $data['problem_name'] . '"></span> 363 </label> 364 </div> 365 '; 366 367 $html .= ' 368 </div></div> 369 '; 370 371 define('DBFILE', dirname(__FILE__) . '/submissions.sqlite'); 372 date_default_timezone_set('Asia/Tehran'); 373 $db = new SQLite3(DBFILE); 374 $submissions = $db->querySingle('SELECT COUNT(*) FROM submissions WHERE problem_name = "' . $data['problem_name'] . '"AND username="' . $_SERVER['REMOTE_USER'] . '";'); 375 if (!empty($submissions)) { 376 $html .= ' 377 <div class="judge" id="previous_submissions-' . $data['problem_name'] . '"> 378 '; 379 } else { 380 $html .= ' 381 <div class="judge" style="display: none;" id="previous_submissions-' . $data['problem_name'] . '"> 382 '; 383 } 384 385 $html .= ' 386 <p onclick="jQuery(this).next().slideToggle();">' . $this->getLang('btn_previous_submissions') . '</p> 387 <div style="display: none;" id="previous_submissions-table-' . $data['problem_name'] . '"> 388 <div class="table sectionedit1"> 389 <table class="inline"> 390 '; 391 392 $crud = plugin_load('helper', 'judge_crud', true); 393 if ($data['type'] === "test-case") { 394 395 $html .= ' 396 <thead> 397 <tr class="row0"> 398 <th class="col0">' . $this->getLang('count_number') . '</th><th class="col1">' . $this->getLang('timestamp') . '</th><th class="col2">' . $this->getLang('language') . '</th><th class="col3">' . $this->getLang('status') . '</th> 399 </tr> 400 </thead> 401 <tbody id="result-row-' . $data['problem_name'] . '">'; 402 403 $html .= $crud->tableRender(array('problem_name' => $data["problem_name"], 'type' => $data["type"], 'user' => $_SERVER['REMOTE_USER']), "html", 1, "timestamp")["submissions_table"]; 404 405 $html .= '</tbody>'; 406 } else { 407 $html .= ' 408 <thead> 409 <tr class="row0"> 410 <th class="col0">' . $this->getLang('count_number') . '</th><th class="col1">' . $this->getLang('timestamp') . '</th><th class="col2">' . $this->getLang('status') . '</th> 411 </tr> 412 </thead> 413 <tbody id="result-row-' . $data['problem_name'] . '"> 414 '; 415 416 /** 417 * get output-only submissions 418 */ 419 $html .= $crud->tableRender(array('problem_name' => $data["problem_name"], 'type' => $data["type"], 'user' => $_SERVER['REMOTE_USER']), "html", 1, "timestamp")["submissions_table"]; 420 $html .= '</tbody>'; 421 } 422 423 $html .= ' 424 </table> 425 </div> 426 <input class="button" type="submit" onclick="resultRefresh(' . "'" . $data['problem_name'] . "','" . $data['type'] . "','" . $_SERVER['REMOTE_USER'] . "'" . '); return false;" value="' . $this->getLang('table_update') . '" tabindex="4" /> 427 </div> 428 </div> 429 '; 430 } 431 }; 432 433 434 if (in_array($this->getConf('editors_group'), $USERINFO['grps']) || in_array("admin", $USERINFO['grps'])) { 435 if (page_exists($data['problem_name'])) { 436 if (auth_quickaclcheck($data['judge']) >= AUTH_EDIT) { 437 if ($data['type'] === "test-case") { 438 if ($media_answer_exist) { 439 $html .= '<div class="judge"><p><a target="_blank" href="?tab_files=files&do=media&ns=' . $data['judge'] . '">' . $this->getLang('btn_edit_testcase_long') . '</a></p></div>'; 440 } elseif ($page_answer_exist) { 441 $html .= '<div class="judge"><p><a target="_blank" href="' . DOKU_URL . $data['judge'] . '">' . $this->getLang('btn_edit_testcase_short') . '</a></p></div>'; 442 } else { 443 $html .= '<div class="judge"><p>' . $this->getLang('btn_submit_testcase') . ' (<a target="_blank" href="' . DOKU_URL . $data['judge'] . '?do=edit">' . $this->getLang('btn_submit_testcase_short') . '</a> - <a target="_blank" href="?tab_files=upload&do=media&ns=' . $data['judge'] . '">' . $this->getLang('btn_submit_testcase_long') . '</a>)</p></div>'; 444 } 445 446 } else { 447 if ($page_answer_exist) { 448 $html .= '<div class="judge"><p><a target="_blank" href="' . DOKU_URL . $data['judge'] . '">' . $this->getLang('btn_edit_correct_answer') . '</a></p></div>'; 449 } else { 450 $html .= '<div class="judge"><p><a target="_blank" href="' . DOKU_URL . $data['judge'] . "?do=edit" . '">' . $this->getLang('btn_submit_correct_answer') . '</a></p></div>'; 451 } 452 } 453 } 454 } else { 455 $html .= '<div class="judge"><p><a target="_blank" href="' . DOKU_URL . $data['problem_name'] . "?do=edit" . '">' . $this->getLang('btn_create_question_page') . '</a></p></div>'; 456 } 457 } 458 } 459 460 return $html; 461 } 462 463 public function render_scoreboard($data) 464 { 465 $crud = plugin_load('helper', 'judge_crud', true); 466 467 $html = ' 468 <div class="table sectionedit1"> 469 <table class="inline"> 470 <thead> 471 <tr class="row0"> 472 <th class="col0">' . $this->getLang('count_number') . '</th><th class="col1">' . $this->getLang('question_name') . '</th><th class="col1">' . $this->getLang('sender') . '</th><th class="col2">' . $this->getLang('timestamp') . '</th><th class="col3">' . $this->getLang('language') . '</th><th class="col4">' . $this->getLang('status') . '</th> 473 </tr> 474 </thead> 475 <tbody id="scoreboard-row-' . implode(",",$data['questions']) . '">'; 476 477 $i = 1; 478 foreach ($data["questions"] as &$problem_name) { 479 $submissions = $crud->tableRender(array('problem_name' => $problem_name, 'type' => "test-case", 'user' => null), "html", $i, "timestamp"); 480 $html .= $submissions["submissions_table"]; 481 $i = $submissions["count"]; 482 } 483 484 $html .= ' 485 </tbody> 486 </table> 487 </div> 488 <input class="button" type="submit" onclick="resultRefresh(' . "'" . implode(",",$data['questions']) . "','test-case','null'" . '); return false;" value="' . $this->getLang('table_update') . '" tabindex="4" /> 489 '; 490 491 return $html; 492 } 493 494} 495 496// vim:ts=4:sw=4:et: