Merge branch 'dev'

master
liangliangyy 9 years ago
commit 99be726e95

@ -19,14 +19,14 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '&3g0bdza#c%dm1lf%5gi&0-*53p3t0m*hmcvo29cn^$ji7je(c' SECRET_KEY = os.environ.get('DJANGO_SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
#DEBUG = True # DEBUG = True
DEBUG = False DEBUG = False
# ALLOWED_HOSTS = [] # ALLOWED_HOSTS = []
ALLOWED_HOSTS = ['www.lylinux.net', '127.0.0.1'] ALLOWED_HOSTS = ['www.lylinux.net', '127.0.0.1', 'example.com']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
@ -51,9 +51,9 @@ MIDDLEWARE_CLASSES = [
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.gzip.GZipMiddleware', 'django.middleware.gzip.GZipMiddleware',
#'django.middleware.cache.UpdateCacheMiddleware', # 'django.middleware.cache.UpdateCacheMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
#'django.middleware.cache.FetchFromCacheMiddleware', # 'django.middleware.cache.FetchFromCacheMiddleware',
'django.middleware.csrf.CsrfViewMiddleware', 'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
@ -195,19 +195,24 @@ CACHE_MIDDLEWARE_SECONDS = 60 * 60 * 10
CACHE_MIDDLEWARE_KEY_PREFIX = "djangoblog" CACHE_MIDDLEWARE_KEY_PREFIX = "djangoblog"
CACHE_MIDDLEWARE_ALIAS = 'default' CACHE_MIDDLEWARE_ALIAS = 'default'
SESSION_ENGINE = "django.contrib.sessions.backends.cache" # SESSION_ENGINE = "django.contrib.sessions.backends.cache"
SESSION_CACHE_ALIAS = 'default' # SESSION_CACHE_ALIAS = 'default'
OAHUTH = { OAHUTH = {
'sina': { 'sina': {
'appkey': '3161614143', 'appkey': '3161614143',
'appsecret': 'ee17c099317f872eeddb25204ea46721', 'appsecret': 'ee17c099317f872eeddb25204ea46721',
'callbackurl': 'http://www.lylinux.net/oauth/weibo' 'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=weibo'
}, },
'google': { 'google': {
'appkey': os.environ.get('GOOGLE_APP_KEY'), 'appkey': os.environ.get('GOOGLE_APP_KEY'),
'appsecret': os.environ.get('GOOGLE_APP_SECRET'), 'appsecret': os.environ.get('GOOGLE_APP_SECRET'),
'callbackurl': 'http://www.lylinux.net/oauth/googleauthorize' 'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=google'
},
'github': {
'appkey': os.environ.get('GITHUB_APP_KEY'),
'appsecret': os.environ.get('GITHUB_APP_SECRET'),
'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=github'
} }
} }

@ -19,12 +19,10 @@ from pygments import highlight
from pygments.lexers import get_lexer_by_name from pygments.lexers import get_lexer_by_name
from pygments.formatters import html from pygments.formatters import html
import logging import logging
import _thread
from django.core.mail import EmailMultiAlternatives
logger = logging.getLogger('djangoblog') logger = logging.getLogger('djangoblog')
from importlib import import_module
from django.conf import settings
SessionStore = import_module(settings.SESSION_ENGINE).SessionStore
def get_max_articleid_commentid(): def get_max_articleid_commentid():
@ -120,3 +118,24 @@ class common_markdown():
mdp = mistune.Markdown(escape=True, renderer=renderer) mdp = mistune.Markdown(escape=True, renderer=renderer)
return mdp(value) return mdp(value)
def send_email(subject, html_content, tomail):
msg = EmailMultiAlternatives(subject, html_content, from_email='no-reply@lylinux.net', to=tomail)
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,))
def parse_dict_to_url(dict):
from urllib.parse import quote
url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/'))
for k, v in dict.items()])
return url

@ -16,6 +16,11 @@ class ArticleForm(forms.ModelForm):
class ArticlelAdmin(admin.ModelAdmin): class ArticlelAdmin(admin.ModelAdmin):
form = ArticleForm form = ArticleForm
list_display = ('id', 'title', 'author', 'created_time', 'views', 'status', 'type')
list_display_links = ('id', 'title')
list_filter = ('author', 'status', 'type', 'category', 'tags')
filter_horizontal = ('tags',)
exclude = ('slug', 'created_time')
def save_model(self, request, obj, form, change): def save_model(self, request, obj, form, change):
super(ArticlelAdmin, self).save_model(request, obj, form, change) super(ArticlelAdmin, self).save_model(request, obj, form, change)
@ -23,8 +28,16 @@ class ArticlelAdmin(admin.ModelAdmin):
cache.clear() cache.clear()
class TagAdmin(admin.ModelAdmin):
exclude = ('slug',)
class CategoryAdmin(admin.ModelAdmin):
exclude = ('slug',)
admin.site.register(Article, ArticlelAdmin) admin.site.register(Article, ArticlelAdmin)
# admin.site.register(BlogPage, ArticlelAdmin) # admin.site.register(BlogPage, ArticlelAdmin)
admin.site.register(Category) admin.site.register(Category, CategoryAdmin)
admin.site.register(Tag) admin.site.register(Tag, TagAdmin)
admin.site.register(Links) admin.site.register(Links)

@ -0,0 +1,44 @@
#!/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: create_testdata.py
@time: 2017/3/11 上午1:58
"""
from django.core.management.base import BaseCommand
from blog.models import Article, Tag, Category
from django.contrib.auth import get_user_model
from django.core.exceptions import ObjectDoesNotExist
import datetime
class Command(BaseCommand):
help = 'create test datas'
def handle(self, *args, **options):
user = \
get_user_model().objects.get_or_create(email='test@test.com', username='testuser',
password='test!q@w#eTYU')[0]
pcategory = Category.objects.get_or_create(name='pcategory', parent_category=None)[0]
category = Category.objects.get_or_create(name='category', parent_category=pcategory)[0]
category.save()
for i in range(1, 10):
article = Article.objects.get_or_create(category=category,
title='nice title ' + str(i),
body='nice content ' + str(i),
author=user
)
from DjangoBlog.utils import cache
cache.clear()
self.stdout.write(self.style.SUCCESS('created test datas \n'))

@ -141,70 +141,6 @@ class Article(BaseModel):
return Article.objects.filter(id__lt=self.id, status='p').first() return Article.objects.filter(id__lt=self.id, status='p').first()
'''
class BlogPage(models.Model):
"""文章"""
STATUS_CHOICES = (
('d', '草稿'),
('p', '发表'),
)
COMMENT_STATUS = (
('o', '打开'),
('c', '关闭'),
)
title = models.CharField('标题', max_length=200)
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,
help_text="不指定发布时间则视为草稿,可以指定未来时间,到时将自动发布。")
status = models.CharField('文章状态', max_length=1, choices=STATUS_CHOICES, default='o')
comment_status = models.CharField('评论状态', max_length=1, choices=COMMENT_STATUS)
# summary = models.CharField('摘要', max_length=200, blank=True, help_text="可选若为空将摘取正文的前300个字符。")
views = models.PositiveIntegerField('浏览量', default=0)
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='作者', on_delete=models.CASCADE)
slug = models.SlugField(default='no-slug', max_length=60, blank=True)
class Meta:
ordering = ['-pub_time']
verbose_name = "页面"
verbose_name_plural = verbose_name
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('blog:pagedetail', kwargs=
{
'page_id': self.id,
'year': self.created_time.year,
'month': self.created_time.month,
'day': self.created_time.day,
'slug': self.slug
})
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)
super().save(*args, **kwargs)
def viewed(self):
self.views += 1
self.save(update_fields=['views'])
def comment_list(self):
comments = self.comment_set.all()
parent_comments = comments.filter(parent_comment=None)
def get_category_tree(self):
return []
'''
class Category(BaseModel): class Category(BaseModel):
"""文章分类""" """文章分类"""
name = models.CharField('分类名', max_length=30, unique=True) name = models.CharField('分类名', max_length=30, unique=True)

@ -0,0 +1,74 @@
/* Make clicks pass-through */
#nprogress {
pointer-events: none;
}
#nprogress .bar {
background: red;
position: fixed;
z-index: 1031;
top: 0;
left: 0;
width: 100%;
height: 2px;
}
/* Fancy blur effect */
#nprogress .peg {
display: block;
position: absolute;
right: 0px;
width: 100px;
height: 100%;
box-shadow: 0 0 10px #29d, 0 0 5px #29d;
opacity: 1.0;
-webkit-transform: rotate(3deg) translate(0px, -4px);
-ms-transform: rotate(3deg) translate(0px, -4px);
transform: rotate(3deg) translate(0px, -4px);
}
/* Remove these to get rid of the spinner */
#nprogress .spinner {
display: block;
position: fixed;
z-index: 1031;
top: 15px;
right: 15px;
}
#nprogress .spinner-icon {
width: 18px;
height: 18px;
box-sizing: border-box;
border: solid 2px transparent;
border-top-color: red;
border-left-color: red;
border-radius: 50%;
-webkit-animation: nprogress-spinner 400ms linear infinite;
animation: nprogress-spinner 400ms linear infinite;
}
.nprogress-custom-parent {
overflow: hidden;
position: relative;
}
.nprogress-custom-parent #nprogress .spinner,
.nprogress-custom-parent #nprogress .bar {
position: absolute;
}
@-webkit-keyframes nprogress-spinner {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
@keyframes nprogress-spinner {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}

@ -0,0 +1,305 @@
.icon-sn-google {
background-position: 0 -28px;
}
.icon-sn-bg-google {
background-color: #4285f4;
background-position: 0 0;
}
.fa-sn-google {
color: #4285f4;
}
.icon-sn-github {
background-position: -28px -28px;
}
.icon-sn-bg-github {
background-color: #333;
background-position: -28px 0;
}
.fa-sn-github {
color: #333;
}
.icon-sn-weibo {
background-position: -56px -28px;
}
.icon-sn-bg-weibo {
background-color: #e90d24;
background-position: -56px 0;
}
.fa-sn-weibo {
color: #e90d24;
}
.icon-sn-qq {
background-position: -84px -28px;
}
.icon-sn-bg-qq {
background-color: #0098e6;
background-position: -84px 0;
}
.fa-sn-qq {
color: #0098e6;
}
.icon-sn-twitter {
background-position: -112px -28px;
}
.icon-sn-bg-twitter {
background-color: #50abf1;
background-position: -112px 0;
}
.fa-sn-twitter {
color: #50abf1;
}
.icon-sn-facebook {
background-position: -140px -28px;
}
.icon-sn-bg-facebook {
background-color: #4862a3;
background-position: -140px 0;
}
.fa-sn-facebook {
color: #4862a3;
}
.icon-sn-renren {
background-position: -168px -28px;
}
.icon-sn-bg-renren {
background-color: #197bc8;
background-position: -168px 0;
}
.fa-sn-renren {
color: #197bc8;
}
.icon-sn-tqq {
background-position: -196px -28px;
}
.icon-sn-bg-tqq {
background-color: #1f9ed2;
background-position: -196px 0;
}
.fa-sn-tqq {
color: #1f9ed2;
}
.icon-sn-douban {
background-position: -224px -28px;
}
.icon-sn-bg-douban {
background-color: #279738;
background-position: -224px 0;
}
.fa-sn-douban {
color: #279738;
}
.icon-sn-weixin {
background-position: -252px -28px;
}
.icon-sn-bg-weixin {
background-color: #00b500;
background-position: -252px 0;
}
.fa-sn-weixin {
color: #00b500;
}
.icon-sn-dotted {
background-position: -280px -28px;
}
.icon-sn-bg-dotted {
background-color: #eee;
background-position: -280px 0;
}
.fa-sn-dotted {
color: #eee;
}
.icon-sn-site {
background-position: -308px -28px;
}
.icon-sn-bg-site {
background-color: #00b500;
background-position: -308px 0;
}
.fa-sn-site {
color: #00b500;
}
.icon-sn-linkedin {
background-position: -336px -28px;
}
.icon-sn-bg-linkedin {
background-color: #0077b9;
background-position: -336px 0;
}
.fa-sn-linkedin {
color: #0077b9;
}
[class*=icon-sn-] {
display: inline-block;
background-image: url('../img/icon-sn.svg');
background-repeat: no-repeat;
width: 28px;
height: 28px;
vertical-align: middle;
background-size: auto 56px;
}
[class*=icon-sn-]:hover {
opacity: .8;
filter: alpha(opacity=80);
}
.btn-sn-google {
background: #4285f4;
}
.btn-sn-google:active, .btn-sn-google:focus, .btn-sn-google:hover {
background: #2a75f3;
}
.btn-sn-github {
background: #333;
}
.btn-sn-github:active, .btn-sn-github:focus, .btn-sn-github:hover {
background: #262626;
}
.btn-sn-weibo {
background: #e90d24;
}
.btn-sn-weibo:active, .btn-sn-weibo:focus, .btn-sn-weibo:hover {
background: #d10c20;
}
.btn-sn-qq {
background: #0098e6;
}
.btn-sn-qq:active, .btn-sn-qq:focus, .btn-sn-qq:hover {
background: #0087cd;
}
.btn-sn-twitter {
background: #50abf1;
}
.btn-sn-twitter:active, .btn-sn-twitter:focus, .btn-sn-twitter:hover {
background: #38a0ef;
}
.btn-sn-facebook {
background: #4862a3;
}
.btn-sn-facebook:active, .btn-sn-facebook:focus, .btn-sn-facebook:hover {
background: #405791;
}
.btn-sn-renren {
background: #197bc8;
}
.btn-sn-renren:active, .btn-sn-renren:focus, .btn-sn-renren:hover {
background: #166db1;
}
.btn-sn-tqq {
background: #1f9ed2;
}
.btn-sn-tqq:active, .btn-sn-tqq:focus, .btn-sn-tqq:hover {
background: #1c8dbc;
}
.btn-sn-douban {
background: #279738;
}
.btn-sn-douban:active, .btn-sn-douban:focus, .btn-sn-douban:hover {
background: #228330;
}
.btn-sn-weixin {
background: #00b500;
}
.btn-sn-weixin:active, .btn-sn-weixin:focus, .btn-sn-weixin:hover {
background: #009c00;
}
.btn-sn-dotted {
background: #eee;
}
.btn-sn-dotted:active, .btn-sn-dotted:focus, .btn-sn-dotted:hover {
background: #e1e1e1;
}
.btn-sn-site {
background: #00b500;
}
.btn-sn-site:active, .btn-sn-site:focus, .btn-sn-site:hover {
background: #009c00;
}
.btn-sn-linkedin {
background: #0077b9;
}
.btn-sn-linkedin:active, .btn-sn-linkedin:focus, .btn-sn-linkedin:hover {
background: #0067a0;
}
[class*=btn-sn-], [class*=btn-sn-]:active, [class*=btn-sn-]:focus, [class*=btn-sn-]:hover {
border: none;
color: #fff;
}
.btn-sn-more {
padding: 0;
}
.btn-sn-more, .btn-sn-more:active, .btn-sn-more:hover {
box-shadow: none;
}
[class*=btn-sn-] [class*=icon-sn-] {
background-color: transparent;
}

@ -1998,6 +1998,9 @@ div {
#wp-auto-top-bottom:hover { #wp-auto-top-bottom:hover {
background-position: right -68px; background-position: right -68px;
} }
.widget-login{
margin-top: 15px!important;
}
/* ------------------------------------------------------------------------- * /* ------------------------------------------------------------------------- *
* Comments * Comments
@ -2358,308 +2361,3 @@ li #reply-title {
font: inherit; font: inherit;
vertical-align: baseline; vertical-align: baseline;
} }
.icon-sn-google {
background-position: 0 -28px;
}
.icon-sn-bg-google {
background-color: #4285f4;
background-position: 0 0;
}
.fa-sn-google {
color: #4285f4;
}
.icon-sn-github {
background-position: -28px -28px;
}
.icon-sn-bg-github {
background-color: #333;
background-position: -28px 0;
}
.fa-sn-github {
color: #333;
}
.icon-sn-weibo {
background-position: -56px -28px;
}
.icon-sn-bg-weibo {
background-color: #e90d24;
background-position: -56px 0;
}
.fa-sn-weibo {
color: #e90d24;
}
.icon-sn-qq {
background-position: -84px -28px;
}
.icon-sn-bg-qq {
background-color: #0098e6;
background-position: -84px 0;
}
.fa-sn-qq {
color: #0098e6;
}
.icon-sn-twitter {
background-position: -112px -28px;
}
.icon-sn-bg-twitter {
background-color: #50abf1;
background-position: -112px 0;
}
.fa-sn-twitter {
color: #50abf1;
}
.icon-sn-facebook {
background-position: -140px -28px;
}
.icon-sn-bg-facebook {
background-color: #4862a3;
background-position: -140px 0;
}
.fa-sn-facebook {
color: #4862a3;
}
.icon-sn-renren {
background-position: -168px -28px;
}
.icon-sn-bg-renren {
background-color: #197bc8;
background-position: -168px 0;
}
.fa-sn-renren {
color: #197bc8;
}
.icon-sn-tqq {
background-position: -196px -28px;
}
.icon-sn-bg-tqq {
background-color: #1f9ed2;
background-position: -196px 0;
}
.fa-sn-tqq {
color: #1f9ed2;
}
.icon-sn-douban {
background-position: -224px -28px;
}
.icon-sn-bg-douban {
background-color: #279738;
background-position: -224px 0;
}
.fa-sn-douban {
color: #279738;
}
.icon-sn-weixin {
background-position: -252px -28px;
}
.icon-sn-bg-weixin {
background-color: #00b500;
background-position: -252px 0;
}
.fa-sn-weixin {
color: #00b500;
}
.icon-sn-dotted {
background-position: -280px -28px;
}
.icon-sn-bg-dotted {
background-color: #eee;
background-position: -280px 0;
}
.fa-sn-dotted {
color: #eee;
}
.icon-sn-site {
background-position: -308px -28px;
}
.icon-sn-bg-site {
background-color: #00b500;
background-position: -308px 0;
}
.fa-sn-site {
color: #00b500;
}
.icon-sn-linkedin {
background-position: -336px -28px;
}
.icon-sn-bg-linkedin {
background-color: #0077b9;
background-position: -336px 0;
}
.fa-sn-linkedin {
color: #0077b9;
}
[class*=icon-sn-] {
display: inline-block;
background-image: url('../img/icon-sn.svg');
background-repeat: no-repeat;
width: 28px;
height: 28px;
vertical-align: middle;
background-size: auto 56px;
}
[class*=icon-sn-]:hover {
opacity: .8;
filter: alpha(opacity=80);
}
.btn-sn-google {
background: #4285f4;
}
.btn-sn-google:active, .btn-sn-google:focus, .btn-sn-google:hover {
background: #2a75f3;
}
.btn-sn-github {
background: #333;
}
.btn-sn-github:active, .btn-sn-github:focus, .btn-sn-github:hover {
background: #262626;
}
.btn-sn-weibo {
background: #e90d24;
}
.btn-sn-weibo:active, .btn-sn-weibo:focus, .btn-sn-weibo:hover {
background: #d10c20;
}
.btn-sn-qq {
background: #0098e6;
}
.btn-sn-qq:active, .btn-sn-qq:focus, .btn-sn-qq:hover {
background: #0087cd;
}
.btn-sn-twitter {
background: #50abf1;
}
.btn-sn-twitter:active, .btn-sn-twitter:focus, .btn-sn-twitter:hover {
background: #38a0ef;
}
.btn-sn-facebook {
background: #4862a3;
}
.btn-sn-facebook:active, .btn-sn-facebook:focus, .btn-sn-facebook:hover {
background: #405791;
}
.btn-sn-renren {
background: #197bc8;
}
.btn-sn-renren:active, .btn-sn-renren:focus, .btn-sn-renren:hover {
background: #166db1;
}
.btn-sn-tqq {
background: #1f9ed2;
}
.btn-sn-tqq:active, .btn-sn-tqq:focus, .btn-sn-tqq:hover {
background: #1c8dbc;
}
.btn-sn-douban {
background: #279738;
}
.btn-sn-douban:active, .btn-sn-douban:focus, .btn-sn-douban:hover {
background: #228330;
}
.btn-sn-weixin {
background: #00b500;
}
.btn-sn-weixin:active, .btn-sn-weixin:focus, .btn-sn-weixin:hover {
background: #009c00;
}
.btn-sn-dotted {
background: #eee;
}
.btn-sn-dotted:active, .btn-sn-dotted:focus, .btn-sn-dotted:hover {
background: #e1e1e1;
}
.btn-sn-site {
background: #00b500;
}
.btn-sn-site:active, .btn-sn-site:focus, .btn-sn-site:hover {
background: #009c00;
}
.btn-sn-linkedin {
background: #0077b9;
}
.btn-sn-linkedin:active, .btn-sn-linkedin:focus, .btn-sn-linkedin:hover {
background: #0067a0;
}
[class*=btn-sn-], [class*=btn-sn-]:active, [class*=btn-sn-]:focus, [class*=btn-sn-]:hover {
border: none;
color: #fff;
}
.btn-sn-more {
padding: 0;
}
.btn-sn-more, .btn-sn-more:active, .btn-sn-more:hover {
box-shadow: none;
}
[class*=btn-sn-] [class*=icon-sn-] {
background-color: transparent;
}

@ -17,3 +17,14 @@ function cancel_reply() {
$("#id_parent_comment_id").val('') $("#id_parent_comment_id").val('')
$("#commentform").appendTo($("#respond")); $("#commentform").appendTo($("#respond"));
} }
NProgress.start();
NProgress.set(0.4);
//Increment
var interval = setInterval(function () {
NProgress.inc();
}, 1000);
$(document).ready(function () {
NProgress.done();
clearInterval(interval);
});

@ -0,0 +1,480 @@
/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress
* @license MIT */
;(function(root, factory) {
if (typeof define === 'function' && define.amd) {
define(factory);
} else if (typeof exports === 'object') {
module.exports = factory();
} else {
root.NProgress = factory();
}
})(this, function() {
var NProgress = {};
NProgress.version = '0.2.0';
var Settings = NProgress.settings = {
minimum: 0.08,
easing: 'linear',
positionUsing: '',
speed: 200,
trickle: true,
trickleSpeed: 200,
showSpinner: true,
barSelector: '[role="bar"]',
spinnerSelector: '[role="spinner"]',
parent: 'body',
template: '<div class="bar" role="bar"><div class="peg"></div></div><div class="spinner" role="spinner"><div class="spinner-icon"></div></div>'
};
/**
* Updates configuration.
*
* NProgress.configure({
* minimum: 0.1
* });
*/
NProgress.configure = function(options) {
var key, value;
for (key in options) {
value = options[key];
if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value;
}
return this;
};
/**
* Last number.
*/
NProgress.status = null;
/**
* Sets the progress bar status, where `n` is a number from `0.0` to `1.0`.
*
* NProgress.set(0.4);
* NProgress.set(1.0);
*/
NProgress.set = function(n) {
var started = NProgress.isStarted();
n = clamp(n, Settings.minimum, 1);
NProgress.status = (n === 1 ? null : n);
var progress = NProgress.render(!started),
bar = progress.querySelector(Settings.barSelector),
speed = Settings.speed,
ease = Settings.easing;
progress.offsetWidth; /* Repaint */
queue(function(next) {
// Set positionUsing if it hasn't already been set
if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS();
// Add transition
css(bar, barPositionCSS(n, speed, ease));
if (n === 1) {
// Fade out
css(progress, {
transition: 'none',
opacity: 1
});
progress.offsetWidth; /* Repaint */
setTimeout(function() {
css(progress, {
transition: 'all ' + speed + 'ms linear',
opacity: 0
});
setTimeout(function() {
NProgress.remove();
next();
}, speed);
}, speed);
} else {
setTimeout(next, speed);
}
});
return this;
};
NProgress.isStarted = function() {
return typeof NProgress.status === 'number';
};
/**
* Shows the progress bar.
* This is the same as setting the status to 0%, except that it doesn't go backwards.
*
* NProgress.start();
*
*/
NProgress.start = function() {
if (!NProgress.status) NProgress.set(0);
var work = function() {
setTimeout(function() {
if (!NProgress.status) return;
NProgress.trickle();
work();
}, Settings.trickleSpeed);
};
if (Settings.trickle) work();
return this;
};
/**
* Hides the progress bar.
* This is the *sort of* the same as setting the status to 100%, with the
* difference being `done()` makes some placebo effect of some realistic motion.
*
* NProgress.done();
*
* If `true` is passed, it will show the progress bar even if its hidden.
*
* NProgress.done(true);
*/
NProgress.done = function(force) {
if (!force && !NProgress.status) return this;
return NProgress.inc(0.3 + 0.5 * Math.random()).set(1);
};
/**
* Increments by a random amount.
*/
NProgress.inc = function(amount) {
var n = NProgress.status;
if (!n) {
return NProgress.start();
} else if(n > 1) {
return;
} else {
if (typeof amount !== 'number') {
if (n >= 0 && n < 0.2) { amount = 0.1; }
else if (n >= 0.2 && n < 0.5) { amount = 0.04; }
else if (n >= 0.5 && n < 0.8) { amount = 0.02; }
else if (n >= 0.8 && n < 0.99) { amount = 0.005; }
else { amount = 0; }
}
n = clamp(n + amount, 0, 0.994);
return NProgress.set(n);
}
};
NProgress.trickle = function() {
return NProgress.inc();
};
/**
* Waits for all supplied jQuery promises and
* increases the progress as the promises resolve.
*
* @param $promise jQUery Promise
*/
(function() {
var initial = 0, current = 0;
NProgress.promise = function($promise) {
if (!$promise || $promise.state() === "resolved") {
return this;
}
if (current === 0) {
NProgress.start();
}
initial++;
current++;
$promise.always(function() {
current--;
if (current === 0) {
initial = 0;
NProgress.done();
} else {
NProgress.set((initial - current) / initial);
}
});
return this;
};
})();
/**
* (Internal) renders the progress bar markup based on the `template`
* setting.
*/
NProgress.render = function(fromStart) {
if (NProgress.isRendered()) return document.getElementById('nprogress');
addClass(document.documentElement, 'nprogress-busy');
var progress = document.createElement('div');
progress.id = 'nprogress';
progress.innerHTML = Settings.template;
var bar = progress.querySelector(Settings.barSelector),
perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0),
parent = document.querySelector(Settings.parent),
spinner;
css(bar, {
transition: 'all 0 linear',
transform: 'translate3d(' + perc + '%,0,0)'
});
if (!Settings.showSpinner) {
spinner = progress.querySelector(Settings.spinnerSelector);
spinner && removeElement(spinner);
}
if (parent != document.body) {
addClass(parent, 'nprogress-custom-parent');
}
parent.appendChild(progress);
return progress;
};
/**
* Removes the element. Opposite of render().
*/
NProgress.remove = function() {
removeClass(document.documentElement, 'nprogress-busy');
removeClass(document.querySelector(Settings.parent), 'nprogress-custom-parent');
var progress = document.getElementById('nprogress');
progress && removeElement(progress);
};
/**
* Checks if the progress bar is rendered.
*/
NProgress.isRendered = function() {
return !!document.getElementById('nprogress');
};
/**
* Determine which positioning CSS rule to use.
*/
NProgress.getPositioningCSS = function() {
// Sniff on document.body.style
var bodyStyle = document.body.style;
// Sniff prefixes
var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' :
('MozTransform' in bodyStyle) ? 'Moz' :
('msTransform' in bodyStyle) ? 'ms' :
('OTransform' in bodyStyle) ? 'O' : '';
if (vendorPrefix + 'Perspective' in bodyStyle) {
// Modern browsers with 3D support, e.g. Webkit, IE10
return 'translate3d';
} else if (vendorPrefix + 'Transform' in bodyStyle) {
// Browsers without 3D support, e.g. IE9
return 'translate';
} else {
// Browsers without translate() support, e.g. IE7-8
return 'margin';
}
};
/**
* Helpers
*/
function clamp(n, min, max) {
if (n < min) return min;
if (n > max) return max;
return n;
}
/**
* (Internal) converts a percentage (`0..1`) to a bar translateX
* percentage (`-100%..0%`).
*/
function toBarPerc(n) {
return (-1 + n) * 100;
}
/**
* (Internal) returns the correct CSS for changing the bar's
* position given an n percentage, and speed and ease from Settings
*/
function barPositionCSS(n, speed, ease) {
var barCSS;
if (Settings.positionUsing === 'translate3d') {
barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' };
} else if (Settings.positionUsing === 'translate') {
barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' };
} else {
barCSS = { 'margin-left': toBarPerc(n)+'%' };
}
barCSS.transition = 'all '+speed+'ms '+ease;
return barCSS;
}
/**
* (Internal) Queues a function to be executed.
*/
var queue = (function() {
var pending = [];
function next() {
var fn = pending.shift();
if (fn) {
fn(next);
}
}
return function(fn) {
pending.push(fn);
if (pending.length == 1) next();
};
})();
/**
* (Internal) Applies css properties to an element, similar to the jQuery
* css method.
*
* While this helper does assist with vendor prefixed property names, it
* does not perform any manipulation of values prior to setting styles.
*/
var css = (function() {
var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ],
cssProps = {};
function camelCase(string) {
return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) {
return letter.toUpperCase();
});
}
function getVendorProp(name) {
var style = document.body.style;
if (name in style) return name;
var i = cssPrefixes.length,
capName = name.charAt(0).toUpperCase() + name.slice(1),
vendorName;
while (i--) {
vendorName = cssPrefixes[i] + capName;
if (vendorName in style) return vendorName;
}
return name;
}
function getStyleProp(name) {
name = camelCase(name);
return cssProps[name] || (cssProps[name] = getVendorProp(name));
}
function applyCss(element, prop, value) {
prop = getStyleProp(prop);
element.style[prop] = value;
}
return function(element, properties) {
var args = arguments,
prop,
value;
if (args.length == 2) {
for (prop in properties) {
value = properties[prop];
if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value);
}
} else {
applyCss(element, args[1], args[2]);
}
}
})();
/**
* (Internal) Determines if an element or space separated list of class names contains a class name.
*/
function hasClass(element, name) {
var list = typeof element == 'string' ? element : classList(element);
return list.indexOf(' ' + name + ' ') >= 0;
}
/**
* (Internal) Adds a class to an element.
*/
function addClass(element, name) {
var oldList = classList(element),
newList = oldList + name;
if (hasClass(oldList, name)) return;
// Trim the opening space.
element.className = newList.substring(1);
}
/**
* (Internal) Removes a class from an element.
*/
function removeClass(element, name) {
var oldList = classList(element),
newList;
if (!hasClass(element, name)) return;
// Replace the class name.
newList = oldList.replace(' ' + name + ' ', ' ');
// Trim the opening and closing spaces.
element.className = newList.substring(1, newList.length - 1);
}
/**
* (Internal) Gets a space separated list of the class names on the element.
* The list is wrapped with a single space on each end to facilitate finding
* matches within the list.
*/
function classList(element) {
return (' ' + (element && element.className || '') + ' ').replace(/\s+/gi, ' ');
}
/**
* (Internal) Removes an element from the DOM.
*/
function removeElement(element) {
element && element.parentNode && element.parentNode.removeChild(element);
}
return NProgress;
});

@ -29,33 +29,6 @@ from django.utils.decorators import classonlymethod
from django.utils.decorators import method_decorator from django.utils.decorators import method_decorator
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
"""
class SeoProcessor():
__metaclass__ = ABCMeta
@abstractmethod
def get_title(self):
pass
@abstractmethod
def get_keywords(self):
pass
@abstractmethod
def get_description(self):
pass
"""
"""
class CachedTemplateView(ListView):
@classonlymethod
def as_view(cls, **initkwargs):
# print(request)
view = super(CachedTemplateView, cls).as_view(**initkwargs)
return cache_page(60 * 60 * 10)(view)
"""
class ArticleListView(ListView): class ArticleListView(ListView):
# template_name属性用于指定使用哪个模板进行渲染 # template_name属性用于指定使用哪个模板进行渲染
@ -117,24 +90,6 @@ class IndexView(ArticleListView):
cache_key = 'index_{page}'.format(page=self.page_number) cache_key = 'index_{page}'.format(page=self.page_number)
return cache_key return cache_key
'''
def get_queryset(self):
# return self.get_queryset_data()
cache_key = 'index_{page}'.format(page=self.page_number)
return self.get_queryset_from_cache(cache_key=cache_key)
"""
value = cache.get(cache_key)
if value:
logger.info('get view cache.key:{key}'.format(key=cache_key))
return value
else:
article_list = Article.objects.filter(type='a', status='p')
cache.set(cache_key, article_list)
logger.info('set view cache.key:{key}'.format(key=cache_key))
return article_list
"""
'''
class ArticleDetailView(DetailView): class ArticleDetailView(DetailView):
template_name = 'blog/article_detail.html' template_name = 'blog/article_detail.html'
@ -175,37 +130,6 @@ class ArticleDetailView(DetailView):
return super(ArticleDetailView, self).get_context_data(**kwargs) return super(ArticleDetailView, self).get_context_data(**kwargs)
"""
@classonlymethod
def as_view(cls, **initkwargs):
self = cls(**initkwargs)
keyperfix = "blogdetail"
return cache_page(60 * 60 * 10, key_prefix=keyperfix)(super(ArticleDetailView, cls).as_view(**initkwargs))
"""
"""
def post(self, request, *args, **kwargs):
form = CommentForm(request.POST)
if form.is_valid():
data = form.cleaned_data
pass
"""
'''
class PageDetailView(ArticleDetailView):
model = BlogPage
pk_url_kwarg = 'page_id'
def get_object(self):
obj = super(PageDetailView, self).get_object()
print(obj.title)
obj.viewed()
# obj.body = markdown2.markdown(obj.body)
return obj
'''
class CategoryDetailView(ArticleListView): class CategoryDetailView(ArticleListView):
page_type = "分类目录归档" page_type = "分类目录归档"
@ -226,35 +150,8 @@ class CategoryDetailView(ArticleListView):
cache_key = 'category_list_{categoryname}_{page}'.format(categoryname=categoryname, page=self.page_number) cache_key = 'category_list_{categoryname}_{page}'.format(categoryname=categoryname, page=self.page_number)
return cache_key return cache_key
'''
def get_queryset(self):
slug = self.kwargs['category_name']
# category = Category.objects.get(slug=slug)
category = get_object_or_404(Category, slug=slug)
categoryname = category.name
self.categoryname = categoryname
try:
categoryname = categoryname.split('/')[-1]
except:
pass
cache_key = 'category_list_{categoryname}_{page}'.format(categoryname=categoryname, page=self.page_number)
value = cache.get(cache_key)
if value:
logger.info('get view cache.key:{key}'.format(key=cache_key))
return value
else:
article_list = Article.objects.filter(category__name=categoryname, status='p')
cache.set(cache_key, article_list)
logger.info('set view cache.key:{key}'.format(key=cache_key))
return article_list
'''
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
# slug = self.kwargs['category_name']
# category = Category.objects.get(slug=slug)
# categoryname = category.name
categoryname = self.categoryname categoryname = self.categoryname
try: try:
categoryname = categoryname.split('/')[-1] categoryname = categoryname.split('/')[-1]
@ -360,25 +257,18 @@ def refresh_memcache(request):
""" """
class BlogSearchView(SearchView): class SeoProcessor():
form_class = BlogSearchForm __metaclass__ = ABCMeta
template_name = 'blog/article_detail.html'
model = Article
# template_name属性用于指定使用哪个模板进行渲染
template_name = 'blog/article_index.html'
# context_object_name属性用于给上下文变量取名在模板中使用该名字 @abstractmethod
context_object_name = 'article_list' def get_title(self):
pass
def get_queryset(self): @abstractmethod
queryset = super(BlogSearchView, self).get_queryset() def get_keywords(self):
# further filter queryset based on some set of criteria pass
# return queryset.filter(pub_date__gte=date(2015, 1, 1))
return queryset
def get_context_data(self, **kwargs): @abstractmethod
tag_name = 'search' def get_description(self):
kwargs['page_type'] = 'search' pass
kwargs['tag_name'] = tag_name
return super(BlogSearchView, self).get_context_data(**kwargs)
""" """

@ -11,9 +11,6 @@ from DjangoBlog.utils import cache
# Create your models here. # Create your models here.
class Comment(models.Model): class Comment(models.Model):
# url = models.URLField('地址', blank=True, null=True)
# email = models.EmailField('电子邮件', blank=True, null=True)
body = models.TextField('正文', max_length=300) body = models.TextField('正文', max_length=300)
created_time = models.DateTimeField('创建时间', auto_now_add=True) created_time = models.DateTimeField('创建时间', auto_now_add=True)
last_mod_time = models.DateTimeField('修改时间', auto_now=True) last_mod_time = models.DateTimeField('修改时间', auto_now=True)

@ -9,17 +9,7 @@ from django.http import HttpResponseRedirect
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django import forms from django import forms
"""
from django.core.urlresolvers import reverse
from django.contrib import auth
from django.utils.decorators import method_decorator
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.contrib.auth.decorators import login_required
"""
# @method_decorator(login_required,name='dispatch')
class CommentPostView(FormView): class CommentPostView(FormView):
form_class = CommentForm form_class = CommentForm
template_name = 'blog/article_detail.html' template_name = 'blog/article_detail.html'

@ -0,0 +1,26 @@
#!/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: forms.py
@time: 2017/3/7 下午8:58
"""
from django.contrib.auth.forms import forms
from django.forms import widgets
class RequireEmailForm(forms.Form):
email = forms.EmailField(label='电子邮箱', required=True)
oauthid = forms.IntegerField(widget=forms.HiddenInput, required=False)
def __init__(self, *args, **kwargs):
super(RequireEmailForm, self).__init__(*args, **kwargs)
self.fields['email'].widget = widgets.EmailInput(attrs={'placeholder': "email", "class": "form-control"})

@ -4,33 +4,14 @@ from django.db import models
from django.conf import settings from django.conf import settings
class BaseModel(models.Model): class OAuthUser(models.Model):
author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='用户') author = models.ForeignKey(settings.AUTH_USER_MODEL, verbose_name='用户', blank=True, null=True)
openid = models.CharField(max_length=50) openid = models.CharField(max_length=50)
nikename = models.CharField(max_length=50, verbose_name='昵称') nikename = models.CharField(max_length=50, verbose_name='昵称')
token = models.CharField(max_length=50) token = models.CharField(max_length=150)
picture = models.CharField(max_length=50, blank=True, null=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)
def __str__(self): def __str__(self):
return self.nikename return self.nikename
class Meta:
abstract = True
class SinaWBUserInfo(BaseModel):
class Meta:
verbose_name = "新浪微博"
verbose_name_plural = verbose_name
class QQUserInfo(BaseModel):
class Meta:
verbose_name = "QQ"
verbose_name_plural = verbose_name
class GoogleUserInfo(BaseModel):
class Meta:
verbose_name = "Google"
verbose_name_plural = verbose_name

@ -14,12 +14,15 @@
""" """
from abc import ABCMeta, abstractmethod, abstractproperty from abc import ABCMeta, abstractmethod, abstractproperty
from oauth.models import OAuthUser
from django.conf import settings
import requests import requests
import json import json
import urllib.parse import urllib.parse
from DjangoBlog.utils import logger, parse_dict_to_url
class BaseManager(metaclass=ABCMeta): class BaseOauthManager(metaclass=ABCMeta):
"""获取用户授权""" """获取用户授权"""
AUTH_URL = None AUTH_URL = None
"""获取token""" """获取token"""
@ -29,10 +32,7 @@ class BaseManager(metaclass=ABCMeta):
'''icon图标名''' '''icon图标名'''
ICON_NAME = None ICON_NAME = None
def __init__(self, client_id, client_secret, callback_url, access_token=None, openid=None): def __init__(self, access_token=None, openid=None):
self.client_id = client_id
self.client_secret = client_secret
self.callback_url = callback_url
self.access_token = access_token self.access_token = access_token
self.openid = openid self.openid = openid
@ -65,14 +65,17 @@ class BaseManager(metaclass=ABCMeta):
return rsp.text return rsp.text
class WBOauthManager(BaseManager): class WBOauthManager(BaseOauthManager):
AUTH_URL = 'https://api.weibo.com/oauth2/authorize' AUTH_URL = 'https://api.weibo.com/oauth2/authorize'
TOKEN_URL = 'https://api.weibo.com/oauth2/access_token' TOKEN_URL = 'https://api.weibo.com/oauth2/access_token'
API_URL = 'https://api.weibo.com/2/users/show.json' API_URL = 'https://api.weibo.com/2/users/show.json'
ICON_NAME = 'weibo'
def __init__(self, client_id, client_secret, callback_url, access_token=None, openid=None): def __init__(self, access_token=None, openid=None):
super(WBOauthManager, self).__init__(client_id=client_id, client_secret=client_secret, self.client_id = settings.OAHUTH['sina']['appkey']
callback_url=callback_url, access_token=access_token, openid=openid) self.client_secret = settings.OAHUTH['sina']['appsecret']
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):
params = { params = {
@ -84,7 +87,7 @@ class WBOauthManager(BaseManager):
return url return url
def get_access_token_by_code(self, code): def get_access_token_by_code(self, code):
print(code)
params = { params = {
'client_id': self.client_id, 'client_id': self.client_id,
'client_secret': self.client_secret, 'client_secret': self.client_secret,
@ -93,7 +96,7 @@ class WBOauthManager(BaseManager):
'redirect_uri': self.callback_url 'redirect_uri': self.callback_url
} }
rsp = self.do_post(self.TOKEN_URL, params) rsp = self.do_post(self.TOKEN_URL, params)
print(rsp)
# return rsp # return rsp
obj = json.loads(rsp) obj = json.loads(rsp)
@ -116,18 +119,20 @@ class WBOauthManager(BaseManager):
'access_token': self.access_token 'access_token': self.access_token
} }
rsp = self.do_get(self.API_URL, params) rsp = self.do_get(self.API_URL, params)
print(rsp)
class GoogleOauthManager(BaseManager):
class GoogleOauthManager(BaseOauthManager):
AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth' AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token' TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token'
API_URL = 'https://www.googleapis.com/oauth2/v3/userinfo' API_URL = 'https://www.googleapis.com/oauth2/v3/userinfo'
ICON_NAME = 'google' ICON_NAME = 'google'
def __init__(self, client_id, client_secret, callback_url, access_token=None, openid=None): def __init__(self, access_token=None, openid=None):
super(GoogleOauthManager, self).__init__(client_id=client_id, client_secret=client_secret, self.client_id = settings.OAHUTH['google']['appkey']
callback_url=callback_url, access_token=access_token, openid=openid) self.client_secret = settings.OAHUTH['google']['appsecret']
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):
params = { params = {
@ -136,7 +141,8 @@ class GoogleOauthManager(BaseManager):
'redirect_uri': self.callback_url, 'redirect_uri': self.callback_url,
'scope': 'openid email', 'scope': 'openid email',
} }
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params, quote_via=urllib.parse.quote) # url = self.AUTH_URL + "?" + urllib.parse.urlencode(params, quote_via=urllib.parse.quote)
url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
return url return url
def get_access_token_by_code(self, code): def get_access_token_by_code(self, code):
@ -149,10 +155,16 @@ class GoogleOauthManager(BaseManager):
'redirect_uri': self.callback_url 'redirect_uri': self.callback_url
} }
rsp = self.do_post(self.TOKEN_URL, params) rsp = self.do_post(self.TOKEN_URL, params)
print(rsp)
obj = json.loads(rsp) obj = json.loads(rsp)
try:
self.access_token = str(obj['access_token']) self.access_token = str(obj['access_token'])
self.openid = str(obj['id_token']) self.openid = str(obj['id_token'])
logger.info(self.ICON_NAME + ' oauth ' + rsp)
return self.access_token
except:
logger.info(self.ICON_NAME + ' oauth error ' + rsp)
return None
def get_oauth_userinfo(self): def get_oauth_userinfo(self):
if not self.is_authorized: if not self.is_authorized:
@ -161,5 +173,95 @@ class GoogleOauthManager(BaseManager):
'access_token': self.access_token 'access_token': self.access_token
} }
rsp = self.do_get(self.API_URL, params) rsp = self.do_get(self.API_URL, params)
print(rsp) try:
return json.loads(rsp)
datas = json.loads(rsp)
user = OAuthUser()
user.picture = datas['picture']
user.nikename = datas['name']
user.openid = datas['sub']
user.type = 'google'
if datas['email']:
user.email = datas['email']
return user
except:
logger.info('google oauth error.rsp:' + rsp)
return None
class GitHubOauthManager(BaseOauthManager):
AUTH_URL = 'https://github.com/login/oauth/authorize'
TOKEN_URL = 'https://github.com/login/oauth/access_token'
API_URL = 'https://api.github.com/user'
ICON_NAME = 'github'
def __init__(self, access_token=None, openid=None):
self.client_id = settings.OAHUTH['github']['appkey']
self.client_secret = settings.OAHUTH['github']['appsecret']
self.callback_url = settings.OAHUTH['github']['callbackurl']
super(GitHubOauthManager, self).__init__(access_token=access_token, openid=openid)
def get_authorization_url(self):
params = {
'client_id': self.client_id,
'response_type': 'code',
'redirect_uri': self.callback_url,
'scope': 'user'
}
# url = self.AUTH_URL + "?" + urllib.parse.urlencode(params, quote_via=urllib.parse.quote)
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:
from urllib import parse
r = parse.parse_qs(rsp)
self.access_token = (r['access_token'][0])
return self.access_token
except:
return None
def get_oauth_userinfo(self):
params = {
'access_token': self.access_token
}
rsp = self.do_get(self.API_URL, params)
try:
datas = json.loads(rsp)
user = OAuthUser()
user.picture = datas['avatar_url']
user.nikename = datas['name']
user.openid = datas['id']
user.type = 'github'
if datas['email']:
user.email = datas['email']
return user
except:
logger.info('github oauth error.rsp:' + rsp)
return None
def get_oauth_apps():
applications = BaseOauthManager.__subclasses__()
return list(map(lambda x: x(), applications))
def get_manager_by_type(type):
applications = get_oauth_apps()
finds = list(filter(lambda x: x.ICON_NAME.lower() == type.lower(), applications))
if finds:
return finds[0]
return None

@ -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
@time: 2017/3/4 下午3:22
"""

@ -0,0 +1,29 @@
#!/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: oauth_tags.py
@time: 2017/3/4 下午3:22
"""
from oauth.oauthmanager import get_oauth_apps
from django import template
from django.conf import settings
register = template.Library()
@register.inclusion_tag('oauth/oauth_applications.html')
def load_oauth_applications():
applications = get_oauth_apps()
apps = list(map(lambda x: (x.ICON_NAME, x.get_authorization_url()), applications))
return {
'apps': apps
}

@ -1,13 +1,3 @@
from django.test import TestCase from django.test import TestCase
# Create your tests here. # Create your tests here.
class OAuthTet(TestCase):
def setUp(self):
pass
from oauth.oauthmanager import WBOauthManager
from django.conf import settings
settings.OAHUTH['sina']
manager=WBOauthManager(client_id=settings.OAHUTH['sina']['appkey'],client_secret=settings.OAHUTH['sina']['appsecret'],callback_url=settings.OAHUTH['sina']['callbackurl'])

@ -17,6 +17,13 @@ from django.conf.urls import url
from django.views.decorators.cache import cache_page from django.views.decorators.cache import cache_page
from . import views from . import views
urlpatterns = [
url(r'^oauth/authorize$', views.authorize),
url(r'^oauth/requireemail/(?P<oauthid>\d+)', views.RequireEmailView.as_view(), name='require_email'),
url(r'^oauth/emailconfirm/(?P<id>\d+)/(?P<sign>\S+)', views.emailconfirm, name='email_confirm'),
]
"""
urlpatterns = [ urlpatterns = [
url(r'^oauth/wbauthorize/(?P<sitename>\w+)$', views.wbauthorize), url(r'^oauth/wbauthorize/(?P<sitename>\w+)$', views.wbauthorize),
url(r'^oauth/wboauthurl$', views.wboauthurl), url(r'^oauth/wboauthurl$', views.wboauthurl),
@ -24,3 +31,4 @@ urlpatterns = [
url(r'^oauth/googleoauthurl', views.googleoauthurl), url(r'^oauth/googleoauthurl', views.googleoauthurl),
url(r'^oauth/googleauthorize', views.googleauthorize), url(r'^oauth/googleauthorize', views.googleauthorize),
] ]
"""

@ -1,13 +1,159 @@
from django.shortcuts import render from django.shortcuts import render
# Create your views here. # Create your views here.
from .oauthmanager import WBOauthManager, GoogleOauthManager
from django.conf import settings from django.conf import settings
from django.http import HttpResponse from django.http import HttpResponse, HttpResponseRedirect
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from .models import GoogleUserInfo from .models import OAuthUser
from django.contrib.auth import login
from django.shortcuts import get_object_or_404
from django.views.generic import FormView, RedirectView
from oauth.forms import RequireEmailForm
from django.core.urlresolvers import reverse
from DjangoBlog.utils import send_email, get_md5
from django.contrib.sites.models import Site
from django.core.exceptions import ObjectDoesNotExist
from django.http import HttpResponseForbidden
from .oauthmanager import WBOauthManager, GoogleOauthManager, get_manager_by_type
def authorize(request):
manager = None
type = request.GET.get('type', None)
if not type:
return HttpResponseRedirect('/')
manager = get_manager_by_type(type)
if not manager:
return HttpResponseRedirect('/')
code = request.GET.get('code', None)
rsp = manager.get_access_token_by_code(code)
if not rsp:
return HttpResponseRedirect(manager.get_authorization_url())
user = manager.get_oauth_userinfo()
if user:
try:
user = OAuthUser.objects.get(type=type, openid=user.openid)
except ObjectDoesNotExist:
pass
email = user.email
if email:
author = None
try:
author = get_user_model().objects.get(email=email)
except ObjectDoesNotExist:
pass
if not author:
author = get_user_model(). \
objects.create_user(username=user.nikename + '_' + str(user.openid), email=email)
user.author = author
user.save()
login(request, author)
return HttpResponseRedirect('/')
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
})
print(url)
return HttpResponseRedirect(url)
def emailconfirm(request, id, sign):
if not sign:
return HttpResponseForbidden()
if not get_md5(settings.SECRET_KEY + str(id) + settings.SECRET_KEY).upper() == sign.upper():
return HttpResponseForbidden()
oauthuser = get_object_or_404(OAuthUser, pk=id)
author = get_user_model().objects.get(pk=oauthuser.author_id)
if oauthuser.email and author.email:
login(request, author)
return HttpResponseRedirect('/')
author.set_password('$%^Q1W2E3R4T5Y6,./')
author.email = oauthuser.email
author.save()
login(request, author)
site = Site.objects.get_current().domain
send_email('恭喜您绑定成功!', '''
<p>恭喜您您已经成功绑定您的邮箱您可以使用{type}来直接免密码登录本网站.欢迎您继续关注本站地址是</p>
<a href="{url}" rel="bookmark">{url}</a>
再次感谢您
<br />
如果上面链接无法打开请将此链接复制至浏览器
{url}
'''.format(type=oauthuser.type, url='http://' + site), [oauthuser.email, ])
return HttpResponseRedirect('/')
class RequireEmailView(FormView):
form_class = RequireEmailForm
template_name = 'oauth/require_email.html'
def get(self, request, *args, **kwargs):
oauthid = self.kwargs['oauthid']
oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
if oauthuser.email:
pass
# return HttpResponseRedirect('/')
return super(RequireEmailView, self).get(request, *args, **kwargs)
def get_initial(self):
oauthid = self.kwargs['oauthid']
return {
'email': '',
'oauthid': oauthid
}
def get_context_data(self, **kwargs):
oauthid = self.kwargs['oauthid']
oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
if oauthuser.picture:
kwargs['picture'] = oauthuser.picture
return super(RequireEmailView, self).get_context_data(**kwargs)
def form_valid(self, form):
email = form.cleaned_data['email']
oauthid = form.cleaned_data['oauthid']
oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
oauthuser.email = email
oauthuser.save()
sign = get_md5(settings.SECRET_KEY + str(oauthuser.id) + settings.SECRET_KEY)
site = Site.objects.get_current().domain
if settings.DEBUG:
site = '127.0.0.1:8000'
path = reverse('oauth:email_confirm', kwargs={
'id': oauthid,
'sign': sign
})
url = "http://{site}{path}".format(site=site, path=path)
print(url)
content = """
<p>请点击下面链接绑定您的邮箱</p>
<a href="{url}" rel="bookmark">{url}</a>
再次感谢您
<br />
如果上面链接无法打开请将此链接复制至浏览器
{url}
""".format(url=url)
send_email('绑定您的电子邮箱', content, [email, ])
return HttpResponseRedirect('/')
"""
def wbauthorize(request, sitename): def wbauthorize(request, sitename):
manager = WBOauthManager(client_id=settings.OAHUTH['sina']['appkey'], manager = WBOauthManager(client_id=settings.OAHUTH['sina']['appkey'],
client_secret=settings.OAHUTH['sina']['appsecret'], client_secret=settings.OAHUTH['sina']['appsecret'],
@ -27,27 +173,24 @@ def wboauthurl(request):
def googleoauthurl(request): def googleoauthurl(request):
manager = GoogleOauthManager(client_id=settings.OAHUTH['google']['appkey'], manager = GoogleOauthManager()
client_secret=settings.OAHUTH['google']['appsecret'],
callback_url=settings.OAHUTH['google']['callbackurl'])
url = manager.get_authorization_url() url = manager.get_authorization_url()
return HttpResponse(url) return HttpResponse(url)
def googleauthorize(request): def googleauthorize(request):
manager = GoogleOauthManager(client_id=settings.OAHUTH['google']['appkey'], manager = GoogleOauthManager()
client_secret=settings.OAHUTH['google']['appsecret'],
callback_url=settings.OAHUTH['google']['callbackurl'])
code = request.GET.get('code', None) code = request.GET.get('code', None)
rsp = manager.get_access_token_by_code(code) rsp = manager.get_access_token_by_code(code)
print(rsp) if not rsp:
return HttpResponseRedirect(manager.get_authorization_url())
user = manager.get_oauth_userinfo() user = manager.get_oauth_userinfo()
if user: if user:
email = user['email'] email = user['email']
if email:
author = get_user_model().objects.get(email=email) author = get_user_model().objects.get(email=email)
if not author: if not author:
author = get_user_model().objects.create_user(username=user["name"], email=email, password=None, author = get_user_model().objects.create_user(username=user["name"], email=email)
nikename=user["name"])
if not GoogleUserInfo.objects.filter(author_id=author.pk): if not GoogleUserInfo.objects.filter(author_id=author.pk):
userinfo = GoogleUserInfo() userinfo = GoogleUserInfo()
userinfo.author = author userinfo.author = author
@ -56,4 +199,8 @@ def googleauthorize(request):
userinfo.openid = manager.openid userinfo.openid = manager.openid
userinfo.nikename = user["name"] userinfo.nikename = user["name"]
userinfo.save() userinfo.save()
return HttpResponse(rsp) login(request, author)
else:
pass
return HttpResponseRedirect('/')
"""

@ -28,7 +28,8 @@
<input type="checkbox" value="remember-me"> Stay signed in <input type="checkbox" value="remember-me"> Stay signed in
</label> </label>
</div> </div>
{% load oauth_tags %}
{% load_oauth_applications %}
</form> </form>
</div> </div>

@ -45,7 +45,17 @@
{% comment %}{% load comments_tags %} {% comment %}{% load comments_tags %}
{% load_post_comment article from %}{% endcomment %} {% load_post_comment article from %}{% endcomment %}
{% include 'comments/tags/comment_list.html' %} {% include 'comments/tags/comment_list.html' %}
{% if user.is_authenticated %}
{% include 'comments/tags/post_comment.html' %} {% include 'comments/tags/post_comment.html' %}
{% else %}
<div class="comments-area">
<h3 class="comment-meta">您还没有登录请您<a href="{% url "account:login" %}">登录</a>后发表评论</h3>
{% load oauth_tags %}
{% load_oauth_applications %}
</div>
{% endif %}
{% endif %} {% endif %}
</div><!-- #primary --> </div><!-- #primary -->

@ -0,0 +1,10 @@
<div class="widget-login">
<small>
快捷登录:
</small>
{% for icon,url in apps %}
<a href="{{ url }}" rel="nofollow">
<span class="icon-sn-{{ icon }}"></span>
</a>
{% endfor %}
</div>

@ -0,0 +1,46 @@
{% extends 'share_layout/base_account.html' %}
{% load static %}
{% block content %}
<div class="container">
<h2 class="form-signin-heading text-center">Binding E-mail account</h2>
<div class="card card-signin">
{% if picture %}
<img class="img-circle profile-img" src="{{ picture }}" alt="">
{% else %}
<img class="img-circle profile-img" src="{% static 'blog/img/avatar.png' %}" alt="">
{% endif %}
<form class="form-signin" action="" method="post">
{% csrf_token %}
{% comment %}<label for="inputEmail" class="sr-only">Email address</label>
<input type="email" id="inputEmail" class="form-control" placeholder="Email" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control" placeholder="Password" required>{% endcomment %}
{{ form.non_field_errors }}
{% for field in form %}
{{ field }}
{{ field.errors }}
{% endfor %}
<button class="btn btn-lg btn-primary btn-block" type="submit">Submit</button>
{% comment %}
<div class="checkbox">
<a class="pull-right">Need help?</a>
<label>
<input type="checkbox" value="remember-me"> Stay signed in
</label>
</div>
{% endcomment %}
</form>
</div>
<p class="text-center">
<a href="{% url "account:login" %}">Sign In</a>
</p>
</div> <!-- /container -->
{% endblock %}

@ -17,7 +17,7 @@
{% block header %} {% block header %}
{% endblock %} {% endblock %}
<link rel="profile" href="http://gmpg.org/xfn/11"/> <link rel="profile" href="http://gmpg.org/xfn/11"/>
{% comment %}<link rel="pingback" href="https://www.lylinux.org/xmlrpc.php"/>{% endcomment %}
<!--[if lt IE 9]> <!--[if lt IE 9]>
<script src="{% static 'blog/js/html5.js' %}" type="text/javascript"></script> <script src="{% static 'blog/js/html5.js' %}" type="text/javascript"></script>
<![endif]--> <![endif]-->
@ -32,11 +32,13 @@
{% compress css %} {% compress css %}
<link rel='stylesheet' id='twentytwelve-style-css' href='{% static 'blog/css/style.css' %}' type='text/css' <link rel='stylesheet' id='twentytwelve-style-css' href='{% static 'blog/css/style.css' %}' type='text/css'
media='all'/> media='all'/>
<link href="{% static 'blog/css/oauth_style.css' %}" rel="stylesheet">
{% comment %}<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>{% endcomment %} {% comment %}<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js"></script>{% endcomment %}
<!--[if lt IE 9]> <!--[if lt IE 9]>
<link rel='stylesheet' id='twentytwelve-ie-css' href='{% static 'blog/css/ie.css' %}' type='text/css' media='all' /> <link rel='stylesheet' id='twentytwelve-ie-css' href='{% static 'blog/css/ie.css' %}' type='text/css' media='all' />
<![endif]--> <![endif]-->
<link rel="stylesheet" href="{% static 'pygments/default.css' %}"/> <link rel="stylesheet" href="{% static 'pygments/default.css' %}"/>
<link rel="stylesheet" href="{% static 'blog/css/nprogress.css' %}">
{% block compress_css %} {% block compress_css %}
{% endblock %} {% endblock %}
{% endcompress %} {% endcompress %}
@ -44,6 +46,7 @@
<script type='text/javascript' src='{% static 'blog/js/jquery-migrate.min.js' %}' defer='defer'></script>{% endcomment %} <script type='text/javascript' src='{% static 'blog/js/jquery-migrate.min.js' %}' defer='defer'></script>{% endcomment %}
{% compress js %} {% compress js %}
<script type="text/javascript" src="{% static 'blog/js/jquery-3.1.1.js' %}"></script> <script type="text/javascript" src="{% static 'blog/js/jquery-3.1.1.js' %}"></script>
<script type="text/javascript" src="{% static 'blog/js/nprogress.js' %}"></script>
{% endcompress %} {% endcompress %}
</head> </head>

@ -15,7 +15,7 @@
{% compress css %} {% compress css %}
<!-- Bootstrap core CSS --> <!-- Bootstrap core CSS -->
<link href="{% static 'assets/css/bootstrap.min.css' %}" rel="stylesheet"> <link href="{% static 'assets/css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static 'blog/css/oauth_style.css' %}" rel="stylesheet">
<!-- IE10 viewport hack for Surface/desktop Windows 8 bug --> <!-- IE10 viewport hack for Surface/desktop Windows 8 bug -->
<link href="{% static 'assets/css/ie10-viewport-bug-workaround.css' %}" rel="stylesheet"> <link href="{% static 'assets/css/ie10-viewport-bug-workaround.css' %}" rel="stylesheet">
<!-- TODC Bootstrap core CSS --> <!-- TODC Bootstrap core CSS -->

Loading…
Cancel
Save