form_name = $form_name; $this->succ_href = $_SERVER['REQUEST_URI']; $this->handle = function(&$vdata) { }; $this->run_at_server_handler["check-{$this->form_name}"] = function() { die(json_encode($this->validateAtServer())); }; $this->run_at_server_handler["submit-{$this->form_name}"] = function() { if ($this->no_submit) { become404Page(); } foreach ($this->data as $field) { if (!isset($field['no_val']) && !isset($_POST[$field['name']])) { becomeMsgPage('The form is incomplete.'); } } if (UOJContext::requestMethod() == 'POST') { $len = UOJContext::contentLength(); if ($len === null) { become403Page(); } elseif ($len > $this->max_post_size) { becomeMsgPage('The form is too large.'); } } crsf_defend(); $errors = $this->validateAtServer(); if ($errors) { $err_str = ''; foreach ($errors as $name => $err) { $esc_err = htmlspecialchars($err); $err_str .= "$name: $esc_err"; } becomeMsgPage($err_str); } $fun = $this->handle; $fun($this->vdata); if ($this->succ_href !== 'none') { header("Location: {$this->succ_href}"); } die(); }; } public function setAjaxSubmit($js) { $GLOBALS['REQUIRE_LIB']['jquery.form'] = ''; $this->ajax_submit_js = $js; } public function add($name, $html, $validator_php, $validator_js, $no_val = false) { $this->main_html .= $html; $data = array( 'name' => $name, 'validator_php' => $validator_php, 'validator_js' => $validator_js); if ($no_val) { $data['no_val'] = ''; } $this->data[] = $data; } public function appendHTML($html) { $this->main_html .= $html; } public function addNoVal($name, $html) { $this->main_html .= $html; $this->data[] = array( 'name' => $name, 'validator_js' => 'always_ok', 'no_val' => ''); } public function addHidden($name, $default_value, $validator_php, $validator_js) { $default_value = htmlspecialchars($default_value); $html = << EOD; $this->add($name, $html, $validator_php, $validator_js); } public function addInput($name, $type, $label_text, $default_value, $validator_php, $validator_js) { $default_value = htmlspecialchars($default_value); $html = << $label_text EOD; $this->add($name, $html, $validator_php, $validator_js); } public function addSelect($name, $options, $label_text, $default_value) { $default_value = htmlspecialchars($default_value); $html = << $label_text EOD; foreach ($options as $opt_name => $opt_label) { if ($opt_name != $default_value) { $html .= <<$opt_label EOD; } else { $html .= <<$opt_label EOD; } } $html .= << EOD; $this->add($name, $html, function($opt) use ($options) { return isset($options[$opt]) ? '' : "无效选项"; }, null ); } public function addVInput($name, $type, $label_text, $default_value, $validator_php, $validator_js) { $html = HTML::div_vinput($name, $type, $label_text, $default_value); $this->add($name, $html, $validator_php, $validator_js); } public function addVSelect($name, $options, $label_text, $default_value) { $default_value = htmlspecialchars($default_value); $html = << $label_text EOD; foreach ($options as $opt_name => $opt_label) { if ($opt_name != $default_value) { $html .= <<$opt_label EOD; } else { $html .= <<$opt_label EOD; } } $html .= << EOD; $this->add($name, $html, function($opt) use ($options) { return isset($options[$opt]) ? '' : "无效选项"; }, null ); } public function addTextArea($name, $label_text, $default_value, $validator_php, $validator_js, $no_val = false) { $default_value = htmlspecialchars($default_value); $this->is_big = true; $html = << $label_text $default_value EOD; $this->add($name, $html, $validator_php, $validator_js, $no_val); } public function addVTextArea($name, $label_text, $default_value, $validator_php, $validator_js, $no_val = false) { $default_value = htmlspecialchars($default_value); $this->is_big = true; $html = << $label_text $default_value EOD; $this->add($name, $html, $validator_php, $validator_js, $no_val); } public function addCheckBox($name, $label_text, $default_value) { $default_value = htmlspecialchars($default_value); $status = $default_value ? 'checked="checked" ' : ''; $html = << $label_text EOD; $this->addNoVal($name, $html); } public function addVCheckboxes($name, $options, $label_text, $default_value) { $default_value = htmlspecialchars($default_value); $html = << $label_text EOD; foreach ($options as $opt_name => $opt_label) { $html .= << EOD; if ($opt_name != $default_value) { $html .= << EOD; } else { $html .= << EOD; } $html .= <<$opt_label EOD; } $html .= << EOD; $this->add($name, $html, function($opt) use ($options) { return isset($options[$opt]) ? '' : "无效选项"; }, null ); } public function addCKEditor($name, $label_text, $default_value, $validator_php, $validator_js) { $default_value = htmlspecialchars($default_value); global $REQUIRE_LIB; $REQUIRE_LIB['ckeditor'] = ''; $this->is_big = true; $html = << $label_text $default_value EOD; $this->add($name, $html, $validator_php, $validator_js); } public function addBlogEditor(UOJBlogEditor $editor) { global $REQUIRE_LIB; $REQUIRE_LIB['blog-editor'] = ''; $this->is_big = true; $name = $editor->name; $this->addVInput("{$name}_title", 'text', '标题', $editor->cur_data['title'], function ($title) use ($editor) { return $editor->validateTitle(); }, null ); $content_md_html = HTML::div_vtextarea("{$name}_content_md", '内容', $editor->cur_data['content_md']); $this->add("{$name}_content_md", $content_md_html, function ($content_md) use ($editor) { return $editor->validateContentMd(); }, 'always_ok' ); $this->appendHTML(<<blog_editor_init("$name"); EOD ); $this->run_at_server_handler["save-{$name}"] = function() use ($name, $editor) { if ($this->no_submit) { become404Page(); } foreach (array("{$name}_title", "{$name}_content_md") as $field_name) { if (!isset($_POST[$field_name])) { becomeMsgPage('The form is incomplete.'); } } crsf_defend(); $errors = $this->validateAtServer(); if (!$errors) { $editor->handleSave(); } die(json_encode($errors)); }; } public function addSlideEditor($name, $label_text, $default_value, $validator_php, $validator_js) { $default_value = htmlspecialchars($default_value); global $REQUIRE_LIB; $REQUIRE_LIB['slide-editor'] = ''; $this->is_big = true; $html = << $label_text $default_value EOD; $this->add($name, $html, $validator_php, $validator_js); } static public function uploadedFileTmpName($name) { if (isset($_FILES[$name]) && is_uploaded_file($_FILES[$name]['tmp_name'])) { return $_FILES[$name]['tmp_name']; } else { return null; } } public function addSourceCodeInput($name, $text, $languages) { $this->add("{$name}_upload_type", '', function($type, &$vdata) use ($name) { if ($type == 'editor') { if (!isset($_POST["{$name}_editor"])) { return '你在干啥……怎么什么都没交过来……?'; } } elseif ($type == 'file') { } else { return '……居然既不是用编辑器上传也不是用文件上传的……= =……'; } }, 'always_ok' ); $this->addNoVal("{$name}_editor", ''); $this->addNoVal("{$name}_file", ''); $this->add("{$name}_language", '', function($lang) use ($languages) { if (!in_array($lang, $languages)) { return '该语言不被支持'; } return ''; }, 'always_ok' ); $preferred_language = Cookie::get('uoj_preferred_language'); if ($preferred_language == null || !in_array($preferred_language, $languages)) { $preferred_language = $languages[0]; } $langs_options_str = ''; foreach ($languages as $lang) { $langs_options_str .= "appendHTML(<< EOD ); $this->is_big = true; $this->has_file = true; } public function addTextFileInput($name, $text) { $this->add("{$name}_upload_type", '', function($type, &$vdata) use ($name) { if ($type == 'editor') { if (!isset($_POST["{$name}_editor"])) { return '你在干啥……怎么什么都没交过来……?'; } } elseif ($type == 'file') { } else { return '……居然既不是用编辑器上传也不是用文件上传的……= =……'; } }, 'always_ok' ); $this->addNoVal("{$name}_editor", ''); $this->addNoVal("{$name}_file", ''); $this->appendHTML(<< EOD ); $this->is_big = true; $this->has_file = true; } public function printHTML() { $form_entype_str = $this->is_big ? ' enctype="multipart/form-data"' : ''; $form_class = "form-horizontal"; if ($this->submit_button_config['align'] == 'inline') { $form_class .= " uoj-form-inline"; } if ($this->submit_button_config['align'] == 'compressed') { $form_class .= " uoj-form-compressed"; } if (isset($this->submit_button_config['narrow']) && $this->submit_button_config['narrow']) { $form_class .= " uoj-form-narrow"; } echo ''; echo HTML::hiddenToken(); echo $this->main_html; if (!$this->no_submit) { if (!isset($this->submit_button_config['align'])) { $this->submit_button_config['align'] = 'center'; } if (!isset($this->submit_button_config['text'])) { $this->submit_button_config['text'] = UOJLocale::get('submit'); } if (!isset($this->submit_button_config['class_str'])) { $this->submit_button_config['class_str'] = 'btn btn-secondary'; } if ($this->submit_button_config['align'] == 'offset') { echo ''; echo ''; } else { echo ''; } echo '', $this->submit_button_config['text'], ''; if ($this->submit_button_config['align'] == 'offset') { echo ''; } echo ''; } echo ''; if ($this->no_submit) { return; } echo << $(document).ready(function() { EOD; if ($this->ctrl_enter_submit) { echo <<form_name}').keydown(function(e) { if (e.keyCode == 13 && e.ctrlKey) { $('#button-submit-{$this->form_name}').click(); } }); EOD; } echo <<form_name}').submit(function(e) { var ok = true; EOD; $need_ajax = false; if ($this->extra_validator) { $need_ajax = true; } foreach ($this->data as $field) { if ($field['validator_js'] != null) { if ($field['validator_js'] != 'always_ok') { echo <<data as $field) { if ($field['validator_js'] == null) { echo <<form_name}'] = ""; $.ajax({ url : '${_SERVER['REQUEST_URI']}', type : 'POST', dataType : 'json', async : false, data : post_data, success : function(data) { EOD; foreach ($this->data as $field) { if ($field['validator_js'] == null) { echo <<data as $field) { if ($field['validator_js'] != 'always_ok') { echo <<submit_button_config['smart_confirm'])) { $this->submit_button_config['confirm_text'] = '你真的要' . $this->submit_button_config['text'] . '吗?'; } if (isset($this->submit_button_config['confirm_text'])) { echo <<submit_button_config['confirm_text']}')) { ok = false; } EOD; } if ($this->has_file) { echo << 10 * 1024 * 1024) { $('#div-' + $(this).attr('name')).addClass('has-error'); // for bootstrap4 $('#input-' + $(this).attr('name')).addClass('is-invalid'); $('#help-' + $(this).attr('name')).text('文件大小不能超过10M'); ok = false; } else { $('#div-' + $(this).attr('name')).removeClass('has-error'); // for bootstrap4 $('#input-' + $(this).attr('name')).removeClass('is-invalid'); $('#help-' + $(this).attr('name')).text(''); } } }); EOD; } if ($this->ajax_submit_js !== null) { echo <<form_name}', value: '{$this->form_name}', type: 'submit'}); }, success : {$this->ajax_submit_js} }); } EOD; } else { echo << EOD; } private function validateAtServer() { $errors = array(); if ($this->extra_validator) { $fun = $this->extra_validator; $err = $fun(); if ($err) { $errors['extra'] = $err; } } foreach ($this->data as $field) { if (!isset($field['no_val']) && isset($_POST[$field['name']])) { $fun = $field['validator_php']; $err = $fun($_POST[$field['name']], $this->vdata); if ($err) { $errors[$field['name']] = $err; } } } return $errors; } public function runAtServer() { foreach ($this->run_at_server_handler as $type => $handler) { if (isset($_POST[$type])) { $handler(); } } } } function newAddDelCmdForm($form_name, $validate, $handle, $final = null) { $form = new UOJForm($form_name); $form->addVTextArea( $form_name . '_cmds', '命令', '', function($str, &$vdata) use ($validate) { $cmds = array(); foreach (explode("\n", $str) as $line_id => $raw_line) { $line = trim($raw_line); if ($line == '') { continue; } if ($line[0] != '+' && $line[0] != '-') { return '第' . ($line_id + 1) . '行:格式错误'; } $obj = trim(substr($line, 1)); if ($err = $validate($obj)) { return '第' . ($line_id + 1) . '行:' . $err; } $cmds[] = array('type' => $line[0], 'obj' => $obj); } $vdata['cmds'] = $cmds; return ''; }, null ); if (!isset($final)) { $form->handle = function(&$vdata) use ($handle) { foreach ($vdata['cmds'] as $cmd) { $handle($cmd['type'], $cmd['obj']); } }; } else { $form->handle = function(&$vdata) use ($handle, $final) { foreach ($vdata['cmds'] as $cmd) { $handle($cmd['type'], $cmd['obj']); } $final(); }; } return $form; } function newSubmissionForm($form_name, $requirement, $zip_file_name_gen, $handle) { $form = new UOJForm($form_name); foreach ($requirement as $req) { if ($req['type'] == "source code") { $languages = isset($req['languages']) ? $req['languages'] : $GLOBALS['uojSupportedLanguages']; $form->addSourceCodeInput("{$form_name}_{$req['name']}", UOJLocale::get('problems::source code').':'.$req['name'], $languages); } elseif ($req['type'] == "text") { $form->addTextFileInput("{$form_name}_{$req['name']}", UOJLocale::get('problems::text file').':'.$req['file_name']); } } $form->handle = function(&$vdata) use ($form_name, $requirement, $zip_file_name_gen, $handle) { global $myUser; if ($myUser == null) { redirectToLogin(); } $tot_size = 0; $zip_file_name = $zip_file_name_gen(); $zip_file = new ZipArchive(); if ($zip_file->open(UOJContext::storagePath().$zip_file_name, ZipArchive::CREATE) !== true) { becomeMsgPage('提交失败'); } $content = array(); $content['file_name'] = $zip_file_name; $content['config'] = array(); foreach ($requirement as $req) { if ($req['type'] == "source code") { $content['config'][] = array("{$req['name']}_language", $_POST["{$form_name}_{$req['name']}_language"]); } } foreach ($requirement as $req) { if ($_POST["{$form_name}_{$req['name']}_upload_type"] == 'editor') { $zip_file->addFromString($req['file_name'], $_POST["{$form_name}_{$req['name']}_editor"]); } else { $tmp_name = UOJForm::uploadedFileTmpName("{$form_name}_{$req['name']}_file"); if ($tmp_name == null) { $zip_file->addFromString($req['file_name'], ''); } else { $zip_file->addFile($tmp_name, $req['file_name']); } } $stat = $zip_file->statName($req['file_name']); if ($req['type'] == 'source code') { $max_size = isset($req['size']) ? (int)$req['size'] : 50; if ($stat['size'] > $max_size * 1024) { $zip_file->close(); unlink(UOJContext::storagePath().$zip_file_name); becomeMsgPage("源代码长度不能超过 {$max_size}KB。"); } } $tot_size += $stat['size']; } $zip_file->close(); $handle($zip_file_name, $content, $tot_size); }; return $form; } function newZipSubmissionForm($form_name, $requirement, $zip_file_name_gen, $handle) { $form = new UOJForm($form_name); $name = "zip_ans_{$form_name}"; $text = UOJLocale::get('problems::zip file upload introduction', join(array_map(function($req) { return $req['file_name']; }, $requirement), ', ')); $browse_text = UOJLocale::get('browse'); $html = << $text $browse_text EOD; $form->addNoVal($name, $html); $form->is_big = true; $form->has_file = true; $form->handle = function() use ($name, $requirement, $zip_file_name_gen, $handle) { global $myUser; if ($myUser == null) { redirectToLogin(); } if (!isset($_FILES[$name])) { becomeMsgPage('你在干啥……怎么什么都没交过来……?'); } elseif (!is_uploaded_file($_FILES[$name]['tmp_name'])) { becomeMsgPage('上传出错,貌似你什么都没交过来……?'); } $up_zip_file = new ZipArchive(); if ($up_zip_file->open($_FILES[$name]['tmp_name']) !== true) { becomeMsgPage('不是合法的zip压缩文件'); } $tot_size = 0; $zip_content = array(); foreach ($requirement as $req) { $stat = $up_zip_file->statName($req['file_name']); if ($stat === false) { $zip_content[$req['name']] = ''; } else { $tot_size += $stat['size']; if ($stat['size'] > 20 * 1024 * 1024) { becomeMsgPage("文件 {$req['file_name']} 实际大小过大。"); } $ret = $up_zip_file->getFromName($req['file_name']); if ($ret === false) { $zip_content[$req['name']] = ''; } else { $zip_content[$req['name']] = $ret; } } } $up_zip_file->close(); $zip_file_name = $zip_file_name_gen(); $zip_file = new ZipArchive(); if ($zip_file->open(UOJContext::storagePath().$zip_file_name, ZipArchive::CREATE) !== true) { becomeMsgPage('提交失败'); } foreach ($requirement as $req) { $zip_file->addFromString($req['file_name'], $zip_content[$req['name']]); } $zip_file->close(); $content = array(); $content['file_name'] = $zip_file_name; $content['config'] = array(); $handle($zip_file_name, $content, $tot_size); }; return $form; } ?>