Forráskód Böngészése

修改了目录没有正常显示的bug

Shellmiao 4 éve
szülő
commit
a497abc20e

+ 1 - 1
.idea/MyBlog.iml

@@ -2,7 +2,7 @@
 <module type="PYTHON_MODULE" version="4">
   <component name="NewModuleRootManager">
     <content url="file://$MODULE_DIR$" />
-    <orderEntry type="inheritedJdk" />
+    <orderEntry type="jdk" jdkName="Python 3.8" jdkType="Python SDK" />
     <orderEntry type="sourceFolder" forTests="false" />
   </component>
   <component name="TemplatesService">

+ 23 - 0
MyBlog/MyBlog/settings.py

@@ -35,6 +35,7 @@ INSTALLED_APPS = [
     'django.contrib.sessions',
     'django.contrib.messages',
     'django.contrib.staticfiles',
+    'django.contrib.sites',
     'article',
     'userprofile',
     'password_reset',
@@ -42,6 +43,13 @@ INSTALLED_APPS = [
     'taggit',
     'ckeditor',
     'mptt',
+    'notifications',
+    'notice',
+    'allauth',
+    'allauth.account',
+    'allauth.socialaccount',
+    'allauth.socialaccount.providers.github',
+    'allauth.socialaccount.providers.weibo',
 ]
 X_FRAME_OPTIONS = 'SAMEORIGIN'
 MIDDLEWARE = [
@@ -67,6 +75,8 @@ TEMPLATES = [
                 'django.template.context_processors.request',
                 'django.contrib.auth.context_processors.auth',
                 'django.contrib.messages.context_processors.messages',
+                # allauth 启动必须项
+                'django.template.context_processors.request',
             ],
         },
     },
@@ -168,3 +178,16 @@ CKEDITOR_CONFIGS = {
         'extraPlugins': ','.join(['codesnippet', 'prism', 'widget', 'lineutils']),
     }
 }
+
+AUTHENTICATION_BACKENDS = (
+    # Django 后台可独立于 allauth 登录
+    'django.contrib.auth.backends.ModelBackend',
+
+    # 配置 allauth 独有的认证方法,如 email 登录
+    'allauth.account.auth_backends.AuthenticationBackend',
+)
+# 设置站点
+SITE_ID = 1
+
+# 登录成功后重定向地址
+LOGIN_REDIRECT_URL = '/'

+ 7 - 0
MyBlog/MyBlog/urls.py

@@ -17,12 +17,19 @@ from django.contrib import admin
 from django.urls import path, include
 from django.conf import settings
 from django.conf.urls.static import static
+import notifications.urls
+from article.views import article_list
 
 urlpatterns = [
+    # 首页
+    path('', article_list, name='home'),
     path('admin/', admin.site.urls),
     path('article/', include('article.urls', namespace='article')),
     path('user/', include('userprofile.urls', namespace='userprofile')),
     path('password_reset/', include('password_reset.urls')),
     path('comment/', include('comment.urls', namespace='comment')),
+    path('inbox/notifications/', include(notifications.urls, namespace='notifications')),
+    path('notice/', include('notice.urls', namespace='notice')),
+    path('accounts/', include('allauth.urls'))
 ]
 urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

+ 46 - 0
MyBlog/article/migrations/0001_initial.py

@@ -0,0 +1,46 @@
+# Generated by Django 3.1.1 on 2021-01-31 06:21
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import taggit.managers
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('taggit', '0003_taggeditem_add_unique_index'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='ArticleColumn',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(blank=True, max_length=100)),
+                ('created', models.DateTimeField(default=django.utils.timezone.now)),
+            ],
+        ),
+        migrations.CreateModel(
+            name='ArticlePost',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=100)),
+                ('body', models.TextField()),
+                ('created', models.DateTimeField(default=django.utils.timezone.now)),
+                ('updated', models.DateTimeField(default=django.utils.timezone.now)),
+                ('total_views', models.PositiveIntegerField(default=0)),
+                ('avatar', models.ImageField(blank=True, upload_to='article/%Y%m%d/')),
+                ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
+                ('column', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='article', to='article.articlecolumn')),
+                ('tags', taggit.managers.TaggableManager(blank=True, help_text='A comma-separated list of tags.', through='taggit.TaggedItem', to='taggit.Tag', verbose_name='Tags')),
+            ],
+            options={
+                'ordering': ('-created',),
+            },
+        ),
+    ]

+ 0 - 0
MyBlog/article/migrations/__init__.py


+ 42 - 0
MyBlog/comment/migrations/0001_initial.py

@@ -0,0 +1,42 @@
+# Generated by Django 3.1.1 on 2021-01-31 06:21
+
+import ckeditor.fields
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+import django.utils.timezone
+import mptt.fields
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        ('article', '0001_initial'),
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Comment',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('body', ckeditor.fields.RichTextField()),
+                ('created', models.DateTimeField(default=django.utils.timezone.now)),
+                ('updated', models.DateTimeField(default=django.utils.timezone.now)),
+                ('total_views', models.PositiveIntegerField(default=0)),
+                ('lft', models.PositiveIntegerField(editable=False)),
+                ('rght', models.PositiveIntegerField(editable=False)),
+                ('tree_id', models.PositiveIntegerField(db_index=True, editable=False)),
+                ('level', models.PositiveIntegerField(editable=False)),
+                ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comment', to='article.articlepost')),
+                ('parent', mptt.fields.TreeForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='children', to='comment.comment')),
+                ('reply_to', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='replyers', to=settings.AUTH_USER_MODEL)),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='comment', to=settings.AUTH_USER_MODEL)),
+            ],
+            options={
+                'abstract': False,
+            },
+        ),
+    ]

+ 0 - 0
MyBlog/comment/migrations/__init__.py


+ 19 - 1
MyBlog/comment/views.py

@@ -1,7 +1,8 @@
 from django.shortcuts import render, get_object_or_404, redirect
 from django.http import HttpResponse
 from django.contrib.auth.decorators import login_required
-
+from django.contrib.auth.models import User
+from notifications.signals import notify
 from article.models import ArticlePost
 from .forms import CommentPost
 from .models import Comment
@@ -24,8 +25,25 @@ def post_comment(request, article_id, parent_comment_id=None):
                 # 被回复人
                 new_comment_form.reply_to = parent_comment.user
                 new_comment_form.save()
+                if not parent_comment.user.is_superuser:
+                    notify.send(
+                        request.user,
+                        recipient=parent_comment.user,
+                        verb='回复了你',
+                        target=article,
+                        action_object=new_comment_form,
+                    )
                 return HttpResponse('200 OK')
             new_comment_form.save()
+            # 给管理员发送通知
+            if not request.user.is_superuser:
+                notify.send(
+                    request.user,
+                    recipient=User.objects.filter(is_superuser=1),
+                    verb='回复了你',
+                    target=article,
+                    action_object=new_comment_form,
+                )
             return redirect(article)
         else:
             return HttpResponse('表单有错误,请重新填写')

BIN
MyBlog/db.sqlite3


+ 0 - 0
MyBlog/notice/__init__.py


+ 3 - 0
MyBlog/notice/admin.py

@@ -0,0 +1,3 @@
+from django.contrib import admin
+
+# Register your models here.

+ 5 - 0
MyBlog/notice/apps.py

@@ -0,0 +1,5 @@
+from django.apps import AppConfig
+
+
+class NoticeConfig(AppConfig):
+    name = 'notice'

+ 0 - 0
MyBlog/notice/migrations/__init__.py


+ 3 - 0
MyBlog/notice/models.py

@@ -0,0 +1,3 @@
+from django.db import models
+
+# Create your models here.

+ 3 - 0
MyBlog/notice/tests.py

@@ -0,0 +1,3 @@
+from django.test import TestCase
+
+# Create your tests here.

+ 11 - 0
MyBlog/notice/urls.py

@@ -0,0 +1,11 @@
+from django.urls import path
+from . import views
+
+app_name = 'notice'
+
+urlpatterns = [
+    # 通知列表
+    path('list/', views.CommentNoticeListView.as_view(), name='list'),
+    # 更新通知状态
+    path('update/', views.CommentNoticeUpdateView.as_view(), name='update'),
+]

+ 35 - 0
MyBlog/notice/views.py

@@ -0,0 +1,35 @@
+from django.shortcuts import render, redirect
+from django.views import View
+from django.views.generic import ListView
+from django.contrib.auth.mixins import LoginRequiredMixin
+from article.models import ArticlePost
+
+
+class CommentNoticeListView(LoginRequiredMixin, ListView):
+    # 通知列表
+    # 上下文的名称
+    context_object_name = 'notices'
+    # 模板位置
+    template_name = 'notice/list.html'
+    # 登录重定向
+    login_url = '/user/login/'
+
+    # 未读通知的查询集
+    def get_queryset(self):
+        return self.request.user.notifications.unread()
+
+
+class CommentNoticeUpdateView(View):
+    # 更新通知状态
+    # 处理get请求
+    def get(self, request):
+        # 获取未读消息
+        notice_id = request.GET.get('notice_id')
+        # 更新单条通知
+        if notice_id:
+            article = ArticlePost.objects.get(id=request.GET.get('article_id'))
+            request.user.notifications.get(id=notice_id).mark_as_read()
+            return redirect(article)
+        else:
+            request.user.notifications.mark_all_as_read()
+            return redirect('notice:list')

+ 71 - 0
MyBlog/templates/account/login.html

@@ -0,0 +1,71 @@
+{% extends "base.html" %}
+{% load i18n %}
+{% load account socialaccount %}
+{% block title %}登录{% endblock %}
+
+{% block content %}
+    <div class="container">
+        <div class="row">
+            <div class="col-12">
+                <br>
+                {% get_providers as socialaccount_providers %}
+                {% if socialaccount_providers %}
+                    <p>
+                        {% blocktrans with site.name as site_name %}请登录已有本地账号或<a href="{{ signup_url }}">注册</a>新账号。
+                            也可以通过第三方登录:{% endblocktrans %}
+                    </p>
+
+                    <div class="socialaccount_ballot">
+                        <h5 class="mb-2 mt-4">第三方登录:</h5>
+                        <ul class="socialaccount_providers">
+                            {% include "socialaccount/snippets/provider_list.html" with process="login" %}
+                        </ul>
+                        <h5 class="mb-2 mt-4">本地登录:</h5>
+                    </div>
+
+                    {% include "socialaccount/snippets/login_extra.html" %}
+
+                {% else %}
+                    <p>{% blocktrans %}If you have not created an account yet, then please
+                        <a href="{{ signup_url }}">sign up</a> first.{% endblocktrans %}</p>
+                {% endif %}
+                <div class="col-6">
+                    <form class="login" id="login_form" method="POST" action="{% url 'account_login' %}">
+                        {% csrf_token %}
+                        <div class="form-group">
+                            <label for="id_login">账号: </label>
+                            <input type="text" name="login" placeholder="请输入用户名或Email" autofocus="autofocus" required
+                                   id="id_login" class="form-control"/>
+                            <small class="form-text text-muted ml-1">
+                                还没有账号?
+                                <a href="{% url 'account_signup' %}" style="color: cornflowerblue; ">
+                                    注册新账号
+                                </a>
+                            </small>
+                        </div>
+                        <div class="form-group mb-1">
+                            <label for="id_password">
+                                密码:
+                            </label>
+                            <input type="password" name="password" placeholder="请输入密码" required id="id_password"
+                                   class="form-control"/>
+                            <small class="form-text text-muted ml-1">
+                                <a class="secondaryAction layui-text" href="{% url 'account_reset_password' %}">
+                                    忘记密码?
+                                </a>
+                            </small>
+                        </div>
+                        <div class="custom-control custom-checkbox mb-2">
+                            <input type="checkbox" name="remember" id="id_remember" checked
+                                   class="custom-control-input"/>
+                            <label for="id_remember" class="custom-control-label">
+                                保持登录
+                            </label>
+                        </div>
+                        <button class="primaryAction btn btn-primary" type="submit" id="submit_login">确认</button>
+                    </form>
+                </div>
+            </div>
+        </div>
+    </div>
+{% endblock %}

+ 1 - 1
MyBlog/templates/article/detail.html

@@ -11,7 +11,7 @@
                     <h4><strong>目录</strong></h4>
                     <hr>
                     <div>
-                        {{ tor|safe }}
+                        {{ toc|safe }}
                     </div>
                 </div>
             </div>

+ 13 - 0
MyBlog/templates/header.html

@@ -21,9 +21,22 @@
                     <li class="nav-item dropdown">
                         <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button"
                            data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
+                            {% if unread_count %}
+                                <svg viewBox="0 0 8 8"
+                                     width="8px"
+                                     height="8px">
+                                    <circle cx="4" cy="4" r="4" fill="#ff6b6b"></circle>
+                                </svg>
+                            {% endif %}
                             {{ user.username }}
                         </a>
                         <div class="dropdown-menu" aria-labelledby="navbarDropdown">
+                            <!--通知的计数-->
+                            <a class="dropdown-item" href="{% url 'notice:list' %}">通知
+                                {% if unread_count %}
+                                    <span class="badge badge-danger">{{ unread_count }}</span>
+                                {% endif %}
+                            </a>
                             <a class="dropdown-item" href="{% url 'userprofile:edit' user.id %}">个人信息</a>
                             <a class="dropdown-item" href="{% url 'userprofile:logout' %}">退出登录</a>
                             <a class="dropdown-item" href="#" onclick="user_delete()">删除用户</a>

+ 39 - 0
MyBlog/templates/notice/list.html

@@ -0,0 +1,39 @@
+{% extends "base.html" %}
+{% load static %}
+{% block title %}
+    通知
+{% endblock title %}
+{% block content %}
+    <div class="container">
+        <div class="row mt-4 ml-4">
+            <a href="{% url 'notice:update' %}" class="btn btn-warning" role="button">清空所有通知</a>
+        </div>
+        <!--未读通知列表-->
+        <div class="row mt-2 ml-4">
+            <ul class="list-group">
+                {% for notice in notices %}
+                    <li class="list-group-item" id="notice_link">
+                        <a href="{% url 'notice:update' %}?article_id={{ notice.target.id }}&notice_id={{ notice.id }}"
+                           target="_blank">
+                        <span style="color: #5897fb;">
+                            {{ notice.actor }}
+                        </span>
+                            在<span style="color: #01a252;">{{ notice.target }}</span>{{ notice.verb }}。
+                        </a>
+                        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{{ notice.timestamp|date:"Y/m/d H:i" }}
+                    </li>
+                {% endfor %}
+            </ul>
+        </div>
+    </div>
+
+    <style>
+        #notice_link a:link {
+            color: black;
+        }
+
+        #notice_link a:visited {
+            color: lightgray;
+        }
+    </style>
+{% endblock content %}

+ 27 - 0
MyBlog/userprofile/migrations/0001_initial.py

@@ -0,0 +1,27 @@
+# Generated by Django 3.1.1 on 2021-01-31 06:21
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name='Profile',
+            fields=[
+                ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('phone', models.CharField(blank=True, max_length=20)),
+                ('avatar', models.ImageField(blank=True, upload_to='avatar/%Y%m%d/')),
+                ('bio', models.TextField(blank=True, max_length=500)),
+                ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='profile', to=settings.AUTH_USER_MODEL)),
+            ],
+        ),
+    ]

+ 0 - 0
MyBlog/userprofile/migrations/__init__.py