Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a29290aadc | ||
|
|
3b61b80598 | ||
|
|
d6346b37e6 | ||
|
|
451f3983e0 | ||
|
|
75525b107a | ||
|
|
10f2c66bb8 | ||
|
|
a58e5fb014 | ||
|
|
fa049a5ea4 | ||
|
|
7e54996527 | ||
|
|
956ba2a167 | ||
|
|
6d30d40b86 | ||
|
|
6ccba22b91 | ||
|
|
63c5ae8797 | ||
|
|
68ba79add6 | ||
|
|
bbbcb175c9 | ||
|
|
062ad09ae0 | ||
|
|
521486d405 | ||
|
|
2a7a96d566 | ||
|
|
b8e54d131b | ||
|
|
51f6b48b98 | ||
|
|
b39ed27543 | ||
|
|
5ce213862d | ||
|
|
ed4f96b1d4 | ||
|
|
e7ed787ab0 | ||
|
|
90ac06a675 | ||
|
|
b7cd5e9a2d | ||
|
|
d65284fb80 | ||
|
|
1c7759738e | ||
|
|
4076b99ff7 | ||
|
|
a360b6a1b8 | ||
|
|
711259beef | ||
|
|
b80eb18510 | ||
|
|
58b32031be | ||
|
|
3067586803 | ||
|
|
f36d5b4856 | ||
|
|
1326c4ea15 | ||
|
|
1af0823d65 | ||
|
|
09f3e50be0 | ||
|
|
9ba3393afb | ||
|
|
0268b4beb5 | ||
|
|
b41b5c210d | ||
|
|
2e4c61a5c8 | ||
|
|
86d2318fed | ||
|
|
1805f8e81e | ||
|
|
588d38d127 | ||
|
|
75243b4b84 | ||
|
|
f6ab2b7ccb | ||
|
|
7e5c3eac12 | ||
|
|
2a9b9cb97c | ||
|
|
249fdf10f3 | ||
|
|
8fb436649c |
@@ -8,3 +8,6 @@ omit =
|
||||
*whoosh_cn_backend*
|
||||
*apps*
|
||||
*commands*
|
||||
*oauth*
|
||||
*travis_test*
|
||||
*settings.py*
|
||||
|
||||
@@ -72,3 +72,6 @@ google93fd32dbd906620a.html
|
||||
baidu_verify_FlHL7cUyC9.html
|
||||
BingSiteAuth.xml
|
||||
cb9339dbe2ff86a5aa169d28dba5f615.txt
|
||||
werobot_session
|
||||
django.jpg
|
||||
uploads/
|
||||
@@ -1,3 +1,4 @@
|
||||
dist: precise
|
||||
language: python
|
||||
python:
|
||||
- "3.4"
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
|
||||
"""
|
||||
@version: ??
|
||||
@author: liangliangyy
|
||||
@license: MIT Licence
|
||||
@contact: liangliangyy@gmail.com
|
||||
@site: https://www.lylinux.org/
|
||||
@software: PyCharm
|
||||
@file: blog_signals.py
|
||||
@time: 2017/8/12 上午10:18
|
||||
"""
|
||||
|
||||
import django.dispatch
|
||||
from django.dispatch import receiver
|
||||
from django.conf import settings
|
||||
from DjangoBlog.utils import cache, send_email, expire_view_cache, logger
|
||||
from DjangoBlog.spider_notify import SpiderNotify
|
||||
from django.contrib.sites.models import Site
|
||||
|
||||
comment_save_signal = django.dispatch.Signal(providing_args=["comment_id", "username", "serverport"])
|
||||
article_save_signal = django.dispatch.Signal(providing_args=['id', 'is_update_views'])
|
||||
user_login_logout_signal = django.dispatch.Signal(providing_args=['id', 'type'])
|
||||
|
||||
|
||||
@receiver(article_save_signal)
|
||||
def article_save_callback(sender, **kwargs):
|
||||
id = kwargs['id']
|
||||
is_update_views = kwargs['is_update_views']
|
||||
type = sender.__name__
|
||||
obj = None
|
||||
from blog.models import Article, Category, Tag
|
||||
if type == 'Article':
|
||||
obj = Article.objects.get(id=id)
|
||||
elif type == 'Category':
|
||||
obj = Category.objects.get(id=id)
|
||||
elif type == 'Tag':
|
||||
obj = Tag.objects.get(id=id)
|
||||
if obj is not None:
|
||||
if not settings.TESTING and not is_update_views:
|
||||
try:
|
||||
notify_url = obj.get_full_url()
|
||||
SpiderNotify.baidu_notify([notify_url])
|
||||
except Exception as ex:
|
||||
logger.error("notify sipder", ex)
|
||||
print(ex)
|
||||
|
||||
|
||||
@receiver(comment_save_signal)
|
||||
def comment_save_callback(sender, **kwargs):
|
||||
from comments.models import Comment
|
||||
|
||||
serverport = kwargs['serverport']
|
||||
username = kwargs['username']
|
||||
comment = Comment.objects.get(id=kwargs['comment_id'])
|
||||
site = Site.objects.get_current().domain
|
||||
article = comment.article
|
||||
# if not settings.DEBUG:
|
||||
if True:
|
||||
subject = '感谢您发表的评论'
|
||||
article_url = "https://{site}{path}".format(site=site, path=comment.article.get_absolute_url())
|
||||
html_content = """
|
||||
<p>非常感谢您在本站发表评论</p>
|
||||
您可以访问
|
||||
<a href="%s" rel="bookmark">%s</a>
|
||||
来查看您的评论,
|
||||
再次感谢您!
|
||||
<br />
|
||||
如果上面链接无法打开,请将此链接复制至浏览器。
|
||||
%s
|
||||
""" % (article_url, comment.article.title, article_url)
|
||||
tomail = comment.author.email
|
||||
send_email([tomail], subject, html_content)
|
||||
|
||||
if comment.parent_comment:
|
||||
html_content = """
|
||||
您在 <a href="%s" rel="bookmark">%s</a> 的评论 <br/> %s <br/> 收到回复啦.快去看看吧
|
||||
<br/>
|
||||
如果上面链接无法打开,请将此链接复制至浏览器。
|
||||
%s
|
||||
""" % (article_url, article.title, comment.parent_comment.body, article_url)
|
||||
tomail = comment.parent_comment.author.email
|
||||
send_email([tomail], subject, html_content)
|
||||
|
||||
path = article.get_absolute_url()
|
||||
site = Site.objects.get_current().domain
|
||||
if site.find(':') > 0:
|
||||
site = site[0:site.find(':')]
|
||||
|
||||
expire_view_cache(path, servername=site, serverport=serverport, key_prefix='blogdetail')
|
||||
if cache.get('seo_processor'):
|
||||
cache.delete('seo_processor')
|
||||
comment_cache_key = 'article_comments_{id}'.format(id=article.id)
|
||||
cache.delete(comment_cache_key)
|
||||
from django.core.cache.utils import make_template_fragment_key
|
||||
|
||||
key = make_template_fragment_key('sidebar', [username])
|
||||
cache.delete(key)
|
||||
+13
-4
@@ -45,6 +45,7 @@ INSTALLED_APPS = [
|
||||
'accounts',
|
||||
'comments',
|
||||
'oauth',
|
||||
'servermanager',
|
||||
'compressor'
|
||||
]
|
||||
|
||||
@@ -163,7 +164,7 @@ SITE_NAME = '且听风吟'
|
||||
SITE_URL = 'http://www.lylinux.net'
|
||||
SITE_DESCRIPTION = '大巧无工,重剑无锋.'
|
||||
SITE_SEO_DESCRIPTION = '小站主要用来分享和记录学习经验,教程,记录个人生活的点滴以及一些随笔.欢迎大家访问小站'
|
||||
SITE_SEO_KEYWORDS = 'linux,apache,mysql,服务器,ubuntu,shell,web,csharp,.net,asp,mac,swift'
|
||||
SITE_SEO_KEYWORDS = 'linux,apache,mysql,服务器,ubuntu,shell,web,csharp,.net,asp,mac,swift,python,django'
|
||||
ARTICLE_SUB_LENGTH = 300
|
||||
SHOW_GOOGLE_ADSENSE = False
|
||||
# bootstrap颜色样式
|
||||
@@ -185,10 +186,12 @@ CACHE_CONTROL_MAX_AGE = 2592000
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
|
||||
'TIMEOUT': 10800,
|
||||
'LOCATION': '127.0.0.1:11211',
|
||||
},
|
||||
'locmemcache': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
'TIMEOUT': 10800,
|
||||
'LOCATION': 'unique-snowflake',
|
||||
}
|
||||
}
|
||||
@@ -214,11 +217,16 @@ OAHUTH = {
|
||||
'appkey': os.environ.get('GITHUB_APP_KEY'),
|
||||
'appsecret': os.environ.get('GITHUB_APP_SECRET'),
|
||||
'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=github'
|
||||
},
|
||||
'facebook': {
|
||||
'appkey': os.environ.get('FACEBOOK_APP_KEY'),
|
||||
'appsecret': os.environ.get('FACEBOOK_APP_SECRET'),
|
||||
'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=facebook'
|
||||
}
|
||||
}
|
||||
|
||||
SITE_ID = 1
|
||||
BAIDU_NOTIFY_URL = "http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn&type=original"
|
||||
BAIDU_NOTIFY_URL = "http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn"
|
||||
|
||||
# Emial:
|
||||
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
@@ -226,7 +234,7 @@ EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
|
||||
EMAIL_USE_TLS = True
|
||||
# EMAIL_USE_SSL = True
|
||||
|
||||
EMAIL_HOST = 'smtp.exmail.qq.com'
|
||||
EMAIL_HOST = 'smtp.zoho.com'
|
||||
EMAIL_PORT = 587
|
||||
EMAIL_HOST_USER = os.environ.get('DJANGO_EMAIL_USER')
|
||||
EMAIL_HOST_PASSWORD = os.environ.get('DJANGO_EMAIL_PASSWORD')
|
||||
@@ -234,7 +242,8 @@ DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
|
||||
SERVER_EMAIL = os.environ.get('DJANGO_EMAIL_USER')
|
||||
# 设置debug=false 未处理异常邮件通知
|
||||
ADMINS = [('liangliang', 'liangliangyy@gmail.com')]
|
||||
|
||||
# 微信管理员密码(两次md5获得)
|
||||
WXADMIN = '995F03AC401D6CABABAEF756FC4D43C7'
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
|
||||
"""
|
||||
@version: ??
|
||||
@author: liangliangyy
|
||||
@license: MIT Licence
|
||||
@contact: liangliangyy@gmail.com
|
||||
@site: https://www.lylinux.net/
|
||||
@software: PyCharm
|
||||
@file: tests.py
|
||||
@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 django.contrib.sites.models import Site
|
||||
from django.core.urlresolvers import reverse
|
||||
import datetime
|
||||
from DjangoBlog.utils import *
|
||||
|
||||
|
||||
class DjangoBlogTest(TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
|
||||
def test_utils(self):
|
||||
md5 = get_md5('test')
|
||||
self.assertIsNotNone(md5)
|
||||
c = CommonMarkdown.get_markdown('''
|
||||
# Title1
|
||||
|
||||
```python
|
||||
import os
|
||||
```
|
||||
|
||||
[url](https://www.lylinux.org/)
|
||||
|
||||
[ddd](http://www.baidu.com)
|
||||
|
||||
|
||||
''')
|
||||
self.assertIsNotNone(c)
|
||||
d = {
|
||||
'd': 'key1',
|
||||
'd2': 'key2'
|
||||
}
|
||||
data = parse_dict_to_url(d)
|
||||
self.assertIsNotNone(data)
|
||||
|
||||
|
||||
+4
-1
@@ -31,6 +31,9 @@ sitemaps = {
|
||||
'static': StaticViewSitemap
|
||||
}
|
||||
|
||||
handler404 = 'blog.views.page_not_found_view'
|
||||
handler500 = 'blog.views.server_error_view'
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^admin/', admin.site.urls),
|
||||
url(r'', include('blog.urls', namespace='blog', app_name='blog')),
|
||||
@@ -41,6 +44,6 @@ urlpatterns = [
|
||||
url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps},
|
||||
name='django.contrib.sitemaps.views.sitemap'),
|
||||
url(r'^feed/$', DjangoBlogFeed()),
|
||||
|
||||
url(r'^search', include('haystack.urls'), name='search'),
|
||||
url(r'', include('servermanager.urls', namespace='servermanager', app_name='servermanagers'))
|
||||
] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
||||
|
||||
+5
-12
@@ -23,6 +23,7 @@ from pygments.formatters import html
|
||||
import logging
|
||||
import _thread
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.conf import settings
|
||||
|
||||
logger = logging.getLogger('djangoblog')
|
||||
|
||||
@@ -106,7 +107,7 @@ def block_code(text, lang, inlinestyles=False, linenos=False):
|
||||
|
||||
|
||||
class BlogMarkDownRenderer(mistune.Renderer):
|
||||
def block_code(self, text, lang):
|
||||
def block_code(self, text, lang=None):
|
||||
# renderer has an options
|
||||
inlinestyles = self.options.get('inlinestyles')
|
||||
linenos = self.options.get('linenos')
|
||||
@@ -144,18 +145,10 @@ class CommonMarkdown():
|
||||
return mdp(value)
|
||||
|
||||
|
||||
def send_email(subject, html_content, tomail):
|
||||
msg = EmailMultiAlternatives(subject, html_content, from_email='no-reply@lylinux.net', to=tomail)
|
||||
def send_email(emailto, title, content):
|
||||
msg = EmailMultiAlternatives(title, content, from_email=settings.DEFAULT_FROM_EMAIL, to=emailto)
|
||||
msg.content_subtype = "html"
|
||||
|
||||
def send_comment_email(msg):
|
||||
try:
|
||||
msg.send()
|
||||
except:
|
||||
print('send email error')
|
||||
pass
|
||||
|
||||
_thread.start_new_thread(send_comment_email, (msg,))
|
||||
_thread.start_new_thread(msg.send, (msg,))
|
||||
|
||||
|
||||
def parse_dict_to_url(dict):
|
||||
|
||||
@@ -4,9 +4,22 @@
|
||||
|
||||
[](https://travis-ci.org/liangliangyy/DjangoBlog) [](https://coveralls.io/github/liangliangyy/DjangoBlog?branch=master) [](https://requires.io/github/liangliangyy/DjangoBlog/requirements/?branch=master) []() []() []() []()
|
||||
|
||||
## 主要功能:
|
||||
- 文章,页面,分类目录,标签的添加,删除,编辑等。文章及页面支持`Markdown`,支持代码高亮。
|
||||
- 支持文章全文搜索。
|
||||
- 完整的评论功能,包括发表回复评论,以及评论的邮件提醒,支持`Markdown`。
|
||||
- 侧边栏功能,最新文章,最多阅读,标签云等。
|
||||
- 支持Oauth登陆,现已有Google,GitHub,facebook,微博登录。
|
||||
- 支持`Memcache`缓存,支持缓存自动刷新。
|
||||
- 简单的SEO功能,新建文章等会自动通知Google和百度。
|
||||
- 集成了简单的图床功能。
|
||||
- 集成`django-compressor`,自动压缩`css`,`js`。
|
||||
- 基于`python3`,支持`Django`多版本。`Django`的1.8,1.9,1.10,1.11均测试通过。
|
||||
- 网站异常邮件提醒,若有未捕捉到的异常会自动发送提醒邮件。
|
||||
- 集成了微信公众号功能,现在可以使用微信公众号来管理你的vps了。
|
||||
## 安装
|
||||
使用pip安装:
|
||||
`pip install -r requirements.txt`
|
||||
`pip install -Ur requirements.txt`
|
||||
|
||||
如果你没有pip,使用如下方式安装:
|
||||
OS X / Linux 电脑,终端下执行:
|
||||
@@ -30,6 +43,10 @@ windows电脑:
|
||||
|
||||
有详细的部署介绍.
|
||||
|
||||
为了安全起见,没有把`SECRET_KEY`上传到Github中而是在环境变量中配置的,如果你想要正常运行的话,需要修改`settings.py`中的`SECRET_KEY`为你自己的就可以了。
|
||||
如:`SECRET_KEY = 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6'`
|
||||
若本地部署后发现静态文件无法加载.请将`settings.py`中的`DEBUG=False`修改为`DEBUG=True`即可.
|
||||
|
||||
## 运行
|
||||
|
||||
修改`DjangoBlog/setting.py` 修改数据库配置,如下所示:
|
||||
@@ -47,22 +64,26 @@ windows电脑:
|
||||
|
||||
### 创建数据库
|
||||
|
||||
终端下执行:
|
||||
终端下执行:
|
||||
|
||||
./manage.py makemigrations
|
||||
./manage.py migrate
|
||||
./manage.py migrate
|
||||
### 创建超级用户
|
||||
|
||||
终端下执行:
|
||||
终端下执行:
|
||||
|
||||
./manage.py createsuperuser
|
||||
### 创建测试数据
|
||||
终端下执行:
|
||||
终端下执行:
|
||||
|
||||
./manage.py create_testdata
|
||||
### 收集静态文件
|
||||
终端下执行:
|
||||
|
||||
./manage.py collectstatic --noinput
|
||||
./manage.py compress --force
|
||||
### 开始运行:
|
||||
执行:
|
||||
执行:
|
||||
`./manage.py runserver`
|
||||
|
||||
|
||||
@@ -70,3 +91,18 @@ windows电脑:
|
||||
|
||||
|
||||
浏览器打开: http://127.0.0.1:8000/ 就可以看到效果了。
|
||||
|
||||
## 问题相关
|
||||
|
||||
有任何问题欢迎提Issue,或者将问题描述发送至我邮箱 `liangliangyy#gmail.com`.我会尽快解答.推荐提交Issue方式.
|
||||
## 捐赠
|
||||
如果您觉得本项目对您有所帮助,欢迎您请我喝杯咖啡,您的支持是我最大的动力,您可以扫描下方二维码为我付款,谢谢。
|
||||
### 支付宝:
|
||||
<div align="center">
|
||||
<img src="https://resource.lylinux.net/image/2017/12/16/IMG_0207.jpg" width="300" height="300" />
|
||||
</div>
|
||||
|
||||
### 微信:
|
||||
<div align="center">
|
||||
<img src="https://resource.lylinux.net/image/2017/12/16/IMG_0206.jpg" width="300" height="300" />
|
||||
</div>
|
||||
|
||||
+9
-1
@@ -3,4 +3,12 @@ from django.contrib import admin
|
||||
# Register your models here.
|
||||
from .models import BlogUser
|
||||
|
||||
admin.site.register(BlogUser)
|
||||
|
||||
|
||||
class BlogUserAdmin(admin.ModelAdmin):
|
||||
list_display = ('id', 'nickname', 'username','email','last_login','date_joined')
|
||||
list_display_links = ('id', 'username')
|
||||
|
||||
|
||||
|
||||
admin.site.register(BlogUser, BlogUserAdmin)
|
||||
|
||||
@@ -2,6 +2,7 @@ from django.db import models
|
||||
from django.contrib.auth.models import AbstractUser, BaseUserManager
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.sites.models import Site
|
||||
from django.utils.timezone import now
|
||||
|
||||
|
||||
# Create your models here.
|
||||
@@ -9,6 +10,8 @@ from django.contrib.sites.models import Site
|
||||
class BlogUser(AbstractUser):
|
||||
nickname = models.CharField('昵称', max_length=50, blank=True)
|
||||
mugshot = models.ImageField('头像', upload_to='upload/mugshots', blank=True)
|
||||
created_time = models.DateTimeField('创建时间', default=now)
|
||||
last_mod_time = models.DateTimeField('修改时间', default=now)
|
||||
|
||||
# objects = BlogUserManager()
|
||||
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@
|
||||
NAME="djangoblog" # Name of the application
|
||||
DJANGODIR=//var/www/DjangoBlog # Django project directory
|
||||
SOCKFILE=/var/www/DjangoBlog/run/gunicorn.sock # we will communicte using this unix socket
|
||||
USER=www-data # the user to run as
|
||||
GROUP=www-data # the group to run as
|
||||
USER=root # the user to run as
|
||||
GROUP=root # the group to run as
|
||||
NUM_WORKERS=3 # how many worker processes should Gunicorn spawn
|
||||
DJANGO_SETTINGS_MODULE=DjangoBlog.settings # which settings file should Django use
|
||||
DJANGO_WSGI_MODULE=DjangoBlog.wsgi # WSGI module name
|
||||
|
||||
+15
-6
@@ -1,7 +1,7 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
from .models import Article, Category, Tag, Links
|
||||
from .models import Article, Category, Tag, Links, SideBar
|
||||
from pagedown.widgets import AdminPagedownWidget
|
||||
from django import forms
|
||||
from django.contrib.auth import get_user_model
|
||||
@@ -39,7 +39,7 @@ class ArticlelAdmin(admin.ModelAdmin):
|
||||
list_display_links = ('id', 'title')
|
||||
list_filter = (ArticleListFilter, 'status', 'type', 'category', 'tags')
|
||||
filter_horizontal = ('tags',)
|
||||
exclude = ('slug', 'created_time')
|
||||
exclude = ('slug', 'created_time', 'last_mod_time')
|
||||
|
||||
def get_form(self, request, obj=None, **kwargs):
|
||||
form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs)
|
||||
@@ -53,15 +53,24 @@ class ArticlelAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
class TagAdmin(admin.ModelAdmin):
|
||||
exclude = ('slug',)
|
||||
exclude = ('slug', 'last_mod_time', 'created_time')
|
||||
|
||||
|
||||
class CategoryAdmin(admin.ModelAdmin):
|
||||
exclude = ('slug',)
|
||||
exclude = ('slug', 'last_mod_time', 'created_time')
|
||||
|
||||
|
||||
class LinksAdmin(admin.ModelAdmin):
|
||||
exclude = ('last_mod_time', 'created_time')
|
||||
|
||||
|
||||
class SideBarAdmin(admin.ModelAdmin):
|
||||
list_display = ('name', 'content', 'is_enable', 'sequence')
|
||||
exclude = ('last_mod_time', 'created_time')
|
||||
|
||||
|
||||
admin.site.register(Article, ArticlelAdmin)
|
||||
# admin.site.register(BlogPage, ArticlelAdmin)
|
||||
admin.site.register(Category, CategoryAdmin)
|
||||
admin.site.register(Tag, TagAdmin)
|
||||
admin.site.register(Links)
|
||||
admin.site.register(Links, LinksAdmin)
|
||||
admin.site.register(SideBar, SideBarAdmin)
|
||||
|
||||
@@ -25,25 +25,28 @@ class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **options):
|
||||
user = \
|
||||
get_user_model().objects.get_or_create(email='test@test.com', username='testuser',
|
||||
get_user_model().objects.get_or_create(email='test@test.com', username='测试用户',
|
||||
password='test!q@w#eTYU')[0]
|
||||
|
||||
pcategory = Category.objects.get_or_create(name='pcategory', parent_category=None)[0]
|
||||
pcategory = Category.objects.get_or_create(name='我是父类目', parent_category=None)[0]
|
||||
|
||||
category = Category.objects.get_or_create(name='category', parent_category=pcategory)[0]
|
||||
category = Category.objects.get_or_create(name='子类目', parent_category=pcategory)[0]
|
||||
|
||||
category.save()
|
||||
|
||||
for i in range(1, 10):
|
||||
basetag = Tag()
|
||||
basetag.name = "标签"
|
||||
basetag.save()
|
||||
for i in range(1, 20):
|
||||
article = Article.objects.get_or_create(category=category,
|
||||
title='nice title ' + str(i),
|
||||
body='nice content ' + str(i),
|
||||
author=user
|
||||
)[0]
|
||||
tag = Tag()
|
||||
tag.name = "nicetag" + str(i)
|
||||
tag.name = "标签" + str(i)
|
||||
tag.save()
|
||||
article.tags.add(tag)
|
||||
article.tags.add(basetag)
|
||||
article.save()
|
||||
|
||||
from DjangoBlog.utils import cache
|
||||
|
||||
+30
-39
@@ -2,31 +2,28 @@ from django.db import models
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.conf import settings
|
||||
from uuslug import slugify
|
||||
from DjangoBlog.spider_notify import SpiderNotify
|
||||
|
||||
from django.contrib.sites.models import Site
|
||||
from DjangoBlog.utils import cache_decorator, logger, cache
|
||||
from django.utils.functional import cached_property
|
||||
from django.utils.timezone import now
|
||||
|
||||
|
||||
class BaseModel(models.Model):
|
||||
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
|
||||
created_time = models.DateTimeField('创建时间', default=now)
|
||||
last_mod_time = models.DateTimeField('修改时间', default=now)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
from DjangoBlog.blog_signals import article_save_signal
|
||||
if not self.slug or self.slug == 'no-slug' or not self.id:
|
||||
# Only set the slug when the object is created.
|
||||
slug = self.title if 'title' in self.__dict__ else self.name
|
||||
self.slug = slugify(slug)
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
if 'update_fields' in kwargs and len(kwargs['update_fields']) == 1 and kwargs['update_fields'][0] == 'views':
|
||||
return
|
||||
try:
|
||||
if not settings.TESTING:
|
||||
notify_url = self.get_full_url()
|
||||
SpiderNotify.baidu_notify([notify_url])
|
||||
except Exception as ex:
|
||||
logger.error("notify sipder", ex)
|
||||
print(ex)
|
||||
# type = self.__class__.__name__
|
||||
is_update_views = 'update_fields' in kwargs and len(kwargs['update_fields']) == 1 and kwargs['update_fields'][
|
||||
0] == 'views'
|
||||
article_save_signal.send(sender=self.__class__, is_update_views=is_update_views, id=self.id)
|
||||
|
||||
def get_full_url(self):
|
||||
site = Site.objects.get_current().domain
|
||||
@@ -53,8 +50,6 @@ class Article(BaseModel):
|
||||
)
|
||||
title = models.CharField('标题', max_length=200, unique=True)
|
||||
body = models.TextField('正文')
|
||||
created_time = models.DateTimeField('创建时间', auto_now_add=True)
|
||||
last_mod_time = models.DateTimeField('修改时间', auto_now=True)
|
||||
pub_time = models.DateTimeField('发布时间', blank=True, null=True)
|
||||
status = models.CharField('文章状态', max_length=1, choices=STATUS_CHOICES, default='p')
|
||||
comment_status = models.CharField('评论状态', max_length=1, choices=COMMENT_STATUS, default='o')
|
||||
@@ -82,17 +77,6 @@ class Article(BaseModel):
|
||||
'day': self.created_time.day
|
||||
})
|
||||
|
||||
# todo remove
|
||||
"""
|
||||
return reverse('blog:detail', kwargs={
|
||||
'article_id': self.id,
|
||||
'year': self.created_time.year,
|
||||
'month': self.created_time.month,
|
||||
'day': self.created_time.day,
|
||||
'slug': self.slug
|
||||
})
|
||||
"""
|
||||
|
||||
@cache_decorator(60 * 60 * 10)
|
||||
def get_category_tree(self):
|
||||
tree = self.category.get_category_tree()
|
||||
@@ -101,17 +85,10 @@ class Article(BaseModel):
|
||||
return names
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# self.summary = self.summary or self.body[:settings.ARTICLE_SUB_LENGTH]
|
||||
if not self.slug or self.slug == 'no-slug' or not self.id:
|
||||
# Only set the slug when the object is created.
|
||||
self.slug = slugify(self.title)
|
||||
"""
|
||||
try:
|
||||
notify = sipder_notify()
|
||||
notify.notify(self.get_full_url())
|
||||
except Exception as e:
|
||||
print(e)
|
||||
"""
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def viewed(self):
|
||||
@@ -148,8 +125,6 @@ class Article(BaseModel):
|
||||
class Category(BaseModel):
|
||||
"""文章分类"""
|
||||
name = models.CharField('分类名', max_length=30, unique=True)
|
||||
created_time = models.DateTimeField('创建时间', auto_now_add=True)
|
||||
last_mod_time = models.DateTimeField('修改时间', auto_now=True)
|
||||
parent_category = models.ForeignKey('self', verbose_name="父级分类", blank=True, null=True)
|
||||
|
||||
class Meta:
|
||||
@@ -204,8 +179,6 @@ class Category(BaseModel):
|
||||
class Tag(BaseModel):
|
||||
"""文章标签"""
|
||||
name = models.CharField('标签名', max_length=30, unique=True)
|
||||
created_time = models.DateTimeField('创建时间', auto_now_add=True)
|
||||
last_mod_time = models.DateTimeField('修改时间', auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
@@ -228,8 +201,8 @@ class Links(models.Model):
|
||||
name = models.CharField('链接名称', max_length=30, unique=True)
|
||||
link = models.URLField('链接地址')
|
||||
sequence = models.IntegerField('排序', unique=True)
|
||||
created_time = models.DateTimeField('创建时间', auto_now_add=True)
|
||||
last_mod_time = models.DateTimeField('修改时间', auto_now=True)
|
||||
created_time = models.DateTimeField('创建时间', default=now)
|
||||
last_mod_time = models.DateTimeField('修改时间', default=now)
|
||||
|
||||
class Meta:
|
||||
ordering = ['sequence']
|
||||
@@ -238,3 +211,21 @@ class Links(models.Model):
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class SideBar(models.Model):
|
||||
"""侧边栏,可以展示一些html内容"""
|
||||
name = models.CharField('标题', max_length=100)
|
||||
content = models.TextField("内容")
|
||||
sequence = models.IntegerField('排序', unique=True)
|
||||
is_enable = models.BooleanField('是否启用', default=True)
|
||||
created_time = models.DateTimeField('创建时间', default=now)
|
||||
last_mod_time = models.DateTimeField('修改时间', default=now)
|
||||
|
||||
class Meta:
|
||||
ordering = ['sequence']
|
||||
verbose_name = '侧边栏'
|
||||
verbose_name_plural = verbose_name
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
@@ -19,13 +19,15 @@ from django.template.defaultfilters import stringfilter
|
||||
from django.utils.safestring import mark_safe
|
||||
import random
|
||||
from django.core.urlresolvers import reverse
|
||||
from blog.models import Article, Category, Tag, Links
|
||||
from blog.models import Article, Category, Tag, Links, SideBar
|
||||
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, logger
|
||||
from DjangoBlog.utils import cache
|
||||
from django.contrib.auth import get_user_model
|
||||
from oauth.models import OAuthUser
|
||||
|
||||
register = template.Library()
|
||||
|
||||
@@ -123,6 +125,7 @@ def load_sidebar(user):
|
||||
logger.info('load sidebar')
|
||||
recent_articles = Article.objects.filter(status='p')[:settings.SIDEBAR_ARTICLE_COUNT]
|
||||
sidebar_categorys = Category.objects.all()
|
||||
extra_sidebars = SideBar.objects.filter(is_enable=True).order_by('sequence')
|
||||
most_read_articles = Article.objects.filter(status='p').order_by('-views')[:settings.SIDEBAR_ARTICLE_COUNT]
|
||||
dates = Article.objects.datetimes('created_time', 'month', order='DESC')
|
||||
links = Links.objects.all()
|
||||
@@ -133,10 +136,10 @@ def load_sidebar(user):
|
||||
increment = 5
|
||||
tags = Tag.objects.all()
|
||||
sidebar_tags = None
|
||||
if tags:
|
||||
if tags and len(tags) > 0:
|
||||
s = list(map(lambda t: (t, t.get_article_count()), tags))
|
||||
count = sum(map(lambda t: t[1], s))
|
||||
dd = count / len(tags)
|
||||
dd = 1 if count == 0 else count / len(tags)
|
||||
sidebar_tags = list(map(lambda x: (x[0], x[1], (x[1] / dd) * increment + 10), s))
|
||||
|
||||
return {
|
||||
@@ -148,7 +151,8 @@ def load_sidebar(user):
|
||||
'sidebar_comments': commment_list,
|
||||
'user': user,
|
||||
'show_adsense': show_adsense,
|
||||
'sidebar_tags': sidebar_tags
|
||||
'sidebar_tags': sidebar_tags,
|
||||
'extra_sidebars': extra_sidebars
|
||||
}
|
||||
|
||||
|
||||
@@ -177,12 +181,13 @@ def load_pagination_info(page_obj, page_type, tag_name):
|
||||
previous_number = page_obj.previous_page_number()
|
||||
previous_url = reverse('blog:index_page', kwargs={'page': previous_number})
|
||||
if page_type == '分类标签归档':
|
||||
tag = get_object_or_404(Tag, name=tag_name)
|
||||
if page_obj.has_next():
|
||||
next_number = page_obj.next_page_number()
|
||||
next_url = reverse('blog:tag_detail_page', kwargs={'page': next_number, 'tag_name': tag_name})
|
||||
next_url = reverse('blog:tag_detail_page', kwargs={'page': next_number, 'tag_name': tag.slug})
|
||||
if page_obj.has_previous():
|
||||
previous_number = page_obj.previous_page_number()
|
||||
previous_url = reverse('blog:tag_detail_page', kwargs={'page': previous_number, 'tag_name': tag_name})
|
||||
previous_url = reverse('blog:tag_detail_page', kwargs={'page': previous_number, 'tag_name': tag.slug})
|
||||
if page_type == '作者文章归档':
|
||||
if page_obj.has_next():
|
||||
next_number = page_obj.next_page_number()
|
||||
@@ -192,13 +197,15 @@ def load_pagination_info(page_obj, page_type, tag_name):
|
||||
previous_url = reverse('blog:author_detail_page', kwargs={'page': previous_number, 'author_name': tag_name})
|
||||
|
||||
if page_type == '分类目录归档':
|
||||
category = get_object_or_404(Category, name=tag_name)
|
||||
if page_obj.has_next():
|
||||
next_number = page_obj.next_page_number()
|
||||
next_url = reverse('blog:category_detail_page', kwargs={'page': next_number, 'category_name': tag_name})
|
||||
next_url = reverse('blog:category_detail_page',
|
||||
kwargs={'page': next_number, 'category_name': category.slug})
|
||||
if page_obj.has_previous():
|
||||
previous_number = page_obj.previous_page_number()
|
||||
previous_url = reverse('blog:category_detail_page',
|
||||
kwargs={'page': previous_number, 'category_name': tag_name})
|
||||
kwargs={'page': previous_number, 'category_name': category.slug})
|
||||
|
||||
return {
|
||||
'previous_url': previous_url,
|
||||
@@ -237,6 +244,11 @@ def load_article_detail(article, isindex, user):
|
||||
@register.filter
|
||||
def gravatar_url(email, size=40):
|
||||
"""获得gravatar头像"""
|
||||
usermodels = OAuthUser.objects.filter(email=email)
|
||||
if usermodels:
|
||||
o = list(filter(lambda x: x.picture is not None, usermodels))
|
||||
if o:
|
||||
return o[0].picture
|
||||
email = email.encode('utf-8')
|
||||
|
||||
default = "https://resource.lylinux.net/image/2017/03/26/120117.jpg".encode('utf-8')
|
||||
|
||||
+92
-2
@@ -1,9 +1,15 @@
|
||||
from django.test import Client, RequestFactory, TestCase
|
||||
from blog.models import Article, Category, Tag
|
||||
from blog.models import Article, Category, Tag, SideBar
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.sites.models import Site
|
||||
from blog.forms import BlogSearchForm
|
||||
from django.core.paginator import Paginator
|
||||
from blog.templatetags.blog_tags import load_pagination_info, load_articletags
|
||||
import datetime
|
||||
from accounts.models import BlogUser
|
||||
from django.core.files.uploadedfile import SimpleUploadedFile
|
||||
from django.conf import settings
|
||||
import os
|
||||
|
||||
|
||||
# Create your tests here.
|
||||
@@ -17,10 +23,19 @@ class ArticleTest(TestCase):
|
||||
site = Site.objects.get_current().domain
|
||||
user = BlogUser.objects.get_or_create(email="liangliangyy@gmail.com", username="liangliangyy")[0]
|
||||
user.set_password("liangliangyy")
|
||||
user.is_staff = True
|
||||
user.is_superuser = True
|
||||
user.save()
|
||||
response = self.client.get(user.get_absolute_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
s = SideBar()
|
||||
s.sequence = 1
|
||||
s.name = 'test'
|
||||
s.content = 'test content'
|
||||
s.is_enable = True
|
||||
s.save()
|
||||
|
||||
category = Category()
|
||||
category.name = "category"
|
||||
category.created_time = datetime.datetime.now()
|
||||
@@ -45,17 +60,69 @@ class ArticleTest(TestCase):
|
||||
article.save()
|
||||
self.assertEqual(1, article.tags.count())
|
||||
|
||||
for i in range(20):
|
||||
article = Article()
|
||||
article.title = "nicetitle" + str(i)
|
||||
article.body = "nicetitle" + str(i)
|
||||
article.author = user
|
||||
article.category = category
|
||||
article.type = 'a'
|
||||
article.status = 'p'
|
||||
article.save()
|
||||
article.tags.add(tag)
|
||||
article.save()
|
||||
response = self.client.get(article.get_absolute_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
from DjangoBlog.spider_notify import SpiderNotify
|
||||
SpiderNotify.notify(article.get_absolute_url())
|
||||
response = self.client.get(tag.get_absolute_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.get(category.get_absolute_url())
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
response = self.client.get('/search', {'q': 'django'})
|
||||
self.assertEqual(response.status_code, 200)
|
||||
s = load_articletags(article)
|
||||
self.assertIsNotNone(s)
|
||||
|
||||
p = Paginator(Article.objects.all(), 2)
|
||||
self.__check_pagination__(p, '', '')
|
||||
|
||||
p = Paginator(Article.objects.filter(tags=tag), 2)
|
||||
self.__check_pagination__(p, '分类标签归档', tag.slug)
|
||||
|
||||
p = Paginator(Article.objects.filter(author__username='liangliangyy'), 2)
|
||||
self.__check_pagination__(p, '作者文章归档', 'liangliangyy')
|
||||
|
||||
p = Paginator(Article.objects.filter(category=category), 2)
|
||||
self.__check_pagination__(p, '分类目录归档', category.slug)
|
||||
|
||||
f = BlogSearchForm()
|
||||
f.search()
|
||||
self.client.login(username='liangliangyy', password='liangliangyy')
|
||||
from DjangoBlog.spider_notify import SpiderNotify
|
||||
SpiderNotify.baidu_notify([article.get_full_url()])
|
||||
rsp = self.client.get('/refresh/')
|
||||
self.assertEqual(rsp.status_code, 200)
|
||||
from blog.templatetags.blog_tags import gravatar_url, gravatar
|
||||
u = gravatar_url('liangliangyy@gmail.com')
|
||||
u = gravatar('liangliangyy@gmail.com')
|
||||
|
||||
def __check_pagination__(self, p, type, value):
|
||||
s = load_pagination_info(p.page(1), type, value)
|
||||
self.assertIsNotNone(s)
|
||||
response = self.client.get(s['previous_url'])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(s['next_url'])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
s = load_pagination_info(p.page(2), type, value)
|
||||
self.assertIsNotNone(s)
|
||||
response = self.client.get(s['previous_url'])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
response = self.client.get(s['next_url'])
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_validate_feed(self):
|
||||
user = BlogUser.objects.get_or_create(email="liangliangyy12@gmail.com", username="liangliangyy")[0]
|
||||
@@ -67,3 +134,26 @@ class ArticleTest(TestCase):
|
||||
|
||||
response = self.client.get('/sitemap.xml')
|
||||
self.assertEqual(response.status_code, 200)
|
||||
|
||||
def test_image(self):
|
||||
import requests
|
||||
rsp = requests.get('https://ss2.baidu.com/6ONYsjip0QIZ8tyhnq/it/u=2909203028,3998034658&fm=96')
|
||||
imagepath = os.path.join(settings.BASE_DIR, 'django.jpg')
|
||||
with open(imagepath, 'wb') as file:
|
||||
file.write(rsp.content)
|
||||
with open(imagepath, 'rb') as file:
|
||||
imgfile = SimpleUploadedFile('django.jpg', file.read(), content_type='image/jpg')
|
||||
form_data = {'django.jpg': imgfile}
|
||||
rsp = self.client.post('/upload', form_data, follow=True)
|
||||
|
||||
self.assertEqual(rsp.status_code, 200)
|
||||
"""
|
||||
data = SimpleUploadedFile(imagepath, b'file_content', content_type='image/jpg')
|
||||
rsp = self.client.post('/upload', {'django.jpg': data})
|
||||
self.assertEqual(rsp.status_code, 200)
|
||||
SimpleUploadedFile()
|
||||
"""
|
||||
|
||||
def test_errorpage(self):
|
||||
self.client.get('/eee')
|
||||
self.client.get('/refresh_memcache')
|
||||
|
||||
+2
-2
@@ -42,8 +42,8 @@ urlpatterns = [
|
||||
url(r'^author/(?P<author_name>\w+)/(?P<page>\d+).html$', views.AuthorDetailView.as_view(),
|
||||
name='author_detail_page'),
|
||||
|
||||
url(r'^tag/(?P<tag_name>.+).html$', views.TagDetailView.as_view(), name='tag_detail'),
|
||||
url(r'^tag/(?P<tag_name>.+)/(?P<page>\d+).html$', views.TagDetailView.as_view(), name='tag_detail_page'),
|
||||
url(r'^tag/(?P<tag_name>[\w-]+).html$', views.TagDetailView.as_view(), name='tag_detail'),
|
||||
url(r'^tag/(?P<tag_name>[\w-]+)/(?P<page>\d+).html$', views.TagDetailView.as_view(), name='tag_detail_page'),
|
||||
|
||||
url(r'^upload', views.fileupload, name='upload'),
|
||||
url(r'^refresh', views.refresh_memcache, name='refresh')
|
||||
|
||||
+19
-1
@@ -84,7 +84,7 @@ class ArticleDetailView(DetailView):
|
||||
pk_url_kwarg = 'article_id'
|
||||
context_object_name = "article"
|
||||
|
||||
def get_object(self):
|
||||
def get_object(self, queryset=None):
|
||||
obj = super(ArticleDetailView, self).get_object()
|
||||
obj.viewed()
|
||||
self.object = obj
|
||||
@@ -219,6 +219,8 @@ def fileupload(request):
|
||||
|
||||
basepath = r'/var/www/resource/{type}/{timestr}'.format(
|
||||
type='files' if not isimage else'image', timestr=timestr)
|
||||
if settings.TESTING:
|
||||
basepath = settings.BASE_DIR + '/uploads'
|
||||
url = 'https://resource.lylinux.net/{type}/{timestr}/{filename}'.format(
|
||||
type='files' if not isimage else'image', timestr=timestr, filename=filename)
|
||||
if not os.path.exists(basepath):
|
||||
@@ -252,3 +254,19 @@ def refresh_memcache(request):
|
||||
return HttpResponseForbidden()
|
||||
except Exception as e:
|
||||
return HttpResponse(e)
|
||||
|
||||
|
||||
def page_not_found_view(request):
|
||||
url = request.get_full_path()
|
||||
return render(request, 'blog/error_page.html',
|
||||
{'message': '哎呀,您访问的地址 ' + url + ' 是一个未知的地方。请点击首页看看别的?', 'statuscode': '404'})
|
||||
|
||||
|
||||
def server_error_view(request):
|
||||
return render(request, 'blog/error_page.html',
|
||||
{'message': '哎呀,出错了,我已经收集到了错误信息,之后会抓紧抢修,请点击首页看看别的?', 'statuscode': '500'})
|
||||
|
||||
|
||||
def permission_denied_view(request):
|
||||
return render(request, 'blog/error_page.html',
|
||||
{'message': '哎呀,您没有权限访问此页面,请点击首页看看别的?', 'statuscode': '403'})
|
||||
|
||||
@@ -18,28 +18,6 @@ from django.forms import ModelForm
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
"""
|
||||
class CommentForm(forms.Form):
|
||||
url = forms.URLField(label='网址', required=False)
|
||||
email = forms.EmailField(label='电子邮箱', required=False)
|
||||
name = forms.CharField(label='姓名')
|
||||
body = forms.CharField(widget=forms.Textarea, label='评论')
|
||||
parent_comment_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
|
||||
|
||||
|
||||
|
||||
class LoginCommentForm(ModelForm):
|
||||
url = forms.URLField(label='网址', required=False)
|
||||
email = forms.EmailField(label='电子邮箱', required=False, widget=forms.HiddenInput)
|
||||
name = forms.CharField(label='姓名', widget=forms.HiddenInput)
|
||||
parent_comment_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
|
||||
def __init__(self):
|
||||
pass
|
||||
class Meta:
|
||||
model = Comment
|
||||
fields = ['body']
|
||||
"""
|
||||
|
||||
|
||||
class CommentForm(ModelForm):
|
||||
url = forms.URLField(label='网址', required=False)
|
||||
@@ -49,21 +27,6 @@ class CommentForm(ModelForm):
|
||||
'aria-required': 'true'}
|
||||
))
|
||||
parent_comment_id = forms.IntegerField(widget=forms.HiddenInput, required=False)
|
||||
"""
|
||||
if get_user_model().is_authenticated:
|
||||
email = forms.EmailField(label='电子邮箱', required=False, widget=forms.HiddenInput)
|
||||
name = forms.CharField(label='姓名', widget=forms.HiddenInput)
|
||||
"""
|
||||
"""
|
||||
def __init__(self, user, *args, **kwargs):
|
||||
self.user = user
|
||||
super(CommentForm, self).__init__(*args, **kwargs)
|
||||
if self.user.is_authenticated:
|
||||
self.fields.update({
|
||||
'email': forms.CharField(widget=forms.HiddenInput()),
|
||||
'name': forms.CharField(widget=forms.HiddenInput()),
|
||||
})
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
model = Comment
|
||||
|
||||
+4
-49
@@ -1,19 +1,16 @@
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.core.mail import send_mail
|
||||
from django.core.mail import EmailMultiAlternatives
|
||||
from django.contrib.sites.models import Site
|
||||
import _thread
|
||||
from blog.models import Article
|
||||
from DjangoBlog.utils import cache
|
||||
from DjangoBlog.utils import logger
|
||||
from django.utils.timezone import now
|
||||
|
||||
|
||||
# Create your models here.
|
||||
|
||||
class Comment(models.Model):
|
||||
body = models.TextField('正文', max_length=300)
|
||||
created_time = models.DateTimeField('创建时间', auto_now_add=True)
|
||||
last_mod_time = models.DateTimeField('修改时间', auto_now=True)
|
||||
created_time = models.DateTimeField('创建时间', default=now)
|
||||
last_mod_time = models.DateTimeField('修改时间', default=now)
|
||||
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='作者', on_delete=models.CASCADE)
|
||||
article = models.ForeignKey(Article, verbose_name='文章', on_delete=models.CASCADE)
|
||||
parent_comment = models.ForeignKey('self', verbose_name="上级评论", blank=True, null=True)
|
||||
@@ -24,50 +21,8 @@ class Comment(models.Model):
|
||||
verbose_name_plural = verbose_name
|
||||
get_latest_by = 'created_time'
|
||||
|
||||
def send_comment_email(self, msg):
|
||||
try:
|
||||
msg.send()
|
||||
except:
|
||||
pass
|
||||
|
||||
def __str__(self):
|
||||
return self.body
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
super().save(*args, **kwargs)
|
||||
if not settings.DEBUG:
|
||||
subject = '感谢您发表的评论'
|
||||
site = Site.objects.get_current().domain
|
||||
article_url = "https://{site}{path}".format(site=site, path=self.article.get_absolute_url())
|
||||
html_content = """
|
||||
<p>非常感谢您在本站发表评论</p>
|
||||
您可以访问
|
||||
<a href="%s" rel="bookmark">%s</a>
|
||||
来查看您的评论,
|
||||
再次感谢您!
|
||||
<br />
|
||||
如果上面链接无法打开,请将此链接复制至浏览器。
|
||||
%s
|
||||
""" % (article_url, self.article.title, article_url)
|
||||
tomail = self.author.email
|
||||
msg = EmailMultiAlternatives(subject, html_content, from_email='no-reply@lylinux.net', to=[tomail])
|
||||
|
||||
msg.content_subtype = "html"
|
||||
|
||||
_thread.start_new_thread(self.send_comment_email, (msg,))
|
||||
|
||||
if self.parent_comment:
|
||||
html_content = """
|
||||
您在 <a href="%s" rel="bookmark">%s</a> 的评论 <br/> %s <br/> 收到回复啦.快去看看吧
|
||||
<br/>
|
||||
如果上面链接无法打开,请将此链接复制至浏览器。
|
||||
%s
|
||||
""" % (article_url, self.article.title, self.parent_comment.body, article_url)
|
||||
tomail = self.parent_comment.author.email
|
||||
msg = EmailMultiAlternatives(subject, html_content, from_email='no-reply@lylinux.net', to=[tomail])
|
||||
msg.content_subtype = "html"
|
||||
|
||||
_thread.start_new_thread(self.send_comment_email, (msg,))
|
||||
|
||||
def __str__(self):
|
||||
return self.body
|
||||
|
||||
@@ -5,6 +5,8 @@ from django.contrib.sites.models import Site
|
||||
from django.core.urlresolvers import reverse
|
||||
import datetime
|
||||
from accounts.models import BlogUser
|
||||
from comments.templatetags.comments_tags import *
|
||||
from DjangoBlog.utils import get_max_articleid_commentid
|
||||
|
||||
|
||||
# Create your tests here.
|
||||
@@ -59,3 +61,36 @@ class CommentsTest(TestCase):
|
||||
|
||||
article = Article.objects.get(pk=article.pk)
|
||||
self.assertEqual(len(article.comment_list()), 1)
|
||||
parent_comment_id = article.comment_list()[0].id
|
||||
|
||||
response = self.client.post(commenturl,
|
||||
{
|
||||
'body': '''
|
||||
# Title1
|
||||
|
||||
```python
|
||||
import os
|
||||
```
|
||||
|
||||
[url](https://www.lylinux.org/)
|
||||
|
||||
[ddd](http://www.baidu.com)
|
||||
|
||||
|
||||
''',
|
||||
'email': user.email,
|
||||
'name': user.username,
|
||||
'parent_comment_id': parent_comment_id
|
||||
})
|
||||
|
||||
self.assertEqual(response.status_code, 302)
|
||||
|
||||
article = Article.objects.get(pk=article.pk)
|
||||
self.assertEqual(len(article.comment_list()), 2)
|
||||
comment = Comment.objects.get(id=parent_comment_id)
|
||||
tree = parse_commenttree(article.comment_list(), comment)
|
||||
self.assertEqual(len(tree), 1)
|
||||
data = show_comment_item(comment, True)
|
||||
self.assertIsNotNone(data)
|
||||
s = get_max_articleid_commentid()
|
||||
self.assertIsNotNone(s)
|
||||
|
||||
+3
-14
@@ -62,25 +62,14 @@ class CommentPostView(FormView):
|
||||
comment.parent_comment = parent_comment
|
||||
|
||||
comment.save(True)
|
||||
from DjangoBlog.utils import expire_view_cache, cache
|
||||
from django.contrib.sites.models import Site
|
||||
path = article.get_absolute_url()
|
||||
site = Site.objects.get_current().domain
|
||||
if site.find(':') > 0:
|
||||
site = site[0:site.find(':')]
|
||||
|
||||
from DjangoBlog.blog_signals import comment_save_signal
|
||||
port = 80
|
||||
try:
|
||||
# django1.8 没有这个方法...
|
||||
port = self.request.get_port()
|
||||
except:
|
||||
pass
|
||||
expire_view_cache(path, servername=site, serverport=port, key_prefix='blogdetail')
|
||||
if cache.get('seo_processor'):
|
||||
cache.delete('seo_processor')
|
||||
comment_cache_key = 'article_comments_{id}'.format(id=article_id)
|
||||
cache.delete(comment_cache_key)
|
||||
from django.core.cache.utils import make_template_fragment_key
|
||||
username = self.request.user.username if self.request.user else ''
|
||||
key = make_template_fragment_key('sidebar', [username])
|
||||
cache.delete(key)
|
||||
comment_save_signal.send(sender=self.__class__, comment_id=comment.id, username=username, serverport=port)
|
||||
return HttpResponseRedirect("%s#div-comment-%d" % (article.get_absolute_url(), comment.pk))
|
||||
|
||||
@@ -11,12 +11,3 @@ class OAuthUserAdmin(admin.ModelAdmin):
|
||||
|
||||
|
||||
admin.site.register(OAuthUser, OAuthUserAdmin)
|
||||
"""
|
||||
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='用户', blank=True, null=True)
|
||||
openid = models.CharField(max_length=50)
|
||||
nikename = models.CharField(max_length=50, verbose_name='昵称')
|
||||
token = models.CharField(max_length=150)
|
||||
picture = models.CharField(max_length=350, blank=True, null=True)
|
||||
type = models.CharField(blank=False, null=False, max_length=50)
|
||||
email = models.CharField(max_length=50, null=True, blank=True)
|
||||
"""
|
||||
|
||||
+5
-2
@@ -2,17 +2,20 @@ from django.db import models
|
||||
|
||||
# Create your models here.
|
||||
from django.conf import settings
|
||||
from django.utils.timezone import now
|
||||
|
||||
|
||||
class OAuthUser(models.Model):
|
||||
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='用户', blank=True, null=True)
|
||||
openid = models.CharField(max_length=50)
|
||||
nikename = models.CharField(max_length=50, verbose_name='昵称')
|
||||
token = models.CharField(max_length=150)
|
||||
token = models.CharField(max_length=150, null=True, blank=True)
|
||||
picture = models.CharField(max_length=350, blank=True, null=True)
|
||||
type = models.CharField(blank=False, null=False, max_length=50)
|
||||
email = models.CharField(max_length=50, null=True, blank=True)
|
||||
token = models.CharField(max_length=50, null=True, blank=True)
|
||||
matedata = models.CharField(max_length=2000, null=True, blank=True)
|
||||
created_time = models.DateTimeField('创建时间', default=now)
|
||||
last_mod_time = models.DateTimeField('修改时间', default=now)
|
||||
|
||||
def __str__(self):
|
||||
return self.nikename
|
||||
|
||||
+74
-7
@@ -45,7 +45,7 @@ class BaseOauthManager(metaclass=ABCMeta):
|
||||
return self.is_access_token_set and self.access_token is not None and self.openid is not None
|
||||
|
||||
@abstractmethod
|
||||
def get_authorization_url(self):
|
||||
def get_authorization_url(self, nexturl='/'):
|
||||
pass
|
||||
|
||||
@abstractmethod
|
||||
@@ -77,11 +77,11 @@ class WBOauthManager(BaseOauthManager):
|
||||
self.callback_url = settings.OAHUTH['sina']['callbackurl']
|
||||
super(WBOauthManager, self).__init__(access_token=access_token, openid=openid)
|
||||
|
||||
def get_authorization_url(self):
|
||||
def get_authorization_url(self, nexturl='/'):
|
||||
params = {
|
||||
'client_id': self.client_id,
|
||||
'response_type': 'code',
|
||||
'redirect_uri': self.callback_url
|
||||
'redirect_uri': self.callback_url + '&next_url=' + nexturl
|
||||
}
|
||||
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
||||
return url
|
||||
@@ -96,7 +96,6 @@ class WBOauthManager(BaseOauthManager):
|
||||
'redirect_uri': self.callback_url
|
||||
}
|
||||
rsp = self.do_post(self.TOKEN_URL, params)
|
||||
print(rsp)
|
||||
try:
|
||||
obj = json.loads(rsp)
|
||||
self.access_token = str(obj['access_token'])
|
||||
@@ -117,6 +116,7 @@ class WBOauthManager(BaseOauthManager):
|
||||
|
||||
datas = json.loads(rsp)
|
||||
user = OAuthUser()
|
||||
user.matedata = rsp
|
||||
user.picture = datas['avatar_large']
|
||||
user.nikename = datas['screen_name']
|
||||
user.openid = datas['id']
|
||||
@@ -142,7 +142,7 @@ class GoogleOauthManager(BaseOauthManager):
|
||||
self.callback_url = settings.OAHUTH['google']['callbackurl']
|
||||
super(GoogleOauthManager, self).__init__(access_token=access_token, openid=openid)
|
||||
|
||||
def get_authorization_url(self):
|
||||
def get_authorization_url(self, nexturl='/'):
|
||||
params = {
|
||||
'client_id': self.client_id,
|
||||
'response_type': 'code',
|
||||
@@ -185,6 +185,7 @@ class GoogleOauthManager(BaseOauthManager):
|
||||
|
||||
datas = json.loads(rsp)
|
||||
user = OAuthUser()
|
||||
user.matedata = rsp
|
||||
user.picture = datas['picture']
|
||||
user.nikename = datas['name']
|
||||
user.openid = datas['sub']
|
||||
@@ -210,11 +211,11 @@ class GitHubOauthManager(BaseOauthManager):
|
||||
self.callback_url = settings.OAHUTH['github']['callbackurl']
|
||||
super(GitHubOauthManager, self).__init__(access_token=access_token, openid=openid)
|
||||
|
||||
def get_authorization_url(self):
|
||||
def get_authorization_url(self, nexturl='/'):
|
||||
params = {
|
||||
'client_id': self.client_id,
|
||||
'response_type': 'code',
|
||||
'redirect_uri': self.callback_url,
|
||||
'redirect_uri': self.callback_url + '&next_url=' + nexturl,
|
||||
'scope': 'user'
|
||||
}
|
||||
# url = self.AUTH_URL + "?" + urllib.parse.urlencode(params, quote_via=urllib.parse.quote)
|
||||
@@ -255,6 +256,7 @@ class GitHubOauthManager(BaseOauthManager):
|
||||
user.openid = datas['id']
|
||||
user.type = 'github'
|
||||
user.token = self.access_token
|
||||
user.matedata = rsp
|
||||
if datas['email']:
|
||||
user.email = datas['email']
|
||||
|
||||
@@ -264,6 +266,71 @@ class GitHubOauthManager(BaseOauthManager):
|
||||
return None
|
||||
|
||||
|
||||
class FaceBookOauthManager(BaseOauthManager):
|
||||
AUTH_URL = 'https://www.facebook.com/v2.10/dialog/oauth'
|
||||
TOKEN_URL = 'https://graph.facebook.com/v2.10/oauth/access_token'
|
||||
API_URL = 'https://graph.facebook.com/me'
|
||||
ICON_NAME = 'facebook'
|
||||
|
||||
def __init__(self, access_token=None, openid=None):
|
||||
self.client_id = settings.OAHUTH['facebook']['appkey']
|
||||
self.client_secret = settings.OAHUTH['facebook']['appsecret']
|
||||
self.callback_url = settings.OAHUTH['facebook']['callbackurl']
|
||||
super(FaceBookOauthManager, self).__init__(access_token=access_token, openid=openid)
|
||||
|
||||
def get_authorization_url(self, nexturl='/'):
|
||||
params = {
|
||||
'client_id': self.client_id,
|
||||
'response_type': 'code',
|
||||
'redirect_uri': self.callback_url, # + '&next_url=' + nexturl,
|
||||
'scope': 'email,public_profile'
|
||||
}
|
||||
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
|
||||
return url
|
||||
|
||||
def get_access_token_by_code(self, code):
|
||||
params = {
|
||||
'client_id': self.client_id,
|
||||
'client_secret': self.client_secret,
|
||||
# 'grant_type': 'authorization_code',
|
||||
'code': code,
|
||||
|
||||
'redirect_uri': self.callback_url
|
||||
}
|
||||
rsp = self.do_post(self.TOKEN_URL, params)
|
||||
|
||||
try:
|
||||
obj = json.loads(rsp)
|
||||
token = str(obj['access_token'])
|
||||
self.access_token = token
|
||||
return self.access_token
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_oauth_userinfo(self):
|
||||
params = {
|
||||
'access_token': self.access_token,
|
||||
'fields': 'id,name,picture,email'
|
||||
}
|
||||
try:
|
||||
rsp = self.do_get(self.API_URL, params)
|
||||
datas = json.loads(rsp)
|
||||
user = OAuthUser()
|
||||
user.nikename = datas['name']
|
||||
user.openid = datas['id']
|
||||
user.type = 'facebook'
|
||||
user.token = self.access_token
|
||||
user.matedata = rsp
|
||||
if datas['email']:
|
||||
user.email = datas['email']
|
||||
if datas['picture'] and datas['picture']['data'] and datas['picture']['data']['url']:
|
||||
user.picture = str(datas['picture']['data']['url'])
|
||||
return user
|
||||
except Exception as e:
|
||||
logger.warn(e)
|
||||
return None
|
||||
|
||||
|
||||
def get_oauth_apps():
|
||||
applications = BaseOauthManager.__subclasses__()
|
||||
return list(map(lambda x: x(), applications))
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
@time: 2017/3/4 下午3:22
|
||||
"""
|
||||
from oauth.oauthmanager import get_oauth_apps
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
|
||||
@@ -21,9 +21,14 @@ register = template.Library()
|
||||
|
||||
|
||||
@register.inclusion_tag('oauth/oauth_applications.html')
|
||||
def load_oauth_applications():
|
||||
def load_oauth_applications(request):
|
||||
applications = get_oauth_apps()
|
||||
apps = list(map(lambda x: (x.ICON_NAME, x.get_authorization_url()), applications))
|
||||
baseurl = reverse('oauth:oauthlogin')
|
||||
path = request.get_full_path()
|
||||
|
||||
apps = list(map(lambda x: (x.ICON_NAME,
|
||||
'{baseurl}?type={type}&next_url={next}'
|
||||
.format(baseurl=baseurl, type=x.ICON_NAME, next=path)), applications))
|
||||
return {
|
||||
'apps': apps
|
||||
}
|
||||
|
||||
+1
-10
@@ -22,14 +22,5 @@ urlpatterns = [
|
||||
url(r'^oauth/requireemail/(?P<oauthid>\d+).html', views.RequireEmailView.as_view(), name='require_email'),
|
||||
url(r'^oauth/emailconfirm/(?P<id>\d+)/(?P<sign>\S+).html', views.emailconfirm, name='email_confirm'),
|
||||
url(r'^oauth/bindsuccess/(?P<oauthid>\d+).html', views.bindsuccess, name='bindsuccess'),
|
||||
url(r'^oauth/oauthlogin$', views.oauthlogin,name='oauthlogin')
|
||||
]
|
||||
|
||||
"""
|
||||
urlpatterns = [
|
||||
url(r'^oauth/wbauthorize/(?P<sitename>\w+)$', views.wbauthorize),
|
||||
url(r'^oauth/wboauthurl$', views.wboauthurl),
|
||||
# url(r'^oauth/wbauthorize/(?P<sitename>\w+)$', views.wbauthorize),
|
||||
url(r'^oauth/googleoauthurl', views.googleoauthurl),
|
||||
url(r'^oauth/googleauthorize', views.googleauthorize),
|
||||
]
|
||||
"""
|
||||
|
||||
+28
-14
@@ -18,6 +18,20 @@ from django.http import HttpResponseForbidden
|
||||
from .oauthmanager import get_manager_by_type
|
||||
|
||||
|
||||
def oauthlogin(request):
|
||||
type = request.GET.get('type', None)
|
||||
if not type:
|
||||
return HttpResponseRedirect('/')
|
||||
manager = get_manager_by_type(type)
|
||||
if not manager:
|
||||
return HttpResponseRedirect('/')
|
||||
nexturl = request.GET.get('next_url', None)
|
||||
if not nexturl or nexturl == '/login/':
|
||||
nexturl = '/'
|
||||
authorizeurl = manager.get_authorization_url(nexturl)
|
||||
return HttpResponseRedirect(authorizeurl)
|
||||
|
||||
|
||||
def authorize(request):
|
||||
manager = None
|
||||
type = request.GET.get('type', None)
|
||||
@@ -28,14 +42,17 @@ def authorize(request):
|
||||
return HttpResponseRedirect('/')
|
||||
code = request.GET.get('code', None)
|
||||
rsp = manager.get_access_token_by_code(code)
|
||||
nexturl = request.GET.get('next_url', None)
|
||||
if not nexturl:
|
||||
nexturl = '/'
|
||||
if not rsp:
|
||||
return HttpResponseRedirect(manager.get_authorization_url())
|
||||
return HttpResponseRedirect(manager.get_authorization_url(nexturl))
|
||||
user = manager.get_oauth_userinfo()
|
||||
|
||||
if user:
|
||||
if not user.nikename:
|
||||
import datetime
|
||||
user.nikename = "djangoblog" + datetime.datetime.now().strftime('%Y%m%f%H%m%s')
|
||||
user.nikename = "djangoblog" + datetime.datetime.now().strftime('%y%m%d%I%M%S')
|
||||
try:
|
||||
token = user.token
|
||||
user = OAuthUser.objects.get(type=type, openid=user.openid)
|
||||
@@ -54,27 +71,22 @@ def authorize(request):
|
||||
result = get_user_model().objects.get_or_create(email=user.email)
|
||||
author = result[0]
|
||||
if result[1]:
|
||||
author.username = user.nikename + '_' + str(user.openid)
|
||||
author.username = user.nikename
|
||||
author.save()
|
||||
|
||||
user.author = author
|
||||
user.save()
|
||||
login(request, author)
|
||||
return HttpResponseRedirect('/')
|
||||
return HttpResponseRedirect(nexturl)
|
||||
if not email:
|
||||
# todo
|
||||
# 未避免用户名重复,暂时使用oauth用户名+openid这种方式来创建用户
|
||||
# author = get_user_model().objects.get_or_create(username=user.nikename + '_' + str(user.openid))[0]
|
||||
# user.author = author
|
||||
user.save()
|
||||
|
||||
url = reverse('oauth:require_email', kwargs={
|
||||
'oauthid': user.id
|
||||
})
|
||||
|
||||
return HttpResponseRedirect(url)
|
||||
else:
|
||||
return HttpResponseRedirect('/')
|
||||
return HttpResponseRedirect(nexturl)
|
||||
|
||||
|
||||
def emailconfirm(request, id, sign):
|
||||
@@ -90,7 +102,7 @@ def emailconfirm(request, id, sign):
|
||||
result = get_user_model().objects.get_or_create(email=oauthuser.email)
|
||||
author = result[0]
|
||||
if result[1]:
|
||||
author.username = oauthuser.nikename + '_' + str(oauthuser.openid)
|
||||
author.username = oauthuser.nikename
|
||||
author.save()
|
||||
"""
|
||||
if oauthuser.email and author.email:
|
||||
@@ -102,7 +114,7 @@ def emailconfirm(request, id, sign):
|
||||
login(request, author)
|
||||
|
||||
site = Site.objects.get_current().domain
|
||||
send_email('恭喜您绑定成功!', '''
|
||||
content = '''
|
||||
<p>恭喜您,您已经成功绑定您的邮箱,您可以使用{type}来直接免密码登录本网站.欢迎您继续关注本站,地址是</p>
|
||||
|
||||
<a href="{url}" rel="bookmark">{url}</a>
|
||||
@@ -111,7 +123,9 @@ def emailconfirm(request, id, sign):
|
||||
<br />
|
||||
如果上面链接无法打开,请将此链接复制至浏览器。
|
||||
{url}
|
||||
'''.format(type=oauthuser.type, url='http://' + site), [oauthuser.email, ])
|
||||
'''.format(type=oauthuser.type, url='http://' + site)
|
||||
|
||||
send_email(emailto=[oauthuser.email, ], title='恭喜您绑定成功!', content=content)
|
||||
url = reverse('oauth:bindsuccess', kwargs={
|
||||
'oauthid': id
|
||||
})
|
||||
@@ -172,7 +186,7 @@ class RequireEmailView(FormView):
|
||||
如果上面链接无法打开,请将此链接复制至浏览器。
|
||||
{url}
|
||||
""".format(url=url)
|
||||
send_email('绑定您的电子邮箱', content, [email, ])
|
||||
send_email(emailto=[email, ], title='绑定您的电子邮箱', content=content)
|
||||
url = reverse('oauth:bindsuccess', kwargs={
|
||||
'oauthid': oauthid
|
||||
})
|
||||
|
||||
+16
-12
@@ -1,30 +1,34 @@
|
||||
appdirs==1.4.3
|
||||
Django==1.11.2
|
||||
coverage==4.4.1
|
||||
Django==1.11.7
|
||||
django-appconf==1.0.2
|
||||
django-autoslug==1.9.3
|
||||
django-compressor==2.1.1
|
||||
django-debug-toolbar==1.8
|
||||
django-compressor==2.2
|
||||
django-debug-toolbar==1.9.1
|
||||
django-haystack==2.6.1
|
||||
django-ipware==1.1.6
|
||||
django-pagedown==0.1.3
|
||||
django-uuslug==1.1.8
|
||||
jieba==0.38
|
||||
markdown2==2.3.4
|
||||
mistune==0.7.4
|
||||
jieba==0.39
|
||||
jsonpickle==0.9.5
|
||||
markdown2==2.3.5
|
||||
mistune==0.8.1
|
||||
olefile==0.44
|
||||
packaging==16.8
|
||||
Pillow==4.1.1
|
||||
Pillow==4.3.0
|
||||
Pygments==2.2.0
|
||||
PyMySQL==0.7.11
|
||||
pyparsing==2.2.0
|
||||
python-memcached==1.58
|
||||
python-slugify==1.2.4
|
||||
pytz==2017.2
|
||||
pytz==2017.3
|
||||
rcssmin==1.0.6
|
||||
requests==2.17.3
|
||||
requests==2.18.4
|
||||
rjsmin==1.0.12
|
||||
six==1.10.0
|
||||
sqlparse==0.2.3
|
||||
Unidecode==0.4.20
|
||||
six==1.11.0
|
||||
sqlparse==0.2.4
|
||||
Unidecode==0.4.21
|
||||
urllib3==1.22
|
||||
webencodings==0.5.1
|
||||
WeRoBot==1.1.1
|
||||
Whoosh==2.7.4
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
|
||||
"""
|
||||
@version: ??
|
||||
@author: liangliangyy
|
||||
@license: MIT Licence
|
||||
@contact: liangliangyy@gmail.com
|
||||
@site: https://www.lylinux.org/
|
||||
@software: PyCharm
|
||||
@file: __init__.py.py
|
||||
@time: 2017/8/27 上午11:40
|
||||
"""
|
||||
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
|
||||
"""
|
||||
@version: ??
|
||||
@author: liangliangyy
|
||||
@license: MIT Licence
|
||||
@contact: liangliangyy@gmail.com
|
||||
@site: https://www.lylinux.org/
|
||||
@software: PyCharm
|
||||
@file: blogapi.py
|
||||
@time: 2017/8/27 上午11:40
|
||||
"""
|
||||
from blog.models import Article, Category, Tag
|
||||
from haystack.query import EmptySearchQuerySet, SearchQuerySet
|
||||
|
||||
|
||||
class BlogApi():
|
||||
def __init__(self):
|
||||
self.searchqueryset = SearchQuerySet()
|
||||
self.searchqueryset.auto_query('')
|
||||
self.__max_takecount__ = 8
|
||||
|
||||
def search_articles(self, query):
|
||||
sqs = self.searchqueryset.auto_query(query)
|
||||
sqs = sqs.load_all()
|
||||
return sqs[:self.__max_takecount__]
|
||||
|
||||
def get_category_lists(self):
|
||||
return Category.objects.all()
|
||||
|
||||
def get_category_articles(self, categoryname):
|
||||
articles = Article.objects.filter(category__name=categoryname)
|
||||
if articles:
|
||||
return articles[:self.__max_takecount__]
|
||||
return None
|
||||
|
||||
def get_recent_articles(self):
|
||||
return Article.objects.all()[:self.__max_takecount__]
|
||||
@@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
|
||||
"""
|
||||
@version: ??
|
||||
@author: liangliangyy
|
||||
@license: MIT Licence
|
||||
@contact: liangliangyy@gmail.com
|
||||
@site: https://www.lylinux.org/
|
||||
@software: PyCharm
|
||||
@file: commonapi.py
|
||||
@time: 2017/9/2 上午1:43
|
||||
"""
|
||||
import requests
|
||||
import json
|
||||
from DjangoBlog.utils import logger
|
||||
|
||||
|
||||
class TuLing():
|
||||
def __init__(self):
|
||||
self.__key__ = '2f1446eb0321804291b0a1e217c25bb5'
|
||||
self.__appid__ = 137762
|
||||
|
||||
def __build_req_url(self, content):
|
||||
return 'http://www.tuling123.com/openapi/api?key=%s&info=%s&userid=%s' % (
|
||||
self.__key__, content, self.__appid__)
|
||||
|
||||
def UserAgent(self, url):
|
||||
rsp = requests.get(url)
|
||||
return rsp.content
|
||||
|
||||
def getdata(self, content):
|
||||
try:
|
||||
requrl = self.__build_req_url(content)
|
||||
res = self.UserAgent(requrl).decode('utf-8')
|
||||
|
||||
jsons = json.loads(res, encoding='utf-8')
|
||||
if str(jsons["code"]) == '100000':
|
||||
return jsons["text"]
|
||||
except Exception as e:
|
||||
logger.warn(e)
|
||||
return "哎呀,出错啦。"
|
||||
@@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
|
||||
"""
|
||||
@version: ??
|
||||
@author: liangliangyy
|
||||
@license: MIT Licence
|
||||
@contact: liangliangyy@gmail.com
|
||||
@site: https://www.lylinux.org/
|
||||
@software: PyCharm
|
||||
@file: MemcacheStorage.py
|
||||
@time: 2017/8/27 上午2:42
|
||||
"""
|
||||
from werobot.session import SessionStorage
|
||||
from werobot.utils import json_loads, json_dumps
|
||||
from DjangoBlog.utils import cache
|
||||
|
||||
|
||||
class MemcacheStorage(SessionStorage):
|
||||
def __init__(self, prefix='ws_'):
|
||||
self.prefix = prefix
|
||||
self.cache = cache
|
||||
|
||||
@property
|
||||
def is_available(self):
|
||||
value = "1"
|
||||
self.set('checkavaliable', value=value)
|
||||
return value == self.get('checkavaliable')
|
||||
|
||||
def key_name(self, s):
|
||||
return '{prefix}{s}'.format(prefix=self.prefix, s=s)
|
||||
|
||||
def get(self, id):
|
||||
id = self.key_name(id)
|
||||
session_json = self.cache.get(id) or '{}'
|
||||
return json_loads(session_json)
|
||||
|
||||
def set(self, id, value):
|
||||
id = self.key_name(id)
|
||||
self.cache.set(id, json_dumps(value))
|
||||
|
||||
def delete(self, id):
|
||||
id = self.key_name(id)
|
||||
self.cache.delete(id)
|
||||
@@ -0,0 +1,11 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
from .models import commands
|
||||
|
||||
|
||||
class CommandsAdmin(admin.ModelAdmin):
|
||||
list_display = ('title', 'command', 'describe')
|
||||
|
||||
|
||||
admin.site.register(commands, CommandsAdmin)
|
||||
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ServermanagerConfig(AppConfig):
|
||||
name = 'servermanager'
|
||||
@@ -0,0 +1,17 @@
|
||||
from django.db import models
|
||||
|
||||
|
||||
# Create your models here.
|
||||
class commands(models.Model):
|
||||
title = models.CharField('命令标题', max_length=300)
|
||||
command = models.CharField('命令', max_length=2000)
|
||||
describe = models.CharField('命令描述', max_length=300)
|
||||
created_time = models.DateTimeField('创建时间', auto_now_add=True)
|
||||
last_mod_time = models.DateTimeField('修改时间', auto_now=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
class Meta:
|
||||
verbose_name = '命令'
|
||||
verbose_name_plural = verbose_name
|
||||
@@ -0,0 +1,245 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
|
||||
"""
|
||||
@version: ??
|
||||
@author: liangliangyy
|
||||
@license: MIT Licence
|
||||
@contact: liangliangyy@gmail.com
|
||||
@site: https://www.lylinux.org/
|
||||
@software: PyCharm
|
||||
@file: robot.py
|
||||
@time: 2017/8/27 上午1:55
|
||||
"""
|
||||
|
||||
from werobot import WeRoBot
|
||||
import re
|
||||
from werobot.replies import ArticlesReply, MusicReply, ImageReply, Article
|
||||
from .MemcacheStorage import MemcacheStorage
|
||||
from servermanager.Api.blogapi import BlogApi
|
||||
from servermanager.Api.commonapi import TuLing
|
||||
import os
|
||||
import json
|
||||
from DjangoBlog.utils import get_md5
|
||||
from django.conf import settings
|
||||
import jsonpickle
|
||||
from servermanager.models import commands
|
||||
|
||||
robot = WeRoBot(token='lylinux', enable_session=True)
|
||||
memstorage = MemcacheStorage()
|
||||
if memstorage.is_available:
|
||||
robot.config['SESSION_STORAGE'] = memstorage
|
||||
else:
|
||||
from werobot.session.filestorage import FileStorage
|
||||
import os
|
||||
from django.conf import settings
|
||||
|
||||
if os.path.exists(os.path.join(settings.BASE_DIR, 'werobot_session')):
|
||||
os.remove(os.path.join(settings.BASE_DIR, 'werobot_session'))
|
||||
robot.config['SESSION_STORAGE'] = FileStorage(filename='werobot_session')
|
||||
blogapi = BlogApi()
|
||||
tuling = TuLing()
|
||||
|
||||
|
||||
def convert_to_articlereply(articles, message):
|
||||
reply = ArticlesReply(message=message)
|
||||
from blog.templatetags.blog_tags import custom_markdown, truncatechars_content
|
||||
from DjangoBlog.utils import CommonMarkdown
|
||||
from django.utils.safestring import mark_safe
|
||||
for post in articles:
|
||||
imgs = re.findall(r'(?:http\:|https\:)?\/\/.*\.(?:png|jpg)', post.body)
|
||||
imgurl = ''
|
||||
if imgs:
|
||||
imgurl = imgs[0]
|
||||
article = Article(
|
||||
title=post.title,
|
||||
description=truncatechars_content(post.body),
|
||||
img=imgurl,
|
||||
url=post.get_full_url()
|
||||
)
|
||||
reply.add_article(article)
|
||||
return reply
|
||||
|
||||
|
||||
@robot.filter(re.compile(r"^\?.*"))
|
||||
def search(message, session):
|
||||
s = message.content
|
||||
searchstr = str(s).replace('?', '')
|
||||
result = blogapi.search_articles(searchstr)
|
||||
if result:
|
||||
articles = list(map(lambda x: x.object, result))
|
||||
reply = convert_to_articlereply(articles, message)
|
||||
return reply
|
||||
else:
|
||||
return '没有找到相关文章。'
|
||||
|
||||
|
||||
@robot.filter(re.compile(r'^category\s*$', re.I))
|
||||
def category(message, session):
|
||||
categorys = blogapi.get_category_lists()
|
||||
content = ','.join(map(lambda x: x.name, categorys))
|
||||
return '所有文章分类目录:' + content
|
||||
|
||||
|
||||
@robot.filter(re.compile(r'^recent\s*$', re.I))
|
||||
def recents(message, session):
|
||||
articles = blogapi.get_recent_articles()
|
||||
if articles:
|
||||
reply = convert_to_articlereply(articles, message)
|
||||
return reply
|
||||
else:
|
||||
return "暂时还没有文章"
|
||||
|
||||
|
||||
@robot.filter(re.compile('^help$', re.I))
|
||||
def help(message, session):
|
||||
return '''欢迎关注!
|
||||
默认会与图灵机器人聊天~~
|
||||
你可以通过下面这些命令来获得信息
|
||||
?关键字搜索文章.
|
||||
如?python.
|
||||
category获得文章分类目录及文章数.
|
||||
category-***获得该分类目录文章
|
||||
如category-python
|
||||
recent获得最新文章
|
||||
help获得帮助.
|
||||
weather:获得天气
|
||||
如weather:西安
|
||||
idcard:获得身份证信息
|
||||
如idcard:61048119xxxxxxxxxx
|
||||
music:音乐搜索
|
||||
如music:阴天快乐
|
||||
PS:以上标点符号都不支持中文标点~~
|
||||
'''
|
||||
|
||||
|
||||
@robot.filter(re.compile('^weather\:.*$', re.I))
|
||||
def weather(message, session):
|
||||
return "建设中..."
|
||||
|
||||
|
||||
@robot.filter(re.compile('^idcard\:.*$', re.I))
|
||||
def idcard(message, session):
|
||||
return "建设中..."
|
||||
|
||||
|
||||
@robot.handler
|
||||
def echo(message, session):
|
||||
handler = MessageHandler(message, session)
|
||||
return handler.handler()
|
||||
|
||||
|
||||
class CommandHandler():
|
||||
def __init__(self):
|
||||
self.commands = commands.objects.all()
|
||||
|
||||
def run(self, title):
|
||||
cmd = list(filter(lambda x: x.title.upper() == title.upper(), self.commands))
|
||||
if cmd:
|
||||
return self.__run_command__(cmd[0].command)
|
||||
else:
|
||||
return "未找到相关命令,请输入hepme获得帮助。"
|
||||
|
||||
def __run_command__(self, cmd):
|
||||
try:
|
||||
str = os.popen(cmd).read()
|
||||
return str
|
||||
except:
|
||||
return '命令执行出错!'
|
||||
|
||||
def get_help(self):
|
||||
rsp = ''
|
||||
for cmd in self.commands:
|
||||
rsp += '{c}:{d}\n'.format(c=cmd.title, d=cmd.describe)
|
||||
return rsp
|
||||
|
||||
|
||||
cmdhandler = CommandHandler()
|
||||
|
||||
|
||||
class MessageHandler():
|
||||
def __init__(self, message, session):
|
||||
userid = message.source
|
||||
self.message = message
|
||||
self.session = session
|
||||
self.userid = userid
|
||||
try:
|
||||
info = session[userid]
|
||||
self.userinfo = jsonpickle.decode(info)
|
||||
except:
|
||||
userinfo = WxUserInfo()
|
||||
self.userinfo = userinfo
|
||||
|
||||
@property
|
||||
def is_admin(self):
|
||||
return self.userinfo.isAdmin
|
||||
|
||||
@property
|
||||
def is_password_set(self):
|
||||
return self.userinfo.isPasswordSet
|
||||
|
||||
def savesession(self):
|
||||
info = jsonpickle.encode(self.userinfo)
|
||||
self.session[self.userid] = info
|
||||
|
||||
def handler(self):
|
||||
info = self.message.content
|
||||
|
||||
if self.userinfo.isAdmin and info.upper() == 'EXIT':
|
||||
self.userinfo = WxUserInfo()
|
||||
self.savesession()
|
||||
return "退出成功"
|
||||
if info.upper() == 'ADMIN':
|
||||
self.userinfo.isAdmin = True
|
||||
self.savesession()
|
||||
return "输入管理员密码"
|
||||
if self.userinfo.isAdmin and not self.userinfo.isPasswordSet:
|
||||
passwd = settings.WXADMIN
|
||||
if settings.TESTING:
|
||||
passwd='123'
|
||||
if passwd.upper() == get_md5(get_md5(info)).upper():
|
||||
self.userinfo.isPasswordSet = True
|
||||
self.savesession()
|
||||
return "验证通过,请输入命令或者要执行的命令代码:输入helpme获得帮助"
|
||||
else:
|
||||
if self.userinfo.Count >= 3:
|
||||
self.userinfo = WxUserInfo()
|
||||
self.savesession()
|
||||
return "超过验证次数"
|
||||
self.userinfo.Count += 1
|
||||
self.savesession()
|
||||
return "验证失败,请重新输入管理员密码:"
|
||||
if self.userinfo.isAdmin and self.userinfo.isPasswordSet:
|
||||
if self.userinfo.Command != '' and info.upper() == 'Y':
|
||||
return cmdhandler.run(self.userinfo.Command)
|
||||
else:
|
||||
if info.upper() == 'HELPME':
|
||||
return cmdhandler.get_help()
|
||||
self.userinfo.Command = info
|
||||
self.savesession()
|
||||
return "确认执行: " + info + " 命令?"
|
||||
rsp = tuling.getdata(info)
|
||||
return rsp
|
||||
|
||||
|
||||
class WxUserInfo():
|
||||
def __init__(self):
|
||||
self.isAdmin = False
|
||||
self.isPasswordSet = False
|
||||
self.Count = 0
|
||||
self.Command = ''
|
||||
|
||||
|
||||
"""
|
||||
@robot.handler
|
||||
def hello(message, session):
|
||||
blogapi = BlogApi()
|
||||
result = blogapi.search_articles(message.content)
|
||||
if result:
|
||||
articles = list(map(lambda x: x.object, result))
|
||||
reply = convert_to_articlereply(articles, message)
|
||||
return reply
|
||||
else:
|
||||
return '没有找到相关文章。'
|
||||
"""
|
||||
@@ -0,0 +1,82 @@
|
||||
from django.test import Client, RequestFactory, TestCase
|
||||
from django.contrib.sites.models import Site
|
||||
from .models import commands
|
||||
import datetime
|
||||
from accounts.models import BlogUser
|
||||
from blog.models import Category, Article
|
||||
from .robot import search, category, recents
|
||||
from werobot.messages.messages import TextMessage
|
||||
from .robot import MessageHandler, CommandHandler
|
||||
from servermanager.Api.commonapi import TuLing
|
||||
|
||||
|
||||
# Create your tests here.
|
||||
class ServerManagerTest(TestCase):
|
||||
def setUp(self):
|
||||
self.client = Client()
|
||||
self.factory = RequestFactory()
|
||||
|
||||
def test_tuling(self):
|
||||
t = TuLing()
|
||||
content = t.getdata('test')
|
||||
self.assertIsNotNone(content)
|
||||
|
||||
def test_validate_comment(self):
|
||||
site = Site.objects.get_current().domain
|
||||
user = BlogUser.objects.create_superuser(email="liangliangyy1@gmail.com",
|
||||
username="liangliangyy1", password="liangliangyy1")
|
||||
|
||||
self.client.login(username='liangliangyy1', password='liangliangyy1')
|
||||
|
||||
c = Category()
|
||||
c.name = "categoryccc"
|
||||
c.created_time = datetime.datetime.now()
|
||||
c.last_mod_time = datetime.datetime.now()
|
||||
c.save()
|
||||
|
||||
article = Article()
|
||||
article.title = "nicetitleccc"
|
||||
article.body = "nicecontentccc"
|
||||
article.author = user
|
||||
article.category = c
|
||||
article.type = 'a'
|
||||
article.status = 'p'
|
||||
article.save()
|
||||
s = TextMessage([])
|
||||
s.content = "nicetitleccc"
|
||||
rsp = search(s, None)
|
||||
self.assertTrue(rsp != '没有找到相关文章。')
|
||||
rsp = category(None, None)
|
||||
self.assertIsNotNone(rsp)
|
||||
rsp = recents(None, None)
|
||||
self.assertTrue(rsp != '暂时还没有文章')
|
||||
|
||||
cmd = commands()
|
||||
cmd.title = "test"
|
||||
cmd.command = "ls"
|
||||
cmd.describe = "test"
|
||||
cmd.save()
|
||||
|
||||
cmdhandler = CommandHandler()
|
||||
rsp = cmdhandler.run('test')
|
||||
self.assertIsNotNone(rsp)
|
||||
s.source = 'u'
|
||||
s.content = 'test'
|
||||
msghandler = MessageHandler(s, {})
|
||||
|
||||
#msghandler.userinfo.isPasswordSet = True
|
||||
#msghandler.userinfo.isAdmin = True
|
||||
msghandler.handler()
|
||||
s.content = 'y'
|
||||
msghandler.handler()
|
||||
s.content='idcard:12321233'
|
||||
msghandler.handler()
|
||||
s.content='weather:上海'
|
||||
msghandler.handler()
|
||||
s.content='admin'
|
||||
msghandler.handler()
|
||||
s.content='123'
|
||||
msghandler.handler()
|
||||
|
||||
s.content = 'exit'
|
||||
msghandler.handler()
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/usr/bin/env python
|
||||
# encoding: utf-8
|
||||
|
||||
|
||||
"""
|
||||
@version: ??
|
||||
@author: liangliangyy
|
||||
@license: MIT Licence
|
||||
@contact: liangliangyy@gmail.com
|
||||
@site: https://www.lylinux.org/
|
||||
@software: PyCharm
|
||||
@file: urls.py
|
||||
@time: 2017/8/27 上午2:27
|
||||
"""
|
||||
|
||||
from django.conf.urls import url
|
||||
from werobot.contrib.django import make_view
|
||||
from .robot import robot
|
||||
|
||||
urlpatterns = [
|
||||
url(r'^robot/', make_view(robot)),
|
||||
|
||||
]
|
||||
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@@ -29,7 +29,7 @@
|
||||
</label>
|
||||
</div>
|
||||
{% load oauth_tags %}
|
||||
{% load_oauth_applications %}
|
||||
{% load_oauth_applications request%}
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
href="{% url "account:login" %}?next={{ request.get_full_path }}">登录</a>后发表评论。</h3>
|
||||
|
||||
{% load oauth_tags %}
|
||||
{% load_oauth_applications %}
|
||||
{% load_oauth_applications request%}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
{% extends 'share_layout/base.html' %}
|
||||
{% load blog_tags %}
|
||||
{% load cache %}
|
||||
{% block header %}
|
||||
{% if tag_name %}
|
||||
{% if statuscode == '404' %}
|
||||
<title>404 NotFound</title>
|
||||
{% elif statuscode == '500' %}
|
||||
<title>Permission Denied</title>
|
||||
{% elif statuscode == '403' %}
|
||||
<title>500 Error</title>
|
||||
{% else %}
|
||||
<title></title>
|
||||
{% endif %}
|
||||
{% comment %}<meta name="description" content="{{ page_type }}:{{ tag_name }}"/>{% endcomment %}
|
||||
{% else %}
|
||||
<title>{{ SITE_NAME }} | {{ SITE_DESCRIPTION }}</title>
|
||||
{% endif %}
|
||||
<meta name="description" content="{{ SITE_SEO_DESCRIPTION }}"/>
|
||||
<meta name="keywords" content="{{ SITE_KEYWORDS }}"/>
|
||||
<meta property="og:type" content="blog"/>
|
||||
<meta property="og:title" content="{{ SITE_NAME }}"/>
|
||||
<meta property="og:description" content="{{ SITE_DESCRIPTION }}"/>
|
||||
<meta property="og:url" content="{{ SITE_BASE_URL }}"/>
|
||||
<meta property="og:site_name" content="{{ SITE_NAME }}"/>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div id="primary" class="site-content">
|
||||
<div id="content" role="main">
|
||||
|
||||
<header class="archive-header">
|
||||
<h1 class="archive-title">{{ message }}</h1>
|
||||
</header><!-- .archive-header -->
|
||||
|
||||
</div><!-- #content -->
|
||||
</div><!-- #primary -->
|
||||
|
||||
{% endblock %}
|
||||
|
||||
|
||||
{% block sidebar %}
|
||||
{% load_sidebar user %}
|
||||
{% endblock %}
|
||||
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
</div>
|
||||
</form>
|
||||
</aside>
|
||||
{% if extra_sidebars %}
|
||||
{% for sidebar in extra_sidebars %}
|
||||
|
||||
<aside class="widget_text widget widget_custom_html"><h3 class="widget-title">
|
||||
{{ sidebar.name }}</h3>
|
||||
<div class="textwidget custom-html-widget">
|
||||
{{ sidebar.content }}
|
||||
</div>
|
||||
</aside>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
{% if most_read_articles %}
|
||||
|
||||
<aside id="views-4" class="widget widget_views"><h3 class="widget-title">Views</h3>
|
||||
@@ -41,8 +52,7 @@
|
||||
{% for c in sidebar_comments %}
|
||||
<li class="recentcomments">
|
||||
<span class="comment-author-link">
|
||||
<a href="{{ c.author.get_absolute_url }}" rel="external"
|
||||
class="url">{{ c.author.username }}</a></span>
|
||||
{{ c.author.username }}</span>
|
||||
发表在《
|
||||
<a href="{{ c.article.get_absolute_url }}">{{ c.article.title }}</a>》
|
||||
</li>
|
||||
@@ -109,8 +119,8 @@
|
||||
{% else %}
|
||||
<li><a href="{% url "account:login" %}">登录</a>
|
||||
{% endif %}
|
||||
|
||||
<li><a href="http://gitbook.lylinux.net" target="_blank" rel="nofollow">GitBook</a></li>
|
||||
</ul>
|
||||
</aside>
|
||||
<div id="rocket" class="show" title="点我返回顶部"></div>
|
||||
<div id="rocket" class="show" title="点我返回顶部"></div>
|
||||
</div><!-- #secondary -->
|
||||
@@ -1,12 +1,9 @@
|
||||
<script type="text/javascript">
|
||||
var cnzz_protocol = (("https:" == document.location.protocol) ? " https://" : " http://");
|
||||
document.write(unescape("%3Cspan id='cnzz_stat_icon_1261145394'%3E%3C/span%3E%3Cscript src='" + cnzz_protocol + "s4.cnzz.com/stat.php%3Fid%3D1261145394%26show%3Dpic' type='text/javascript'%3E%3C/script%3E"));
|
||||
|
||||
(function (i, s, o, g, r, a, m) {
|
||||
i['GoogleAnalyticsObject'] = r;
|
||||
i[r] = i[r] || function () {
|
||||
(i[r].q = i[r].q || []).push(arguments)
|
||||
}, i[r].l = 1 * new Date();
|
||||
(i[r].q = i[r].q || []).push(arguments)
|
||||
}, i[r].l = 1 * new Date();
|
||||
a = s.createElement(o),
|
||||
m = s.getElementsByTagName(o)[0];
|
||||
a.async = 1;
|
||||
@@ -16,5 +13,12 @@
|
||||
|
||||
ga('create', 'UA-27354903-4', 'auto');
|
||||
ga('send', 'pageview');
|
||||
|
||||
</script>
|
||||
var _hmt = _hmt || [];
|
||||
(function () {
|
||||
var hm = document.createElement("script");
|
||||
hm.src = "https://hm.baidu.com/hm.js?30dd00aec5f76607ba37c6770ae48e45";
|
||||
var s = document.getElementsByTagName("script")[0];
|
||||
s.parentNode.insertBefore(hm, s);
|
||||
})();
|
||||
</script>
|
||||
<a href="https://tongji.baidu.com/sc-web/3478620/home/ico?siteId=11261596" target="_blank" rel="nofollow">百度统计</a>
|
||||
@@ -21,7 +21,9 @@
|
||||
Powered by
|
||||
<a href="https://www.djangoproject.com/" rel="nofollow" target="blank">Django</a>
|
||||
|
|
||||
<a href="https://github.com/liangliangyy/DjangoBlog" rel="nofollow" target="blank"> LiangLiangyy</a>
|
||||
<a href="https://github.com/liangliangyy/DjangoBlog" rel="nofollow" target="blank">LiangLiangyy</a>
|
||||
|
|
||||
<a href="https://www.lylinux.net" target="blank">Lylinux</a>
|
||||
|
|
||||
本页面加载耗时:<!!LOAD_TIMES!!>s
|
||||
</div>
|
||||
|
||||
@@ -1,29 +1,33 @@
|
||||
appdirs==1.4.3
|
||||
Django==1.11.2
|
||||
coverage==4.4.1
|
||||
Django==1.11.7
|
||||
django-appconf==1.0.2
|
||||
django-autoslug==1.9.3
|
||||
django-compressor==2.1.1
|
||||
django-debug-toolbar==1.8
|
||||
django-compressor==2.2
|
||||
django-debug-toolbar==1.9.1
|
||||
django-haystack==2.6.1
|
||||
django-ipware==1.1.6
|
||||
django-pagedown==0.1.3
|
||||
django-uuslug==1.1.8
|
||||
jieba==0.38
|
||||
markdown2==2.3.4
|
||||
mistune==0.7.4
|
||||
jieba==0.39
|
||||
jsonpickle==0.9.5
|
||||
markdown2==2.3.5
|
||||
mistune==0.8.1
|
||||
olefile==0.44
|
||||
packaging==16.8
|
||||
Pillow==4.1.1
|
||||
Pillow==4.3.0
|
||||
Pygments==2.2.0
|
||||
PyMySQL==0.7.11
|
||||
pyparsing==2.2.0
|
||||
python-slugify==1.2.4
|
||||
pytz==2017.2
|
||||
pytz==2017.3
|
||||
rcssmin==1.0.6
|
||||
requests==2.17.3
|
||||
requests==2.18.4
|
||||
rjsmin==1.0.12
|
||||
six==1.10.0
|
||||
sqlparse==0.2.3
|
||||
Unidecode==0.4.20
|
||||
six==1.11.0
|
||||
sqlparse==0.2.4
|
||||
Unidecode==0.4.21
|
||||
urllib3==1.22
|
||||
webencodings==0.5.1
|
||||
WeRoBot==1.1.1
|
||||
Whoosh==2.7.4
|
||||
|
||||
@@ -47,6 +47,7 @@ INSTALLED_APPS = [
|
||||
'accounts',
|
||||
'comments',
|
||||
'oauth',
|
||||
'servermanager',
|
||||
'compressor'
|
||||
]
|
||||
|
||||
@@ -215,6 +216,11 @@ OAHUTH = {
|
||||
'appkey': 'githubappkey',
|
||||
'appsecret': 'githubappsecret',
|
||||
'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=github'
|
||||
},
|
||||
'facebook': {
|
||||
'appkey': 'facebookappkey',
|
||||
'appsecret': 'facebookappsecret',
|
||||
'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=facebook'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -235,7 +241,8 @@ DEFAULT_FROM_EMAIL = EMAIL_HOST_USER
|
||||
SERVER_EMAIL = os.environ.get('DJANGO_EMAIL_USER')
|
||||
# 设置debug=false 未处理异常邮件通知
|
||||
ADMINS = [('liangliang', 'liangliangyy@gmail.com')]
|
||||
|
||||
# 微信管理员密码(两次md5获得)
|
||||
WXADMIN = '995F03AC401D6CABABAEF756FC4D43C7'
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False,
|
||||
|
||||
Reference in New Issue
Block a user