diff --git a/.gitignore b/.gitignore index c21fc33..9735536 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,4 @@ atlassian-ide-plugin.xml .env *.log *.sqlite3 +.ropeproject/ diff --git a/readme.rst b/readme.rst index e2a22f8..3d2fae3 100644 --- a/readme.rst +++ b/readme.rst @@ -31,6 +31,13 @@ the5fire的技术博客源码 ------------------------------- 后台修改为xadmin(0.5v) +2016-07-02 +------------------------------- +* Post增加is_md字段,增加对markdown格式的支持 +* 拆分settings.py文件为develop.py,product.py让配置更清晰 +* 通过DJANGOSELFBLOG_PROFILE来加载对应的配置develop/product +* 修复创建Post时不填英文标题导致的文章无法访问的bug + 哪些技术 ------------------------------ diff --git a/requirements.txt b/requirements.txt index 7aacf5b..39b5f3e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,3 +12,4 @@ python-memcached==1.53 django-debug-toolbar==0.10.2 django-xadmin==0.5.0 django-ipware +markdown diff --git a/selfblog/blog/adminx.py b/selfblog/blog/adminx.py index dc25657..4748503 100644 --- a/selfblog/blog/adminx.py +++ b/selfblog/blog/adminx.py @@ -1,4 +1,5 @@ -#coding:utf-8 +# coding:utf-8 +import markdown import xadmin from django.core import urlresolvers @@ -35,7 +36,10 @@ def save_models(self): obj.author = self.request.user if not obj.summary: obj.summary = obj.content - if not obj.is_old: + + if obj.is_md: + obj.content_html = markdown.markdown(obj.content, extensions=['codehilite']) + elif not obj.is_old: obj.content_html = restructuredtext(obj.content) else: obj.content_html = obj.content.replace('\r\n', '
') diff --git a/selfblog/blog/models.py b/selfblog/blog/models.py index 4c62be8..cd4aaaa 100644 --- a/selfblog/blog/models.py +++ b/selfblog/blog/models.py @@ -1,11 +1,13 @@ # coding:utf-8 from datetime import datetime -from django.db import models from django.contrib.auth.models import User +from django.db import models +from django.db.models import signals +from django.dispatch import receiver from django.utils.functional import cached_property -from selfblog import settings +from django.conf import settings from utils.cache import cache_decorator STATUS = { @@ -55,7 +57,7 @@ class Post(models.Model): is_top = models.BooleanField(default=False, verbose_name=u'置顶') summary = models.TextField(verbose_name=u'摘要') - content = models.TextField(verbose_name=u'文章正文rst格式') + content = models.TextField(verbose_name=u'文章正文rst/md格式') content_html = models.TextField(verbose_name=u'文章正文html') view_times = models.IntegerField(default=1) @@ -63,6 +65,7 @@ class Post(models.Model): tags = models.CharField(max_length=100, null=True, blank=True, verbose_name=u'标签', help_text=u'用英文逗号分割') status = models.IntegerField(default=0, choices=STATUS.items(), verbose_name=u'状态') + is_md = models.BooleanField(default=False, verbose_name=u'是否为Markdown格式') is_old = models.BooleanField(default=False, verbose_name=u'是否为旧数据') pub_time = models.DateTimeField(default=datetime.now, verbose_name=u'发布时间') @@ -122,6 +125,14 @@ class Meta: verbose_name_plural = verbose_name = u"文章" +@receiver(signals.post_save, sender=Post) +def check_or_update_post_alias(sender, instance=None, **kwargs): + # 如果alias未设置则使用id + if not instance.alias: + instance.alias = instance.id + instance.save() + + class Page(models.Model): author = models.ForeignKey(User, verbose_name=u'作者') title = models.CharField(max_length=100, verbose_name=u'标题') diff --git a/selfblog/blog/templates/base.html b/selfblog/blog/templates/base.html index 9e3ae04..b066fa4 100644 --- a/selfblog/blog/templates/base.html +++ b/selfblog/blog/templates/base.html @@ -100,5 +100,6 @@

the5fire的技术博客

{% include "includes/footer.html" %} + {% block extend_js %}{% endblock %} diff --git a/selfblog/blog/templates/detail.html b/selfblog/blog/templates/detail.html index bad2b12..6cecc01 100644 --- a/selfblog/blog/templates/detail.html +++ b/selfblog/blog/templates/detail.html @@ -6,12 +6,11 @@ {% block keywords %}{{ post.tags }}{% endblock %} {% block extend_style %} +{% if post.is_md %} + +{% else %} - - - +{% endif %} {% endblock %} {% block main %} @@ -99,3 +98,12 @@

{% include "pingback.html" %} {% endblock %} + +{% block extend_js %} + + + +{% endblock %} + diff --git a/selfblog/blog/templates/includes/footer.html b/selfblog/blog/templates/includes/footer.html index a2e1618..a886468 100644 --- a/selfblog/blog/templates/includes/footer.html +++ b/selfblog/blog/templates/includes/footer.html @@ -4,13 +4,14 @@
-

版权所有 © 2010-2013 the5fire.com

+

版权所有 © 2010-2016 }} the5fire.com

CC BY-NC-SA 3.0

- Powered by Django. - Host on WebFaction. + Powered by Django.

+ Host on DigitalOcean $5/月. +

当前{{ online_num }}人在线,表示毫无压力 {% if online_num > 50 %} 囧~ diff --git a/selfblog/blog/views.py b/selfblog/blog/views.py index a709606..b30741b 100644 --- a/selfblog/blog/views.py +++ b/selfblog/blog/views.py @@ -8,7 +8,7 @@ from django.shortcuts import render from ipware.ip import get_real_ip -from selfblog.settings import PAGE_NUM, RECENTLY_NUM, HOT_NUM, FIF_MIN +from django.conf import settings from .models import Post, Category, Page, Widget from utils.cache import LRUCacheDict, cache @@ -26,8 +26,8 @@ def get_context_data(self, *args, **kwargs): try: context['categories'] = Category.available_list() context['widgets'] = Widget.available_list() - context['recently_posts'] = Post.get_recently_posts(RECENTLY_NUM) - context['hot_posts'] = Post.get_hots_posts(HOT_NUM) + context['recently_posts'] = Post.get_recently_posts(settings.RECENTLY_NUM) + context['hot_posts'] = Post.get_hots_posts(settings.HOT_NUM) context['pages'] = Page.objects.filter(status=0) context['online_num'] = len(cache.get('online_ips', [])) except Exception as e: @@ -52,7 +52,7 @@ def get(self, request, *args, **kwargs): return super(IndexView, self).get(request, *args, **kwargs) def get_context_data(self, **kwargs): - paginator = Paginator(self.object_list, PAGE_NUM) + paginator = Paginator(self.object_list, settings.PAGE_NUM) kwargs['posts'] = paginator.page(self.cur_page) kwargs['query'] = self.query return super(IndexView, self).get_context_data(**kwargs) @@ -146,12 +146,12 @@ def set_lru_read(self, ip, post): # 保存别人正在读 lru_views = cache.get('lru_views') if not lru_views: - lru_views = LRUCacheDict(max_size=10, expiration=FIF_MIN) + lru_views = LRUCacheDict(max_size=10, expiration=settings.FIF_MIN) if post not in lru_views.values(): lru_views[ip] = post - cache.set('lru_views', lru_views, FIF_MIN) + cache.set('lru_views', lru_views, settings.FIF_MIN) def get_context_data(self, **kwargs): context = super(PostDetailView, self).get_context_data(**kwargs) diff --git a/selfblog/manage.py b/selfblog/manage.py index c600d1e..9332fcf 100755 --- a/selfblog/manage.py +++ b/selfblog/manage.py @@ -3,6 +3,7 @@ import sys if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "selfblog.settings") + PROFILE = os.environ.get('DJANGOSELFBLOG_PROFILE', 'develop') + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "selfblog.settings.%s" % PROFILE) from django.core.management import execute_from_command_line execute_from_command_line(sys.argv) diff --git a/selfblog/selfblog/settings/__init__.py b/selfblog/selfblog/settings/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/selfblog/selfblog/settings.py b/selfblog/selfblog/settings/base.py similarity index 55% rename from selfblog/selfblog/settings.py rename to selfblog/selfblog/settings/base.py index 89dd55b..f9adf80 100644 --- a/selfblog/selfblog/settings.py +++ b/selfblog/selfblog/settings/base.py @@ -1,12 +1,6 @@ -#coding:utf-8 -# Django settings for selfblog project. +# coding:utf-8 +import os from os import path -from platform import platform -if 'centos' in platform(): - DEBUG = False -else: - DEBUG = True -TEMPLATE_DEBUG = DEBUG ROOT_PATH = path.abspath(path.join(path.dirname('settings.py'), path.pardir)) @@ -16,67 +10,15 @@ ALLOWED_HOSTS = ['localhost', '.the5fire.com'] MANAGERS = ADMINS - -if DEBUG: - DOMAIN = 'http://localhost:8000' - DB_NAME = 'selfblog.sqlite3' - DB_USER = 'root' - DB_PWD = 'root' -else: - DOMAIN = 'http://www.the5fire.com' - DB_NAME = 'mydb' - DB_USER = 'the5fire' - DB_PWD = 'the5fire' - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', # Add 'postgresql_psycopg2', 'postgresql', 'mysql', 'sqlite3' or 'oracle'. - 'NAME': DB_NAME, # Or path to database file if using sqlite3. - 'USER': DB_USER, # Not used with sqlite3. - 'PASSWORD': DB_PWD, # Not used with sqlite3. - 'HOST': '', # Set to empty string for localhost. Not used with sqlite3. - 'PORT': '', # Set to empty string for default. Not used with sqlite3. - } -} - -# Local time zone for this installation. Choices can be found here: -# http://en.wikipedia.org/wiki/List_of_tz_zones_by_name -# although not all choices may be available on all operating systems. -# On Unix systems, a value of None will cause Django to use the same -# timezone as the operating system. -# If running in a Windows environment this must be set to the same as your -# system time zone. TIME_ZONE = 'Asia/Shanghai' - -# Language code for this installation. All choices can be found here: -# http://www.i18nguy.com/unicode/language-identifiers.html LANGUAGE_CODE = 'zh-cn' - SITE_ID = 1 - -# If you set this to False, Django will make some optimizations so as not -# to load the internationalization machinery. USE_I18N = True - -# If you set this to False, Django will not format dates, numbers and -# calendars according to the current locale USE_L10N = True MEDIA_ROOT = '' - -# URL that handles the media served from MEDIA_ROOT. Make sure to use a -# trailing slash. -# Examples: "http://media.lawrence.com/media/", "http://example.com/media/" MEDIA_URL = '/media/' - -# Absolute path to the directory static files should be collected to. -# Don't put anything in this directory yourself; store your static files -# in apps' "static/" subdirectories and in STATICFILES_DIRS. -# Example: "/home/media/media.lawrence.com/static/" -#STATIC_ROOT = '' - -# URL prefix for static files. -# Example: "http://media.lawrence.com/static/" +# STATIC_ROOT = '' STATIC_URL = '/static/' # Additional locations of static files @@ -89,7 +31,7 @@ STATICFILES_FINDERS = ( 'django.contrib.staticfiles.finders.FileSystemFinder', 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - #'django.contrib.staticfiles.finders.DefaultStorageFinder', + # 'django.contrib.staticfiles.finders.DefaultStorageFinder', ) # Make this unique, and don't share it with anybody. @@ -99,7 +41,7 @@ TEMPLATE_LOADERS = ( 'django.template.loaders.filesystem.Loader', 'django.template.loaders.app_directories.Loader', - #'django.template.loaders.eggs.Loader', + # 'django.template.loaders.eggs.Loader', ) MIDDLEWARE_CLASSES = ( @@ -145,22 +87,11 @@ 'weixin', ) -# A sample logging configuration. The only tangible logging -# performed by this configuration is to send an email to -# the site admins on every HTTP 500 error. -# See http://docs.djangoproject.com/en/dev/topics/logging for -# more details on how to customize your logging configuration. -if DEBUG: - INSTALLED_APPS = INSTALLED_APPS + ('debug_toolbar', ) - MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + ('debug_toolbar.middleware.DebugToolbarMiddleware', ) - LOG_FILE = '/tmp/blog.log' -else: - LOG_FILE = '/home/the5fire/virtualenvs/bloga/logs/all.log' +LOG_PATH = os.path.join(ROOT_PATH, '..', '..') LOGGING = { 'version': 1, 'disable_existing_loggers': True, - 'filters': { 'require_debug_false': { '()': 'django.utils.log.RequireDebugFalse' @@ -176,20 +107,18 @@ }, 'handlers': { - 'null': { - 'level': 'DEBUG', - 'class': 'django.utils.log.NullHandler', - }, 'console': { 'level': 'INFO', 'class': 'logging.StreamHandler', 'formatter': 'verbose' }, - 'file': { + 'file_info': { 'level': 'INFO', - 'class': 'logging.FileHandler', + 'class': 'logging.handlers.RotatingFileHandler', 'formatter': 'verbose', - 'filename': LOG_FILE, + 'filename': os.path.join(LOG_PATH, 'info.log'), + 'maxBytes': 1024 * 1024 * 100, + 'backupCount': 10, 'mode': 'a', }, 'mail_admins': { @@ -200,12 +129,12 @@ }, 'loggers': { '': { - 'handlers': ['file', 'console'], + 'handlers': ['file_info', 'console'], 'level': 'INFO', 'propagate': True, }, 'django': { - 'handlers': ['file', 'console'], + 'handlers': ['file_info', 'console'], 'level': 'DEBUG', 'propagate': True, }, @@ -216,7 +145,6 @@ }, } } - CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', @@ -235,7 +163,7 @@ } -#配置文章开头使用rst格式无显示的问题 +# 配置文章开头使用rst格式无显示的问题 RESTRUCTUREDTEXT_FILTER_SETTINGS = { 'doctitle_xform': False, } diff --git a/selfblog/selfblog/settings/develop.py b/selfblog/selfblog/settings/develop.py new file mode 100644 index 0000000..8f502a7 --- /dev/null +++ b/selfblog/selfblog/settings/develop.py @@ -0,0 +1,21 @@ +# coding:utf-8 + +from .base import * # noqa + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +DOMAIN = 'http://localhost:8000' +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': 'selfblog.sqlite3', + 'USER': 'root', + 'PASSWORD': 'root', + 'HOST': '', + 'PORT': '', + } +} + +INSTALLED_APPS = INSTALLED_APPS + ('debug_toolbar', ) +MIDDLEWARE_CLASSES = MIDDLEWARE_CLASSES + ('debug_toolbar.middleware.DebugToolbarMiddleware', ) diff --git a/selfblog/selfblog/settings/product.py b/selfblog/selfblog/settings/product.py new file mode 100644 index 0000000..82ebb44 --- /dev/null +++ b/selfblog/selfblog/settings/product.py @@ -0,0 +1,22 @@ +# coding:utf-8 + +from .base import * # noqa + +DEBUG = True +TEMPLATE_DEBUG = DEBUG + +DOMAIN = 'http://www.the5fire.com' # yourdomain +DB_NAME = 'mydb' +DB_USER = 'the5fire' +DB_PWD = 'the5fire' + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': 'mydb', + 'USER': 'the5fire', + 'PASSWORD': 'the5fire', + 'HOST': '', + 'PORT': '', + } +} diff --git a/selfblog/selfblog/wsgi.py b/selfblog/selfblog/wsgi.py index 106eabe..5d8159b 100644 --- a/selfblog/selfblog/wsgi.py +++ b/selfblog/selfblog/wsgi.py @@ -1,29 +1,8 @@ -""" -WSGI config for selfblog project. - -This module contains the WSGI application used by Django's development server -and any production WSGI deployments. It should expose a module-level variable -named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover -this application via the ``WSGI_APPLICATION`` setting. - -Usually you will have the standard Django WSGI application here, but it also -might make sense to replace the whole Django WSGI application with a custom one -that later delegates to the Django one. For example, you could introduce WSGI -middleware here, or combine a Django application with an application of another -framework. - -""" import os -# We defer to a DJANGO_SETTINGS_MODULE already in the environment. This breaks -# if running multiple sites in the same mod_wsgi process. To fix this, use -# mod_wsgi daemon mode with each site in its own daemon process, or use -# os.environ["DJANGO_SETTINGS_MODULE"] = "myblog.settings" -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "selfblog.settings") +PROFILE = os.environ.get('DJANGOSELFBLOG_PROFILE', 'develop') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "selfblog.settings.%s" % PROFILE) -# This application object is used by any WSGI server configured to use this -# file. This includes Django's development server, if the WSGI_APPLICATION -# setting points here. from django.core.wsgi import get_wsgi_application application = get_wsgi_application() diff --git a/selfblog/static/blog/css/codehilite.css b/selfblog/static/blog/css/codehilite.css new file mode 100644 index 0000000..ef95359 --- /dev/null +++ b/selfblog/static/blog/css/codehilite.css @@ -0,0 +1,62 @@ +.codehilite .hll { background-color: #ffffcc } +.codehilite { background: #f8f8f8; } +.codehilite .c { color: #408080; font-style: italic } /* Comment */ +.codehilite .err { border: 1px solid #FF0000 } /* Error */ +.codehilite .k { color: #008000; font-weight: bold } /* Keyword */ +.codehilite .o { color: #666666 } /* Operator */ +.codehilite .cm { color: #408080; font-style: italic } /* Comment.Multiline */ +.codehilite .cp { color: #BC7A00 } /* Comment.Preproc */ +.codehilite .c1 { color: #408080; font-style: italic } /* Comment.Single */ +.codehilite .cs { color: #408080; font-style: italic } /* Comment.Special */ +.codehilite .gd { color: #A00000 } /* Generic.Deleted */ +.codehilite .ge { font-style: italic } /* Generic.Emph */ +.codehilite .gr { color: #FF0000 } /* Generic.Error */ +.codehilite .gh { color: #000080; font-weight: bold } /* Generic.Heading */ +.codehilite .gi { color: #00A000 } /* Generic.Inserted */ +.codehilite .go { color: #808080 } /* Generic.Output */ +.codehilite .gp { color: #000080; font-weight: bold } /* Generic.Prompt */ +.codehilite .gs { font-weight: bold } /* Generic.Strong */ +.codehilite .gu { color: #800080; font-weight: bold } /* Generic.Subheading */ +.codehilite .gt { color: #0040D0 } /* Generic.Traceback */ +.codehilite .kc { color: #008000; font-weight: bold } /* Keyword.Constant */ +.codehilite .kd { color: #008000; font-weight: bold } /* Keyword.Declaration */ +.codehilite .kn { color: #008000; font-weight: bold } /* Keyword.Namespace */ +.codehilite .kp { color: #008000 } /* Keyword.Pseudo */ +.codehilite .kr { color: #008000; font-weight: bold } /* Keyword.Reserved */ +.codehilite .kt { color: #B00040 } /* Keyword.Type */ +.codehilite .m { color: #666666 } /* Literal.Number */ +.codehilite .s { color: #BA2121 } /* Literal.String */ +.codehilite .na { color: #7D9029 } /* Name.Attribute */ +.codehilite .nb { color: #008000 } /* Name.Builtin */ +.codehilite .nc { color: #0000FF; font-weight: bold } /* Name.Class */ +.codehilite .no { color: #880000 } /* Name.Constant */ +.codehilite .nd { color: #AA22FF } /* Name.Decorator */ +.codehilite .ni { color: #999999; font-weight: bold } /* Name.Entity */ +.codehilite .ne { color: #D2413A; font-weight: bold } /* Name.Exception */ +.codehilite .nf { color: #0000FF } /* Name.Function */ +.codehilite .nl { color: #A0A000 } /* Name.Label */ +.codehilite .nn { color: #0000FF; font-weight: bold } /* Name.Namespace */ +.codehilite .nt { color: #008000; font-weight: bold } /* Name.Tag */ +.codehilite .nv { color: #19177C } /* Name.Variable */ +.codehilite .ow { color: #AA22FF; font-weight: bold } /* Operator.Word */ +.codehilite .w { color: #bbbbbb } /* Text.Whitespace */ +.codehilite .mf { color: #666666 } /* Literal.Number.Float */ +.codehilite .mh { color: #666666 } /* Literal.Number.Hex */ +.codehilite .mi { color: #666666 } /* Literal.Number.Integer */ +.codehilite .mo { color: #666666 } /* Literal.Number.Oct */ +.codehilite .sb { color: #BA2121 } /* Literal.String.Backtick */ +.codehilite .sc { color: #BA2121 } /* Literal.String.Char */ +.codehilite .sd { color: #BA2121; font-style: italic } /* Literal.String.Doc */ +.codehilite .s2 { color: #BA2121 } /* Literal.String.Double */ +.codehilite .se { color: #BB6622; font-weight: bold } /* Literal.String.Escape */ +.codehilite .sh { color: #BA2121 } /* Literal.String.Heredoc */ +.codehilite .si { color: #BB6688; font-weight: bold } /* Literal.String.Interpol */ +.codehilite .sx { color: #008000 } /* Literal.String.Other */ +.codehilite .sr { color: #BB6688 } /* Literal.String.Regex */ +.codehilite .s1 { color: #BA2121 } /* Literal.String.Single */ +.codehilite .ss { color: #19177C } /* Literal.String.Symbol */ +.codehilite .bp { color: #008000 } /* Name.Builtin.Pseudo */ +.codehilite .vc { color: #19177C } /* Name.Variable.Class */ +.codehilite .vg { color: #19177C } /* Name.Variable.Global */ +.codehilite .vi { color: #19177C } /* Name.Variable.Instance */ +.codehilite .il { color: #666666 } /* Literal.Number.Integer.Long */