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: