mirror of
https://github.com/renbaoshuo/S2OJ.git
synced 2024-11-22 13:28:41 +00:00
feat(problem): insert pdf in statement
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
This commit is contained in:
parent
24c5ad4e5f
commit
5f7bf17fe0
@ -98,6 +98,12 @@ $new_remote_problem_form->handle = function (&$vdata) {
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$id = DB::insert_id();
|
$id = DB::insert_id();
|
||||||
|
dataNewProblem($id);
|
||||||
|
|
||||||
|
if ($data['type'] == 'pdf') {
|
||||||
|
file_put_contents(UOJContext::storagePath() , "/problem_resources/$id/statement.pdf", $data['pdf_data']);
|
||||||
|
$data['statement'] = "<div data-pdf data-src=\"/problem/$id/resources/statement.pdf\"></div>\n" . $data['statement'];
|
||||||
|
}
|
||||||
|
|
||||||
DB::insert([
|
DB::insert([
|
||||||
"insert into problems_contents",
|
"insert into problems_contents",
|
||||||
@ -105,7 +111,6 @@ $new_remote_problem_form->handle = function (&$vdata) {
|
|||||||
"values",
|
"values",
|
||||||
DB::tuple([$id, HTML::purifier(['a' => ['target' => 'Enum#_blank']])->purify($data['statement']), '', ''])
|
DB::tuple([$id, HTML::purifier(['a' => ['target' => 'Enum#_blank']])->purify($data['statement']), '', ''])
|
||||||
]);
|
]);
|
||||||
dataNewProblem($id);
|
|
||||||
|
|
||||||
DB::insert([
|
DB::insert([
|
||||||
"insert into problems_tags",
|
"insert into problems_tags",
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
requireLib('bootstrap5');
|
requireLib('bootstrap5');
|
||||||
requireLib('hljs');
|
requireLib('hljs');
|
||||||
requireLib('mathjax');
|
requireLib('mathjax');
|
||||||
|
requireLib('pdf.js');
|
||||||
requirePHPLib('form');
|
requirePHPLib('form');
|
||||||
requirePHPLib('judger');
|
requirePHPLib('judger');
|
||||||
|
|
||||||
|
@ -144,6 +144,11 @@ if (UOJProblem::info('type') == 'remote') {
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($data['type'] == 'pdf') {
|
||||||
|
file_put_contents(UOJContext::storagePath() . "/problem_resources/" . UOJProblem::info('id') . "/statement.pdf", $data['pdf_data']);
|
||||||
|
$data['statement'] = '<div data-pdf data-src="/problem/' . UOJProblem::info('id') . '/resources/statement.pdf"></div>' . "\n" . $data['statement'];
|
||||||
|
}
|
||||||
|
|
||||||
DB::update([
|
DB::update([
|
||||||
"update problems_contents",
|
"update problems_contents",
|
||||||
"set", [
|
"set", [
|
||||||
|
@ -436,6 +436,10 @@ class HTML {
|
|||||||
$def->addElement('footer', 'Block', 'Flow', 'Common');
|
$def->addElement('footer', 'Block', 'Flow', 'Common');
|
||||||
|
|
||||||
mergeConfig($extra_allowed_html, [
|
mergeConfig($extra_allowed_html, [
|
||||||
|
'div' => [
|
||||||
|
'data-pdf' => 'Text',
|
||||||
|
'data-src' => 'URI',
|
||||||
|
],
|
||||||
'span' => [
|
'span' => [
|
||||||
'class' => 'Enum#uoj-username',
|
'class' => 'Enum#uoj-username',
|
||||||
'data-realname' => 'Text',
|
'data-realname' => 'Text',
|
||||||
|
@ -9,7 +9,7 @@ class UOJBlogEditor {
|
|||||||
public $post_data = [];
|
public $post_data = [];
|
||||||
public $show_editor = true;
|
public $show_editor = true;
|
||||||
public $show_tags = true;
|
public $show_tags = true;
|
||||||
|
|
||||||
public $label_text = [
|
public $label_text = [
|
||||||
'title' => '标题',
|
'title' => '标题',
|
||||||
'tags' => '标签(多个标签用逗号隔开)',
|
'tags' => '标签(多个标签用逗号隔开)',
|
||||||
@ -19,15 +19,15 @@ class UOJBlogEditor {
|
|||||||
'private' => '未公开',
|
'private' => '未公开',
|
||||||
'public' => '公开'
|
'public' => '公开'
|
||||||
];
|
];
|
||||||
|
|
||||||
public $validator = array();
|
public $validator = array();
|
||||||
|
|
||||||
function __construct() {
|
function __construct() {
|
||||||
global $REQUIRE_LIB;
|
global $REQUIRE_LIB;
|
||||||
$REQUIRE_LIB['blog-editor'] = '';
|
$REQUIRE_LIB['blog-editor'] = '';
|
||||||
|
|
||||||
$this->validator = [
|
$this->validator = [
|
||||||
'title' => function(&$title) {
|
'title' => function (&$title) {
|
||||||
if ($title == '') {
|
if ($title == '') {
|
||||||
return '标题不能为空';
|
return '标题不能为空';
|
||||||
}
|
}
|
||||||
@ -39,13 +39,13 @@ class UOJBlogEditor {
|
|||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
'content_md' => function(&$content_md) {
|
'content_md' => function (&$content_md) {
|
||||||
if (strlen($content_md) > 1000000) {
|
if (strlen($content_md) > 1000000) {
|
||||||
return '内容过长';
|
return '内容过长';
|
||||||
}
|
}
|
||||||
return '';
|
return '';
|
||||||
},
|
},
|
||||||
'tags' => function(&$tags) {
|
'tags' => function (&$tags) {
|
||||||
$tags = str_replace(',', ',', $tags);
|
$tags = str_replace(',', ',', $tags);
|
||||||
$tags_raw = explode(',', $tags);
|
$tags_raw = explode(',', $tags);
|
||||||
if (count($tags_raw) > 10) {
|
if (count($tags_raw) > 10) {
|
||||||
@ -58,10 +58,10 @@ class UOJBlogEditor {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (strlen($tag) > 30) {
|
if (strlen($tag) > 30) {
|
||||||
return '标签 “' . HTML::escape($tag) .'” 太长';
|
return '标签 “' . HTML::escape($tag) . '” 太长';
|
||||||
}
|
}
|
||||||
if (in_array($tag, $tags, true)) {
|
if (in_array($tag, $tags, true)) {
|
||||||
return '标签 “' . HTML::escape($tag) .'” 重复出现';
|
return '标签 “' . HTML::escape($tag) . '” 重复出现';
|
||||||
}
|
}
|
||||||
$tags[] = $tag;
|
$tags[] = $tag;
|
||||||
}
|
}
|
||||||
@ -69,7 +69,7 @@ class UOJBlogEditor {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function validate($name) {
|
public function validate($name) {
|
||||||
if (!isset($_POST["{$this->name}_{$name}"])) {
|
if (!isset($_POST["{$this->name}_{$name}"])) {
|
||||||
return '不能为空';
|
return '不能为空';
|
||||||
@ -98,14 +98,14 @@ class UOJBlogEditor {
|
|||||||
die(json_encode($errors));
|
die(json_encode($errors));
|
||||||
}
|
}
|
||||||
crsf_defend();
|
crsf_defend();
|
||||||
|
|
||||||
$this->post_data['is_hidden'] = isset($_POST["{$this->name}_is_hidden"]) ? 1 : 0;
|
$this->post_data['is_hidden'] = isset($_POST["{$this->name}_is_hidden"]) ? 1 : 0;
|
||||||
|
|
||||||
$purifier = HTML::purifier();
|
$purifier = HTML::purifier();
|
||||||
$parsedown = HTML::parsedown();
|
$parsedown = HTML::parsedown();
|
||||||
|
|
||||||
$this->post_data['title'] = HTML::escape($this->post_data['title']);
|
$this->post_data['title'] = HTML::escape($this->post_data['title']);
|
||||||
|
|
||||||
if ($this->show_editor) {
|
if ($this->show_editor) {
|
||||||
if ($this->type == 'blog') {
|
if ($this->type == 'blog') {
|
||||||
$this->post_data['content'] = $parsedown->text($this->post_data['content_md']);
|
$this->post_data['content'] = $parsedown->text($this->post_data['content_md']);
|
||||||
@ -113,7 +113,7 @@ class UOJBlogEditor {
|
|||||||
if (preg_match('/^.*<!--.*readmore.*-->.*$/m', $this->post_data['content'], $matches, PREG_OFFSET_CAPTURE)) {
|
if (preg_match('/^.*<!--.*readmore.*-->.*$/m', $this->post_data['content'], $matches, PREG_OFFSET_CAPTURE)) {
|
||||||
$content_less = substr($this->post_data['content'], 0, $matches[0][1]);
|
$content_less = substr($this->post_data['content'], 0, $matches[0][1]);
|
||||||
$content_more = substr($this->post_data['content'], $matches[0][1] + strlen($matches[0][0]));
|
$content_more = substr($this->post_data['content'], $matches[0][1] + strlen($matches[0][0]));
|
||||||
$this->post_data['content'] = $purifier->purify($content_less).'<!-- readmore -->'.$purifier->purify($content_more);
|
$this->post_data['content'] = $purifier->purify($content_less) . '<!-- readmore -->' . $purifier->purify($content_more);
|
||||||
} else {
|
} else {
|
||||||
$this->post_data['content'] = $purifier->purify($this->post_data['content']);
|
$this->post_data['content'] = $purifier->purify($this->post_data['content']);
|
||||||
}
|
}
|
||||||
@ -122,20 +122,22 @@ class UOJBlogEditor {
|
|||||||
if ($content_array === false || !is_array($content_array)) {
|
if ($content_array === false || !is_array($content_array)) {
|
||||||
die(json_encode(array('content_md' => '不合法的 YAML 格式')));
|
die(json_encode(array('content_md' => '不合法的 YAML 格式')));
|
||||||
}
|
}
|
||||||
|
|
||||||
$marked = function($md) use ($parsedown, $purifier) {
|
$marked = function ($md) use ($parsedown, $purifier) {
|
||||||
$dom = new DOMDocument;
|
$dom = new DOMDocument;
|
||||||
$dom->loadHTML(mb_convert_encoding($parsedown->text($md), 'HTML-ENTITIES', 'UTF-8'));
|
$dom->loadHTML(mb_convert_encoding($parsedown->text($md), 'HTML-ENTITIES', 'UTF-8'));
|
||||||
$elements = $dom->getElementsByTagName('li');
|
$elements = $dom->getElementsByTagName('li');
|
||||||
|
|
||||||
foreach ($elements as $element) {
|
foreach ($elements as $element) {
|
||||||
$element->setAttribute('class',
|
$element->setAttribute(
|
||||||
$element->getAttribute('class') . ' fragment');
|
'class',
|
||||||
|
$element->getAttribute('class') . ' fragment'
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $purifier->purify($dom->saveHTML());
|
return $purifier->purify($dom->saveHTML());
|
||||||
};
|
};
|
||||||
|
|
||||||
$config = array();
|
$config = array();
|
||||||
$this->post_data['content'] = '';
|
$this->post_data['content'] = '';
|
||||||
foreach ($content_array as $slide_name => $slide_content) {
|
foreach ($content_array as $slide_name => $slide_content) {
|
||||||
@ -147,9 +149,9 @@ class UOJBlogEditor {
|
|||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->post_data['content'] .= '<section>';
|
$this->post_data['content'] .= '<section>';
|
||||||
|
|
||||||
if (is_string($slide_content)) {
|
if (is_string($slide_content)) {
|
||||||
$this->post_data['content'] .= $marked($slide_content);
|
$this->post_data['content'] .= $marked($slide_content);
|
||||||
} elseif (is_array($slide_content)) {
|
} elseif (is_array($slide_content)) {
|
||||||
@ -167,7 +169,7 @@ class UOJBlogEditor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function handleSave() {
|
public function handleSave() {
|
||||||
global $REQUIRE_LIB;
|
global $REQUIRE_LIB;
|
||||||
|
|
||||||
@ -177,11 +179,14 @@ class UOJBlogEditor {
|
|||||||
if (!$ret) {
|
if (!$ret) {
|
||||||
$ret = array();
|
$ret = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($_POST['need_preview'])) {
|
if (isset($_POST['need_preview'])) {
|
||||||
ob_start();
|
ob_start();
|
||||||
if ($this->type == 'blog') {
|
if ($this->type == 'blog') {
|
||||||
$req_lib = array('mathjax' => '');
|
$req_lib = [
|
||||||
|
'mathjax' => '',
|
||||||
|
'pdf.js' => '',
|
||||||
|
];
|
||||||
|
|
||||||
if (isset($REQUIRE_LIB['bootstrap5'])) {
|
if (isset($REQUIRE_LIB['bootstrap5'])) {
|
||||||
$req_lib['bootstrap5'] = '';
|
$req_lib['bootstrap5'] = '';
|
||||||
@ -210,10 +215,10 @@ class UOJBlogEditor {
|
|||||||
$ret['html'] = ob_get_contents();
|
$ret['html'] = ob_get_contents();
|
||||||
ob_end_clean();
|
ob_end_clean();
|
||||||
}
|
}
|
||||||
|
|
||||||
die(json_encode($ret));
|
die(json_encode($ret));
|
||||||
}
|
}
|
||||||
|
|
||||||
public function runAtServer() {
|
public function runAtServer() {
|
||||||
if (isset($_POST["save-{$this->name}"])) {
|
if (isset($_POST["save-{$this->name}"])) {
|
||||||
$this->handleSave();
|
$this->handleSave();
|
||||||
@ -221,7 +226,7 @@ class UOJBlogEditor {
|
|||||||
}
|
}
|
||||||
public function printHTML() {
|
public function printHTML() {
|
||||||
global $REQUIRE_LIB;
|
global $REQUIRE_LIB;
|
||||||
|
|
||||||
uojIncludeView('blog-editor', ['editor' => $this, 'REQUIRE_LIB' => $REQUIRE_LIB]);
|
uojIncludeView('blog-editor', ['editor' => $this, 'REQUIRE_LIB' => $REQUIRE_LIB]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,9 +7,12 @@ class UOJMarkdown extends ParsedownMath {
|
|||||||
|
|
||||||
$this->options['username_with_color'] = $options['username_with_color'] ?: false;
|
$this->options['username_with_color'] = $options['username_with_color'] ?: false;
|
||||||
|
|
||||||
|
// Special Block
|
||||||
|
$this->inlineMarkerList .= '@';
|
||||||
|
$this->InlineTypes['@'][] = 'SpecialBlock';
|
||||||
|
|
||||||
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
|
// https://gist.github.com/ShNURoK42/b5ce8baa570975db487c
|
||||||
$this->InlineTypes['@'][] = 'UserMention';
|
$this->InlineTypes['@'][] = 'UserMention';
|
||||||
$this->inlineMarkerList .= '@';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/taufik-nurrohman/parsedown-extra-plugin/blob/1653418c5a9cf5277cd28b0b23ba2d95d18e9bc4/ParsedownExtraPlugin.php#L340-L345
|
// https://github.com/taufik-nurrohman/parsedown-extra-plugin/blob/1653418c5a9cf5277cd28b0b23ba2d95d18e9bc4/ParsedownExtraPlugin.php#L340-L345
|
||||||
@ -85,4 +88,35 @@ class UOJMarkdown extends ParsedownMath {
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function inlineSpecialBlock($Excerpt) {
|
||||||
|
if (!isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[') {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$Excerpt['text'] = substr($Excerpt['text'], 1);
|
||||||
|
|
||||||
|
$Link = $this->inlineLink($Excerpt);
|
||||||
|
|
||||||
|
if ($Link === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$Inline = [
|
||||||
|
'extent' => $Link['extent'] + 1,
|
||||||
|
'element' => [
|
||||||
|
'name' => 'div',
|
||||||
|
'attributes' => [
|
||||||
|
'data-src' => $Link['element']['attributes']['href'],
|
||||||
|
"data-{$Link['element']['text']}" => $Link['element']['text'],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
];
|
||||||
|
|
||||||
|
$Inline['element']['attributes'] += $Link['element']['attributes'];
|
||||||
|
|
||||||
|
unset($Inline['element']['attributes']['href']);
|
||||||
|
|
||||||
|
return $Inline;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -196,13 +196,14 @@ class UOJRemoteProblem {
|
|||||||
'time_limit' => null,
|
'time_limit' => null,
|
||||||
'memory_limit' => null,
|
'memory_limit' => null,
|
||||||
'difficulty' => -1,
|
'difficulty' => -1,
|
||||||
|
'pdf_data' => $res['response'],
|
||||||
'statement' => HTML::tag('h3', [], '提示') .
|
'statement' => HTML::tag('h3', [], '提示') .
|
||||||
HTML::tag(
|
HTML::tag(
|
||||||
'p',
|
'p',
|
||||||
[],
|
[],
|
||||||
'本题题面为 PDF 题面,请' .
|
'若无法正常加载 PDF,请' .
|
||||||
HTML::tag('a', ['href' => static::getCodeforcesProblemUrl($id), 'target' => '_blank'], '点此') .
|
HTML::tag('a', ['href' => static::getCodeforcesProblemUrl($id), 'target' => '_blank'], '点此') .
|
||||||
'以查看题面。'
|
'查看原题面。'
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
} else {
|
} else {
|
||||||
|
@ -265,6 +265,54 @@ if (!isset($ShowPageHeader)) {
|
|||||||
<?= HTML::css_link('/css/font-awesome.min.css') ?>
|
<?= HTML::css_link('/css/font-awesome.min.css') ?>
|
||||||
<?php endif ?>
|
<?php endif ?>
|
||||||
|
|
||||||
|
<?php if (isset($REQUIRE_LIB['pdf.js'])) : ?>
|
||||||
|
<!-- pdf.js -->
|
||||||
|
<?= HTML::js_src('/js/pdf.js') ?>
|
||||||
|
<script>
|
||||||
|
pdfjsLib.GlobalWorkerOptions.workerSrc = '/js/pdf.worker.js';
|
||||||
|
|
||||||
|
function randstr(prefix) {
|
||||||
|
return Math.random().toString(36).replace('0.', prefix || '');
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
$('div[data-pdf]').each(function() {
|
||||||
|
var _this = $(this);
|
||||||
|
var pdf_src = $(this).data('src');
|
||||||
|
|
||||||
|
$(this).css('width', '100%').css('height', '100%');
|
||||||
|
|
||||||
|
var task = pdfjsLib.getDocument(pdf_src);
|
||||||
|
var id = 'pdf_' + task.docId;
|
||||||
|
|
||||||
|
$(this).attr('id', id + '_container');
|
||||||
|
|
||||||
|
task.promise.then(function(pdf) {
|
||||||
|
for (var i = 1; i <= pdf.numPages; i++) {
|
||||||
|
$(_this).append('<canvas id="' + id + '_page_' + i + '" class="pdf-page-canvas"></canvas>');
|
||||||
|
|
||||||
|
pdf.getPage(i).then(function(page) {
|
||||||
|
var viewport = page.getViewport({
|
||||||
|
scale: 2,
|
||||||
|
});
|
||||||
|
|
||||||
|
var canvas = document.getElementById(id + '_page_' + page.pageNumber);
|
||||||
|
|
||||||
|
canvas.height = viewport.height;
|
||||||
|
canvas.width = viewport.width;
|
||||||
|
|
||||||
|
page.render({
|
||||||
|
canvasContext: canvas.getContext('2d'),
|
||||||
|
viewport: viewport,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
<?php endif ?>
|
||||||
|
|
||||||
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
<!-- HTML5 shim and Respond.js IE8 support of HTML5 elements and media queries -->
|
||||||
<!--[if lt IE 9]>
|
<!--[if lt IE 9]>
|
||||||
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
<script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
|
||||||
|
@ -238,6 +238,10 @@ h6,
|
|||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.pdf-page-canvas {
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
form.uoj-bs4-form-inline {
|
form.uoj-bs4-form-inline {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-right: 0.3em;
|
margin-right: 0.3em;
|
||||||
|
22
web/js/pdf.js
Normal file
22
web/js/pdf.js
Normal file
File diff suppressed because one or more lines are too long
22
web/js/pdf.sandbox.js
Normal file
22
web/js/pdf.sandbox.js
Normal file
File diff suppressed because one or more lines are too long
22
web/js/pdf.worker.js
vendored
Normal file
22
web/js/pdf.worker.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user