From ea09e9abc2a2370fbf1e158c08b58f84656ad20d Mon Sep 17 00:00:00 2001 From: liangliangyy Date: Sun, 5 Mar 2017 22:14:00 +0800 Subject: [PATCH 1/7] =?UTF-8?q?oauth=E5=8A=9F=E8=83=BD,=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DjangoBlog/settings.py | 13 +- blog/static/blog/css/oauth_style.css | 305 +++++++++++++++++++++++ blog/static/blog/css/style.css | 305 ----------------------- oauth/oauthmanager.py | 84 ++++++- oauth/templatetags/__init__.py | 14 ++ oauth/templatetags/oauth_tags.py | 34 +++ oauth/views.py | 41 ++- templates/account/login.html | 3 +- templates/oauth/oauth_applications.html | 7 + templates/share_layout/base.html | 1 + templates/share_layout/base_account.html | 2 +- 11 files changed, 464 insertions(+), 345 deletions(-) create mode 100644 blog/static/blog/css/oauth_style.css create mode 100644 oauth/templatetags/__init__.py create mode 100644 oauth/templatetags/oauth_tags.py create mode 100644 templates/oauth/oauth_applications.html diff --git a/DjangoBlog/settings.py b/DjangoBlog/settings.py index 569679d..6489a36 100644 --- a/DjangoBlog/settings.py +++ b/DjangoBlog/settings.py @@ -22,8 +22,8 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) SECRET_KEY = '&3g0bdza#c%dm1lf%5gi&0-*53p3t0m*hmcvo29cn^$ji7je(c' # SECURITY WARNING: don't run with debug turned on in production! -#DEBUG = True -DEBUG = False +DEBUG = True +# DEBUG = False # ALLOWED_HOSTS = [] ALLOWED_HOSTS = ['www.lylinux.net', '127.0.0.1'] @@ -51,9 +51,9 @@ MIDDLEWARE_CLASSES = [ 'django.middleware.security.SecurityMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware', 'django.middleware.gzip.GZipMiddleware', - #'django.middleware.cache.UpdateCacheMiddleware', + # 'django.middleware.cache.UpdateCacheMiddleware', 'django.middleware.common.CommonMiddleware', - #'django.middleware.cache.FetchFromCacheMiddleware', + # 'django.middleware.cache.FetchFromCacheMiddleware', 'django.middleware.csrf.CsrfViewMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.messages.middleware.MessageMiddleware', @@ -208,6 +208,11 @@ OAHUTH = { 'appkey': os.environ.get('GOOGLE_APP_KEY'), 'appsecret': os.environ.get('GOOGLE_APP_SECRET'), 'callbackurl': 'http://www.lylinux.net/oauth/googleauthorize' + }, + 'github': { + 'appkey': os.environ.get('GITHUB_APP_KEY'), + 'appsecret': os.environ.get('GITHUB_APP_SECRET'), + 'callbackurl': 'http://www.lylinux.net/oauth/githubauthorize' } } diff --git a/blog/static/blog/css/oauth_style.css b/blog/static/blog/css/oauth_style.css new file mode 100644 index 0000000..8af78af --- /dev/null +++ b/blog/static/blog/css/oauth_style.css @@ -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; +} \ No newline at end of file diff --git a/blog/static/blog/css/style.css b/blog/static/blog/css/style.css index fe85ae8..406ea31 100755 --- a/blog/static/blog/css/style.css +++ b/blog/static/blog/css/style.css @@ -2358,308 +2358,3 @@ li #reply-title { font: inherit; 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; -} \ No newline at end of file diff --git a/oauth/oauthmanager.py b/oauth/oauthmanager.py index fce22d4..3fcd81f 100644 --- a/oauth/oauthmanager.py +++ b/oauth/oauthmanager.py @@ -14,12 +14,14 @@ """ from abc import ABCMeta, abstractmethod, abstractproperty +from django.conf import settings import requests import json import urllib.parse +from DjangoBlog.utils import logger -class BaseManager(metaclass=ABCMeta): +class BaseOauthManager(metaclass=ABCMeta): """获取用户授权""" AUTH_URL = None """获取token""" @@ -29,10 +31,7 @@ class BaseManager(metaclass=ABCMeta): '''icon图标名''' ICON_NAME = None - def __init__(self, client_id, client_secret, callback_url, access_token=None, openid=None): - self.client_id = client_id - self.client_secret = client_secret - self.callback_url = callback_url + def __init__(self, access_token=None, openid=None): self.access_token = access_token self.openid = openid @@ -65,14 +64,17 @@ class BaseManager(metaclass=ABCMeta): return rsp.text -class WBOauthManager(BaseManager): +class WBOauthManager(BaseOauthManager): AUTH_URL = 'https://api.weibo.com/oauth2/authorize' TOKEN_URL = 'https://api.weibo.com/oauth2/access_token' 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): - super(WBOauthManager, self).__init__(client_id=client_id, client_secret=client_secret, - callback_url=callback_url, access_token=access_token, openid=openid) + def __init__(self, access_token=None, openid=None): + self.client_id = settings.OAHUTH['sina']['appkey'] + 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): params = { @@ -119,15 +121,17 @@ class WBOauthManager(BaseManager): print(rsp) -class GoogleOauthManager(BaseManager): +class GoogleOauthManager(BaseOauthManager): AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth' TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token' API_URL = 'https://www.googleapis.com/oauth2/v3/userinfo' ICON_NAME = 'google' - def __init__(self, client_id, client_secret, callback_url, access_token=None, openid=None): - super(GoogleOauthManager, self).__init__(client_id=client_id, client_secret=client_secret, - callback_url=callback_url, access_token=access_token, openid=openid) + def __init__(self, access_token=None, openid=None): + self.client_id = settings.OAHUTH['google']['appkey'] + 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): params = { @@ -139,6 +143,60 @@ class GoogleOauthManager(BaseManager): url = self.AUTH_URL + "?" + urllib.parse.urlencode(params, quote_via=urllib.parse.quote) 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) + + obj = json.loads(rsp) + try: + self.access_token = str(obj['access_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): + if not self.is_authorized: + return None + params = { + 'access_token': self.access_token + } + rsp = self.do_get(self.API_URL, params) + print(rsp) + return json.loads(rsp) + + +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:email', + } + url = self.AUTH_URL + "?" + urllib.parse.urlencode(params, quote_via=urllib.parse.quote) + return url + def get_access_token_by_code(self, code): params = { 'client_id': self.client_id, diff --git a/oauth/templatetags/__init__.py b/oauth/templatetags/__init__.py new file mode 100644 index 0000000..a8d9fd4 --- /dev/null +++ b/oauth/templatetags/__init__.py @@ -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 +""" \ No newline at end of file diff --git a/oauth/templatetags/oauth_tags.py b/oauth/templatetags/oauth_tags.py new file mode 100644 index 0000000..26da1f7 --- /dev/null +++ b/oauth/templatetags/oauth_tags.py @@ -0,0 +1,34 @@ +#!/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 * + +from django import template +from django.conf import settings + +register = template.Library() + + +@register.inclusion_tag('oauth/oauth_applications.html') +def load_oauth_applications(): + applications = BaseOauthManager.__subclasses__() + apps = [] + for application in applications: + app = application() + icon = app.ICON_NAME + authorizeurl = app.get_authorization_url() + apps.append((icon, authorizeurl)) + return { + 'apps': apps + } diff --git a/oauth/views.py b/oauth/views.py index a88051f..d9a0d81 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -3,9 +3,10 @@ from django.shortcuts import render # Create your views here. from .oauthmanager import WBOauthManager, GoogleOauthManager 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 .models import GoogleUserInfo +from django.contrib.auth import login def wbauthorize(request, sitename): @@ -27,33 +28,31 @@ def wboauthurl(request): def googleoauthurl(request): - manager = GoogleOauthManager(client_id=settings.OAHUTH['google']['appkey'], - client_secret=settings.OAHUTH['google']['appsecret'], - callback_url=settings.OAHUTH['google']['callbackurl']) + manager = GoogleOauthManager() url = manager.get_authorization_url() return HttpResponse(url) def googleauthorize(request): - manager = GoogleOauthManager(client_id=settings.OAHUTH['google']['appkey'], - client_secret=settings.OAHUTH['google']['appsecret'], - callback_url=settings.OAHUTH['google']['callbackurl']) + manager = GoogleOauthManager() code = request.GET.get('code', None) rsp = manager.get_access_token_by_code(code) - print(rsp) + if not rsp: + return HttpResponseRedirect(manager.get_authorization_url()) user = manager.get_oauth_userinfo() if user: email = user['email'] - author = get_user_model().objects.get(email=email) - if not author: - author = get_user_model().objects.create_user(username=user["name"], email=email, password=None, - nikename=user["name"]) - if not GoogleUserInfo.objects.filter(author_id=author.pk): - userinfo = GoogleUserInfo() - userinfo.author = author - userinfo.picture = user["picture"] - userinfo.token = manager.access_token - userinfo.openid = manager.openid - userinfo.nikename = user["name"] - userinfo.save() - return HttpResponse(rsp) + if email: + author = get_user_model().objects.get(email=email) + if not author: + author = get_user_model().objects.create_user(username=user["name"], email=email) + if not GoogleUserInfo.objects.filter(author_id=author.pk): + userinfo = GoogleUserInfo() + userinfo.author = author + userinfo.picture = user["picture"] + userinfo.token = manager.access_token + userinfo.openid = manager.openid + userinfo.nikename = user["name"] + userinfo.save() + login(request, author) + return HttpResponseRedirect('/') diff --git a/templates/account/login.html b/templates/account/login.html index 10800ba..57c55a3 100644 --- a/templates/account/login.html +++ b/templates/account/login.html @@ -28,7 +28,8 @@ Stay signed in - + {% load oauth_tags %} + {% load_oauth_applications %} diff --git a/templates/oauth/oauth_applications.html b/templates/oauth/oauth_applications.html new file mode 100644 index 0000000..c876343 --- /dev/null +++ b/templates/oauth/oauth_applications.html @@ -0,0 +1,7 @@ +
+ {% for icon,url in apps %} + + + + {% endfor %} +
\ No newline at end of file diff --git a/templates/share_layout/base.html b/templates/share_layout/base.html index 7eff3d5..3ee1107 100644 --- a/templates/share_layout/base.html +++ b/templates/share_layout/base.html @@ -32,6 +32,7 @@ {% compress css %} + {% comment %}{% endcomment %} - + From ebde6b6271e83e213736abc9624e49c7175f02cd Mon Sep 17 00:00:00 2001 From: liangliangyy Date: Tue, 7 Mar 2017 17:41:20 +0800 Subject: [PATCH 2/7] =?UTF-8?q?oauth=E6=9A=82=E5=AD=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DjangoBlog/settings.py | 10 ++++---- oauth/models.py | 17 +++++++++++- oauth/oauthmanager.py | 44 +++++++++++++++++++++++++++----- oauth/templatetags/oauth_tags.py | 11 +++----- oauth/urls.py | 4 +++ oauth/views.py | 36 ++++++++++++++++++++++++-- 6 files changed, 99 insertions(+), 23 deletions(-) diff --git a/DjangoBlog/settings.py b/DjangoBlog/settings.py index 6489a36..6897111 100644 --- a/DjangoBlog/settings.py +++ b/DjangoBlog/settings.py @@ -195,24 +195,24 @@ CACHE_MIDDLEWARE_SECONDS = 60 * 60 * 10 CACHE_MIDDLEWARE_KEY_PREFIX = "djangoblog" CACHE_MIDDLEWARE_ALIAS = 'default' -SESSION_ENGINE = "django.contrib.sessions.backends.cache" -SESSION_CACHE_ALIAS = 'default' +# SESSION_ENGINE = "django.contrib.sessions.backends.cache" +# SESSION_CACHE_ALIAS = 'default' OAHUTH = { 'sina': { 'appkey': '3161614143', 'appsecret': 'ee17c099317f872eeddb25204ea46721', - 'callbackurl': 'http://www.lylinux.net/oauth/weibo' + 'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=weibo' }, 'google': { 'appkey': os.environ.get('GOOGLE_APP_KEY'), '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/githubauthorize' + 'callbackurl': 'http://www.lylinux.net/oauth/authorize?type=github' } } diff --git a/oauth/models.py b/oauth/models.py index 76a10b0..5c0497d 100644 --- a/oauth/models.py +++ b/oauth/models.py @@ -4,8 +4,22 @@ from django.db import models from django.conf import settings +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=50) + picture = models.CharField(max_length=50, 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): + return self.nikename + + +""" class BaseModel(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) nikename = models.CharField(max_length=50, verbose_name='昵称') token = models.CharField(max_length=50) @@ -34,3 +48,4 @@ class GoogleUserInfo(BaseModel): class Meta: verbose_name = "Google" verbose_name_plural = verbose_name +""" diff --git a/oauth/oauthmanager.py b/oauth/oauthmanager.py index 3fcd81f..477b568 100644 --- a/oauth/oauthmanager.py +++ b/oauth/oauthmanager.py @@ -14,6 +14,7 @@ """ from abc import ABCMeta, abstractmethod, abstractproperty +from oauth.models import oauthuser from django.conf import settings import requests import json @@ -192,7 +193,7 @@ class GitHubOauthManager(BaseOauthManager): 'client_id': self.client_id, 'response_type': 'code', 'redirect_uri': self.callback_url, - 'scope': 'user:email', + 'scope': 'user' } url = self.AUTH_URL + "?" + urllib.parse.urlencode(params, quote_via=urllib.parse.quote) return url @@ -208,16 +209,45 @@ class GitHubOauthManager(BaseOauthManager): } rsp = self.do_post(self.TOKEN_URL, params) print(rsp) - obj = json.loads(rsp) - self.access_token = str(obj['access_token']) - self.openid = str(obj['id_token']) + 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): - if not self.is_authorized: - return None + params = { 'access_token': self.access_token } rsp = self.do_get(self.API_URL, params) print(rsp) - return json.loads(rsp) + 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 diff --git a/oauth/templatetags/oauth_tags.py b/oauth/templatetags/oauth_tags.py index 26da1f7..eb37aab 100644 --- a/oauth/templatetags/oauth_tags.py +++ b/oauth/templatetags/oauth_tags.py @@ -12,7 +12,7 @@ @file: oauth_tags.py @time: 2017/3/4 下午3:22 """ -from oauth.oauthmanager import * +from oauth.oauthmanager import get_oauth_apps from django import template from django.conf import settings @@ -22,13 +22,8 @@ register = template.Library() @register.inclusion_tag('oauth/oauth_applications.html') def load_oauth_applications(): - applications = BaseOauthManager.__subclasses__() - apps = [] - for application in applications: - app = application() - icon = app.ICON_NAME - authorizeurl = app.get_authorization_url() - apps.append((icon, authorizeurl)) + applications = get_oauth_apps() + apps = list(map(lambda x: (x.ICON_NAME, x.get_authorization_url()), applications)) return { 'apps': apps } diff --git a/oauth/urls.py b/oauth/urls.py index b980ec2..6a43a66 100644 --- a/oauth/urls.py +++ b/oauth/urls.py @@ -17,6 +17,9 @@ from django.conf.urls import url from django.views.decorators.cache import cache_page from . import views +urlpatterns = [url(r'^oauth/authorize$', views.authorize), ] + +""" urlpatterns = [ url(r'^oauth/wbauthorize/(?P\w+)$', views.wbauthorize), url(r'^oauth/wboauthurl$', views.wboauthurl), @@ -24,3 +27,4 @@ urlpatterns = [ url(r'^oauth/googleoauthurl', views.googleoauthurl), url(r'^oauth/googleauthorize', views.googleauthorize), ] +""" diff --git a/oauth/views.py b/oauth/views.py index d9a0d81..f9d6f5e 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -1,14 +1,43 @@ from django.shortcuts import render # Create your views here. -from .oauthmanager import WBOauthManager, GoogleOauthManager +from .oauthmanager import WBOauthManager, GoogleOauthManager, get_manager_by_type from django.conf import settings from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth import get_user_model -from .models import GoogleUserInfo +from .models import oauthuser from django.contrib.auth import login +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() + author = None + if user: + email = user.email + if email: + author = get_user_model().objects.get(email=email) + if not author: + author = get_user_model().objects.create_user(username=user["name"], email=email) + user.author = author + user.save() + login(request, author) + return HttpResponseRedirect('/') + if not email: + author = get_user_model().objects.create_user(username=user["name"], email=email) + + +""" def wbauthorize(request, sitename): manager = WBOauthManager(client_id=settings.OAHUTH['sina']['appkey'], client_secret=settings.OAHUTH['sina']['appsecret'], @@ -55,4 +84,7 @@ def googleauthorize(request): userinfo.nikename = user["name"] userinfo.save() login(request, author) + else: + pass return HttpResponseRedirect('/') +""" From dcf7dd31d35b15440abeb17601636ea52bb82d84 Mon Sep 17 00:00:00 2001 From: liangliangyy Date: Tue, 7 Mar 2017 22:32:16 +0800 Subject: [PATCH 3/7] =?UTF-8?q?oauth=E9=82=AE=E7=AE=B1=E7=BB=91=E5=AE=9A?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DjangoBlog/utils.py | 16 +++++++ oauth/forms.py | 26 +++++++++++ oauth/models.py | 2 +- oauth/oauthmanager.py | 4 +- oauth/tests.py | 10 ---- oauth/urls.py | 5 +- oauth/views.py | 75 ++++++++++++++++++++++++++++-- templates/oauth/require_email.html | 46 ++++++++++++++++++ 8 files changed, 167 insertions(+), 17 deletions(-) create mode 100644 oauth/forms.py create mode 100644 templates/oauth/require_email.html diff --git a/DjangoBlog/utils.py b/DjangoBlog/utils.py index cdfcfda..7be4976 100644 --- a/DjangoBlog/utils.py +++ b/DjangoBlog/utils.py @@ -19,6 +19,8 @@ from pygments import highlight from pygments.lexers import get_lexer_by_name from pygments.formatters import html import logging +import _thread +from django.core.mail import EmailMultiAlternatives logger = logging.getLogger('djangoblog') from importlib import import_module @@ -120,3 +122,17 @@ class common_markdown(): mdp = mistune.Markdown(escape=True, renderer=renderer) 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,)) diff --git a/oauth/forms.py b/oauth/forms.py new file mode 100644 index 0000000..86851f8 --- /dev/null +++ b/oauth/forms.py @@ -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"}) diff --git a/oauth/models.py b/oauth/models.py index 5c0497d..4613a8c 100644 --- a/oauth/models.py +++ b/oauth/models.py @@ -4,7 +4,7 @@ from django.db import models from django.conf import settings -class oauthuser(models.Model): +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='昵称') diff --git a/oauth/oauthmanager.py b/oauth/oauthmanager.py index 477b568..66c0bbd 100644 --- a/oauth/oauthmanager.py +++ b/oauth/oauthmanager.py @@ -14,7 +14,7 @@ """ from abc import ABCMeta, abstractmethod, abstractproperty -from oauth.models import oauthuser +from oauth.models import OAuthUser from django.conf import settings import requests import json @@ -226,7 +226,7 @@ class GitHubOauthManager(BaseOauthManager): print(rsp) try: datas = json.loads(rsp) - user = oauthuser() + user = OAuthUser() user.picture = datas['avatar_url'] user.nikename = datas['name'] user.openid = datas['id'] diff --git a/oauth/tests.py b/oauth/tests.py index d65f3fd..7ce503c 100644 --- a/oauth/tests.py +++ b/oauth/tests.py @@ -1,13 +1,3 @@ from django.test import TestCase # 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']) \ No newline at end of file diff --git a/oauth/urls.py b/oauth/urls.py index 6a43a66..b14dbfd 100644 --- a/oauth/urls.py +++ b/oauth/urls.py @@ -17,7 +17,10 @@ from django.conf.urls import url from django.views.decorators.cache import cache_page from . import views -urlpatterns = [url(r'^oauth/authorize$', views.authorize), ] +urlpatterns = [ + url(r'^oauth/authorize$', views.authorize), + url(r'^oauth/requireemail/(?P\d+)', views.RequireEmailView.as_view(), name='require_email'), +] """ urlpatterns = [ diff --git a/oauth/views.py b/oauth/views.py index f9d6f5e..c44fab3 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -5,8 +5,12 @@ from .oauthmanager import WBOauthManager, GoogleOauthManager, get_manager_by_typ from django.conf import settings from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth import get_user_model -from .models import oauthuser +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 def authorize(request): @@ -25,16 +29,81 @@ def authorize(request): author = None if user: email = user.email + email = None if email: author = get_user_model().objects.get(email=email) if not author: - author = get_user_model().objects.create_user(username=user["name"], email=email) + author = get_user_model().objects.create_user(username=user.nikename, email=email) user.author = author user.save() login(request, author) return HttpResponseRedirect('/') if not email: - author = get_user_model().objects.create_user(username=user["name"], email=email) + author = get_user_model().objects.create_user(username=user.nikename) + user.author = author + user.save() + url = reverse('oauth:require_email', kwargs= + { + 'oauthid': user.id + }) + print(url) + return HttpResponseRedirect(url) + + +""" +def require_email(request, oauthid): + oauthuser = get_object_or_404(OAuthUser, pk=oauthid) + if 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) + from DjangoBlog.utils import send_email + url = '123' + content = """ +

请点击下面链接绑定您的邮箱

+ + {url} + + 再次感谢您! +
+ 如果上面链接无法打开,请将此链接复制至浏览器。 + {url} + """.format(url=url) + send_email('绑定您的电子邮箱', content, [email, ]) + return HttpResponseRedirect('/') """ diff --git a/templates/oauth/require_email.html b/templates/oauth/require_email.html new file mode 100644 index 0000000..a8ebb95 --- /dev/null +++ b/templates/oauth/require_email.html @@ -0,0 +1,46 @@ +{% extends 'share_layout/base_account.html' %} + +{% load static %} +{% block content %} +
+ + + + + +

+ Sign In +

+ +
+{% endblock %} \ No newline at end of file From 0d01e580385f54cbb04f0d091d622708631f2f74 Mon Sep 17 00:00:00 2001 From: liangliangyy Date: Sat, 11 Mar 2017 02:25:03 +0800 Subject: [PATCH 4/7] =?UTF-8?q?=E5=AE=8C=E5=96=84oauth=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=EF=BC=8C=E5=88=A0=E9=99=A4=E6=B3=A8=E9=87=8A=E6=8E=89=E7=9A=84?= =?UTF-8?q?=E6=97=A0=E7=94=A8=E4=BB=A3=E7=A0=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DjangoBlog/settings.py | 2 +- DjangoBlog/utils.py | 4 - blog/admin.py | 17 ++- blog/management/commands/create_testdata.py | 44 +++++++ blog/models.py | 64 ---------- blog/views.py | 134 ++------------------ comments/models.py | 3 - comments/views.py | 10 -- oauth/models.py | 38 +----- oauth/oauthmanager.py | 16 ++- oauth/urls.py | 1 + oauth/views.py | 73 +++++++++-- 12 files changed, 149 insertions(+), 257 deletions(-) create mode 100644 blog/management/commands/create_testdata.py diff --git a/DjangoBlog/settings.py b/DjangoBlog/settings.py index 6897111..73f0500 100644 --- a/DjangoBlog/settings.py +++ b/DjangoBlog/settings.py @@ -19,7 +19,7 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # 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! DEBUG = True diff --git a/DjangoBlog/utils.py b/DjangoBlog/utils.py index 7be4976..cae7626 100644 --- a/DjangoBlog/utils.py +++ b/DjangoBlog/utils.py @@ -23,10 +23,6 @@ import _thread from django.core.mail import EmailMultiAlternatives 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(): diff --git a/blog/admin.py b/blog/admin.py index 5c646ed..b4173d7 100644 --- a/blog/admin.py +++ b/blog/admin.py @@ -16,6 +16,11 @@ class ArticleForm(forms.ModelForm): class ArticlelAdmin(admin.ModelAdmin): 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): super(ArticlelAdmin, self).save_model(request, obj, form, change) @@ -23,8 +28,16 @@ class ArticlelAdmin(admin.ModelAdmin): cache.clear() +class TagAdmin(admin.ModelAdmin): + exclude = ('slug',) + + +class CategoryAdmin(admin.ModelAdmin): + exclude = ('slug',) + + admin.site.register(Article, ArticlelAdmin) # admin.site.register(BlogPage, ArticlelAdmin) -admin.site.register(Category) -admin.site.register(Tag) +admin.site.register(Category, CategoryAdmin) +admin.site.register(Tag, TagAdmin) admin.site.register(Links) diff --git a/blog/management/commands/create_testdata.py b/blog/management/commands/create_testdata.py new file mode 100644 index 0000000..aa41421 --- /dev/null +++ b/blog/management/commands/create_testdata.py @@ -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')) diff --git a/blog/models.py b/blog/models.py index 8331a9a..169df92 100644 --- a/blog/models.py +++ b/blog/models.py @@ -141,70 +141,6 @@ class Article(BaseModel): 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): """文章分类""" name = models.CharField('分类名', max_length=30, unique=True) diff --git a/blog/views.py b/blog/views.py index b53a9c2..067b0cb 100644 --- a/blog/views.py +++ b/blog/views.py @@ -29,33 +29,6 @@ from django.utils.decorators import classonlymethod from django.utils.decorators import method_decorator 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): # template_name属性用于指定使用哪个模板进行渲染 @@ -117,24 +90,6 @@ class IndexView(ArticleListView): cache_key = 'index_{page}'.format(page=self.page_number) 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): template_name = 'blog/article_detail.html' @@ -175,37 +130,6 @@ class ArticleDetailView(DetailView): 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): page_type = "分类目录归档" @@ -226,35 +150,8 @@ class CategoryDetailView(ArticleListView): cache_key = 'category_list_{categoryname}_{page}'.format(categoryname=categoryname, page=self.page_number) 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): - # slug = self.kwargs['category_name'] - # category = Category.objects.get(slug=slug) - # categoryname = category.name + categoryname = self.categoryname try: categoryname = categoryname.split('/')[-1] @@ -360,25 +257,18 @@ def refresh_memcache(request): """ -class BlogSearchView(SearchView): - form_class = BlogSearchForm - template_name = 'blog/article_detail.html' - model = Article - # template_name属性用于指定使用哪个模板进行渲染 - template_name = 'blog/article_index.html' +class SeoProcessor(): + __metaclass__ = ABCMeta - # context_object_name属性用于给上下文变量取名(在模板中使用该名字) - context_object_name = 'article_list' + @abstractmethod + def get_title(self): + pass - def get_queryset(self): - queryset = super(BlogSearchView, self).get_queryset() - # further filter queryset based on some set of criteria - # return queryset.filter(pub_date__gte=date(2015, 1, 1)) - return queryset + @abstractmethod + def get_keywords(self): + pass - def get_context_data(self, **kwargs): - tag_name = 'search' - kwargs['page_type'] = 'search' - kwargs['tag_name'] = tag_name - return super(BlogSearchView, self).get_context_data(**kwargs) + @abstractmethod + def get_description(self): + pass """ diff --git a/comments/models.py b/comments/models.py index 61650f3..106e5cf 100644 --- a/comments/models.py +++ b/comments/models.py @@ -11,9 +11,6 @@ from DjangoBlog.utils import cache # Create your models here. class Comment(models.Model): - # url = models.URLField('地址', blank=True, null=True) - # email = models.EmailField('电子邮件', blank=True, null=True) - body = models.TextField('正文', max_length=300) created_time = models.DateTimeField('创建时间', auto_now_add=True) last_mod_time = models.DateTimeField('修改时间', auto_now=True) diff --git a/comments/views.py b/comments/views.py index c5bcf10..787f397 100644 --- a/comments/views.py +++ b/comments/views.py @@ -9,17 +9,7 @@ from django.http import HttpResponseRedirect from django.contrib.auth import get_user_model 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): form_class = CommentForm template_name = 'blog/article_detail.html' diff --git a/oauth/models.py b/oauth/models.py index 4613a8c..33ec0da 100644 --- a/oauth/models.py +++ b/oauth/models.py @@ -8,44 +8,10 @@ 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=50) - picture = models.CharField(max_length=50, blank=True, null=True) + 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) def __str__(self): return self.nikename - - -""" -class BaseModel(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=50) - picture = models.CharField(max_length=50, blank=True, null=True) - - def __str__(self): - 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 -""" diff --git a/oauth/oauthmanager.py b/oauth/oauthmanager.py index 66c0bbd..3a49f66 100644 --- a/oauth/oauthmanager.py +++ b/oauth/oauthmanager.py @@ -172,8 +172,20 @@ class GoogleOauthManager(BaseOauthManager): 'access_token': self.access_token } rsp = self.do_get(self.API_URL, params) - print(rsp) - return json.loads(rsp) + try: + print(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): diff --git a/oauth/urls.py b/oauth/urls.py index b14dbfd..37d3542 100644 --- a/oauth/urls.py +++ b/oauth/urls.py @@ -20,6 +20,7 @@ from . import views urlpatterns = [ url(r'^oauth/authorize$', views.authorize), url(r'^oauth/requireemail/(?P\d+)', views.RequireEmailView.as_view(), name='require_email'), + url(r'^oauth/emailconfirm/(?P\d+)/(?P\S+)', views.emailconfirm, name='email_confirm'), ] """ diff --git a/oauth/views.py b/oauth/views.py index c44fab3..cc82d97 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -1,7 +1,7 @@ from django.shortcuts import render # Create your views here. -from .oauthmanager import WBOauthManager, GoogleOauthManager, get_manager_by_type + from django.conf import settings from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth import get_user_model @@ -11,6 +11,11 @@ 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): @@ -26,22 +31,33 @@ def authorize(request): if not rsp: return HttpResponseRedirect(manager.get_authorization_url()) user = manager.get_oauth_userinfo() - author = None + if user: + try: + user = OAuthUser.objects.get(type=type, openid=user.openid) + except ObjectDoesNotExist: + pass email = user.email - email = None if email: - author = get_user_model().objects.get(email=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, email=email) + 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: - author = get_user_model().objects.create_user(username=user.nikename) + # 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 @@ -50,13 +66,34 @@ def authorize(request): return HttpResponseRedirect(url) -""" -def require_email(request, oauthid): - oauthuser = get_object_or_404(OAuthUser, pk=oauthid) - if oauthuser.email: +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('恭喜您绑定成功!', ''' +

恭喜您,您已经成功绑定您的邮箱,您可以使用{type}来直接免密码登录本网站.欢迎您继续关注本站,地址是

+ + {url} + + 再次感谢您! +
+ 如果上面链接无法打开,请将此链接复制至浏览器。 + {url} + '''.format(type=oauthuser.type, url='http://' + site), [oauthuser.email, ]) + + return HttpResponseRedirect('/') class RequireEmailView(FormView): @@ -90,8 +127,18 @@ class RequireEmailView(FormView): email = form.cleaned_data['email'] oauthid = form.cleaned_data['oauthid'] oauthuser = get_object_or_404(OAuthUser, pk=oauthid) - from DjangoBlog.utils import send_email - url = '123' + 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 = """

请点击下面链接绑定您的邮箱

From 69819ff7a55fd1721f8cb6a213feb86c70962ebf Mon Sep 17 00:00:00 2001 From: liangliangyy Date: Sat, 11 Mar 2017 14:55:50 +0800 Subject: [PATCH 5/7] =?UTF-8?q?=E7=AC=AC=E4=B8=89=E6=96=B9=E7=99=BB?= =?UTF-8?q?=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DjangoBlog/settings.py | 2 +- blog/static/blog/css/style.css | 3 +++ templates/blog/article_detail.html | 12 +++++++++++- templates/oauth/oauth_applications.html | 9 ++++++--- templates/share_layout/base.html | 2 +- 5 files changed, 22 insertions(+), 6 deletions(-) diff --git a/DjangoBlog/settings.py b/DjangoBlog/settings.py index 73f0500..37bb161 100644 --- a/DjangoBlog/settings.py +++ b/DjangoBlog/settings.py @@ -26,7 +26,7 @@ DEBUG = True # DEBUG = False # ALLOWED_HOSTS = [] -ALLOWED_HOSTS = ['www.lylinux.net', '127.0.0.1'] +ALLOWED_HOSTS = ['www.lylinux.net', '127.0.0.1','example.com'] # Application definition INSTALLED_APPS = [ diff --git a/blog/static/blog/css/style.css b/blog/static/blog/css/style.css index 406ea31..45b560e 100755 --- a/blog/static/blog/css/style.css +++ b/blog/static/blog/css/style.css @@ -1998,6 +1998,9 @@ div { #wp-auto-top-bottom:hover { background-position: right -68px; } +.widget-login{ + margin-top: 15px!important; +} /* ------------------------------------------------------------------------- * * Comments diff --git a/templates/blog/article_detail.html b/templates/blog/article_detail.html index 653eaa3..65049b2 100755 --- a/templates/blog/article_detail.html +++ b/templates/blog/article_detail.html @@ -45,7 +45,17 @@ {% comment %}{% load comments_tags %} {% load_post_comment article from %}{% endcomment %} {% include 'comments/tags/comment_list.html' %} - {% include 'comments/tags/post_comment.html' %} + {% if user.is_authenticated %} + {% include 'comments/tags/post_comment.html' %} + {% else %} +
+

您还没有登录,请您登录后发表评论。

+ + {% load oauth_tags %} + {% load_oauth_applications %} + +
+ {% endif %} {% endif %} diff --git a/templates/oauth/oauth_applications.html b/templates/oauth/oauth_applications.html index c876343..7b83c8f 100644 --- a/templates/oauth/oauth_applications.html +++ b/templates/oauth/oauth_applications.html @@ -1,7 +1,10 @@ -
+ \ No newline at end of file +
diff --git a/templates/share_layout/base.html b/templates/share_layout/base.html index 3ee1107..05450de 100644 --- a/templates/share_layout/base.html +++ b/templates/share_layout/base.html @@ -17,7 +17,7 @@ {% block header %} {% endblock %} - {% comment %}{% endcomment %} + From 840878574dc61eddf3c895afd8666bbe6d149bf9 Mon Sep 17 00:00:00 2001 From: liangliangyy Date: Sat, 11 Mar 2017 15:20:32 +0800 Subject: [PATCH 6/7] =?UTF-8?q?=E6=B7=BB=E5=8A=A0nprogress=E8=BF=9B?= =?UTF-8?q?=E5=BA=A6=E6=9D=A1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- blog/static/blog/css/nprogress.css | 74 +++++ blog/static/blog/js/blog.js | 11 + blog/static/blog/js/nprogress.js | 480 +++++++++++++++++++++++++++++ templates/share_layout/base.html | 2 + 4 files changed, 567 insertions(+) create mode 100644 blog/static/blog/css/nprogress.css create mode 100644 blog/static/blog/js/nprogress.js diff --git a/blog/static/blog/css/nprogress.css b/blog/static/blog/css/nprogress.css new file mode 100644 index 0000000..90c7b6c --- /dev/null +++ b/blog/static/blog/css/nprogress.css @@ -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); } +} + diff --git a/blog/static/blog/js/blog.js b/blog/static/blog/js/blog.js index 4551434..a6fa29c 100644 --- a/blog/static/blog/js/blog.js +++ b/blog/static/blog/js/blog.js @@ -17,3 +17,14 @@ function cancel_reply() { $("#id_parent_comment_id").val('') $("#commentform").appendTo($("#respond")); } + +NProgress.start(); +NProgress.set(0.4); +//Increment +var interval = setInterval(function () { + NProgress.inc(); +}, 1000); +$(document).ready(function () { + NProgress.done(); + clearInterval(interval); +}); \ No newline at end of file diff --git a/blog/static/blog/js/nprogress.js b/blog/static/blog/js/nprogress.js new file mode 100644 index 0000000..beb9d2c --- /dev/null +++ b/blog/static/blog/js/nprogress.js @@ -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: '
' + }; + + /** + * 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; +}); diff --git a/templates/share_layout/base.html b/templates/share_layout/base.html index 05450de..0d19bf6 100644 --- a/templates/share_layout/base.html +++ b/templates/share_layout/base.html @@ -38,6 +38,7 @@ + {% block compress_css %} {% endblock %} {% endcompress %} @@ -45,6 +46,7 @@ {% endcomment %} {% compress js %} + {% endcompress %} From eb8e63a3d47bfdc645cc8c801994075ce8f306ee Mon Sep 17 00:00:00 2001 From: liangliangyy Date: Sat, 11 Mar 2017 15:45:13 +0800 Subject: [PATCH 7/7] =?UTF-8?q?=E2=80=9C=E4=BF=AE=E5=A4=8Durl=E7=BC=96?= =?UTF-8?q?=E7=A0=81=E9=94=99=E8=AF=AF=E2=80=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- DjangoBlog/utils.py | 7 +++++++ oauth/oauthmanager.py | 20 +++++++++++--------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/DjangoBlog/utils.py b/DjangoBlog/utils.py index cae7626..4326680 100644 --- a/DjangoBlog/utils.py +++ b/DjangoBlog/utils.py @@ -132,3 +132,10 @@ def send_email(subject, html_content, tomail): 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 diff --git a/oauth/oauthmanager.py b/oauth/oauthmanager.py index 3a49f66..c22a1ac 100644 --- a/oauth/oauthmanager.py +++ b/oauth/oauthmanager.py @@ -19,7 +19,7 @@ from django.conf import settings import requests import json import urllib.parse -from DjangoBlog.utils import logger +from DjangoBlog.utils import logger, parse_dict_to_url class BaseOauthManager(metaclass=ABCMeta): @@ -87,7 +87,7 @@ class WBOauthManager(BaseOauthManager): return url def get_access_token_by_code(self, code): - print(code) + params = { 'client_id': self.client_id, 'client_secret': self.client_secret, @@ -96,7 +96,7 @@ class WBOauthManager(BaseOauthManager): 'redirect_uri': self.callback_url } rsp = self.do_post(self.TOKEN_URL, params) - print(rsp) + # return rsp obj = json.loads(rsp) @@ -119,7 +119,7 @@ class WBOauthManager(BaseOauthManager): 'access_token': self.access_token } rsp = self.do_get(self.API_URL, params) - print(rsp) + class GoogleOauthManager(BaseOauthManager): @@ -141,7 +141,8 @@ class GoogleOauthManager(BaseOauthManager): 'redirect_uri': self.callback_url, '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 def get_access_token_by_code(self, code): @@ -173,7 +174,7 @@ class GoogleOauthManager(BaseOauthManager): } rsp = self.do_get(self.API_URL, params) try: - print(rsp) + datas = json.loads(rsp) user = OAuthUser() user.picture = datas['picture'] @@ -207,7 +208,8 @@ class GitHubOauthManager(BaseOauthManager): '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, quote_via=urllib.parse.quote) + url = self.AUTH_URL + "?" + urllib.parse.urlencode(params) return url def get_access_token_by_code(self, code): @@ -220,7 +222,7 @@ class GitHubOauthManager(BaseOauthManager): 'redirect_uri': self.callback_url } rsp = self.do_post(self.TOKEN_URL, params) - print(rsp) + try: from urllib import parse r = parse.parse_qs(rsp) @@ -235,7 +237,7 @@ class GitHubOauthManager(BaseOauthManager): 'access_token': self.access_token } rsp = self.do_get(self.API_URL, params) - print(rsp) + try: datas = json.loads(rsp) user = OAuthUser()