CJ před 1 rokem
revize
81b5377b04
100 změnil soubory, kde provedl 1588 přidání a 0 odebrání
  1. 8 0
      .idea/.gitignore
  2. 23 0
      .idea/CyberSicko.iml
  3. 12 0
      .idea/dataSources.xml
  4. 24 0
      .idea/inspectionProfiles/Project_Default.xml
  5. 6 0
      .idea/inspectionProfiles/profiles_settings.xml
  6. 4 0
      .idea/misc.xml
  7. 8 0
      .idea/modules.xml
  8. 6 0
      .idea/vcs.xml
  9. binární
      Pillow-8.4.0-cp310-cp310-win32.whl
  10. binární
      Pillow-8.4.0-cp310-cp310-win_amd64.whl
  11. 14 0
      Pipfile
  12. 148 0
      Pipfile.lock
  13. binární
      __pycache__/manage.cpython-310.pyc
  14. binární
      avatars/1/093b8f4a58bce8db03b321f26797bc44.jpg
  15. binární
      avatars/1/093b8f4a58bce8db03b321f26797bc44_vpMX8f2.jpg
  16. binární
      avatars/1/77d4e5d40fdaeb321eadd5cb855cb685.gif
  17. binární
      avatars/1/l4kz0sz0xe2.jpg
  18. binární
      avatars/1/resized/50/50/l4kz0sz0xe2.png
  19. binární
      avatars/1/resized/50/50/v2-e2efcba214c980e079aca377e03b8ec6_xll.png
  20. binární
      avatars/1/resized/65/50/77d4e5d40fdaeb321eadd5cb855cb685.png
  21. binární
      avatars/1/resized/65/50/l4kz0sz0xe2.png
  22. binární
      avatars/1/resized/65/50/v2-e2efcba214c980e079aca377e03b8ec6_xll.png
  23. binární
      avatars/1/resized/80/80/093b8f4a58bce8db03b321f26797bc44.png
  24. binární
      avatars/1/v2-e2efcba214c980e079aca377e03b8ec6_xll.jpg
  25. binární
      avatars/1/wallhaven-5gdp69.jpg
  26. binární
      avatars/1/wallhaven-5gdp69_2.jpg
  27. 0 0
      blog/__init__.py
  28. binární
      blog/__pycache__/__init__.cpython-310.pyc
  29. binární
      blog/__pycache__/admin.cpython-310.pyc
  30. binární
      blog/__pycache__/apps.cpython-310.pyc
  31. binární
      blog/__pycache__/context_processors.cpython-310.pyc
  32. binární
      blog/__pycache__/models.cpython-310.pyc
  33. binární
      blog/__pycache__/views.cpython-310.pyc
  34. 3 0
      blog/admin.py
  35. 6 0
      blog/apps.py
  36. 26 0
      blog/backends.py
  37. 13 0
      blog/context_processors.py
  38. binární
      blog/controller/__pycache__/article.cpython-310.pyc
  39. binární
      blog/controller/__pycache__/category.cpython-310.pyc
  40. binární
      blog/controller/__pycache__/files.cpython-310.pyc
  41. binární
      blog/controller/__pycache__/login.cpython-310.pyc
  42. binární
      blog/controller/__pycache__/tag.cpython-310.pyc
  43. binární
      blog/controller/admin/__pycache__/views.cpython-310.pyc
  44. 13 0
      blog/controller/admin/views.py
  45. 340 0
      blog/controller/article.py
  46. 84 0
      blog/controller/category.py
  47. 106 0
      blog/controller/files.py
  48. 52 0
      blog/controller/login.py
  49. 103 0
      blog/controller/tag.py
  50. 62 0
      blog/migrations/0001_initial.py
  51. 18 0
      blog/migrations/0002_rename_is_show_index_article_is_top.py
  52. 18 0
      blog/migrations/0003_category_seq.py
  53. 0 0
      blog/migrations/__init__.py
  54. binární
      blog/migrations/__pycache__/0001_initial.cpython-310.pyc
  55. binární
      blog/migrations/__pycache__/0002_rename_is_show_index_article_is_top.cpython-310.pyc
  56. binární
      blog/migrations/__pycache__/0003_category_seq.cpython-310.pyc
  57. binární
      blog/migrations/__pycache__/__init__.cpython-310.pyc
  58. 72 0
      blog/models.py
  59. 0 0
      blog/router/__init__.py
  60. binární
      blog/router/__pycache__/__init__.cpython-310.pyc
  61. 0 0
      blog/router/admin_url/__init__.py
  62. binární
      blog/router/admin_url/__pycache__/__init__.cpython-310.pyc
  63. binární
      blog/router/admin_url/__pycache__/admin_url.cpython-310.pyc
  64. 34 0
      blog/router/admin_url/admin_url.py
  65. 0 0
      blog/router/article_url/__init__.py
  66. binární
      blog/router/article_url/__pycache__/__init__.cpython-310.pyc
  67. binární
      blog/router/article_url/__pycache__/article_url.cpython-310.pyc
  68. 31 0
      blog/router/article_url/article_url.py
  69. 0 0
      blog/router/category_url/__init__.py
  70. binární
      blog/router/category_url/__pycache__/__init__.cpython-310.pyc
  71. binární
      blog/router/category_url/__pycache__/category_url.cpython-310.pyc
  72. 29 0
      blog/router/category_url/category_url.py
  73. 0 0
      blog/router/files_url/__init__.py
  74. binární
      blog/router/files_url/__pycache__/__init__.cpython-310.pyc
  75. binární
      blog/router/files_url/__pycache__/files_url.cpython-310.pyc
  76. 27 0
      blog/router/files_url/files_url.py
  77. 0 0
      blog/router/tag_url/__init__.py
  78. binární
      blog/router/tag_url/__pycache__/__init__.cpython-310.pyc
  79. binární
      blog/router/tag_url/__pycache__/tag_url.cpython-310.pyc
  80. 30 0
      blog/router/tag_url/tag_url.py
  81. 0 0
      blog/templatetags/__init__.py
  82. binární
      blog/templatetags/__pycache__/__init__.cpython-310.pyc
  83. binární
      blog/templatetags/__pycache__/day_util.cpython-310.pyc
  84. binární
      blog/templatetags/__pycache__/page_tag.cpython-310.pyc
  85. 19 0
      blog/templatetags/day_util.py
  86. 43 0
      blog/templatetags/page_tag.py
  87. 3 0
      blog/tests.py
  88. 93 0
      blog/views.py
  89. binární
      db.sqlite3
  90. 22 0
      manage.py
  91. 18 0
      static/2.4.1/semantic.css
  92. binární
      static/2.4.1/themes/default/assets/fonts/brand-icons.eot
  93. 29 0
      static/2.4.1/themes/default/assets/fonts/brand-icons.svg
  94. binární
      static/2.4.1/themes/default/assets/fonts/brand-icons.ttf
  95. binární
      static/2.4.1/themes/default/assets/fonts/brand-icons.woff
  96. binární
      static/2.4.1/themes/default/assets/fonts/brand-icons.woff2
  97. binární
      static/2.4.1/themes/default/assets/fonts/icons.eot
  98. binární
      static/2.4.1/themes/default/assets/fonts/icons.otf
  99. 41 0
      static/2.4.1/themes/default/assets/fonts/icons.svg
  100. binární
      static/2.4.1/themes/default/assets/fonts/icons.ttf

+ 8 - 0
.idea/.gitignore

@@ -0,0 +1,8 @@
+# Default ignored files
+/shelf/
+/workspace.xml
+# Editor-based HTTP Client requests
+/httpRequests/
+# Datasource local storage ignored files
+/dataSources/
+/dataSources.local.xml

+ 23 - 0
.idea/CyberSicko.iml

@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<module type="PYTHON_MODULE" version="4">
+  <component name="FacetManager">
+    <facet type="django" name="Django">
+      <configuration>
+        <option name="rootFolder" value="$MODULE_DIR$" />
+        <option name="settingsModule" value="system/settings.py" />
+        <option name="manageScript" value="manage.py" />
+        <option name="environment" value="&lt;map/&gt;" />
+        <option name="doNotUseTestRunner" value="false" />
+        <option name="trackFilePattern" value="" />
+      </configuration>
+    </facet>
+  </component>
+  <component name="NewModuleRootManager">
+    <content url="file://$MODULE_DIR$" />
+    <orderEntry type="inheritedJdk" />
+    <orderEntry type="sourceFolder" forTests="false" />
+  </component>
+  <component name="TemplatesService">
+    <option name="TEMPLATE_CONFIGURATION" value="Django" />
+  </component>
+</module>

+ 12 - 0
.idea/dataSources.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="DataSourceManagerImpl" format="xml" multifile-model="true">
+    <data-source source="LOCAL" name="db.sqlite3" uuid="328665ab-6d9a-4b39-904d-97019ba3acff">
+      <driver-ref>sqlite.xerial</driver-ref>
+      <synchronize>true</synchronize>
+      <jdbc-driver>org.sqlite.JDBC</jdbc-driver>
+      <jdbc-url>jdbc:sqlite:$PROJECT_DIR$/db.sqlite3</jdbc-url>
+      <working-dir>$ProjectFileDir$</working-dir>
+    </data-source>
+  </component>
+</project>

+ 24 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,24 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="PyPackageRequirementsInspection" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="ignoredPackages">
+        <value>
+          <list size="2">
+            <item index="0" class="java.lang.String" itemvalue="PyMySQL" />
+            <item index="1" class="java.lang.String" itemvalue="pymongo" />
+          </list>
+        </value>
+      </option>
+    </inspection_tool>
+    <inspection_tool class="PyUnresolvedReferencesInspection" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="ignoredIdentifiers">
+        <list>
+          <option value="str.decode" />
+          <option value="pywintypes.TimeType" />
+          <option value="dict.__getitem__" />
+        </list>
+      </option>
+    </inspection_tool>
+  </profile>
+</component>

+ 6 - 0
.idea/inspectionProfiles/profiles_settings.xml

@@ -0,0 +1,6 @@
+<component name="InspectionProjectProfileManager">
+  <settings>
+    <option name="USE_PROJECT_PROFILE" value="false" />
+    <version value="1.0" />
+  </settings>
+</component>

+ 4 - 0
.idea/misc.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectRootManager" version="2" project-jdk-name="Pipenv (CyberSicko)" project-jdk-type="Python SDK" />
+</project>

+ 8 - 0
.idea/modules.xml

@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectModuleManager">
+    <modules>
+      <module fileurl="file://$PROJECT_DIR$/.idea/CyberSicko.iml" filepath="$PROJECT_DIR$/.idea/CyberSicko.iml" />
+    </modules>
+  </component>
+</project>

+ 6 - 0
.idea/vcs.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="VcsDirectoryMappings">
+    <mapping directory="$PROJECT_DIR$" vcs="Git" />
+  </component>
+</project>

binární
Pillow-8.4.0-cp310-cp310-win32.whl


binární
Pillow-8.4.0-cp310-cp310-win_amd64.whl


+ 14 - 0
Pipfile

@@ -0,0 +1,14 @@
+[[source]]
+url = "https://pypi.org/simple"
+verify_ssl = true
+name = "pypi"
+
+[packages]
+pillow = {path = "./Pillow-8.4.0-cp310-cp310-win32.whl"}
+django = "*"
+django-avatar = "*"
+
+[dev-packages]
+
+[requires]
+python_version = "3.10"

+ 148 - 0
Pipfile.lock

@@ -0,0 +1,148 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "35ff7213de11801b0bc0acc12c21b63a635a1b107671f826a1c0b15f81c869a8"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.10"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {
+        "anyio": {
+            "hashes": [
+                "sha256:44a3c9aba0f5defa43261a8b3efb97891f2bd7d804e0e1f56419befa1adfc780",
+                "sha256:91dee416e570e92c64041bd18b900d1d6fa78dff7048769ce5ac5ddad004fbb5"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==3.7.1"
+        },
+        "asgiref": {
+            "hashes": [
+                "sha256:89b2ef2247e3b562a16eef663bc0e2e703ec6468e2fa8a5cd61cd449786d4f6e",
+                "sha256:9e0ce3aa93a819ba5b45120216b23878cf6e8525eb3848653452b4192b92afed"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==3.7.2"
+        },
+        "certifi": {
+            "hashes": [
+                "sha256:0f0d56dc5a6ad56fd4ba36484d6cc34451e1c6548c61daad8c320169f91eddc7",
+                "sha256:c6c2e98f5c7869efca1f8916fed228dd91539f9f1b444c314c06eef02980c716"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==2023.5.7"
+        },
+        "django": {
+            "hashes": [
+                "sha256:45a747e1c5b3d6df1b141b1481e193b033fd1fdbda3ff52677dc81afdaacbaed",
+                "sha256:f7c7852a5ac5a3da5a8d5b35cc6168f31b605971441798dac845f17ca8028039"
+            ],
+            "index": "pypi",
+            "version": "==4.2.3"
+        },
+        "django-appconf": {
+            "hashes": [
+                "sha256:ae9f864ee1958c815a965ed63b3fba4874eec13de10236ba063a788f9a17389d",
+                "sha256:be3db0be6c81fa84742000b89a81c016d70ae66a7ccb620cdef592b1f1a6aaa4"
+            ],
+            "markers": "python_version >= '3.6'",
+            "version": "==1.0.5"
+        },
+        "django-avatar": {
+            "hashes": [
+                "sha256:63746160a76c69e040ddf021d38a73389a268672e6e3cb3d1152346f2dcb0bd5",
+                "sha256:a80343492fa83450d3b3e545277dcd0d527f82d8918435f1e6a1b7589acf7196"
+            ],
+            "index": "pypi",
+            "version": "==7.1.1"
+        },
+        "dnspython": {
+            "hashes": [
+                "sha256:46b4052a55b56beea3a3bdd7b30295c292bd6827dd442348bc116f2d35b17f0a",
+                "sha256:758e691dbb454d5ccf4e1b154a19e52847f79e21a42fef17b969144af29a4e6c"
+            ],
+            "markers": "python_version >= '3.8' and python_version < '4.0'",
+            "version": "==2.4.0"
+        },
+        "exceptiongroup": {
+            "hashes": [
+                "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5",
+                "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"
+            ],
+            "markers": "python_version < '3.11'",
+            "version": "==1.1.2"
+        },
+        "h11": {
+            "hashes": [
+                "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d",
+                "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==0.14.0"
+        },
+        "httpcore": {
+            "hashes": [
+                "sha256:a6f30213335e34c1ade7be6ec7c47f19f50c56db36abef1a9dfa3815b1cb3888",
+                "sha256:c2789b767ddddfa2a5782e3199b2b7f6894540b17b16ec26b2c4d8e103510b87"
+            ],
+            "markers": "python_version >= '3.8'",
+            "version": "==0.17.3"
+        },
+        "idna": {
+            "hashes": [
+                "sha256:814f528e8dead7d329833b91c5faa87d60bf71824cd12a7530b5526063d02cb4",
+                "sha256:90b77e79eaa3eba6de819a0c442c0b4ceefc341a7a2ab77d7562bf49f425c5c2"
+            ],
+            "markers": "python_version >= '3.5'",
+            "version": "==3.4"
+        },
+        "pillow": {
+            "hashes": [
+                "sha256:e3dacecfbeec9a33e932f00c6cd7996e62f53ad46fbe677577394aaa90ee419a"
+            ],
+            "path": "./Pillow-8.4.0-cp310-cp310-win32.whl",
+            "version": "==8.4.0"
+        },
+        "sniffio": {
+            "hashes": [
+                "sha256:e60305c5e5d314f5389259b7f22aaa33d8f7dee49763119234af3755c55b9101",
+                "sha256:eecefdce1e5bbfb7ad2eeaabf7c1eeb404d7757c379bd1f7e5cce9d8bf425384"
+            ],
+            "markers": "python_version >= '3.7'",
+            "version": "==1.3.0"
+        },
+        "sqlparse": {
+            "hashes": [
+                "sha256:5430a4fe2ac7d0f93e66f1efc6e1338a41884b7ddf2a350cedd20ccc4d9d28f3",
+                "sha256:d446183e84b8349fa3061f0fe7f06ca94ba65b426946ffebe6e3e8295332420c"
+            ],
+            "markers": "python_version >= '3.5'",
+            "version": "==0.4.4"
+        },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:440d5dd3af93b060174bf433bccd69b0babc3b15b1a8dca43789fd7f61514b36",
+                "sha256:b75ddc264f0ba5615db7ba217daeb99701ad295353c45f9e95963337ceeeffb2"
+            ],
+            "markers": "python_version < '3.11'",
+            "version": "==4.7.1"
+        },
+        "tzdata": {
+            "hashes": [
+                "sha256:11ef1e08e54acb0d4f95bdb1be05da659673de4acbd21bf9c69e94cc5e907a3a",
+                "sha256:7e65763eef3120314099b6939b5546db7adce1e7d6f2e179e3df563c70511eda"
+            ],
+            "markers": "sys_platform == 'win32'",
+            "version": "==2023.3"
+        }
+    },
+    "develop": {}
+}

binární
__pycache__/manage.cpython-310.pyc


binární
avatars/1/093b8f4a58bce8db03b321f26797bc44.jpg


binární
avatars/1/093b8f4a58bce8db03b321f26797bc44_vpMX8f2.jpg


binární
avatars/1/77d4e5d40fdaeb321eadd5cb855cb685.gif


binární
avatars/1/l4kz0sz0xe2.jpg


binární
avatars/1/resized/50/50/l4kz0sz0xe2.png


binární
avatars/1/resized/50/50/v2-e2efcba214c980e079aca377e03b8ec6_xll.png


binární
avatars/1/resized/65/50/77d4e5d40fdaeb321eadd5cb855cb685.png


binární
avatars/1/resized/65/50/l4kz0sz0xe2.png


binární
avatars/1/resized/65/50/v2-e2efcba214c980e079aca377e03b8ec6_xll.png


binární
avatars/1/resized/80/80/093b8f4a58bce8db03b321f26797bc44.png


binární
avatars/1/v2-e2efcba214c980e079aca377e03b8ec6_xll.jpg


binární
avatars/1/wallhaven-5gdp69.jpg


binární
avatars/1/wallhaven-5gdp69_2.jpg


+ 0 - 0
blog/__init__.py


binární
blog/__pycache__/__init__.cpython-310.pyc


binární
blog/__pycache__/admin.cpython-310.pyc


binární
blog/__pycache__/apps.cpython-310.pyc


binární
blog/__pycache__/context_processors.cpython-310.pyc


binární
blog/__pycache__/models.cpython-310.pyc


binární
blog/__pycache__/views.cpython-310.pyc


+ 3 - 0
blog/admin.py

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

+ 6 - 0
blog/apps.py

@@ -0,0 +1,6 @@
+from django.apps import AppConfig
+
+
+class BlogConfig(AppConfig):
+    default_auto_field = 'django.db.models.BigAutoField'
+    name = 'blog'

+ 26 - 0
blog/backends.py

@@ -0,0 +1,26 @@
+from django.contrib.auth.backends import ModelBackend, UserModel
+from django.contrib.auth.models import User
+from django.core.exceptions import MultipleObjectsReturned
+from django.db.models import Q
+
+
+class EmailBackend(ModelBackend):
+    def authenticate(self, request, username=None, password=None, **kwargs):
+        try:
+            user = UserModel.objects.get(
+                Q(username__iexact=username) | Q(email__iexact=username))
+        except UserModel.DoesNotExist:
+            UserModel().set_password(password)
+        except MultipleObjectsReturned:
+            return User.objects.filter(email=username).order_by('id').first()
+        else:
+            if user.check_password(password) and self.user_can_authenticate(user):
+                return user
+
+    def get_user(self, user_id):
+        try:
+            user = UserModel.objects.get(pk=user_id)
+        except UserModel.DoesNotExist:
+            return None
+
+        return user if self.user_can_authenticate(user) else None

+ 13 - 0
blog/context_processors.py

@@ -0,0 +1,13 @@
+from blog import models
+from system import settings
+
+
+def global_content(request):
+    context = {'is_login': request.session['is_login'] if request.session.has_key('is_login') else False,
+               'user': request.session['login_user'] if request.session.has_key('login_user') else None,
+               'msg': request.session['msg'] if request.session.has_key('msg') else '','domain':settings.DOMAIN,
+               'category': models.Category.objects.all().order_by('seq')}
+    if context['is_login']:
+        context['tags'] = models.Tags.objects.all()
+
+    return context

binární
blog/controller/__pycache__/article.cpython-310.pyc


binární
blog/controller/__pycache__/category.cpython-310.pyc


binární
blog/controller/__pycache__/files.cpython-310.pyc


binární
blog/controller/__pycache__/login.cpython-310.pyc


binární
blog/controller/__pycache__/tag.cpython-310.pyc


binární
blog/controller/admin/__pycache__/views.cpython-310.pyc


+ 13 - 0
blog/controller/admin/views.py

@@ -0,0 +1,13 @@
+from django.contrib.auth.decorators import login_required
+from django.shortcuts import render
+from django.views.decorators.clickjacking import xframe_options_sameorigin
+
+import blog
+from blog import models
+
+
+@xframe_options_sameorigin
+@login_required(login_url='/login')
+def index(request):
+    return render(request, 'management/index.html')
+

+ 340 - 0
blog/controller/article.py

@@ -0,0 +1,340 @@
+import json
+from datetime import datetime
+
+from django.contrib.auth.decorators import login_required
+from django.core import serializers
+from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
+from django.http import JsonResponse, HttpResponseRedirect
+from django.shortcuts import render
+
+import blog
+from blog import models
+from system.error.ServerException import ServerException
+
+
+@login_required(login_url='/login')
+def article(request):
+    page = request.GET.get('page')
+    search_title = request.GET.get('search_title')
+    search_content = request.GET.get('search_content')
+    if page is None:
+        page = 0
+    articles = blog.controller.article.get_article(is_paginator=True, page=int(page), search_title=search_title,
+                                                   search_content=search_content)
+    return render(request, 'management/article/article.html', context={'articles': articles})
+
+
+def new(request):
+    return render(request, 'management/article/new_article.html')
+
+
+def show_article(request, pk):
+    article = models.Article.objects.filter(id=int(pk)).values('id', 'title', 'intro', 'category',
+                                                               'user', 'created_time', 'type',
+                                                               'cover__file_net_path',
+                                                               'music__file_net_path', 'status',
+                                                               'category__name',
+                                                               'user__first_name', 'user_id',
+                                                               'user__last_name', 'markdown_text', 'is_top',
+                                                               'html_text').first()
+    from blog.views import get_record_and_tags
+    result = get_record_and_tags()
+
+    return render(request, 'blog.html', context={'article': article, 'records': result['records'],
+               'tags': result['tags']})
+
+
+@login_required(login_url='/')
+def to_edit(request, pk):
+    article = models.Article.objects.filter(id=int(pk)).values('id', 'title', 'intro', 'category',
+                                                               'user', 'created_time', 'type',
+                                                               'cover__file_net_path',
+                                                               'music__file_net_path', 'status',
+                                                               'category__name',
+                                                               'user__first_name', 'user_id',
+                                                               'user__last_name', 'markdown_text', 'html_text',
+                                                               'is_top').first()
+    tags = models.Tags.objects.raw(
+        'select * from blog_tags left join blog_article_tags bat on blog_tags.id = bat.tags_id where  bat.article_id = {0}'.format(
+            int(pk)))
+    tags_str = []
+    for tag in tags:
+        tags_str.append(tag.name)
+    return render(request, 'management/article/edit_article.html',
+                  context={'article': article, 'tags': ','.join(tags_str)})
+
+
+@login_required(login_url='/')
+def edit_article(request):
+    if request.method == 'POST':
+        title = request.POST['title']
+        intro = request.POST['intro']
+        category = request.POST['category']
+        html_text = request.POST['html']
+        markdown_text = request.POST['markdown']
+        type_ = request.POST['type']
+        status = request.POST['status']
+        tags = request.POST['tags']
+        pk = request.POST['pk']
+        is_top = request.POST['is_top']
+        category_obj = models.Category.objects.get(id=category)
+
+        article_obj = models.Article.objects.get(id=int(pk))
+        tags_id = []
+        for tag in str(tags).split(','):
+            if tag == '':
+                break
+            else:
+                tag_obj = models.Tags.objects.filter(name=tag).first()
+                if tag_obj is None:
+                    obj = models.Tags(name=tag)
+                    obj.save()
+                    tags_id.append(obj.pk)
+                else:
+                    tags_id.append(tag_obj.pk)
+        if len(tags_id) > 0:
+            article_obj.tags.set(tags_id)
+        # models.Article.objects.filter(id=int(pk)).update(title=title, intro=intro, category=category_obj,
+        #                                                  html_text=html_text,
+        #                                                  markdown_text=markdown_text,
+        #                                                  type=type_, status=status,
+        #                                                  tags=str(tags).split(',')
+        #                                                  )
+        article_obj.title = title
+        article_obj.intro = intro
+        article_obj.category = category_obj
+        article_obj.html_text = html_text
+        article_obj.markdown_text = markdown_text
+        article_obj.type = type_
+        article_obj.status = status
+        article_obj.is_top = is_top
+        article_obj.save()
+        return JsonResponse({"success": True, "message": "修改成功"})
+    else:
+        raise ServerException("错误的请求")
+
+
+@login_required(login_url='/')
+def add_article(request):
+    if request.method == 'POST':
+        title = request.POST['title']
+        intro = request.POST['intro']
+        category = request.POST['category']
+        html_text = request.POST['html']
+        markdown_text = request.POST['markdown']
+        type_ = request.POST['type']
+        status = request.POST['status']
+        tags = request.POST['tags']
+        is_top = request.POST['is_top']
+
+        category_obj = models.Category.objects.get(id=category)
+
+        article_obj = models.Article.objects.create(title=title, intro=intro, category=category_obj,
+                                                    html_text=html_text,
+                                                    markdown_text=markdown_text,
+                                                    type=type_, status=status, user=request.user,
+                                                    created_time=datetime.now(),
+                                                    is_top=is_top
+                                                    )
+        tags_id = []
+        for tag in str(tags).split(','):
+            if tag == '':
+                break
+            else:
+                tag_obj = models.Tags.objects.filter(name=tag).first()
+                if tag_obj is None:
+                    obj = models.Tags(name=tag)
+                    obj.save()
+                    tags_id.append(obj.pk)
+                else:
+                    tags_id.append(tag_obj.pk)
+        if len(tags_id) > 0:
+            article_obj.tags.set(tags_id)
+        return JsonResponse({"success": True, "message": "添加成功"})
+    else:
+        raise ServerException("错误的请求")
+
+
+@login_required(login_url='/')
+def add_media(request):
+    if request.method == 'POST':
+        title = request.POST['title']
+        category = request.POST['category']
+        type_ = request.POST['type']
+        status = request.POST['status']
+        tags = request.POST['tags']
+        cover = request.POST['cover_id']
+        music_id = request.POST['music_id']
+        cover_obj = models.FileRecord.objects.get(id=cover)
+        music_obj = models.FileRecord.objects.get(id=music_id)
+
+        category_obj = models.Category.objects.get(id=category)
+        article_obj = models.Article.objects.create(title=title, category=category_obj,
+                                                    type=type_, status=status, user=request.user,
+                                                    created_time=datetime.now(),
+                                                    cover=cover_obj, music=music_obj
+                                                    )
+        if tags != 'null':
+            article_obj.tags.set(str(tags).split(','))
+        return JsonResponse({"success": True, "message": "添加成功"})
+    else:
+        raise ServerException("错误的请求")
+
+
+@login_required(login_url='/')
+def add_music(request):
+    if request.method == 'POST':
+        title = request.POST['title']
+        category = request.POST['category']
+        type_ = request.POST['type']
+        status = request.POST['status']
+        tags = request.POST['tags']
+        cover = request.POST['cover_id']
+        music_id = request.POST['music_id']
+        cover_obj = models.FileRecord.objects.get(id=cover)
+        music_obj = models.FileRecord.objects.get(id=music_id)
+
+        category_obj = models.Category.objects.get(id=category)
+        article_obj = models.Article.objects.create(title=title, category=category_obj,
+                                                    type=type_, status=status, user=request.user,
+                                                    created_time=datetime.now(),
+                                                    cover=cover_obj, music=music_obj
+                                                    )
+        if tags != 'null':
+            article_obj.tags.set(str(tags).split(','))
+        return JsonResponse({"success": True, "message": "添加成功"})
+    else:
+        raise ServerException("错误的请求")
+
+
+def set_tag(articles):
+    for i in range(len(articles)):
+        tags = models.Tags.objects.raw(
+            'select * from blog_tags left join blog_article_tags bat on blog_tags.id = bat.tags_id where  bat.article_id = {0}'.format(
+                articles[i]['id']))
+        tags_field = []
+        for item in tags:
+            tags_field.append(item.name)
+        articles[i]['tags'] = tags_field
+
+
+def get_article(top: int = -1, page: int = -1, is_paginator: bool = False, category: models.Category = None,
+                tag: models.Tags = None, search_title: str = None, search_content: str = None, date_record: str = None):
+    if category is None and tag is None:
+        search_dict = dict()
+        if search_title:
+            search_dict['title__contains'] = search_title
+        if search_content:
+            search_dict['markdown_text__contains'] = search_content
+        articles = models.Article.objects.filter(**search_dict).order_by('-created_time').values(
+            'id', 'title', 'intro', 'category',
+            'user', 'created_time', 'type',
+            'cover__file_net_path',
+            'music__file_net_path', 'status',
+            'category__name',
+            'user__first_name', 'user_id',
+            'user__last_name',
+            'is_top', 'html_text')
+    elif date_record is not None:
+        date_format = "%Y-%m"
+        date = datetime.strptime(date_record, date_format)
+        articles = models.Article.objects.filter(created_time__year=date.year,created_time__month=date.month).order_by('-created_time').values('id', 'title',
+                                                                                                       'intro',
+                                                                                                       'category',
+                                                                                                       'user',
+                                                                                                       'created_time',
+                                                                                                       'type',
+                                                                                                       'cover__file_net_path',
+                                                                                                       'music__file_net_path',
+                                                                                                       'status',
+                                                                                                       'category__name',
+                                                                                                       'user__first_name',
+                                                                                                       'user_id',
+                                                                                                       'user__last_name',
+                                                                                                       'tags__name',
+                                                                                                       'is_top',
+                                                                                                       'html_text')
+    elif tag is not None:
+        articles = models.Article.objects.filter(tags__name=tag.name).order_by('-created_time').values('id', 'title',
+                                                                                                       'intro',
+                                                                                                       'category',
+                                                                                                       'user',
+                                                                                                       'created_time',
+                                                                                                       'type',
+                                                                                                       'cover__file_net_path',
+                                                                                                       'music__file_net_path',
+                                                                                                       'status',
+                                                                                                       'category__name',
+                                                                                                       'user__first_name',
+                                                                                                       'user_id',
+                                                                                                       'user__last_name',
+                                                                                                       'tags__name',
+                                                                                                       'is_top',
+                                                                                                       'html_text')
+    else:
+        articles = models.Article.objects.filter(category=category).order_by('-created_time').values('id', 'title',
+                                                                                                     'intro',
+                                                                                                     'category',
+                                                                                                     'user',
+                                                                                                     'created_time',
+                                                                                                     'type',
+                                                                                                     'cover__file_net_path',
+                                                                                                     'music__file_net_path',
+                                                                                                     'status',
+                                                                                                     'category__name',
+                                                                                                     'user__first_name',
+                                                                                                     'user_id',
+                                                                                                     'user__last_name',
+                                                                                                     'is_top',
+                                                                                                     'html_text')
+
+    if is_paginator:
+        if page <= 0:
+            page = 1
+        paginator = Paginator(articles, 15)
+        try:
+            articles = paginator.page(page)
+        except PageNotAnInteger:
+            articles = paginator.page(1)
+        except EmptyPage:
+            articles = paginator.page(paginator.num_pages)
+        set_tag(articles=articles)
+
+        return articles
+    else:
+        articles = list(articles)
+    if top > -1:
+        articles = articles[:top]
+    set_tag(articles=articles)
+    return articles
+    # return JsonResponse({'all_article': json.loads(json.dumps(articles, default=str))})
+
+
+@login_required(login_url='/')
+def get_all_article(request):
+    articles = models.Article.objects.order_by('-created_time').values('id', 'title',
+                                                                       'intro',
+                                                                       'category',
+                                                                       'user',
+                                                                       'created_time',
+                                                                       'type',
+                                                                       'cover__file_net_path',
+                                                                       'music__file_net_path',
+                                                                       'status',
+                                                                       'category__name',
+                                                                       'user__first_name', 'user_id',
+                                                                       'user__last_name')
+    return JsonResponse(json.loads(json.dumps(list(articles), default=str)), safe=False)
+
+
+@login_required(login_url='/')
+def delete_article(request):
+    if request.method == 'GET':
+        id = request.GET.get("id")
+        article = models.Article.objects.get(id=int(id))
+        article.tags.clear()
+        article.delete()
+
+        return HttpResponseRedirect('/management/article')  # 跳转到主界面
+    else:
+        raise ServerException("错误的请求")

+ 84 - 0
blog/controller/category.py

@@ -0,0 +1,84 @@
+import json
+
+from django.contrib.auth.decorators import login_required
+from django.core import serializers
+from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
+from django.http import JsonResponse, HttpResponseRedirect
+from django.shortcuts import render
+
+from blog.views import get_record_and_tags
+from system.error.ServerException import ServerException
+from blog import models
+from blog.controller import article
+
+
+@login_required(login_url='/')
+def category(request):
+    # page: int = -1, is_paginator: bool = False, search_name: str = None
+    if request.method == 'GET':
+        page = request.GET.get('page')
+        if page is None:
+            page = 1
+        else:
+            page = int(page)
+        search_name = request.GET.get('search_name')
+        if search_name is None:
+            category = models.Category.objects.all().values('id', 'name','seq')
+        else:
+            category = models.Category.objects.filter(name__contains=search_name).values('id', 'name','seq')
+        if page <= 0:
+            page = 1
+        paginator = Paginator(category, 15)
+        try:
+            category = paginator.page(page)
+        except PageNotAnInteger:
+            category = paginator.page(1)
+        except EmptyPage:
+            category = paginator.page(paginator.num_pages)
+        return render(request, 'management/category/category.html', context={'category': category})
+
+
+def current_category(request, name):
+    category_obj = models.Category.objects.get(name=name)
+    page = request.GET.get('page')
+    if page is None:
+        page = 0
+    result = get_record_and_tags()
+    context = {'current_category': category_obj.name,
+               'articles': article.get_article(is_paginator=True, page=int(page), category=category_obj),
+               'records': result['records'],
+               'tags': result['tags']
+               }
+
+    return render(request, 'category.html', context=context)
+
+
+@login_required(login_url='/')
+def get_all_category(request):
+    category = serializers.serialize("json", models.Category.objects.all())
+    return JsonResponse(json.loads(category), safe=False)
+
+
+@login_required(login_url='/')
+def add_category(request):
+    if request.method == 'POST':
+        category_name = request.POST.get("category_name")
+        seq = request.POST.get("seq")
+        if category_name == '':
+            return HttpResponseRedirect('/management/category')
+        else:
+            obj = models.Category(name=category_name, seq=int(seq))
+            obj.save()
+            return HttpResponseRedirect('/management/category')
+    else:
+        raise ServerException("错误的请求")
+
+
+@login_required(login_url='/')
+def delete_category(request):
+    if request.method == 'GET':
+        ids_str = request.GET.get("id")
+        models.Category.objects.get(id=int(ids_str)).delete()
+        return HttpResponseRedirect('/management/category')  # 跳转到主界面
+    else:
+        raise ServerException("错误的请求")

+ 106 - 0
blog/controller/files.py

@@ -0,0 +1,106 @@
+import hashlib
+import os
+import uuid
+from datetime import datetime
+
+from django.contrib.auth.decorators import login_required
+from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
+from django.http import HttpResponseRedirect, JsonResponse
+from django.shortcuts import render
+from django.views.decorators.clickjacking import xframe_options_sameorigin
+
+from blog import models
+from system import settings
+from system.error.ServerException import ServerException
+
+
+@xframe_options_sameorigin
+@login_required(login_url='/')
+def upload_markdown_img(request):
+    file_obj = request.FILES.get('file')
+    try:
+        file_record = save_file(file_obj=file_obj)
+    except Exception as e:
+        return JsonResponse({"errno": 1, "message": str(e)})
+    from PIL import Image
+
+    img = Image.open(file_record.file_path + file_record.file_name)
+    width, height = img.size
+    print('Image size: {}x{}'.format(width, height))
+
+    return JsonResponse({"errno": 0, "data": {
+        'url': file_record.file_net_path,
+        'alt': f'{width},{height}',
+        "href": file_record.file_net_path,
+        "width": width,
+        "height": height
+    }})
+
+
+def save_file(file_obj):
+    def get_unique_str():
+        uuid_str = str(uuid.uuid4())
+        md5 = hashlib.md5()
+        md5.update(uuid_str.encode('utf-8'))
+        return md5.hexdigest()
+
+    if file_obj is None:
+        raise ServerException('文件不存在')
+    else:
+        file_type = file_obj.name.split('.')[-1]
+
+        filename = get_unique_str() + '.' + file_obj.name.split('.')[-1]
+        filepath = os.path.join(settings.UPLOAD_ROOT + '/' + file_type + '/')
+        if not os.path.exists(filepath):
+            os.mkdir(filepath)
+
+        file_net_path = settings.DOMAIN + settings.UPLOAD_URL + '/' + file_type + '/' + filename
+
+        f = open(filepath + filename, 'wb')
+        for i in file_obj.chunks():
+            f.write(i)
+        f.close()
+
+    file_record = models.FileRecord.objects.create(origin_name=file_obj.name, file_name=filename, file_path=filepath,
+                                                   create_date=datetime.now(),
+                                                   suffix=file_obj.name.split('.')[-1], file_net_path=file_net_path)
+    return file_record
+
+
+@login_required(login_url='/')
+def files(request):
+    # page: int = -1, is_paginator: bool = False, search_name: str = None
+    if request.method == 'GET':
+        page = request.GET.get('page')
+        if page is None:
+            page = 1
+        else:
+            page = int(page)
+        search_name = request.GET.get('search_name')
+        if search_name is None:
+            files = models.FileRecord.objects.all()
+        else:
+            files = models.FileRecord.objects.filter(origin_name__contains=search_name)
+        if page <= 0:
+            page = 1
+        paginator = Paginator(files, 15)
+        try:
+            files = paginator.page(page)
+        except PageNotAnInteger:
+            files = paginator.page(1)
+        except EmptyPage:
+            files = paginator.page(paginator.num_pages)
+        return render(request, 'management/files/files.html', context={'files': files})
+
+
+@login_required(login_url='/')
+def delete(request):
+    # page: int = -1, is_paginator: bool = False, search_name: str = None
+
+    if request.method == 'GET':
+        ids_str = request.GET.get("id")
+        # models.files.objects.raw('DELETE FROM blog_files WHERE id IN (%s)', ids_str)
+        models.FileRecord.objects.get(id=int(ids_str)).delete()
+        return HttpResponseRedirect('/management/files')  # 跳转到主界面
+    else:
+        raise ServerException("错误的请求")

+ 52 - 0
blog/controller/login.py

@@ -0,0 +1,52 @@
+import logging
+
+from django.contrib import auth
+from django.contrib.auth.decorators import login_required
+from django.http import HttpResponseRedirect
+from django.shortcuts import render
+
+# Create your views here.
+from system.enum import status
+from system.error.ServerException import ServerException
+
+LOG = logging.getLogger('blog.views')
+
+
+def login(request):
+    if request.method == 'POST':
+        username = request.POST.get("username")
+        password = request.POST.get('password')
+        logging.info('{0} {1} 用户尝试登录'.format(username, password))
+        context = {
+            'msg': '',
+        }
+        if username is None or password is None:
+            request.session['msg'] = status.USER_OR_PASSWORD_NOT_EMPTY
+            return HttpResponseRedirect('/')  # 跳转到主界面
+        else:
+            user = auth.authenticate(username=username, password=password)
+            if user is not None:
+                auth.login(request, user)
+                request.session["login_user"] = user
+                request.session["is_login"] = True
+                request.session['msg'] = ''
+                LOG.info("登录成功")
+                return HttpResponseRedirect('/management')  # 跳转到主界面
+
+            else:
+                request.session['msg'] = status.USER_OR_PASSWORD_ERROR
+                return render(request, 'login.html')
+    else:
+        if request.session["is_login"]:
+            return HttpResponseRedirect('/management')  # 跳转到主界面
+        else:
+            return render(request, 'login.html')
+
+
+@login_required(login_url='/')
+def logout(request):
+    request.session["login_user"] = None
+    request.session["is_login"] = False
+    request.session['msg'] = ''
+    auth.logout(request)
+    return HttpResponseRedirect('/')  # 跳转到主界面

+ 103 - 0
blog/controller/tag.py

@@ -0,0 +1,103 @@
+import json
+
+from django.contrib.auth.decorators import login_required
+from django.core import serializers
+from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
+from django.http import JsonResponse, HttpResponseRedirect
+from django.shortcuts import render
+
+from blog.views import get_record_and_tags
+from system.error.ServerException import ServerException
+from blog import models
+from blog.controller import article
+
+
+@login_required(login_url='/')
+def tags_page(request):
+    # page: int = -1, is_paginator: bool = False, search_name: str = None
+    if request.method == 'GET':
+        page = request.GET.get('page')
+        if page is None:
+            page = 1
+        else:
+            page = int(page)
+        search_name = request.GET.get('search_name')
+        if search_name is None:
+            tags = models.Tags.objects.all().values('id','name')
+        else:
+            tags = models.Tags.objects.filter(name__contains=search_name).values('id','name')
+        if page <= 0:
+            page = 1
+        paginator = Paginator(tags, 15)
+        try:
+            tags = paginator.page(page)
+        except PageNotAnInteger:
+            tags = paginator.page(1)
+        except EmptyPage:
+            tags = paginator.page(paginator.num_pages)
+        return render(request, 'management/tags/tags.html', context={'tags': tags})
+
+
+def current_tag(request, name):
+    tag_obj = models.Tags.objects.get(name=name)
+    page = request.GET.get('page')
+    if page is None:
+        page = 0
+    result = get_record_and_tags()
+
+    context = {'current_category': tag_obj.name,
+               'articles': article.get_article(is_paginator=True, page=int(page), tag=tag_obj),
+               'records': result['records'],
+               'tags': result['tags']
+               }
+
+    return render(request, 'category.html', context=context)
+
+
+@login_required(login_url='/')
+def get_all_tag(request):
+    tags = serializers.serialize("json", models.Tags.objects.all())
+
+    return JsonResponse(json.loads(tags), safe=False)
+
+
+@login_required(login_url='/')
+def search(request):
+    term = request.GET.get("term")
+    if term is not None and term != '':
+        arr = []
+        tags = models.Tags.objects.filter(name__contains=term)
+        for item in tags:
+            arr.append(item.name)
+        return JsonResponse(arr, safe=False)
+    else:
+        arr = []
+        tags = models.Tags.objects.all()
+        for item in tags:
+            arr.append(item.name)
+        return JsonResponse(arr, safe=False)
+
+
+@login_required(login_url='/')
+def add_tag(request):
+    if request.method == 'POST':
+        tag_name = request.POST.get("tag_name")
+        if tag_name == '':
+            return JsonResponse({'success': False}, safe=False)
+        else:
+            obj = models.Tags(name=tag_name)
+            obj.save()
+            return JsonResponse({'success': True}, safe=False)
+    else:
+        raise ServerException("错误的请求")
+
+
+@login_required(login_url='/')
+def delete_tags(request):
+    if request.method == 'GET':
+        ids_str = request.GET.get("id")
+        # models.Tags.objects.raw('DELETE FROM blog_tags WHERE id IN (%s)', ids_str)
+        models.Tags.objects.get(id=int(ids_str)).delete()
+        return HttpResponseRedirect('/management/tags')  # 跳转到主界面
+    else:
+        raise ServerException("错误的请求")

+ 62 - 0
blog/migrations/0001_initial.py

@@ -0,0 +1,62 @@
+# Generated by Django 4.2.3 on 2023-07-14 15:58
+
+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='Category',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=100, unique=True, verbose_name='分类')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='FileRecord',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('origin_name', models.CharField(max_length=200, verbose_name='源文件名称')),
+                ('file_name', models.CharField(max_length=200, verbose_name='文件名称')),
+                ('file_path', models.FilePathField(max_length=255, verbose_name='文件路径')),
+                ('create_date', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
+                ('suffix', models.CharField(max_length=10, verbose_name='文件类型')),
+                ('file_net_path', models.CharField(max_length=200, verbose_name='文件网络路径')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Tags',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('name', models.CharField(max_length=100, unique=True, verbose_name='标签')),
+            ],
+        ),
+        migrations.CreateModel(
+            name='Article',
+            fields=[
+                ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+                ('title', models.CharField(max_length=70, verbose_name='标题')),
+                ('intro', models.TextField(blank=True, max_length=200, verbose_name='摘要')),
+                ('html_text', models.TextField(blank=True)),
+                ('markdown_text', models.TextField(blank=True)),
+                ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='发布时间')),
+                ('type', models.SmallIntegerField(choices=[(1, 'ARTICLE'), (2, 'MUSIC'), (3, 'ESSAY'), (4, 'MEDIA')], default=1, verbose_name='类型')),
+                ('status', models.SmallIntegerField(choices=[(1, 'RELEASE'), (0, 'PADDING'), (-1, 'DELETE')], default=1, verbose_name='状态')),
+                ('is_top', models.BooleanField(default=True, verbose_name='是否在首页显示')),
+                ('category', models.ForeignKey(default='1', on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='分类')),
+                ('cover', models.ForeignKey(blank=True, default='', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='cover', to='blog.filerecord', verbose_name='封面')),
+                ('music', models.ForeignKey(blank=True, default='', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='music', to='blog.filerecord', verbose_name='音乐路径')),
+                ('tags', models.ManyToManyField(blank=True, to='blog.tags')),
+                ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')),
+            ],
+        ),
+    ]

+ 18 - 0
blog/migrations/0002_rename_is_show_index_article_is_top.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.2.3 on 2023-07-15 02:26
+
+from django.db import migrations
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('blog', '0001_initial'),
+    ]
+
+    operations = [
+        migrations.RenameField(
+            model_name='article',
+            old_name='is_top',
+            new_name='is_top',
+        ),
+    ]

+ 18 - 0
blog/migrations/0003_category_seq.py

@@ -0,0 +1,18 @@
+# Generated by Django 4.2.3 on 2023-07-16 06:19
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('blog', '0002_rename_is_show_index_article_is_top'),
+    ]
+
+    operations = [
+        migrations.AddField(
+            model_name='category',
+            name='seq',
+            field=models.IntegerField(default=1, verbose_name='排序'),
+        ),
+    ]

+ 0 - 0
blog/migrations/__init__.py


binární
blog/migrations/__pycache__/0001_initial.cpython-310.pyc


binární
blog/migrations/__pycache__/0002_rename_is_show_index_article_is_top.cpython-310.pyc


binární
blog/migrations/__pycache__/0003_category_seq.cpython-310.pyc


binární
blog/migrations/__pycache__/__init__.cpython-310.pyc


+ 72 - 0
blog/models.py

@@ -0,0 +1,72 @@
+from django.db import models
+from django.contrib.auth.models import User, AbstractUser
+from django.contrib.auth import get_user_model as user_model
+
+# Create your models here.
+User = user_model()
+
+
+# Create your models here.
+class Category(models.Model):
+    """
+    Django 要求模型必须继承 models.Model 类。
+    Category 只需要一个简单的分类名 name 就可以了。
+    CharField 指定了分类名 name 的数据类型,CharField 是字符型,
+    CharField 的 max_length 参数指定其最大长度,超过这个长度的分类名就不能被存入数据库。
+    然后给name设置了一个'分类'的名称
+    """
+    name = models.CharField('分类', max_length=100, unique=True)
+    seq = models.IntegerField('排序',default=1)
+
+
+class Tags(models.Model):
+    """
+    标签 Tag 也比较简单,和 Category 一样。
+    再次强调一定要继承 models.Model 类!
+    """
+    name = models.CharField('标签', max_length=100, unique=True)
+
+
+class FileRecord(models.Model):
+    origin_name = models.CharField('源文件名称', max_length=200)
+    file_name = models.CharField('文件名称', max_length=200)
+    file_path = models.FilePathField('文件路径', max_length=255)
+    create_date = models.DateTimeField('创建时间', auto_now_add=True)
+    suffix = models.CharField('文件类型', max_length=10)
+    file_net_path = models.CharField('文件网络路径', max_length=200)
+
+
+class Article(models.Model):
+    # 文章正文,我们使用了 TextField,并且指定了标题的长度
+    title = models.CharField('标题', max_length=70)
+    # 使用 TextField 来存储大段文本,文章摘要,我们指定了最大长度和允许可以为空。
+    intro = models.TextField('摘要', max_length=200, blank=True)
+    category = models.ForeignKey(Category, on_delete=models.CASCADE, verbose_name='分类', default='1')
+    tags = models.ManyToManyField(Tags, blank=True)
+    # 存储比较短的字符串可以使用 CharField,但对于文章的正文来说可能会是一大段文本,因此使用 TextField 来存储大段文本。
+    html_text = models.TextField(blank=True)
+    markdown_text = models.TextField(blank=True)
+
+    # 文章作者,这里 User 是从 django.contrib.auth.models 导入的。
+    # django.contrib.auth 是 Django 内置的应用,专门用于处理网站用户的注册、登录等流程,User 是 Django 为我们已经写好的用户模型。
+    # 这里我们通过 ForeignKey 把文章和 User 关联了起来。
+    # 因为我们规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关联关系,和 Category 类似。
+    user = models.ForeignKey(User, on_delete=models.CASCADE, verbose_name='作者')
+    # created_time,我们使用了DateTimeField字段,添加了一个auto_now_add参数,自动获取添加时间!
+    created_time = models.DateTimeField('发布时间', auto_now_add=True)
+    type_choices = (
+        (1, "ARTICLE"),
+        (2, "NEWS"),
+        (3, "MEDIA"))
+    type = models.SmallIntegerField(verbose_name="类型", choices=type_choices, default=1)
+    cover = models.ForeignKey(FileRecord, related_name="cover", on_delete=models.CASCADE, verbose_name='封面',
+                              default='',
+                              blank=True, null=True)
+    music = models.ForeignKey(FileRecord, related_name="music", on_delete=models.CASCADE, verbose_name='音乐路径',
+                              default='', blank=True, null=True)
+    status_choices = (
+        (1, "RELEASE"),
+        (0, "PADDING"),
+        (-1, "DELETE"))
+    status = models.SmallIntegerField(verbose_name="状态", choices=status_choices, default=1)
+    is_top = models.BooleanField(verbose_name="是否在首页显示", default=True)

+ 0 - 0
blog/router/__init__.py


binární
blog/router/__pycache__/__init__.cpython-310.pyc


+ 0 - 0
blog/router/admin_url/__init__.py


binární
blog/router/admin_url/__pycache__/__init__.cpython-310.pyc


binární
blog/router/admin_url/__pycache__/admin_url.cpython-310.pyc


+ 34 - 0
blog/router/admin_url/admin_url.py

@@ -0,0 +1,34 @@
+"""IBE URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from django.urls import path, include
+from blog.controller import login, category, article, tag, files
+from blog.controller.admin import views
+
+urlpatterns = [
+    path('', views.index, name=''),
+    path('article', article.article, name='admin_article'),
+    path('article/', include('blog.router.article_url.article_url')),
+    path('tags/', include('blog.router.tag_url.tag_url')),
+    path('tags', tag.tags_page, name='admin_tags'),
+    path('category', category.category, name='admin_category'),
+    path('category/', include('blog.router.category_url.category_url')),
+    path('files', files.files, name='admin_files'),
+    path('files/', include('blog.router.files_url.files_url')),
+
+]
+urlpatterns += staticfiles_urlpatterns()

+ 0 - 0
blog/router/article_url/__init__.py


binární
blog/router/article_url/__pycache__/__init__.cpython-310.pyc


binární
blog/router/article_url/__pycache__/article_url.cpython-310.pyc


+ 31 - 0
blog/router/article_url/article_url.py

@@ -0,0 +1,31 @@
+"""IBE URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from django.urls import path
+from blog import views
+from blog.controller import login, category, article
+
+urlpatterns = [
+    path('new', article.new, name='article_new'),
+    path('add_article', article.add_article),
+    path('get_all_article', article.get_all_article),
+    path('to_edit/<pk>', article.to_edit),
+    path(r'<pk>.html', article.show_article),
+    path('edit_article', article.edit_article),
+    path('delete_article', article.delete_article),
+]
+urlpatterns += staticfiles_urlpatterns()

+ 0 - 0
blog/router/category_url/__init__.py


binární
blog/router/category_url/__pycache__/__init__.cpython-310.pyc


binární
blog/router/category_url/__pycache__/category_url.cpython-310.pyc


+ 29 - 0
blog/router/category_url/category_url.py

@@ -0,0 +1,29 @@
+"""IBE URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from django.urls import path
+from blog import views
+from blog.controller import login, category
+
+urlpatterns = [
+    path('all', category.get_all_category),
+    path('add', category.add_category),
+    path('delete', category.delete_category),
+    path('<name>', category.current_category),
+
+]
+urlpatterns += staticfiles_urlpatterns()

+ 0 - 0
blog/router/files_url/__init__.py


binární
blog/router/files_url/__pycache__/__init__.cpython-310.pyc


binární
blog/router/files_url/__pycache__/files_url.cpython-310.pyc


+ 27 - 0
blog/router/files_url/files_url.py

@@ -0,0 +1,27 @@
+
+
+"""IBE URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from django.urls import path
+
+from blog.controller import files
+
+urlpatterns = [
+    path('delete',files.delete),
+    path('upload',files.upload_markdown_img),
+]
+urlpatterns += staticfiles_urlpatterns()

+ 0 - 0
blog/router/tag_url/__init__.py


binární
blog/router/tag_url/__pycache__/__init__.cpython-310.pyc


binární
blog/router/tag_url/__pycache__/tag_url.cpython-310.pyc


+ 30 - 0
blog/router/tag_url/tag_url.py

@@ -0,0 +1,30 @@
+"""IBE URL Configuration
+
+The `urlpatterns` list routes URLs to views. For more information please see:
+    https://docs.djangoproject.com/en/3.2/topics/http/urls/
+Examples:
+Function views
+    1. Add an import:  from my_app import views
+    2. Add a URL to urlpatterns:  path('', views.home, name='home')
+Class-based views
+    1. Add an import:  from other_app.views import Home
+    2. Add a URL to urlpatterns:  path('', Home.as_view(), name='home')
+Including another URLconf
+    1. Import the include() function: from django.urls import include, path
+    2. Add a URL to urlpatterns:  path('blog/', include('blog.urls'))
+"""
+from django.contrib import admin
+from django.contrib.staticfiles.urls import staticfiles_urlpatterns
+from django.urls import path
+from blog import views
+from blog.controller import login, tag
+
+urlpatterns = [
+    path('all', tag.get_all_tag),
+    path('search', tag.search),
+    path('add', tag.add_tag),
+    path('delete', tag.delete_tags),
+    path('<name>', tag.current_tag),
+
+]
+urlpatterns += staticfiles_urlpatterns()

+ 0 - 0
blog/templatetags/__init__.py


binární
blog/templatetags/__pycache__/__init__.cpython-310.pyc


binární
blog/templatetags/__pycache__/day_util.cpython-310.pyc


binární
blog/templatetags/__pycache__/page_tag.cpython-310.pyc


+ 19 - 0
blog/templatetags/day_util.py

@@ -0,0 +1,19 @@
+from datetime import datetime
+
+from django import template
+
+register = template.Library()
+
+
+@register.filter
+def days_until(date):
+    delta = datetime.date(date) - datetime.now().date()
+    date_str = date.strftime("%H点%M分")
+    day = abs(delta.days)
+    if day == 0:
+        return '今天 ' + date_str
+    elif day == 1:
+        return '昨天' + date_str
+    elif (day / 365) >= 1:
+        return str(int((day / 365))) + '年前'
+    return str(day) + '天前'

+ 43 - 0
blog/templatetags/page_tag.py

@@ -0,0 +1,43 @@
+#!/usr/bin/env python
+# _*_ coding:utf-8 _*_
+
+from django import template
+from django.utils.html import format_html
+from datetime import datetime
+from django import template
+
+# 注册标签
+register = template.Library()
+
+
+@register.simple_tag
+def circle_page(curr_page, loop_page):
+    curr_page = int(curr_page)
+    loop_page = int(loop_page)
+    offset = abs(curr_page - loop_page)
+    if offset < 3:
+        if curr_page == loop_page:
+            page_ele = ' <a class="item active" href="?page=%s">%s</a> ' % (
+                loop_page, loop_page)
+        else:
+            page_ele = ' <a class="item" href="?page=%s">%s</a>' % (loop_page, loop_page)
+        return format_html(page_ele)
+    else:
+        return ''
+
+
+@register.simple_tag
+def circle_page_index(curr_page, loop_page):
+    curr_page = int(curr_page)
+    loop_page = int(loop_page)
+    offset = abs(curr_page - loop_page)
+    if offset < 3:
+        if curr_page == loop_page:
+            page_ele = ' <li><a href="?page=%s" aria-current="page"><span class="visuallyhidden">page </span>%s</a></li>' % (
+                loop_page, loop_page)
+        else:
+            page_ele = '  <li><a  href="?page=%s"><span class="visuallyhidden">page </span>%s</a></li>' % (
+            loop_page, loop_page)
+        return format_html(page_ele)
+    else:
+        return ''

+ 3 - 0
blog/tests.py

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

+ 93 - 0
blog/views.py

@@ -0,0 +1,93 @@
+from django.db import connection
+from django.http import HttpResponseRedirect
+from django.shortcuts import render
+from django.views.decorators.clickjacking import xframe_options_sameorigin
+
+from blog import models
+from blog.controller import article
+
+
+def current_record(request, date):
+    page = request.GET.get('page')
+    if page is None:
+        page = 0
+    result = get_record_and_tags()
+    context = {'current_record': date,
+               'articles': article.get_article(is_paginator=True, page=int(page), date_record=date),
+               'records': result['records'],
+               'tags': result['tags']
+               }
+
+    return render(request, 'category.html', context=context)
+
+def avatar(request, pk):
+    sql = '''select avatar from auth_user au left join avatar_avatar aa on au.id = aa.user_id where au.id = {0}'''.format(
+        pk)
+    cursor = connection.cursor()
+    cursor.execute(sql)
+    fetchall = cursor.fetchall()
+    return HttpResponseRedirect('/' + fetchall[0][0])  # 跳转到主界面
+
+
+# Create your views here.
+
+@xframe_options_sameorigin
+def index(request):
+    article_sql = '''SELECT count(*) as count,strftime('%Y-%m',created_time) as datetime FROM blog_article group by strftime('%Y,%m',created_time)'''
+    tag_sql = '''SELECT bt.id,bt.name,count() as count FROM blog_tags bt  left join blog_article_tags bat on bt.id = bat.tags_id group by bt.name
+order by count desc limit 10'''
+    cursor = connection.cursor()
+    cursor.execute(article_sql)
+    fetchall = cursor.fetchall()
+
+    records = []
+    for obj in fetchall:
+        dic = {}
+        dic['count'] = obj[0]
+        dic['datetime'] = obj[1]
+        records.append(dic)
+    cursor.execute(tag_sql)
+    fetchall = cursor.fetchall()
+    tags = []
+    for obj in fetchall:
+        dic = {}
+        dic['id'] = obj[0]
+        dic['name'] = obj[1]
+        dic['count'] = obj[2]
+        tags.append(dic)
+    context = {
+        'articles_new': article.get_article(20),
+        'records': records,
+        'tags': tags
+    }
+    return render(request, 'index.html', context=context)
+
+
+def get_record_and_tags():
+    article_sql = '''SELECT count(*) as count,strftime('%Y-%m',created_time) as datetime FROM blog_article group by strftime('%Y,%m',created_time)'''
+    tag_sql = '''SELECT bt.id,bt.name,count() as count FROM blog_tags bt  left join blog_article_tags bat on bt.id = bat.tags_id group by bt.name
+    order by count desc limit 10'''
+    cursor = connection.cursor()
+    cursor.execute(article_sql)
+    fetchall = cursor.fetchall()
+
+    records = []
+    for obj in fetchall:
+        dic = {}
+        dic['count'] = obj[0]
+        dic['datetime'] = obj[1]
+        records.append(dic)
+    cursor.execute(tag_sql)
+    fetchall = cursor.fetchall()
+    tags = []
+    for obj in fetchall:
+        dic = {}
+        dic['id'] = obj[0]
+        dic['name'] = obj[1]
+        dic['count'] = obj[2]
+        tags.append(dic)
+    context = {
+        'records': records,
+        'tags': tags
+    }
+    return context

binární
db.sqlite3


+ 22 - 0
manage.py

@@ -0,0 +1,22 @@
+#!/usr/bin/env python
+"""Django's command-line utility for administrative tasks."""
+import os
+import sys
+
+
+def main():
+    """Run administrative tasks."""
+    os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'system.settings')
+    try:
+        from django.core.management import execute_from_command_line
+    except ImportError as exc:
+        raise ImportError(
+            "Couldn't import Django. Are you sure it's installed and "
+            "available on your PYTHONPATH environment variable? Did you "
+            "forget to activate a virtual environment?"
+        ) from exc
+    execute_from_command_line(sys.argv)
+
+
+if __name__ == '__main__':
+    main()

Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 18 - 0
static/2.4.1/semantic.css


binární
static/2.4.1/themes/default/assets/fonts/brand-icons.eot


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 29 - 0
static/2.4.1/themes/default/assets/fonts/brand-icons.svg


binární
static/2.4.1/themes/default/assets/fonts/brand-icons.ttf


binární
static/2.4.1/themes/default/assets/fonts/brand-icons.woff


binární
static/2.4.1/themes/default/assets/fonts/brand-icons.woff2


binární
static/2.4.1/themes/default/assets/fonts/icons.eot


binární
static/2.4.1/themes/default/assets/fonts/icons.otf


Rozdílová data souboru nebyla zobrazena, protože soubor je příliš velký
+ 41 - 0
static/2.4.1/themes/default/assets/fonts/icons.svg


binární
static/2.4.1/themes/default/assets/fonts/icons.ttf


Některé soubory nejsou zobrazeny, neboť je v těchto rozdílových datech změněno mnoho souborů