1<?php 2/** 3 * Action Plugin: Action on Ajax requests, My submissions page and export CSV 4 * 5 * @license GPL 3 (http://www.gnu.org/licenses/gpl.html) 6 * @author Masoud Sadrnezhaad <masoud@sadrnezhaad.ir> 7 */ 8 9// must be run within Dokuwiki 10if (!defined('DOKU_INC')) { 11 die(); 12} 13 14class action_plugin_judge extends DokuWiki_Action_Plugin 15{ 16 17 /** 18 * Register the events 19 * 20 * @param Doku_Event_Handler $controller 21 */ 22 public function register(Doku_Event_Handler $controller) 23 { 24 25 /** 26 * Submission button in top user menu bar 27 */ 28 $controller->register_hook('TEMPLATE_USERTOOLS_DISPLAY', 'BEFORE', $this, 'addButton'); 29 30 /** 31 * Submissions page content 32 */ 33 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'submissionsPageAction'); 34 $controller->register_hook('TPL_ACT_UNKNOWN', 'BEFORE', $this, 'submissionsPageContent'); 35 36 /** 37 * Remove page cache after login 38 */ 39 $controller->register_hook('AUTH_LOGIN_CHECK', 'AFTER', $this, 'removePageCache'); 40 41 /** 42 * export to csv icon in submissions page 43 */ 44 $controller->register_hook('TEMPLATE_PAGETOOLS_DISPLAY', 'BEFORE', $this, 'addCsvButton', array()); 45 $controller->register_hook('ACTION_ACT_PREPROCESS', 'BEFORE', $this, 'exportToCSV'); 46 47 /** 48 * Ajax calls 49 */ 50 $controller->register_hook('AJAX_CALL_UNKNOWN', 'BEFORE', $this, 'ajaxHandler'); 51 52 53 } 54 55 public function addButton(Doku_Event $event) 56 { 57 if ($_SERVER['REMOTE_USER']) { 58 $event->data['items'] = array_slice($event->data['items'], 0, -1, true) 59 + array('submissions' => '<li ><a href="' . DOKU_URL . '?do=submissions" id="user_submissions" rel="nofollow" title="' . $this->getLang('btn_my_submissions') . '">' . $this->getLang('btn_my_submissions') . '</a></li>') 60 + array_slice($event->data['items'], -1, 1, true); 61 } 62 } 63 64 public function submissionsPageAction(&$event) 65 { 66 if ($event->data != 'submissions') { 67 return; 68 } 69 $event->preventDefault(); 70 return true; 71 } 72 73 public function submissionsPageContent(&$event) 74 { 75 if ($event->data != 'submissions') { 76 return; 77 } 78 $event->preventDefault(); 79 80 $table_html = $this->buildResultTable(); 81 print $table_html; 82 } 83 84 public function buildResultTable() 85 { 86 $crud = plugin_load('helper', 'judge_crud'); 87 $table_html = ' 88 <h1>' . $this->getLang('programming_questions') . '</h1> 89 <div> 90 <div class="table sectionedit1"> 91 <table class="inline"> 92 <thead> 93 <tr class="row0"> 94 <th class="col0">' . $this->getLang('count_number') . '</th><th class="col1">' . $this->getLang('question_name') . '</th><th class="col2">' . $this->getLang('timestamp') . '</th><th class="col3">' . $this->getLang('language') . '</th><th class="col4">' . $this->getLang('status') . '</th> 95 </tr> 96 </thead> 97 <tbody>'; 98 99 $table_html .= $crud->tableRender(array('type' => "test-case", 'user' => $_SERVER['REMOTE_USER']), "html", 1, "timestamp")["submissions_table"]; 100 101 $table_html .= ' 102 </tbody> 103 </table> 104 </div> 105 </div>'; 106 107 $table_html .= ' 108 <h1>' . $this->getLang('outputonly_questions') . '</h1> 109 <div> 110 <div class="table sectionedit1"> 111 <table class="inline"> 112 <thead> 113 <tr class="row0"> 114 <th class="col0">' . $this->getLang('count_number') . '</th><th class="col1">' . $this->getLang('question_name') . '</th><th class="col2">' . $this->getLang('timestamp') . '</th><th class="col3">' . $this->getLang('status') . '</th> 115 </tr> 116 </thead> 117 <tbody>'; 118 119 $table_html .= $crud->tableRender(array('type' => "output-only", 'user' => $_SERVER['REMOTE_USER']), "html", 1, "timestamp")["submissions_table"]; 120 121 $table_html .= ' 122 </tbody> 123 </table> 124 </div> 125 <input class="button" type="button" onClick="history.go(0)" value="' . $this->getLang('table_update') . '" tabindex="4" /> 126 </div>'; 127 return $table_html; 128 } 129 130 131 public function addCsvButton(Doku_Event $event) 132 { 133 global $ID, $ACT; 134 if ($ACT != 'submissions') { 135 return; 136 } 137 if ($event->data['view'] == 'main') { 138 $params = array('do' => 'export_csv'); 139 140 // insert button at position before last (up to top) 141 $event->data['items'] = array_slice($event->data['items'], 0, -1, true) + 142 array('export_pdf' => 143 '<li>' 144 . '<a href="' . wl($ID, $params) . '" class="action export_pdf" rel="nofollow" title="' . $this->getLang('btn_export_csv') . '">' 145 . '<span>' . $this->getLang('btn_export_csv') . '</span>' 146 . '</a>' 147 . '</li>' 148 ) + 149 array_slice($event->data['items'], -1, 1, true); 150 } 151 } 152 153 public function exportToCSV(&$event) 154 { 155 if ($event->data != 'export_csv') { 156 return; 157 } 158 $event->preventDefault(); 159 $crud = plugin_load('helper', 'judge_crud'); 160 ob_start('ob_gzhandler'); 161 ob_clean(); 162 $csvOutput = "#\tType\tProblem name\tTimestamp\tLanguage\tStatus\n"; 163 $csvOutput .= $crud->tableRender(array('type' => "output-only", 'user' => $_SERVER['REMOTE_USER']), "csv", 1, "timestamp")["submissions_table"]; 164 $csvOutput .= $crud->tableRender(array('type' => "test-case", 'user' => $_SERVER['REMOTE_USER']), "csv", 1, "timestamp")["submissions_table"]; 165 print $csvOutput; 166 ob_end_flush(); 167 header("Content-type: application/octet-stream"); 168 header("Content-Disposition: attachment; filename=" . $_SERVER['REMOTE_USER'] . "_submissions_" . date("Y-m-d_H-i", time()) . ".csv"); 169 header("Pragma: no-cache"); 170 header("Expires: 0"); 171 } 172 173 public function removePageCache(&$event) 174 { 175 global $config_cascade; 176 @touch(reset($config_cascade['main']['local'])); 177 } 178 179 function ajaxHandler(Doku_Event $event, $param) 180 { 181 if ($event->data !== 'plugin_judge') { 182 return; 183 } 184 //no other ajax call handlers needed 185 $event->stopPropagation(); 186 $event->preventDefault(); 187 188 global $INPUT; 189 $task = $INPUT->str('name'); 190 191 //data 192 $data = array(); 193 194 switch ($task) { 195 case "submit": 196 define('DBFILE', dirname(__FILE__) . '/submissions.sqlite'); 197 define('DBENGINE', 'sqlite3'); 198 199 if (file_exists(DBFILE)) { 200 $data = $this->submit_to_db(); 201 } 202 case "outputonlyResult": 203 $data[] = $this->compare($INPUT->str('user_output'), $INPUT->str('problem_name')); 204 break; 205 case "resultRefresh": 206 $crud = plugin_load('helper', 'judge_crud'); 207 $data[] = $crud->tableRender(array('problem_name' => $INPUT->str('problem_name'), 'type' => $INPUT->str('type'), 'user' => $INPUT->str('user')), "html", 1, "timestamp")["submissions_table"]; 208 break; 209 case "judge": 210 sleep(10); 211 $run_status = rand(0, 4); 212 define('DBFILE', dirname(__FILE__) . '/submissions.sqlite'); 213 $db = new SQLite3(DBFILE); 214 $query = 'UPDATE submissions SET status_code =1 WHERE submit_id = ' . $_POST['id'] . ';'; 215 $db->exec($query); 216 $query = 'UPDATE submission_test_case SET valid =' . $run_status . ' WHERE submit_id = ' . $_POST['id'] . ';'; 217 $db->exec($query); 218 break; 219 case "upload": 220 $data[] = $this->upload($INPUT->str('file_name'), $INPUT->str('path'), $INPUT->str('code')); 221 break; 222 case "scoreboardRefresh": 223 $crud = plugin_load('helper', 'judge_crud'); 224 $i = 1; 225 $html = ""; 226 foreach (explode(",",$INPUT->str('questions')) as &$problem_name) { 227 $submissions = $crud->tableRender(array('problem_name' => $problem_name, 'type' => "test-case", 'user' => null), "html", $i, "timestamp"); 228 $html .= $submissions["submissions_table"]; 229 $i = $submissions["count"]; 230 } 231 $data[] = $html; 232 break; 233 } 234 235 //json library of DokuWiki 236 $json = new JSON(); 237 238 //set content type 239 header('Content-Type: application/json'); 240 echo $json->encode($data); 241 } 242 243 function submit_to_db() 244 { 245 246 if (!defined('DOKU_INC')) { 247 define('DOKU_INC', dirname(__FILE__) . '/../../../../'); 248 include_once DOKU_INC . 'inc/init.php'; 249 include_once DOKU_INC . 'inc/plugin.php'; 250 } 251 252 global $conf; 253 254 include_once dirname(__FILE__) . '/helper/jdatetime.class.php'; 255 $pdate = new jDateTime(false, true, 'Asia/Tehran'); 256 date_default_timezone_set('Asia/Tehran'); 257 $query = 'INSERT INTO submissions VALUES (NULL,"' . time() . '","' . $_POST['problem_name'] . '","' . $_POST['user'] . '","' . $_POST['type'] . '",' . $_POST['status_code'] . ')'; 258 259 $db = new SQLite3(DBFILE); 260 $db->exec($query); 261 $id = $db->lastInsertRowID(); 262 $result_id_query = 'SELECT COUNT(*) FROM submissions WHERE problem_name = "' . $_POST['problem_name'] . '"AND username="' . $_POST['user'] . '"'; 263 $row_number = $db->querySingle($result_id_query); 264 265 if ($conf['lang'] == "fa") { 266 $date = $pdate->date("l j F Y H:i:s"); 267 } else { 268 $date = date('l j F Y H:i:s'); 269 } 270 271 if ($_POST['type'] === "output-only") { 272 $result = array('date' => $date, 'row_number' => $row_number); 273 } elseif ($_POST['type'] === "test-case") { 274 $test_case_query = 'INSERT INTO submission_test_case VALUES (' . $id . ',"' . $_POST['language'] . '",' . '0,"' . $_POST['runtime'] . '")'; 275 $db->exec($test_case_query); 276 $result = array('date' => $date, 'row_number' => $row_number, 'id' => $id); 277 } 278 return $result; 279 } 280 281 public function compare($user_output, $problem_name) 282 { 283 $extension = $this->loadHelper('judge_numbers'); 284 $answer = $extension->parseNumber(rawWiki("داوری:" . $problem_name)); 285 if ($answer == $extension->parseNumber($user_output)) { 286 return true; 287 } else { 288 return false; 289 } 290 } 291 292 public function upload($filename, $path, $code) 293 { 294 $allowedExts = array("java", "py", "c", "cpp"); 295 296 if (!in_array(pathinfo(basename($filename), PATHINFO_EXTENSION), $allowedExts, true)) { 297 return $this->getLang('err_file_format'); 298 } 299 300 $targetdir = $path; 301 if (substr($targetdir, -1) != "/") { 302 $targetdir .= "/"; 303 } 304 if (substr($targetdir, 1) != "/") { 305 $targetdir = "/" . $targetdir; 306 } 307 308 $temp = explode(".", $filename); 309 310 if (file_exists(TARGETFILE)) { 311 return $filename . $this->getLang('err_file_exist'); 312 } elseif (!file_exists($targetdir)) { 313 return $this->getLang('err_dir') . $targetdir . $this->getLang('err_upload_dir'); 314 } else { 315 define('DBFILE', dirname(__FILE__) . '/submissions.sqlite'); 316 $db = new SQLite3(DBFILE); 317 $query = 'SELECT submit_id FROM submissions ORDER BY submit_id DESC LIMIT 1'; 318 $id = $db->exec($query); 319 define('TARGETFILE', $targetdir . $id . "." . end($temp)); 320 file_put_contents(TARGETFILE, $code); 321 } 322 } 323 324} 325 326