diff --git a/Django b/Django index db6090e..de6af35 160000 --- a/Django +++ b/Django @@ -1 +1 @@ -Subproject commit db6090e01f0f159a01f48651caf20f3a80417181 +Subproject commit de6af3510d198edb88d9eb751c46957da2bd78ea diff --git a/comments-jrx/__init__.py b/comments-jrx/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/comments-jrx/__pycache__/__init__.cpython-312.pyc b/comments-jrx/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..4a16df2 Binary files /dev/null and b/comments-jrx/__pycache__/__init__.cpython-312.pyc differ diff --git a/comments-jrx/__pycache__/admin.cpython-312.pyc b/comments-jrx/__pycache__/admin.cpython-312.pyc new file mode 100644 index 0000000..4ec8558 Binary files /dev/null and b/comments-jrx/__pycache__/admin.cpython-312.pyc differ diff --git a/comments-jrx/__pycache__/apps.cpython-312.pyc b/comments-jrx/__pycache__/apps.cpython-312.pyc new file mode 100644 index 0000000..72aea10 Binary files /dev/null and b/comments-jrx/__pycache__/apps.cpython-312.pyc differ diff --git a/comments-jrx/__pycache__/forms.cpython-312.pyc b/comments-jrx/__pycache__/forms.cpython-312.pyc new file mode 100644 index 0000000..d67ba44 Binary files /dev/null and b/comments-jrx/__pycache__/forms.cpython-312.pyc differ diff --git a/comments-jrx/__pycache__/models.cpython-312.pyc b/comments-jrx/__pycache__/models.cpython-312.pyc new file mode 100644 index 0000000..3cefff9 Binary files /dev/null and b/comments-jrx/__pycache__/models.cpython-312.pyc differ diff --git a/comments-jrx/__pycache__/urls.cpython-312.pyc b/comments-jrx/__pycache__/urls.cpython-312.pyc new file mode 100644 index 0000000..e2483f5 Binary files /dev/null and b/comments-jrx/__pycache__/urls.cpython-312.pyc differ diff --git a/comments-jrx/__pycache__/utils.cpython-312.pyc b/comments-jrx/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000..e8cc5fb Binary files /dev/null and b/comments-jrx/__pycache__/utils.cpython-312.pyc differ diff --git a/comments-jrx/__pycache__/views.cpython-312.pyc b/comments-jrx/__pycache__/views.cpython-312.pyc new file mode 100644 index 0000000..dbff5a7 Binary files /dev/null and b/comments-jrx/__pycache__/views.cpython-312.pyc differ diff --git a/comments-jrx/admin.py b/comments-jrx/admin.py new file mode 100644 index 0000000..a4c2ab4 --- /dev/null +++ b/comments-jrx/admin.py @@ -0,0 +1,64 @@ +from django.contrib import admin +from django.urls import reverse +from django.utils.html import format_html +from django.utils.translation import gettext_lazy as _ + + +#jrx: 批量禁用评论的动作 +def disable_commentstatus(modeladmin, request, queryset): + queryset.update(is_enable=False) + + +#jrx: 批量启用评论的动作 +def enable_commentstatus(modeladmin, request, queryset): + queryset.update(is_enable=True) + + +disable_commentstatus.short_description = _('Disable comments') +enable_commentstatus.short_description = _('Enable comments') + + +class CommentAdmin(admin.ModelAdmin): + #jrx: 每页显示20条评论 + list_per_page = 20 + #jrx: 列表页显示的字段 + list_display = ( + 'id', + 'body', + 'link_to_userinfo', + 'link_to_article', + 'is_enable', + 'creation_time') + #jrx: 列表页可点击跳转的字段 + list_display_links = ('id', 'body', 'is_enable') + #jrx: 筛选器(按是否启用) + list_filter = ('is_enable',) + #jrx: 编辑页排除的字段(创建时间和修改时间不允许手动编辑) + exclude = ('creation_time', 'last_modify_time') + #jrx: 自定义动作(批量启用/禁用) + actions = [disable_commentstatus, enable_commentstatus] + + #jrx: 生成评论作者的后台编辑链接 + def link_to_userinfo(self, obj): + #jrx: 获取用户模型的app标签和模型名称 + info = (obj.author._meta.app_label, obj.author._meta.model_name) + #jrx: 生成用户编辑页URL + link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,)) + #jrx: 返回HTML格式的链接(显示昵称或邮箱) + return format_html( + u'%s' % + (link, obj.author.nickname if obj.author.nickname else obj.author.email)) + + #jrx: 生成评论关联文章的后台编辑链接 + def link_to_article(self, obj): + #jrx: 获取文章模型的app标签和模型名称 + info = (obj.article._meta.app_label, obj.article._meta.model_name) + #jrx: 生成文章编辑页URL + link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,)) + #jrx: 返回HTML格式的链接(显示文章标题) + return format_html( + u'%s' % (link, obj.article.title)) + + #jrx: 自定义字段在列表页的显示名称 + link_to_userinfo.short_description = _('User') + link_to_article.short_description = _('Article') \ No newline at end of file diff --git a/comments-jrx/apps.py b/comments-jrx/apps.py new file mode 100644 index 0000000..ea70691 --- /dev/null +++ b/comments-jrx/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class CommentsConfig(AppConfig): + # jrx: 评论应用的配置类,指定应用名称 + name = 'comments' diff --git a/comments-jrx/forms.py b/comments-jrx/forms.py new file mode 100644 index 0000000..85f5cc8 --- /dev/null +++ b/comments-jrx/forms.py @@ -0,0 +1,14 @@ +from django import forms +from django.forms import ModelForm + +from .models import Comment + + +class CommentForm(ModelForm): + #jrx: 父评论ID(隐藏字段,用于回复功能,非必填) + parent_comment_id = forms.IntegerField( + widget=forms.HiddenInput, required=False) + + class Meta: + model = Comment #jrx: 关联的模型 + fields = ['body'] #jrx: 需要显示的字段(评论正文) \ No newline at end of file diff --git a/comments-jrx/migrations/0001_initial.py b/comments-jrx/migrations/0001_initial.py new file mode 100644 index 0000000..19a341b --- /dev/null +++ b/comments-jrx/migrations/0001_initial.py @@ -0,0 +1,51 @@ +# Generated by Django 4.1.7 on 2023-03-02 07:14 +#jrx: 该文件为Django数据迁移文件,定义了评论模型(Comment)的数据库结构初始化操作 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + #jrx: 标识这是初始迁移(首次创建模型时生成) + initial = True + + #jrx: 依赖关系:执行此迁移前需先完成blog应用的0001_initial迁移和用户模型迁移 + dependencies = [ + ('blog', '0001_initial'), #jrx: 依赖blog应用的初始迁移(因关联Article模型) + migrations.swappable_dependency(settings.AUTH_USER_MODEL), #jrx: 依赖用户模型的迁移 + ] + + #jrx: 迁移操作列表 + operations = [ + #jrx: 创建Comment模型对应的数据库表 + migrations.CreateModel( + name='Comment', #jrx: 模型名称 + fields=[ #jrx: 模型字段定义 + #jrx: 自增主键ID(BigAutoField适用于大数据量场景) + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + #jrx: 评论正文字段,最大长度300,显示名称为“正文” + ('body', models.TextField(max_length=300, verbose_name='正文')), + #jrx: 创建时间字段,默认值为当前时间,显示名称“创建时间” + ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), + #jrx: 最后修改时间字段,默认值为当前时间,显示名称“修改时间” + ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), + #jrx: 是否显示字段,布尔类型,默认值为True,显示名称“是否显示” + ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')), + #jrx: 外键关联到blog应用的Article模型,级联删除,显示名称“文章” + ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='文章')), + #jrx: 外键关联到用户模型,级联删除,显示名称“作者” + ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')), + #jrx: 自关联外键,指向自身,用于表示上级评论(回复功能),允许为空,级联删除,显示名称“上级评论” + ('parent_comment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='上级评论')), + ], + options={ #jrx: 模型元数据配置 + 'verbose_name': '评论', #jrx: 单数显示名称 + 'verbose_name_plural': '评论', #jrx: 复数显示名称 + 'ordering': ['-id'], #jrx: 默认排序方式:按ID倒序(最新评论在前) + 'get_latest_by': 'id', #jrx: 指定通过id字段获取最新记录 + }, + ), + ] diff --git a/comments-jrx/migrations/0002_alter_comment_is_enable.py b/comments-jrx/migrations/0002_alter_comment_is_enable.py new file mode 100644 index 0000000..b223849 --- /dev/null +++ b/comments-jrx/migrations/0002_alter_comment_is_enable.py @@ -0,0 +1,24 @@ +# Generated by Django 4.1.7 on 2023-04-24 13:48 +#jrx: 该文件为Django数据迁移文件,用于修改Comment模型中is_enable字段的默认值 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + #jrx: 依赖关系:需先执行comments应用的0001_initial初始迁移 + dependencies = [ + ('comments', '0001_initial'), #jrx: 基于初始迁移进行修改 + ] + + #jrx: 迁移操作列表 + operations = [ + #jrx: 修改Comment模型的is_enable字段属性 + migrations.AlterField( + model_name='Comment', #jrx: 目标模型名称 + name='is_enable', #jrx: 要修改的字段名称 + #jrx: 字段新定义:布尔类型,默认值从True改为False,显示名称仍为“是否显示” + #jrx: 此修改意味着新评论默认不显示,需要人工审核后启用 + field=models.BooleanField(default=False, verbose_name='是否显示'), + ), + ] \ No newline at end of file diff --git a/comments-jrx/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py b/comments-jrx/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py new file mode 100644 index 0000000..80cc3cc --- /dev/null +++ b/comments-jrx/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py @@ -0,0 +1,72 @@ +# Generated by Django 4.2.5 on 2023-09-06 13:13 +#jrx: 该迁移文件用于更新Comment模型的字段名称、 verbose_name 及模型选项,可能为了国际化或统一命名规范 + +from django.conf import settings +from django.db import migrations, models +import django.db.models.deletion +import django.utils.timezone + + +class Migration(migrations.Migration): + + #jrx: 依赖关系:需先执行用户模型迁移、blog应用的0005迁移及comments应用的0002迁移 + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ('blog', '0005_alter_article_options_alter_category_options_and_more'), #jrx: 依赖blog应用的最新迁移 + ('comments', '0002_alter_comment_is_enable'), #jrx: 基于comments应用的0002迁移进行修改 + ] + + operations = [ + #jrx: 修改Comment模型的元数据选项 + migrations.AlterModelOptions( + name='comment', + #jrx: 更新模型的显示名称为英文(可能为了国际化),其他选项保持不变 + options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', 'verbose_name_plural': 'comment'}, + ), + #jrx: 删除原有的创建时间字段(旧字段名) + migrations.RemoveField( + model_name='comment', + name='created_time', + ), + #jrx: 删除原有的最后修改时间字段(旧字段名) + migrations.RemoveField( + model_name='comment', + name='last_mod_time', + ), + #jrx: 添加新的创建时间字段(新字段名),默认值为当前时间,显示名称改为英文 + migrations.AddField( + model_name='comment', + name='creation_time', + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), + ), + #jrx: 添加新的最后修改时间字段(新字段名),默认值为当前时间,显示名称改为英文 + migrations.AddField( + model_name='comment', + name='last_modify_time', + field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'), + ), + #jrx: 更新article字段的显示名称为英文 + migrations.AlterField( + model_name='comment', + name='article', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article'), + ), + #jrx: 更新author字段的显示名称为英文 + migrations.AlterField( + model_name='comment', + name='author', + field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'), + ), + #jrx: 更新is_enable字段的显示名称为英文,默认值保持False(需审核) + migrations.AlterField( + model_name='comment', + name='is_enable', + field=models.BooleanField(default=False, verbose_name='enable'), + ), + #jrx: 更新parent_comment字段的显示名称为英文 + migrations.AlterField( + model_name='comment', + name='parent_comment', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='parent comment'), + ), + ] \ No newline at end of file diff --git a/comments-jrx/migrations/__init__.py b/comments-jrx/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/comments-jrx/migrations/__pycache__/0001_initial.cpython-312.pyc b/comments-jrx/migrations/__pycache__/0001_initial.cpython-312.pyc new file mode 100644 index 0000000..14ecabf Binary files /dev/null and b/comments-jrx/migrations/__pycache__/0001_initial.cpython-312.pyc differ diff --git a/comments-jrx/migrations/__pycache__/0002_alter_comment_is_enable.cpython-312.pyc b/comments-jrx/migrations/__pycache__/0002_alter_comment_is_enable.cpython-312.pyc new file mode 100644 index 0000000..272bb7f Binary files /dev/null and b/comments-jrx/migrations/__pycache__/0002_alter_comment_is_enable.cpython-312.pyc differ diff --git a/comments-jrx/migrations/__pycache__/0003_alter_comment_options_remove_comment_created_time_and_more.cpython-312.pyc b/comments-jrx/migrations/__pycache__/0003_alter_comment_options_remove_comment_created_time_and_more.cpython-312.pyc new file mode 100644 index 0000000..07b0b9d Binary files /dev/null and b/comments-jrx/migrations/__pycache__/0003_alter_comment_options_remove_comment_created_time_and_more.cpython-312.pyc differ diff --git a/comments-jrx/migrations/__pycache__/__init__.cpython-312.pyc b/comments-jrx/migrations/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..d6b09cc Binary files /dev/null and b/comments-jrx/migrations/__pycache__/__init__.cpython-312.pyc differ diff --git a/comments-jrx/models.py b/comments-jrx/models.py new file mode 100644 index 0000000..9670a14 --- /dev/null +++ b/comments-jrx/models.py @@ -0,0 +1,48 @@ +from django.conf import settings +from django.db import models +from django.utils.timezone import now +from django.utils.translation import gettext_lazy as _ + +from blog.models import Article + + +# Create your models here. + +class Comment(models.Model): + #jrx: 评论正文,最大长度300 + body = models.TextField('正文', max_length=300) + #jrx: 评论创建时间,默认当前时间 + creation_time = models.DateTimeField(_('creation time'), default=now) + #jrx: 评论最后修改时间,默认当前时间 + last_modify_time = models.DateTimeField(_('last modify time'), default=now) + #jrx: 评论作者(外键关联用户表) + author = models.ForeignKey( + settings.AUTH_USER_MODEL, + verbose_name=_('author'), + on_delete=models.CASCADE) #jrx: 级联删除,用户删除则评论也删除 + #jrx: 关联的文章(外键关联文章表) + article = models.ForeignKey( + Article, + verbose_name=_('article'), + on_delete=models.CASCADE) #jrx: 级联删除,文章删除则评论也删除 + #jrx: 父评论(自关联,用于实现评论回复功能) + parent_comment = models.ForeignKey( + 'self', + verbose_name=_('parent comment'), + blank=True, + null=True, + on_delete=models.CASCADE) #jrx: 级联删除,父评论删除则子评论也删除 + #jrx: 评论是否启用(是否显示) + is_enable = models.BooleanField(_('enable'), + default=False, blank=False, null=False) + + class Meta: + #jrx: 按id倒序排序(最新评论在前) + ordering = ['-id'] + verbose_name = _('comment') + verbose_name_plural = verbose_name + get_latest_by = 'id' + + #jrx: 打印模型实例时显示评论正文 + def __str__(self): + return self.body \ No newline at end of file diff --git a/comments-jrx/templatetags/__init__.py b/comments-jrx/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/comments-jrx/templatetags/__pycache__/__init__.cpython-312.pyc b/comments-jrx/templatetags/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000..f562592 Binary files /dev/null and b/comments-jrx/templatetags/__pycache__/__init__.cpython-312.pyc differ diff --git a/comments-jrx/templatetags/__pycache__/comments_tags.cpython-312.pyc b/comments-jrx/templatetags/__pycache__/comments_tags.cpython-312.pyc new file mode 100644 index 0000000..3553f25 Binary files /dev/null and b/comments-jrx/templatetags/__pycache__/comments_tags.cpython-312.pyc differ diff --git a/comments-jrx/templatetags/comments_tags.py b/comments-jrx/templatetags/comments_tags.py new file mode 100644 index 0000000..5e47c9f --- /dev/null +++ b/comments-jrx/templatetags/comments_tags.py @@ -0,0 +1,40 @@ +from django import template + +register = template.Library() + + +@register.simple_tag +def parse_commenttree(commentlist, comment): + #jrx: 模板标签函数,用于获取当前评论的所有子评论(递归获取所有层级的子评论) + #jrx: 参数说明: + #jrx: commentlist: 评论查询集(包含所有评论) + #jrx: comment: 父评论对象,以此为起点查找子评论 + #jrx: 用法示例:{% parse_commenttree article_comments comment as childcomments %} + datas = [] #jrx: 存储子评论的列表 + + def parse(c): + #jrx: 递归函数:获取当前评论c的直接子评论,并继续递归查找子评论的子评论 + #jrx: 筛选出父评论为c且已启用的子评论 + childs = commentlist.filter(parent_comment=c, is_enable=True) + for child in childs: + datas.append(child) #jrx: 将子评论添加到列表 + parse(child) #jrx: 递归处理子评论的子评论 + + parse(comment) #jrx: 从传入的comment开始递归查找所有子评论 + return datas #jrx: 返回所有层级的子评论列表 + + +@register.inclusion_tag('comments/tags/comment_item.html') +def show_comment_item(comment, ischild): + #jrx: 包含标签函数,用于渲染单个评论项的HTML + #jrx: 参数说明: + #jrx: comment: 要渲染的评论对象 + #jrx: ischild: 布尔值,标识该评论是否为子评论(用于控制显示样式深度) + #jrx: 模板路径:comments/tags/comment_item.html + #jrx: 评论显示深度:子评论深度为1,顶级评论深度为2(用于CSS样式区分层级) + depth = 1 if ischild else 2 + #jrx: 传递给模板的上下文变量 + return { + 'comment_item': comment, #jrx: 评论对象 + 'depth': depth #jrx: 评论层级深度(用于样式控制) + } \ No newline at end of file diff --git a/comments-jrx/tests.py b/comments-jrx/tests.py new file mode 100644 index 0000000..f20bae0 --- /dev/null +++ b/comments-jrx/tests.py @@ -0,0 +1,119 @@ +from django.test import Client, RequestFactory, TransactionTestCase +from django.urls import reverse + +from accounts.models import BlogUser +from blog.models import Category, Article +from comments.models import Comment +from comments.templatetags.comments_tags import * +from djangoblog.utils import get_max_articleid_commentid + + +# Create your tests here. + +class CommentsTest(TransactionTestCase): + def setUp(self): + #jrx: 初始化测试客户端和工厂 + self.client = Client() + self.factory = RequestFactory() + #jrx: 设置博客评论需要审核 + from blog.models import BlogSettings + value = BlogSettings() + value.comment_need_review = True + value.save() + + #jrx: 创建超级用户用于测试 + self.user = BlogUser.objects.create_superuser( + email="liangliangyy1@gmail.com", + username="liangliangyy1", + password="liangliangyy1") + + #jrx: 辅助方法:更新文章下所有评论为启用状态 + def update_article_comment_status(self, article): + comments = article.comment_set.all() + for comment in comments: + comment.is_enable = True + comment.save() + + #jrx: 测试评论功能(提交、回复、显示等) + def test_validate_comment(self): + #jrx: 登录测试用户 + self.client.login(username='liangliangyy1', password='liangliangyy1') + + #jrx: 创建测试分类和文章 + category = Category() + category.name = "categoryccc" + category.save() + + article = Article() + article.title = "nicetitleccc" + article.body = "nicecontentccc" + article.author = self.user + article.category = category + article.type = 'a' + article.status = 'p' + article.save() + + #jrx: 获取评论提交URL + comment_url = reverse( + 'comments:postcomment', kwargs={ + 'article_id': article.id}) + + #jrx: 测试提交第一条评论 + response = self.client.post(comment_url, + { + 'body': '123ffffffffff' + }) + self.assertEqual(response.status_code, 302) #jrx: 验证重定向状态码 + + #jrx: 验证评论需审核(未启用时不显示) + article = Article.objects.get(pk=article.pk) + self.assertEqual(len(article.comment_list()), 0) + self.update_article_comment_status(article) #jrx: 手动启用评论 + self.assertEqual(len(article.comment_list()), 1) #jrx: 验证评论数量 + + #jrx: 测试提交第二条评论 + response = self.client.post(comment_url, + { + 'body': '123ffffffffff', + }) + self.assertEqual(response.status_code, 302) + self.update_article_comment_status(article) + self.assertEqual(len(article.comment_list()), 2) #jrx: 验证评论数量 + + #jrx: 测试回复评论功能 + parent_comment_id = article.comment_list()[0].id #jrx: 获取父评论ID + response = self.client.post(comment_url, + { + 'body': ''' + # Title1 + + ```python + import os + ``` + + [url](https://www.lylinux.net/) + + [ddd](http://www.baidu.com) + + + ''', + 'parent_comment_id': parent_comment_id#jrx: 指定父评论 + }) + + self.assertEqual(response.status_code, 302) + self.update_article_comment_status(article) + article = Article.objects.get(pk=article.pk) + self.assertEqual(len(article.comment_list()), 3)#jrx: 验证包含回复的总评论数 + # jrx: 测试评论树解析和模板标签 + comment = Comment.objects.get(id=parent_comment_id) + tree = parse_commenttree(article.comment_list(), comment)#jrx: 解析评论树 + self.assertEqual(len(tree), 1)#jrx: 验证评论树结构 + data = show_comment_item(comment, True)#jrx: 测试评论项渲染 + self.assertIsNotNone(data) + # jrx: 测试最大 ID 工具函数 + s = get_max_articleid_commentid() + self.assertIsNotNone(s) + + # jrx: 测试评论邮件发送功能 + from comments.utils import send_comment_email + send_comment_email(comment) diff --git a/comments-jrx/urls.py b/comments-jrx/urls.py new file mode 100644 index 0000000..6e9cfcd --- /dev/null +++ b/comments-jrx/urls.py @@ -0,0 +1,13 @@ +from django.urls import path + +from . import views + +#jrx: 应用命名空间 +app_name = "comments" +#jrx: 评论相关URL配置 +urlpatterns = [ + path( + 'article//postcomment', #jrx: 接收文章ID参数 + views.CommentPostView.as_view(), #jrx: 关联评论提交视图 + name='postcomment'), #jrx: URL名称,用于反向解析 +] \ No newline at end of file diff --git a/comments-jrx/utils.py b/comments-jrx/utils.py new file mode 100644 index 0000000..a0010a4 --- /dev/null +++ b/comments-jrx/utils.py @@ -0,0 +1,51 @@ +import logging + +from django.utils.translation import gettext_lazy as _ + +from djangoblog.utils import get_current_site +from djangoblog.utils import send_email + +logger = logging.getLogger(__name__) + + +def send_comment_email(comment): + # jrx: 发送评论相关邮件(感谢评论和回复通知) + # jrx: 获取当前站点域名 + site = get_current_site().domain + # jrx: 邮件主题(感谢评论) + subject = _('Thanks for your comment') + # jrx: 构建文章访问链接 + article_url = f"https://{site}{comment.article.get_absolute_url()}" + # jrx: 构建感谢评论的邮件内容(HTML格式) + html_content = _("""

Thank you very much for your comments on this site

+ You can visit %(article_title)s + to review your comments, + Thank you again! +
+ If the link above cannot be opened, please copy this link to your browser. + %(article_url)s""") % {'article_url': article_url, 'article_title': comment.article.title} + # jrx: 评论者邮箱 + tomail = comment.author.email + # jrx: 发送感谢邮件给评论者 + send_email([tomail], subject, html_content) + + try: + # jrx: 如果是回复评论,给被回复者发送通知邮件 + if comment.parent_comment: + # jrx: 构建回复通知的邮件内容(HTML格式) + html_content = _("""Your comment on %(article_title)s
has + received a reply.
%(comment_body)s +
+ go check it out! +
+ If the link above cannot be opened, please copy this link to your browser. + %(article_url)s + """) % {'article_url': article_url, 'article_title': comment.article.title, + 'comment_body': comment.parent_comment.body} + # jrx: 被回复者(父评论作者)邮箱 + tomail = comment.parent_comment.author.email + # jrx: 发送回复通知邮件 + send_email([tomail], subject, html_content) + except Exception as e: + # jrx: 记录邮件发送失败的错误日志 + logger.error(e) \ No newline at end of file diff --git a/comments-jrx/views.py b/comments-jrx/views.py new file mode 100644 index 0000000..caab71e --- /dev/null +++ b/comments-jrx/views.py @@ -0,0 +1,78 @@ +# Create your views here. +from django.core.exceptions import ValidationError +from django.http import HttpResponseRedirect +from django.shortcuts import get_object_or_404 +from django.utils.decorators import method_decorator +from django.views.decorators.csrf import csrf_protect +from django.views.generic.edit import FormView + +from accounts.models import BlogUser +from blog.models import Article +from .forms import CommentForm +from .models import Comment + + +class CommentPostView(FormView): + # jrx: 评论表单类 + form_class = CommentForm + # jrx: 渲染的模板(文章详情页) + template_name = 'blog/article_detail.html' + + # jrx: 启用CSRF保护 + @method_decorator(csrf_protect) + def dispatch(self, *args, **kwargs): + return super(CommentPostView, self).dispatch(*args, **kwargs) + + # jrx: 处理GET请求:重定向到文章详情页的评论区 + def get(self, request, *args, **kwargs): + article_id = self.kwargs['article_id'] # jrx: 从URL获取文章ID + article = get_object_or_404(Article, pk=article_id) # jrx: 获取文章对象,不存在则404 + url = article.get_absolute_url() # jrx: 获取文章详情页URL + return HttpResponseRedirect(url + "#comments") # jrx: 重定向到评论区锚点 + + # jrx: 表单验证失败时的处理 + def form_invalid(self, form): + article_id = self.kwargs['article_id'] + article = get_object_or_404(Article, pk=article_id) + # jrx: 重新渲染文章详情页,携带错误表单和文章数据 + return self.render_to_response({ + 'form': form, + 'article': article + }) + + # jrx: 表单验证通过后的处理(核心逻辑) + def form_valid(self, form): + """提交的数据验证合法后的逻辑""" + user = self.request.user # jrx: 当前登录用户 + author = BlogUser.objects.get(pk=user.pk) # jrx: 获取用户详细信息 + article_id = self.kwargs['article_id'] # jrx: 文章ID + article = get_object_or_404(Article, pk=article_id) # jrx: 获取文章对象 + + # jrx: 检查文章是否允许评论 + if article.comment_status == 'c' or article.status == 'c': + raise ValidationError("该文章评论已关闭.") + + # jrx: 不立即保存表单数据,先处理关联关系 + comment = form.save(False) + comment.article = article # jrx: 关联评论到文章 + + # jrx: 根据站点设置决定评论是否需要审核 + from djangoblog.utils import get_blog_setting + settings = get_blog_setting() + if not settings.comment_need_review: + comment.is_enable = True # jrx: 无需审核则直接启用 + + comment.author = author # jrx: 设置评论作者 + + # jrx: 处理回复功能(如果存在父评论ID) + if form.cleaned_data['parent_comment_id']: + parent_comment = Comment.objects.get( + pk=form.cleaned_data['parent_comment_id']) + comment.parent_comment = parent_comment # jrx: 关联到父评论 + + comment.save(True) # jrx: 保存评论到数据库 + + # jrx: 重定向到文章详情页的当前评论位置 + return HttpResponseRedirect( + "%s#div-comment-%d" % + (article.get_absolute_url(), comment.pk)) \ No newline at end of file diff --git a/itory (or any of the parent directories) .git b/itory (or any of the parent directories) .git new file mode 100644 index 0000000..74570f6 --- /dev/null +++ b/itory (or any of the parent directories) .git @@ -0,0 +1,324 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + ESC-j * Forward one file line (or _N file lines). + ESC-k * Backward one file line (or _N file lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + ESC-b * Backward one window, but don't stop at beginning-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ^O^N ^On * Search forward for (_N-th) OSC8 hyperlink. + ^O^P ^Op * Search backward for (_N-th) OSC8 hyperlink. + ^O^L ^Ol Jump to the currently selected OSC8 hyperlink. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + Search is case-sensitive unless changed with -i or -I. + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^S _n Search for match in _n-th parenthesized subpattern. + ^W WRAP search if no match found. + ^L Enter next character literally into pattern. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-m_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + ^O^O Open the currently selected OSC8 hyperlink. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + #_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k _f_i_l_e ... --lesskey-file=_f_i_l_e + Use a compiled lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n ......... --line-numbers + Suppress line numbers in prompts and messages. + -N ......... --LINE-NUMBERS + Display line number at start of each line. + -o [_f_i_l_e] .. --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] .. --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p _p_a_t_t_e_r_n . --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t _t_a_g .... --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces, tabs and carriage returns. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + + --exit-follow-on-close + Exit F command on a pipe when writer closes pipe. + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --form-feed + Stop scrolling when a form feed character is reached. + --header=[_L[,_C[,_N]]] + Use _L lines (starting at line _N) and _C columns as headers. + --incsearch + Search file as each pattern character is typed in. + --intr=[_C] + Use _C instead of ^X to interrupt a read. + --lesskey-context=_t_e_x_t + Use lesskey source file contents. + --lesskey-src=_f_i_l_e + Use a lesskey source file. + --line-num-width=[_N] + Set the width of the -N line number field to _N characters. + --match-shift=[_N] + Show at least _N characters to the left of a search match. + --modelines=[_N] + Read _N lines from the input file and look for vim modelines. + --mouse + Enable mouse input. + --no-edit-warn + Don't warn when using v command on a file opened via LESSOPEN. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --no-number-headers + Don't give line numbers to header lines. + --no-paste + Ignore pasted input. + --no-search-header-lines + Searches do not include header lines. + --no-search-header-columns + Searches do not include header columns. + --no-search-headers + Searches do not include header lines or columns. + --no-vbell + Disable the terminal's visual bell. + --redraw-on-quit + Redraw final screen when quitting. + --rscroll=[_C] + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --search-options=[EFKNRW-] + Set default options for every search. + --show-preproc-errors + Display a message if preprocessor exits with an error status. + --proc-backspace + Process backspaces for bold/underline. + --PROC-BACKSPACE + Treat backspaces as control characters. + --proc-return + Delete carriage returns before newline. + --PROC-RETURN + Treat carriage returns as control characters. + --proc-tab + Expand tabs to spaces. + --PROC-TAB + Treat tabs as control characters. + --status-col-width=[_N] + Set the width of the -J status column to _N characters. + --status-line + Highlight or color the entire line containing a mark. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=[_N] + Each click of the mouse wheel moves _N lines. + --wordwrap + Wrap lines at spaces. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all. diff --git a/tergit checkout -b recovery-branch commit-hash b/tergit checkout -b recovery-branch commit-hash new file mode 100644 index 0000000..74570f6 --- /dev/null +++ b/tergit checkout -b recovery-branch commit-hash @@ -0,0 +1,324 @@ + + SSUUMMMMAARRYY OOFF LLEESSSS CCOOMMMMAANNDDSS + + Commands marked with * may be preceded by a number, _N. + Notes in parentheses indicate the behavior if _N is given. + A key preceded by a caret indicates the Ctrl key; thus ^K is ctrl-K. + + h H Display this help. + q :q Q :Q ZZ Exit. + --------------------------------------------------------------------------- + + MMOOVVIINNGG + + e ^E j ^N CR * Forward one line (or _N lines). + y ^Y k ^K ^P * Backward one line (or _N lines). + ESC-j * Forward one file line (or _N file lines). + ESC-k * Backward one file line (or _N file lines). + f ^F ^V SPACE * Forward one window (or _N lines). + b ^B ESC-v * Backward one window (or _N lines). + z * Forward one window (and set window to _N). + w * Backward one window (and set window to _N). + ESC-SPACE * Forward one window, but don't stop at end-of-file. + ESC-b * Backward one window, but don't stop at beginning-of-file. + d ^D * Forward one half-window (and set half-window to _N). + u ^U * Backward one half-window (and set half-window to _N). + ESC-) RightArrow * Right one half screen width (or _N positions). + ESC-( LeftArrow * Left one half screen width (or _N positions). + ESC-} ^RightArrow Right to last column displayed. + ESC-{ ^LeftArrow Left to first column. + F Forward forever; like "tail -f". + ESC-F Like F but stop when search pattern is found. + r ^R ^L Repaint screen. + R Repaint screen, discarding buffered input. + --------------------------------------------------- + Default "window" is the screen height. + Default "half-window" is half of the screen height. + --------------------------------------------------------------------------- + + SSEEAARRCCHHIINNGG + + /_p_a_t_t_e_r_n * Search forward for (_N-th) matching line. + ?_p_a_t_t_e_r_n * Search backward for (_N-th) matching line. + n * Repeat previous search (for _N-th occurrence). + N * Repeat previous search in reverse direction. + ESC-n * Repeat previous search, spanning files. + ESC-N * Repeat previous search, reverse dir. & spanning files. + ^O^N ^On * Search forward for (_N-th) OSC8 hyperlink. + ^O^P ^Op * Search backward for (_N-th) OSC8 hyperlink. + ^O^L ^Ol Jump to the currently selected OSC8 hyperlink. + ESC-u Undo (toggle) search highlighting. + ESC-U Clear search highlighting. + &_p_a_t_t_e_r_n * Display only matching lines. + --------------------------------------------------- + Search is case-sensitive unless changed with -i or -I. + A search pattern may begin with one or more of: + ^N or ! Search for NON-matching lines. + ^E or * Search multiple files (pass thru END OF FILE). + ^F or @ Start search at FIRST file (for /) or last file (for ?). + ^K Highlight matches, but don't move (KEEP position). + ^R Don't use REGULAR EXPRESSIONS. + ^S _n Search for match in _n-th parenthesized subpattern. + ^W WRAP search if no match found. + ^L Enter next character literally into pattern. + --------------------------------------------------------------------------- + + JJUUMMPPIINNGG + + g < ESC-< * Go to first line in file (or line _N). + G > ESC-> * Go to last line in file (or line _N). + p % * Go to beginning of file (or _N percent into file). + t * Go to the (_N-th) next tag. + T * Go to the (_N-th) previous tag. + { ( [ * Find close bracket } ) ]. + } ) ] * Find open bracket { ( [. + ESC-^F _<_c_1_> _<_c_2_> * Find close bracket _<_c_2_>. + ESC-^B _<_c_1_> _<_c_2_> * Find open bracket _<_c_1_>. + --------------------------------------------------- + Each "find close bracket" command goes forward to the close bracket + matching the (_N-th) open bracket in the top line. + Each "find open bracket" command goes backward to the open bracket + matching the (_N-th) close bracket in the bottom line. + + m_<_l_e_t_t_e_r_> Mark the current top line with . + M_<_l_e_t_t_e_r_> Mark the current bottom line with . + '_<_l_e_t_t_e_r_> Go to a previously marked position. + '' Go to the previous position. + ^X^X Same as '. + ESC-m_<_l_e_t_t_e_r_> Clear a mark. + --------------------------------------------------- + A mark is any upper-case or lower-case letter. + Certain marks are predefined: + ^ means beginning of the file + $ means end of the file + --------------------------------------------------------------------------- + + CCHHAANNGGIINNGG FFIILLEESS + + :e [_f_i_l_e] Examine a new file. + ^X^V Same as :e. + :n * Examine the (_N-th) next file from the command line. + :p * Examine the (_N-th) previous file from the command line. + :x * Examine the first (or _N-th) file from the command line. + ^O^O Open the currently selected OSC8 hyperlink. + :d Delete the current file from the command line list. + = ^G :f Print current file name. + --------------------------------------------------------------------------- + + MMIISSCCEELLLLAANNEEOOUUSS CCOOMMMMAANNDDSS + + -_<_f_l_a_g_> Toggle a command line option [see OPTIONS below]. + --_<_n_a_m_e_> Toggle a command line option, by name. + __<_f_l_a_g_> Display the setting of a command line option. + ___<_n_a_m_e_> Display the setting of an option, by name. + +_c_m_d Execute the less cmd each time a new file is examined. + + !_c_o_m_m_a_n_d Execute the shell command with $SHELL. + #_c_o_m_m_a_n_d Execute the shell command, expanded like a prompt. + |XX_c_o_m_m_a_n_d Pipe file between current pos & mark XX to shell command. + s _f_i_l_e Save input to a file. + v Edit the current file with $VISUAL or $EDITOR. + V Print version number of "less". + --------------------------------------------------------------------------- + + OOPPTTIIOONNSS + + Most options may be changed either on the command line, + or from within less by using the - or -- command. + Options may be given in one of two forms: either a single + character preceded by a -, or a name preceded by --. + + -? ........ --help + Display help (from command line). + -a ........ --search-skip-screen + Search skips current screen. + -A ........ --SEARCH-SKIP-SCREEN + Search starts just after target line. + -b [_N] .... --buffers=[_N] + Number of buffers. + -B ........ --auto-buffers + Don't automatically allocate buffers for pipes. + -c ........ --clear-screen + Repaint by clearing rather than scrolling. + -d ........ --dumb + Dumb terminal. + -D xx_c_o_l_o_r . --color=xx_c_o_l_o_r + Set screen colors. + -e -E .... --quit-at-eof --QUIT-AT-EOF + Quit at end of file. + -f ........ --force + Force open non-regular files. + -F ........ --quit-if-one-screen + Quit if entire file fits on first screen. + -g ........ --hilite-search + Highlight only last match for searches. + -G ........ --HILITE-SEARCH + Don't highlight any matches for searches. + -h [_N] .... --max-back-scroll=[_N] + Backward scroll limit. + -i ........ --ignore-case + Ignore case in searches that do not contain uppercase. + -I ........ --IGNORE-CASE + Ignore case in all searches. + -j [_N] .... --jump-target=[_N] + Screen position of target lines. + -J ........ --status-column + Display a status column at left edge of screen. + -k _f_i_l_e ... --lesskey-file=_f_i_l_e + Use a compiled lesskey file. + -K ........ --quit-on-intr + Exit less in response to ctrl-C. + -L ........ --no-lessopen + Ignore the LESSOPEN environment variable. + -m -M .... --long-prompt --LONG-PROMPT + Set prompt style. + -n ......... --line-numbers + Suppress line numbers in prompts and messages. + -N ......... --LINE-NUMBERS + Display line number at start of each line. + -o [_f_i_l_e] .. --log-file=[_f_i_l_e] + Copy to log file (standard input only). + -O [_f_i_l_e] .. --LOG-FILE=[_f_i_l_e] + Copy to log file (unconditionally overwrite). + -p _p_a_t_t_e_r_n . --pattern=[_p_a_t_t_e_r_n] + Start at pattern (from command line). + -P [_p_r_o_m_p_t] --prompt=[_p_r_o_m_p_t] + Define new prompt. + -q -Q .... --quiet --QUIET --silent --SILENT + Quiet the terminal bell. + -r -R .... --raw-control-chars --RAW-CONTROL-CHARS + Output "raw" control characters. + -s ........ --squeeze-blank-lines + Squeeze multiple blank lines. + -S ........ --chop-long-lines + Chop (truncate) long lines rather than wrapping. + -t _t_a_g .... --tag=[_t_a_g] + Find a tag. + -T [_t_a_g_s_f_i_l_e] --tag-file=[_t_a_g_s_f_i_l_e] + Use an alternate tags file. + -u -U .... --underline-special --UNDERLINE-SPECIAL + Change handling of backspaces, tabs and carriage returns. + -V ........ --version + Display the version number of "less". + -w ........ --hilite-unread + Highlight first new line after forward-screen. + -W ........ --HILITE-UNREAD + Highlight first new line after any forward movement. + -x [_N[,...]] --tabs=[_N[,...]] + Set tab stops. + -X ........ --no-init + Don't use termcap init/deinit strings. + -y [_N] .... --max-forw-scroll=[_N] + Forward scroll limit. + -z [_N] .... --window=[_N] + Set size of window. + -" [_c[_c]] . --quotes=[_c[_c]] + Set shell quote characters. + -~ ........ --tilde + Don't display tildes after end of file. + -# [_N] .... --shift=[_N] + Set horizontal scroll amount (0 = one half screen width). + + --exit-follow-on-close + Exit F command on a pipe when writer closes pipe. + --file-size + Automatically determine the size of the input file. + --follow-name + The F command changes files if the input file is renamed. + --form-feed + Stop scrolling when a form feed character is reached. + --header=[_L[,_C[,_N]]] + Use _L lines (starting at line _N) and _C columns as headers. + --incsearch + Search file as each pattern character is typed in. + --intr=[_C] + Use _C instead of ^X to interrupt a read. + --lesskey-context=_t_e_x_t + Use lesskey source file contents. + --lesskey-src=_f_i_l_e + Use a lesskey source file. + --line-num-width=[_N] + Set the width of the -N line number field to _N characters. + --match-shift=[_N] + Show at least _N characters to the left of a search match. + --modelines=[_N] + Read _N lines from the input file and look for vim modelines. + --mouse + Enable mouse input. + --no-edit-warn + Don't warn when using v command on a file opened via LESSOPEN. + --no-keypad + Don't send termcap keypad init/deinit strings. + --no-histdups + Remove duplicates from command history. + --no-number-headers + Don't give line numbers to header lines. + --no-paste + Ignore pasted input. + --no-search-header-lines + Searches do not include header lines. + --no-search-header-columns + Searches do not include header columns. + --no-search-headers + Searches do not include header lines or columns. + --no-vbell + Disable the terminal's visual bell. + --redraw-on-quit + Redraw final screen when quitting. + --rscroll=[_C] + Set the character used to mark truncated lines. + --save-marks + Retain marks across invocations of less. + --search-options=[EFKNRW-] + Set default options for every search. + --show-preproc-errors + Display a message if preprocessor exits with an error status. + --proc-backspace + Process backspaces for bold/underline. + --PROC-BACKSPACE + Treat backspaces as control characters. + --proc-return + Delete carriage returns before newline. + --PROC-RETURN + Treat carriage returns as control characters. + --proc-tab + Expand tabs to spaces. + --PROC-TAB + Treat tabs as control characters. + --status-col-width=[_N] + Set the width of the -J status column to _N characters. + --status-line + Highlight or color the entire line containing a mark. + --use-backslash + Subsequent options use backslash as escape char. + --use-color + Enables colored text. + --wheel-lines=[_N] + Each click of the mouse wheel moves _N lines. + --wordwrap + Wrap lines at spaces. + + + --------------------------------------------------------------------------- + + LLIINNEE EEDDIITTIINNGG + + These keys can be used to edit text being entered + on the "command line" at the bottom of the screen. + + RightArrow ..................... ESC-l ... Move cursor right one character. + LeftArrow ...................... ESC-h ... Move cursor left one character. + ctrl-RightArrow ESC-RightArrow ESC-w ... Move cursor right one word. + ctrl-LeftArrow ESC-LeftArrow ESC-b ... Move cursor left one word. + HOME ........................... ESC-0 ... Move cursor to start of line. + END ............................ ESC-$ ... Move cursor to end of line. + BACKSPACE ................................ Delete char to left of cursor. + DELETE ......................... ESC-x ... Delete char under cursor. + ctrl-BACKSPACE ESC-BACKSPACE ........... Delete word to left of cursor. + ctrl-DELETE .... ESC-DELETE .... ESC-X ... Delete word under cursor. + ctrl-U ......... ESC (MS-DOS only) ....... Delete entire line. + UpArrow ........................ ESC-k ... Retrieve previous command line. + DownArrow ...................... ESC-j ... Retrieve next command line. + TAB ...................................... Complete filename & cycle. + SHIFT-TAB ...................... ESC-TAB Complete filename & reverse cycle. + ctrl-L ................................... Complete filename, list all.