From 79a0651c396ffd98165c0d4df6bb94cc1f22d4b4 Mon Sep 17 00:00:00 2001 From: liangliangyy Date: Mon, 18 Oct 2021 15:01:59 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0toc=E7=9B=AE=E5=BD=95?= =?UTF-8?q?=E6=94=AF=E6=8C=81=EF=BC=8Cclose=20#509?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DjangoBlog/tests.py | 13 +- DjangoBlog/utils.py | 94 ++----- blog/models.py | 16 +- blog/static/blog/css/style.css | 23 +- blog/static/pygments/default.css | 352 +++++++++++++++++++++----- blog/templatetags/blog_tags.py | 29 ++- blog/tests.py | 24 +- requirements.txt | Bin 507 -> 1078 bytes templates/blog/tags/article_info.html | 12 +- 9 files changed, 383 insertions(+), 180 deletions(-) diff --git a/DjangoBlog/tests.py b/DjangoBlog/tests.py index 8dff824..47fda83 100644 --- a/DjangoBlog/tests.py +++ b/DjangoBlog/tests.py @@ -13,12 +13,8 @@ @time: 2017/10/25 下午10:16 """ -from django.test import Client, RequestFactory, TestCase -from blog.models import Article, Category, Tag -from django.contrib.auth import get_user_model -from DjangoBlog.utils import get_current_site -from django.urls import reverse -import datetime +from django.test import TestCase + from DjangoBlog.utils import * @@ -49,8 +45,3 @@ class DjangoBlogTest(TestCase): } data = parse_dict_to_url(d) self.assertIsNotNone(data) - render = BlogMarkDownRenderer() - s = render.autolink('http://www.baidu.com') - self.assertTrue(s.find('nofollow') > 0) - s = render.link('http://www.baidu.com', 'test', 'test') - self.assertTrue(s.find('nofollow') > 0) diff --git a/DjangoBlog/utils.py b/DjangoBlog/utils.py index 6afd817..8cdbbfa 100644 --- a/DjangoBlog/utils.py +++ b/DjangoBlog/utils.py @@ -9,14 +9,9 @@ import string import uuid from hashlib import sha256 -import mistune import requests from django.contrib.sites.models import Site from django.core.cache import cache -from mistune import escape, escape_link -from pygments import highlight -from pygments.formatters import html -from pygments.lexers import get_lexer_by_name logger = logging.getLogger(__name__) @@ -93,82 +88,37 @@ def expire_view_cache(path, servername, serverport, key_prefix=None): return False -def block_code(text, lang, inlinestyles=False, linenos=False): - ''' - markdown代码高亮 - :param text: - :param lang: - :param inlinestyles: - :param linenos: - :return: - ''' - if not lang: - text = text.strip() - return u'
%s
\n' % mistune.escape(text) - - try: - lexer = get_lexer_by_name(lang, stripall=True) - formatter = html.HtmlFormatter( - noclasses=inlinestyles, linenos=linenos - ) - code = highlight(text, lexer, formatter) - if linenos: - return '
%s
\n' % code - return code - except BaseException: - return '
%s
\n' % ( - lang, mistune.escape(text) - ) - - @cache_decorator() def get_current_site(): site = Site.objects.get_current() return site -class BlogMarkDownRenderer(mistune.Renderer): - ''' - markdown渲染 - ''' +class CommonMarkdown: + @staticmethod + def _convert_markdown(value): + import markdown + md = markdown.Markdown( + extensions=[ + 'extra', + 'codehilite', + 'toc', + 'tables', + ] + ) + body = md.convert(value) + toc = md.toc + return body, toc - def block_code(self, text, lang=None): - # renderer has an options - inlinestyles = self.options.get('inlinestyles') - linenos = self.options.get('linenos') - return block_code(text, lang, inlinestyles, linenos) - - def autolink(self, link, is_email=False): - text = link = escape(link) - - if is_email: - link = 'mailto:%s' % link - if not link: - link = "#" - site = get_current_site() - nofollow = "" if link.find(site.domain) > 0 else "rel='nofollow'" - return '%s' % (link, nofollow, text) - - def link(self, link, title, text): - link = escape_link(link) - site = get_current_site() - nofollow = "" if link.find(site.domain) > 0 else "rel='nofollow'" - if not link: - link = "#" - if not title: - return '%s' % (link, nofollow, text) - title = escape(title, quote=True) - return '%s' % ( - link, title, nofollow, text) - - -class CommonMarkdown(): @staticmethod - def get_markdown(value): - renderer = BlogMarkDownRenderer(inlinestyles=False) + def get_markdown_with_toc(value): + body, toc = CommonMarkdown._convert_markdown(value) + return body, toc - mdp = mistune.Markdown(escape=True, renderer=renderer) - return mdp(value) + @staticmethod + def get_markdown(value): + body, toc = CommonMarkdown._convert_markdown(value) + return body def send_email(emailto, title, content): diff --git a/blog/models.py b/blog/models.py index 067de72..c04a4cd 100644 --- a/blog/models.py +++ b/blog/models.py @@ -1,16 +1,17 @@ import logging -from abc import ABCMeta, abstractmethod, abstractproperty +from abc import abstractmethod -from django.db import models -from django.urls import reverse from django.conf import settings -from uuslug import slugify from django.core.exceptions import ValidationError -from django.utils.translation import gettext_lazy as _ -from DjangoBlog.utils import get_current_site -from DjangoBlog.utils import cache_decorator, cache +from django.db import models +from django.urls import reverse from django.utils.timezone import now +from django.utils.translation import gettext_lazy as _ from mdeditor.fields import MDTextField +from uuslug import slugify + +from DjangoBlog.utils import cache_decorator, cache +from DjangoBlog.utils import get_current_site logger = logging.getLogger(__name__) @@ -94,6 +95,7 @@ class Article(BaseModel): on_delete=models.CASCADE) article_order = models.IntegerField( '排序,数字越大越靠前', blank=False, null=False, default=0) + show_toc = models.BooleanField("是否显示toc目录", blank=False, null=False, default=False) category = models.ForeignKey( 'Category', verbose_name='分类', diff --git a/blog/static/blog/css/style.css b/blog/static/blog/css/style.css index 0cc1df1..d43f7f3 100644 --- a/blog/static/blog/css/style.css +++ b/blog/static/blog/css/style.css @@ -2479,17 +2479,26 @@ li #reply-title { } .breadcrumb { - margin-bottom: 20px; - list-style: none; - border-radius: 4px; + margin-bottom: 20px; + list-style: none; + border-radius: 4px; } + .breadcrumb > li { - display: inline-block; + display: inline-block; } + .breadcrumb > li + li:before { - color: #ccc; - content: "/\00a0"; + color: #ccc; + content: "/\00a0"; } + .breadcrumb > .active { - color: #777; + color: #777; +} + +.break_line { + height: 1px; + border: none; + /*border-top: 1px dashed #f5d6d6;*/ } \ No newline at end of file diff --git a/blog/static/pygments/default.css b/blog/static/pygments/default.css index 0f81fc8..73e6e49 100755 --- a/blog/static/pygments/default.css +++ b/blog/static/pygments/default.css @@ -1,59 +1,293 @@ -.highlight .hll { background-color: #ffffcc } -.highlight { background: #ffffff; } -.highlight .c { color: #177500 } /* Comment */ -.highlight .err { color: #000000 } /* Error */ -.highlight .k { color: #A90D91 } /* Keyword */ -.highlight .l { color: #1C01CE } /* Literal */ -.highlight .n { color: #000000 } /* Name */ -.highlight .o { color: #000000 } /* Operator */ -.highlight .ch { color: #177500 } /* Comment.Hashbang */ -.highlight .cm { color: #177500 } /* Comment.Multiline */ -.highlight .cp { color: #633820 } /* Comment.Preproc */ -.highlight .cpf { color: #177500 } /* Comment.PreprocFile */ -.highlight .c1 { color: #177500 } /* Comment.Single */ -.highlight .cs { color: #177500 } /* Comment.Special */ -.highlight .kc { color: #A90D91 } /* Keyword.Constant */ -.highlight .kd { color: #A90D91 } /* Keyword.Declaration */ -.highlight .kn { color: #A90D91 } /* Keyword.Namespace */ -.highlight .kp { color: #A90D91 } /* Keyword.Pseudo */ -.highlight .kr { color: #A90D91 } /* Keyword.Reserved */ -.highlight .kt { color: #A90D91 } /* Keyword.Type */ -.highlight .ld { color: #1C01CE } /* Literal.Date */ -.highlight .m { color: #1C01CE } /* Literal.Number */ -.highlight .s { color: #C41A16 } /* Literal.String */ -.highlight .na { color: #836C28 } /* Name.Attribute */ -.highlight .nb { color: #A90D91 } /* Name.Builtin */ -.highlight .nc { color: #3F6E75 } /* Name.Class */ -.highlight .no { color: #000000 } /* Name.Constant */ -.highlight .nd { color: #000000 } /* Name.Decorator */ -.highlight .ni { color: #000000 } /* Name.Entity */ -.highlight .ne { color: #000000 } /* Name.Exception */ -.highlight .nf { color: #000000 } /* Name.Function */ -.highlight .nl { color: #000000 } /* Name.Label */ -.highlight .nn { color: #000000 } /* Name.Namespace */ -.highlight .nx { color: #000000 } /* Name.Other */ -.highlight .py { color: #000000 } /* Name.Property */ -.highlight .nt { color: #000000 } /* Name.Tag */ -.highlight .nv { color: #000000 } /* Name.Variable */ -.highlight .ow { color: #000000 } /* Operator.Word */ -.highlight .mb { color: #1C01CE } /* Literal.Number.Bin */ -.highlight .mf { color: #1C01CE } /* Literal.Number.Float */ -.highlight .mh { color: #1C01CE } /* Literal.Number.Hex */ -.highlight .mi { color: #1C01CE } /* Literal.Number.Integer */ -.highlight .mo { color: #1C01CE } /* Literal.Number.Oct */ -.highlight .sb { color: #C41A16 } /* Literal.String.Backtick */ -.highlight .sc { color: #2300CE } /* Literal.String.Char */ -.highlight .sd { color: #C41A16 } /* Literal.String.Doc */ -.highlight .s2 { color: #C41A16 } /* Literal.String.Double */ -.highlight .se { color: #C41A16 } /* Literal.String.Escape */ -.highlight .sh { color: #C41A16 } /* Literal.String.Heredoc */ -.highlight .si { color: #C41A16 } /* Literal.String.Interpol */ -.highlight .sx { color: #C41A16 } /* Literal.String.Other */ -.highlight .sr { color: #C41A16 } /* Literal.String.Regex */ -.highlight .s1 { color: #C41A16 } /* Literal.String.Single */ -.highlight .ss { color: #C41A16 } /* Literal.String.Symbol */ -.highlight .bp { color: #5B269A } /* Name.Builtin.Pseudo */ -.highlight .vc { color: #000000 } /* Name.Variable.Class */ -.highlight .vg { color: #000000 } /* Name.Variable.Global */ -.highlight .vi { color: #000000 } /* Name.Variable.Instance */ -.highlight .il { color: #1C01CE } /* Literal.Number.Integer.Long */ \ No newline at end of file +.codehilite .hll { + background-color: #ffffcc +} + +.codehilite { + background: #ffffff; +} + +.codehilite .c { + color: #177500 +} + +/* Comment */ +.codehilite .err { + color: #000000 +} + +/* Error */ +.codehilite .k { + color: #A90D91 +} + +/* Keyword */ +.codehilite .l { + color: #1C01CE +} + +/* Literal */ +.codehilite .n { + color: #000000 +} + +/* Name */ +.codehilite .o { + color: #000000 +} + +/* Operator */ +.codehilite .ch { + color: #177500 +} + +/* Comment.Hashbang */ +.codehilite .cm { + color: #177500 +} + +/* Comment.Multiline */ +.codehilite .cp { + color: #633820 +} + +/* Comment.Preproc */ +.codehilite .cpf { + color: #177500 +} + +/* Comment.PreprocFile */ +.codehilite .c1 { + color: #177500 +} + +/* Comment.Single */ +.codehilite .cs { + color: #177500 +} + +/* Comment.Special */ +.codehilite .kc { + color: #A90D91 +} + +/* Keyword.Constant */ +.codehilite .kd { + color: #A90D91 +} + +/* Keyword.Declaration */ +.codehilite .kn { + color: #A90D91 +} + +/* Keyword.Namespace */ +.codehilite .kp { + color: #A90D91 +} + +/* Keyword.Pseudo */ +.codehilite .kr { + color: #A90D91 +} + +/* Keyword.Reserved */ +.codehilite .kt { + color: #A90D91 +} + +/* Keyword.Type */ +.codehilite .ld { + color: #1C01CE +} + +/* Literal.Date */ +.codehilite .m { + color: #1C01CE +} + +/* Literal.Number */ +.codehilite .s { + color: #C41A16 +} + +/* Literal.String */ +.codehilite .na { + color: #836C28 +} + +/* Name.Attribute */ +.codehilite .nb { + color: #A90D91 +} + +/* Name.Builtin */ +.codehilite .nc { + color: #3F6E75 +} + +/* Name.Class */ +.codehilite .no { + color: #000000 +} + +/* Name.Constant */ +.codehilite .nd { + color: #000000 +} + +/* Name.Decorator */ +.codehilite .ni { + color: #000000 +} + +/* Name.Entity */ +.codehilite .ne { + color: #000000 +} + +/* Name.Exception */ +.codehilite .nf { + color: #000000 +} + +/* Name.Function */ +.codehilite .nl { + color: #000000 +} + +/* Name.Label */ +.codehilite .nn { + color: #000000 +} + +/* Name.Namespace */ +.codehilite .nx { + color: #000000 +} + +/* Name.Other */ +.codehilite .py { + color: #000000 +} + +/* Name.Property */ +.codehilite .nt { + color: #000000 +} + +/* Name.Tag */ +.codehilite .nv { + color: #000000 +} + +/* Name.Variable */ +.codehilite .ow { + color: #000000 +} + +/* Operator.Word */ +.codehilite .mb { + color: #1C01CE +} + +/* Literal.Number.Bin */ +.codehilite .mf { + color: #1C01CE +} + +/* Literal.Number.Float */ +.codehilite .mh { + color: #1C01CE +} + +/* Literal.Number.Hex */ +.codehilite .mi { + color: #1C01CE +} + +/* Literal.Number.Integer */ +.codehilite .mo { + color: #1C01CE +} + +/* Literal.Number.Oct */ +.codehilite .sb { + color: #C41A16 +} + +/* Literal.String.Backtick */ +.codehilite .sc { + color: #2300CE +} + +/* Literal.String.Char */ +.codehilite .sd { + color: #C41A16 +} + +/* Literal.String.Doc */ +.codehilite .s2 { + color: #C41A16 +} + +/* Literal.String.Double */ +.codehilite .se { + color: #C41A16 +} + +/* Literal.String.Escape */ +.codehilite .sh { + color: #C41A16 +} + +/* Literal.String.Heredoc */ +.codehilite .si { + color: #C41A16 +} + +/* Literal.String.Interpol */ +.codehilite .sx { + color: #C41A16 +} + +/* Literal.String.Other */ +.codehilite .sr { + color: #C41A16 +} + +/* Literal.String.Regex */ +.codehilite .s1 { + color: #C41A16 +} + +/* Literal.String.Single */ +.codehilite .ss { + color: #C41A16 +} + +/* Literal.String.Symbol */ +.codehilite .bp { + color: #5B269A +} + +/* Name.Builtin.Pseudo */ +.codehilite .vc { + color: #000000 +} + +/* Name.Variable.Class */ +.codehilite .vg { + color: #000000 +} + +/* Name.Variable.Global */ +.codehilite .vi { + color: #000000 +} + +/* Name.Variable.Instance */ +.codehilite .il { + color: #1C01CE +} + +/* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/blog/templatetags/blog_tags.py b/blog/templatetags/blog_tags.py index 73df621..c899099 100644 --- a/blog/templatetags/blog_tags.py +++ b/blog/templatetags/blog_tags.py @@ -13,24 +13,24 @@ @time: 2016/11/2 下午11:10 """ +import hashlib +import logging +import random +import urllib + from django import template -from django.db.models import Q from django.conf import settings +from django.db.models import Q +from django.shortcuts import get_object_or_404 from django.template.defaultfilters import stringfilter -from django.utils.safestring import mark_safe -import random from django.urls import reverse +from django.utils.safestring import mark_safe + +from DjangoBlog.utils import cache +from DjangoBlog.utils import get_current_site from blog.models import Article, Category, Tag, Links, SideBar, LinkShowType -from django.utils.encoding import force_text -from django.shortcuts import get_object_or_404 -import hashlib -import urllib from comments.models import Comment -from DjangoBlog.utils import cache_decorator, cache -from django.contrib.auth import get_user_model from oauth.models import OAuthUser -from DjangoBlog.utils import get_current_site -import logging logger = logging.getLogger(__name__) @@ -64,6 +64,13 @@ def custom_markdown(content): return mark_safe(CommonMarkdown.get_markdown(content)) +@register.simple_tag +def get_markdown_toc(content): + from DjangoBlog.utils import CommonMarkdown + body, toc = CommonMarkdown.get_markdown_with_toc(content) + return mark_safe(toc), mark_safe(body) + + @register.filter(is_safe=True) @stringfilter def truncatechars_content(content): diff --git a/blog/tests.py b/blog/tests.py index 6c90727..acf6ad8 100644 --- a/blog/tests.py +++ b/blog/tests.py @@ -1,17 +1,18 @@ +import os + +from django.conf import settings +from django.core.files.uploadedfile import SimpleUploadedFile +from django.core.management import call_command +from django.core.paginator import Paginator from django.test import Client, RequestFactory, TestCase -from blog.models import Article, Category, Tag, SideBar, Links -from django.contrib.auth import get_user_model +from django.urls import reverse +from django.utils import timezone + from DjangoBlog.utils import get_current_site, get_sha256 +from accounts.models import BlogUser from blog.forms import BlogSearchForm -from django.core.paginator import Paginator +from blog.models import Article, Category, Tag, SideBar, Links from blog.templatetags.blog_tags import load_pagination_info, load_articletags -from accounts.models import BlogUser -from django.core.files.uploadedfile import SimpleUploadedFile -from django.conf import settings -from django.urls import reverse -from django.utils import timezone -import os -from django.core.management import call_command # Create your tests here. @@ -147,8 +148,7 @@ class ArticleTest(TestCase): response = self.client.get('/sitemap.xml') self.assertEqual(response.status_code, 200) - from DjangoBlog.utils import block_code - block = block_code("`python`", 'python') + self.client.get("/admin/blog/article/1/delete/") self.client.get('/admin/servermanager/emailsendlog/') self.client.get('admin/admin/logentry/') diff --git a/requirements.txt b/requirements.txt index d1f77d7ce1d1f98a7ec908431bba47e71390284a..675479f54c8617aa95350daeb4a1bdc9328df3bb 100644 GIT binary patch literal 1078 zcmZ{k%TB^z5QS%L;-f&g3NG9jw?@GeCEzB zq|^s{Mr7hz3$$+GmX5@xjvY92=#g{R(G4`Iiu5b%oTC|N9!nI5b5bxoDVWhqX0Oy$ zRK4_EUpPC1wIX)~N?tC&220(eXIDK(Lxqz|M`)@#J$JG6iLA*_II4Ko6HHyljfgFH z#G^XWdG2KdOU^z-_V%QtU7Obm%ntvgy5UT@`zx2j)#$OwR?5@AcyX6#)aFe2o_Y;A z*0=i$_~T>?d!w!t_ra<{?0T?&9hA2CRb0`lZb(ck3w$C;8W9&{> bR0(gj*|C37K^whc4)I^0nb(!7AK`uhIaZ&4 literal 507 zcmY*WL2`pI5WD*?crYO(J^0e;12Vnj?3gW>*qC@B8T$IlNtsTME6G}EZR4Gi=}7Cn z+>4LFj9t`vCo6dn?Q<$@3{xWJn6$2BE!9T!X64z~Yu2kj*i8?UU|7pimYX)T)Veua zlv#V+@MU4YbXqHHECqSPv$ISl*9E zp*JHSR>@|A0|jerPi=0qf7{^P&wZ`=z$HxK@eRWgnUzQe1+;En_Q^5_hgm_{h(x!A z__e-jus#hN4S`sa_wKTLp00uXOrP;R;uRead more

{% else %} - {{ article.body|custom_markdown }} + {% get_markdown_toc article.body as markdown %} + {% if article.show_toc %} + + 目录: + {{ markdown.0|safe }} + +
+ {% endif %} +
+ {{ markdown.1|safe }} +
{% endif %}