diff --git a/.gitignore b/.gitignore index df0809f..9aa5e14 100644 --- a/.gitignore +++ b/.gitignore @@ -76,3 +76,4 @@ werobot_session django.jpg uploads/ settings_production.py +werobot_session.db diff --git a/.travis.yml b/.travis.yml index dcd2f41..422693b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -2,6 +2,12 @@ language: python python: - "3.5" - "3.6" +matrix: + include: + - python: "3.7" + dist: xenial + allow_failures: + - python: "3.7" services: - mysql env: @@ -29,4 +35,4 @@ script: - coverage run manage.py test after_success: - coveralls - - codecov \ No newline at end of file + - codecov diff --git a/DjangoBlog/elasticsearch_backend.py b/DjangoBlog/elasticsearch_backend.py index 5356eac..c5ca846 100644 --- a/DjangoBlog/elasticsearch_backend.py +++ b/DjangoBlog/elasticsearch_backend.py @@ -31,7 +31,10 @@ class ElasticSearchBackend(BaseSearchBackend): def __init__(self, connection_alias, **connection_options): super(ElasticSearchBackend, self).__init__(connection_alias, **connection_options) self.manager = ArticleDocumentManager() - self._rebuild(None) + try: + self._rebuild(None) + except: + pass def _get_models(self, iterable): models = iterable if iterable else Article.objects.all() @@ -54,6 +57,7 @@ class ElasticSearchBackend(BaseSearchBackend): self.manager.update_docs(docs) def update(self, index, iterable, commit=True): + models = self._get_models(iterable) self.manager.update_docs(models) diff --git a/DjangoBlog/urls.py b/DjangoBlog/urls.py index 442d559..a760104 100644 --- a/DjangoBlog/urls.py +++ b/DjangoBlog/urls.py @@ -46,6 +46,7 @@ urlpatterns = [ url(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, name='django.contrib.sitemaps.views.sitemap'), url(r'^feed/$', DjangoBlogFeed()), + url(r'^rss/$', DjangoBlogFeed()), url(r'^search', include('haystack.urls'), name='search'), url(r'', include('servermanager.urls', namespace='servermanager')), url(r'', include('owntracks.urls', namespace='owntracks')) diff --git a/DjangoBlog/whoosh_cn_backend.py b/DjangoBlog/whoosh_cn_backend.py index 52e304a..d8b43cc 100644 --- a/DjangoBlog/whoosh_cn_backend.py +++ b/DjangoBlog/whoosh_cn_backend.py @@ -624,7 +624,7 @@ class WhooshSearchBackend(BaseSearchBackend): if string_key in index.fields and hasattr(index.fields[string_key], 'convert'): # Special-cased due to the nature of KEYWORD fields. if index.fields[string_key].is_multivalued: - if value is None or len(value) is 0: + if value is None or len(value) == 0: additional_fields[string_key] = [] else: additional_fields[string_key] = value.split(',') diff --git a/README-en.md b/README-en.md index 8c31168..5ad8d41 100644 --- a/README-en.md +++ b/README-en.md @@ -109,7 +109,7 @@ Execute: `./manage.py runserver` Open up a browser and visit: http://127.0.0.1:8000/ , the you will see the blog. ## More configurations -[More configurations details](/bin/config.md) +[More configurations details](/docs/config-en.md) ## About the issues diff --git a/README.md b/README.md index 9e24346..a29611e 100644 --- a/README.md +++ b/README.md @@ -111,7 +111,7 @@ CREATE DATABASE `djangoblog` /*!40100 DEFAULT CHARACTER SET utf8mb4 COLLATE utf8 浏览器打开: http://127.0.0.1:8000/ 就可以看到效果了。 ## 更多配置: -[更多配置介绍](/bin/config.md) +[更多配置介绍](/docs/config.md) ## 问题相关 diff --git a/accounts/admin.py b/accounts/admin.py index eebb165..7333b8d 100644 --- a/accounts/admin.py +++ b/accounts/admin.py @@ -5,6 +5,8 @@ from django.contrib.auth.forms import UserCreationForm, UserChangeForm from django.contrib.auth.forms import ReadOnlyPasswordHashField # Register your models here. from .models import BlogUser +from django.utils.translation import gettext, gettext_lazy as _ +from django.contrib.auth.forms import UsernameField class BlogUserCreationForm(forms.ModelForm): @@ -28,23 +30,29 @@ class BlogUserCreationForm(forms.ModelForm): user = super().save(commit=False) user.set_password(self.cleaned_data["password1"]) if commit: + user.source = 'adminsite' user.save() return user -class BlogUserChangeForm(forms.ModelForm): - password = ReadOnlyPasswordHashField +class BlogUserChangeForm(UserChangeForm): + password = ReadOnlyPasswordHashField( + label=_("Password"), + help_text=_( + "Raw passwords are not stored, so there is no way to see this " + "user's password, but you can change the password using " + "this form." + ), + ) email = forms.EmailField(label="Email", widget=forms.EmailInput) class Meta: model = BlogUser - fields = ('email', 'password', 'is_active') + fields = '__all__' + field_classes = {'username': UsernameField} - def clean_password(self): - # Regardless of what the user provides, return the initial value. - # This is done here, rather than on the field, because the - # field does not have access to the initial value - return self.initial["password"] + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) class BlogUserAdmin(UserAdmin): diff --git a/accounts/models.py b/accounts/models.py index 12b4f61..b7d4025 100644 --- a/accounts/models.py +++ b/accounts/models.py @@ -11,6 +11,7 @@ class BlogUser(AbstractUser): nickname = models.CharField('昵称', max_length=100, blank=True) created_time = models.DateTimeField('创建时间', default=now) last_mod_time = models.DateTimeField('修改时间', default=now) + source = models.CharField("创建涞源", max_length=100, blank=True) # objects = BlogUserManager() diff --git a/accounts/views.py b/accounts/views.py index 4845782..77a5e4c 100644 --- a/accounts/views.py +++ b/accounts/views.py @@ -33,6 +33,7 @@ class RegisterView(FormView): if form.is_valid(): user = form.save(False) user.is_active = False + user.source = 'Register' user.save(True) site = get_current_site().domain sign = get_md5(get_md5(settings.SECRET_KEY + str(user.id))) @@ -106,7 +107,7 @@ class LoginView(FormView): if cache and cache is not None: cache.clear() logger.info(self.redirect_field_name) - redirect_to = self.request.GET.get(self.redirect_field_name) + auth.login(self.request, form.get_user()) return super(LoginView, self).form_valid(form) # return HttpResponseRedirect('/') diff --git a/docs/config-en.md b/docs/config-en.md new file mode 100644 index 0000000..b877efb --- /dev/null +++ b/docs/config-en.md @@ -0,0 +1,64 @@ +# Introduction to main features settings + +## Cache: +Cache using `memcache` for default. If you don't have `memcache` environment, you can remove the `default` setting in `CACHES` and change `locmemcache` to `default`. +```python +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': '127.0.0.1:11211', + 'KEY_PREFIX': 'django_test' if TESTING else 'djangoblog', + 'TIMEOUT': 60 * 60 * 10 + }, + 'locmemcache': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'TIMEOUT': 10800, + 'LOCATION': 'unique-snowflake', + } +} +``` + +## OAuth Login: +QQ, Weibo, Google, GitHub and Facebook are now supported for OAuth login. Fetch OAuth login permissions from the corresponding open platform, and save them with `appkey`, `appsecret` and callback address in **Backend->OAuth** configuration. + +### Callback address examples: +QQ: http://your-domain-name/oauth/authorize?type=qq +Weibo: http://your-domain-name/oauth/authorize?type=weibo +type is in the type field of `oauthmanager`. + +## owntracks: +owntracks is a location tracking application. It will send your locaiton to the server by timing.Simple support owntracks features. Just install owntracks app and set api address as `your-domain-name/owntracks/logtracks`. Visit `your-domain-name/owntracks/show_dates` and you will see the date with latitude and langitude, click it and see the motion track. The map is drawn by AMap. + +## Email feature: +Same as before, Configure your own error msg recvie email information with`ADMINS = [('liangliang', 'liangliangyy@gmail.com')]` in `settings.py`. And modify: +```python +EMAIL_HOST = 'smtp.zoho.com' +EMAIL_PORT = 587 +EMAIL_HOST_USER = os.environ.get('DJANGO_EMAIL_USER') +EMAIL_HOST_PASSWORD = os.environ.get('DJANGO_EMAIL_PASSWORD') +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER +SERVER_EMAIL = os.environ.get('DJANGO_EMAIL_USER') +``` +with your email account information. + +## WeChat Official Account +Simple wechat official account features integrated. Set token as `your-domain-name/robot` in wechat backend. Default token is `lylinux`, you can change it to your own in `servermanager/robot.py`. Add a new command in `Backend->Servermanager->command`, in this way, you can manage the system through wechat official account. + +## Introduction to website configuration +You can add website configuration in **Backend->BLOG->WebSiteConfiguration**. Such as: keywords, description, Google Ad, website stats code, case number, etc. +OAuth user avatar path is saved in *StaticFileSavedAddress*. Please input absolute path, code directory for default. + +## Source code highlighting +If the code block in your article didn't show hightlight, please write the code blocks as following: + +![](https://resource.lylinux.net/image/codelang.png) + +That is, you should add the corresponding language name before the code block. + +## Update +If you get errors as following while executing database migrations: +```python +django.db.migrations.exceptions.MigrationSchemaMissing: Unable to create the django_migrations table ((1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(6) NOT NULL)' at line 1")) +``` +This problem may cause by the mysql version under 5.6, a new version( >= 5.6 ) mysql is needed. + diff --git a/bin/config.md b/docs/config.md similarity index 95% rename from bin/config.md rename to docs/config.md index 76dac74..73555dc 100644 --- a/bin/config.md +++ b/docs/config.md @@ -19,7 +19,7 @@ CACHES = { ``` ## oauth登录: -现在已经支持微博,Google,GitHub,Facebook登录,需要在其对应的开放平台申请oauth登录权限,然后在 +现在已经支持QQ,微博,Google,GitHub,Facebook登录,需要在其对应的开放平台申请oauth登录权限,然后在 **后台->Oauth** 配置中新增配置,填写对应的`appkey`和`appsecret`以及回调地址。 ### 回调地址示例: qq:http://你的域名/oauth/authorize?type=qq diff --git a/oauth/views.py b/oauth/views.py index 9c828dc..51aef0a 100644 --- a/oauth/views.py +++ b/oauth/views.py @@ -2,6 +2,7 @@ from django.shortcuts import render # Create your views here. from urllib.parse import urlparse +import datetime from django.conf import settings from django.http import HttpResponse, HttpResponseRedirect from django.contrib.auth import get_user_model @@ -70,7 +71,7 @@ def authorize(request): return HttpResponseRedirect(manager.get_authorization_url(nexturl)) user = manager.get_oauth_userinfo() if user: - if not user.nikename: + if not user.nikename.strip(): import datetime user.nikename = "djangoblog" + datetime.datetime.now().strftime('%y%m%d%I%M%S') try: @@ -96,6 +97,7 @@ def authorize(request): author = result[0] if result[1]: author.username = user.nikename + author.source = 'authorize' author.save() user.author = author @@ -127,7 +129,9 @@ def emailconfirm(request, id, sign): result = get_user_model().objects.get_or_create(email=oauthuser.email) author = result[0] if result[1]: - author.username = oauthuser.nikename + author.source = 'emailconfirm' + author.username = oauthuser.nikename.strip() if oauthuser.nikename.strip() else "djangoblog" + datetime.datetime.now().strftime( + '%y%m%d%I%M%S') author.save() oauthuser.author = author oauthuser.save() diff --git a/requirements.txt b/requirements.txt index ca731ee..e3c7892 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,27 +1,26 @@ appdirs==1.4.3 asn1crypto==0.24.0 astroid==2.2.5 -bottle==0.12.16 +bottle==0.12.17 certifi==2019.6.16 cffi==1.12.3 chardet==3.0.4 -colorama==0.4.1 coverage==4.5.3 cryptography==2.7 -Django==2.2.2 +Django==2.2.3 django-appconf==1.0.3 django-autoslug==1.9.4 django-compressor==2.3 -django-debug-toolbar==1.11 +django-debug-toolbar==2.0 django-haystack==2.8.1 django-ipware==2.1.0 django-mdeditor==0.1.14 django-uuslug==1.1.8 -elasticsearch==7.0.2 +elasticsearch==7.0.0 elasticsearch-dsl==7.0.0 idna==2.8 ipaddress==1.0.22 -isort==4.3.20 +isort==4.3.21 jieba==0.39 jsonpickle==1.2 lazy-object-proxy==1.4.1 @@ -48,11 +47,11 @@ rjsmin==1.1.0 six==1.12.0 sqlparse==0.3.0 text-unidecode==1.2 -typed-ast==1.4.0 -Unidecode==1.1.0 +typed-ast==1.3.1 +Unidecode==1.1.1 urllib3==1.25.3 webencodings==0.5.1 WeRoBot==1.9.0 Whoosh==2.7.4 -wrapt==1.11.1 +wrapt==1.11.2 xmltodict==0.12.0 diff --git a/travis_test/requirements.txt b/travis_test/requirements.txt index 643c5d7..f2649db 100644 --- a/travis_test/requirements.txt +++ b/travis_test/requirements.txt @@ -1,27 +1,26 @@ appdirs==1.4.3 asn1crypto==0.24.0 astroid==2.2.5 -bottle==0.12.16 +bottle==0.12.17 certifi==2019.6.16 cffi==1.12.3 chardet==3.0.4 -colorama==0.4.1 coverage==4.5.3 cryptography==2.7 -Django==2.2.2 +Django==2.2.3 django-appconf==1.0.3 django-autoslug==1.9.4 django-compressor==2.3 -django-debug-toolbar==1.11 +django-debug-toolbar==2.0 django-haystack==2.8.1 django-ipware==2.1.0 django-mdeditor==0.1.14 django-uuslug==1.1.8 -elasticsearch==7.0.2 +elasticsearch==7.0.0 elasticsearch-dsl==7.0.0 idna==2.8 ipaddress==1.0.22 -isort==4.3.20 +isort==4.3.21 jieba==0.39 jsonpickle==1.2 lazy-object-proxy==1.4.1 @@ -47,11 +46,11 @@ rjsmin==1.1.0 six==1.12.0 sqlparse==0.3.0 text-unidecode==1.2 -typed-ast==1.4.0 -Unidecode==1.1.0 +typed-ast==1.3.1 +Unidecode==1.1.1 urllib3==1.25.3 webencodings==0.5.1 WeRoBot==1.9.0 Whoosh==2.7.4 -wrapt==1.11.1 +wrapt==1.11.2 xmltodict==0.12.0