博客部分
当用户需要针对OJ上的比赛以及题目进行相关讨论的时候,OJ平台给大家一个博客平台供大家交流讨论。博客部分中利用blogs.php
文件进行最近前10个博客的显示,用户可以利用右上角的按钮查看自己的博客情况以及自己写一个博客。
博客总览
blogs.php
文件用于生成一个博客总览页面,展示了所有非隐藏博客的标题、发表者和发表日期,并提供了一些额外的功能按钮和表格样式。
1. 引入和函数定义:
<?php
requirePHPLib('form'); // 引入表单相关的 PHP 库
function echoBlogCell($blog) {
echo '<tr>';
echo '<td>' . getBlogLink($blog['id']) . '</td>';
echo '<td>' . getUserLink($blog['poster']) . '</td>';
echo '<td>' . $blog['post_time'] . '</td>';
echo '</tr>';
}
?>
requirePHPLib('form');
: 引入了一个与表单相关的 PHP 库,可能包含了一些表单生成和处理的函数。echoBlogCell($blog)
: 定义了一个函数,用于输出每个博客的表格行。显示了博客的 ID、发表者和发表日期。
2. 页面头部设置:
<?php
$header = <<<EOD
<tr>
<th width="60%">标题</th>
<th width="20%">发表者</th>
<th width="20%">发表日期</th>
</tr>
EOD;
?>
$header
: 一个用 HEREDOC 语法定义的字符串,包含了表格的列标题,分别为“标题”、“发表者”和“发表日期”。
3. 配置数组设置:
<?php
$config = array();
$config['table_classes'] = array('table', 'table-hover');
?>
$config
: 一个包含表格样式类的关联数组,用于定义表格的外观。这里设置了表格的类为'table'
和'table-hover'
,使其具有 Bootstrap 的表格和鼠标悬停效果。
4. 页面内容渲染:
<?php echoUOJPageHeader(UOJLocale::get('blogs')) ?>
<?php if (Auth::check()): ?>
<div class="float-right">
<div class="btn-group">
<a href="<?= HTML::blog_url(Auth::id(), '/') ?>" class="btn btn-secondary btn-sm">我的博客首页</a>
<a href="<?= HTML::blog_url(Auth::id(), '/post/new/write')?>" class="btn btn-primary btn-sm"><span class="glyphicon glyphicon-edit"></span> 写新博客</a>
</div>
</div>
<?php endif ?>
<h3>博客总览</h3>
<?php echoLongTable(array('id', 'poster', 'title', 'post_time', 'zan'), 'blogs', 'is_hidden = 0', 'order by post_time desc', $header, 'echoBlogCell', $config); ?>
<?php echoUOJPageFooter() ?>
echoUOJPageHeader(UOJLocale::get('blogs'))
: 输出页面的标题,使用了 UOJLocale 获取本地化的“blogs”。if (Auth::check())
: 如果用户已登录,则显示一个按钮组,包含“我的博客首页”和“写新博客”的链接。echoLongTable(...)
: 调用了一个自定义的函数echoLongTable
来生成一个长格式的表格,显示了数据库中满足条件的博客信息。array('id', 'poster', 'title', 'post_time', 'zan')
: 指定了要显示的列。'blogs'
: 表格所对应的数据库表名。'is_hidden = 0'
: 查询条件,仅显示未隐藏的博客。'order by post_time desc'
: 按照发表日期降序排序。$header
: 表格的列标题。'echoBlogCell'
: 用于渲染每一行数据的回调函数。$config
: 包含了表格样式的配置数组。
5. 页面底部设置:
<?php echoUOJPageFooter() ?>
echoUOJPageFooter()
: 输出页面的底部内容,可能包括一些脚本、版权信息或其他页面结尾的内容。
博客显示
1. 条件判断部分:
if ($is_preview) {
$readmore_pos = strpos($blog['content'], '<!-- readmore -->');
if ($readmore_pos !== false) {
$content = substr($blog['content'], 0, $readmore_pos).'<p><a href="'.HTML::blog_url(UOJContext::userid(), '/post/'.$blog['id']).'">阅读更多……</a></p>';
} else {
$content = $blog['content'];
}
} else {
$content = $blog['content'];
}
- 如果
$is_preview
为真,则检查$blog['content']
是否包含<!-- readmore -->
标记。 - 如果包含,则将文章内容截取到
<!-- readmore -->
之前,并在末尾添加一个链接“阅读更多……”。 - 如果不包含或不是预览模式,则直接使用完整的
$blog['content']
。
2. 额外文本:
$extra_text = $blog['is_hidden'] ? '<span class="text-muted">[已隐藏]</span> ' : '';
- 如果
$blog['is_hidden']
为真,则在标题前面添加一个灰色文本“[已隐藏]”。
3. 页面显示部分:
<h2><?= $extra_text ?><a class="header-a" href="<?= HTML::blog_url(UOJContext::userid(), '/post/'.$blog['id']) ?>"><?= $blog['title'] ?></a></h2>
<div><?= $blog['post_time'] ?> <strong>By</strong> <?= getUserLink($blog['poster']) ?></div>
<?php if (!$show_title_only): ?>
<div class="card mb-4">
<div class="card-body">
<?php if ($blog_type == 'post'): ?>
<article><?= $content ?></article>
<?php elseif ($blog_type == 'slide'): ?>
<article>
<div class="embed-responsive embed-responsive-16by9">
<iframe class="embed-responsive-item" src="<?= HTML::blog_url(UOJContext::userid(), '/slide/'.$blog['id']) ?>"></iframe>
</div>
<div class="text-right top-buffer-sm">
<a class="btn btn-secondary btn-md" href="<?= HTML::blog_url(UOJContext::userid(), '/slide/'.$blog['id']) ?>"><span class="glyphicon glyphicon-fullscreen"></span> 全屏</a>
</div>
</article>
<?php endif ?>
</div>
<div class="card-footer text-right">
<ul class="list-inline bot-buffer-no">
<li class="list-inline-item">
<?php foreach (queryBlogTags($blog['id']) as $tag): ?>
<?php echoBlogTag($tag) ?>
<?php endforeach ?>
</li>
<?php if ($is_preview): ?>
<li class="list-inline-item"><a href="<?= HTML::blog_url(UOJContext::userid(), '/post/'.$blog['id']) ?>">阅读全文</a></li>
<?php endif ?>
<?php if (Auth::check() && (isSuperUser(Auth::user()) || Auth::id() == $blog['poster'])): ?>
<li class="list-inline-item"><a href="<?=HTML::blog_url(UOJContext::userid(), '/'.$blog_type.'/'.$blog['id'].'/write')?>">修改</a></li>
<li class="list-inline-item"><a href="<?=HTML::blog_url(UOJContext::userid(), '/post/'.$blog['id'].'/delete')?>">删除</a></li>
<?php endif ?>
<li class="list-inline-item"><?= getClickZanBlock('B', $blog['id'], $blog['zan']) ?></li>
</ul>
</div>
</div>
<?php endif ?>
- 首先显示博客的标题,包括可能的隐藏标记。
- 显示发布时间和作者信息。
- 如果不是仅显示标题 (
$show_title_only
为假),则显示博客内容或幻灯片。 - 根据
$blog_type
的值选择显示不同的内容结构。 - 最后显示标签、阅读全文链接(如果是预览模式)、编辑和删除链接(如果用户有权限),以及点赞按钮。
博客评论系统实现
blog.php
实现了一个博客评论系统的功能。让我们逐步解释其主要部分和功能:
1. 验证博客ID和权限:
if (!isset($_GET['id']) || !validateUInt($_GET['id']) || !($blog = queryBlog($_GET['id'])) || !UOJContext::isHis($blog)) {
become404Page();
}
if ($blog['is_hidden'] && !UOJContext::hasBlogPermission()) {
become403Page();
}
- 首先检查是否传递了有效的博客ID,并验证其有效性。
- 使用
queryBlog($_GET['id'])
函数查询数据库获取博客信息,并确保当前用户有权查看此博客(通过UOJContext::isHis($blog)
函数)。 - 如果博客被隐藏且当前用户没有博客权限,则跳转到404页面或403页面。
2. 评论表单处理:
$comment_form = new UOJForm('comment');
$comment_form->addVTextArea('comment', '内容', '',
function($comment) {
global $myUser;
if ($myUser == null) {
return '请先登录';
}
if (!$comment) {
return '评论不能为空';
}
if (strlen($comment) > 1000) {
return '不能超过1000个字节';
}
return '';
},
null
);
$comment_form->handle = function() {
// 处理评论提交的逻辑
// ...
};
$comment_form->ctrl_enter_submit = true;
$comment_form->runAtServer();
- 创建名为
comment
的表单,包含一个验证文本区域用于评论内容。 - 定义了匿名函数作为验证回调,验证评论内容的合法性(登录状态、非空、字节长度限制)。
handle
属性定义了当提交表单时执行的逻辑,处理评论的插入、通知等操作。ctrl_enter_submit
设置为true
,允许使用 Ctrl + Enter 快捷键提交评论。runAtServer()
方法运行表单处理逻辑,处理用户提交的评论数据。
3. 回复表单处理:
$reply_form = new UOJForm('reply');
$reply_form->addHidden('reply_id', '0',
function($reply_id, &$vdata) {
// 验证回复对象的有效性
// ...
},
null
);
$reply_form->addVTextArea('reply_comment', '内容', '',
function($comment) {
// 验证回复内容的合法性
// ...
},
null
);
$reply_form->handle = function(&$vdata) {
// 处理回复提交的逻辑
// ...
};
$reply_form->ctrl_enter_submit = true;
$reply_form->runAtServer();
- 创建名为
reply
的表单,包含一个隐藏字段用于回复的父评论ID和一个验证文本区域用于回复内容。 - 定义了匿名函数作为回复ID验证的回调,验证回复对象的存在和有效性。
- 定义了匿名函数作为回复内容验证的回调,验证回复内容的合法性(登录状态、非空、字节长度限制)。
handle
属性定义了当提交表单时执行的逻辑,处理回复的插入、通知等操作。ctrl_enter_submit
设置为true
,允许使用 Ctrl + Enter 快捷键提交回复。
4. 评论分页处理:
$comments_pag = new Paginator(array(
'col_names' => array('*'),
'table_name' => 'blogs_comments',
'cond' => 'blog_id = ' . $blog['id'] . ' and reply_id = 0',
'tail' => 'order by id asc',
'page_len' => 20
));
- 创建一个名为
comments_pag
的分页器对象,用于处理博客评论的分页显示。 - 指定了表名、条件(当前博客ID并且不是回复评论)、排序方式等参数。
博客编辑
blog_editor.php
展示了一个基于HTML和PHP的表单,主要用于编辑博客或类似内容的功能。
1. 表单字段:
<div class="row">
<div class="col-sm-6">
<?= HTML::div_vinput("{$editor->name}_title", 'text', $editor->label_text['title'], html_entity_decode($editor->cur_data['title'])) ?>
</div>
<div class="col-sm-6">
<?= HTML::div_vinput("{$editor->name}_tags", 'text', $editor->label_text['tags'], join(', ', $editor->cur_data['tags'])) ?>
</div>
</div>
- 显示了两个输入框,分别用于标题和标签。这里使用了
HTML::div_vinput
方法生成带有标签和初始值的输入框。
2. 文本区域:
<?= HTML::div_vtextarea("{$editor->name}_content_md", $editor->label_text['content'], $editor->cur_data['content_md']) ?>
- 显示了一个文本域,用于输入博客内容。同样使用了
HTML::div_vtextarea
方法生成带有标签和初始值的文本域。
public static function div_vtextarea($name, $label_text, $default_value) {
return '<div id="'."div-$name".'">'
. '<label for="'."input-$name".'" class="control-label">'.$label_text.'</label>'
. '<textarea class="form-control" name="'.$name.'" id="'."input-$name".'">'.HTML::escape($default_value).'</textarea>'
. '<span class="help-block" id="'."help-$name".'"></span>'
. '</div>';
}
- 这段代码的核心功能是生成带有标签、默认值和安全转义的文本域,适用于构建表单页面中的各种输入字段
3. 按钮和复选框:
<div class="row mt-2">
<div class="col-sm-6">
<?php if ($editor->blog_url): ?>
<a id="a-<?= $editor->name ?>_view_blog" class="btn btn-info" href="<?= HTML::escape($editor->blog_url) ?>"><?= $editor->label_text['view blog'] ?></a>
<?php else: ?>
<a id="a-<?= $editor->name ?>_view_blog" class="btn btn-info" style="display: none;"><?= $editor->label_text['view blog'] ?></a>
<?php endif ?>
</div>
<div class="col-sm-6 text-right">
<?= HTML::checkbox("{$editor->name}_is_hidden", $editor->cur_data['is_hidden']) ?>
</div>
</div>
- 左侧是一个条件显示的按钮,根据
$editor->blog_url
的值决定显示与否。 - 右侧是一个复选框,用于设置博客的可见性(公开或私密)。
4. JavaScript初始化:
<script type="text/javascript">
$('#<?= "input-{$editor->name}_is_hidden" ?>').bootstrapSwitch({
onText: <?= json_encode($editor->label_text['private']) ?>,
onColor: 'danger',
offText: <?= json_encode($editor->label_text['public']) ?>,
offColor: 'primary',
labelText: <?= json_encode($editor->label_text['blog visibility']) ?>,
handleWidth: 100
});
blog_editor_init("<?= $editor->name ?>", <?= json_encode(array('type' => $editor->type)) ?>);
</script>
- 使用了jQuery选择器
$('#<?= "input-{$editor->name}_is_hidden" ?>')
来初始化复选框插件bootstrapSwitch
,以便用户可以直观地切换博客的私密或公开状态。 - 调用了自定义的
blog_editor_init
函数,初始化博客编辑器,传入了编辑器的名称和类型。