From 37c01aedbbea55a87d8e5687a40ef47772ed0733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9D=8E=E8=A3=95=E7=A5=A5?= <15377883426@163.com> Date: Sun, 28 Sep 2025 17:55:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .idea/.gitignore | 5 - .idea/DjangoBlog-master.iml | 11 - .../inspectionProfiles/profiles_settings.xml | 6 - .idea/misc.xml | 7 - .idea/modules.xml | 8 - .idea/vcs.xml | 6 - DjangoBlog-master/.coveragerc | 10 - DjangoBlog-master/.dockerignore | 11 - DjangoBlog-master/.gitattributes | 6 - DjangoBlog-master/.github/ISSUE_TEMPLATE.md | 18 - .../.github/workflows/codeql-analysis.yml | 47 - .../.github/workflows/django.yml | 136 - .../.github/workflows/docker.yml | 43 - .../.github/workflows/publish-release.yml | 39 - DjangoBlog-master/.gitignore | 80 - DjangoBlog-master/Dockerfile | 15 - DjangoBlog-master/LICENSE | 20 - DjangoBlog-master/README.md | 158 - DjangoBlog-master/accounts/__init__.py | 0 DjangoBlog-master/accounts/admin.py | 59 - DjangoBlog-master/accounts/apps.py | 5 - DjangoBlog-master/accounts/forms.py | 117 - .../accounts/migrations/0001_initial.py | 49 - ...s_remove_bloguser_created_time_and_more.py | 46 - .../accounts/migrations/__init__.py | 0 DjangoBlog-master/accounts/models.py | 35 - .../accounts/templatetags/__init__.py | 0 DjangoBlog-master/accounts/tests.py | 207 - DjangoBlog-master/accounts/urls.py | 28 - .../accounts/user_login_backend.py | 26 - DjangoBlog-master/accounts/utils.py | 49 - DjangoBlog-master/accounts/views.py | 204 - DjangoBlog-master/blog/__init__.py | 0 DjangoBlog-master/blog/admin.py | 112 - DjangoBlog-master/blog/apps.py | 5 - DjangoBlog-master/blog/context_processors.py | 43 - DjangoBlog-master/blog/documents.py | 213 - DjangoBlog-master/blog/forms.py | 19 - DjangoBlog-master/blog/management/__init__.py | 0 .../blog/management/commands/__init__.py | 0 .../blog/management/commands/build_index.py | 18 - .../management/commands/build_search_words.py | 13 - .../blog/management/commands/clear_cache.py | 11 - .../management/commands/create_testdata.py | 40 - .../blog/management/commands/ping_baidu.py | 50 - .../management/commands/sync_user_avatar.py | 47 - DjangoBlog-master/blog/middleware.py | 42 - .../blog/migrations/0001_initial.py | 137 - ...002_blogsettings_global_footer_and_more.py | 23 - .../0003_blogsettings_comment_need_review.py | 17 - ...de_blogsettings_analytics_code_and_more.py | 27 - ...options_alter_category_options_and_more.py | 300 -- .../0006_alter_blogsettings_options.py | 17 - DjangoBlog-master/blog/migrations/__init__.py | 0 DjangoBlog-master/blog/models.py | 376 -- DjangoBlog-master/blog/search_indexes.py | 13 - .../blog/templatetags/__init__.py | 0 .../blog/templatetags/blog_tags.py | 344 -- DjangoBlog-master/blog/tests.py | 232 - DjangoBlog-master/blog/urls.py | 62 - DjangoBlog-master/blog/views.py | 379 -- DjangoBlog-master/comments/__init__.py | 0 DjangoBlog-master/comments/admin.py | 47 - DjangoBlog-master/comments/apps.py | 5 - DjangoBlog-master/comments/forms.py | 13 - .../comments/migrations/0001_initial.py | 38 - .../0002_alter_comment_is_enable.py | 18 - ...ns_remove_comment_created_time_and_more.py | 60 - .../comments/migrations/__init__.py | 0 DjangoBlog-master/comments/models.py | 39 - .../comments/templatetags/__init__.py | 0 .../comments/templatetags/comments_tags.py | 30 - DjangoBlog-master/comments/tests.py | 109 - DjangoBlog-master/comments/urls.py | 11 - DjangoBlog-master/comments/utils.py | 38 - DjangoBlog-master/comments/views.py | 63 - DjangoBlog-master/db.sqlite3 | 0 .../docker-compose/docker-compose.es.yml | 48 - .../deploy/docker-compose/docker-compose.yml | 60 - DjangoBlog-master/deploy/entrypoint.sh | 31 - DjangoBlog-master/deploy/k8s/configmap.yaml | 119 - DjangoBlog-master/deploy/k8s/deployment.yaml | 274 -- DjangoBlog-master/deploy/k8s/gateway.yaml | 17 - DjangoBlog-master/deploy/k8s/pv.yaml | 94 - DjangoBlog-master/deploy/k8s/pvc.yaml | 60 - DjangoBlog-master/deploy/k8s/service.yaml | 80 - .../deploy/k8s/storageclass.yaml | 10 - DjangoBlog-master/deploy/nginx.conf | 50 - DjangoBlog-master/djangoblog/__init__.py | 1 - DjangoBlog-master/djangoblog/admin_site.py | 64 - DjangoBlog-master/djangoblog/apps.py | 11 - DjangoBlog-master/djangoblog/blog_signals.py | 122 - .../djangoblog/elasticsearch_backend.py | 183 - DjangoBlog-master/djangoblog/feeds.py | 40 - DjangoBlog-master/djangoblog/logentryadmin.py | 91 - .../djangoblog/plugin_manage/base_plugin.py | 41 - .../plugin_manage/hook_constants.py | 7 - .../djangoblog/plugin_manage/hooks.py | 44 - .../djangoblog/plugin_manage/loader.py | 19 - DjangoBlog-master/djangoblog/settings.py | 343 -- DjangoBlog-master/djangoblog/sitemap.py | 59 - DjangoBlog-master/djangoblog/spider_notify.py | 21 - DjangoBlog-master/djangoblog/tests.py | 32 - DjangoBlog-master/djangoblog/urls.py | 64 - DjangoBlog-master/djangoblog/utils.py | 232 - .../djangoblog/whoosh_cn_backend.py | 1044 ----- DjangoBlog-master/djangoblog/wsgi.py | 16 - DjangoBlog-master/docs/README-en.md | 158 - DjangoBlog-master/docs/config-en.md | 64 - DjangoBlog-master/docs/config.md | 58 - DjangoBlog-master/docs/docker-en.md | 114 - DjangoBlog-master/docs/docker.md | 114 - DjangoBlog-master/docs/es.md | 28 - DjangoBlog-master/docs/imgs/alipay.jpg | Bin 17961 -> 0 bytes DjangoBlog-master/docs/imgs/pycharm_logo.png | Bin 132045 -> 0 bytes DjangoBlog-master/docs/imgs/wechat.jpg | Bin 24722 -> 0 bytes DjangoBlog-master/docs/k8s-en.md | 141 - DjangoBlog-master/docs/k8s.md | 141 - DjangoBlog-master/get-pip.py | 4094 ----------------- .../locale/en/LC_MESSAGES/django.mo | Bin 11097 -> 0 bytes .../locale/en/LC_MESSAGES/django.po | 685 --- .../locale/zh_Hans/LC_MESSAGES/django.mo | Bin 10321 -> 0 bytes .../locale/zh_Hans/LC_MESSAGES/django.po | 667 --- .../locale/zh_Hant/LC_MESSAGES/django.mo | Bin 10268 -> 0 bytes .../locale/zh_Hant/LC_MESSAGES/django.po | 668 --- DjangoBlog-master/manage.py | 22 - DjangoBlog-master/oauth/__init__.py | 0 DjangoBlog-master/oauth/admin.py | 54 - DjangoBlog-master/oauth/apps.py | 5 - DjangoBlog-master/oauth/forms.py | 12 - .../oauth/migrations/0001_initial.py | 57 - ...ptions_alter_oauthuser_options_and_more.py | 86 - .../0003_alter_oauthuser_nickname.py | 18 - .../oauth/migrations/__init__.py | 0 DjangoBlog-master/oauth/models.py | 67 - DjangoBlog-master/oauth/oauthmanager.py | 504 -- .../oauth/templatetags/__init__.py | 1 - .../oauth/templatetags/oauth_tags.py | 22 - DjangoBlog-master/oauth/tests.py | 249 - DjangoBlog-master/oauth/urls.py | 25 - DjangoBlog-master/oauth/views.py | 253 - DjangoBlog-master/owntracks/__init__.py | 0 DjangoBlog-master/owntracks/admin.py | 7 - DjangoBlog-master/owntracks/apps.py | 5 - .../owntracks/migrations/0001_initial.py | 31 - ...0002_alter_owntracklog_options_and_more.py | 22 - .../owntracks/migrations/__init__.py | 0 DjangoBlog-master/owntracks/models.py | 20 - DjangoBlog-master/owntracks/tests.py | 64 - DjangoBlog-master/owntracks/urls.py | 12 - DjangoBlog-master/owntracks/views.py | 127 - DjangoBlog-master/plugins/__init__.py | 1 - .../plugins/article_copyright/__init__.py | 1 - .../plugins/article_copyright/plugin.py | 32 - .../plugins/external_links/__init__.py | 1 - .../plugins/external_links/plugin.py | 48 - .../plugins/reading_time/__init__.py | 1 - .../plugins/reading_time/plugin.py | 43 - .../plugins/seo_optimizer/__init__.py | 1 - .../plugins/seo_optimizer/plugin.py | 142 - .../plugins/view_count/__init__.py | 1 - .../plugins/view_count/plugin.py | 18 - DjangoBlog-master/requirements.txt | Bin 2554 -> 0 bytes .../servermanager/MemcacheStorage.py | 32 - DjangoBlog-master/servermanager/__init__.py | 0 DjangoBlog-master/servermanager/admin.py | 19 - .../servermanager/api/__init__.py | 1 - .../servermanager/api/blogapi.py | 27 - .../servermanager/api/commonapi.py | 64 - DjangoBlog-master/servermanager/apps.py | 5 - .../servermanager/migrations/0001_initial.py | 45 - ...002_alter_emailsendlog_options_and_more.py | 32 - .../servermanager/migrations/__init__.py | 0 DjangoBlog-master/servermanager/models.py | 33 - DjangoBlog-master/servermanager/robot.py | 187 - DjangoBlog-master/servermanager/tests.py | 79 - DjangoBlog-master/servermanager/urls.py | 10 - DjangoBlog-master/servermanager/views.py | 1 - .../templates/account/forget_password.html | 30 - .../templates/account/login.html | 46 - .../templates/account/registration_form.html | 29 - .../templates/account/result.html | 27 - .../templates/blog/article_archives.html | 60 - .../templates/blog/article_detail.html | 52 - .../templates/blog/article_index.html | 42 - .../templates/blog/error_page.html | 45 - .../templates/blog/links_list.html | 44 - .../templates/blog/tags/article_info.html | 74 - .../blog/tags/article_meta_info.html | 59 - .../blog/tags/article_pagination.html | 17 - .../templates/blog/tags/article_tag_list.html | 19 - .../templates/blog/tags/breadcrumb.html | 19 - .../templates/blog/tags/sidebar.html | 136 - .../templates/comments/tags/comment_item.html | 34 - .../comments/tags/comment_item_tree.html | 54 - .../templates/comments/tags/comment_list.html | 45 - .../templates/comments/tags/post_comment.html | 33 - .../templates/oauth/bindsuccess.html | 22 - .../templates/oauth/oauth_applications.html | 13 - .../templates/oauth/require_email.html | 46 - .../templates/owntracks/show_log_dates.html | 17 - .../templates/owntracks/show_maps.html | 135 - .../search/indexes/blog/article_text.txt | 3 - .../templates/search/search.html | 66 - .../templates/share_layout/adsense.html | 6 - .../templates/share_layout/base.html | 123 - .../templates/share_layout/base_account.html | 47 - .../templates/share_layout/footer.html | 56 - .../templates/share_layout/nav.html | 30 - .../templates/share_layout/nav_node.html | 19 - main.py | 16 - setup.py | 12 - 212 files changed, 18634 deletions(-) delete mode 100644 .idea/.gitignore delete mode 100644 .idea/DjangoBlog-master.iml delete mode 100644 .idea/inspectionProfiles/profiles_settings.xml delete mode 100644 .idea/misc.xml delete mode 100644 .idea/modules.xml delete mode 100644 .idea/vcs.xml delete mode 100644 DjangoBlog-master/.coveragerc delete mode 100644 DjangoBlog-master/.dockerignore delete mode 100644 DjangoBlog-master/.gitattributes delete mode 100644 DjangoBlog-master/.github/ISSUE_TEMPLATE.md delete mode 100644 DjangoBlog-master/.github/workflows/codeql-analysis.yml delete mode 100644 DjangoBlog-master/.github/workflows/django.yml delete mode 100644 DjangoBlog-master/.github/workflows/docker.yml delete mode 100644 DjangoBlog-master/.github/workflows/publish-release.yml delete mode 100644 DjangoBlog-master/.gitignore delete mode 100644 DjangoBlog-master/Dockerfile delete mode 100644 DjangoBlog-master/LICENSE delete mode 100644 DjangoBlog-master/README.md delete mode 100644 DjangoBlog-master/accounts/__init__.py delete mode 100644 DjangoBlog-master/accounts/admin.py delete mode 100644 DjangoBlog-master/accounts/apps.py delete mode 100644 DjangoBlog-master/accounts/forms.py delete mode 100644 DjangoBlog-master/accounts/migrations/0001_initial.py delete mode 100644 DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py delete mode 100644 DjangoBlog-master/accounts/migrations/__init__.py delete mode 100644 DjangoBlog-master/accounts/models.py delete mode 100644 DjangoBlog-master/accounts/templatetags/__init__.py delete mode 100644 DjangoBlog-master/accounts/tests.py delete mode 100644 DjangoBlog-master/accounts/urls.py delete mode 100644 DjangoBlog-master/accounts/user_login_backend.py delete mode 100644 DjangoBlog-master/accounts/utils.py delete mode 100644 DjangoBlog-master/accounts/views.py delete mode 100644 DjangoBlog-master/blog/__init__.py delete mode 100644 DjangoBlog-master/blog/admin.py delete mode 100644 DjangoBlog-master/blog/apps.py delete mode 100644 DjangoBlog-master/blog/context_processors.py delete mode 100644 DjangoBlog-master/blog/documents.py delete mode 100644 DjangoBlog-master/blog/forms.py delete mode 100644 DjangoBlog-master/blog/management/__init__.py delete mode 100644 DjangoBlog-master/blog/management/commands/__init__.py delete mode 100644 DjangoBlog-master/blog/management/commands/build_index.py delete mode 100644 DjangoBlog-master/blog/management/commands/build_search_words.py delete mode 100644 DjangoBlog-master/blog/management/commands/clear_cache.py delete mode 100644 DjangoBlog-master/blog/management/commands/create_testdata.py delete mode 100644 DjangoBlog-master/blog/management/commands/ping_baidu.py delete mode 100644 DjangoBlog-master/blog/management/commands/sync_user_avatar.py delete mode 100644 DjangoBlog-master/blog/middleware.py delete mode 100644 DjangoBlog-master/blog/migrations/0001_initial.py delete mode 100644 DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py delete mode 100644 DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py delete mode 100644 DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py delete mode 100644 DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py delete mode 100644 DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py delete mode 100644 DjangoBlog-master/blog/migrations/__init__.py delete mode 100644 DjangoBlog-master/blog/models.py delete mode 100644 DjangoBlog-master/blog/search_indexes.py delete mode 100644 DjangoBlog-master/blog/templatetags/__init__.py delete mode 100644 DjangoBlog-master/blog/templatetags/blog_tags.py delete mode 100644 DjangoBlog-master/blog/tests.py delete mode 100644 DjangoBlog-master/blog/urls.py delete mode 100644 DjangoBlog-master/blog/views.py delete mode 100644 DjangoBlog-master/comments/__init__.py delete mode 100644 DjangoBlog-master/comments/admin.py delete mode 100644 DjangoBlog-master/comments/apps.py delete mode 100644 DjangoBlog-master/comments/forms.py delete mode 100644 DjangoBlog-master/comments/migrations/0001_initial.py delete mode 100644 DjangoBlog-master/comments/migrations/0002_alter_comment_is_enable.py delete mode 100644 DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py delete mode 100644 DjangoBlog-master/comments/migrations/__init__.py delete mode 100644 DjangoBlog-master/comments/models.py delete mode 100644 DjangoBlog-master/comments/templatetags/__init__.py delete mode 100644 DjangoBlog-master/comments/templatetags/comments_tags.py delete mode 100644 DjangoBlog-master/comments/tests.py delete mode 100644 DjangoBlog-master/comments/urls.py delete mode 100644 DjangoBlog-master/comments/utils.py delete mode 100644 DjangoBlog-master/comments/views.py delete mode 100644 DjangoBlog-master/db.sqlite3 delete mode 100644 DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml delete mode 100644 DjangoBlog-master/deploy/docker-compose/docker-compose.yml delete mode 100644 DjangoBlog-master/deploy/entrypoint.sh delete mode 100644 DjangoBlog-master/deploy/k8s/configmap.yaml delete mode 100644 DjangoBlog-master/deploy/k8s/deployment.yaml delete mode 100644 DjangoBlog-master/deploy/k8s/gateway.yaml delete mode 100644 DjangoBlog-master/deploy/k8s/pv.yaml delete mode 100644 DjangoBlog-master/deploy/k8s/pvc.yaml delete mode 100644 DjangoBlog-master/deploy/k8s/service.yaml delete mode 100644 DjangoBlog-master/deploy/k8s/storageclass.yaml delete mode 100644 DjangoBlog-master/deploy/nginx.conf delete mode 100644 DjangoBlog-master/djangoblog/__init__.py delete mode 100644 DjangoBlog-master/djangoblog/admin_site.py delete mode 100644 DjangoBlog-master/djangoblog/apps.py delete mode 100644 DjangoBlog-master/djangoblog/blog_signals.py delete mode 100644 DjangoBlog-master/djangoblog/elasticsearch_backend.py delete mode 100644 DjangoBlog-master/djangoblog/feeds.py delete mode 100644 DjangoBlog-master/djangoblog/logentryadmin.py delete mode 100644 DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py delete mode 100644 DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py delete mode 100644 DjangoBlog-master/djangoblog/plugin_manage/hooks.py delete mode 100644 DjangoBlog-master/djangoblog/plugin_manage/loader.py delete mode 100644 DjangoBlog-master/djangoblog/settings.py delete mode 100644 DjangoBlog-master/djangoblog/sitemap.py delete mode 100644 DjangoBlog-master/djangoblog/spider_notify.py delete mode 100644 DjangoBlog-master/djangoblog/tests.py delete mode 100644 DjangoBlog-master/djangoblog/urls.py delete mode 100644 DjangoBlog-master/djangoblog/utils.py delete mode 100644 DjangoBlog-master/djangoblog/whoosh_cn_backend.py delete mode 100644 DjangoBlog-master/djangoblog/wsgi.py delete mode 100644 DjangoBlog-master/docs/README-en.md delete mode 100644 DjangoBlog-master/docs/config-en.md delete mode 100644 DjangoBlog-master/docs/config.md delete mode 100644 DjangoBlog-master/docs/docker-en.md delete mode 100644 DjangoBlog-master/docs/docker.md delete mode 100644 DjangoBlog-master/docs/es.md delete mode 100644 DjangoBlog-master/docs/imgs/alipay.jpg delete mode 100644 DjangoBlog-master/docs/imgs/pycharm_logo.png delete mode 100644 DjangoBlog-master/docs/imgs/wechat.jpg delete mode 100644 DjangoBlog-master/docs/k8s-en.md delete mode 100644 DjangoBlog-master/docs/k8s.md delete mode 100644 DjangoBlog-master/get-pip.py delete mode 100644 DjangoBlog-master/locale/en/LC_MESSAGES/django.mo delete mode 100644 DjangoBlog-master/locale/en/LC_MESSAGES/django.po delete mode 100644 DjangoBlog-master/locale/zh_Hans/LC_MESSAGES/django.mo delete mode 100644 DjangoBlog-master/locale/zh_Hans/LC_MESSAGES/django.po delete mode 100644 DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.mo delete mode 100644 DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.po delete mode 100644 DjangoBlog-master/manage.py delete mode 100644 DjangoBlog-master/oauth/__init__.py delete mode 100644 DjangoBlog-master/oauth/admin.py delete mode 100644 DjangoBlog-master/oauth/apps.py delete mode 100644 DjangoBlog-master/oauth/forms.py delete mode 100644 DjangoBlog-master/oauth/migrations/0001_initial.py delete mode 100644 DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py delete mode 100644 DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py delete mode 100644 DjangoBlog-master/oauth/migrations/__init__.py delete mode 100644 DjangoBlog-master/oauth/models.py delete mode 100644 DjangoBlog-master/oauth/oauthmanager.py delete mode 100644 DjangoBlog-master/oauth/templatetags/__init__.py delete mode 100644 DjangoBlog-master/oauth/templatetags/oauth_tags.py delete mode 100644 DjangoBlog-master/oauth/tests.py delete mode 100644 DjangoBlog-master/oauth/urls.py delete mode 100644 DjangoBlog-master/oauth/views.py delete mode 100644 DjangoBlog-master/owntracks/__init__.py delete mode 100644 DjangoBlog-master/owntracks/admin.py delete mode 100644 DjangoBlog-master/owntracks/apps.py delete mode 100644 DjangoBlog-master/owntracks/migrations/0001_initial.py delete mode 100644 DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py delete mode 100644 DjangoBlog-master/owntracks/migrations/__init__.py delete mode 100644 DjangoBlog-master/owntracks/models.py delete mode 100644 DjangoBlog-master/owntracks/tests.py delete mode 100644 DjangoBlog-master/owntracks/urls.py delete mode 100644 DjangoBlog-master/owntracks/views.py delete mode 100644 DjangoBlog-master/plugins/__init__.py delete mode 100644 DjangoBlog-master/plugins/article_copyright/__init__.py delete mode 100644 DjangoBlog-master/plugins/article_copyright/plugin.py delete mode 100644 DjangoBlog-master/plugins/external_links/__init__.py delete mode 100644 DjangoBlog-master/plugins/external_links/plugin.py delete mode 100644 DjangoBlog-master/plugins/reading_time/__init__.py delete mode 100644 DjangoBlog-master/plugins/reading_time/plugin.py delete mode 100644 DjangoBlog-master/plugins/seo_optimizer/__init__.py delete mode 100644 DjangoBlog-master/plugins/seo_optimizer/plugin.py delete mode 100644 DjangoBlog-master/plugins/view_count/__init__.py delete mode 100644 DjangoBlog-master/plugins/view_count/plugin.py delete mode 100644 DjangoBlog-master/requirements.txt delete mode 100644 DjangoBlog-master/servermanager/MemcacheStorage.py delete mode 100644 DjangoBlog-master/servermanager/__init__.py delete mode 100644 DjangoBlog-master/servermanager/admin.py delete mode 100644 DjangoBlog-master/servermanager/api/__init__.py delete mode 100644 DjangoBlog-master/servermanager/api/blogapi.py delete mode 100644 DjangoBlog-master/servermanager/api/commonapi.py delete mode 100644 DjangoBlog-master/servermanager/apps.py delete mode 100644 DjangoBlog-master/servermanager/migrations/0001_initial.py delete mode 100644 DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py delete mode 100644 DjangoBlog-master/servermanager/migrations/__init__.py delete mode 100644 DjangoBlog-master/servermanager/models.py delete mode 100644 DjangoBlog-master/servermanager/robot.py delete mode 100644 DjangoBlog-master/servermanager/tests.py delete mode 100644 DjangoBlog-master/servermanager/urls.py delete mode 100644 DjangoBlog-master/servermanager/views.py delete mode 100644 DjangoBlog-master/templates/account/forget_password.html delete mode 100644 DjangoBlog-master/templates/account/login.html delete mode 100644 DjangoBlog-master/templates/account/registration_form.html delete mode 100644 DjangoBlog-master/templates/account/result.html delete mode 100644 DjangoBlog-master/templates/blog/article_archives.html delete mode 100644 DjangoBlog-master/templates/blog/article_detail.html delete mode 100644 DjangoBlog-master/templates/blog/article_index.html delete mode 100644 DjangoBlog-master/templates/blog/error_page.html delete mode 100644 DjangoBlog-master/templates/blog/links_list.html delete mode 100644 DjangoBlog-master/templates/blog/tags/article_info.html delete mode 100644 DjangoBlog-master/templates/blog/tags/article_meta_info.html delete mode 100644 DjangoBlog-master/templates/blog/tags/article_pagination.html delete mode 100644 DjangoBlog-master/templates/blog/tags/article_tag_list.html delete mode 100644 DjangoBlog-master/templates/blog/tags/breadcrumb.html delete mode 100644 DjangoBlog-master/templates/blog/tags/sidebar.html delete mode 100644 DjangoBlog-master/templates/comments/tags/comment_item.html delete mode 100644 DjangoBlog-master/templates/comments/tags/comment_item_tree.html delete mode 100644 DjangoBlog-master/templates/comments/tags/comment_list.html delete mode 100644 DjangoBlog-master/templates/comments/tags/post_comment.html delete mode 100644 DjangoBlog-master/templates/oauth/bindsuccess.html delete mode 100644 DjangoBlog-master/templates/oauth/oauth_applications.html delete mode 100644 DjangoBlog-master/templates/oauth/require_email.html delete mode 100644 DjangoBlog-master/templates/owntracks/show_log_dates.html delete mode 100644 DjangoBlog-master/templates/owntracks/show_maps.html delete mode 100644 DjangoBlog-master/templates/search/indexes/blog/article_text.txt delete mode 100644 DjangoBlog-master/templates/search/search.html delete mode 100644 DjangoBlog-master/templates/share_layout/adsense.html delete mode 100644 DjangoBlog-master/templates/share_layout/base.html delete mode 100644 DjangoBlog-master/templates/share_layout/base_account.html delete mode 100644 DjangoBlog-master/templates/share_layout/footer.html delete mode 100644 DjangoBlog-master/templates/share_layout/nav.html delete mode 100644 DjangoBlog-master/templates/share_layout/nav_node.html delete mode 100644 main.py delete mode 100644 setup.py diff --git a/.idea/.gitignore b/.idea/.gitignore deleted file mode 100644 index 10b731c..0000000 --- a/.idea/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# 默认忽略的文件 -/shelf/ -/workspace.xml -# 基于编辑器的 HTTP 客户端请求 -/httpRequests/ diff --git a/.idea/DjangoBlog-master.iml b/.idea/DjangoBlog-master.iml deleted file mode 100644 index d2720cc..0000000 --- a/.idea/DjangoBlog-master.iml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml deleted file mode 100644 index 105ce2d..0000000 --- a/.idea/inspectionProfiles/profiles_settings.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml deleted file mode 100644 index 5668ada..0000000 --- a/.idea/misc.xml +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml deleted file mode 100644 index 5494c00..0000000 --- a/.idea/modules.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml deleted file mode 100644 index 94a25f7..0000000 --- a/.idea/vcs.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/DjangoBlog-master/.coveragerc b/DjangoBlog-master/.coveragerc deleted file mode 100644 index 9757484..0000000 --- a/DjangoBlog-master/.coveragerc +++ /dev/null @@ -1,10 +0,0 @@ -[run] -source = . -include = *.py -omit = - *migrations* - *tests* - *.html - *whoosh_cn_backend* - *settings.py* - *venv* diff --git a/DjangoBlog-master/.dockerignore b/DjangoBlog-master/.dockerignore deleted file mode 100644 index 2818c38..0000000 --- a/DjangoBlog-master/.dockerignore +++ /dev/null @@ -1,11 +0,0 @@ -bin/data/ -# virtualenv -venv/ -collectedstatic/ -djangoblog/whoosh_index/ -uploads/ -settings_production.py -*.md -docs/ -logs/ -static/ \ No newline at end of file diff --git a/DjangoBlog-master/.gitattributes b/DjangoBlog-master/.gitattributes deleted file mode 100644 index fd52ece..0000000 --- a/DjangoBlog-master/.gitattributes +++ /dev/null @@ -1,6 +0,0 @@ -blog/static/* linguist-vendored -*.js linguist-vendored -*.css linguist-vendored -* text=auto -*.sh text eol=lf -*.conf text eol=lf \ No newline at end of file diff --git a/DjangoBlog-master/.github/ISSUE_TEMPLATE.md b/DjangoBlog-master/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 2b5b7aa..0000000 --- a/DjangoBlog-master/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,18 +0,0 @@ - - -**我确定我已经查看了** (标注`[ ]`为`[x]`) - -- [ ] [DjangoBlog的readme](https://github.com/liangliangyy/DjangoBlog/blob/master/README.md) -- [ ] [配置说明](https://github.com/liangliangyy/DjangoBlog/blob/master/bin/config.md) -- [ ] [其他 Issues](https://github.com/liangliangyy/DjangoBlog/issues) - ----- - -**我要申请** (标注`[ ]`为`[x]`) - -- [ ] BUG 反馈 -- [ ] 添加新的特性或者功能 -- [ ] 请求技术支持 diff --git a/DjangoBlog-master/.github/workflows/codeql-analysis.yml b/DjangoBlog-master/.github/workflows/codeql-analysis.yml deleted file mode 100644 index 6b76522..0000000 --- a/DjangoBlog-master/.github/workflows/codeql-analysis.yml +++ /dev/null @@ -1,47 +0,0 @@ -name: "CodeQL" - -on: - push: - branches: - - master - - dev - paths-ignore: - - '**/*.md' - - '**/*.css' - - '**/*.js' - - '**/*.yml' - - '**/*.txt' - pull_request: - branches: - - master - - dev - paths-ignore: - - '**/*.md' - - '**/*.css' - - '**/*.js' - - '**/*.yml' - - '**/*.txt' - schedule: - - cron: '30 1 * * 0' - - -jobs: - CodeQL-Build: - runs-on: ubuntu-latest - permissions: - security-events: write - actions: read - contents: read - - steps: - - name: Checkout repository - uses: actions/checkout@v3 - - - name: Initialize CodeQL - uses: github/codeql-action/init@v2 - - - name: Autobuild - uses: github/codeql-action/autobuild@v2 - - - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v2 \ No newline at end of file diff --git a/DjangoBlog-master/.github/workflows/django.yml b/DjangoBlog-master/.github/workflows/django.yml deleted file mode 100644 index 94baea9..0000000 --- a/DjangoBlog-master/.github/workflows/django.yml +++ /dev/null @@ -1,136 +0,0 @@ -name: Django CI - -on: - push: - branches: - - master - - dev - paths-ignore: - - '**/*.md' - - '**/*.css' - - '**/*.js' - pull_request: - branches: - - master - - dev - paths-ignore: - - '**/*.md' - - '**/*.css' - - '**/*.js' - -jobs: - build-normal: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: ["3.10","3.11" ] - - steps: - - name: Start MySQL - uses: samin/mysql-action@v1.3 - with: - host port: 3306 - container port: 3306 - character set server: utf8mb4 - collation server: utf8mb4_general_ci - mysql version: latest - mysql root password: root - mysql database: djangoblog - mysql user: root - mysql password: root - - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Run Tests - env: - DJANGO_MYSQL_PASSWORD: root - DJANGO_MYSQL_HOST: 127.0.0.1 - run: | - python manage.py makemigrations - python manage.py migrate - python manage.py test - - build-with-es: - runs-on: ubuntu-latest - strategy: - max-parallel: 4 - matrix: - python-version: ["3.10","3.11" ] - - steps: - - name: Start MySQL - uses: samin/mysql-action@v1.3 - with: - host port: 3306 - container port: 3306 - character set server: utf8mb4 - collation server: utf8mb4_general_ci - mysql version: latest - mysql root password: root - mysql database: djangoblog - mysql user: root - mysql password: root - - - name: Configure sysctl limits - run: | - sudo swapoff -a - sudo sysctl -w vm.swappiness=1 - sudo sysctl -w fs.file-max=262144 - sudo sysctl -w vm.max_map_count=262144 - - - uses: miyataka/elasticsearch-github-actions@1 - - with: - stack-version: '7.12.1' - plugins: 'https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip' - - - - uses: actions/checkout@v3 - - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v4 - with: - python-version: ${{ matrix.python-version }} - cache: 'pip' - - name: Install Dependencies - run: | - python -m pip install --upgrade pip - pip install -r requirements.txt - - name: Run Tests - env: - DJANGO_MYSQL_PASSWORD: root - DJANGO_MYSQL_HOST: 127.0.0.1 - DJANGO_ELASTICSEARCH_HOST: 127.0.0.1:9200 - run: | - python manage.py makemigrations - python manage.py migrate - coverage run manage.py test - coverage xml - - - name: Upload coverage to Codecov - uses: codecov/codecov-action@v1 - - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v2 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - push: false - tags: djangoblog/djangoblog:dev diff --git a/DjangoBlog-master/.github/workflows/docker.yml b/DjangoBlog-master/.github/workflows/docker.yml deleted file mode 100644 index a312e2f..0000000 --- a/DjangoBlog-master/.github/workflows/docker.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: docker - -on: - push: - paths-ignore: - - '**/*.md' - - '**/*.yml' - branches: - - 'master' - - 'dev' - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Set env to docker dev tag - if: endsWith(github.ref, '/dev') - run: | - echo "DOCKER_TAG=test" >> $GITHUB_ENV - - name: Set env to docker latest tag - if: endsWith(github.ref, '/master') - run: | - echo "DOCKER_TAG=latest" >> $GITHUB_ENV - - name: Checkout - uses: actions/checkout@v3 - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - push: true - tags: ${{ secrets.DOCKERHUB_USERNAME }}/djangoblog:${{env.DOCKER_TAG}} - - diff --git a/DjangoBlog-master/.github/workflows/publish-release.yml b/DjangoBlog-master/.github/workflows/publish-release.yml deleted file mode 100644 index 5eb0853..0000000 --- a/DjangoBlog-master/.github/workflows/publish-release.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: publish release - -on: - release: - types: [ published ] - -jobs: - docker: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v3 - - - name: Docker meta - id: meta - uses: docker/metadata-action@v3 - with: - images: name/app - - name: Set up QEMU - uses: docker/setup-qemu-action@v2 - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v2 - - name: Login to DockerHub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKERHUB_USERNAME }} - password: ${{ secrets.DOCKERHUB_TOKEN }} - - name: Build and push - uses: docker/build-push-action@v3 - with: - context: . - push: true - platforms: | - linux/amd64 - linux/arm64 - linux/arm/v7 - linux/arm/v6 - linux/386 - tags: ${{ secrets.DOCKERHUB_USERNAME }}/djangoblog:${{ github.event.release.tag_name }} diff --git a/DjangoBlog-master/.gitignore b/DjangoBlog-master/.gitignore deleted file mode 100644 index 3015816..0000000 --- a/DjangoBlog-master/.gitignore +++ /dev/null @@ -1,80 +0,0 @@ -# Byte-compiled / optimized / DLL files -__pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging -.Python -env/ -build/ -develop-eggs/ -dist/ -downloads/ -eggs/ -.eggs/ -lib/ -lib64/ -parts/ -sdist/ -var/ -*.egg-info/ -.installed.cfg -*.egg - -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest -*.spec - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*,cover - -# Translations -*.pot - -# Django stuff: -*.log -logs/ - -# Sphinx documentation -docs/_build/ - -# PyBuilder -target/ - - -# PyCharm -# http://www.jetbrains.com/pycharm/webhelp/project.html -.idea -.iml -static/ -# virtualenv -venv/ - -collectedstatic/ -djangoblog/whoosh_index/ -google93fd32dbd906620a.html -baidu_verify_FlHL7cUyC9.html -BingSiteAuth.xml -cb9339dbe2ff86a5aa169d28dba5f615.txt -werobot_session.* -django.jpg -uploads/ -settings_production.py -werobot_session.db -bin/datas/ diff --git a/DjangoBlog-master/Dockerfile b/DjangoBlog-master/Dockerfile deleted file mode 100644 index 80b46ac..0000000 --- a/DjangoBlog-master/Dockerfile +++ /dev/null @@ -1,15 +0,0 @@ -FROM python:3.11 -ENV PYTHONUNBUFFERED 1 -WORKDIR /code/djangoblog/ -RUN apt-get update && \ - apt-get install default-libmysqlclient-dev gettext -y && \ - rm -rf /var/lib/apt/lists/* -ADD requirements.txt requirements.txt -RUN pip install --upgrade pip && \ - pip install --no-cache-dir -r requirements.txt && \ - pip install --no-cache-dir gunicorn[gevent] && \ - pip cache purge - -ADD . . -RUN chmod +x /code/djangoblog/deploy/entrypoint.sh -ENTRYPOINT ["/code/djangoblog/deploy/entrypoint.sh"] diff --git a/DjangoBlog-master/LICENSE b/DjangoBlog-master/LICENSE deleted file mode 100644 index 3b08474..0000000 --- a/DjangoBlog-master/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2025 车亮亮 - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/DjangoBlog-master/README.md b/DjangoBlog-master/README.md deleted file mode 100644 index 56aa4cc..0000000 --- a/DjangoBlog-master/README.md +++ /dev/null @@ -1,158 +0,0 @@ -# DjangoBlog - -

- Django CI - CodeQL - codecov - license -

- -

- 一款功能强大、设计优雅的现代化博客系统 -
- English简体中文 -

- ---- - -DjangoBlog 是一款基于 Python 3.10 和 Django 4.0 构建的高性能博客平台。它不仅提供了传统博客的所有核心功能,还通过一个灵活的插件系统,让您可以轻松扩展和定制您的网站。无论您是个人博主、技术爱好者还是内容创作者,DjangoBlog 都旨在为您提供一个稳定、高效且易于维护的写作和发布环境。 - -## ✨ 特性亮点 - -- **强大的内容管理**: 支持文章、独立页面、分类和标签的完整管理。内置强大的 Markdown 编辑器,支持代码语法高亮。 -- **全文搜索**: 集成搜索引擎,提供快速、精准的文章内容搜索。 -- **互动评论系统**: 支持回复、邮件提醒等功能,评论内容同样支持 Markdown。 -- **灵活的侧边栏**: 可自定义展示最新文章、最多阅读、标签云等模块。 -- **社交化登录**: 内置 OAuth 支持,已集成 Google, GitHub, Facebook, 微博, QQ 等主流平台。 -- **高性能缓存**: 原生支持 Redis 缓存,并提供自动刷新机制,确保网站高速响应。 -- **SEO 友好**: 具备基础 SEO 功能,新内容发布后可自动通知 Google 和百度。 -- **便捷的插件系统**: 通过创建独立的插件来扩展博客功能,代码解耦,易于维护。我们已经通过插件实现了文章浏览计数、SEO 优化等功能! -- **集成图床**: 内置简单的图床功能,方便图片上传和管理。 -- **自动化前端**: 集成 `django-compressor`,自动压缩和优化 CSS 及 JavaScript 文件。 -- **健壮的运维**: 内置网站异常邮件提醒和微信公众号管理功能。 - -## 🛠️ 技术栈 - -- **后端**: Python 3.10, Django 4.0 -- **数据库**: MySQL, SQLite (可配置) -- **缓存**: Redis -- **前端**: HTML5, CSS3, JavaScript -- **搜索**: Whoosh, Elasticsearch (可配置) -- **编辑器**: Markdown (mdeditor) - -## 🚀 快速开始 - -### 1. 环境准备 - -确保您的系统中已安装 Python 3.10+ 和 MySQL/MariaDB。 - -### 2. 克隆与安装 - -```bash -# 克隆项目到本地 -git clone https://github.com/liangliangyy/DjangoBlog.git -cd DjangoBlog - -# 安装依赖 -pip install -r requirements.txt -``` - -### 3. 项目配置 - -- **数据库**: - 打开 `djangoblog/settings.py` 文件,找到 `DATABASES` 配置项,修改为您的 MySQL 连接信息。 - - ```python - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'djangoblog', - 'USER': 'root', - 'PASSWORD': 'your_password', - 'HOST': '127.0.0.1', - 'PORT': 3306, - } - } - ``` - 在 MySQL 中创建数据库: - ```sql - CREATE DATABASE `djangoblog` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ``` - -- **更多配置**: - 关于邮件发送、OAuth 登录、缓存等更多高级配置,请参阅我们的 [详细配置文档](/docs/config.md)。 - -### 4. 初始化数据库 - -```bash -python manage.py makemigrations -python manage.py migrate - -# 创建一个超级管理员账户 -python manage.py createsuperuser -``` - -### 5. 运行项目 - -```bash -# (可选) 生成一些测试数据 -python manage.py create_testdata - -# (可选) 收集和压缩静态文件 -python manage.py collectstatic --noinput -python manage.py compress --force - -# 启动开发服务器 -python manage.py runserver -``` - -现在,在您的浏览器中访问 `http://127.0.0.1:8000/`,您应该能看到 DjangoBlog 的首页了! - -## 部署 - -- **传统部署**: 我们为您准备了非常详细的 [服务器部署教程](https://www.lylinux.net/article/2019/8/5/58.html)。 -- **Docker 部署**: 项目已全面支持 Docker。如果您熟悉容器化技术,请参考 [Docker 部署文档](/docs/docker.md) 来快速启动。 -- **Kubernetes 部署**: 我们也提供了完整的 [Kubernetes 部署指南](/docs/k8s.md),助您轻松上云。 - -## 🧩 插件系统 - -插件系统是 DjangoBlog 的核心特色之一。它允许您在不修改核心代码的情况下,通过编写独立的插件来为您的博客添加新功能。 - -- **工作原理**: 插件通过在预定义的“钩子”上注册回调函数来工作。例如,当一篇文章被渲染时,`after_article_body_get` 钩子会被触发,所有注册到此钩子的函数都会被执行。 -- **现有插件**: `view_count`(浏览计数), `seo_optimizer`(SEO优化)等都是通过插件系统实现的。 -- **开发您自己的插件**: 只需在 `plugins` 目录下创建一个新的文件夹,并编写您的 `plugin.py`。欢迎探索并为 DjangoBlog 社区贡献您的创意! - -## 🤝 贡献指南 - -我们热烈欢迎任何形式的贡献!如果您有好的想法或发现了 Bug,请随时提交 Issue 或 Pull Request。 - -## 📄 许可证 - -本项目基于 [MIT License](LICENSE) 开源。 - ---- - -## ❤️ 支持与赞助 - -如果您觉得这个项目对您有帮助,并且希望支持我继续维护和开发新功能,欢迎请我喝杯咖啡!您的每一份支持都是我前进的最大动力。 - -

- 支付宝赞助 - 微信赞助 -

-

- (左) 支付宝 / (右) 微信 -

- -## 🙏 鸣谢 - -特别感谢 **JetBrains** 为本项目提供的免费开源许可证。 - -

- - JetBrains Logo - -

- ---- -> 如果本项目帮助到了你,请在[这里](https://github.com/liangliangyy/DjangoBlog/issues/214)留下你的网址,让更多的人看到。您的回复将会是我继续更新维护下去的动力。 diff --git a/DjangoBlog-master/accounts/__init__.py b/DjangoBlog-master/accounts/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/accounts/admin.py b/DjangoBlog-master/accounts/admin.py deleted file mode 100644 index 32e483c..0000000 --- a/DjangoBlog-master/accounts/admin.py +++ /dev/null @@ -1,59 +0,0 @@ -from django import forms -from django.contrib.auth.admin import UserAdmin -from django.contrib.auth.forms import UserChangeForm -from django.contrib.auth.forms import UsernameField -from django.utils.translation import gettext_lazy as _ - -# Register your models here. -from .models import BlogUser - - -class BlogUserCreationForm(forms.ModelForm): - password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput) - password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput) - - class Meta: - model = BlogUser - fields = ('email',) - - def clean_password2(self): - # Check that the two password entries match - password1 = self.cleaned_data.get("password1") - password2 = self.cleaned_data.get("password2") - if password1 and password2 and password1 != password2: - raise forms.ValidationError(_("passwords do not match")) - return password2 - - def save(self, commit=True): - # Save the provided password in hashed format - user = super().save(commit=False) - user.set_password(self.cleaned_data["password1"]) - if commit: - user.source = 'adminsite' - user.save() - return user - - -class BlogUserChangeForm(UserChangeForm): - class Meta: - model = BlogUser - fields = '__all__' - field_classes = {'username': UsernameField} - - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - -class BlogUserAdmin(UserAdmin): - form = BlogUserChangeForm - add_form = BlogUserCreationForm - list_display = ( - 'id', - 'nickname', - 'username', - 'email', - 'last_login', - 'date_joined', - 'source') - list_display_links = ('id', 'username') - ordering = ('-id',) diff --git a/DjangoBlog-master/accounts/apps.py b/DjangoBlog-master/accounts/apps.py deleted file mode 100644 index 9b3fc5a..0000000 --- a/DjangoBlog-master/accounts/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class AccountsConfig(AppConfig): - name = 'accounts' diff --git a/DjangoBlog-master/accounts/forms.py b/DjangoBlog-master/accounts/forms.py deleted file mode 100644 index fce4137..0000000 --- a/DjangoBlog-master/accounts/forms.py +++ /dev/null @@ -1,117 +0,0 @@ -from django import forms -from django.contrib.auth import get_user_model, password_validation -from django.contrib.auth.forms import AuthenticationForm, UserCreationForm -from django.core.exceptions import ValidationError -from django.forms import widgets -from django.utils.translation import gettext_lazy as _ -from . import utils -from .models import BlogUser - - -class LoginForm(AuthenticationForm): - def __init__(self, *args, **kwargs): - super(LoginForm, self).__init__(*args, **kwargs) - self.fields['username'].widget = widgets.TextInput( - attrs={'placeholder': "username", "class": "form-control"}) - self.fields['password'].widget = widgets.PasswordInput( - attrs={'placeholder': "password", "class": "form-control"}) - - -class RegisterForm(UserCreationForm): - def __init__(self, *args, **kwargs): - super(RegisterForm, self).__init__(*args, **kwargs) - - self.fields['username'].widget = widgets.TextInput( - attrs={'placeholder': "username", "class": "form-control"}) - self.fields['email'].widget = widgets.EmailInput( - attrs={'placeholder': "email", "class": "form-control"}) - self.fields['password1'].widget = widgets.PasswordInput( - attrs={'placeholder': "password", "class": "form-control"}) - self.fields['password2'].widget = widgets.PasswordInput( - attrs={'placeholder': "repeat password", "class": "form-control"}) - - def clean_email(self): - email = self.cleaned_data['email'] - if get_user_model().objects.filter(email=email).exists(): - raise ValidationError(_("email already exists")) - return email - - class Meta: - model = get_user_model() - fields = ("username", "email") - - -class ForgetPasswordForm(forms.Form): - new_password1 = forms.CharField( - label=_("New password"), - widget=forms.PasswordInput( - attrs={ - "class": "form-control", - 'placeholder': _("New password") - } - ), - ) - - new_password2 = forms.CharField( - label="确认密码", - widget=forms.PasswordInput( - attrs={ - "class": "form-control", - 'placeholder': _("Confirm password") - } - ), - ) - - email = forms.EmailField( - label='邮箱', - widget=forms.TextInput( - attrs={ - 'class': 'form-control', - 'placeholder': _("Email") - } - ), - ) - - code = forms.CharField( - label=_('Code'), - widget=forms.TextInput( - attrs={ - 'class': 'form-control', - 'placeholder': _("Code") - } - ), - ) - - def clean_new_password2(self): - password1 = self.data.get("new_password1") - password2 = self.data.get("new_password2") - if password1 and password2 and password1 != password2: - raise ValidationError(_("passwords do not match")) - password_validation.validate_password(password2) - - return password2 - - def clean_email(self): - user_email = self.cleaned_data.get("email") - if not BlogUser.objects.filter( - email=user_email - ).exists(): - # todo 这里的报错提示可以判断一个邮箱是不是注册过,如果不想暴露可以修改 - raise ValidationError(_("email does not exist")) - return user_email - - def clean_code(self): - code = self.cleaned_data.get("code") - error = utils.verify( - email=self.cleaned_data.get("email"), - code=code, - ) - if error: - raise ValidationError(error) - return code - - -class ForgetPasswordCodeForm(forms.Form): - email = forms.EmailField( - label=_('Email'), - ) diff --git a/DjangoBlog-master/accounts/migrations/0001_initial.py b/DjangoBlog-master/accounts/migrations/0001_initial.py deleted file mode 100644 index d2fbcab..0000000 --- a/DjangoBlog-master/accounts/migrations/0001_initial.py +++ /dev/null @@ -1,49 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-02 07:14 - -import django.contrib.auth.models -import django.contrib.auth.validators -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('auth', '0012_alter_user_first_name_max_length'), - ] - - operations = [ - migrations.CreateModel( - name='BlogUser', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('password', models.CharField(max_length=128, verbose_name='password')), - ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), - ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), - ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')), - ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), - ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), - ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')), - ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')), - ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), - ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), - ('nickname', models.CharField(blank=True, max_length=100, verbose_name='昵称')), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')), - ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), - ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), - ], - options={ - 'verbose_name': '用户', - 'verbose_name_plural': '用户', - 'ordering': ['-id'], - 'get_latest_by': 'id', - }, - managers=[ - ('objects', django.contrib.auth.models.UserManager()), - ], - ), - ] diff --git a/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py b/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py deleted file mode 100644 index 1a9f509..0000000 --- a/DjangoBlog-master/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py +++ /dev/null @@ -1,46 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-06 13:13 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - ('accounts', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='bloguser', - options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'user', 'verbose_name_plural': 'user'}, - ), - migrations.RemoveField( - model_name='bloguser', - name='created_time', - ), - migrations.RemoveField( - model_name='bloguser', - name='last_mod_time', - ), - migrations.AddField( - model_name='bloguser', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='bloguser', - name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'), - ), - migrations.AlterField( - model_name='bloguser', - name='nickname', - field=models.CharField(blank=True, max_length=100, verbose_name='nick name'), - ), - migrations.AlterField( - model_name='bloguser', - name='source', - field=models.CharField(blank=True, max_length=100, verbose_name='create source'), - ), - ] diff --git a/DjangoBlog-master/accounts/migrations/__init__.py b/DjangoBlog-master/accounts/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/accounts/models.py b/DjangoBlog-master/accounts/models.py deleted file mode 100644 index 3baddbb..0000000 --- a/DjangoBlog-master/accounts/models.py +++ /dev/null @@ -1,35 +0,0 @@ -from django.contrib.auth.models import AbstractUser -from django.db import models -from django.urls import reverse -from django.utils.timezone import now -from django.utils.translation import gettext_lazy as _ -from djangoblog.utils import get_current_site - - -# Create your models here. - -class BlogUser(AbstractUser): - nickname = models.CharField(_('nick name'), max_length=100, blank=True) - creation_time = models.DateTimeField(_('creation time'), default=now) - last_modify_time = models.DateTimeField(_('last modify time'), default=now) - source = models.CharField(_('create source'), max_length=100, blank=True) - - def get_absolute_url(self): - return reverse( - 'blog:author_detail', kwargs={ - 'author_name': self.username}) - - def __str__(self): - return self.email - - def get_full_url(self): - site = get_current_site().domain - url = "https://{site}{path}".format(site=site, - path=self.get_absolute_url()) - return url - - class Meta: - ordering = ['-id'] - verbose_name = _('user') - verbose_name_plural = verbose_name - get_latest_by = 'id' diff --git a/DjangoBlog-master/accounts/templatetags/__init__.py b/DjangoBlog-master/accounts/templatetags/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/accounts/tests.py b/DjangoBlog-master/accounts/tests.py deleted file mode 100644 index 6893411..0000000 --- a/DjangoBlog-master/accounts/tests.py +++ /dev/null @@ -1,207 +0,0 @@ -from django.test import Client, RequestFactory, TestCase -from django.urls import reverse -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ - -from accounts.models import BlogUser -from blog.models import Article, Category -from djangoblog.utils import * -from . import utils - - -# Create your tests here. - -class AccountTest(TestCase): - def setUp(self): - self.client = Client() - self.factory = RequestFactory() - self.blog_user = BlogUser.objects.create_user( - username="test", - email="admin@admin.com", - password="12345678" - ) - self.new_test = "xxx123--=" - - def test_validate_account(self): - site = get_current_site().domain - user = BlogUser.objects.create_superuser( - email="liangliangyy1@gmail.com", - username="liangliangyy1", - password="qwer!@#$ggg") - testuser = BlogUser.objects.get(username='liangliangyy1') - - loginresult = self.client.login( - username='liangliangyy1', - password='qwer!@#$ggg') - self.assertEqual(loginresult, True) - response = self.client.get('/admin/') - self.assertEqual(response.status_code, 200) - - category = Category() - category.name = "categoryaaa" - category.creation_time = timezone.now() - category.last_modify_time = timezone.now() - category.save() - - article = Article() - article.title = "nicetitleaaa" - article.body = "nicecontentaaa" - article.author = user - article.category = category - article.type = 'a' - article.status = 'p' - article.save() - - response = self.client.get(article.get_admin_url()) - self.assertEqual(response.status_code, 200) - - def test_validate_register(self): - self.assertEquals( - 0, len( - BlogUser.objects.filter( - email='user123@user.com'))) - response = self.client.post(reverse('account:register'), { - 'username': 'user1233', - 'email': 'user123@user.com', - 'password1': 'password123!q@wE#R$T', - 'password2': 'password123!q@wE#R$T', - }) - self.assertEquals( - 1, len( - BlogUser.objects.filter( - email='user123@user.com'))) - user = BlogUser.objects.filter(email='user123@user.com')[0] - sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id))) - path = reverse('accounts:result') - url = '{path}?type=validation&id={id}&sign={sign}'.format( - path=path, id=user.id, sign=sign) - response = self.client.get(url) - self.assertEqual(response.status_code, 200) - - self.client.login(username='user1233', password='password123!q@wE#R$T') - user = BlogUser.objects.filter(email='user123@user.com')[0] - user.is_superuser = True - user.is_staff = True - user.save() - delete_sidebar_cache() - category = Category() - category.name = "categoryaaa" - category.creation_time = timezone.now() - category.last_modify_time = timezone.now() - category.save() - - article = Article() - article.category = category - article.title = "nicetitle333" - article.body = "nicecontentttt" - article.author = user - - article.type = 'a' - article.status = 'p' - article.save() - - response = self.client.get(article.get_admin_url()) - self.assertEqual(response.status_code, 200) - - response = self.client.get(reverse('account:logout')) - self.assertIn(response.status_code, [301, 302, 200]) - - response = self.client.get(article.get_admin_url()) - self.assertIn(response.status_code, [301, 302, 200]) - - response = self.client.post(reverse('account:login'), { - 'username': 'user1233', - 'password': 'password123' - }) - self.assertIn(response.status_code, [301, 302, 200]) - - response = self.client.get(article.get_admin_url()) - self.assertIn(response.status_code, [301, 302, 200]) - - def test_verify_email_code(self): - to_email = "admin@admin.com" - code = generate_code() - utils.set_code(to_email, code) - utils.send_verify_email(to_email, code) - - err = utils.verify("admin@admin.com", code) - self.assertEqual(err, None) - - err = utils.verify("admin@123.com", code) - self.assertEqual(type(err), str) - - def test_forget_password_email_code_success(self): - resp = self.client.post( - path=reverse("account:forget_password_code"), - data=dict(email="admin@admin.com") - ) - - self.assertEqual(resp.status_code, 200) - self.assertEqual(resp.content.decode("utf-8"), "ok") - - def test_forget_password_email_code_fail(self): - resp = self.client.post( - path=reverse("account:forget_password_code"), - data=dict() - ) - self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱") - - resp = self.client.post( - path=reverse("account:forget_password_code"), - data=dict(email="admin@com") - ) - self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱") - - def test_forget_password_email_success(self): - code = generate_code() - utils.set_code(self.blog_user.email, code) - data = dict( - new_password1=self.new_test, - new_password2=self.new_test, - email=self.blog_user.email, - code=code, - ) - resp = self.client.post( - path=reverse("account:forget_password"), - data=data - ) - self.assertEqual(resp.status_code, 302) - - # 验证用户密码是否修改成功 - blog_user = BlogUser.objects.filter( - email=self.blog_user.email, - ).first() # type: BlogUser - self.assertNotEqual(blog_user, None) - self.assertEqual(blog_user.check_password(data["new_password1"]), True) - - def test_forget_password_email_not_user(self): - data = dict( - new_password1=self.new_test, - new_password2=self.new_test, - email="123@123.com", - code="123456", - ) - resp = self.client.post( - path=reverse("account:forget_password"), - data=data - ) - - self.assertEqual(resp.status_code, 200) - - - def test_forget_password_email_code_error(self): - code = generate_code() - utils.set_code(self.blog_user.email, code) - data = dict( - new_password1=self.new_test, - new_password2=self.new_test, - email=self.blog_user.email, - code="111111", - ) - resp = self.client.post( - path=reverse("account:forget_password"), - data=data - ) - - self.assertEqual(resp.status_code, 200) - diff --git a/DjangoBlog-master/accounts/urls.py b/DjangoBlog-master/accounts/urls.py deleted file mode 100644 index 107a801..0000000 --- a/DjangoBlog-master/accounts/urls.py +++ /dev/null @@ -1,28 +0,0 @@ -from django.urls import path -from django.urls import re_path - -from . import views -from .forms import LoginForm - -app_name = "accounts" - -urlpatterns = [re_path(r'^login/$', - views.LoginView.as_view(success_url='/'), - name='login', - kwargs={'authentication_form': LoginForm}), - re_path(r'^register/$', - views.RegisterView.as_view(success_url="/"), - name='register'), - re_path(r'^logout/$', - views.LogoutView.as_view(), - name='logout'), - path(r'account/result.html', - views.account_result, - name='result'), - re_path(r'^forget_password/$', - views.ForgetPasswordView.as_view(), - name='forget_password'), - re_path(r'^forget_password_code/$', - views.ForgetPasswordEmailCode.as_view(), - name='forget_password_code'), - ] diff --git a/DjangoBlog-master/accounts/user_login_backend.py b/DjangoBlog-master/accounts/user_login_backend.py deleted file mode 100644 index 73cdca1..0000000 --- a/DjangoBlog-master/accounts/user_login_backend.py +++ /dev/null @@ -1,26 +0,0 @@ -from django.contrib.auth import get_user_model -from django.contrib.auth.backends import ModelBackend - - -class EmailOrUsernameModelBackend(ModelBackend): - """ - 允许使用用户名或邮箱登录 - """ - - def authenticate(self, request, username=None, password=None, **kwargs): - if '@' in username: - kwargs = {'email': username} - else: - kwargs = {'username': username} - try: - user = get_user_model().objects.get(**kwargs) - if user.check_password(password): - return user - except get_user_model().DoesNotExist: - return None - - def get_user(self, username): - try: - return get_user_model().objects.get(pk=username) - except get_user_model().DoesNotExist: - return None diff --git a/DjangoBlog-master/accounts/utils.py b/DjangoBlog-master/accounts/utils.py deleted file mode 100644 index 4b94bdf..0000000 --- a/DjangoBlog-master/accounts/utils.py +++ /dev/null @@ -1,49 +0,0 @@ -import typing -from datetime import timedelta - -from django.core.cache import cache -from django.utils.translation import gettext -from django.utils.translation import gettext_lazy as _ - -from djangoblog.utils import send_email - -_code_ttl = timedelta(minutes=5) - - -def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")): - """发送重设密码验证码 - Args: - to_mail: 接受邮箱 - subject: 邮件主题 - code: 验证码 - """ - html_content = _( - "You are resetting the password, the verification code is:%(code)s, valid within 5 minutes, please keep it " - "properly") % {'code': code} - send_email([to_mail], subject, html_content) - - -def verify(email: str, code: str) -> typing.Optional[str]: - """验证code是否有效 - Args: - email: 请求邮箱 - code: 验证码 - Return: - 如果有错误就返回错误str - Node: - 这里的错误处理不太合理,应该采用raise抛出 - 否测调用方也需要对error进行处理 - """ - cache_code = get_code(email) - if cache_code != code: - return gettext("Verification code error") - - -def set_code(email: str, code: str): - """设置code""" - cache.set(email, code, _code_ttl.seconds) - - -def get_code(email: str) -> typing.Optional[str]: - """获取code""" - return cache.get(email) diff --git a/DjangoBlog-master/accounts/views.py b/DjangoBlog-master/accounts/views.py deleted file mode 100644 index ae67aec..0000000 --- a/DjangoBlog-master/accounts/views.py +++ /dev/null @@ -1,204 +0,0 @@ -import logging -from django.utils.translation import gettext_lazy as _ -from django.conf import settings -from django.contrib import auth -from django.contrib.auth import REDIRECT_FIELD_NAME -from django.contrib.auth import get_user_model -from django.contrib.auth import logout -from django.contrib.auth.forms import AuthenticationForm -from django.contrib.auth.hashers import make_password -from django.http import HttpResponseRedirect, HttpResponseForbidden -from django.http.request import HttpRequest -from django.http.response import HttpResponse -from django.shortcuts import get_object_or_404 -from django.shortcuts import render -from django.urls import reverse -from django.utils.decorators import method_decorator -from django.utils.http import url_has_allowed_host_and_scheme -from django.views import View -from django.views.decorators.cache import never_cache -from django.views.decorators.csrf import csrf_protect -from django.views.decorators.debug import sensitive_post_parameters -from django.views.generic import FormView, RedirectView - -from djangoblog.utils import send_email, get_sha256, get_current_site, generate_code, delete_sidebar_cache -from . import utils -from .forms import RegisterForm, LoginForm, ForgetPasswordForm, ForgetPasswordCodeForm -from .models import BlogUser - -logger = logging.getLogger(__name__) - - -# Create your views here. - -class RegisterView(FormView): - form_class = RegisterForm - template_name = 'account/registration_form.html' - - @method_decorator(csrf_protect) - def dispatch(self, *args, **kwargs): - return super(RegisterView, self).dispatch(*args, **kwargs) - - def form_valid(self, form): - 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_sha256(get_sha256(settings.SECRET_KEY + str(user.id))) - - if settings.DEBUG: - site = '127.0.0.1:8000' - path = reverse('account:result') - url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format( - site=site, path=path, id=user.id, sign=sign) - - content = """ -

请点击下面链接验证您的邮箱

- - {url} - - 再次感谢您! -
- 如果上面链接无法打开,请将此链接复制至浏览器。 - {url} - """.format(url=url) - send_email( - emailto=[ - user.email, - ], - title='验证您的电子邮箱', - content=content) - - url = reverse('accounts:result') + \ - '?type=register&id=' + str(user.id) - return HttpResponseRedirect(url) - else: - return self.render_to_response({ - 'form': form - }) - - -class LogoutView(RedirectView): - url = '/login/' - - @method_decorator(never_cache) - def dispatch(self, request, *args, **kwargs): - return super(LogoutView, self).dispatch(request, *args, **kwargs) - - def get(self, request, *args, **kwargs): - logout(request) - delete_sidebar_cache() - return super(LogoutView, self).get(request, *args, **kwargs) - - -class LoginView(FormView): - form_class = LoginForm - template_name = 'account/login.html' - success_url = '/' - redirect_field_name = REDIRECT_FIELD_NAME - login_ttl = 2626560 # 一个月的时间 - - @method_decorator(sensitive_post_parameters('password')) - @method_decorator(csrf_protect) - @method_decorator(never_cache) - def dispatch(self, request, *args, **kwargs): - - return super(LoginView, self).dispatch(request, *args, **kwargs) - - def get_context_data(self, **kwargs): - redirect_to = self.request.GET.get(self.redirect_field_name) - if redirect_to is None: - redirect_to = '/' - kwargs['redirect_to'] = redirect_to - - return super(LoginView, self).get_context_data(**kwargs) - - def form_valid(self, form): - form = AuthenticationForm(data=self.request.POST, request=self.request) - - if form.is_valid(): - delete_sidebar_cache() - logger.info(self.redirect_field_name) - - auth.login(self.request, form.get_user()) - if self.request.POST.get("remember"): - self.request.session.set_expiry(self.login_ttl) - return super(LoginView, self).form_valid(form) - # return HttpResponseRedirect('/') - else: - return self.render_to_response({ - 'form': form - }) - - def get_success_url(self): - - redirect_to = self.request.POST.get(self.redirect_field_name) - if not url_has_allowed_host_and_scheme( - url=redirect_to, allowed_hosts=[ - self.request.get_host()]): - redirect_to = self.success_url - return redirect_to - - -def account_result(request): - type = request.GET.get('type') - id = request.GET.get('id') - - user = get_object_or_404(get_user_model(), id=id) - logger.info(type) - if user.is_active: - return HttpResponseRedirect('/') - if type and type in ['register', 'validation']: - if type == 'register': - content = ''' - 恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。 - ''' - title = '注册成功' - else: - c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id))) - sign = request.GET.get('sign') - if sign != c_sign: - return HttpResponseForbidden() - user.is_active = True - user.save() - content = ''' - 恭喜您已经成功的完成邮箱验证,您现在可以使用您的账号来登录本站。 - ''' - title = '验证成功' - return render(request, 'account/result.html', { - 'title': title, - 'content': content - }) - else: - return HttpResponseRedirect('/') - - -class ForgetPasswordView(FormView): - form_class = ForgetPasswordForm - template_name = 'account/forget_password.html' - - def form_valid(self, form): - if form.is_valid(): - blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get() - blog_user.password = make_password(form.cleaned_data["new_password2"]) - blog_user.save() - return HttpResponseRedirect('/login/') - else: - return self.render_to_response({'form': form}) - - -class ForgetPasswordEmailCode(View): - - def post(self, request: HttpRequest): - form = ForgetPasswordCodeForm(request.POST) - if not form.is_valid(): - return HttpResponse("错误的邮箱") - to_email = form.cleaned_data["email"] - - code = generate_code() - utils.send_verify_email(to_email, code) - utils.set_code(to_email, code) - - return HttpResponse("ok") diff --git a/DjangoBlog-master/blog/__init__.py b/DjangoBlog-master/blog/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/blog/admin.py b/DjangoBlog-master/blog/admin.py deleted file mode 100644 index 46c3420..0000000 --- a/DjangoBlog-master/blog/admin.py +++ /dev/null @@ -1,112 +0,0 @@ -from django import forms -from django.contrib import admin -from django.contrib.auth import get_user_model -from django.urls import reverse -from django.utils.html import format_html -from django.utils.translation import gettext_lazy as _ - -# Register your models here. -from .models import Article - - -class ArticleForm(forms.ModelForm): - # body = forms.CharField(widget=AdminPagedownWidget()) - - class Meta: - model = Article - fields = '__all__' - - -def makr_article_publish(modeladmin, request, queryset): - queryset.update(status='p') - - -def draft_article(modeladmin, request, queryset): - queryset.update(status='d') - - -def close_article_commentstatus(modeladmin, request, queryset): - queryset.update(comment_status='c') - - -def open_article_commentstatus(modeladmin, request, queryset): - queryset.update(comment_status='o') - - -makr_article_publish.short_description = _('Publish selected articles') -draft_article.short_description = _('Draft selected articles') -close_article_commentstatus.short_description = _('Close article comments') -open_article_commentstatus.short_description = _('Open article comments') - - -class ArticlelAdmin(admin.ModelAdmin): - list_per_page = 20 - search_fields = ('body', 'title') - form = ArticleForm - list_display = ( - 'id', - 'title', - 'author', - 'link_to_category', - 'creation_time', - 'views', - 'status', - 'type', - 'article_order') - list_display_links = ('id', 'title') - list_filter = ('status', 'type', 'category') - filter_horizontal = ('tags',) - exclude = ('creation_time', 'last_modify_time') - view_on_site = True - actions = [ - makr_article_publish, - draft_article, - close_article_commentstatus, - open_article_commentstatus] - - def link_to_category(self, obj): - info = (obj.category._meta.app_label, obj.category._meta.model_name) - link = reverse('admin:%s_%s_change' % info, args=(obj.category.id,)) - return format_html(u'%s' % (link, obj.category.name)) - - link_to_category.short_description = _('category') - - def get_form(self, request, obj=None, **kwargs): - form = super(ArticlelAdmin, self).get_form(request, obj, **kwargs) - form.base_fields['author'].queryset = get_user_model( - ).objects.filter(is_superuser=True) - return form - - def save_model(self, request, obj, form, change): - super(ArticlelAdmin, self).save_model(request, obj, form, change) - - def get_view_on_site_url(self, obj=None): - if obj: - url = obj.get_full_url() - return url - else: - from djangoblog.utils import get_current_site - site = get_current_site().domain - return site - - -class TagAdmin(admin.ModelAdmin): - exclude = ('slug', 'last_mod_time', 'creation_time') - - -class CategoryAdmin(admin.ModelAdmin): - list_display = ('name', 'parent_category', 'index') - exclude = ('slug', 'last_mod_time', 'creation_time') - - -class LinksAdmin(admin.ModelAdmin): - exclude = ('last_mod_time', 'creation_time') - - -class SideBarAdmin(admin.ModelAdmin): - list_display = ('name', 'content', 'is_enable', 'sequence') - exclude = ('last_mod_time', 'creation_time') - - -class BlogSettingsAdmin(admin.ModelAdmin): - pass diff --git a/DjangoBlog-master/blog/apps.py b/DjangoBlog-master/blog/apps.py deleted file mode 100644 index 7930587..0000000 --- a/DjangoBlog-master/blog/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class BlogConfig(AppConfig): - name = 'blog' diff --git a/DjangoBlog-master/blog/context_processors.py b/DjangoBlog-master/blog/context_processors.py deleted file mode 100644 index 73e3088..0000000 --- a/DjangoBlog-master/blog/context_processors.py +++ /dev/null @@ -1,43 +0,0 @@ -import logging - -from django.utils import timezone - -from djangoblog.utils import cache, get_blog_setting -from .models import Category, Article - -logger = logging.getLogger(__name__) - - -def seo_processor(requests): - key = 'seo_processor' - value = cache.get(key) - if value: - return value - else: - logger.info('set processor cache.') - setting = get_blog_setting() - value = { - 'SITE_NAME': setting.site_name, - 'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense, - 'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes, - 'SITE_SEO_DESCRIPTION': setting.site_seo_description, - 'SITE_DESCRIPTION': setting.site_description, - 'SITE_KEYWORDS': setting.site_keywords, - 'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/', - 'ARTICLE_SUB_LENGTH': setting.article_sub_length, - 'nav_category_list': Category.objects.all(), - 'nav_pages': Article.objects.filter( - type='p', - status='p'), - 'OPEN_SITE_COMMENT': setting.open_site_comment, - 'BEIAN_CODE': setting.beian_code, - 'ANALYTICS_CODE': setting.analytics_code, - "BEIAN_CODE_GONGAN": setting.gongan_beiancode, - "SHOW_GONGAN_CODE": setting.show_gongan_code, - "CURRENT_YEAR": timezone.now().year, - "GLOBAL_HEADER": setting.global_header, - "GLOBAL_FOOTER": setting.global_footer, - "COMMENT_NEED_REVIEW": setting.comment_need_review, - } - cache.set(key, value, 60 * 60 * 10) - return value diff --git a/DjangoBlog-master/blog/documents.py b/DjangoBlog-master/blog/documents.py deleted file mode 100644 index 0f1db7b..0000000 --- a/DjangoBlog-master/blog/documents.py +++ /dev/null @@ -1,213 +0,0 @@ -import time - -import elasticsearch.client -from django.conf import settings -from elasticsearch_dsl import Document, InnerDoc, Date, Integer, Long, Text, Object, GeoPoint, Keyword, Boolean -from elasticsearch_dsl.connections import connections - -from blog.models import Article - -ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL') - -if ELASTICSEARCH_ENABLED: - connections.create_connection( - hosts=[settings.ELASTICSEARCH_DSL['default']['hosts']]) - from elasticsearch import Elasticsearch - - es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts']) - from elasticsearch.client import IngestClient - - c = IngestClient(es) - try: - c.get_pipeline('geoip') - except elasticsearch.exceptions.NotFoundError: - c.put_pipeline('geoip', body='''{ - "description" : "Add geoip info", - "processors" : [ - { - "geoip" : { - "field" : "ip" - } - } - ] - }''') - - -class GeoIp(InnerDoc): - continent_name = Keyword() - country_iso_code = Keyword() - country_name = Keyword() - location = GeoPoint() - - -class UserAgentBrowser(InnerDoc): - Family = Keyword() - Version = Keyword() - - -class UserAgentOS(UserAgentBrowser): - pass - - -class UserAgentDevice(InnerDoc): - Family = Keyword() - Brand = Keyword() - Model = Keyword() - - -class UserAgent(InnerDoc): - browser = Object(UserAgentBrowser, required=False) - os = Object(UserAgentOS, required=False) - device = Object(UserAgentDevice, required=False) - string = Text() - is_bot = Boolean() - - -class ElapsedTimeDocument(Document): - url = Keyword() - time_taken = Long() - log_datetime = Date() - ip = Keyword() - geoip = Object(GeoIp, required=False) - useragent = Object(UserAgent, required=False) - - class Index: - name = 'performance' - settings = { - "number_of_shards": 1, - "number_of_replicas": 0 - } - - class Meta: - doc_type = 'ElapsedTime' - - -class ElaspedTimeDocumentManager: - @staticmethod - def build_index(): - from elasticsearch import Elasticsearch - client = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts']) - res = client.indices.exists(index="performance") - if not res: - ElapsedTimeDocument.init() - - @staticmethod - def delete_index(): - from elasticsearch import Elasticsearch - es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts']) - es.indices.delete(index='performance', ignore=[400, 404]) - - @staticmethod - def create(url, time_taken, log_datetime, useragent, ip): - ElaspedTimeDocumentManager.build_index() - ua = UserAgent() - ua.browser = UserAgentBrowser() - ua.browser.Family = useragent.browser.family - ua.browser.Version = useragent.browser.version_string - - ua.os = UserAgentOS() - ua.os.Family = useragent.os.family - ua.os.Version = useragent.os.version_string - - ua.device = UserAgentDevice() - ua.device.Family = useragent.device.family - ua.device.Brand = useragent.device.brand - ua.device.Model = useragent.device.model - ua.string = useragent.ua_string - ua.is_bot = useragent.is_bot - - doc = ElapsedTimeDocument( - meta={ - 'id': int( - round( - time.time() * - 1000)) - }, - url=url, - time_taken=time_taken, - log_datetime=log_datetime, - useragent=ua, ip=ip) - doc.save(pipeline="geoip") - - -class ArticleDocument(Document): - body = Text(analyzer='ik_max_word', search_analyzer='ik_smart') - title = Text(analyzer='ik_max_word', search_analyzer='ik_smart') - author = Object(properties={ - 'nickname': Text(analyzer='ik_max_word', search_analyzer='ik_smart'), - 'id': Integer() - }) - category = Object(properties={ - 'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'), - 'id': Integer() - }) - tags = Object(properties={ - 'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'), - 'id': Integer() - }) - - pub_time = Date() - status = Text() - comment_status = Text() - type = Text() - views = Integer() - article_order = Integer() - - class Index: - name = 'blog' - settings = { - "number_of_shards": 1, - "number_of_replicas": 0 - } - - class Meta: - doc_type = 'Article' - - -class ArticleDocumentManager(): - - def __init__(self): - self.create_index() - - def create_index(self): - ArticleDocument.init() - - def delete_index(self): - from elasticsearch import Elasticsearch - es = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts']) - es.indices.delete(index='blog', ignore=[400, 404]) - - def convert_to_doc(self, articles): - return [ - ArticleDocument( - meta={ - 'id': article.id}, - body=article.body, - title=article.title, - author={ - 'nickname': article.author.username, - 'id': article.author.id}, - category={ - 'name': article.category.name, - 'id': article.category.id}, - tags=[ - { - 'name': t.name, - 'id': t.id} for t in article.tags.all()], - pub_time=article.pub_time, - status=article.status, - comment_status=article.comment_status, - type=article.type, - views=article.views, - article_order=article.article_order) for article in articles] - - def rebuild(self, articles=None): - ArticleDocument.init() - articles = articles if articles else Article.objects.all() - docs = self.convert_to_doc(articles) - for doc in docs: - doc.save() - - def update_docs(self, docs): - for doc in docs: - doc.save() diff --git a/DjangoBlog-master/blog/forms.py b/DjangoBlog-master/blog/forms.py deleted file mode 100644 index 715be76..0000000 --- a/DjangoBlog-master/blog/forms.py +++ /dev/null @@ -1,19 +0,0 @@ -import logging - -from django import forms -from haystack.forms import SearchForm - -logger = logging.getLogger(__name__) - - -class BlogSearchForm(SearchForm): - querydata = forms.CharField(required=True) - - def search(self): - datas = super(BlogSearchForm, self).search() - if not self.is_valid(): - return self.no_query_found() - - if self.cleaned_data['querydata']: - logger.info(self.cleaned_data['querydata']) - return datas diff --git a/DjangoBlog-master/blog/management/__init__.py b/DjangoBlog-master/blog/management/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/blog/management/commands/__init__.py b/DjangoBlog-master/blog/management/commands/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/blog/management/commands/build_index.py b/DjangoBlog-master/blog/management/commands/build_index.py deleted file mode 100644 index 3c4acd7..0000000 --- a/DjangoBlog-master/blog/management/commands/build_index.py +++ /dev/null @@ -1,18 +0,0 @@ -from django.core.management.base import BaseCommand - -from blog.documents import ElapsedTimeDocument, ArticleDocumentManager, ElaspedTimeDocumentManager, \ - ELASTICSEARCH_ENABLED - - -# TODO 参数化 -class Command(BaseCommand): - help = 'build search index' - - def handle(self, *args, **options): - if ELASTICSEARCH_ENABLED: - ElaspedTimeDocumentManager.build_index() - manager = ElapsedTimeDocument() - manager.init() - manager = ArticleDocumentManager() - manager.delete_index() - manager.rebuild() diff --git a/DjangoBlog-master/blog/management/commands/build_search_words.py b/DjangoBlog-master/blog/management/commands/build_search_words.py deleted file mode 100644 index cfe7e0d..0000000 --- a/DjangoBlog-master/blog/management/commands/build_search_words.py +++ /dev/null @@ -1,13 +0,0 @@ -from django.core.management.base import BaseCommand - -from blog.models import Tag, Category - - -# TODO 参数化 -class Command(BaseCommand): - help = 'build search words' - - def handle(self, *args, **options): - datas = set([t.name for t in Tag.objects.all()] + - [t.name for t in Category.objects.all()]) - print('\n'.join(datas)) diff --git a/DjangoBlog-master/blog/management/commands/clear_cache.py b/DjangoBlog-master/blog/management/commands/clear_cache.py deleted file mode 100644 index 0d66172..0000000 --- a/DjangoBlog-master/blog/management/commands/clear_cache.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.core.management.base import BaseCommand - -from djangoblog.utils import cache - - -class Command(BaseCommand): - help = 'clear the whole cache' - - def handle(self, *args, **options): - cache.clear() - self.stdout.write(self.style.SUCCESS('Cleared cache\n')) diff --git a/DjangoBlog-master/blog/management/commands/create_testdata.py b/DjangoBlog-master/blog/management/commands/create_testdata.py deleted file mode 100644 index 675d2ba..0000000 --- a/DjangoBlog-master/blog/management/commands/create_testdata.py +++ /dev/null @@ -1,40 +0,0 @@ -from django.contrib.auth import get_user_model -from django.contrib.auth.hashers import make_password -from django.core.management.base import BaseCommand - -from blog.models import Article, Tag, Category - - -class Command(BaseCommand): - help = 'create test datas' - - def handle(self, *args, **options): - user = get_user_model().objects.get_or_create( - email='test@test.com', username='测试用户', password=make_password('test!q@w#eTYU'))[0] - - pcategory = Category.objects.get_or_create( - name='我是父类目', parent_category=None)[0] - - category = Category.objects.get_or_create( - name='子类目', parent_category=pcategory)[0] - - category.save() - basetag = Tag() - basetag.name = "标签" - basetag.save() - for i in range(1, 20): - article = Article.objects.get_or_create( - category=category, - title='nice title ' + str(i), - body='nice content ' + str(i), - author=user)[0] - tag = Tag() - tag.name = "标签" + str(i) - tag.save() - article.tags.add(tag) - article.tags.add(basetag) - article.save() - - from djangoblog.utils import cache - cache.clear() - self.stdout.write(self.style.SUCCESS('created test datas \n')) diff --git a/DjangoBlog-master/blog/management/commands/ping_baidu.py b/DjangoBlog-master/blog/management/commands/ping_baidu.py deleted file mode 100644 index 2c7fbdd..0000000 --- a/DjangoBlog-master/blog/management/commands/ping_baidu.py +++ /dev/null @@ -1,50 +0,0 @@ -from django.core.management.base import BaseCommand - -from djangoblog.spider_notify import SpiderNotify -from djangoblog.utils import get_current_site -from blog.models import Article, Tag, Category - -site = get_current_site().domain - - -class Command(BaseCommand): - help = 'notify baidu url' - - def add_arguments(self, parser): - parser.add_argument( - 'data_type', - type=str, - choices=[ - 'all', - 'article', - 'tag', - 'category'], - help='article : all article,tag : all tag,category: all category,all: All of these') - - def get_full_url(self, path): - url = "https://{site}{path}".format(site=site, path=path) - return url - - def handle(self, *args, **options): - type = options['data_type'] - self.stdout.write('start get %s' % type) - - urls = [] - if type == 'article' or type == 'all': - for article in Article.objects.filter(status='p'): - urls.append(article.get_full_url()) - if type == 'tag' or type == 'all': - for tag in Tag.objects.all(): - url = tag.get_absolute_url() - urls.append(self.get_full_url(url)) - if type == 'category' or type == 'all': - for category in Category.objects.all(): - url = category.get_absolute_url() - urls.append(self.get_full_url(url)) - - self.stdout.write( - self.style.SUCCESS( - 'start notify %d urls' % - len(urls))) - SpiderNotify.baidu_notify(urls) - self.stdout.write(self.style.SUCCESS('finish notify')) diff --git a/DjangoBlog-master/blog/management/commands/sync_user_avatar.py b/DjangoBlog-master/blog/management/commands/sync_user_avatar.py deleted file mode 100644 index d0f4612..0000000 --- a/DjangoBlog-master/blog/management/commands/sync_user_avatar.py +++ /dev/null @@ -1,47 +0,0 @@ -import requests -from django.core.management.base import BaseCommand -from django.templatetags.static import static - -from djangoblog.utils import save_user_avatar -from oauth.models import OAuthUser -from oauth.oauthmanager import get_manager_by_type - - -class Command(BaseCommand): - help = 'sync user avatar' - - def test_picture(self, url): - try: - if requests.get(url, timeout=2).status_code == 200: - return True - except: - pass - - def handle(self, *args, **options): - static_url = static("../") - users = OAuthUser.objects.all() - self.stdout.write(f'开始同步{len(users)}个用户头像') - for u in users: - self.stdout.write(f'开始同步:{u.nickname}') - url = u.picture - if url: - if url.startswith(static_url): - if self.test_picture(url): - continue - else: - if u.metadata: - manage = get_manager_by_type(u.type) - url = manage.get_picture(u.metadata) - url = save_user_avatar(url) - else: - url = static('blog/img/avatar.png') - else: - url = save_user_avatar(url) - else: - url = static('blog/img/avatar.png') - if url: - self.stdout.write( - f'结束同步:{u.nickname}.url:{url}') - u.picture = url - u.save() - self.stdout.write('结束同步') diff --git a/DjangoBlog-master/blog/middleware.py b/DjangoBlog-master/blog/middleware.py deleted file mode 100644 index 94dd70c..0000000 --- a/DjangoBlog-master/blog/middleware.py +++ /dev/null @@ -1,42 +0,0 @@ -import logging -import time - -from ipware import get_client_ip -from user_agents import parse - -from blog.documents import ELASTICSEARCH_ENABLED, ElaspedTimeDocumentManager - -logger = logging.getLogger(__name__) - - -class OnlineMiddleware(object): - def __init__(self, get_response=None): - self.get_response = get_response - super().__init__() - - def __call__(self, request): - ''' page render time ''' - start_time = time.time() - response = self.get_response(request) - http_user_agent = request.META.get('HTTP_USER_AGENT', '') - ip, _ = get_client_ip(request) - user_agent = parse(http_user_agent) - if not response.streaming: - try: - cast_time = time.time() - start_time - if ELASTICSEARCH_ENABLED: - time_taken = round((cast_time) * 1000, 2) - url = request.path - from django.utils import timezone - ElaspedTimeDocumentManager.create( - url=url, - time_taken=time_taken, - log_datetime=timezone.now(), - useragent=user_agent, - ip=ip) - response.content = response.content.replace( - b'', str.encode(str(cast_time)[:5])) - except Exception as e: - logger.error("Error OnlineMiddleware: %s" % e) - - return response diff --git a/DjangoBlog-master/blog/migrations/0001_initial.py b/DjangoBlog-master/blog/migrations/0001_initial.py deleted file mode 100644 index 3d391b6..0000000 --- a/DjangoBlog-master/blog/migrations/0001_initial.py +++ /dev/null @@ -1,137 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-02 07:14 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone -import mdeditor.fields - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='BlogSettings', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('sitename', models.CharField(default='', max_length=200, verbose_name='网站名称')), - ('site_description', models.TextField(default='', max_length=1000, verbose_name='网站描述')), - ('site_seo_description', models.TextField(default='', max_length=1000, verbose_name='网站SEO描述')), - ('site_keywords', models.TextField(default='', max_length=1000, verbose_name='网站关键字')), - ('article_sub_length', models.IntegerField(default=300, verbose_name='文章摘要长度')), - ('sidebar_article_count', models.IntegerField(default=10, verbose_name='侧边栏文章数目')), - ('sidebar_comment_count', models.IntegerField(default=5, verbose_name='侧边栏评论数目')), - ('article_comment_count', models.IntegerField(default=5, verbose_name='文章页面默认显示评论数目')), - ('show_google_adsense', models.BooleanField(default=False, verbose_name='是否显示谷歌广告')), - ('google_adsense_codes', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='广告内容')), - ('open_site_comment', models.BooleanField(default=True, verbose_name='是否打开网站评论功能')), - ('beiancode', models.CharField(blank=True, default='', max_length=2000, null=True, verbose_name='备案号')), - ('analyticscode', models.TextField(default='', max_length=1000, verbose_name='网站统计代码')), - ('show_gongan_code', models.BooleanField(default=False, verbose_name='是否显示公安备案号')), - ('gongan_beiancode', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='公安备案号')), - ], - options={ - 'verbose_name': '网站配置', - 'verbose_name_plural': '网站配置', - }, - ), - migrations.CreateModel( - name='Links', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=30, unique=True, verbose_name='链接名称')), - ('link', models.URLField(verbose_name='链接地址')), - ('sequence', models.IntegerField(unique=True, verbose_name='排序')), - ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')), - ('show_type', models.CharField(choices=[('i', '首页'), ('l', '列表页'), ('p', '文章页面'), ('a', '全站'), ('s', '友情链接页面')], default='i', max_length=1, verbose_name='显示类型')), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ], - options={ - 'verbose_name': '友情链接', - 'verbose_name_plural': '友情链接', - 'ordering': ['sequence'], - }, - ), - migrations.CreateModel( - name='SideBar', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=100, verbose_name='标题')), - ('content', models.TextField(verbose_name='内容')), - ('sequence', models.IntegerField(unique=True, verbose_name='排序')), - ('is_enable', models.BooleanField(default=True, verbose_name='是否启用')), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ], - options={ - 'verbose_name': '侧边栏', - 'verbose_name_plural': '侧边栏', - 'ordering': ['sequence'], - }, - ), - migrations.CreateModel( - name='Tag', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ('name', models.CharField(max_length=30, unique=True, verbose_name='标签名')), - ('slug', models.SlugField(blank=True, default='no-slug', max_length=60)), - ], - options={ - 'verbose_name': '标签', - 'verbose_name_plural': '标签', - 'ordering': ['name'], - }, - ), - migrations.CreateModel( - name='Category', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ('name', models.CharField(max_length=30, unique=True, verbose_name='分类名')), - ('slug', models.SlugField(blank=True, default='no-slug', max_length=60)), - ('index', models.IntegerField(default=0, verbose_name='权重排序-越大越靠前')), - ('parent_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='父级分类')), - ], - options={ - 'verbose_name': '分类', - 'verbose_name_plural': '分类', - 'ordering': ['-index'], - }, - ), - migrations.CreateModel( - name='Article', - fields=[ - ('id', models.AutoField(primary_key=True, serialize=False)), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ('title', models.CharField(max_length=200, unique=True, verbose_name='标题')), - ('body', mdeditor.fields.MDTextField(verbose_name='正文')), - ('pub_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='发布时间')), - ('status', models.CharField(choices=[('d', '草稿'), ('p', '发表')], default='p', max_length=1, verbose_name='文章状态')), - ('comment_status', models.CharField(choices=[('o', '打开'), ('c', '关闭')], default='o', max_length=1, verbose_name='评论状态')), - ('type', models.CharField(choices=[('a', '文章'), ('p', '页面')], default='a', max_length=1, verbose_name='类型')), - ('views', models.PositiveIntegerField(default=0, verbose_name='浏览量')), - ('article_order', models.IntegerField(default=0, verbose_name='排序,数字越大越靠前')), - ('show_toc', models.BooleanField(default=False, verbose_name='是否显示toc目录')), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')), - ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='分类')), - ('tags', models.ManyToManyField(blank=True, to='blog.tag', verbose_name='标签集合')), - ], - options={ - 'verbose_name': '文章', - 'verbose_name_plural': '文章', - 'ordering': ['-article_order', '-pub_time'], - 'get_latest_by': 'id', - }, - ), - ] diff --git a/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py b/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py deleted file mode 100644 index adbaa36..0000000 --- a/DjangoBlog-master/blog/migrations/0002_blogsettings_global_footer_and_more.py +++ /dev/null @@ -1,23 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-29 06:08 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('blog', '0001_initial'), - ] - - operations = [ - migrations.AddField( - model_name='blogsettings', - name='global_footer', - field=models.TextField(blank=True, default='', null=True, verbose_name='公共尾部'), - ), - migrations.AddField( - model_name='blogsettings', - name='global_header', - field=models.TextField(blank=True, default='', null=True, verbose_name='公共头部'), - ), - ] diff --git a/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py b/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py deleted file mode 100644 index e9f5502..0000000 --- a/DjangoBlog-master/blog/migrations/0003_blogsettings_comment_need_review.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 4.2.1 on 2023-05-09 07:45 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ('blog', '0002_blogsettings_global_footer_and_more'), - ] - - operations = [ - migrations.AddField( - model_name='blogsettings', - name='comment_need_review', - field=models.BooleanField(default=False, verbose_name='评论是否需要审核'), - ), - ] diff --git a/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py b/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py deleted file mode 100644 index ceb1398..0000000 --- a/DjangoBlog-master/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py +++ /dev/null @@ -1,27 +0,0 @@ -# Generated by Django 4.2.1 on 2023-05-09 07:51 - -from django.db import migrations - - -class Migration(migrations.Migration): - dependencies = [ - ('blog', '0003_blogsettings_comment_need_review'), - ] - - operations = [ - migrations.RenameField( - model_name='blogsettings', - old_name='analyticscode', - new_name='analytics_code', - ), - migrations.RenameField( - model_name='blogsettings', - old_name='beiancode', - new_name='beian_code', - ), - migrations.RenameField( - model_name='blogsettings', - old_name='sitename', - new_name='site_name', - ), - ] diff --git a/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py b/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py deleted file mode 100644 index d08e853..0000000 --- a/DjangoBlog-master/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py +++ /dev/null @@ -1,300 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-06 13:13 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone -import mdeditor.fields - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('blog', '0004_rename_analyticscode_blogsettings_analytics_code_and_more'), - ] - - operations = [ - migrations.AlterModelOptions( - name='article', - options={'get_latest_by': 'id', 'ordering': ['-article_order', '-pub_time'], 'verbose_name': 'article', 'verbose_name_plural': 'article'}, - ), - migrations.AlterModelOptions( - name='category', - options={'ordering': ['-index'], 'verbose_name': 'category', 'verbose_name_plural': 'category'}, - ), - migrations.AlterModelOptions( - name='links', - options={'ordering': ['sequence'], 'verbose_name': 'link', 'verbose_name_plural': 'link'}, - ), - migrations.AlterModelOptions( - name='sidebar', - options={'ordering': ['sequence'], 'verbose_name': 'sidebar', 'verbose_name_plural': 'sidebar'}, - ), - migrations.AlterModelOptions( - name='tag', - options={'ordering': ['name'], 'verbose_name': 'tag', 'verbose_name_plural': 'tag'}, - ), - migrations.RemoveField( - model_name='article', - name='created_time', - ), - migrations.RemoveField( - model_name='article', - name='last_mod_time', - ), - migrations.RemoveField( - model_name='category', - name='created_time', - ), - migrations.RemoveField( - model_name='category', - name='last_mod_time', - ), - migrations.RemoveField( - model_name='links', - name='created_time', - ), - migrations.RemoveField( - model_name='sidebar', - name='created_time', - ), - migrations.RemoveField( - model_name='tag', - name='created_time', - ), - migrations.RemoveField( - model_name='tag', - name='last_mod_time', - ), - migrations.AddField( - model_name='article', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='article', - name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), - ), - migrations.AddField( - model_name='category', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='category', - name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), - ), - migrations.AddField( - model_name='links', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='sidebar', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='tag', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='tag', - name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), - ), - migrations.AlterField( - model_name='article', - name='article_order', - field=models.IntegerField(default=0, verbose_name='order'), - ), - migrations.AlterField( - model_name='article', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'), - ), - migrations.AlterField( - model_name='article', - name='body', - field=mdeditor.fields.MDTextField(verbose_name='body'), - ), - migrations.AlterField( - model_name='article', - name='category', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='category'), - ), - migrations.AlterField( - model_name='article', - name='comment_status', - field=models.CharField(choices=[('o', 'Open'), ('c', 'Close')], default='o', max_length=1, verbose_name='comment status'), - ), - migrations.AlterField( - model_name='article', - name='pub_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='publish time'), - ), - migrations.AlterField( - model_name='article', - name='show_toc', - field=models.BooleanField(default=False, verbose_name='show toc'), - ), - migrations.AlterField( - model_name='article', - name='status', - field=models.CharField(choices=[('d', 'Draft'), ('p', 'Published')], default='p', max_length=1, verbose_name='status'), - ), - migrations.AlterField( - model_name='article', - name='tags', - field=models.ManyToManyField(blank=True, to='blog.tag', verbose_name='tag'), - ), - migrations.AlterField( - model_name='article', - name='title', - field=models.CharField(max_length=200, unique=True, verbose_name='title'), - ), - migrations.AlterField( - model_name='article', - name='type', - field=models.CharField(choices=[('a', 'Article'), ('p', 'Page')], default='a', max_length=1, verbose_name='type'), - ), - migrations.AlterField( - model_name='article', - name='views', - field=models.PositiveIntegerField(default=0, verbose_name='views'), - ), - migrations.AlterField( - model_name='blogsettings', - name='article_comment_count', - field=models.IntegerField(default=5, verbose_name='article comment count'), - ), - migrations.AlterField( - model_name='blogsettings', - name='article_sub_length', - field=models.IntegerField(default=300, verbose_name='article sub length'), - ), - migrations.AlterField( - model_name='blogsettings', - name='google_adsense_codes', - field=models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='adsense code'), - ), - migrations.AlterField( - model_name='blogsettings', - name='open_site_comment', - field=models.BooleanField(default=True, verbose_name='open site comment'), - ), - migrations.AlterField( - model_name='blogsettings', - name='show_google_adsense', - field=models.BooleanField(default=False, verbose_name='show adsense'), - ), - migrations.AlterField( - model_name='blogsettings', - name='sidebar_article_count', - field=models.IntegerField(default=10, verbose_name='sidebar article count'), - ), - migrations.AlterField( - model_name='blogsettings', - name='sidebar_comment_count', - field=models.IntegerField(default=5, verbose_name='sidebar comment count'), - ), - migrations.AlterField( - model_name='blogsettings', - name='site_description', - field=models.TextField(default='', max_length=1000, verbose_name='site description'), - ), - migrations.AlterField( - model_name='blogsettings', - name='site_keywords', - field=models.TextField(default='', max_length=1000, verbose_name='site keywords'), - ), - migrations.AlterField( - model_name='blogsettings', - name='site_name', - field=models.CharField(default='', max_length=200, verbose_name='site name'), - ), - migrations.AlterField( - model_name='blogsettings', - name='site_seo_description', - field=models.TextField(default='', max_length=1000, verbose_name='site seo description'), - ), - migrations.AlterField( - model_name='category', - name='index', - field=models.IntegerField(default=0, verbose_name='index'), - ), - migrations.AlterField( - model_name='category', - name='name', - field=models.CharField(max_length=30, unique=True, verbose_name='category name'), - ), - migrations.AlterField( - model_name='category', - name='parent_category', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='parent category'), - ), - migrations.AlterField( - model_name='links', - name='is_enable', - field=models.BooleanField(default=True, verbose_name='is show'), - ), - migrations.AlterField( - model_name='links', - name='last_mod_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), - ), - migrations.AlterField( - model_name='links', - name='link', - field=models.URLField(verbose_name='link'), - ), - migrations.AlterField( - model_name='links', - name='name', - field=models.CharField(max_length=30, unique=True, verbose_name='link name'), - ), - migrations.AlterField( - model_name='links', - name='sequence', - field=models.IntegerField(unique=True, verbose_name='order'), - ), - migrations.AlterField( - model_name='links', - name='show_type', - field=models.CharField(choices=[('i', 'index'), ('l', 'list'), ('p', 'post'), ('a', 'all'), ('s', 'slide')], default='i', max_length=1, verbose_name='show type'), - ), - migrations.AlterField( - model_name='sidebar', - name='content', - field=models.TextField(verbose_name='content'), - ), - migrations.AlterField( - model_name='sidebar', - name='is_enable', - field=models.BooleanField(default=True, verbose_name='is enable'), - ), - migrations.AlterField( - model_name='sidebar', - name='last_mod_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'), - ), - migrations.AlterField( - model_name='sidebar', - name='name', - field=models.CharField(max_length=100, verbose_name='title'), - ), - migrations.AlterField( - model_name='sidebar', - name='sequence', - field=models.IntegerField(unique=True, verbose_name='order'), - ), - migrations.AlterField( - model_name='tag', - name='name', - field=models.CharField(max_length=30, unique=True, verbose_name='tag name'), - ), - ] diff --git a/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py b/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py deleted file mode 100644 index e36feb4..0000000 --- a/DjangoBlog-master/blog/migrations/0006_alter_blogsettings_options.py +++ /dev/null @@ -1,17 +0,0 @@ -# Generated by Django 4.2.7 on 2024-01-26 02:41 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('blog', '0005_alter_article_options_alter_category_options_and_more'), - ] - - operations = [ - migrations.AlterModelOptions( - name='blogsettings', - options={'verbose_name': 'Website configuration', 'verbose_name_plural': 'Website configuration'}, - ), - ] diff --git a/DjangoBlog-master/blog/migrations/__init__.py b/DjangoBlog-master/blog/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/blog/models.py b/DjangoBlog-master/blog/models.py deleted file mode 100644 index 083788b..0000000 --- a/DjangoBlog-master/blog/models.py +++ /dev/null @@ -1,376 +0,0 @@ -import logging -import re -from abc import abstractmethod - -from django.conf import settings -from django.core.exceptions import ValidationError -from django.db import models -from django.urls import reverse -from django.utils.timezone import now -from django.utils.translation import gettext_lazy as _ -from mdeditor.fields import MDTextField -from uuslug import slugify - -from djangoblog.utils import cache_decorator, cache -from djangoblog.utils import get_current_site - -logger = logging.getLogger(__name__) - - -class LinkShowType(models.TextChoices): - I = ('i', _('index')) - L = ('l', _('list')) - P = ('p', _('post')) - A = ('a', _('all')) - S = ('s', _('slide')) - - -class BaseModel(models.Model): - id = models.AutoField(primary_key=True) - creation_time = models.DateTimeField(_('creation time'), default=now) - last_modify_time = models.DateTimeField(_('modify time'), default=now) - - def save(self, *args, **kwargs): - is_update_views = isinstance( - self, - Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views'] - if is_update_views: - Article.objects.filter(pk=self.pk).update(views=self.views) - else: - if 'slug' in self.__dict__: - slug = getattr( - self, 'title') if 'title' in self.__dict__ else getattr( - self, 'name') - setattr(self, 'slug', slugify(slug)) - super().save(*args, **kwargs) - - def get_full_url(self): - site = get_current_site().domain - url = "https://{site}{path}".format(site=site, - path=self.get_absolute_url()) - return url - - class Meta: - abstract = True - - @abstractmethod - def get_absolute_url(self): - pass - - -class Article(BaseModel): - """文章""" - STATUS_CHOICES = ( - ('d', _('Draft')), - ('p', _('Published')), - ) - COMMENT_STATUS = ( - ('o', _('Open')), - ('c', _('Close')), - ) - TYPE = ( - ('a', _('Article')), - ('p', _('Page')), - ) - title = models.CharField(_('title'), max_length=200, unique=True) - body = MDTextField(_('body')) - pub_time = models.DateTimeField( - _('publish time'), blank=False, null=False, default=now) - status = models.CharField( - _('status'), - max_length=1, - choices=STATUS_CHOICES, - default='p') - comment_status = models.CharField( - _('comment status'), - max_length=1, - choices=COMMENT_STATUS, - default='o') - type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a') - views = models.PositiveIntegerField(_('views'), default=0) - author = models.ForeignKey( - settings.AUTH_USER_MODEL, - verbose_name=_('author'), - blank=False, - null=False, - on_delete=models.CASCADE) - article_order = models.IntegerField( - _('order'), blank=False, null=False, default=0) - show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False) - category = models.ForeignKey( - 'Category', - verbose_name=_('category'), - on_delete=models.CASCADE, - blank=False, - null=False) - tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True) - - def body_to_string(self): - return self.body - - def __str__(self): - return self.title - - class Meta: - ordering = ['-article_order', '-pub_time'] - verbose_name = _('article') - verbose_name_plural = verbose_name - get_latest_by = 'id' - - def get_absolute_url(self): - return reverse('blog:detailbyid', kwargs={ - 'article_id': self.id, - 'year': self.creation_time.year, - 'month': self.creation_time.month, - 'day': self.creation_time.day - }) - - @cache_decorator(60 * 60 * 10) - def get_category_tree(self): - tree = self.category.get_category_tree() - names = list(map(lambda c: (c.name, c.get_absolute_url()), tree)) - - return names - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - - def viewed(self): - self.views += 1 - self.save(update_fields=['views']) - - def comment_list(self): - cache_key = 'article_comments_{id}'.format(id=self.id) - value = cache.get(cache_key) - if value: - logger.info('get article comments:{id}'.format(id=self.id)) - return value - else: - comments = self.comment_set.filter(is_enable=True).order_by('-id') - cache.set(cache_key, comments, 60 * 100) - logger.info('set article comments:{id}'.format(id=self.id)) - return comments - - def get_admin_url(self): - info = (self._meta.app_label, self._meta.model_name) - return reverse('admin:%s_%s_change' % info, args=(self.pk,)) - - @cache_decorator(expiration=60 * 100) - def next_article(self): - # 下一篇 - return Article.objects.filter( - id__gt=self.id, status='p').order_by('id').first() - - @cache_decorator(expiration=60 * 100) - def prev_article(self): - # 前一篇 - return Article.objects.filter(id__lt=self.id, status='p').first() - - def get_first_image_url(self): - """ - Get the first image url from article.body. - :return: - """ - match = re.search(r'!\[.*?\]\((.+?)\)', self.body) - if match: - return match.group(1) - return "" - - -class Category(BaseModel): - """文章分类""" - name = models.CharField(_('category name'), max_length=30, unique=True) - parent_category = models.ForeignKey( - 'self', - verbose_name=_('parent category'), - blank=True, - null=True, - on_delete=models.CASCADE) - slug = models.SlugField(default='no-slug', max_length=60, blank=True) - index = models.IntegerField(default=0, verbose_name=_('index')) - - class Meta: - ordering = ['-index'] - verbose_name = _('category') - verbose_name_plural = verbose_name - - def get_absolute_url(self): - return reverse( - 'blog:category_detail', kwargs={ - 'category_name': self.slug}) - - def __str__(self): - return self.name - - @cache_decorator(60 * 60 * 10) - def get_category_tree(self): - """ - 递归获得分类目录的父级 - :return: - """ - categorys = [] - - def parse(category): - categorys.append(category) - if category.parent_category: - parse(category.parent_category) - - parse(self) - return categorys - - @cache_decorator(60 * 60 * 10) - def get_sub_categorys(self): - """ - 获得当前分类目录所有子集 - :return: - """ - categorys = [] - all_categorys = Category.objects.all() - - def parse(category): - if category not in categorys: - categorys.append(category) - childs = all_categorys.filter(parent_category=category) - for child in childs: - if category not in categorys: - categorys.append(child) - parse(child) - - parse(self) - return categorys - - -class Tag(BaseModel): - """文章标签""" - name = models.CharField(_('tag name'), max_length=30, unique=True) - slug = models.SlugField(default='no-slug', max_length=60, blank=True) - - def __str__(self): - return self.name - - def get_absolute_url(self): - return reverse('blog:tag_detail', kwargs={'tag_name': self.slug}) - - @cache_decorator(60 * 60 * 10) - def get_article_count(self): - return Article.objects.filter(tags__name=self.name).distinct().count() - - class Meta: - ordering = ['name'] - verbose_name = _('tag') - verbose_name_plural = verbose_name - - -class Links(models.Model): - """友情链接""" - - name = models.CharField(_('link name'), max_length=30, unique=True) - link = models.URLField(_('link')) - sequence = models.IntegerField(_('order'), unique=True) - is_enable = models.BooleanField( - _('is show'), default=True, blank=False, null=False) - show_type = models.CharField( - _('show type'), - max_length=1, - choices=LinkShowType.choices, - default=LinkShowType.I) - creation_time = models.DateTimeField(_('creation time'), default=now) - last_mod_time = models.DateTimeField(_('modify time'), default=now) - - class Meta: - ordering = ['sequence'] - verbose_name = _('link') - verbose_name_plural = verbose_name - - def __str__(self): - return self.name - - -class SideBar(models.Model): - """侧边栏,可以展示一些html内容""" - name = models.CharField(_('title'), max_length=100) - content = models.TextField(_('content')) - sequence = models.IntegerField(_('order'), unique=True) - is_enable = models.BooleanField(_('is enable'), default=True) - creation_time = models.DateTimeField(_('creation time'), default=now) - last_mod_time = models.DateTimeField(_('modify time'), default=now) - - class Meta: - ordering = ['sequence'] - verbose_name = _('sidebar') - verbose_name_plural = verbose_name - - def __str__(self): - return self.name - - -class BlogSettings(models.Model): - """blog的配置""" - site_name = models.CharField( - _('site name'), - max_length=200, - null=False, - blank=False, - default='') - site_description = models.TextField( - _('site description'), - max_length=1000, - null=False, - blank=False, - default='') - site_seo_description = models.TextField( - _('site seo description'), max_length=1000, null=False, blank=False, default='') - site_keywords = models.TextField( - _('site keywords'), - max_length=1000, - null=False, - blank=False, - default='') - article_sub_length = models.IntegerField(_('article sub length'), default=300) - sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10) - sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5) - article_comment_count = models.IntegerField(_('article comment count'), default=5) - show_google_adsense = models.BooleanField(_('show adsense'), default=False) - google_adsense_codes = models.TextField( - _('adsense code'), max_length=2000, null=True, blank=True, default='') - open_site_comment = models.BooleanField(_('open site comment'), default=True) - global_header = models.TextField("公共头部", null=True, blank=True, default='') - global_footer = models.TextField("公共尾部", null=True, blank=True, default='') - beian_code = models.CharField( - '备案号', - max_length=2000, - null=True, - blank=True, - default='') - analytics_code = models.TextField( - "网站统计代码", - max_length=1000, - null=False, - blank=False, - default='') - show_gongan_code = models.BooleanField( - '是否显示公安备案号', default=False, null=False) - gongan_beiancode = models.TextField( - '公安备案号', - max_length=2000, - null=True, - blank=True, - default='') - comment_need_review = models.BooleanField( - '评论是否需要审核', default=False, null=False) - - class Meta: - verbose_name = _('Website configuration') - verbose_name_plural = verbose_name - - def __str__(self): - return self.site_name - - def clean(self): - if BlogSettings.objects.exclude(id=self.id).count(): - raise ValidationError(_('There can only be one configuration')) - - def save(self, *args, **kwargs): - super().save(*args, **kwargs) - from djangoblog.utils import cache - cache.clear() diff --git a/DjangoBlog-master/blog/search_indexes.py b/DjangoBlog-master/blog/search_indexes.py deleted file mode 100644 index 7f1dfac..0000000 --- a/DjangoBlog-master/blog/search_indexes.py +++ /dev/null @@ -1,13 +0,0 @@ -from haystack import indexes - -from blog.models import Article - - -class ArticleIndex(indexes.SearchIndex, indexes.Indexable): - text = indexes.CharField(document=True, use_template=True) - - def get_model(self): - return Article - - def index_queryset(self, using=None): - return self.get_model().objects.filter(status='p') diff --git a/DjangoBlog-master/blog/templatetags/__init__.py b/DjangoBlog-master/blog/templatetags/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/blog/templatetags/blog_tags.py b/DjangoBlog-master/blog/templatetags/blog_tags.py deleted file mode 100644 index d6cd5d5..0000000 --- a/DjangoBlog-master/blog/templatetags/blog_tags.py +++ /dev/null @@ -1,344 +0,0 @@ -import hashlib -import logging -import random -import urllib - -from django import template -from django.conf import settings -from django.db.models import Q -from django.shortcuts import get_object_or_404 -from django.template.defaultfilters import stringfilter -from django.templatetags.static import static -from django.urls import reverse -from django.utils.safestring import mark_safe - -from blog.models import Article, Category, Tag, Links, SideBar, LinkShowType -from comments.models import Comment -from djangoblog.utils import CommonMarkdown, sanitize_html -from djangoblog.utils import cache -from djangoblog.utils import get_current_site -from oauth.models import OAuthUser -from djangoblog.plugin_manage import hooks - -logger = logging.getLogger(__name__) - -register = template.Library() - - -@register.simple_tag(takes_context=True) -def head_meta(context): - return mark_safe(hooks.apply_filters('head_meta', '', context)) - - -@register.simple_tag -def timeformat(data): - try: - return data.strftime(settings.TIME_FORMAT) - except Exception as e: - logger.error(e) - return "" - - -@register.simple_tag -def datetimeformat(data): - try: - return data.strftime(settings.DATE_TIME_FORMAT) - except Exception as e: - logger.error(e) - return "" - - -@register.filter() -@stringfilter -def custom_markdown(content): - return mark_safe(CommonMarkdown.get_markdown(content)) - - -@register.simple_tag -def get_markdown_toc(content): - from djangoblog.utils import CommonMarkdown - body, toc = CommonMarkdown.get_markdown_with_toc(content) - return mark_safe(toc) - - -@register.filter() -@stringfilter -def comment_markdown(content): - content = CommonMarkdown.get_markdown(content) - return mark_safe(sanitize_html(content)) - - -@register.filter(is_safe=True) -@stringfilter -def truncatechars_content(content): - """ - 获得文章内容的摘要 - :param content: - :return: - """ - from django.template.defaultfilters import truncatechars_html - from djangoblog.utils import get_blog_setting - blogsetting = get_blog_setting() - return truncatechars_html(content, blogsetting.article_sub_length) - - -@register.filter(is_safe=True) -@stringfilter -def truncate(content): - from django.utils.html import strip_tags - - return strip_tags(content)[:150] - - -@register.inclusion_tag('blog/tags/breadcrumb.html') -def load_breadcrumb(article): - """ - 获得文章面包屑 - :param article: - :return: - """ - names = article.get_category_tree() - from djangoblog.utils import get_blog_setting - blogsetting = get_blog_setting() - site = get_current_site().domain - names.append((blogsetting.site_name, '/')) - names = names[::-1] - - return { - 'names': names, - 'title': article.title, - 'count': len(names) + 1 - } - - -@register.inclusion_tag('blog/tags/article_tag_list.html') -def load_articletags(article): - """ - 文章标签 - :param article: - :return: - """ - tags = article.tags.all() - tags_list = [] - for tag in tags: - url = tag.get_absolute_url() - count = tag.get_article_count() - tags_list.append(( - url, count, tag, random.choice(settings.BOOTSTRAP_COLOR_TYPES) - )) - return { - 'article_tags_list': tags_list - } - - -@register.inclusion_tag('blog/tags/sidebar.html') -def load_sidebar(user, linktype): - """ - 加载侧边栏 - :return: - """ - value = cache.get("sidebar" + linktype) - if value: - value['user'] = user - return value - else: - logger.info('load sidebar') - from djangoblog.utils import get_blog_setting - blogsetting = get_blog_setting() - recent_articles = Article.objects.filter( - status='p')[:blogsetting.sidebar_article_count] - sidebar_categorys = Category.objects.all() - extra_sidebars = SideBar.objects.filter( - is_enable=True).order_by('sequence') - most_read_articles = Article.objects.filter(status='p').order_by( - '-views')[:blogsetting.sidebar_article_count] - dates = Article.objects.datetimes('creation_time', 'month', order='DESC') - links = Links.objects.filter(is_enable=True).filter( - Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A)) - commment_list = Comment.objects.filter(is_enable=True).order_by( - '-id')[:blogsetting.sidebar_comment_count] - # 标签云 计算字体大小 - # 根据总数计算出平均值 大小为 (数目/平均值)*步长 - increment = 5 - tags = Tag.objects.all() - sidebar_tags = None - if tags and len(tags) > 0: - s = [t for t in [(t, t.get_article_count()) for t in tags] if t[1]] - count = sum([t[1] for t in s]) - dd = 1 if (count == 0 or not len(tags)) else count / len(tags) - import random - sidebar_tags = list( - map(lambda x: (x[0], x[1], (x[1] / dd) * increment + 10), s)) - random.shuffle(sidebar_tags) - - value = { - 'recent_articles': recent_articles, - 'sidebar_categorys': sidebar_categorys, - 'most_read_articles': most_read_articles, - 'article_dates': dates, - 'sidebar_comments': commment_list, - 'sidabar_links': links, - 'show_google_adsense': blogsetting.show_google_adsense, - 'google_adsense_codes': blogsetting.google_adsense_codes, - 'open_site_comment': blogsetting.open_site_comment, - 'show_gongan_code': blogsetting.show_gongan_code, - 'sidebar_tags': sidebar_tags, - 'extra_sidebars': extra_sidebars - } - cache.set("sidebar" + linktype, value, 60 * 60 * 60 * 3) - logger.info('set sidebar cache.key:{key}'.format(key="sidebar" + linktype)) - value['user'] = user - return value - - -@register.inclusion_tag('blog/tags/article_meta_info.html') -def load_article_metas(article, user): - """ - 获得文章meta信息 - :param article: - :return: - """ - return { - 'article': article, - 'user': user - } - - -@register.inclusion_tag('blog/tags/article_pagination.html') -def load_pagination_info(page_obj, page_type, tag_name): - previous_url = '' - next_url = '' - if page_type == '': - if page_obj.has_next(): - next_number = page_obj.next_page_number() - next_url = reverse('blog:index_page', kwargs={'page': next_number}) - if page_obj.has_previous(): - previous_number = page_obj.previous_page_number() - previous_url = reverse( - 'blog:index_page', kwargs={ - 'page': previous_number}) - if page_type == '分类标签归档': - tag = get_object_or_404(Tag, name=tag_name) - if page_obj.has_next(): - next_number = page_obj.next_page_number() - next_url = reverse( - 'blog:tag_detail_page', - kwargs={ - 'page': next_number, - 'tag_name': tag.slug}) - if page_obj.has_previous(): - previous_number = page_obj.previous_page_number() - previous_url = reverse( - 'blog:tag_detail_page', - kwargs={ - 'page': previous_number, - 'tag_name': tag.slug}) - if page_type == '作者文章归档': - if page_obj.has_next(): - next_number = page_obj.next_page_number() - next_url = reverse( - 'blog:author_detail_page', - kwargs={ - 'page': next_number, - 'author_name': tag_name}) - if page_obj.has_previous(): - previous_number = page_obj.previous_page_number() - previous_url = reverse( - 'blog:author_detail_page', - kwargs={ - 'page': previous_number, - 'author_name': tag_name}) - - if page_type == '分类目录归档': - category = get_object_or_404(Category, name=tag_name) - if page_obj.has_next(): - next_number = page_obj.next_page_number() - next_url = reverse( - 'blog:category_detail_page', - kwargs={ - 'page': next_number, - 'category_name': category.slug}) - if page_obj.has_previous(): - previous_number = page_obj.previous_page_number() - previous_url = reverse( - 'blog:category_detail_page', - kwargs={ - 'page': previous_number, - 'category_name': category.slug}) - - return { - 'previous_url': previous_url, - 'next_url': next_url, - 'page_obj': page_obj - } - - -@register.inclusion_tag('blog/tags/article_info.html') -def load_article_detail(article, isindex, user): - """ - 加载文章详情 - :param article: - :param isindex:是否列表页,若是列表页只显示摘要 - :return: - """ - from djangoblog.utils import get_blog_setting - blogsetting = get_blog_setting() - - return { - 'article': article, - 'isindex': isindex, - 'user': user, - 'open_site_comment': blogsetting.open_site_comment, - } - - -# return only the URL of the gravatar -# TEMPLATE USE: {{ email|gravatar_url:150 }} -@register.filter -def gravatar_url(email, size=40): - """获得gravatar头像""" - cachekey = 'gravatat/' + email - url = cache.get(cachekey) - if url: - return url - else: - usermodels = OAuthUser.objects.filter(email=email) - if usermodels: - o = list(filter(lambda x: x.picture is not None, usermodels)) - if o: - return o[0].picture - email = email.encode('utf-8') - - default = static('blog/img/avatar.png') - - url = "https://www.gravatar.com/avatar/%s?%s" % (hashlib.md5( - email.lower()).hexdigest(), urllib.parse.urlencode({'d': default, 's': str(size)})) - cache.set(cachekey, url, 60 * 60 * 10) - logger.info('set gravatar cache.key:{key}'.format(key=cachekey)) - return url - - -@register.filter -def gravatar(email, size=40): - """获得gravatar头像""" - url = gravatar_url(email, size) - return mark_safe( - '' % - (url, size, size)) - - -@register.simple_tag -def query(qs, **kwargs): - """ template tag which allows queryset filtering. Usage: - {% query books author=author as mybooks %} - {% for book in mybooks %} - ... - {% endfor %} - """ - return qs.filter(**kwargs) - - -@register.filter -def addstr(arg1, arg2): - """concatenate arg1 & arg2""" - return str(arg1) + str(arg2) diff --git a/DjangoBlog-master/blog/tests.py b/DjangoBlog-master/blog/tests.py deleted file mode 100644 index ee13505..0000000 --- a/DjangoBlog-master/blog/tests.py +++ /dev/null @@ -1,232 +0,0 @@ -import os - -from django.conf import settings -from django.core.files.uploadedfile import SimpleUploadedFile -from django.core.management import call_command -from django.core.paginator import Paginator -from django.templatetags.static import static -from django.test import Client, RequestFactory, TestCase -from django.urls import reverse -from django.utils import timezone - -from accounts.models import BlogUser -from blog.forms import BlogSearchForm -from blog.models import Article, Category, Tag, SideBar, Links -from blog.templatetags.blog_tags import load_pagination_info, load_articletags -from djangoblog.utils import get_current_site, get_sha256 -from oauth.models import OAuthUser, OAuthConfig - - -# Create your tests here. - -class ArticleTest(TestCase): - def setUp(self): - self.client = Client() - self.factory = RequestFactory() - - def test_validate_article(self): - site = get_current_site().domain - user = BlogUser.objects.get_or_create( - email="liangliangyy@gmail.com", - username="liangliangyy")[0] - user.set_password("liangliangyy") - user.is_staff = True - user.is_superuser = True - user.save() - response = self.client.get(user.get_absolute_url()) - self.assertEqual(response.status_code, 200) - response = self.client.get('/admin/servermanager/emailsendlog/') - response = self.client.get('admin/admin/logentry/') - s = SideBar() - s.sequence = 1 - s.name = 'test' - s.content = 'test content' - s.is_enable = True - s.save() - - category = Category() - category.name = "category" - category.creation_time = timezone.now() - category.last_mod_time = timezone.now() - category.save() - - tag = Tag() - tag.name = "nicetag" - tag.save() - - article = Article() - article.title = "nicetitle" - article.body = "nicecontent" - article.author = user - article.category = category - article.type = 'a' - article.status = 'p' - - article.save() - self.assertEqual(0, article.tags.count()) - article.tags.add(tag) - article.save() - self.assertEqual(1, article.tags.count()) - - for i in range(20): - article = Article() - article.title = "nicetitle" + str(i) - article.body = "nicetitle" + str(i) - article.author = user - article.category = category - article.type = 'a' - article.status = 'p' - article.save() - article.tags.add(tag) - article.save() - from blog.documents import ELASTICSEARCH_ENABLED - if ELASTICSEARCH_ENABLED: - call_command("build_index") - response = self.client.get('/search', {'q': 'nicetitle'}) - self.assertEqual(response.status_code, 200) - - response = self.client.get(article.get_absolute_url()) - self.assertEqual(response.status_code, 200) - from djangoblog.spider_notify import SpiderNotify - SpiderNotify.notify(article.get_absolute_url()) - response = self.client.get(tag.get_absolute_url()) - self.assertEqual(response.status_code, 200) - - response = self.client.get(category.get_absolute_url()) - self.assertEqual(response.status_code, 200) - - response = self.client.get('/search', {'q': 'django'}) - self.assertEqual(response.status_code, 200) - s = load_articletags(article) - self.assertIsNotNone(s) - - self.client.login(username='liangliangyy', password='liangliangyy') - - response = self.client.get(reverse('blog:archives')) - self.assertEqual(response.status_code, 200) - - p = Paginator(Article.objects.all(), settings.PAGINATE_BY) - self.check_pagination(p, '', '') - - p = Paginator(Article.objects.filter(tags=tag), settings.PAGINATE_BY) - self.check_pagination(p, '分类标签归档', tag.slug) - - p = Paginator( - Article.objects.filter( - author__username='liangliangyy'), settings.PAGINATE_BY) - self.check_pagination(p, '作者文章归档', 'liangliangyy') - - p = Paginator(Article.objects.filter(category=category), settings.PAGINATE_BY) - self.check_pagination(p, '分类目录归档', category.slug) - - f = BlogSearchForm() - f.search() - # self.client.login(username='liangliangyy', password='liangliangyy') - from djangoblog.spider_notify import SpiderNotify - SpiderNotify.baidu_notify([article.get_full_url()]) - - from blog.templatetags.blog_tags import gravatar_url, gravatar - u = gravatar_url('liangliangyy@gmail.com') - u = gravatar('liangliangyy@gmail.com') - - link = Links( - sequence=1, - name="lylinux", - link='https://wwww.lylinux.net') - link.save() - response = self.client.get('/links.html') - self.assertEqual(response.status_code, 200) - - response = self.client.get('/feed/') - self.assertEqual(response.status_code, 200) - - response = self.client.get('/sitemap.xml') - self.assertEqual(response.status_code, 200) - - self.client.get("/admin/blog/article/1/delete/") - self.client.get('/admin/servermanager/emailsendlog/') - self.client.get('/admin/admin/logentry/') - self.client.get('/admin/admin/logentry/1/change/') - - def check_pagination(self, p, type, value): - for page in range(1, p.num_pages + 1): - s = load_pagination_info(p.page(page), type, value) - self.assertIsNotNone(s) - if s['previous_url']: - response = self.client.get(s['previous_url']) - self.assertEqual(response.status_code, 200) - if s['next_url']: - response = self.client.get(s['next_url']) - self.assertEqual(response.status_code, 200) - - def test_image(self): - import requests - rsp = requests.get( - 'https://www.python.org/static/img/python-logo.png') - imagepath = os.path.join(settings.BASE_DIR, 'python.png') - with open(imagepath, 'wb') as file: - file.write(rsp.content) - rsp = self.client.post('/upload') - self.assertEqual(rsp.status_code, 403) - sign = get_sha256(get_sha256(settings.SECRET_KEY)) - with open(imagepath, 'rb') as file: - imgfile = SimpleUploadedFile( - 'python.png', file.read(), content_type='image/jpg') - form_data = {'python.png': imgfile} - rsp = self.client.post( - '/upload?sign=' + sign, form_data, follow=True) - self.assertEqual(rsp.status_code, 200) - os.remove(imagepath) - from djangoblog.utils import save_user_avatar, send_email - send_email(['qq@qq.com'], 'testTitle', 'testContent') - save_user_avatar( - 'https://www.python.org/static/img/python-logo.png') - - def test_errorpage(self): - rsp = self.client.get('/eee') - self.assertEqual(rsp.status_code, 404) - - def test_commands(self): - user = BlogUser.objects.get_or_create( - email="liangliangyy@gmail.com", - username="liangliangyy")[0] - user.set_password("liangliangyy") - user.is_staff = True - user.is_superuser = True - user.save() - - c = OAuthConfig() - c.type = 'qq' - c.appkey = 'appkey' - c.appsecret = 'appsecret' - c.save() - - u = OAuthUser() - u.type = 'qq' - u.openid = 'openid' - u.user = user - u.picture = static("/blog/img/avatar.png") - u.metadata = ''' -{ -"figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30" -}''' - u.save() - - u = OAuthUser() - u.type = 'qq' - u.openid = 'openid1' - u.picture = 'https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30' - u.metadata = ''' - { - "figureurl": "https://qzapp.qlogo.cn/qzapp/101513904/C740E30B4113EAA80E0D9918ABC78E82/30" - }''' - u.save() - - from blog.documents import ELASTICSEARCH_ENABLED - if ELASTICSEARCH_ENABLED: - call_command("build_index") - call_command("ping_baidu", "all") - call_command("create_testdata") - call_command("clear_cache") - call_command("sync_user_avatar") - call_command("build_search_words") diff --git a/DjangoBlog-master/blog/urls.py b/DjangoBlog-master/blog/urls.py deleted file mode 100644 index adf2703..0000000 --- a/DjangoBlog-master/blog/urls.py +++ /dev/null @@ -1,62 +0,0 @@ -from django.urls import path -from django.views.decorators.cache import cache_page - -from . import views - -app_name = "blog" -urlpatterns = [ - path( - r'', - views.IndexView.as_view(), - name='index'), - path( - r'page//', - views.IndexView.as_view(), - name='index_page'), - path( - r'article////.html', - views.ArticleDetailView.as_view(), - name='detailbyid'), - path( - r'category/.html', - views.CategoryDetailView.as_view(), - name='category_detail'), - path( - r'category//.html', - views.CategoryDetailView.as_view(), - name='category_detail_page'), - path( - r'author/.html', - views.AuthorDetailView.as_view(), - name='author_detail'), - path( - r'author//.html', - views.AuthorDetailView.as_view(), - name='author_detail_page'), - path( - r'tag/.html', - views.TagDetailView.as_view(), - name='tag_detail'), - path( - r'tag//.html', - views.TagDetailView.as_view(), - name='tag_detail_page'), - path( - 'archives.html', - cache_page( - 60 * 60)( - views.ArchivesView.as_view()), - name='archives'), - path( - 'links.html', - views.LinkListView.as_view(), - name='links'), - path( - r'upload', - views.fileupload, - name='upload'), - path( - r'clean', - views.clean_cache_view, - name='clean'), -] diff --git a/DjangoBlog-master/blog/views.py b/DjangoBlog-master/blog/views.py deleted file mode 100644 index d5dc7ec..0000000 --- a/DjangoBlog-master/blog/views.py +++ /dev/null @@ -1,379 +0,0 @@ -import logging -import os -import uuid - -from django.conf import settings -from django.core.paginator import Paginator -from django.http import HttpResponse, HttpResponseForbidden -from django.shortcuts import get_object_or_404 -from django.shortcuts import render -from django.templatetags.static import static -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ -from django.views.decorators.csrf import csrf_exempt -from django.views.generic.detail import DetailView -from django.views.generic.list import ListView -from haystack.views import SearchView - -from blog.models import Article, Category, LinkShowType, Links, Tag -from comments.forms import CommentForm -from djangoblog.plugin_manage import hooks -from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME -from djangoblog.utils import cache, get_blog_setting, get_sha256 - -logger = logging.getLogger(__name__) - - -class ArticleListView(ListView): - # template_name属性用于指定使用哪个模板进行渲染 - template_name = 'blog/article_index.html' - - # context_object_name属性用于给上下文变量取名(在模板中使用该名字) - context_object_name = 'article_list' - - # 页面类型,分类目录或标签列表等 - page_type = '' - paginate_by = settings.PAGINATE_BY - page_kwarg = 'page' - link_type = LinkShowType.L - - def get_view_cache_key(self): - return self.request.get['pages'] - - @property - def page_number(self): - page_kwarg = self.page_kwarg - page = self.kwargs.get( - page_kwarg) or self.request.GET.get(page_kwarg) or 1 - return page - - def get_queryset_cache_key(self): - """ - 子类重写.获得queryset的缓存key - """ - raise NotImplementedError() - - def get_queryset_data(self): - """ - 子类重写.获取queryset的数据 - """ - raise NotImplementedError() - - def get_queryset_from_cache(self, cache_key): - ''' - 缓存页面数据 - :param cache_key: 缓存key - :return: - ''' - value = cache.get(cache_key) - if value: - logger.info('get view cache.key:{key}'.format(key=cache_key)) - return value - else: - article_list = self.get_queryset_data() - cache.set(cache_key, article_list) - logger.info('set view cache.key:{key}'.format(key=cache_key)) - return article_list - - def get_queryset(self): - ''' - 重写默认,从缓存获取数据 - :return: - ''' - key = self.get_queryset_cache_key() - value = self.get_queryset_from_cache(key) - return value - - def get_context_data(self, **kwargs): - kwargs['linktype'] = self.link_type - return super(ArticleListView, self).get_context_data(**kwargs) - - -class IndexView(ArticleListView): - ''' - 首页 - ''' - # 友情链接类型 - link_type = LinkShowType.I - - def get_queryset_data(self): - article_list = Article.objects.filter(type='a', status='p') - return article_list - - def get_queryset_cache_key(self): - cache_key = 'index_{page}'.format(page=self.page_number) - return cache_key - - -class ArticleDetailView(DetailView): - ''' - 文章详情页面 - ''' - template_name = 'blog/article_detail.html' - model = Article - pk_url_kwarg = 'article_id' - context_object_name = "article" - - def get_context_data(self, **kwargs): - comment_form = CommentForm() - - article_comments = self.object.comment_list() - parent_comments = article_comments.filter(parent_comment=None) - blog_setting = get_blog_setting() - paginator = Paginator(parent_comments, blog_setting.article_comment_count) - page = self.request.GET.get('comment_page', '1') - if not page.isnumeric(): - page = 1 - else: - page = int(page) - if page < 1: - page = 1 - if page > paginator.num_pages: - page = paginator.num_pages - - p_comments = paginator.page(page) - next_page = p_comments.next_page_number() if p_comments.has_next() else None - prev_page = p_comments.previous_page_number() if p_comments.has_previous() else None - - if next_page: - kwargs[ - 'comment_next_page_url'] = self.object.get_absolute_url() + f'?comment_page={next_page}#commentlist-container' - if prev_page: - kwargs[ - 'comment_prev_page_url'] = self.object.get_absolute_url() + f'?comment_page={prev_page}#commentlist-container' - kwargs['form'] = comment_form - kwargs['article_comments'] = article_comments - kwargs['p_comments'] = p_comments - kwargs['comment_count'] = len( - article_comments) if article_comments else 0 - - kwargs['next_article'] = self.object.next_article - kwargs['prev_article'] = self.object.prev_article - - context = super(ArticleDetailView, self).get_context_data(**kwargs) - article = self.object - # Action Hook, 通知插件"文章详情已获取" - hooks.run_action('after_article_body_get', article=article, request=self.request) - # # Filter Hook, 允许插件修改文章正文 - article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article, - request=self.request) - - return context - - -class CategoryDetailView(ArticleListView): - ''' - 分类目录列表 - ''' - page_type = "分类目录归档" - - def get_queryset_data(self): - slug = self.kwargs['category_name'] - category = get_object_or_404(Category, slug=slug) - - categoryname = category.name - self.categoryname = categoryname - categorynames = list( - map(lambda c: c.name, category.get_sub_categorys())) - article_list = Article.objects.filter( - category__name__in=categorynames, status='p') - return article_list - - def get_queryset_cache_key(self): - slug = self.kwargs['category_name'] - category = get_object_or_404(Category, slug=slug) - categoryname = category.name - self.categoryname = categoryname - cache_key = 'category_list_{categoryname}_{page}'.format( - categoryname=categoryname, page=self.page_number) - return cache_key - - def get_context_data(self, **kwargs): - - categoryname = self.categoryname - try: - categoryname = categoryname.split('/')[-1] - except BaseException: - pass - kwargs['page_type'] = CategoryDetailView.page_type - kwargs['tag_name'] = categoryname - return super(CategoryDetailView, self).get_context_data(**kwargs) - - -class AuthorDetailView(ArticleListView): - ''' - 作者详情页 - ''' - page_type = '作者文章归档' - - def get_queryset_cache_key(self): - from uuslug import slugify - author_name = slugify(self.kwargs['author_name']) - cache_key = 'author_{author_name}_{page}'.format( - author_name=author_name, page=self.page_number) - return cache_key - - def get_queryset_data(self): - author_name = self.kwargs['author_name'] - article_list = Article.objects.filter( - author__username=author_name, type='a', status='p') - return article_list - - def get_context_data(self, **kwargs): - author_name = self.kwargs['author_name'] - kwargs['page_type'] = AuthorDetailView.page_type - kwargs['tag_name'] = author_name - return super(AuthorDetailView, self).get_context_data(**kwargs) - - -class TagDetailView(ArticleListView): - ''' - 标签列表页面 - ''' - page_type = '分类标签归档' - - def get_queryset_data(self): - slug = self.kwargs['tag_name'] - tag = get_object_or_404(Tag, slug=slug) - tag_name = tag.name - self.name = tag_name - article_list = Article.objects.filter( - tags__name=tag_name, type='a', status='p') - return article_list - - def get_queryset_cache_key(self): - slug = self.kwargs['tag_name'] - tag = get_object_or_404(Tag, slug=slug) - tag_name = tag.name - self.name = tag_name - cache_key = 'tag_{tag_name}_{page}'.format( - tag_name=tag_name, page=self.page_number) - return cache_key - - def get_context_data(self, **kwargs): - # tag_name = self.kwargs['tag_name'] - tag_name = self.name - kwargs['page_type'] = TagDetailView.page_type - kwargs['tag_name'] = tag_name - return super(TagDetailView, self).get_context_data(**kwargs) - - -class ArchivesView(ArticleListView): - ''' - 文章归档页面 - ''' - page_type = '文章归档' - paginate_by = None - page_kwarg = None - template_name = 'blog/article_archives.html' - - def get_queryset_data(self): - return Article.objects.filter(status='p').all() - - def get_queryset_cache_key(self): - cache_key = 'archives' - return cache_key - - -class LinkListView(ListView): - model = Links - template_name = 'blog/links_list.html' - - def get_queryset(self): - return Links.objects.filter(is_enable=True) - - -class EsSearchView(SearchView): - def get_context(self): - paginator, page = self.build_page() - context = { - "query": self.query, - "form": self.form, - "page": page, - "paginator": paginator, - "suggestion": None, - } - if hasattr(self.results, "query") and self.results.query.backend.include_spelling: - context["suggestion"] = self.results.query.get_spelling_suggestion() - context.update(self.extra_context()) - - return context - - -@csrf_exempt -def fileupload(request): - """ - 该方法需自己写调用端来上传图片,该方法仅提供图床功能 - :param request: - :return: - """ - if request.method == 'POST': - sign = request.GET.get('sign', None) - if not sign: - return HttpResponseForbidden() - if not sign == get_sha256(get_sha256(settings.SECRET_KEY)): - return HttpResponseForbidden() - response = [] - for filename in request.FILES: - timestr = timezone.now().strftime('%Y/%m/%d') - imgextensions = ['jpg', 'png', 'jpeg', 'bmp'] - fname = u''.join(str(filename)) - isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0 - base_dir = os.path.join(settings.STATICFILES, "files" if not isimage else "image", timestr) - if not os.path.exists(base_dir): - os.makedirs(base_dir) - savepath = os.path.normpath(os.path.join(base_dir, f"{uuid.uuid4().hex}{os.path.splitext(filename)[-1]}")) - if not savepath.startswith(base_dir): - return HttpResponse("only for post") - with open(savepath, 'wb+') as wfile: - for chunk in request.FILES[filename].chunks(): - wfile.write(chunk) - if isimage: - from PIL import Image - image = Image.open(savepath) - image.save(savepath, quality=20, optimize=True) - url = static(savepath) - response.append(url) - return HttpResponse(response) - - else: - return HttpResponse("only for post") - - -def page_not_found_view( - request, - exception, - template_name='blog/error_page.html'): - if exception: - logger.error(exception) - url = request.get_full_path() - return render(request, - template_name, - {'message': _('Sorry, the page you requested is not found, please click the home page to see other?'), - 'statuscode': '404'}, - status=404) - - -def server_error_view(request, template_name='blog/error_page.html'): - return render(request, - template_name, - {'message': _('Sorry, the server is busy, please click the home page to see other?'), - 'statuscode': '500'}, - status=500) - - -def permission_denied_view( - request, - exception, - template_name='blog/error_page.html'): - if exception: - logger.error(exception) - return render( - request, template_name, { - 'message': _('Sorry, you do not have permission to access this page?'), - 'statuscode': '403'}, status=403) - - -def clean_cache_view(request): - cache.clear() - return HttpResponse('ok') diff --git a/DjangoBlog-master/comments/__init__.py b/DjangoBlog-master/comments/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/comments/admin.py b/DjangoBlog-master/comments/admin.py deleted file mode 100644 index a814f3f..0000000 --- a/DjangoBlog-master/comments/admin.py +++ /dev/null @@ -1,47 +0,0 @@ -from django.contrib import admin -from django.urls import reverse -from django.utils.html import format_html -from django.utils.translation import gettext_lazy as _ - - -def disable_commentstatus(modeladmin, request, queryset): - queryset.update(is_enable=False) - - -def enable_commentstatus(modeladmin, request, queryset): - queryset.update(is_enable=True) - - -disable_commentstatus.short_description = _('Disable comments') -enable_commentstatus.short_description = _('Enable comments') - - -class CommentAdmin(admin.ModelAdmin): - list_per_page = 20 - list_display = ( - 'id', - 'body', - 'link_to_userinfo', - 'link_to_article', - 'is_enable', - 'creation_time') - list_display_links = ('id', 'body', 'is_enable') - list_filter = ('is_enable',) - exclude = ('creation_time', 'last_modify_time') - actions = [disable_commentstatus, enable_commentstatus] - - def link_to_userinfo(self, obj): - info = (obj.author._meta.app_label, obj.author._meta.model_name) - link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,)) - return format_html( - u'%s' % - (link, obj.author.nickname if obj.author.nickname else obj.author.email)) - - def link_to_article(self, obj): - info = (obj.article._meta.app_label, obj.article._meta.model_name) - link = reverse('admin:%s_%s_change' % info, args=(obj.article.id,)) - return format_html( - u'%s' % (link, obj.article.title)) - - link_to_userinfo.short_description = _('User') - link_to_article.short_description = _('Article') diff --git a/DjangoBlog-master/comments/apps.py b/DjangoBlog-master/comments/apps.py deleted file mode 100644 index ff01b77..0000000 --- a/DjangoBlog-master/comments/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class CommentsConfig(AppConfig): - name = 'comments' diff --git a/DjangoBlog-master/comments/forms.py b/DjangoBlog-master/comments/forms.py deleted file mode 100644 index e83737d..0000000 --- a/DjangoBlog-master/comments/forms.py +++ /dev/null @@ -1,13 +0,0 @@ -from django import forms -from django.forms import ModelForm - -from .models import Comment - - -class CommentForm(ModelForm): - parent_comment_id = forms.IntegerField( - widget=forms.HiddenInput, required=False) - - class Meta: - model = Comment - fields = ['body'] diff --git a/DjangoBlog-master/comments/migrations/0001_initial.py b/DjangoBlog-master/comments/migrations/0001_initial.py deleted file mode 100644 index 61d1e53..0000000 --- a/DjangoBlog-master/comments/migrations/0001_initial.py +++ /dev/null @@ -1,38 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-02 07:14 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ('blog', '0001_initial'), - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='Comment', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('body', models.TextField(max_length=300, verbose_name='正文')), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')), - ('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='文章')), - ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')), - ('parent_comment', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='上级评论')), - ], - options={ - 'verbose_name': '评论', - 'verbose_name_plural': '评论', - 'ordering': ['-id'], - 'get_latest_by': 'id', - }, - ), - ] diff --git a/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enable.py b/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enable.py deleted file mode 100644 index 17c44db..0000000 --- a/DjangoBlog-master/comments/migrations/0002_alter_comment_is_enable.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.1.7 on 2023-04-24 13:48 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('comments', '0001_initial'), - ] - - operations = [ - migrations.AlterField( - model_name='comment', - name='is_enable', - field=models.BooleanField(default=False, verbose_name='是否显示'), - ), - ] diff --git a/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py b/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py deleted file mode 100644 index a1ca970..0000000 --- a/DjangoBlog-master/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py +++ /dev/null @@ -1,60 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-06 13:13 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('blog', '0005_alter_article_options_alter_category_options_and_more'), - ('comments', '0002_alter_comment_is_enable'), - ] - - operations = [ - migrations.AlterModelOptions( - name='comment', - options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'comment', 'verbose_name_plural': 'comment'}, - ), - migrations.RemoveField( - model_name='comment', - name='created_time', - ), - migrations.RemoveField( - model_name='comment', - name='last_mod_time', - ), - migrations.AddField( - model_name='comment', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='comment', - name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'), - ), - migrations.AlterField( - model_name='comment', - name='article', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.article', verbose_name='article'), - ), - migrations.AlterField( - model_name='comment', - name='author', - field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'), - ), - migrations.AlterField( - model_name='comment', - name='is_enable', - field=models.BooleanField(default=False, verbose_name='enable'), - ), - migrations.AlterField( - model_name='comment', - name='parent_comment', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='comments.comment', verbose_name='parent comment'), - ), - ] diff --git a/DjangoBlog-master/comments/migrations/__init__.py b/DjangoBlog-master/comments/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/comments/models.py b/DjangoBlog-master/comments/models.py deleted file mode 100644 index 7c3bbc8..0000000 --- a/DjangoBlog-master/comments/models.py +++ /dev/null @@ -1,39 +0,0 @@ -from django.conf import settings -from django.db import models -from django.utils.timezone import now -from django.utils.translation import gettext_lazy as _ - -from blog.models import Article - - -# Create your models here. - -class Comment(models.Model): - body = models.TextField('正文', max_length=300) - creation_time = models.DateTimeField(_('creation time'), default=now) - last_modify_time = models.DateTimeField(_('last modify time'), default=now) - author = models.ForeignKey( - settings.AUTH_USER_MODEL, - verbose_name=_('author'), - on_delete=models.CASCADE) - article = models.ForeignKey( - Article, - verbose_name=_('article'), - on_delete=models.CASCADE) - parent_comment = models.ForeignKey( - 'self', - verbose_name=_('parent comment'), - blank=True, - null=True, - on_delete=models.CASCADE) - is_enable = models.BooleanField(_('enable'), - default=False, blank=False, null=False) - - class Meta: - ordering = ['-id'] - verbose_name = _('comment') - verbose_name_plural = verbose_name - get_latest_by = 'id' - - def __str__(self): - return self.body diff --git a/DjangoBlog-master/comments/templatetags/__init__.py b/DjangoBlog-master/comments/templatetags/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/comments/templatetags/comments_tags.py b/DjangoBlog-master/comments/templatetags/comments_tags.py deleted file mode 100644 index fde02b4..0000000 --- a/DjangoBlog-master/comments/templatetags/comments_tags.py +++ /dev/null @@ -1,30 +0,0 @@ -from django import template - -register = template.Library() - - -@register.simple_tag -def parse_commenttree(commentlist, comment): - """获得当前评论子评论的列表 - 用法: {% parse_commenttree article_comments comment as childcomments %} - """ - datas = [] - - def parse(c): - childs = commentlist.filter(parent_comment=c, is_enable=True) - for child in childs: - datas.append(child) - parse(child) - - parse(comment) - return datas - - -@register.inclusion_tag('comments/tags/comment_item.html') -def show_comment_item(comment, ischild): - """评论""" - depth = 1 if ischild else 2 - return { - 'comment_item': comment, - 'depth': depth - } diff --git a/DjangoBlog-master/comments/tests.py b/DjangoBlog-master/comments/tests.py deleted file mode 100644 index 2a7f55f..0000000 --- a/DjangoBlog-master/comments/tests.py +++ /dev/null @@ -1,109 +0,0 @@ -from django.test import Client, RequestFactory, TransactionTestCase -from django.urls import reverse - -from accounts.models import BlogUser -from blog.models import Category, Article -from comments.models import Comment -from comments.templatetags.comments_tags import * -from djangoblog.utils import get_max_articleid_commentid - - -# Create your tests here. - -class CommentsTest(TransactionTestCase): - def setUp(self): - self.client = Client() - self.factory = RequestFactory() - from blog.models import BlogSettings - value = BlogSettings() - value.comment_need_review = True - value.save() - - self.user = BlogUser.objects.create_superuser( - email="liangliangyy1@gmail.com", - username="liangliangyy1", - password="liangliangyy1") - - def update_article_comment_status(self, article): - comments = article.comment_set.all() - for comment in comments: - comment.is_enable = True - comment.save() - - def test_validate_comment(self): - self.client.login(username='liangliangyy1', password='liangliangyy1') - - category = Category() - category.name = "categoryccc" - category.save() - - article = Article() - article.title = "nicetitleccc" - article.body = "nicecontentccc" - article.author = self.user - article.category = category - article.type = 'a' - article.status = 'p' - article.save() - - comment_url = reverse( - 'comments:postcomment', kwargs={ - 'article_id': article.id}) - - response = self.client.post(comment_url, - { - 'body': '123ffffffffff' - }) - - self.assertEqual(response.status_code, 302) - - article = Article.objects.get(pk=article.pk) - self.assertEqual(len(article.comment_list()), 0) - self.update_article_comment_status(article) - - self.assertEqual(len(article.comment_list()), 1) - - response = self.client.post(comment_url, - { - 'body': '123ffffffffff', - }) - - self.assertEqual(response.status_code, 302) - - article = Article.objects.get(pk=article.pk) - self.update_article_comment_status(article) - self.assertEqual(len(article.comment_list()), 2) - parent_comment_id = article.comment_list()[0].id - - response = self.client.post(comment_url, - { - 'body': ''' - # Title1 - - ```python - import os - ``` - - [url](https://www.lylinux.net/) - - [ddd](http://www.baidu.com) - - - ''', - 'parent_comment_id': parent_comment_id - }) - - self.assertEqual(response.status_code, 302) - self.update_article_comment_status(article) - article = Article.objects.get(pk=article.pk) - self.assertEqual(len(article.comment_list()), 3) - comment = Comment.objects.get(id=parent_comment_id) - tree = parse_commenttree(article.comment_list(), comment) - self.assertEqual(len(tree), 1) - data = show_comment_item(comment, True) - self.assertIsNotNone(data) - s = get_max_articleid_commentid() - self.assertIsNotNone(s) - - from comments.utils import send_comment_email - send_comment_email(comment) diff --git a/DjangoBlog-master/comments/urls.py b/DjangoBlog-master/comments/urls.py deleted file mode 100644 index 7df3fab..0000000 --- a/DjangoBlog-master/comments/urls.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.urls import path - -from . import views - -app_name = "comments" -urlpatterns = [ - path( - 'article//postcomment', - views.CommentPostView.as_view(), - name='postcomment'), -] diff --git a/DjangoBlog-master/comments/utils.py b/DjangoBlog-master/comments/utils.py deleted file mode 100644 index f01dba7..0000000 --- a/DjangoBlog-master/comments/utils.py +++ /dev/null @@ -1,38 +0,0 @@ -import logging - -from django.utils.translation import gettext_lazy as _ - -from djangoblog.utils import get_current_site -from djangoblog.utils import send_email - -logger = logging.getLogger(__name__) - - -def send_comment_email(comment): - site = get_current_site().domain - subject = _('Thanks for your comment') - article_url = f"https://{site}{comment.article.get_absolute_url()}" - html_content = _("""

Thank you very much for your comments on this site

- You can visit %(article_title)s - to review your comments, - Thank you again! -
- If the link above cannot be opened, please copy this link to your browser. - %(article_url)s""") % {'article_url': article_url, 'article_title': comment.article.title} - tomail = comment.author.email - send_email([tomail], subject, html_content) - try: - if comment.parent_comment: - html_content = _("""Your comment on %(article_title)s
has - received a reply.
%(comment_body)s -
- go check it out! -
- If the link above cannot be opened, please copy this link to your browser. - %(article_url)s - """) % {'article_url': article_url, 'article_title': comment.article.title, - 'comment_body': comment.parent_comment.body} - tomail = comment.parent_comment.author.email - send_email([tomail], subject, html_content) - except Exception as e: - logger.error(e) diff --git a/DjangoBlog-master/comments/views.py b/DjangoBlog-master/comments/views.py deleted file mode 100644 index ad9b2b9..0000000 --- a/DjangoBlog-master/comments/views.py +++ /dev/null @@ -1,63 +0,0 @@ -# Create your views here. -from django.core.exceptions import ValidationError -from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404 -from django.utils.decorators import method_decorator -from django.views.decorators.csrf import csrf_protect -from django.views.generic.edit import FormView - -from accounts.models import BlogUser -from blog.models import Article -from .forms import CommentForm -from .models import Comment - - -class CommentPostView(FormView): - form_class = CommentForm - template_name = 'blog/article_detail.html' - - @method_decorator(csrf_protect) - def dispatch(self, *args, **kwargs): - return super(CommentPostView, self).dispatch(*args, **kwargs) - - def get(self, request, *args, **kwargs): - article_id = self.kwargs['article_id'] - article = get_object_or_404(Article, pk=article_id) - url = article.get_absolute_url() - return HttpResponseRedirect(url + "#comments") - - def form_invalid(self, form): - article_id = self.kwargs['article_id'] - article = get_object_or_404(Article, pk=article_id) - - return self.render_to_response({ - 'form': form, - 'article': article - }) - - def form_valid(self, form): - """提交的数据验证合法后的逻辑""" - user = self.request.user - author = BlogUser.objects.get(pk=user.pk) - article_id = self.kwargs['article_id'] - article = get_object_or_404(Article, pk=article_id) - - if article.comment_status == 'c' or article.status == 'c': - raise ValidationError("该文章评论已关闭.") - comment = form.save(False) - comment.article = article - from djangoblog.utils import get_blog_setting - settings = get_blog_setting() - if not settings.comment_need_review: - comment.is_enable = True - comment.author = author - - if form.cleaned_data['parent_comment_id']: - parent_comment = Comment.objects.get( - pk=form.cleaned_data['parent_comment_id']) - comment.parent_comment = parent_comment - - comment.save(True) - return HttpResponseRedirect( - "%s#div-comment-%d" % - (article.get_absolute_url(), comment.pk)) diff --git a/DjangoBlog-master/db.sqlite3 b/DjangoBlog-master/db.sqlite3 deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml b/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml deleted file mode 100644 index 83e35ff..0000000 --- a/DjangoBlog-master/deploy/docker-compose/docker-compose.es.yml +++ /dev/null @@ -1,48 +0,0 @@ -version: '3' - -services: - es: - image: liangliangyy/elasticsearch-analysis-ik:8.6.1 - container_name: es - restart: always - environment: - - discovery.type=single-node - - "ES_JAVA_OPTS=-Xms512m -Xmx512m" - ports: - - 9200:9200 - volumes: - - ./bin/datas/es/:/usr/share/elasticsearch/data/ - - kibana: - image: kibana:8.6.1 - restart: always - container_name: kibana - ports: - - 5601:5601 - environment: - - ELASTICSEARCH_HOSTS=http://es:9200 - - djangoblog: - build: . - restart: always - command: bash -c 'sh /code/djangoblog/bin/docker_start.sh' - ports: - - "8000:8000" - volumes: - - ./collectedstatic:/code/djangoblog/collectedstatic - - ./uploads:/code/djangoblog/uploads - environment: - - DJANGO_MYSQL_DATABASE=djangoblog - - DJANGO_MYSQL_USER=root - - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E - - DJANGO_MYSQL_HOST=db - - DJANGO_MYSQL_PORT=3306 - - DJANGO_MEMCACHED_LOCATION=memcached:11211 - - DJANGO_ELASTICSEARCH_HOST=es:9200 - links: - - db - - memcached - depends_on: - - db - container_name: djangoblog - diff --git a/DjangoBlog-master/deploy/docker-compose/docker-compose.yml b/DjangoBlog-master/deploy/docker-compose/docker-compose.yml deleted file mode 100644 index 9609af3..0000000 --- a/DjangoBlog-master/deploy/docker-compose/docker-compose.yml +++ /dev/null @@ -1,60 +0,0 @@ -version: '3' - -services: - db: - image: mysql:latest - restart: always - environment: - - MYSQL_DATABASE=djangoblog - - MYSQL_ROOT_PASSWORD=DjAnGoBlOg!2!Q@W#E - ports: - - 3306:3306 - volumes: - - ./bin/datas/mysql/:/var/lib/mysql - depends_on: - - redis - container_name: db - - djangoblog: - build: - context: ../../ - restart: always - command: bash -c 'sh /code/djangoblog/bin/docker_start.sh' - ports: - - "8000:8000" - volumes: - - ./collectedstatic:/code/djangoblog/collectedstatic - - ./logs:/code/djangoblog/logs - - ./uploads:/code/djangoblog/uploads - environment: - - DJANGO_MYSQL_DATABASE=djangoblog - - DJANGO_MYSQL_USER=root - - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E - - DJANGO_MYSQL_HOST=db - - DJANGO_MYSQL_PORT=3306 - - DJANGO_REDIS_URL=redis:6379 - links: - - db - - redis - depends_on: - - db - container_name: djangoblog - nginx: - restart: always - image: nginx:latest - ports: - - "80:80" - - "443:443" - volumes: - - ./bin/nginx.conf:/etc/nginx/nginx.conf - - ./collectedstatic:/code/djangoblog/collectedstatic - links: - - djangoblog:djangoblog - container_name: nginx - - redis: - restart: always - image: redis:latest - container_name: redis - ports: - - "6379:6379" diff --git a/DjangoBlog-master/deploy/entrypoint.sh b/DjangoBlog-master/deploy/entrypoint.sh deleted file mode 100644 index 2fb6491..0000000 --- a/DjangoBlog-master/deploy/entrypoint.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/usr/bin/env bash -NAME="djangoblog" -DJANGODIR=/code/djangoblog -USER=root -GROUP=root -NUM_WORKERS=1 -DJANGO_WSGI_MODULE=djangoblog.wsgi - - -echo "Starting $NAME as `whoami`" - -cd $DJANGODIR - -export PYTHONPATH=$DJANGODIR:$PYTHONPATH - -python manage.py makemigrations && \ - python manage.py migrate && \ - python manage.py collectstatic --noinput && \ - python manage.py compress --force && \ - python manage.py build_index && \ - python manage.py compilemessages || exit 1 - -exec gunicorn ${DJANGO_WSGI_MODULE}:application \ ---name $NAME \ ---workers $NUM_WORKERS \ ---user=$USER --group=$GROUP \ ---bind 0.0.0.0:8000 \ ---log-level=debug \ ---log-file=- \ ---worker-class gevent \ ---threads 4 diff --git a/DjangoBlog-master/deploy/k8s/configmap.yaml b/DjangoBlog-master/deploy/k8s/configmap.yaml deleted file mode 100644 index 835d4ad..0000000 --- a/DjangoBlog-master/deploy/k8s/configmap.yaml +++ /dev/null @@ -1,119 +0,0 @@ -apiVersion: v1 -kind: ConfigMap -metadata: - name: web-nginx-config - namespace: djangoblog -data: - nginx.conf: | - user nginx; - worker_processes auto; - error_log /var/log/nginx/error.log notice; - pid /var/run/nginx.pid; - - events { - worker_connections 1024; - multi_accept on; - use epoll; - } - - http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile on; - keepalive_timeout 65; - gzip on; - gzip_disable "msie6"; - - gzip_vary on; - gzip_proxied any; - gzip_comp_level 8; - gzip_buffers 16 8k; - gzip_http_version 1.1; - gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png; - - # Include server configurations - include /etc/nginx/conf.d/*.conf; - } - djangoblog.conf: | - server { - server_name lylinux.net; - root /code/djangoblog/collectedstatic/; - listen 80; - keepalive_timeout 70; - location /static/ { - expires max; - alias /code/djangoblog/collectedstatic/; - } - - location ~* (robots\.txt|ads\.txt|favicon\.ico|favion\.ico|crossdomain\.xml|google93fd32dbd906620a\.html|BingSiteAuth\.xml|baidu_verify_Ijeny6KrmS\.html)$ { - root /resource/djangopub; - expires 1d; - access_log off; - error_log off; - } - - location / { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header X-NginX-Proxy true; - proxy_redirect off; - if (!-f $request_filename) { - proxy_pass http://djangoblog:8000; - break; - } - } - } - server { - server_name www.lylinux.net; - listen 80; - return 301 https://lylinux.net$request_uri; - } - resource.lylinux.net.conf: | - server { - index index.html index.htm; - server_name resource.lylinux.net; - root /resource/; - - location /djangoblog/ { - alias /code/djangoblog/collectedstatic/; - } - - access_log off; - error_log off; - include lylinux/resource.conf; - } - lylinux.resource.conf: | - expires max; - access_log off; - log_not_found off; - add_header Pragma public; - add_header Cache-Control "public"; - add_header "Access-Control-Allow-Origin" "*"; - ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: djangoblog-env - namespace: djangoblog -data: - DJANGO_MYSQL_DATABASE: djangoblog - DJANGO_MYSQL_USER: db_user - DJANGO_MYSQL_PASSWORD: db_password - DJANGO_MYSQL_HOST: db_host - DJANGO_MYSQL_PORT: db_port - DJANGO_REDIS_URL: "redis:6379" - DJANGO_DEBUG: "False" - MYSQL_ROOT_PASSWORD: db_password - MYSQL_DATABASE: djangoblog - MYSQL_PASSWORD: db_password - DJANGO_SECRET_KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx - diff --git a/DjangoBlog-master/deploy/k8s/deployment.yaml b/DjangoBlog-master/deploy/k8s/deployment.yaml deleted file mode 100644 index 414fdcc..0000000 --- a/DjangoBlog-master/deploy/k8s/deployment.yaml +++ /dev/null @@ -1,274 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: djangoblog - namespace: djangoblog - labels: - app: djangoblog -spec: - replicas: 3 - selector: - matchLabels: - app: djangoblog - template: - metadata: - labels: - app: djangoblog - spec: - containers: - - name: djangoblog - image: liangliangyy/djangoblog:latest - imagePullPolicy: Always - ports: - - containerPort: 8000 - envFrom: - - configMapRef: - name: djangoblog-env - readinessProbe: - httpGet: - path: / - port: 8000 - initialDelaySeconds: 10 - periodSeconds: 30 - livenessProbe: - httpGet: - path: / - port: 8000 - initialDelaySeconds: 10 - periodSeconds: 30 - resources: - requests: - cpu: 10m - memory: 100Mi - limits: - cpu: "2" - memory: 2Gi - volumeMounts: - - name: djangoblog - mountPath: /code/djangoblog/collectedstatic - - name: resource - mountPath: /resource - volumes: - - name: djangoblog - persistentVolumeClaim: - claimName: djangoblog-pvc - - name: resource - persistentVolumeClaim: - claimName: resource-pvc - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: redis - namespace: djangoblog - labels: - app: redis -spec: - replicas: 1 - selector: - matchLabels: - app: redis - template: - metadata: - labels: - app: redis - spec: - containers: - - name: redis - image: redis:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 6379 - resources: - requests: - cpu: 10m - memory: 100Mi - limits: - cpu: 200m - memory: 2Gi - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: db - namespace: djangoblog - labels: - app: db -spec: - replicas: 1 - selector: - matchLabels: - app: db - template: - metadata: - labels: - app: db - spec: - containers: - - name: db - image: mysql:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 3306 - envFrom: - - configMapRef: - name: djangoblog-env - readinessProbe: - exec: - command: - - mysqladmin - - ping - - "-h" - - "127.0.0.1" - - "-u" - - "root" - - "-p$MYSQL_ROOT_PASSWORD" - initialDelaySeconds: 10 - periodSeconds: 10 - livenessProbe: - exec: - command: - - mysqladmin - - ping - - "-h" - - "127.0.0.1" - - "-u" - - "root" - - "-p$MYSQL_ROOT_PASSWORD" - initialDelaySeconds: 10 - periodSeconds: 10 - resources: - requests: - cpu: 10m - memory: 100Mi - limits: - cpu: "2" - memory: 2Gi - volumeMounts: - - name: db-data - mountPath: /var/lib/mysql - volumes: - - name: db-data - persistentVolumeClaim: - claimName: db-pvc - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: nginx - namespace: djangoblog - labels: - app: nginx -spec: - replicas: 1 - selector: - matchLabels: - app: nginx - template: - metadata: - labels: - app: nginx - spec: - containers: - - name: nginx - image: nginx:latest - imagePullPolicy: IfNotPresent - ports: - - containerPort: 80 - resources: - requests: - cpu: 10m - memory: 100Mi - limits: - cpu: "2" - memory: 2Gi - volumeMounts: - - name: nginx-config - mountPath: /etc/nginx/nginx.conf - subPath: nginx.conf - - name: nginx-config - mountPath: /etc/nginx/conf.d/default.conf - subPath: djangoblog.conf - - name: nginx-config - mountPath: /etc/nginx/conf.d/resource.lylinux.net.conf - subPath: resource.lylinux.net.conf - - name: nginx-config - mountPath: /etc/nginx/lylinux/resource.conf - subPath: lylinux.resource.conf - - name: djangoblog-pvc - mountPath: /code/djangoblog/collectedstatic - - name: resource-pvc - mountPath: /resource - volumes: - - name: nginx-config - configMap: - name: web-nginx-config - - name: djangoblog-pvc - persistentVolumeClaim: - claimName: djangoblog-pvc - - name: resource-pvc - persistentVolumeClaim: - claimName: resource-pvc - ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: elasticsearch - namespace: djangoblog - labels: - app: elasticsearch -spec: - replicas: 1 - selector: - matchLabels: - app: elasticsearch - template: - metadata: - labels: - app: elasticsearch - spec: - containers: - - name: elasticsearch - image: liangliangyy/elasticsearch-analysis-ik:8.6.1 - imagePullPolicy: IfNotPresent - env: - - name: discovery.type - value: single-node - - name: ES_JAVA_OPTS - value: "-Xms256m -Xmx256m" - - name: xpack.security.enabled - value: "false" - - name: xpack.monitoring.templates.enabled - value: "false" - ports: - - containerPort: 9200 - resources: - requests: - cpu: 10m - memory: 100Mi - limits: - cpu: "2" - memory: 2Gi - readinessProbe: - httpGet: - path: / - port: 9200 - initialDelaySeconds: 15 - periodSeconds: 30 - livenessProbe: - httpGet: - path: / - port: 9200 - initialDelaySeconds: 15 - periodSeconds: 30 - volumeMounts: - - name: elasticsearch-data - mountPath: /usr/share/elasticsearch/data/ - volumes: - - name: elasticsearch-data - persistentVolumeClaim: - claimName: elasticsearch-pvc diff --git a/DjangoBlog-master/deploy/k8s/gateway.yaml b/DjangoBlog-master/deploy/k8s/gateway.yaml deleted file mode 100644 index a8de073..0000000 --- a/DjangoBlog-master/deploy/k8s/gateway.yaml +++ /dev/null @@ -1,17 +0,0 @@ -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: nginx - namespace: djangoblog -spec: - ingressClassName: nginx - rules: - - http: - paths: - - path: / - pathType: Prefix - backend: - service: - name: nginx - port: - number: 80 \ No newline at end of file diff --git a/DjangoBlog-master/deploy/k8s/pv.yaml b/DjangoBlog-master/deploy/k8s/pv.yaml deleted file mode 100644 index 874b72f..0000000 --- a/DjangoBlog-master/deploy/k8s/pv.yaml +++ /dev/null @@ -1,94 +0,0 @@ -apiVersion: v1 -kind: PersistentVolume -metadata: - name: local-pv-db -spec: - capacity: - storage: 10Gi - volumeMode: Filesystem - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: local-storage - local: - path: /mnt/local-storage-db - nodeAffinity: - required: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/hostname - operator: In - values: - - master ---- -apiVersion: v1 -kind: PersistentVolume -metadata: - name: local-pv-djangoblog -spec: - capacity: - storage: 5Gi - volumeMode: Filesystem - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: local-storage - local: - path: /mnt/local-storage-djangoblog - nodeAffinity: - required: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/hostname - operator: In - values: - - master - - ---- -apiVersion: v1 -kind: PersistentVolume -metadata: - name: local-pv-resource -spec: - capacity: - storage: 5Gi - volumeMode: Filesystem - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: local-storage - local: - path: /mnt/resource/ - nodeAffinity: - required: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/hostname - operator: In - values: - - master - ---- -apiVersion: v1 -kind: PersistentVolume -metadata: - name: local-pv-elasticsearch -spec: - capacity: - storage: 5Gi - volumeMode: Filesystem - accessModes: - - ReadWriteOnce - persistentVolumeReclaimPolicy: Retain - storageClassName: local-storage - local: - path: /mnt/local-storage-elasticsearch - nodeAffinity: - required: - nodeSelectorTerms: - - matchExpressions: - - key: kubernetes.io/hostname - operator: In - values: - - master \ No newline at end of file diff --git a/DjangoBlog-master/deploy/k8s/pvc.yaml b/DjangoBlog-master/deploy/k8s/pvc.yaml deleted file mode 100644 index ef238c5..0000000 --- a/DjangoBlog-master/deploy/k8s/pvc.yaml +++ /dev/null @@ -1,60 +0,0 @@ -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: db-pvc - namespace: djangoblog -spec: - storageClassName: local-storage - volumeName: local-pv-db - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 10Gi - - ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: djangoblog-pvc - namespace: djangoblog -spec: - volumeName: local-pv-djangoblog - storageClassName: local-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi - ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: resource-pvc - namespace: djangoblog -spec: - volumeName: local-pv-resource - storageClassName: local-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi - ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: elasticsearch-pvc - namespace: djangoblog -spec: - volumeName: local-pv-elasticsearch - storageClassName: local-storage - accessModes: - - ReadWriteOnce - resources: - requests: - storage: 5Gi - \ No newline at end of file diff --git a/DjangoBlog-master/deploy/k8s/service.yaml b/DjangoBlog-master/deploy/k8s/service.yaml deleted file mode 100644 index 4ef2931..0000000 --- a/DjangoBlog-master/deploy/k8s/service.yaml +++ /dev/null @@ -1,80 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: djangoblog - namespace: djangoblog - labels: - app: djangoblog -spec: - selector: - app: djangoblog - ports: - - protocol: TCP - port: 8000 - targetPort: 8000 - type: ClusterIP ---- -apiVersion: v1 -kind: Service -metadata: - name: nginx - namespace: djangoblog - labels: - app: nginx -spec: - selector: - app: nginx - ports: - - protocol: TCP - port: 80 - targetPort: 80 - type: ClusterIP ---- -apiVersion: v1 -kind: Service -metadata: - name: redis - namespace: djangoblog - labels: - app: redis -spec: - selector: - app: redis - ports: - - protocol: TCP - port: 6379 - targetPort: 6379 - type: ClusterIP ---- -apiVersion: v1 -kind: Service -metadata: - name: db - namespace: djangoblog - labels: - app: db -spec: - selector: - app: db - ports: - - protocol: TCP - port: 3306 - targetPort: 3306 - type: ClusterIP ---- -apiVersion: v1 -kind: Service -metadata: - name: elasticsearch - namespace: djangoblog - labels: - app: elasticsearch -spec: - selector: - app: elasticsearch - ports: - - protocol: TCP - port: 9200 - targetPort: 9200 - type: ClusterIP - diff --git a/DjangoBlog-master/deploy/k8s/storageclass.yaml b/DjangoBlog-master/deploy/k8s/storageclass.yaml deleted file mode 100644 index 5d5a14c..0000000 --- a/DjangoBlog-master/deploy/k8s/storageclass.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: storage.k8s.io/v1 -kind: StorageClass -metadata: - name: local-storage - annotations: - storageclass.kubernetes.io/is-default-class: "true" -provisioner: kubernetes.io/no-provisioner -volumeBindingMode: Immediate - - diff --git a/DjangoBlog-master/deploy/nginx.conf b/DjangoBlog-master/deploy/nginx.conf deleted file mode 100644 index 32161d8..0000000 --- a/DjangoBlog-master/deploy/nginx.conf +++ /dev/null @@ -1,50 +0,0 @@ -user nginx; -worker_processes auto; - -error_log /var/log/nginx/error.log notice; -pid /var/run/nginx.pid; - - -events { - worker_connections 1024; -} - - -http { - include /etc/nginx/mime.types; - default_type application/octet-stream; - - log_format main '$remote_addr - $remote_user [$time_local] "$request" ' - '$status $body_bytes_sent "$http_referer" ' - '"$http_user_agent" "$http_x_forwarded_for"'; - - access_log /var/log/nginx/access.log main; - - sendfile on; - #tcp_nopush on; - - keepalive_timeout 65; - - #gzip on; - - server { - root /code/djangoblog/collectedstatic/; - listen 80; - keepalive_timeout 70; - location /static/ { - expires max; - alias /code/djangoblog/collectedstatic/; - } - location / { - proxy_set_header X-Real-IP $remote_addr; - proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; - proxy_set_header Host $http_host; - proxy_set_header X-NginX-Proxy true; - proxy_redirect off; - if (!-f $request_filename) { - proxy_pass http://djangoblog:8000; - break; - } - } - } -} diff --git a/DjangoBlog-master/djangoblog/__init__.py b/DjangoBlog-master/djangoblog/__init__.py deleted file mode 100644 index 1e205f4..0000000 --- a/DjangoBlog-master/djangoblog/__init__.py +++ /dev/null @@ -1 +0,0 @@ -default_app_config = 'djangoblog.apps.DjangoblogAppConfig' diff --git a/DjangoBlog-master/djangoblog/admin_site.py b/DjangoBlog-master/djangoblog/admin_site.py deleted file mode 100644 index f120405..0000000 --- a/DjangoBlog-master/djangoblog/admin_site.py +++ /dev/null @@ -1,64 +0,0 @@ -from django.contrib.admin import AdminSite -from django.contrib.admin.models import LogEntry -from django.contrib.sites.admin import SiteAdmin -from django.contrib.sites.models import Site - -from accounts.admin import * -from blog.admin import * -from blog.models import * -from comments.admin import * -from comments.models import * -from djangoblog.logentryadmin import LogEntryAdmin -from oauth.admin import * -from oauth.models import * -from owntracks.admin import * -from owntracks.models import * -from servermanager.admin import * -from servermanager.models import * - - -class DjangoBlogAdminSite(AdminSite): - site_header = 'djangoblog administration' - site_title = 'djangoblog site admin' - - def __init__(self, name='admin'): - super().__init__(name) - - def has_permission(self, request): - return request.user.is_superuser - - # def get_urls(self): - # urls = super().get_urls() - # from django.urls import path - # from blog.views import refresh_memcache - # - # my_urls = [ - # path('refresh/', self.admin_view(refresh_memcache), name="refresh"), - # ] - # return urls + my_urls - - -admin_site = DjangoBlogAdminSite(name='admin') - -admin_site.register(Article, ArticlelAdmin) -admin_site.register(Category, CategoryAdmin) -admin_site.register(Tag, TagAdmin) -admin_site.register(Links, LinksAdmin) -admin_site.register(SideBar, SideBarAdmin) -admin_site.register(BlogSettings, BlogSettingsAdmin) - -admin_site.register(commands, CommandsAdmin) -admin_site.register(EmailSendLog, EmailSendLogAdmin) - -admin_site.register(BlogUser, BlogUserAdmin) - -admin_site.register(Comment, CommentAdmin) - -admin_site.register(OAuthUser, OAuthUserAdmin) -admin_site.register(OAuthConfig, OAuthConfigAdmin) - -admin_site.register(OwnTrackLog, OwnTrackLogsAdmin) - -admin_site.register(Site, SiteAdmin) - -admin_site.register(LogEntry, LogEntryAdmin) diff --git a/DjangoBlog-master/djangoblog/apps.py b/DjangoBlog-master/djangoblog/apps.py deleted file mode 100644 index d29e318..0000000 --- a/DjangoBlog-master/djangoblog/apps.py +++ /dev/null @@ -1,11 +0,0 @@ -from django.apps import AppConfig - -class DjangoblogAppConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'djangoblog' - - def ready(self): - super().ready() - # Import and load plugins here - from .plugin_manage.loader import load_plugins - load_plugins() \ No newline at end of file diff --git a/DjangoBlog-master/djangoblog/blog_signals.py b/DjangoBlog-master/djangoblog/blog_signals.py deleted file mode 100644 index 393f441..0000000 --- a/DjangoBlog-master/djangoblog/blog_signals.py +++ /dev/null @@ -1,122 +0,0 @@ -import _thread -import logging - -import django.dispatch -from django.conf import settings -from django.contrib.admin.models import LogEntry -from django.contrib.auth.signals import user_logged_in, user_logged_out -from django.core.mail import EmailMultiAlternatives -from django.db.models.signals import post_save -from django.dispatch import receiver - -from comments.models import Comment -from comments.utils import send_comment_email -from djangoblog.spider_notify import SpiderNotify -from djangoblog.utils import cache, expire_view_cache, delete_sidebar_cache, delete_view_cache -from djangoblog.utils import get_current_site -from oauth.models import OAuthUser - -logger = logging.getLogger(__name__) - -oauth_user_login_signal = django.dispatch.Signal(['id']) -send_email_signal = django.dispatch.Signal( - ['emailto', 'title', 'content']) - - -@receiver(send_email_signal) -def send_email_signal_handler(sender, **kwargs): - emailto = kwargs['emailto'] - title = kwargs['title'] - content = kwargs['content'] - - msg = EmailMultiAlternatives( - title, - content, - from_email=settings.DEFAULT_FROM_EMAIL, - to=emailto) - msg.content_subtype = "html" - - from servermanager.models import EmailSendLog - log = EmailSendLog() - log.title = title - log.content = content - log.emailto = ','.join(emailto) - - try: - result = msg.send() - log.send_result = result > 0 - except Exception as e: - logger.error(f"失败邮箱号: {emailto}, {e}") - log.send_result = False - log.save() - - -@receiver(oauth_user_login_signal) -def oauth_user_login_signal_handler(sender, **kwargs): - id = kwargs['id'] - oauthuser = OAuthUser.objects.get(id=id) - site = get_current_site().domain - if oauthuser.picture and not oauthuser.picture.find(site) >= 0: - from djangoblog.utils import save_user_avatar - oauthuser.picture = save_user_avatar(oauthuser.picture) - oauthuser.save() - - delete_sidebar_cache() - - -@receiver(post_save) -def model_post_save_callback( - sender, - instance, - created, - raw, - using, - update_fields, - **kwargs): - clearcache = False - if isinstance(instance, LogEntry): - return - if 'get_full_url' in dir(instance): - is_update_views = update_fields == {'views'} - if not settings.TESTING and not is_update_views: - try: - notify_url = instance.get_full_url() - SpiderNotify.baidu_notify([notify_url]) - except Exception as ex: - logger.error("notify sipder", ex) - if not is_update_views: - clearcache = True - - if isinstance(instance, Comment): - if instance.is_enable: - path = instance.article.get_absolute_url() - site = get_current_site().domain - if site.find(':') > 0: - site = site[0:site.find(':')] - - expire_view_cache( - path, - servername=site, - serverport=80, - key_prefix='blogdetail') - if cache.get('seo_processor'): - cache.delete('seo_processor') - comment_cache_key = 'article_comments_{id}'.format( - id=instance.article.id) - cache.delete(comment_cache_key) - delete_sidebar_cache() - delete_view_cache('article_comments', [str(instance.article.pk)]) - - _thread.start_new_thread(send_comment_email, (instance,)) - - if clearcache: - cache.clear() - - -@receiver(user_logged_in) -@receiver(user_logged_out) -def user_auth_callback(sender, request, user, **kwargs): - if user and user.username: - logger.info(user) - delete_sidebar_cache() - # cache.clear() diff --git a/DjangoBlog-master/djangoblog/elasticsearch_backend.py b/DjangoBlog-master/djangoblog/elasticsearch_backend.py deleted file mode 100644 index 4afe498..0000000 --- a/DjangoBlog-master/djangoblog/elasticsearch_backend.py +++ /dev/null @@ -1,183 +0,0 @@ -from django.utils.encoding import force_str -from elasticsearch_dsl import Q -from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, log_query -from haystack.forms import ModelSearchForm -from haystack.models import SearchResult -from haystack.utils import log as logging - -from blog.documents import ArticleDocument, ArticleDocumentManager -from blog.models import Article - -logger = logging.getLogger(__name__) - - -class ElasticSearchBackend(BaseSearchBackend): - def __init__(self, connection_alias, **connection_options): - super( - ElasticSearchBackend, - self).__init__( - connection_alias, - **connection_options) - self.manager = ArticleDocumentManager() - self.include_spelling = True - - def _get_models(self, iterable): - models = iterable if iterable and iterable[0] else Article.objects.all() - docs = self.manager.convert_to_doc(models) - return docs - - def _create(self, models): - self.manager.create_index() - docs = self._get_models(models) - self.manager.rebuild(docs) - - def _delete(self, models): - for m in models: - m.delete() - return True - - def _rebuild(self, models): - models = models if models else Article.objects.all() - docs = self.manager.convert_to_doc(models) - self.manager.update_docs(docs) - - def update(self, index, iterable, commit=True): - - models = self._get_models(iterable) - self.manager.update_docs(models) - - def remove(self, obj_or_string): - models = self._get_models([obj_or_string]) - self._delete(models) - - def clear(self, models=None, commit=True): - self.remove(None) - - @staticmethod - def get_suggestion(query: str) -> str: - """获取推荐词, 如果没有找到添加原搜索词""" - - search = ArticleDocument.search() \ - .query("match", body=query) \ - .suggest('suggest_search', query, term={'field': 'body'}) \ - .execute() - - keywords = [] - for suggest in search.suggest.suggest_search: - if suggest["options"]: - keywords.append(suggest["options"][0]["text"]) - else: - keywords.append(suggest["text"]) - - return ' '.join(keywords) - - @log_query - def search(self, query_string, **kwargs): - logger.info('search query_string:' + query_string) - - start_offset = kwargs.get('start_offset') - end_offset = kwargs.get('end_offset') - - # 推荐词搜索 - if getattr(self, "is_suggest", None): - suggestion = self.get_suggestion(query_string) - else: - suggestion = query_string - - q = Q('bool', - should=[Q('match', body=suggestion), Q('match', title=suggestion)], - minimum_should_match="70%") - - search = ArticleDocument.search() \ - .query('bool', filter=[q]) \ - .filter('term', status='p') \ - .filter('term', type='a') \ - .source(False)[start_offset: end_offset] - - results = search.execute() - hits = results['hits'].total - raw_results = [] - for raw_result in results['hits']['hits']: - app_label = 'blog' - model_name = 'Article' - additional_fields = {} - - result_class = SearchResult - - result = result_class( - app_label, - model_name, - raw_result['_id'], - raw_result['_score'], - **additional_fields) - raw_results.append(result) - facets = {} - spelling_suggestion = None if query_string == suggestion else suggestion - - return { - 'results': raw_results, - 'hits': hits, - 'facets': facets, - 'spelling_suggestion': spelling_suggestion, - } - - -class ElasticSearchQuery(BaseSearchQuery): - def _convert_datetime(self, date): - if hasattr(date, 'hour'): - return force_str(date.strftime('%Y%m%d%H%M%S')) - else: - return force_str(date.strftime('%Y%m%d000000')) - - def clean(self, query_fragment): - """ - Provides a mechanism for sanitizing user input before presenting the - value to the backend. - - Whoosh 1.X differs here in that you can no longer use a backslash - to escape reserved characters. Instead, the whole word should be - quoted. - """ - words = query_fragment.split() - cleaned_words = [] - - for word in words: - if word in self.backend.RESERVED_WORDS: - word = word.replace(word, word.lower()) - - for char in self.backend.RESERVED_CHARACTERS: - if char in word: - word = "'%s'" % word - break - - cleaned_words.append(word) - - return ' '.join(cleaned_words) - - def build_query_fragment(self, field, filter_type, value): - return value.query_string - - def get_count(self): - results = self.get_results() - return len(results) if results else 0 - - def get_spelling_suggestion(self, preferred_query=None): - return self._spelling_suggestion - - def build_params(self, spelling_query=None): - kwargs = super(ElasticSearchQuery, self).build_params(spelling_query=spelling_query) - return kwargs - - -class ElasticSearchModelSearchForm(ModelSearchForm): - - def search(self): - # 是否建议搜索 - self.searchqueryset.query.backend.is_suggest = self.data.get("is_suggest") != "no" - sqs = super().search() - return sqs - - -class ElasticSearchEngine(BaseEngine): - backend = ElasticSearchBackend - query = ElasticSearchQuery diff --git a/DjangoBlog-master/djangoblog/feeds.py b/DjangoBlog-master/djangoblog/feeds.py deleted file mode 100644 index 8c4e851..0000000 --- a/DjangoBlog-master/djangoblog/feeds.py +++ /dev/null @@ -1,40 +0,0 @@ -from django.contrib.auth import get_user_model -from django.contrib.syndication.views import Feed -from django.utils import timezone -from django.utils.feedgenerator import Rss201rev2Feed - -from blog.models import Article -from djangoblog.utils import CommonMarkdown - - -class DjangoBlogFeed(Feed): - feed_type = Rss201rev2Feed - - description = '大巧无工,重剑无锋.' - title = "且听风吟 大巧无工,重剑无锋. " - link = "/feed/" - - def author_name(self): - return get_user_model().objects.first().nickname - - def author_link(self): - return get_user_model().objects.first().get_absolute_url() - - def items(self): - return Article.objects.filter(type='a', status='p').order_by('-pub_time')[:5] - - def item_title(self, item): - return item.title - - def item_description(self, item): - return CommonMarkdown.get_markdown(item.body) - - def feed_copyright(self): - now = timezone.now() - return "Copyright© {year} 且听风吟".format(year=now.year) - - def item_link(self, item): - return item.get_absolute_url() - - def item_guid(self, item): - return diff --git a/DjangoBlog-master/djangoblog/logentryadmin.py b/DjangoBlog-master/djangoblog/logentryadmin.py deleted file mode 100644 index 2f6a535..0000000 --- a/DjangoBlog-master/djangoblog/logentryadmin.py +++ /dev/null @@ -1,91 +0,0 @@ -from django.contrib import admin -from django.contrib.admin.models import DELETION -from django.contrib.contenttypes.models import ContentType -from django.urls import reverse, NoReverseMatch -from django.utils.encoding import force_str -from django.utils.html import escape -from django.utils.safestring import mark_safe -from django.utils.translation import gettext_lazy as _ - - -class LogEntryAdmin(admin.ModelAdmin): - list_filter = [ - 'content_type' - ] - - search_fields = [ - 'object_repr', - 'change_message' - ] - - list_display_links = [ - 'action_time', - 'get_change_message', - ] - list_display = [ - 'action_time', - 'user_link', - 'content_type', - 'object_link', - 'get_change_message', - ] - - def has_add_permission(self, request): - return False - - def has_change_permission(self, request, obj=None): - return ( - request.user.is_superuser or - request.user.has_perm('admin.change_logentry') - ) and request.method != 'POST' - - def has_delete_permission(self, request, obj=None): - return False - - def object_link(self, obj): - object_link = escape(obj.object_repr) - content_type = obj.content_type - - if obj.action_flag != DELETION and content_type is not None: - # try returning an actual link instead of object repr string - try: - url = reverse( - 'admin:{}_{}_change'.format(content_type.app_label, - content_type.model), - args=[obj.object_id] - ) - object_link = '{}'.format(url, object_link) - except NoReverseMatch: - pass - return mark_safe(object_link) - - object_link.admin_order_field = 'object_repr' - object_link.short_description = _('object') - - def user_link(self, obj): - content_type = ContentType.objects.get_for_model(type(obj.user)) - user_link = escape(force_str(obj.user)) - try: - # try returning an actual link instead of object repr string - url = reverse( - 'admin:{}_{}_change'.format(content_type.app_label, - content_type.model), - args=[obj.user.pk] - ) - user_link = '{}'.format(url, user_link) - except NoReverseMatch: - pass - return mark_safe(user_link) - - user_link.admin_order_field = 'user' - user_link.short_description = _('user') - - def get_queryset(self, request): - queryset = super(LogEntryAdmin, self).get_queryset(request) - return queryset.prefetch_related('content_type') - - def get_actions(self, request): - actions = super(LogEntryAdmin, self).get_actions(request) - if 'delete_selected' in actions: - del actions['delete_selected'] - return actions diff --git a/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py b/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py deleted file mode 100644 index 2b4be5c..0000000 --- a/DjangoBlog-master/djangoblog/plugin_manage/base_plugin.py +++ /dev/null @@ -1,41 +0,0 @@ -import logging - -logger = logging.getLogger(__name__) - - -class BasePlugin: - # 插件元数据 - PLUGIN_NAME = None - PLUGIN_DESCRIPTION = None - PLUGIN_VERSION = None - - def __init__(self): - if not all([self.PLUGIN_NAME, self.PLUGIN_DESCRIPTION, self.PLUGIN_VERSION]): - raise ValueError("Plugin metadata (PLUGIN_NAME, PLUGIN_DESCRIPTION, PLUGIN_VERSION) must be defined.") - self.init_plugin() - self.register_hooks() - - def init_plugin(self): - """ - 插件初始化逻辑 - 子类可以重写此方法来实现特定的初始化操作 - """ - logger.info(f'{self.PLUGIN_NAME} initialized.') - - def register_hooks(self): - """ - 注册插件钩子 - 子类可以重写此方法来注册特定的钩子 - """ - pass - - def get_plugin_info(self): - """ - 获取插件信息 - :return: 包含插件元数据的字典 - """ - return { - 'name': self.PLUGIN_NAME, - 'description': self.PLUGIN_DESCRIPTION, - 'version': self.PLUGIN_VERSION - } diff --git a/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py b/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py deleted file mode 100644 index 6685b7c..0000000 --- a/DjangoBlog-master/djangoblog/plugin_manage/hook_constants.py +++ /dev/null @@ -1,7 +0,0 @@ -ARTICLE_DETAIL_LOAD = 'article_detail_load' -ARTICLE_CREATE = 'article_create' -ARTICLE_UPDATE = 'article_update' -ARTICLE_DELETE = 'article_delete' - -ARTICLE_CONTENT_HOOK_NAME = "the_content" - diff --git a/DjangoBlog-master/djangoblog/plugin_manage/hooks.py b/DjangoBlog-master/djangoblog/plugin_manage/hooks.py deleted file mode 100644 index d712540..0000000 --- a/DjangoBlog-master/djangoblog/plugin_manage/hooks.py +++ /dev/null @@ -1,44 +0,0 @@ -import logging - -logger = logging.getLogger(__name__) - -_hooks = {} - - -def register(hook_name: str, callback: callable): - """ - 注册一个钩子回调。 - """ - if hook_name not in _hooks: - _hooks[hook_name] = [] - _hooks[hook_name].append(callback) - logger.debug(f"Registered hook '{hook_name}' with callback '{callback.__name__}'") - - -def run_action(hook_name: str, *args, **kwargs): - """ - 执行一个 Action Hook。 - 它会按顺序执行所有注册到该钩子上的回调函数。 - """ - if hook_name in _hooks: - logger.debug(f"Running action hook '{hook_name}'") - for callback in _hooks[hook_name]: - try: - callback(*args, **kwargs) - except Exception as e: - logger.error(f"Error running action hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True) - - -def apply_filters(hook_name: str, value, *args, **kwargs): - """ - 执行一个 Filter Hook。 - 它会把 value 依次传递给所有注册的回调函数进行处理。 - """ - if hook_name in _hooks: - logger.debug(f"Applying filter hook '{hook_name}'") - for callback in _hooks[hook_name]: - try: - value = callback(value, *args, **kwargs) - except Exception as e: - logger.error(f"Error applying filter hook '{hook_name}' callback '{callback.__name__}': {e}", exc_info=True) - return value diff --git a/DjangoBlog-master/djangoblog/plugin_manage/loader.py b/DjangoBlog-master/djangoblog/plugin_manage/loader.py deleted file mode 100644 index 12e824b..0000000 --- a/DjangoBlog-master/djangoblog/plugin_manage/loader.py +++ /dev/null @@ -1,19 +0,0 @@ -import os -import logging -from django.conf import settings - -logger = logging.getLogger(__name__) - -def load_plugins(): - """ - Dynamically loads and initializes plugins from the 'plugins' directory. - This function is intended to be called when the Django app registry is ready. - """ - for plugin_name in settings.ACTIVE_PLUGINS: - plugin_path = os.path.join(settings.PLUGINS_DIR, plugin_name) - if os.path.isdir(plugin_path) and os.path.exists(os.path.join(plugin_path, 'plugin.py')): - try: - __import__(f'plugins.{plugin_name}.plugin') - logger.info(f"Successfully loaded plugin: {plugin_name}") - except ImportError as e: - logger.error(f"Failed to import plugin: {plugin_name}", exc_info=e) \ No newline at end of file diff --git a/DjangoBlog-master/djangoblog/settings.py b/DjangoBlog-master/djangoblog/settings.py deleted file mode 100644 index d2d7f28..0000000 --- a/DjangoBlog-master/djangoblog/settings.py +++ /dev/null @@ -1,343 +0,0 @@ -""" -Django settings for djangoblog project. - -Generated by 'django-admin startproject' using Django 1.10.2. - -For more information on this file, see -https://docs.djangoproject.com/en/1.10/topics/settings/ - -For the full list of settings and their values, see -https://docs.djangoproject.com/en/1.10/ref/settings/ -""" -import os -import sys -from pathlib import Path - -from django.utils.translation import gettext_lazy as _ - - -def env_to_bool(env, default): - str_val = os.environ.get(env) - return default if str_val is None else str_val == 'True' - - -# Build paths inside the project like this: BASE_DIR / 'subdir'. -BASE_DIR = Path(__file__).resolve().parent.parent - -# Quick-start development settings - unsuitable for production -# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ - -# SECURITY WARNING: keep the secret key used in production secret! -SECRET_KEY = os.environ.get( - 'DJANGO_SECRET_KEY') or 'n9ceqv38)#&mwuat@(mjb_p%em$e8$qyr#fw9ot!=ba6lijx-6' -# SECURITY WARNING: don't run with debug turned on in production! -DEBUG = env_to_bool('DJANGO_DEBUG', True) -# DEBUG = False -TESTING = len(sys.argv) > 1 and sys.argv[1] == 'test' - -# ALLOWED_HOSTS = [] -ALLOWED_HOSTS = ['*', '127.0.0.1', 'example.com'] -# django 4.0新增配置 -CSRF_TRUSTED_ORIGINS = ['http://example.com'] -# Application definition - - -INSTALLED_APPS = [ - # 'django.contrib.admin', - 'django.contrib.admin.apps.SimpleAdminConfig', - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', - 'django.contrib.sites', - 'django.contrib.sitemaps', - 'mdeditor', - 'haystack', - 'blog', - 'accounts', - 'comments', - 'oauth', - 'servermanager', - 'owntracks', - 'compressor', - 'djangoblog' -] - -MIDDLEWARE = [ - - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.locale.LocaleMiddleware', - 'django.middleware.gzip.GZipMiddleware', - # 'django.middleware.cache.UpdateCacheMiddleware', - 'django.middleware.common.CommonMiddleware', - # 'django.middleware.cache.FetchFromCacheMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'django.middleware.http.ConditionalGetMiddleware', - 'blog.middleware.OnlineMiddleware' -] - -ROOT_URLCONF = 'djangoblog.urls' - -TEMPLATES = [ - { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [os.path.join(BASE_DIR, 'templates')], - 'APP_DIRS': True, - 'OPTIONS': { - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'blog.context_processors.seo_processor' - ], - }, - }, -] - -WSGI_APPLICATION = 'djangoblog.wsgi.application' - -# Database -# https://docs.djangoproject.com/en/1.10/ref/settings/#databases - - -DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': os.environ.get('DJANGO_MYSQL_DATABASE') or 'djangoblog', - 'USER': os.environ.get('DJANGO_MYSQL_USER') or 'root', - 'PASSWORD': os.environ.get('DJANGO_MYSQL_PASSWORD') or '050816', - 'HOST': os.environ.get('DJANGO_MYSQL_HOST') or '127.0.0.1', - 'PORT': int( - os.environ.get('DJANGO_MYSQL_PORT') or 3306), - 'OPTIONS': { - 'charset': 'utf8mb4'}, - }} - -# Password validation -# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators - -AUTH_PASSWORD_VALIDATORS = [ - { - 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', - }, - { - 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', - }, -] - -LANGUAGES = ( - ('en', _('English')), - ('zh-hans', _('Simplified Chinese')), - ('zh-hant', _('Traditional Chinese')), -) -LOCALE_PATHS = ( - os.path.join(BASE_DIR, 'locale'), -) - -LANGUAGE_CODE = 'zh-hans' - -TIME_ZONE = 'Asia/Shanghai' - -USE_I18N = True - -USE_L10N = True - -USE_TZ = False - -# Static files (CSS, JavaScript, Images) -# https://docs.djangoproject.com/en/1.10/howto/static-files/ - - -HAYSTACK_CONNECTIONS = { - 'default': { - 'ENGINE': 'djangoblog.whoosh_cn_backend.WhooshEngine', - 'PATH': os.path.join(os.path.dirname(__file__), 'whoosh_index'), - }, -} -# Automatically update searching index -HAYSTACK_SIGNAL_PROCESSOR = 'haystack.signals.RealtimeSignalProcessor' -# Allow user login with username and password -AUTHENTICATION_BACKENDS = [ - 'accounts.user_login_backend.EmailOrUsernameModelBackend'] - -STATIC_ROOT = os.path.join(BASE_DIR, 'collectedstatic') - -STATIC_URL = '/static/' -STATICFILES = os.path.join(BASE_DIR, 'static') - -AUTH_USER_MODEL = 'accounts.BlogUser' -LOGIN_URL = '/login/' - -TIME_FORMAT = '%Y-%m-%d %H:%M:%S' -DATE_TIME_FORMAT = '%Y-%m-%d' - -# bootstrap color styles -BOOTSTRAP_COLOR_TYPES = [ - 'default', 'primary', 'success', 'info', 'warning', 'danger' -] - -# paginate -PAGINATE_BY = 10 -# http cache timeout -CACHE_CONTROL_MAX_AGE = 2592000 -# cache setting -CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - 'TIMEOUT': 10800, - 'LOCATION': 'unique-snowflake', - } -} -# 使用redis作为缓存 -if os.environ.get("DJANGO_REDIS_URL"): - CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.redis.RedisCache', - 'LOCATION': f'redis://{os.environ.get("DJANGO_REDIS_URL")}', - } - } - -SITE_ID = 1 -BAIDU_NOTIFY_URL = os.environ.get('DJANGO_BAIDU_NOTIFY_URL') \ - or 'http://data.zz.baidu.com/urls?site=https://www.lylinux.net&token=1uAOGrMsUm5syDGn' - -# Email: -EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' -EMAIL_USE_TLS = env_to_bool('DJANGO_EMAIL_TLS', False) -EMAIL_USE_SSL = env_to_bool('DJANGO_EMAIL_SSL', True) -EMAIL_HOST = os.environ.get('DJANGO_EMAIL_HOST') or 'smtp.mxhichina.com' -EMAIL_PORT = int(os.environ.get('DJANGO_EMAIL_PORT') or 465) -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 = EMAIL_HOST_USER -# Setting debug=false did NOT handle except email notifications -ADMINS = [('admin', os.environ.get('DJANGO_ADMIN_EMAIL') or 'admin@admin.com')] -# WX ADMIN password(Two times md5) -WXADMIN = os.environ.get( - 'DJANGO_WXADMIN_PASSWORD') or '995F03AC401D6CABABAEF756FC4D43C7' - -LOG_PATH = os.path.join(BASE_DIR, 'logs') -if not os.path.exists(LOG_PATH): - os.makedirs(LOG_PATH, exist_ok=True) - -LOGGING = { - 'version': 1, - 'disable_existing_loggers': False, - 'root': { - 'level': 'INFO', - 'handlers': ['console', 'log_file'], - }, - 'formatters': { - 'verbose': { - 'format': '[%(asctime)s] %(levelname)s [%(name)s.%(funcName)s:%(lineno)d %(module)s] %(message)s', - } - }, - 'filters': { - 'require_debug_false': { - '()': 'django.utils.log.RequireDebugFalse', - }, - 'require_debug_true': { - '()': 'django.utils.log.RequireDebugTrue', - }, - }, - 'handlers': { - 'log_file': { - 'level': 'INFO', - 'class': 'logging.handlers.TimedRotatingFileHandler', - 'filename': os.path.join(LOG_PATH, 'djangoblog.log'), - 'when': 'D', - 'formatter': 'verbose', - 'interval': 1, - 'delay': True, - 'backupCount': 5, - 'encoding': 'utf-8' - }, - 'console': { - 'level': 'DEBUG', - 'filters': ['require_debug_true'], - 'class': 'logging.StreamHandler', - 'formatter': 'verbose' - }, - 'null': { - 'class': 'logging.NullHandler', - }, - 'mail_admins': { - 'level': 'ERROR', - 'filters': ['require_debug_false'], - 'class': 'django.utils.log.AdminEmailHandler' - } - }, - 'loggers': { - 'djangoblog': { - 'handlers': ['log_file', 'console'], - 'level': 'INFO', - 'propagate': True, - }, - 'django.request': { - 'handlers': ['mail_admins'], - 'level': 'ERROR', - 'propagate': False, - } - } -} - -STATICFILES_FINDERS = ( - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - # other - 'compressor.finders.CompressorFinder', -) -COMPRESS_ENABLED = True -# COMPRESS_OFFLINE = True - - -COMPRESS_CSS_FILTERS = [ - # creates absolute urls from relative ones - 'compressor.filters.css_default.CssAbsoluteFilter', - # css minimizer - 'compressor.filters.cssmin.CSSMinFilter' -] -COMPRESS_JS_FILTERS = [ - 'compressor.filters.jsmin.JSMinFilter' -] - -MEDIA_ROOT = os.path.join(BASE_DIR, 'uploads') -MEDIA_URL = '/media/' -X_FRAME_OPTIONS = 'SAMEORIGIN' - -DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' - -if os.environ.get('DJANGO_ELASTICSEARCH_HOST'): - ELASTICSEARCH_DSL = { - 'default': { - 'hosts': os.environ.get('DJANGO_ELASTICSEARCH_HOST') - }, - } - HAYSTACK_CONNECTIONS = { - 'default': { - 'ENGINE': 'djangoblog.elasticsearch_backend.ElasticSearchEngine', - }, - } - -# Plugin System -PLUGINS_DIR = BASE_DIR / 'plugins' -ACTIVE_PLUGINS = [ - 'article_copyright', - 'reading_time', - 'external_links', - 'view_count', - 'seo_optimizer' -] \ No newline at end of file diff --git a/DjangoBlog-master/djangoblog/sitemap.py b/DjangoBlog-master/djangoblog/sitemap.py deleted file mode 100644 index 8b7d446..0000000 --- a/DjangoBlog-master/djangoblog/sitemap.py +++ /dev/null @@ -1,59 +0,0 @@ -from django.contrib.sitemaps import Sitemap -from django.urls import reverse - -from blog.models import Article, Category, Tag - - -class StaticViewSitemap(Sitemap): - priority = 0.5 - changefreq = 'daily' - - def items(self): - return ['blog:index', ] - - def location(self, item): - return reverse(item) - - -class ArticleSiteMap(Sitemap): - changefreq = "monthly" - priority = "0.6" - - def items(self): - return Article.objects.filter(status='p') - - def lastmod(self, obj): - return obj.last_modify_time - - -class CategorySiteMap(Sitemap): - changefreq = "Weekly" - priority = "0.6" - - def items(self): - return Category.objects.all() - - def lastmod(self, obj): - return obj.last_modify_time - - -class TagSiteMap(Sitemap): - changefreq = "Weekly" - priority = "0.3" - - def items(self): - return Tag.objects.all() - - def lastmod(self, obj): - return obj.last_modify_time - - -class UserSiteMap(Sitemap): - changefreq = "Weekly" - priority = "0.3" - - def items(self): - return list(set(map(lambda x: x.author, Article.objects.all()))) - - def lastmod(self, obj): - return obj.date_joined diff --git a/DjangoBlog-master/djangoblog/spider_notify.py b/DjangoBlog-master/djangoblog/spider_notify.py deleted file mode 100644 index 7b909e9..0000000 --- a/DjangoBlog-master/djangoblog/spider_notify.py +++ /dev/null @@ -1,21 +0,0 @@ -import logging - -import requests -from django.conf import settings - -logger = logging.getLogger(__name__) - - -class SpiderNotify(): - @staticmethod - def baidu_notify(urls): - try: - data = '\n'.join(urls) - result = requests.post(settings.BAIDU_NOTIFY_URL, data=data) - logger.info(result.text) - except Exception as e: - logger.error(e) - - @staticmethod - def notify(url): - SpiderNotify.baidu_notify(url) diff --git a/DjangoBlog-master/djangoblog/tests.py b/DjangoBlog-master/djangoblog/tests.py deleted file mode 100644 index 01237d9..0000000 --- a/DjangoBlog-master/djangoblog/tests.py +++ /dev/null @@ -1,32 +0,0 @@ -from django.test import TestCase - -from djangoblog.utils import * - - -class DjangoBlogTest(TestCase): - def setUp(self): - pass - - def test_utils(self): - md5 = get_sha256('test') - self.assertIsNotNone(md5) - c = CommonMarkdown.get_markdown(''' - # Title1 - - ```python - import os - ``` - - [url](https://www.lylinux.net/) - - [ddd](http://www.baidu.com) - - - ''') - self.assertIsNotNone(c) - d = { - 'd': 'key1', - 'd2': 'key2' - } - data = parse_dict_to_url(d) - self.assertIsNotNone(data) diff --git a/DjangoBlog-master/djangoblog/urls.py b/DjangoBlog-master/djangoblog/urls.py deleted file mode 100644 index 4aae58a..0000000 --- a/DjangoBlog-master/djangoblog/urls.py +++ /dev/null @@ -1,64 +0,0 @@ -"""djangoblog URL Configuration - -The `urlpatterns` list routes URLs to views. For more information please see: - https://docs.djangoproject.com/en/1.10/topics/http/urls/ -Examples: -Function views - 1. Add an import: from my_app import views - 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') -Class-based views - 1. Add an import: from other_app.views import Home - 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') -Including another URLconf - 1. Import the include() function: from django.conf.urls import url, include - 2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) -""" -from django.conf import settings -from django.conf.urls.i18n import i18n_patterns -from django.conf.urls.static import static -from django.contrib.sitemaps.views import sitemap -from django.urls import path, include -from django.urls import re_path -from haystack.views import search_view_factory - -from blog.views import EsSearchView -from djangoblog.admin_site import admin_site -from djangoblog.elasticsearch_backend import ElasticSearchModelSearchForm -from djangoblog.feeds import DjangoBlogFeed -from djangoblog.sitemap import ArticleSiteMap, CategorySiteMap, StaticViewSitemap, TagSiteMap, UserSiteMap - -sitemaps = { - - 'blog': ArticleSiteMap, - 'Category': CategorySiteMap, - 'Tag': TagSiteMap, - 'User': UserSiteMap, - 'static': StaticViewSitemap -} - -handler404 = 'blog.views.page_not_found_view' -handler500 = 'blog.views.server_error_view' -handle403 = 'blog.views.permission_denied_view' - -urlpatterns = [ - path('i18n/', include('django.conf.urls.i18n')), -] -urlpatterns += i18n_patterns( - re_path(r'^admin/', admin_site.urls), - re_path(r'', include('blog.urls', namespace='blog')), - re_path(r'mdeditor/', include('mdeditor.urls')), - re_path(r'', include('comments.urls', namespace='comment')), - re_path(r'', include('accounts.urls', namespace='account')), - re_path(r'', include('oauth.urls', namespace='oauth')), - re_path(r'^sitemap\.xml$', sitemap, {'sitemaps': sitemaps}, - name='django.contrib.sitemaps.views.sitemap'), - re_path(r'^feed/$', DjangoBlogFeed()), - re_path(r'^rss/$', DjangoBlogFeed()), - re_path('^search', search_view_factory(view_class=EsSearchView, form_class=ElasticSearchModelSearchForm), - name='search'), - re_path(r'', include('servermanager.urls', namespace='servermanager')), - re_path(r'', include('owntracks.urls', namespace='owntracks')) - , prefix_default_language=False) + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) -if settings.DEBUG: - urlpatterns += static(settings.MEDIA_URL, - document_root=settings.MEDIA_ROOT) diff --git a/DjangoBlog-master/djangoblog/utils.py b/DjangoBlog-master/djangoblog/utils.py deleted file mode 100644 index 57f63dc..0000000 --- a/DjangoBlog-master/djangoblog/utils.py +++ /dev/null @@ -1,232 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 - - -import logging -import os -import random -import string -import uuid -from hashlib import sha256 - -import bleach -import markdown -import requests -from django.conf import settings -from django.contrib.sites.models import Site -from django.core.cache import cache -from django.templatetags.static import static - -logger = logging.getLogger(__name__) - - -def get_max_articleid_commentid(): - from blog.models import Article - from comments.models import Comment - return (Article.objects.latest().pk, Comment.objects.latest().pk) - - -def get_sha256(str): - m = sha256(str.encode('utf-8')) - return m.hexdigest() - - -def cache_decorator(expiration=3 * 60): - def wrapper(func): - def news(*args, **kwargs): - try: - view = args[0] - key = view.get_cache_key() - except: - key = None - if not key: - unique_str = repr((func, args, kwargs)) - - m = sha256(unique_str.encode('utf-8')) - key = m.hexdigest() - value = cache.get(key) - if value is not None: - # logger.info('cache_decorator get cache:%s key:%s' % (func.__name__, key)) - if str(value) == '__default_cache_value__': - return None - else: - return value - else: - logger.debug( - 'cache_decorator set cache:%s key:%s' % - (func.__name__, key)) - value = func(*args, **kwargs) - if value is None: - cache.set(key, '__default_cache_value__', expiration) - else: - cache.set(key, value, expiration) - return value - - return news - - return wrapper - - -def expire_view_cache(path, servername, serverport, key_prefix=None): - ''' - 刷新视图缓存 - :param path:url路径 - :param servername:host - :param serverport:端口 - :param key_prefix:前缀 - :return:是否成功 - ''' - from django.http import HttpRequest - from django.utils.cache import get_cache_key - - request = HttpRequest() - request.META = {'SERVER_NAME': servername, 'SERVER_PORT': serverport} - request.path = path - - key = get_cache_key(request, key_prefix=key_prefix, cache=cache) - if key: - logger.info('expire_view_cache:get key:{path}'.format(path=path)) - if cache.get(key): - cache.delete(key) - return True - return False - - -@cache_decorator() -def get_current_site(): - site = Site.objects.get_current() - return site - - -class CommonMarkdown: - @staticmethod - def _convert_markdown(value): - md = markdown.Markdown( - extensions=[ - 'extra', - 'codehilite', - 'toc', - 'tables', - ] - ) - body = md.convert(value) - toc = md.toc - return body, toc - - @staticmethod - def get_markdown_with_toc(value): - body, toc = CommonMarkdown._convert_markdown(value) - return body, toc - - @staticmethod - def get_markdown(value): - body, toc = CommonMarkdown._convert_markdown(value) - return body - - -def send_email(emailto, title, content): - from djangoblog.blog_signals import send_email_signal - send_email_signal.send( - send_email.__class__, - emailto=emailto, - title=title, - content=content) - - -def generate_code() -> str: - """生成随机数验证码""" - return ''.join(random.sample(string.digits, 6)) - - -def parse_dict_to_url(dict): - from urllib.parse import quote - url = '&'.join(['{}={}'.format(quote(k, safe='/'), quote(v, safe='/')) - for k, v in dict.items()]) - return url - - -def get_blog_setting(): - value = cache.get('get_blog_setting') - if value: - return value - else: - from blog.models import BlogSettings - if not BlogSettings.objects.count(): - setting = BlogSettings() - setting.site_name = 'djangoblog' - setting.site_description = '基于Django的博客系统' - setting.site_seo_description = '基于Django的博客系统' - setting.site_keywords = 'Django,Python' - setting.article_sub_length = 300 - setting.sidebar_article_count = 10 - setting.sidebar_comment_count = 5 - setting.show_google_adsense = False - setting.open_site_comment = True - setting.analytics_code = '' - setting.beian_code = '' - setting.show_gongan_code = False - setting.comment_need_review = False - setting.save() - value = BlogSettings.objects.first() - logger.info('set cache get_blog_setting') - cache.set('get_blog_setting', value) - return value - - -def save_user_avatar(url): - ''' - 保存用户头像 - :param url:头像url - :return: 本地路径 - ''' - logger.info(url) - - try: - basedir = os.path.join(settings.STATICFILES, 'avatar') - rsp = requests.get(url, timeout=2) - if rsp.status_code == 200: - if not os.path.exists(basedir): - os.makedirs(basedir) - - image_extensions = ['.jpg', '.png', 'jpeg', '.gif'] - isimage = len([i for i in image_extensions if url.endswith(i)]) > 0 - ext = os.path.splitext(url)[1] if isimage else '.jpg' - save_filename = str(uuid.uuid4().hex) + ext - logger.info('保存用户头像:' + basedir + save_filename) - with open(os.path.join(basedir, save_filename), 'wb+') as file: - file.write(rsp.content) - return static('avatar/' + save_filename) - except Exception as e: - logger.error(e) - return static('blog/img/avatar.png') - - -def delete_sidebar_cache(): - from blog.models import LinkShowType - keys = ["sidebar" + x for x in LinkShowType.values] - for k in keys: - logger.info('delete sidebar key:' + k) - cache.delete(k) - - -def delete_view_cache(prefix, keys): - from django.core.cache.utils import make_template_fragment_key - key = make_template_fragment_key(prefix, keys) - cache.delete(key) - - -def get_resource_url(): - if settings.STATIC_URL: - return settings.STATIC_URL - else: - site = get_current_site() - return 'http://' + site.domain + '/static/' - - -ALLOWED_TAGS = ['a', 'abbr', 'acronym', 'b', 'blockquote', 'code', 'em', 'i', 'li', 'ol', 'pre', 'strong', 'ul', 'h1', - 'h2', 'p'] -ALLOWED_ATTRIBUTES = {'a': ['href', 'title'], 'abbr': ['title'], 'acronym': ['title']} - - -def sanitize_html(html): - return bleach.clean(html, tags=ALLOWED_TAGS, attributes=ALLOWED_ATTRIBUTES) diff --git a/DjangoBlog-master/djangoblog/whoosh_cn_backend.py b/DjangoBlog-master/djangoblog/whoosh_cn_backend.py deleted file mode 100644 index 04e3f7f..0000000 --- a/DjangoBlog-master/djangoblog/whoosh_cn_backend.py +++ /dev/null @@ -1,1044 +0,0 @@ -# encoding: utf-8 - -from __future__ import absolute_import, division, print_function, unicode_literals - -import json -import os -import re -import shutil -import threading -import warnings - -import six -from django.conf import settings -from django.core.exceptions import ImproperlyConfigured -from datetime import datetime -from django.utils.encoding import force_str -from haystack.backends import BaseEngine, BaseSearchBackend, BaseSearchQuery, EmptyResults, log_query -from haystack.constants import DJANGO_CT, DJANGO_ID, ID -from haystack.exceptions import MissingDependency, SearchBackendError, SkipDocument -from haystack.inputs import Clean, Exact, PythonData, Raw -from haystack.models import SearchResult -from haystack.utils import get_identifier, get_model_ct -from haystack.utils import log as logging -from haystack.utils.app_loading import haystack_get_model -from jieba.analyse import ChineseAnalyzer -from whoosh import index -from whoosh.analysis import StemmingAnalyzer -from whoosh.fields import BOOLEAN, DATETIME, IDLIST, KEYWORD, NGRAM, NGRAMWORDS, NUMERIC, Schema, TEXT -from whoosh.fields import ID as WHOOSH_ID -from whoosh.filedb.filestore import FileStorage, RamStorage -from whoosh.highlight import ContextFragmenter, HtmlFormatter -from whoosh.highlight import highlight as whoosh_highlight -from whoosh.qparser import QueryParser -from whoosh.searching import ResultsPage -from whoosh.writing import AsyncWriter - -try: - import whoosh -except ImportError: - raise MissingDependency( - "The 'whoosh' backend requires the installation of 'Whoosh'. Please refer to the documentation.") - -# Handle minimum requirement. -if not hasattr(whoosh, '__version__') or whoosh.__version__ < (2, 5, 0): - raise MissingDependency( - "The 'whoosh' backend requires version 2.5.0 or greater.") - -# Bubble up the correct error. - -DATETIME_REGEX = re.compile( - '^(?P\d{4})-(?P\d{2})-(?P\d{2})T(?P\d{2}):(?P\d{2}):(?P\d{2})(\.\d{3,6}Z?)?$') -LOCALS = threading.local() -LOCALS.RAM_STORE = None - - -class WhooshHtmlFormatter(HtmlFormatter): - """ - This is a HtmlFormatter simpler than the whoosh.HtmlFormatter. - We use it to have consistent results across backends. Specifically, - Solr, Xapian and Elasticsearch are using this formatting. - """ - template = '<%(tag)s>%(t)s' - - -class WhooshSearchBackend(BaseSearchBackend): - # Word reserved by Whoosh for special use. - RESERVED_WORDS = ( - 'AND', - 'NOT', - 'OR', - 'TO', - ) - - # Characters reserved by Whoosh for special use. - # The '\\' must come first, so as not to overwrite the other slash - # replacements. - RESERVED_CHARACTERS = ( - '\\', '+', '-', '&&', '||', '!', '(', ')', '{', '}', - '[', ']', '^', '"', '~', '*', '?', ':', '.', - ) - - def __init__(self, connection_alias, **connection_options): - super( - WhooshSearchBackend, - self).__init__( - connection_alias, - **connection_options) - self.setup_complete = False - self.use_file_storage = True - self.post_limit = getattr( - connection_options, - 'POST_LIMIT', - 128 * 1024 * 1024) - self.path = connection_options.get('PATH') - - if connection_options.get('STORAGE', 'file') != 'file': - self.use_file_storage = False - - if self.use_file_storage and not self.path: - raise ImproperlyConfigured( - "You must specify a 'PATH' in your settings for connection '%s'." % - connection_alias) - - self.log = logging.getLogger('haystack') - - def setup(self): - """ - Defers loading until needed. - """ - from haystack import connections - new_index = False - - # Make sure the index is there. - if self.use_file_storage and not os.path.exists(self.path): - os.makedirs(self.path) - new_index = True - - if self.use_file_storage and not os.access(self.path, os.W_OK): - raise IOError( - "The path to your Whoosh index '%s' is not writable for the current user/group." % - self.path) - - if self.use_file_storage: - self.storage = FileStorage(self.path) - else: - global LOCALS - - if getattr(LOCALS, 'RAM_STORE', None) is None: - LOCALS.RAM_STORE = RamStorage() - - self.storage = LOCALS.RAM_STORE - - self.content_field_name, self.schema = self.build_schema( - connections[self.connection_alias].get_unified_index().all_searchfields()) - self.parser = QueryParser(self.content_field_name, schema=self.schema) - - if new_index is True: - self.index = self.storage.create_index(self.schema) - else: - try: - self.index = self.storage.open_index(schema=self.schema) - except index.EmptyIndexError: - self.index = self.storage.create_index(self.schema) - - self.setup_complete = True - - def build_schema(self, fields): - schema_fields = { - ID: WHOOSH_ID(stored=True, unique=True), - DJANGO_CT: WHOOSH_ID(stored=True), - DJANGO_ID: WHOOSH_ID(stored=True), - } - # Grab the number of keys that are hard-coded into Haystack. - # We'll use this to (possibly) fail slightly more gracefully later. - initial_key_count = len(schema_fields) - content_field_name = '' - - for field_name, field_class in fields.items(): - if field_class.is_multivalued: - if field_class.indexed is False: - schema_fields[field_class.index_fieldname] = IDLIST( - stored=True, field_boost=field_class.boost) - else: - schema_fields[field_class.index_fieldname] = KEYWORD( - stored=True, commas=True, scorable=True, field_boost=field_class.boost) - elif field_class.field_type in ['date', 'datetime']: - schema_fields[field_class.index_fieldname] = DATETIME( - stored=field_class.stored, sortable=True) - elif field_class.field_type == 'integer': - schema_fields[field_class.index_fieldname] = NUMERIC( - stored=field_class.stored, numtype=int, field_boost=field_class.boost) - elif field_class.field_type == 'float': - schema_fields[field_class.index_fieldname] = NUMERIC( - stored=field_class.stored, numtype=float, field_boost=field_class.boost) - elif field_class.field_type == 'boolean': - # Field boost isn't supported on BOOLEAN as of 1.8.2. - schema_fields[field_class.index_fieldname] = BOOLEAN( - stored=field_class.stored) - elif field_class.field_type == 'ngram': - schema_fields[field_class.index_fieldname] = NGRAM( - minsize=3, maxsize=15, stored=field_class.stored, field_boost=field_class.boost) - elif field_class.field_type == 'edge_ngram': - schema_fields[field_class.index_fieldname] = NGRAMWORDS(minsize=2, maxsize=15, at='start', - stored=field_class.stored, - field_boost=field_class.boost) - else: - # schema_fields[field_class.index_fieldname] = TEXT(stored=True, analyzer=StemmingAnalyzer(), field_boost=field_class.boost, sortable=True) - schema_fields[field_class.index_fieldname] = TEXT( - stored=True, analyzer=ChineseAnalyzer(), field_boost=field_class.boost, sortable=True) - if field_class.document is True: - content_field_name = field_class.index_fieldname - schema_fields[field_class.index_fieldname].spelling = True - - # Fail more gracefully than relying on the backend to die if no fields - # are found. - if len(schema_fields) <= initial_key_count: - raise SearchBackendError( - "No fields were found in any search_indexes. Please correct this before attempting to search.") - - return (content_field_name, Schema(**schema_fields)) - - def update(self, index, iterable, commit=True): - if not self.setup_complete: - self.setup() - - self.index = self.index.refresh() - writer = AsyncWriter(self.index) - - for obj in iterable: - try: - doc = index.full_prepare(obj) - except SkipDocument: - self.log.debug(u"Indexing for object `%s` skipped", obj) - else: - # Really make sure it's unicode, because Whoosh won't have it any - # other way. - for key in doc: - doc[key] = self._from_python(doc[key]) - - # Document boosts aren't supported in Whoosh 2.5.0+. - if 'boost' in doc: - del doc['boost'] - - try: - writer.update_document(**doc) - except Exception as e: - if not self.silently_fail: - raise - - # We'll log the object identifier but won't include the actual object - # to avoid the possibility of that generating encoding errors while - # processing the log message: - self.log.error( - u"%s while preparing object for update" % - e.__class__.__name__, - exc_info=True, - extra={ - "data": { - "index": index, - "object": get_identifier(obj)}}) - - if len(iterable) > 0: - # For now, commit no matter what, as we run into locking issues - # otherwise. - writer.commit() - - def remove(self, obj_or_string, commit=True): - if not self.setup_complete: - self.setup() - - self.index = self.index.refresh() - whoosh_id = get_identifier(obj_or_string) - - try: - self.index.delete_by_query( - q=self.parser.parse( - u'%s:"%s"' % - (ID, whoosh_id))) - except Exception as e: - if not self.silently_fail: - raise - - self.log.error( - "Failed to remove document '%s' from Whoosh: %s", - whoosh_id, - e, - exc_info=True) - - def clear(self, models=None, commit=True): - if not self.setup_complete: - self.setup() - - self.index = self.index.refresh() - - if models is not None: - assert isinstance(models, (list, tuple)) - - try: - if models is None: - self.delete_index() - else: - models_to_delete = [] - - for model in models: - models_to_delete.append( - u"%s:%s" % - (DJANGO_CT, get_model_ct(model))) - - self.index.delete_by_query( - q=self.parser.parse( - u" OR ".join(models_to_delete))) - except Exception as e: - if not self.silently_fail: - raise - - if models is not None: - self.log.error( - "Failed to clear Whoosh index of models '%s': %s", - ','.join(models_to_delete), - e, - exc_info=True) - else: - self.log.error( - "Failed to clear Whoosh index: %s", e, exc_info=True) - - def delete_index(self): - # Per the Whoosh mailing list, if wiping out everything from the index, - # it's much more efficient to simply delete the index files. - if self.use_file_storage and os.path.exists(self.path): - shutil.rmtree(self.path) - elif not self.use_file_storage: - self.storage.clean() - - # Recreate everything. - self.setup() - - def optimize(self): - if not self.setup_complete: - self.setup() - - self.index = self.index.refresh() - self.index.optimize() - - def calculate_page(self, start_offset=0, end_offset=None): - # Prevent against Whoosh throwing an error. Requires an end_offset - # greater than 0. - if end_offset is not None and end_offset <= 0: - end_offset = 1 - - # Determine the page. - page_num = 0 - - if end_offset is None: - end_offset = 1000000 - - if start_offset is None: - start_offset = 0 - - page_length = end_offset - start_offset - - if page_length and page_length > 0: - page_num = int(start_offset / page_length) - - # Increment because Whoosh uses 1-based page numbers. - page_num += 1 - return page_num, page_length - - @log_query - def search( - self, - query_string, - sort_by=None, - start_offset=0, - end_offset=None, - fields='', - highlight=False, - facets=None, - date_facets=None, - query_facets=None, - narrow_queries=None, - spelling_query=None, - within=None, - dwithin=None, - distance_point=None, - models=None, - limit_to_registered_models=None, - result_class=None, - **kwargs): - if not self.setup_complete: - self.setup() - - # A zero length query should return no results. - if len(query_string) == 0: - return { - 'results': [], - 'hits': 0, - } - - query_string = force_str(query_string) - - # A one-character query (non-wildcard) gets nabbed by a stopwords - # filter and should yield zero results. - if len(query_string) <= 1 and query_string != u'*': - return { - 'results': [], - 'hits': 0, - } - - reverse = False - - if sort_by is not None: - # Determine if we need to reverse the results and if Whoosh can - # handle what it's being asked to sort by. Reversing is an - # all-or-nothing action, unfortunately. - sort_by_list = [] - reverse_counter = 0 - - for order_by in sort_by: - if order_by.startswith('-'): - reverse_counter += 1 - - if reverse_counter and reverse_counter != len(sort_by): - raise SearchBackendError("Whoosh requires all order_by fields" - " to use the same sort direction") - - for order_by in sort_by: - if order_by.startswith('-'): - sort_by_list.append(order_by[1:]) - - if len(sort_by_list) == 1: - reverse = True - else: - sort_by_list.append(order_by) - - if len(sort_by_list) == 1: - reverse = False - - sort_by = sort_by_list[0] - - if facets is not None: - warnings.warn( - "Whoosh does not handle faceting.", - Warning, - stacklevel=2) - - if date_facets is not None: - warnings.warn( - "Whoosh does not handle date faceting.", - Warning, - stacklevel=2) - - if query_facets is not None: - warnings.warn( - "Whoosh does not handle query faceting.", - Warning, - stacklevel=2) - - narrowed_results = None - self.index = self.index.refresh() - - if limit_to_registered_models is None: - limit_to_registered_models = getattr( - settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True) - - if models and len(models): - model_choices = sorted(get_model_ct(model) for model in models) - elif limit_to_registered_models: - # Using narrow queries, limit the results to only models handled - # with the current routers. - model_choices = self.build_models_list() - else: - model_choices = [] - - if len(model_choices) > 0: - if narrow_queries is None: - narrow_queries = set() - - narrow_queries.add(' OR '.join( - ['%s:%s' % (DJANGO_CT, rm) for rm in model_choices])) - - narrow_searcher = None - - if narrow_queries is not None: - # Potentially expensive? I don't see another way to do it in - # Whoosh... - narrow_searcher = self.index.searcher() - - for nq in narrow_queries: - recent_narrowed_results = narrow_searcher.search( - self.parser.parse(force_str(nq)), limit=None) - - if len(recent_narrowed_results) <= 0: - return { - 'results': [], - 'hits': 0, - } - - if narrowed_results: - narrowed_results.filter(recent_narrowed_results) - else: - narrowed_results = recent_narrowed_results - - self.index = self.index.refresh() - - if self.index.doc_count(): - searcher = self.index.searcher() - parsed_query = self.parser.parse(query_string) - - # In the event of an invalid/stopworded query, recover gracefully. - if parsed_query is None: - return { - 'results': [], - 'hits': 0, - } - - page_num, page_length = self.calculate_page( - start_offset, end_offset) - - search_kwargs = { - 'pagelen': page_length, - 'sortedby': sort_by, - 'reverse': reverse, - } - - # Handle the case where the results have been narrowed. - if narrowed_results is not None: - search_kwargs['filter'] = narrowed_results - - try: - raw_page = searcher.search_page( - parsed_query, - page_num, - **search_kwargs - ) - except ValueError: - if not self.silently_fail: - raise - - return { - 'results': [], - 'hits': 0, - 'spelling_suggestion': None, - } - - # Because as of Whoosh 2.5.1, it will return the wrong page of - # results if you request something too high. :( - if raw_page.pagenum < page_num: - return { - 'results': [], - 'hits': 0, - 'spelling_suggestion': None, - } - - results = self._process_results( - raw_page, - highlight=highlight, - query_string=query_string, - spelling_query=spelling_query, - result_class=result_class) - searcher.close() - - if hasattr(narrow_searcher, 'close'): - narrow_searcher.close() - - return results - else: - if self.include_spelling: - if spelling_query: - spelling_suggestion = self.create_spelling_suggestion( - spelling_query) - else: - spelling_suggestion = self.create_spelling_suggestion( - query_string) - else: - spelling_suggestion = None - - return { - 'results': [], - 'hits': 0, - 'spelling_suggestion': spelling_suggestion, - } - - def more_like_this( - self, - model_instance, - additional_query_string=None, - start_offset=0, - end_offset=None, - models=None, - limit_to_registered_models=None, - result_class=None, - **kwargs): - if not self.setup_complete: - self.setup() - - # Deferred models will have a different class ("RealClass_Deferred_fieldname") - # which won't be in our registry: - model_klass = model_instance._meta.concrete_model - - field_name = self.content_field_name - narrow_queries = set() - narrowed_results = None - self.index = self.index.refresh() - - if limit_to_registered_models is None: - limit_to_registered_models = getattr( - settings, 'HAYSTACK_LIMIT_TO_REGISTERED_MODELS', True) - - if models and len(models): - model_choices = sorted(get_model_ct(model) for model in models) - elif limit_to_registered_models: - # Using narrow queries, limit the results to only models handled - # with the current routers. - model_choices = self.build_models_list() - else: - model_choices = [] - - if len(model_choices) > 0: - if narrow_queries is None: - narrow_queries = set() - - narrow_queries.add(' OR '.join( - ['%s:%s' % (DJANGO_CT, rm) for rm in model_choices])) - - if additional_query_string and additional_query_string != '*': - narrow_queries.add(additional_query_string) - - narrow_searcher = None - - if narrow_queries is not None: - # Potentially expensive? I don't see another way to do it in - # Whoosh... - narrow_searcher = self.index.searcher() - - for nq in narrow_queries: - recent_narrowed_results = narrow_searcher.search( - self.parser.parse(force_str(nq)), limit=None) - - if len(recent_narrowed_results) <= 0: - return { - 'results': [], - 'hits': 0, - } - - if narrowed_results: - narrowed_results.filter(recent_narrowed_results) - else: - narrowed_results = recent_narrowed_results - - page_num, page_length = self.calculate_page(start_offset, end_offset) - - self.index = self.index.refresh() - raw_results = EmptyResults() - - if self.index.doc_count(): - query = "%s:%s" % (ID, get_identifier(model_instance)) - searcher = self.index.searcher() - parsed_query = self.parser.parse(query) - results = searcher.search(parsed_query) - - if len(results): - raw_results = results[0].more_like_this( - field_name, top=end_offset) - - # Handle the case where the results have been narrowed. - if narrowed_results is not None and hasattr(raw_results, 'filter'): - raw_results.filter(narrowed_results) - - try: - raw_page = ResultsPage(raw_results, page_num, page_length) - except ValueError: - if not self.silently_fail: - raise - - return { - 'results': [], - 'hits': 0, - 'spelling_suggestion': None, - } - - # Because as of Whoosh 2.5.1, it will return the wrong page of - # results if you request something too high. :( - if raw_page.pagenum < page_num: - return { - 'results': [], - 'hits': 0, - 'spelling_suggestion': None, - } - - results = self._process_results(raw_page, result_class=result_class) - searcher.close() - - if hasattr(narrow_searcher, 'close'): - narrow_searcher.close() - - return results - - def _process_results( - self, - raw_page, - highlight=False, - query_string='', - spelling_query=None, - result_class=None): - from haystack import connections - results = [] - - # It's important to grab the hits first before slicing. Otherwise, this - # can cause pagination failures. - hits = len(raw_page) - - if result_class is None: - result_class = SearchResult - - facets = {} - spelling_suggestion = None - unified_index = connections[self.connection_alias].get_unified_index() - indexed_models = unified_index.get_indexed_models() - - for doc_offset, raw_result in enumerate(raw_page): - score = raw_page.score(doc_offset) or 0 - app_label, model_name = raw_result[DJANGO_CT].split('.') - additional_fields = {} - model = haystack_get_model(app_label, model_name) - - if model and model in indexed_models: - for key, value in raw_result.items(): - index = unified_index.get_index(model) - string_key = str(key) - - 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) == 0: - additional_fields[string_key] = [] - else: - additional_fields[string_key] = value.split( - ',') - else: - additional_fields[string_key] = index.fields[string_key].convert( - value) - else: - additional_fields[string_key] = self._to_python(value) - - del (additional_fields[DJANGO_CT]) - del (additional_fields[DJANGO_ID]) - - if highlight: - sa = StemmingAnalyzer() - formatter = WhooshHtmlFormatter('em') - terms = [token.text for token in sa(query_string)] - - whoosh_result = whoosh_highlight( - additional_fields.get(self.content_field_name), - terms, - sa, - ContextFragmenter(), - formatter - ) - additional_fields['highlighted'] = { - self.content_field_name: [whoosh_result], - } - - result = result_class( - app_label, - model_name, - raw_result[DJANGO_ID], - score, - **additional_fields) - results.append(result) - else: - hits -= 1 - - if self.include_spelling: - if spelling_query: - spelling_suggestion = self.create_spelling_suggestion( - spelling_query) - else: - spelling_suggestion = self.create_spelling_suggestion( - query_string) - - return { - 'results': results, - 'hits': hits, - 'facets': facets, - 'spelling_suggestion': spelling_suggestion, - } - - def create_spelling_suggestion(self, query_string): - spelling_suggestion = None - reader = self.index.reader() - corrector = reader.corrector(self.content_field_name) - cleaned_query = force_str(query_string) - - if not query_string: - return spelling_suggestion - - # Clean the string. - for rev_word in self.RESERVED_WORDS: - cleaned_query = cleaned_query.replace(rev_word, '') - - for rev_char in self.RESERVED_CHARACTERS: - cleaned_query = cleaned_query.replace(rev_char, '') - - # Break it down. - query_words = cleaned_query.split() - suggested_words = [] - - for word in query_words: - suggestions = corrector.suggest(word, limit=1) - - if len(suggestions) > 0: - suggested_words.append(suggestions[0]) - - spelling_suggestion = ' '.join(suggested_words) - return spelling_suggestion - - def _from_python(self, value): - """ - Converts Python values to a string for Whoosh. - - Code courtesy of pysolr. - """ - if hasattr(value, 'strftime'): - if not hasattr(value, 'hour'): - value = datetime(value.year, value.month, value.day, 0, 0, 0) - elif isinstance(value, bool): - if value: - value = 'true' - else: - value = 'false' - elif isinstance(value, (list, tuple)): - value = u','.join([force_str(v) for v in value]) - elif isinstance(value, (six.integer_types, float)): - # Leave it alone. - pass - else: - value = force_str(value) - return value - - def _to_python(self, value): - """ - Converts values from Whoosh to native Python values. - - A port of the same method in pysolr, as they deal with data the same way. - """ - if value == 'true': - return True - elif value == 'false': - return False - - if value and isinstance(value, six.string_types): - possible_datetime = DATETIME_REGEX.search(value) - - if possible_datetime: - date_values = possible_datetime.groupdict() - - for dk, dv in date_values.items(): - date_values[dk] = int(dv) - - return datetime( - date_values['year'], - date_values['month'], - date_values['day'], - date_values['hour'], - date_values['minute'], - date_values['second']) - - try: - # Attempt to use json to load the values. - converted_value = json.loads(value) - - # Try to handle most built-in types. - if isinstance( - converted_value, - (list, - tuple, - set, - dict, - six.integer_types, - float, - complex)): - return converted_value - except BaseException: - # If it fails (SyntaxError or its ilk) or we don't trust it, - # continue on. - pass - - return value - - -class WhooshSearchQuery(BaseSearchQuery): - def _convert_datetime(self, date): - if hasattr(date, 'hour'): - return force_str(date.strftime('%Y%m%d%H%M%S')) - else: - return force_str(date.strftime('%Y%m%d000000')) - - def clean(self, query_fragment): - """ - Provides a mechanism for sanitizing user input before presenting the - value to the backend. - - Whoosh 1.X differs here in that you can no longer use a backslash - to escape reserved characters. Instead, the whole word should be - quoted. - """ - words = query_fragment.split() - cleaned_words = [] - - for word in words: - if word in self.backend.RESERVED_WORDS: - word = word.replace(word, word.lower()) - - for char in self.backend.RESERVED_CHARACTERS: - if char in word: - word = "'%s'" % word - break - - cleaned_words.append(word) - - return ' '.join(cleaned_words) - - def build_query_fragment(self, field, filter_type, value): - from haystack import connections - query_frag = '' - is_datetime = False - - if not hasattr(value, 'input_type_name'): - # Handle when we've got a ``ValuesListQuerySet``... - if hasattr(value, 'values_list'): - value = list(value) - - if hasattr(value, 'strftime'): - is_datetime = True - - if isinstance(value, six.string_types) and value != ' ': - # It's not an ``InputType``. Assume ``Clean``. - value = Clean(value) - else: - value = PythonData(value) - - # Prepare the query using the InputType. - prepared_value = value.prepare(self) - - if not isinstance(prepared_value, (set, list, tuple)): - # Then convert whatever we get back to what pysolr wants if needed. - prepared_value = self.backend._from_python(prepared_value) - - # 'content' is a special reserved word, much like 'pk' in - # Django's ORM layer. It indicates 'no special field'. - if field == 'content': - index_fieldname = '' - else: - index_fieldname = u'%s:' % connections[self._using].get_unified_index( - ).get_index_fieldname(field) - - filter_types = { - 'content': '%s', - 'contains': '*%s*', - 'endswith': "*%s", - 'startswith': "%s*", - 'exact': '%s', - 'gt': "{%s to}", - 'gte': "[%s to]", - 'lt': "{to %s}", - 'lte': "[to %s]", - 'fuzzy': u'%s~', - } - - if value.post_process is False: - query_frag = prepared_value - else: - if filter_type in [ - 'content', - 'contains', - 'startswith', - 'endswith', - 'fuzzy']: - if value.input_type_name == 'exact': - query_frag = prepared_value - else: - # Iterate over terms & incorportate the converted form of - # each into the query. - terms = [] - - if isinstance(prepared_value, six.string_types): - possible_values = prepared_value.split(' ') - else: - if is_datetime is True: - prepared_value = self._convert_datetime( - prepared_value) - - possible_values = [prepared_value] - - for possible_value in possible_values: - terms.append( - filter_types[filter_type] % - self.backend._from_python(possible_value)) - - if len(terms) == 1: - query_frag = terms[0] - else: - query_frag = u"(%s)" % " AND ".join(terms) - elif filter_type == 'in': - in_options = [] - - for possible_value in prepared_value: - is_datetime = False - - if hasattr(possible_value, 'strftime'): - is_datetime = True - - pv = self.backend._from_python(possible_value) - - if is_datetime is True: - pv = self._convert_datetime(pv) - - if isinstance(pv, six.string_types) and not is_datetime: - in_options.append('"%s"' % pv) - else: - in_options.append('%s' % pv) - - query_frag = "(%s)" % " OR ".join(in_options) - elif filter_type == 'range': - start = self.backend._from_python(prepared_value[0]) - end = self.backend._from_python(prepared_value[1]) - - if hasattr(prepared_value[0], 'strftime'): - start = self._convert_datetime(start) - - if hasattr(prepared_value[1], 'strftime'): - end = self._convert_datetime(end) - - query_frag = u"[%s to %s]" % (start, end) - elif filter_type == 'exact': - if value.input_type_name == 'exact': - query_frag = prepared_value - else: - prepared_value = Exact(prepared_value).prepare(self) - query_frag = filter_types[filter_type] % prepared_value - else: - if is_datetime is True: - prepared_value = self._convert_datetime(prepared_value) - - query_frag = filter_types[filter_type] % prepared_value - - if len(query_frag) and not isinstance(value, Raw): - if not query_frag.startswith('(') and not query_frag.endswith(')'): - query_frag = "(%s)" % query_frag - - return u"%s%s" % (index_fieldname, query_frag) - - # if not filter_type in ('in', 'range'): - # # 'in' is a bit of a special case, as we don't want to - # # convert a valid list/tuple to string. Defer handling it - # # until later... - # value = self.backend._from_python(value) - - -class WhooshEngine(BaseEngine): - backend = WhooshSearchBackend - query = WhooshSearchQuery diff --git a/DjangoBlog-master/djangoblog/wsgi.py b/DjangoBlog-master/djangoblog/wsgi.py deleted file mode 100644 index 2295efd..0000000 --- a/DjangoBlog-master/djangoblog/wsgi.py +++ /dev/null @@ -1,16 +0,0 @@ -""" -WSGI config for djangoblog project. - -It exposes the WSGI callable as a module-level variable named ``application``. - -For more information on this file, see -https://docs.djangoproject.com/en/1.10/howto/deployment/wsgi/ -""" - -import os - -from django.core.wsgi import get_wsgi_application - -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings") - -application = get_wsgi_application() diff --git a/DjangoBlog-master/docs/README-en.md b/DjangoBlog-master/docs/README-en.md deleted file mode 100644 index 37ea069..0000000 --- a/DjangoBlog-master/docs/README-en.md +++ /dev/null @@ -1,158 +0,0 @@ -# DjangoBlog - -

- Django CI - CodeQL - codecov - license -

- -

- A powerful, elegant, and modern blog system. -
- English简体中文 -

- ---- - -DjangoBlog is a high-performance blog platform built with Python 3.10 and Django 4.0. It not only provides all the core functionalities of a traditional blog but also features a flexible plugin system, allowing you to easily extend and customize your website. Whether you are a personal blogger, a tech enthusiast, or a content creator, DjangoBlog aims to provide a stable, efficient, and easy-to-maintain environment for writing and publishing. - -## ✨ Features - -- **Powerful Content Management**: Full support for managing articles, standalone pages, categories, and tags. Comes with a powerful built-in Markdown editor with syntax highlighting. -- **Full-Text Search**: Integrated search engine for fast and accurate content searching. -- **Interactive Comment System**: Supports replies, email notifications, and Markdown formatting in comments. -- **Flexible Sidebar**: Customizable modules for displaying recent articles, most viewed posts, tag cloud, and more. -- **Social Login**: Built-in OAuth support, with integrations for Google, GitHub, Facebook, Weibo, QQ, and other major platforms. -- **High-Performance Caching**: Native support for Redis caching with an automatic refresh mechanism to ensure high-speed website responses. -- **SEO Friendly**: Basic SEO features are included, with automatic notifications to Google and Baidu upon new content publication. -- **Extensible Plugin System**: Extend blog functionalities by creating standalone plugins, ensuring decoupled and maintainable code. We have already implemented features like view counting and SEO optimization through plugins! -- **Integrated Image Hosting**: A simple, built-in image hosting feature for easy uploads and management. -- **Automated Frontend**: Integrated with `django-compressor` to automatically compress and optimize CSS and JavaScript files. -- **Robust Operations**: Built-in email notifications for website exceptions and management capabilities through a WeChat Official Account. - -## 🛠️ Tech Stack - -- **Backend**: Python 3.10, Django 4.0 -- **Database**: MySQL, SQLite (configurable) -- **Cache**: Redis -- **Frontend**: HTML5, CSS3, JavaScript -- **Search**: Whoosh, Elasticsearch (configurable) -- **Editor**: Markdown (mdeditor) - -## 🚀 Getting Started - -### 1. Prerequisites - -Ensure you have Python 3.10+ and MySQL/MariaDB installed on your system. - -### 2. Clone & Installation - -```bash -# Clone the project to your local machine -git clone https://github.com/liangliangyy/DjangoBlog.git -cd DjangoBlog - -# Install dependencies -pip install -r requirements.txt -``` - -### 3. Project Configuration - -- **Database**: - Open `djangoblog/settings.py`, locate the `DATABASES` section, and update it with your MySQL connection details. - - ```python - DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.mysql', - 'NAME': 'djangoblog', - 'USER': 'root', - 'PASSWORD': 'your_password', - 'HOST': '127.0.0.1', - 'PORT': 3306, - } - } - ``` - Create the database in MySQL: - ```sql - CREATE DATABASE `djangoblog` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; - ``` - -- **More Configurations**: - For advanced settings such as email, OAuth, caching, and more, please refer to our [Detailed Configuration Guide](/docs/config-en.md). - -### 4. Database Initialization - -```bash -python manage.py makemigrations -python manage.py migrate - -# Create a superuser account -python manage.py createsuperuser -``` - -### 5. Running the Project - -```bash -# (Optional) Generate some test data -python manage.py create_testdata - -# (Optional) Collect and compress static files -python manage.py collectstatic --noinput -python manage.py compress --force - -# Start the development server -python manage.py runserver -``` - -Now, open your browser and navigate to `http://127.0.0.1:8000/`. You should see the DjangoBlog homepage! - -## Deployment - -- **Traditional Deployment**: A detailed guide for server deployment is available here: [Deployment Tutorial](https://www.lylinux.net/article/2019/8/5/58.html) (in Chinese). -- **Docker Deployment**: This project fully supports Docker. If you are familiar with containerization, please refer to the [Docker Deployment Guide](/docs/docker-en.md) for a quick start. -- **Kubernetes Deployment**: We also provide a complete [Kubernetes Deployment Guide](/docs/k8s-en.md) to help you go cloud-native easily. - -## 🧩 Plugin System - -The plugin system is a core feature of DjangoBlog. It allows you to add new functionalities to your blog without modifying the core codebase by writing standalone plugins. - -- **How it Works**: Plugins operate by registering callback functions to predefined "hooks". For instance, when an article is rendered, the `after_article_body_get` hook is triggered, and all functions registered to this hook are executed. -- **Existing Plugins**: Features like `view_count` and `seo_optimizer` are implemented through this plugin system. -- **Develop Your Own Plugin**: Simply create a new folder under the `plugins` directory and write your `plugin.py`. We welcome you to explore and contribute your creative ideas to the DjangoBlog community! - -## 🤝 Contributing - -We warmly welcome contributions of any kind! If you have great ideas or have found a bug, please feel free to open an issue or submit a pull request. - -## 📄 License - -This project is open-sourced under the [MIT License](LICENSE). - ---- - -## ❤️ Support & Sponsorship - -If you find this project helpful and wish to support its continued maintenance and development, please consider buying me a coffee! Your support is my greatest motivation. - -

- Alipay Sponsorship - WeChat Sponsorship -

-

- (Left) Alipay / (Right) WeChat -

- -## 🙏 Acknowledgements - -A special thanks to **JetBrains** for providing a free open-source license for this project. - -

- - JetBrains Logo - -

- ---- -> If this project has helped you, please leave your website URL [here](https://github.com/liangliangyy/DjangoBlog/issues/214) to let more people see it. Your feedback is the driving force for my continued updates and maintenance. diff --git a/DjangoBlog-master/docs/config-en.md b/DjangoBlog-master/docs/config-en.md deleted file mode 100644 index b877efb..0000000 --- a/DjangoBlog-master/docs/config-en.md +++ /dev/null @@ -1,64 +0,0 @@ -# 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/DjangoBlog-master/docs/config.md b/DjangoBlog-master/docs/config.md deleted file mode 100644 index 24673a3..0000000 --- a/DjangoBlog-master/docs/config.md +++ /dev/null @@ -1,58 +0,0 @@ -# 主要功能配置介绍: - -## 缓存: -缓存默认使用`localmem`缓存,如果你有`redis`环境,可以设置`DJANGO_REDIS_URL`环境变量,则会自动使用该redis来作为缓存,或者你也可以直接修改如下代码来使用。 -https://github.com/liangliangyy/DjangoBlog/blob/ffcb2c3711de805f2067dd3c1c57449cd24d84ee/djangoblog/settings.py#L185-L199 - - -## oauth登录: - -现在已经支持QQ,微博,Google,GitHub,Facebook登录,需要在其对应的开放平台申请oauth登录权限,然后在 -**后台->Oauth** 配置中新增配置,填写对应的`appkey`和`appsecret`以及回调地址。 -### 回调地址示例: -qq:http://你的域名/oauth/authorize?type=qq -微博:http://你的域名/oauth/authorize?type=weibo -type对应在`oauthmanager`中的type字段。 - -## owntracks: -owntracks是一个位置追踪软件,可以定时的将你的坐标提交到你的服务器上,现在简单的支持owntracks功能,需要安装owntracks的app,然后将api地址设置为: -`你的域名/owntracks/logtracks`就可以了。然后访问`你的域名/owntracks/show_dates`就可以看到有经纬度记录的日期,点击之后就可以看到运动轨迹了。地图是使用高德地图绘制。 - -## 邮件功能: -同样,将`settings.py`中的`ADMINS = [('liangliang', 'liangliangyy@gmail.com')]`配置为你自己的错误接收邮箱,另外修改: -```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') -``` -为你自己的邮箱配置。 - -## 微信公众号 -集成了简单的微信公众号功能,在微信后台将token地址设置为:`你的域名/robot` 即可,默认token为`lylinux`,当然你可以修改为你自己的,在`servermanager/robot.py`中。 -然后在**后台->Servermanager->命令**中新增命令,这样就可以使用微信公众号来管理了。 -## 网站配置介绍 -在**后台->BLOG->网站配置**中,可以新增网站配置,比如关键字,描述等,以及谷歌广告,网站统计代码及备案号等等。 -其中的*静态文件保存地址*是保存oauth用户登录的头像路径,填写绝对路径,默认是代码目录。 -## 代码高亮 -如果你发现你文章的代码没有高亮,请这样书写代码块: - -![](https://resource.lylinux.net/image/codelang.png) - - -也就是说,需要在代码块开始位置加入这段代码对应的语言。 - -## update -如果你发现执行数据库迁移的时候出现如下报错: -```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")) -``` -可能是因为你的mysql版本低于5.6,需要升级mysql版本>=5.6即可。 - - -django 4.0登录可能会报错CSRF,需要配置下`settings.py`中的`CSRF_TRUSTED_ORIGINS` - -https://github.com/liangliangyy/DjangoBlog/blob/master/djangoblog/settings.py#L39 - diff --git a/DjangoBlog-master/docs/docker-en.md b/DjangoBlog-master/docs/docker-en.md deleted file mode 100644 index 8d5d59e..0000000 --- a/DjangoBlog-master/docs/docker-en.md +++ /dev/null @@ -1,114 +0,0 @@ -# Deploying DjangoBlog with Docker - -![Docker Pulls](https://img.shields.io/docker/pulls/liangliangyy/djangoblog) -![Docker Image Version (latest by date)](https://img.shields.io/docker/v/liangliangyy/djangoblog?sort=date) -![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/liangliangyy/djangoblog) - -This project fully supports containerized deployment using Docker, providing you with a fast, consistent, and isolated runtime environment. We recommend using `docker-compose` to launch the entire blog service stack with a single command. - -## 1. Prerequisites - -Before you begin, please ensure you have the following software installed on your system: -- [Docker Engine](https://docs.docker.com/engine/install/) -- [Docker Compose](https://docs.docker.com/compose/install/) (Included with Docker Desktop for Mac and Windows) - -## 2. Recommended Method: Using `docker-compose` (One-Click Deployment) - -This is the simplest and most recommended way to deploy. It automatically creates and manages the Django application, a MySQL database, and an optional Elasticsearch service for you. - -### Step 1: Start the Basic Services - -From the project's root directory, run the following command: - -```bash -# Build and start the containers in detached mode (includes Django app and MySQL) -docker-compose up -d --build -``` - -`docker-compose` will read the `docker-compose.yml` file, pull the necessary images, build the project image, and start all services. - -- **Access Your Blog**: Once the services are up, you can access the blog by navigating to `http://127.0.0.1` in your browser. -- **Data Persistence**: MySQL data files will be stored in the `data/mysql` directory within the project root, ensuring that your data persists across container restarts. - -### Step 2: (Optional) Enable Elasticsearch for Full-Text Search - -If you want to use Elasticsearch for more powerful full-text search capabilities, you can include the `docker-compose.es.yml` configuration file: - -```bash -# Build and start all services in detached mode (Django, MySQL, Elasticsearch) -docker-compose -f docker-compose.yml -f deploy/docker-compose/docker-compose.es.yml up -d --build -``` -- **Data Persistence**: Elasticsearch data will be stored in the `data/elasticsearch` directory. - -### Step 3: First-Time Initialization - -After the containers start for the first time, you'll need to execute some initialization commands inside the application container. - -```bash -# Get a shell inside the djangoblog application container (named 'web') -docker-compose exec web bash - -# Inside the container, run the following commands: -# Create a superuser account (follow the prompts to set username, email, and password) -python manage.py createsuperuser - -# (Optional) Create some test data -python manage.py create_testdata - -# (Optional, if ES is enabled) Create the search index -python manage.py rebuild_index - -# Exit the container -exit -``` - -## 3. Alternative Method: Using the Standalone Docker Image - -If you already have an external MySQL database running, you can run the DjangoBlog application image by itself. - -```bash -# Pull the latest image from Docker Hub -docker pull liangliangyy/djangoblog:latest - -# Run the container and connect it to your external database -docker run -d \ - -p 8000:8000 \ - -e DJANGO_SECRET_KEY='your-strong-secret-key' \ - -e DJANGO_MYSQL_HOST='your-mysql-host' \ - -e DJANGO_MYSQL_USER='your-mysql-user' \ - -e DJANGO_MYSQL_PASSWORD='your-mysql-password' \ - -e DJANGO_MYSQL_DATABASE='djangoblog' \ - --name djangoblog \ - liangliangyy/djangoblog:latest -``` - -- **Access Your Blog**: After startup, visit `http://127.0.0.1:8000`. -- **Create Superuser**: `docker exec -it djangoblog python manage.py createsuperuser` - -## 4. Configuration (Environment Variables) - -Most of the project's configuration is managed through environment variables. You can modify them in the `docker-compose.yml` file or pass them using the `-e` flag with the `docker run` command. - -| Environment Variable | Default/Example Value | Notes | -|---------------------------|--------------------------------------------------------------------------|---------------------------------------------------------------------| -| `DJANGO_SECRET_KEY` | `your-strong-secret-key` | **Must be changed to a random, complex string!** | -| `DJANGO_DEBUG` | `False` | Toggles Django's debug mode. | -| `DJANGO_MYSQL_HOST` | `mysql` | Database hostname. | -| `DJANGO_MYSQL_PORT` | `3306` | Database port. | -| `DJANGO_MYSQL_DATABASE` | `djangoblog` | Database name. | -| `DJANGO_MYSQL_USER` | `root` | Database username. | -| `DJANGO_MYSQL_PASSWORD` | `djangoblog_123` | Database password. | -| `DJANGO_REDIS_URL` | `redis:6379/0` | Redis connection URL (for caching). | -| `DJANGO_ELASTICSEARCH_HOST`| `elasticsearch:9200` | Elasticsearch host address. | -| `DJANGO_EMAIL_HOST` | `smtp.example.org` | Email server address. | -| `DJANGO_EMAIL_PORT` | `465` | Email server port. | -| `DJANGO_EMAIL_USER` | `user@example.org` | Email account username. | -| `DJANGO_EMAIL_PASSWORD` | `your-email-password` | Email account password. | -| `DJANGO_EMAIL_USE_SSL` | `True` | Whether to use SSL. | -| `DJANGO_EMAIL_USE_TLS` | `False` | Whether to use TLS. | -| `DJANGO_ADMIN_EMAIL` | `admin@example.org` | Admin email for receiving error reports. | -| `DJANGO_BAIDU_NOTIFY_URL` | `http://data.zz.baidu.com/...` | Push API from [Baidu Webmaster Tools](https://ziyuan.baidu.com/linksubmit/index). | - ---- - -After deployment, please review and adjust these environment variables according to your needs, especially `DJANGO_SECRET_KEY` and the database and email settings. \ No newline at end of file diff --git a/DjangoBlog-master/docs/docker.md b/DjangoBlog-master/docs/docker.md deleted file mode 100644 index e7c255a..0000000 --- a/DjangoBlog-master/docs/docker.md +++ /dev/null @@ -1,114 +0,0 @@ -# 使用 Docker 部署 DjangoBlog - -![Docker Pulls](https://img.shields.io/docker/pulls/liangliangyy/djangoblog) -![Docker Image Version (latest by date)](https://img.shields.io/docker/v/liangliangyy/djangoblog?sort=date) -![Docker Image Size (latest by date)](https://img.shields.io/docker/image-size/liangliangyy/djangoblog) - -本项目全面支持使用 Docker 进行容器化部署,为您提供了快速、一致且隔离的运行环境。我们推荐使用 `docker-compose` 来一键启动整个博客服务栈。 - -## 1. 环境准备 - -在开始之前,请确保您的系统中已经安装了以下软件: -- [Docker Engine](https://docs.docker.com/engine/install/) -- [Docker Compose](https://docs.docker.com/compose/install/) (对于 Docker Desktop 用户,它已内置) - -## 2. 推荐方式:使用 `docker-compose` (一键部署) - -这是最简单、最推荐的部署方式。它会自动为您创建并管理 Django 应用、MySQL 数据库,以及可选的 Elasticsearch 服务。 - -### 步骤 1: 启动基础服务 - -在项目根目录下,执行以下命令: - -```bash -# 构建并以后台模式启动容器 (包含 Django 应用和 MySQL) -docker-compose up -d --build -``` - -`docker-compose` 会读取 `docker-compose.yml` 文件,自动拉取所需镜像、构建项目镜像,并启动所有服务。 - -- **访问您的博客**: 服务启动后,在浏览器中访问 `http://127.0.0.1` 即可看到博客首页。 -- **数据持久化**: MySQL 的数据文件将存储在项目根目录下的 `data/mysql` 文件夹中,确保数据在容器重启后不丢失。 - -### 步骤 2: (可选) 启用 Elasticsearch 全文搜索 - -如果您希望使用 Elasticsearch 提供更强大的全文搜索功能,可以额外加载 `docker-compose.es.yml` 配置文件: - -```bash -# 构建并以后台模式启动所有服务 (Django, MySQL, Elasticsearch) -docker-compose -f docker-compose.yml -f deploy/docker-compose/docker-compose.es.yml up -d --build -``` -- **数据持久化**: Elasticsearch 的数据将存储在 `data/elasticsearch` 文件夹中。 - -### 步骤 3: 首次运行的初始化操作 - -当容器首次启动后,您需要进入容器来执行一些初始化命令。 - -```bash -# 进入 djangoblog 应用容器 -docker-compose exec web bash - -# 在容器内执行以下命令: -# 创建超级管理员账户 (请按照提示设置用户名、邮箱和密码) -python manage.py createsuperuser - -# (可选) 创建一些测试数据 -python manage.py create_testdata - -# (可选,如果启用了 ES) 创建索引 -python manage.py rebuild_index - -# 退出容器 -exit -``` - -## 3. 备选方式:使用独立的 Docker 镜像 - -如果您已经拥有一个正在运行的外部 MySQL 数据库,您也可以只运行 DjangoBlog 的应用镜像。 - -```bash -# 从 Docker Hub 拉取最新镜像 -docker pull liangliangyy/djangoblog:latest - -# 运行容器,并链接到您的外部数据库 -docker run -d \ - -p 8000:8000 \ - -e DJANGO_SECRET_KEY='your-strong-secret-key' \ - -e DJANGO_MYSQL_HOST='your-mysql-host' \ - -e DJANGO_MYSQL_USER='your-mysql-user' \ - -e DJANGO_MYSQL_PASSWORD='your-mysql-password' \ - -e DJANGO_MYSQL_DATABASE='djangoblog' \ - --name djangoblog \ - liangliangyy/djangoblog:latest -``` - -- **访问您的博客**: 启动完成后,访问 `http://127.0.0.1:8000`。 -- **创建管理员**: `docker exec -it djangoblog python manage.py createsuperuser` - -## 4. 配置说明 (环境变量) - -本项目的大部分配置都通过环境变量来管理。您可以在 `docker-compose.yml` 文件中修改它们,或者在使用 `docker run` 命令时通过 `-e` 参数传入。 - -| 环境变量名称 | 默认值/示例 | 备注 | -|-------------------------|--------------------------------------------------------------------------|---------------------------------------------------------------------| -| `DJANGO_SECRET_KEY` | `your-strong-secret-key` | **请务必修改为一个随机且复杂的字符串!** | -| `DJANGO_DEBUG` | `False` | 是否开启 Django 的调试模式 | -| `DJANGO_MYSQL_HOST` | `mysql` | 数据库主机名 | -| `DJANGO_MYSQL_PORT` | `3306` | 数据库端口 | -| `DJANGO_MYSQL_DATABASE` | `djangoblog` | 数据库名称 | -| `DJANGO_MYSQL_USER` | `root` | 数据库用户名 | -| `DJANGO_MYSQL_PASSWORD` | `djangoblog_123` | 数据库密码 | -| `DJANGO_REDIS_URL` | `redis:6379/0` | Redis 连接地址 (用于缓存) | -| `DJANGO_ELASTICSEARCH_HOST` | `elasticsearch:9200` | Elasticsearch 主机地址 | -| `DJANGO_EMAIL_HOST` | `smtp.example.org` | 邮件服务器地址 | -| `DJANGO_EMAIL_PORT` | `465` | 邮件服务器端口 | -| `DJANGO_EMAIL_USER` | `user@example.org` | 邮件账户 | -| `DJANGO_EMAIL_PASSWORD` | `your-email-password` | 邮件密码 | -| `DJANGO_EMAIL_USE_SSL` | `True` | 是否使用 SSL | -| `DJANGO_EMAIL_USE_TLS` | `False` | 是否使用 TLS | -| `DJANGO_ADMIN_EMAIL` | `admin@example.org` | 接收异常报告的管理员邮箱 | -| `DJANGO_BAIDU_NOTIFY_URL` | `http://data.zz.baidu.com/...` | [百度站长平台](https://ziyuan.baidu.com/linksubmit/index) 的推送接口 | - ---- - -部署完成后,请务必检查并根据您的实际需求调整这些环境变量,特别是 `DJANGO_SECRET_KEY` 和数据库、邮件相关的配置。 diff --git a/DjangoBlog-master/docs/es.md b/DjangoBlog-master/docs/es.md deleted file mode 100644 index 97226c5..0000000 --- a/DjangoBlog-master/docs/es.md +++ /dev/null @@ -1,28 +0,0 @@ -# 集成Elasticsearch -如果你已经有了`Elasticsearch`环境,那么可以将搜索从`Whoosh`换成`Elasticsearch`,集成方式也很简单, -首先需要注意如下几点: -1. 你的`Elasticsearch`支持`ik`中文分词 -2. 你的`Elasticsearch`版本>=7.3.0 - -接下来在`settings.py`做如下改动即可: -- 增加es链接,如下所示: -```python -ELASTICSEARCH_DSL = { - 'default': { - 'hosts': '127.0.0.1:9200' - }, -} -``` -- 修改`HAYSTACK`配置: -```python -HAYSTACK_CONNECTIONS = { - 'default': { - 'ENGINE': 'djangoblog.elasticsearch_backend.ElasticSearchEngine', - }, -} -``` -然后终端执行: -```shell script -./manage.py build_index -``` -这将会在你的es中创建两个索引,分别是`blog`和`performance`,其中`blog`索引就是搜索所使用的,而`performance`会记录每个请求的响应时间,以供将来优化使用。 \ No newline at end of file diff --git a/DjangoBlog-master/docs/imgs/alipay.jpg b/DjangoBlog-master/docs/imgs/alipay.jpg deleted file mode 100644 index 424d70a2ffbb629b481e0c27d72d6076727e8041..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 17961 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⁡O0bI!3Bi@jSTgKn>Bg&Q53II7XEA`NhoOY$25L(eK?<1>~>hujoRs}yUHVX zQTt}~MN1Zw0!xe7-aUbnm3fxd*KjwLLobx-SSZdGT`vUtB-=hS^~#Rs9K<#Wao{Zp z{kmB)_tVwwglj-W2qla4$Dx zz53|611jp`Ro8}#TnY+1OdA}SN;lGY+!=)=QY;q0b}yA~i0O^|s1#r34{5DET&Qh` z+yAF+h#H($?p6{{X zz@F@jlhUjuyW%|!{Q!~$q>$)y1Hsl$FMYS;#r=c7U7SN-8Uv-lv<@mZOXKOlXLy4C z3v?;Yh1KbtsO!ynC%cyr-`4MR8{Y~e28GXK7yRZ9{3d5(xoP=XYdX*Uy?#pU$a5>& z`JUOV_ zRl}`OH8`y;U%XD+MFBD!HxL@B`JSH_G!;0V9-A+_4Hhf%=wJ%txE{qNaHkfkLsS+h zfe@JrYreKrFKhU2`1kD)K_#LCd!v$sI$is2ee^7Am=@kq2Z-`cU(l{<8*ON_J?@@& zOR+4E3%sN-vIK-%81R}RdPK$bCrBoGF zRu$>N^0s*R^!6R919|j@|cY|A}(o`$`Onha=;(7(EQNg;0 z^sK+j)DQ>i-wCKpkXH^lH{QNw|FWJ%bUle^xtPfLdv#(gNpA7lv59GyIOXp*E+vz_!y|AkM=lB7mYXSWGfxr6#WlV5UW{v7tVn1!e zwg=v}kG!o+0qXfY-FyUozk}%&PS$ZRL^NoDuz%nVw(p)FVMPG6Ph*mgs4z0~C)P`9 zWsg_X!-)T2FkH=VqaRBu8Zkq=AX6oLSWpg(yQ^NIftdmxjx diff --git a/DjangoBlog-master/docs/imgs/pycharm_logo.png b/DjangoBlog-master/docs/imgs/pycharm_logo.png deleted file mode 100644 index 7f2a4b0ea66469bd218774de8cb3027a9c18b84d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 132045 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 z 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

*$hI3G#u%WdV|9<`IE_VBVx2~ei~?p& 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


AewDOjh!MMu(vjQJBsiX3ZCmAui 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 diff --git a/DjangoBlog-master/docs/k8s-en.md b/DjangoBlog-master/docs/k8s-en.md deleted file mode 100644 index 20e9527..0000000 --- a/DjangoBlog-master/docs/k8s-en.md +++ /dev/null @@ -1,141 +0,0 @@ -# Deploying DjangoBlog with Kubernetes - -This document guides you through deploying the DjangoBlog application on a Kubernetes (K8s) cluster. We provide a complete set of `.yaml` configuration files in the `deploy/k8s` directory to deploy a full service stack, including the DjangoBlog application, Nginx, MySQL, Redis, and Elasticsearch. - -## Architecture Overview - -This deployment utilizes a microservices-based, cloud-native architecture: - -- **Core Components**: Each core service (DjangoBlog, Nginx, MySQL, Redis, Elasticsearch) runs as a separate `Deployment`. -- **Configuration Management**: Nginx configurations and Django application environment variables are managed via `ConfigMap`. **Note: For sensitive information like passwords, using `Secret` is highly recommended.** -- **Service Discovery**: All services are exposed internally within the cluster as `ClusterIP` type `Service`, enabling communication via service names. -- **External Access**: An `Ingress` resource is used to route external HTTP traffic to the Nginx service, which acts as the single entry point for the entire blog application. -- **Data Persistence**: A `local-storage` solution based on node-local paths is used. This requires you to manually create storage directories on a specific K8s node and statically bind them using `PersistentVolume` (PV) and `PersistentVolumeClaim` (PVC). - -## 1. Prerequisites - -Before you begin, please ensure you have the following: - -- A running Kubernetes cluster. -- The `kubectl` command-line tool configured to connect to your cluster. -- An [Nginx Ingress Controller](https://kubernetes.github.io/ingress-nginx/deploy/) installed and configured in your cluster. -- Filesystem access to one of the nodes in your cluster (defaulted to `master` in the configs) to create local storage directories. - -## 2. Deployment Steps - -### Step 1: Create a Namespace - -We recommend deploying all DjangoBlog-related resources in a dedicated namespace for better management. - -```bash -# Create a namespace named 'djangoblog' -kubectl create namespace djangoblog -``` - -### Step 2: Configure Persistent Storage - -This setup uses Local Persistent Volumes. You need to create the data storage directories on a node within your cluster (the default is the `master` node in `pv.yaml`). - -```bash -# Log in to your master node -ssh user@master-node - -# Create the required storage directories -sudo mkdir -p /mnt/local-storage-db -sudo mkdir -p /mnt/local-storage-djangoblog -sudo mkdir -p /mnt/resource/ -sudo mkdir -p /mnt/local-storage-elasticsearch - -# Log out from the node -exit -``` -**Note**: If you wish to store data on a different node or use different paths, you must modify the `nodeAffinity` and `local.path` settings in the `deploy/k8s/pv.yaml` file. - -After creating the directories, apply the storage-related configurations: - -```bash -# Apply the StorageClass -kubectl apply -f deploy/k8s/storageclass.yaml - -# Apply the PersistentVolumes (PVs) -kubectl apply -f deploy/k8s/pv.yaml - -# Apply the PersistentVolumeClaims (PVCs) -kubectl apply -f deploy/k8s/pvc.yaml -``` - -### Step 3: Configure the Application - -Before deploying the application, you need to edit the `deploy/k8s/configmap.yaml` file to modify sensitive information and custom settings. - -**It is strongly recommended to change the following fields:** -- `DJANGO_SECRET_KEY`: Change to a random, complex string. -- `DJANGO_MYSQL_PASSWORD` and `MYSQL_ROOT_PASSWORD`: Change to your own secure database password. - -```bash -# Edit the ConfigMap file -vim deploy/k8s/configmap.yaml - -# Apply the configuration -kubectl apply -f deploy/k8s/configmap.yaml -``` - -### Step 4: Deploy the Application Stack - -Now, we can deploy all the core services. - -```bash -# Deploy the Deployments (DjangoBlog, MySQL, Redis, Nginx, ES) -kubectl apply -f deploy/k8s/deployment.yaml - -# Deploy the Services (to create internal endpoints for the Deployments) -kubectl apply -f deploy/k8s/service.yaml -``` - -The deployment may take some time. You can run the following command to check if all Pods are running successfully (STATUS should be `Running`): - -```bash -kubectl get pods -n djangoblog -w -``` - -### Step 5: Expose the Application Externally - -Finally, expose the Nginx service to external traffic by applying the `Ingress` rule. - -```bash -# Apply the Ingress rule -kubectl apply -f deploy/k8s/gateway.yaml -``` - -Once deployed, you can access your blog via the external IP address of your Ingress Controller. Use the following command to find the address: - -```bash -kubectl get ingress -n djangoblog -``` - -### Step 6: First-Time Initialization - -Similar to the Docker deployment, you need to get a shell into the DjangoBlog application Pod to perform database initialization and create a superuser on the first run. - -```bash -# First, get the name of a djangoblog pod -kubectl get pods -n djangoblog | grep djangoblog - -# Exec into one of the Pods (replace [pod-name] with the name from the previous step) -kubectl exec -it [pod-name] -n djangoblog -- bash - -# Inside the Pod, run the following commands: -# Create a superuser account (follow the prompts) -python manage.py createsuperuser - -# (Optional) Create some test data -python manage.py create_testdata - -# (Optional, if ES is enabled) Create the search index -python manage.py rebuild_index - -# Exit the Pod -exit -``` - -Congratulations! You have successfully deployed DjangoBlog on your Kubernetes cluster. \ No newline at end of file diff --git a/DjangoBlog-master/docs/k8s.md b/DjangoBlog-master/docs/k8s.md deleted file mode 100644 index 9da3c28..0000000 --- a/DjangoBlog-master/docs/k8s.md +++ /dev/null @@ -1,141 +0,0 @@ -# 使用 Kubernetes 部署 DjangoBlog - -本文档将指导您如何在 Kubernetes (K8s) 集群上部署 DjangoBlog 应用。我们提供了一套完整的 `.yaml` 配置文件,位于 `deploy/k8s` 目录下,用于部署一个包含 DjangoBlog 应用、Nginx、MySQL、Redis 和 Elasticsearch 的完整服务栈。 - -## 架构概览 - -本次部署采用的是微服务化的云原生架构: - -- **核心组件**: 每个核心服务 (DjangoBlog, Nginx, MySQL, Redis, Elasticsearch) 都将作为独立的 `Deployment` 运行。 -- **配置管理**: Nginx 的配置文件和 Django 应用的环境变量通过 `ConfigMap` 进行管理。**注意:敏感信息(如密码)建议使用 `Secret` 进行管理。** -- **服务发现**: 所有服务都通过 `ClusterIP` 类型的 `Service` 在集群内部暴露,并通过服务名相互通信。 -- **外部访问**: 使用 `Ingress` 资源将外部的 HTTP 流量路由到 Nginx 服务,作为整个博客应用的统一入口。 -- **数据持久化**: 采用基于节点本地路径的 `local-storage` 方案。这需要您在指定的 K8s 节点上手动创建存储目录,并通过 `PersistentVolume` (PV) 和 `PersistentVolumeClaim` (PVC) 进行静态绑定。 - -## 1. 环境准备 - -在开始之前,请确保您已具备以下环境: - -- 一个正在运行的 Kubernetes 集群。 -- `kubectl` 命令行工具已配置并能够连接到您的集群。 -- 集群中已安装并配置好 [Nginx Ingress Controller](https://kubernetes.github.io/ingress-nginx/deploy/)。 -- 对集群中的一个节点(默认为 `master`)拥有文件系统访问权限,用于创建本地存储目录。 - -## 2. 部署步骤 - -### 步骤 1: 创建命名空间 - -我们建议将 DjangoBlog 相关的所有资源都部署在一个独立的命名空间中,便于管理。 - -```bash -# 创建一个名为 djangoblog 的命名空间 -kubectl create namespace djangoblog -``` - -### 步骤 2: 配置持久化存储 - -此方案使用本地持久卷 (Local Persistent Volume)。您需要在集群的一个节点上(在 `pv.yaml` 文件中默认为 `master` 节点)创建用于数据存储的目录。 - -```bash -# 登录到您的 master 节点 -ssh user@master-node - -# 创建所需的存储目录 -sudo mkdir -p /mnt/local-storage-db -sudo mkdir -p /mnt/local-storage-djangoblog -sudo mkdir -p /mnt/resource/ -sudo mkdir -p /mnt/local-storage-elasticsearch - -# 退出节点 -exit -``` -**注意**: 如果您希望将数据存储在其他节点或使用不同的路径,请务必修改 `deploy/k8s/pv.yaml` 文件中 `nodeAffinity` 和 `local.path` 的配置。 - -创建目录后,应用存储相关的配置文件: - -```bash -# 应用 StorageClass -kubectl apply -f deploy/k8s/storageclass.yaml - -# 应用 PersistentVolume (PV) -kubectl apply -f deploy/k8s/pv.yaml - -# 应用 PersistentVolumeClaim (PVC) -kubectl apply -f deploy/k8s/pvc.yaml -``` - -### 步骤 3: 配置应用 - -在部署应用之前,您需要编辑 `deploy/k8s/configmap.yaml` 文件,修改其中的敏感信息和个性化配置。 - -**强烈建议修改以下字段:** -- `DJANGO_SECRET_KEY`: 修改为一个随机且复杂的字符串。 -- `DJANGO_MYSQL_PASSWORD` 和 `MYSQL_ROOT_PASSWORD`: 修改为您自己的数据库密码。 - -```bash -# 编辑 ConfigMap 文件 -vim deploy/k8s/configmap.yaml - -# 应用配置 -kubectl apply -f deploy/k8s/configmap.yaml -``` - -### 步骤 4: 部署应用服务栈 - -现在,我们可以部署所有的核心服务了。 - -```bash -# 部署 Deployments (DjangoBlog, MySQL, Redis, Nginx, ES) -kubectl apply -f deploy/k8s/deployment.yaml - -# 部署 Services (为 Deployments 创建内部访问端点) -kubectl apply -f deploy/k8s/service.yaml -``` - -部署需要一些时间,您可以运行以下命令检查所有 Pod 是否都已成功运行 (STATUS 为 `Running`): - -```bash -kubectl get pods -n djangoblog -w -``` - -### 步骤 5: 暴露应用到外部 - -最后,通过应用 `Ingress` 规则来将外部流量引导至我们的 Nginx 服务。 - -```bash -# 应用 Ingress 规则 -kubectl apply -f deploy/k8s/gateway.yaml -``` - -部署完成后,您可以通过 Ingress Controller 的外部 IP 地址来访问您的博客。执行以下命令获取地址: - -```bash -kubectl get ingress -n djangoblog -``` - -### 步骤 6: 首次运行的初始化操作 - -与 Docker 部署类似,首次运行时,您需要进入 DjangoBlog 应用的 Pod 来执行数据库初始化和创建管理员账户。 - -```bash -# 首先,获取 djangoblog pod 的名称 -kubectl get pods -n djangoblog | grep djangoblog - -# 进入其中一个 Pod (将 [pod-name] 替换为上一步获取到的名称) -kubectl exec -it [pod-name] -n djangoblog -- bash - -# 在 Pod 内部执行以下命令: -# 创建超级管理员账户 (请按照提示操作) -python manage.py createsuperuser - -# (可选) 创建测试数据 -python manage.py create_testdata - -# (可选,如果启用了 ES) 创建索引 -python manage.py rebuild_index - -# 退出 Pod -exit -``` - -至此,您已成功在 Kubernetes 集群上完成了 DjangoBlog 的部署! \ No newline at end of file diff --git a/DjangoBlog-master/get-pip.py b/DjangoBlog-master/get-pip.py deleted file mode 100644 index b86d2d7..0000000 --- a/DjangoBlog-master/get-pip.py +++ /dev/null @@ -1,4094 +0,0 @@ -#!/usr/bin/env python -# -# Hi There! -# -# You may be wondering what this giant blob of binary data here is, you might -# even be worried that we're up to something nefarious (good for you for being -# paranoid!). This is a base85 encoding of a zip file, this zip file contains -# an entire copy of pip (version 25.2). -# -# Pip is a thing that installs packages, pip itself is a package that someone -# might want to install, especially if they're looking to run this get-pip.py -# script. Pip has a lot of code to deal with the security of installing -# packages, various edge cases on various platforms, and other such sort of -# "tribal knowledge" that has been encoded in its code base. Because of this -# we basically include an entire copy of pip inside this blob. We do this -# because the alternatives are attempt to implement a "minipip" that probably -# doesn't do things correctly and has weird edge cases, or compress pip itself -# down into a single file. -# -# If you're wondering how this is created, it is generated using -# `scripts/generate.py` in https://github.com/pypa/get-pip. - -import sys - -this_python = sys.version_info[:2] -min_version = (3, 9) -if this_python < min_version: - message_parts = [ - "This script does not work on Python {}.{}.".format(*this_python), - "The minimum supported Python version is {}.{}.".format(*min_version), - "Please use https://bootstrap.pypa.io/pip/{}.{}/get-pip.py instead.".format(*this_python), - ] - print("ERROR: " + " ".join(message_parts)) - sys.exit(1) - - -import os.path -import pkgutil -import shutil -import tempfile -import argparse -import importlib -from base64 import b85decode - - -def include_setuptools(args): - """ - Install setuptools only if absent, not excluded and when using Python <3.12. - """ - cli = not args.no_setuptools - env = not os.environ.get("PIP_NO_SETUPTOOLS") - absent = not importlib.util.find_spec("setuptools") - python_lt_3_12 = this_python < (3, 12) - return cli and env and absent and python_lt_3_12 - - -def include_wheel(args): - """ - Install wheel only if absent, not excluded and when using Python <3.12. - """ - cli = not args.no_wheel - env = not os.environ.get("PIP_NO_WHEEL") - absent = not importlib.util.find_spec("wheel") - python_lt_3_12 = this_python < (3, 12) - return cli and env and absent and python_lt_3_12 - - -def determine_pip_install_arguments(): - pre_parser = argparse.ArgumentParser() - pre_parser.add_argument("--no-setuptools", action="store_true") - pre_parser.add_argument("--no-wheel", action="store_true") - pre, args = pre_parser.parse_known_args() - - args.append("pip") - - if include_setuptools(pre): - args.append("setuptools") - - if include_wheel(pre): - args.append("wheel") - - return ["install", "--upgrade", "--force-reinstall"] + args - - -def monkeypatch_for_cert(tmpdir): - """Patches `pip install` to provide default certificate with the lowest priority. - - This ensures that the bundled certificates are used unless the user specifies a - custom cert via any of pip's option passing mechanisms (config, env-var, CLI). - - A monkeypatch is the easiest way to achieve this, without messing too much with - the rest of pip's internals. - """ - from pip._internal.commands.install import InstallCommand - - # We want to be using the internal certificates. - cert_path = os.path.join(tmpdir, "cacert.pem") - with open(cert_path, "wb") as cert: - cert.write(pkgutil.get_data("pip._vendor.certifi", "cacert.pem")) - - install_parse_args = InstallCommand.parse_args - - def cert_parse_args(self, args): - if not self.parser.get_default_values().cert: - # There are no user provided cert -- force use of bundled cert - self.parser.defaults["cert"] = cert_path # calculated above - return install_parse_args(self, args) - - InstallCommand.parse_args = cert_parse_args - - -def bootstrap(tmpdir): - monkeypatch_for_cert(tmpdir) - - # Execute the included pip and use it to install the latest pip and - # any user-requested packages from PyPI. - from pip._internal.cli.main import main as pip_entry_point - args = determine_pip_install_arguments() - sys.exit(pip_entry_point(args)) - - -def main(): - tmpdir = None - try: - # Create a temporary working directory - tmpdir = tempfile.mkdtemp() - - # Unpack the zipfile into the temporary directory - pip_zip = os.path.join(tmpdir, "pip.zip") - with open(pip_zip, "wb") as fp: - fp.write(b85decode(DATA.replace(b"\n", b""))) - - # Add the zipfile to sys.path so that we can import it - sys.path.insert(0, pip_zip) - - # Run the bootstrap - bootstrap(tmpdir=tmpdir) - finally: - # Clean up our temporary working directory - if tmpdir: - shutil.rmtree(tmpdir, ignore_errors=True) - - -DATA = b""" -P)h>@6aWAK2mo!S{#s3Wt_Jr2003bD000jF003}la4%n9X>MtBUtcb8c|DLpOT<77h41q#LNB_YQ&<$ -Wpx{ODA|AYn471yGJJ4o^By}nH<4r3y3}kqDJU(8>d4v$UNhih-AdMxnLL|x>HDwa#Lik1&``i5ys{O -6sSI)T>I~Zf4%g85bU`V2$qWlFv@RkY9x~v^sKS3gG9N1J<_0cB*dyy6ync@J?@2`+)m|?_73SDNH1m -Q44N##Nyp9zk}k_QAiaw;m`t${CQjcuD2R10cRV;bZN1QB}GB28VNlJ?WVAM;q3xy0Hs>m)Lv)OR-z_ -kJT=cefN`4j(o;KdD7}B;~neQ)O!@oH>?){D;D59n!zJ*0OWIP)h>@6aWAK2mo!S{#tMMuh+Z*00344 -000jF003}la4%n9ZDDC{Utcb8d0kRVZ`&{ozWY}Y>A}w6x;YO^fgN`(b{hsmkm;C+EeWC=&-?Ww6}xG -tlOFsY{3x1&j|n^%bl>sHMezi0IQbhwS%PsL*f~aY2^Jr( -->p)--qr&r|06wh_J-^zioU^jr0+!=aW4nhPeobk*_rwE$UrRTF%uPDhR9M-K~vi3o9aPFu(U7!^k`W -x6h>k&oK#&LP;J12Kqozi_UZ*<5gUDHflV(D$5 -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 -?0BE|h2rF$;xrRkEEuEtL(!%-3?Sz`%V7~ -O0hXWU`;*U?eJ`UC*Fa+DkwSM52uPZr>{Q!xEfJJ&rhjGE8=K*cNY!C2dd2H2-^eUvm`x=SRi)TUg!+ -eqGeN|apI_MGHBXH(YDZb~S5u@eGx@ffXYOj6O)Vg`B4w(ZjJk4~mpCo9!w=X6-}-C|gsF_G25Rk -SQYfC2RC+s$SX>KzU=TJnsb&}bg`cdN~6!hEp9lF*lgwFE5K&?C6;5ujid32Nm|=@H`~~iINeuWk#LP4wx2CU#wXVm2Ty@AJ1-{7j}de*Y37jBB<$OhFt8~ -R@N>rW-uIYE+#F8TlPeL!Cq5-D}NJaD5D#H!mtGYRB)zdX$$d-eHwT#+(MT*>dO$aMG?V^dG3O1cG%N -%IzGN1pCyI%!Kc5MN9+M2z53u`N9N+~|)2tSUcmrEwT+3JPluqL`SiHxgU*` -?JqgqFu-Yx$W4K9jlan}a@UYpXaXPOG#OQ*CDa5&aS+O{Z~toJXu5@yC$_$F%t^@64Vb*=|z^%YV!*1oY^5bo*wb%)oL?=rx_Cg(qK&Jcrk(Thc=JCPkkkyrkxl5(P)ep1zyp(# -$CL<*52>whIS91tJ!VlhfK4e!Qjd0qXMs*C~PsKr76uhJzvf4Ob`9-C#bV%5KBj9Xc%I{_>7b^p)eJ} -35_8=VPM7qKMFW!6bS<>LiXL-NjIQ|XIKMHISkv3K8sNjvu%JVrZ^+7a%uCZM?nJs{Ts<1n(>Q0aB9W -52wm9IT)7|=7o}u1L6YTL6c2Cxi9 -;i1RInID69`X!7Tv*ZP3so4SZ}d7Dxf`M;VG?|uvhRI3!j -yS-q-*p%}wii@NG!^T!yv8`}crkil_&UrqJ}I!IAG$;xK*tcj(XfF$LJl95q6D@DEn)Cj_2SV~aLr<^ -}t^_!^3&cz|@5m%2JIfu6@ni`4kSF&0{GeK6B1Mt+H%W1agi*~dgZk<3Ygil%JtN@!Mr;(_f&e#7fh? -db#|@M9=9Vb0A?Gd12=G2L@{iyxwxnK*_ToxAa7c$``}MEn>LJxq?#(_+51<3O{lJ281kpOvjqppn$oFsj#1+g-MH(0)#+}^PT#5UR>xisqnp -5)3iUC&t+dm`?JKY?hwVja*!#ifdlbCP;XjhW)MfM>oNmrJf5(bxX4<}#mL*UfBfxq#a=h`qFUNUlzs -Z}?k_#Gee3LPl{hO;-Wxub$322y{1P$aDl=aw1TPE!*H?*Py=P0T9ek{ViO*HY*3g9cO|%(T(AQ7_#r -_j1G-`OZA1P*JEn|Zt12Fe%G`e)3S0$vMHU5`4y{zqZ82_C&@7r(!7l#YOw*fx@yGuAc;|Y368Jpggk -bW_lhnUA_fODz`hW?B0egghGY2wCw=Md{Pe!=!7LjJ!6o8%>Q4(E+N+e06Ph@v=^vXRg={0C4=0|XQR -000O8ZKnQOuGux}3=9AOd?)|_761SMaA|NaUukZ1WpZv|Y%gPBV`ybAaCzNYZI9c=5&nL^VynOiRmeQ -oaNJgaiaO3YcHx)6cHj?(1F;fU()uVa!zE8v+Wz-GGrJ_0B6U7DOnz&ig#G)MPSwDpy-2Udd -c;)K2BqWHLEBTbE`hl4M<1b*Yj>=v`sTO2|Anm8`VM?b(?h+e*95^s;&UXieUH&&+0{^G)+;tfO1Vig -q+af54-gs15~|H{(z8gJ-1BMV#EIJT+xp$knwZs&OSZwpsgLZe~K~l`4x;;ct@5o$8O=sM3P*+bGtR& -RUWynVVd%WTt;sOfF=G3Fk74)u)vzSRu{67x@kLq#w3QWzTe`c+@<5ZVwRV8(qH3Yunksr(K -mR*_7ubcHR@%@&idt#(n%Uq2uX3^tH?TQ2(wQ9w)W5<+-nV@ORCf7Po?P)5IrPT)6I;jlFcXmepph~f -593$SS>Lrgw39C3sre|kw-oE($#p~p6@7}ylUO#ztf%OwpYHWzfx!|2=salJK91qN{swkZc`v1~aXSGwbUsKoB{$i^yP!AQ}B*H5O?c&k38dZTO=K@n%GO -j;{AKwC+kK#CN3?EPR$?IrSFP7+ITAq_UENtwnBw$R$#9;(zCMkjdxbJ+_v{RHT&( --+3x-&UL$3@vyh9k|eS!ThBrYV2d^X+T@;LR%Ttmz|*+hH*KfDzJik=sdQunI5svW$BDIttWi7FZKR{ -)Zp)&-oz4&Vg_k96p1l!*SJpF63Yl{RG2@xG>_t}{_YnPiMShG;q#61G^~g%>YFmk=YMK3Lbgojd*VV -RTr_~&Nz7ijl*vlLQjM%7L!Gjc%s-Wn{P;K#qQ|i)N5)L;Ldsx)uUsuh}fZRu7p;WYwEcWBumtbfm=~ -;t8z1dQJz5$PzMpPq!5t%tJUYBx1`92^g^Rzo{B};I#G&PB9toH=^fGej&=>6{#@IINdA}R0_5+8e#h -WtO%4DS>n!E$wO*?dtO!o@0hv@4C~#C -zjk9=hfNApfbPOR}EK*Yu7Y9=LQqk(N&aq>50KreNstAn7@90vrpRaMye`S} -JC)yo)*&x=DL&rvae?v|S$0nbX7|Xfvm?EH-VNurGzbD2hNx9_4zFKq1LVWgCf*u_fPpGmrk_H`NON -37xVJr{89JtX#Ti+cs#$l>ZJ8mokg|H<-LUd&eY*TVPs)4 -YetD|GYn^|Hb%#J;}{eQtP35L9lR{Itp(kBFoZJy^S#TO5T(yoFMgIpFG$jGcnjnH{*su#N=<7P^w7iDS6CcSv(q -8^UvGHu`_fzFRyMErxxVz&bLilDYi6H2>t=RRnNuG|t9m1vI0YAG#HD*-uWC?lB<=}dj1f-`gQVNb%9 -ejf$`76IdY^$yRtn_fK1eWz)T`x6Gd5&xZbxYw6TL^>Bb2W?%03WEUGXJ|eCvp_Wfp1*-_1!8fSw%fn -;^`+w6N3iTwxLD=~K?sV`0mDV#4}_<+#0XrlB0&vi&SzZR@jx{k-Am@tZO7-H<}zchDN)x&kduhaR#z -zX?I7~M$+H7lL@`jtCJGoF+x8UXTzEJ*Xj)_tPE%9Xbm$GOyxuJ_M9r7usl%owuOzS(C5MhI*HC|=Ut -e}!O6jl8^-r~m#YMb{MSSjKD+J1^`58SNd0FFAbfhbgy2Hw(vj%}RcnW(S#1G;)rb&nhZfQxyi@lAdG -aN&RuBHQ~L9r;lq+ycBQp$FXJC#YVFzBH!QLG7MEER@Tidanp?fa(A`6Bgm9NfO2WB*`5_FKK$I_$Cs -zHHRTE;Sy(O}XUMKi*LF)4%WR`ZX%3#qZejy%&?=d{@E^=x~Ei@?onL^=YF6cht~}|31Ve|06lKLj;6 -6f}#rBl56(P9IKN3oN%JdFNW7vQMjNpkn;m5%wLtc?|k=@_srY^nd}zua#Yx>^3WfkkMrk+yMv0lv!} -h4?m}(!7Hvq9@_^23)thjz=12*AT+GLb-z`o-u)zWD#wt$Lvff04S^7*~Y|zqC;D%{j+;h1Z{~=gWNh -jl*Q=?p`yOR}%q=EtpBwi4ra4j{O14=Gz-Fql-vr$5UB}eD8o_~j*Q2ybH>77+hLO3P2u@Uv7WI9yuS -K?dFkX*)sR@QZ@=dxUF&sDX$8xx(Y#wNc224g;F>XoQvbP0lfxuly2<0mgp`uDG&qfv~t9BTb|GxfbooR;$ZkOa)tU-M -SM?&qOh3M)%yOQCc`{BTJ)>)N -Ijk_ZB%s{zPyJXzlTCYt_9e@M0miY^}L;0nxevKC^r(TpKE^{}RM}t=Tbb%g=zrlUG -Z<(;RER=*&R6JDvI8Zz0G`E{=m+xM0|0^d6==I;eL;7gB`t4qvx~P~&JU*c*3Hq%lv()Nih~u7>*UC$ -7GNb^iYS^Os>rh%GT99dn-@rdXf{bQTO~T;t6&_R{tf!$?s@I%1zxrOd+Dub%PH0?6!bJAM>9{jxw3E -r#$vRP^F>575{4qW>78oxrI)cRm?rJr@rAr&E69cFY2m_;=^0BtDAV{|kwq$;=*!Oq;rQ*?+b4)0@>! -p*Kp~U_)6pWtybG&6xrGaP>Os7mt}}=XJg|0b&~|E571~5Unc^cSB@f3d3$iU?I(05Z12;sGgcSOWCw -^{4s=TU*fvxCny!4YWF|TZJVqJ`tvI?bEC?Asp%C*b*i-w#Qy?F)ms3^8tc8^zxnRXoL~8MYF9ti8lL -IjC+8!(ZhMzo07sqw;lVeLA5kzMk8S9B@|Z^JC-j2>x{c{MCk+m{rO#cAMW4wVXCeAI#Lz(Z=tj49>( -U(08^$fiWbfJ7fk^zz^tA$Hl=K5Av8*?OZ!p|B2*|Q_UdJ!DPq_mVWtRy3pt;6YB3F9>TY$g`*Pu_Q$ -V>WaCyaB|8LtkmjB&<1y{wOHpWqRb{C5q1HQY=B<%!~CPmV -AW?>jw#iCPV$ofk?P%3!`YU3kid7odG7(>fMtQfDcc@xsE2}7^aIIjr{q0;;|lj6Rp5|K!?s#1Wt755ui0ribEs -973u;t?Y!$~djWijH1a#dZ_~!DvM9L=>%e^{$dpB+_kJRJDlnyr_A<8jZ|%8f+O?N>=u}tXRdFw4VxP -->JRQpjSnfXfYBGq5O&jM5yN@;D3S*Oe?8q}?(GV}$T -zdsq3aLjBC66x3i0r%ipdKFNbp7Iz- ->f`W)YYvj=xjJ_q&C@`nYfGq*nUeYmog0i!FFr@K{yI8)cX4@jdO{oTWThYjzz#MtE -1`d{#7AiC7aXWWvG!G0kv2o8<4T4jy=2bRYH@$MKE0X;J5U37pyw0ObzRC;x=vTT;@$b>Psiu;;5I96 -;!Ll)-m6+}#cydMdCm3N$J2SB(pu{0i_d9Q@8WDJPV>98DsuJ?9$1hz4`hC)H?X`uoPCH4qu!pKpW0> -7=i@703Ja{9D&o3^OWM`=wh(zyZR -0HcA4z_QAi!BBaxJ3f0mB{Be>2uNarB?`cg8$~k9r?}k;Z?LdZ-lg1KTQj8&?U~#(JZQUA2;8EFH!c6 -?^9i`8k5c5a#>`)^`t@CSfJZEM7t7BhtyEV8Sb**1Ss9aaAjF&*R_-4>e95Sc^Z+mL%9B&-S9MUsZ$MkM0I^J(0)=(>YM4~V;Atdi%q0$#)@`FZ~mLvY-E}?^Qu6G&&W=P7De(j&h05a(NYX2079 -vSj$389jM90&glCevmCqt|!dA${Gh>Y$+?Xk>DE{}d*cFLboS7sHkV1R9|~H0QhSX*MLgX=f?n(PtwXvzx6-9p9gfE`(5E@ -Z6s#7!*-I%+En&;d>|JOf)KaUE;=0gRAU2wv$5nTHXwri8sWGPAUfB4p{=dWzLWMjV;}@gjrQK~5P_G -?)oato~PA6gk-l{b3hHZ(0|wb|uH62JB?iJcErk;0P^p;|z_^Zl9RY&uZlN}&a!KOqBUe|QW+Vs~8k;W12vSM40OaXCiVMdI2)cQWbi^uK985#vM4cW~Ce^%t}V&s*e -M-o6q)$yH2o?4Gg{S&F)fS6_%4T}$EXTX3S$4z8NK6C#~mt64AVpqR`g#h$6?q~6Qr68JoLl$r7lPHD --kHlInReoENvdh8&g)!V~^-!ca6JJeQBp8xRpjpuKR1o^Q{T8=!+1`55))~ZNCY*7Oy0`sFvO?9nUSC -~D+X6tZLL7M@#=(0Mvv1B}PZ7$~w$l{(t6)^9}Lkhd6ZRFbyxo#&jCBUkUE8f^(tN0;B%}`)L#>}2tPqYN0bp{2%!c$5#)prK60X -zS)Oq-paJIb*gay@!CLcxU}Iri?d=J^CNMH4WB|D~+XoFE1@9z+to6Ix9_fa!&_x0|aZ5D>6?fcR(l7 -l@x6Hz~lfyFQpzOPnhRftL9=+9TQ($Sz6scv86Z+A3;-Aw!E8LZEuyX7`n+&OkyJrDXdg_I8XF$1LiM -HHgS%YIJY2hZs7V(}+ne{HyrIfIx6m>?BwQtL8-vJg}6o}cY4Y4?A(uzH0>#s6VtQDc_)bQR -t8$Z{L0{H9T@&1?C~dpzDNp1jRAz_c6T9A>)pu%({9)O2l)`X{SYzPZ#B$h;7+eSg)1lkMDl$jg_V0k -%n8k6D^cIf>)`to)f`siG(v=*Sg4e2^u8EcWE|(d&Zd=K0oQtUgk0E>Rx7($n3@5>SamAvk)0n -8-L*(o~UlF@mPDQT~$f0ypFbDOSOlWLuwTEOPFW94(Df{Z)t41SFL%Z>F$4+=Ide{{8ptpK`z913RV1 -CtA9szP8k{GSrvpwSj(>{|CFyB?WNV&whQr|y{d-#EZEmdlyo<%=oV?5w{;9%%t|T_e68u}0lm1WDoU -AK?FsIK2^q?|zm{G`OZkZL+CRXk$mKO_2mz6ORiW3vX5s7LI5%cptXL%yalhe;uocH}S4;E_T|<0eY9 -CSKGvLRA4&jylUwo+2JE)$hiXDzL?Xp)+8*Equ4=8!M9wFFV9Sn}Ux1%;{VeM@{>3iy_q2;#?so2mNKzVK7=P927D#n@}C+sn$DEwgKdQP^fqycmh-xy^yzG^Z~ -tj3#u~iUHp=d?_wS9F{#|$7g_l>iu(&R7P|ZzGw;=ecpNtK9DzEWw@%Y2MvvN3q_KJP- -1J|+Qxh9wr}mhBTby2WnVaT2IqE&_6cUUaoi%QSH4wu!r+$^P)GZRz32zHpMSYT_x5w*)H`W}+B?4?R -?j&fFt~E1Wx}2D_f3 -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 -E3G`z*VUVv=* -uT7~6lQdCqW?kDLQz;;;G(7o!T9dKBf-L~QGG-hJy0-HiyAV-7H|5wRDr-o)8Qq;v+g1>HB?z-u(M;~qEg_ -$=SSvNl?4TI;s@TW3p6cHL5c>uhPsuiM)1ovrOk=+^c-w63@HhTm2X>LdlxJk^S~U&eLY9r%;6sXJ+! -zLgAcZQzLmF+ufojGHy`aHl)_z)DH7JcPHAsK%W=vxz16O>^&R%W!sJizU(Z*236bJ-eQWE9Ys&RPf4 -cK=K^efC@?oJuyE}VH{7hG}+W8&l7RR%8WD*X)w(LBUWy?OypgVS~pCaW3GJD!_{`FV<6TCtg!917@I -#XXO22sgw6GIe|VTtFu;+sDlio*p#Ax( -)re%=vF#Q11%%+(Ap}iTx96Pl_kx;`z67yXW=1Z~@h}Z$qm&V+hd84TaW=L8oNs2i?{ApmnW-Y*V{;M -eS;?fXZO@vkXK?B>XxSu3A<6X*3wN=6NuY4xgt5`6=@ZH{glTBoWLfPO>#ivRfYJg7(9?_l6AMtp&65 -bQIwvEK2Z<$Rkwngx$vH6r(G1a?twR#_wf{-h+_FD@6w;bbGp={*^U7&AcrQrS9<}A< -tB*Wr6beMENMGe}{Z#C3;5Mx;OekoJ2-mJO1Uu3HUO>}WXuGd&?z^7Z?jKHHUfb8|hCZeZq{I+X`_QH -y>8(a8exBJ+1_c}Z{b`L%unP71h%Be4W%WAEyzA^mf-gDqb?M;O6P;5DTH_+gRy9W0eLlB*+_6G6DuQ -g#}q*s88PvOWWH8H^K|CF{xBZV%mT#8%^4p3uIoJprGmNdek?MB>Kii3x;@#`>$^%+zKs%O1$KvJnvT -9@8V0fTSATEGy&EV~1{h-ekB{RUYoJC+FJT}Ua;PS3<|zWS}0gTzLdkt_n3Gf4fl4C8qm64&~kgWu5E -6-2aOmVuqccpSs(UcVT_Vgx-^AsHQgvHT$w^ -gvg@JUp1JDHOl(`f~A6jdXZcSzhWf(cJrsR}pZ#*?LRffH5l*m0g?A57Lk9;`IZC2$3=qvd@&vZ+DefEwM;@ll9XShVY(0F -otL!D1VD1`XTKx=K#+j2G5aTh1`i0G2DWTJFlCr?CoHD(O^DIRvMY0RC+|)^JK!*#+dfG^qedG&xQrf9LL`d7Y!c;qTx4U| -Ce?-mNflVNiaM7;6PVb*uj3I!z*A_1J}@lo0aO&Q83gf2$h{2X68&Ak8tNfx71={-6M-p8t`_wlBjp6 -JQ%TfxM>xynU7VGQtjZU0e(+FF6=goSH~Dl)92U5AWFA$!(jDy`=g7f#Ou6);Z@5>NmuCo6H{%&SN+V -K_=}IjeZO40$^ZyfD3DB1j(jshPY6lKX47n=lHJ})S;Q3khOr7mG_F5z=7378+gX2vVnI>hc_9#Bnhf -*uL#G%oEXK`#MR)1~j1r6+HH@;A}lIvr-g#d0VoDB=O3V~JwlkYOnDCb2@y-H*bCtbsv8P%|94b<`>? -EFTe8dUchK2i}WovEt`*mB)|zmzgTDT#V{pM2EqahFMUi?K&d)O<)`Hdq@X8SFlAPFn_9gXAK22vP&- -FMQaOd0wuIp$o90SIyHiMvM0ItmWw}B?p^yJ1S{um`WT^WQ*-FhMLu|G(GWjYXR+^@+jyi6(-!Jhut9 -~Yd6n#LP*+>dR=)N#XgFmEG;VY%T_%&-b&#beT_YRlOoa9o$D+e+{*V1G9@V+I>{UL!j -Im4I{h3YsqE^*AyQpvY`$4v7AFwZH(NMfLR6J(G$5?9D_$hn2bo7rPlm&G&f;)xIZJH+n*;LWltl(M< -$VGrTG-b)t^`PbDuz_B}Tx&L-?!yg5bg%`rUYipz#&{Zg>;=75#oBQ#O;j=hjr-T2yxwbf_`RwnBF|X -{AqXE$6mEo${>csag&~pxbH!wbbgcgWn%C{omSRIcMYSkqQ0lOX9OP3bc0#+1$};>(LToANg7G8S)vA -iN@J=$E`2QnGQj*Y159+joNW;*OgKh02t<2eM^y;o@iXvcq~7HBNdI&o*1*W^hs4e+G3o7a~kIHx{}| -OVFd$C!z=<}9;Z&~gDaJ$Xlkkn*y^gdf_~Svs+^9cjpwzV-IX!A1a~->UucB2n^=R=*SdzpIV17Y6_r)H<26Pb?x -|MkjGVS9025y1Ql{4?gxbLpL##Uh6Q8ZSyaY3-q)5)ZOp;A!EFVQwjP(P^I9tm^`y-$xN?TjEzIbNcd -dHtCfZ7-0)xKvQFEAhgi^-Mm&Mq7Y?Vw>`26QEb`#OTW9<-k&a>W)s1z!7RHf$6dbr6{9n_5pBV$aYJ -MW6ZnXz{o0Lt!Uar0|5c^&xj{>xeUR*z+Zctic|!*e?U?*)a`zys)sxEDt|TxM`<;%JFTrKMu!$AsPZwH*W&}#j^~L^`PuBi)xvLU1r -T?Snw1Rwp8y -F8I)${;*oZ-r-D5aiG8AG55N}l#Ke&=orB4f(KSuvRP09j?w4gv7U_? -J%$0b+k(5{`O`=8VOvjJUFDwi`HU`2!#@9fbNP?4vGRPMvShIf%0F2J&ol9I9I1macodg>#FV`9RTruVJKrwC{>(?IIql^|vOIznWiE$9}a4_xF_Hx)OF -TmPZkUA)=31ScBIl9VbPv$TwPBm3!G)fCLz!92_oV?Rhip5ztcqlF8+~gf_9VIAbkb@s@j%GNnMW=68 -k9$%}BpfPe=4Z`}$ce{d{8hE5Xb*RbDGVv|jzVJ#jE@{S6ZA`MF-okqrK#(s@+;Gc2nM4Bz}0wODj{T -9dno*lXGr3dj)b_D6(=Gq`tWKX8#r+bXB?f^d~jK@3O~E8ttuDK)+7_=1*u3&XBB|c)@4HBDA=Yr(_E -@7fn5pfI5jWPSGWaR{>Y_Y+m^#GxBls%A<;PH=W^iJ$gG&E9em}|X`=B~Gq -SJ^vsy9X(5G)u|XoH#0$R!#@dI;RT1Zj=38p|C30+?6JjKSd`Tr-s^C+kWZ820X$?&hw{Sxi#`IIXEz -8JSuURa-e_<&Z_9qNJzsxeqp6i~$oiIX68!lIkihz##*buFt_aIZvHZ`j97oH#Gg~lW2<3*U7VI-L!= -!8<(!@3`H;MOHH6I;vs=_E#w2lN~Nqw6K7v$jW(ggAmfv@B#reLa}F$O^EMrr5YIaNAK#RO@xANpB?d0Jgu1XJmCHaftbep;-gigf&(A -Cp?K(Emh~3B&D4Zfis(D^*ZUZ+%Hdle1HD>@^t#@SdiA@099tlfyC -~DKC!B6xL72?dWP9LHr{vVSoM=c6$os|;eKC615LNkiezY1KXr%a)Xx&NcTCO8qF$nV1?d-3r&~yVyZ5el>7K67m|@ESo6!AMq1vvF<|9Cq-5#R2N_L3l -7^H~8&$crYtq5WeMeWLTpy;1E$9;hK!tC~0)%R@ToHr820+u2tlNx+RosaB)uYbxBLG?8%_|)WUmZGz --m(o%Zw;L^t2 -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 -k$T73^}3i8e7lhRp?Vgvxda@HDXDxfBkJY3d -(8hcGaqJHfK$kx&4G@PN{Vj);L3=qziSuRC_kxos3t5RD*DiLrp&M10LXoa=pJW$Y}R3-WQe-kT?Pin -MRp7aACw(TFlZbcLmkEHVJB^#SR@2zB-62F-HaXWBN=#z7?bIBL~^m-(46E7z_KTCNcc*GiYZ$>v|gf -Go-n<7X*dsU@-_~1Bl;4zB31!Ajs}I8ak{*tGH?cz+i -!FSKwBw+v3h$bfiiOWr_u_ciFuR&0`yyW+agZXyX3N-`>!gR-0@AoKbXd^3It<&Tx-zS?9^$Q-Ac=%4 -aU2(GI!?I!~LwJBEKA$0U|51{3n%aUfHE#PUH&Y+tdj$G?XvCRje3!scBO7GV9)b=j00D+g4eY>PxN?E1q`m2EZ>!kP$mfYQ -Us0+BU;;ga%~I9f0ORv_w5OMyg!IM!)JfkOr(Hhkyy4=-Iw(>E%R1aH1UQ5^WGRg^ys-GEhW?%?iTwj -?h&OP_T%S^ePpB0BfmVn6cA9DtN1ADStR?boyvwcWT7+VB6iI+(08_;Fg*O`o$yD%2If*?Z>;^1)53y -RtNyc~XG@gy;J^{;_;06}AfjjYy?4&!OnepheYfGmy#)Kq8jI6`cD(&ngr| -Y23W%&XmV&xOmSGw(6XBPzxO+HAvqMCWVd>-#o;+;&i$scl{=QEWv9EArzsP=Mz)$|buD$Kg{;+L;m( -Rob1inz~mt?Efh6*rmQ`bTw8i;@__m04b8N^!*oF6 -@zo(1!+e{mbv`!&+sOI9^r=TwBTjdRXXMMooHpf-A#w`1PfzIKoBqT_3u|$1Dd|sopgyygR<8*gI<)J+1_Z -FZ!{41+r{o;Huq?N^9!+W@NX94Y7UMos5g=k5jMf&0V{707bStX`FFul&AB0H$$FMy$}0k2DZ`iP;e* -XArr(>mKBVF79Z&tfSlQXyq=64|_rZuw!ulx|n-DlSY>9dowv-i)w*_?f|Ieqpki*%cbiYWYQ8QyP^I+#!l37)C0Aq(?@T_2 -dND8yYNQ%shOyaNJQ`myJ8EccN#ae(8+kDb3o%U_pkw*2$y>E`rwxgz74TuyOaJ}39NAQM`Z7-hmQg- -+85`JjX&XYes{p+TQ-WbNSTLtwG>gAFT0t}g+%rn+zivwnJ*12hgp!2t-Ow&;PLu=nkORA0%gFskPFL -TS>b!R!G=Iby9p{mebC;Kd)2IHCZ-1d^u(T@e8jSWw)YlbZ|RpsRpN_=F6#Il5#oz=>>l9nJB$W+pBm -YO?HfdGgogii%wibWCp|Erg^7ASTsCJzB^Yzt4Gt{POnVylo|<{Uxh9?!N!X59ZtI_^KyJMolGhp{s- -O;g*s*tYQh0`>q)da?bXg6|#P+**>c^OgTe~(=AMPn#@Q>HtJ`@F2xBFb5@BWKEiCM2{EVNH7yj`>O= -3hFo;oWn0n~r0tY!erphMn7d;ZJ>;1fITa&^mKO6^{r^^02aevAp^T^*aQzE}YT<=n2HUlkQ4L+uN9~(oMbozB} -7dqS%_NufoW-R3#&LlZ#+Xj-T*DeOHCG_4hmEBP>|BS!&X=qORv7N2Afm4e -*2v-5KW9C8#Yo%onwF_;M6`o4{G)q2pLhaDqVC1YSQtJ`5t8p~}^+qT4mf7BAEjG)gIWQpx+aqi2rEF -N5M$5-r!%)@+tz-I@A&)Qr!ooJwmMI#wC)TY=<5{PaEQi2PAaf5I+&#q(ik>dT30crHqjJ8RmJU{7VP -W*)D7Na;B^PuJ})d`3HJz#!FaLhnLv0yZCN -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 -pz$7wE(8wva8X-S$tL2#a855VH1`P -|~{5$P9ozL!s3}jn{Fa$lUl6|Ksqn4Km+k`N)a4t6Y;-gg952-zF; -7@*&qvX1>4z@Z)kPja}U1CQqt28nr3sO`nkA|a$Jy0=Y_&9%QSaYRl*bZn#(s`OSd@EUI9}fuc|!i9b9*jr%~}^RVVOK;i>e -hXOo5vpoTi$e35;H5~;>#ut}pyc&!e?W4m*?_uaH& -m(bb>=jAsbtZ2m{`u~XhwDGI8ZFq{QAai2EqJU**UcoVQj1gS#j_t@%pD=x+l!4La+o%Oaf@zL7gq`vml_+P9{3mL$XX&BEPufR;on;)oCc9O8 -A-lS7E4w95z0Len@bNvRy>i*uPgG`ab5()9bRLeA|HDHBvN7+nIXE#;pZUxk7a$HiDww%S>kqtVa>#Q -b3Vpmz$hcR>_YXj!!iR;Q+=OW-c5)4M@%|!#AJc^^j_hrnOegkj;e&XlI!OJw5m2N)`w=pTY+x3V%CP -qI8sXptriVvK4V}Qipm{a`3j{1SXp?rqze5Oab- -Y%d2FLkFP0+FJc5-m$KZs^BXm^=TDeJ$dO1rP-o6e8!Rn2v0i}$ncdR!H_GOXwPH(Syn=hJNZn4O8d> -69H7%}rf3ykOAY?LkBO)_Hty>LEso3=G}eEtiaLzX`y8;2p1f@8(PN@F(Yx3{_8gBdo_RAf0hpb#V)b -1ai?e!~UQy={nK!PINE>#wh5(bpdN7`mYfKNC0m(2AR$-^sllyl4fR^RFaIH`Amg2Et4LzUt2sK)={a -xBDaAGVuk>FBfh0?++y!jqSz^ZJy(kXV1pMixWE+V_M+AfP5^@|Mf+XJf&k9RL6TaA|NaU -ukZ1WpZv|Y%gPMX)j-2X>MtBUtcb8c_oZ74g(rfczF|*SohJr-pVW -e08mQ<1QY-O00;nWrv6&TRWc!JX>N37a&BR4FJo+JFJX0bZ)0z5aBO9CX>V ->WaCx0s-H+Qg5`Xt!!BZbrLcMB}M_*kaNEdg(<(fs3;vNh`=E}6qMkaM6wYvuHkG~myNTeihlKLTQlb -r9F-wc^7%ihZ)uZcNPLt6R7lqR$@vX<8)oceUrWSq3J4b&kQA8M>#21_noFsy#c -VX*$i@F9 -~Q$Zt~Im_W%0KadW+a-tg7wd_=lNkH|fir61`!%E8m2(tTk_tl#}K7Xj*e)#wO`@7F~8G#Id9D*>H-Y -TaweiVar5m&{J5;1ZLd8CSCDriqk6|;g`#WXt2>w`;!;1XP%XQ)~ApAR2@`;-*~Ty^!#TRfB%v+CAQQ -bFw35#=W4ye)OxaFZ9CSF7zn0R#}Cd_v?zZ3}x6B00#cJEhRzwHLW@d~V4r$Cf?UtcB7Ld-i;%lvH!1 -SfRM$^L#y%;<=MOBnTSUaBcD=vE3=3J8)vNUHAwLlp{u!pl7xs+9>iro3ucNf9x^LRcjDrh%<1|AL19 -bBDIG788N^bNs=QEVC$EOlEH?MDPu-l+r$`(%rj_N&1pkws==n46KmR=m@5hRk_YTosF9A&sm7l`j!I?_pV6kLr_rvi05xzuohKt*+Tu$f6oZ*Vb5{FeR)iqo24r<=HJ1Ns1iaKZ -0x$#WNI|Ez`ALezdVoyfUXw>Jg|E(C-R5=0$S0sWG{|84b3x(Sg|HRDb-S2g{lYDL6RS8ag?H{>5|X0 -Z^pGHZ-VS;g;eLosEBPgH%b;T-Kje$BGFzaKVhwI08*QrNV&BkOipqND+`{>UjM8fU?br0dP9-^!d}9 -vLpxi3wU3DAtt1B)L2r#9>|sOjeYjQG5oX&0TK^S&5mp1rG&PP0Ro&S9KJWhu|Pxi_Kb?JB(D{nT875 -A)SS?G>}a8RvI(LaFk4%iHP)UezinXO{4W$dcvlDDwjUx~HB|; -hb+q$3b%1?jX-+#RO^r`yi-TV8gO`(PSI9G@G$>Br=wY0SC1yx5~1T}Hp&J?gyHBuZa_y)Y(0)#~wyE -0+CT|}S?lMB=?k)0hI9=Yr^;Hib$1804QAa-`6kCfZ!POu$moqRCm+4ucyofVs(w^rfjxF*Sp3>|!^$ -f%>{&r9;?O!Z%#rFI{@{T#}?7rdP@%AbdkJ&XZdw4Iq1_@x(!-$vrT#Kq!y6|qD>=6KwM(VLMYe`LXC -TU>+tuC!qM3|uP9#i2gteKp!jE8EFgPS?_Io0xP(rNX40T)NVIYa<_viB4V`L{er~ -5?siN34&?9^Cs6o-M#8TcT}Ir}60*<%4tfz#MjTS`h!X(mmgMIm!79saJ4Oh1BMj85G0O^q%o?)x2cQ -H-O^3EE1K-`OZ-%C#$UpEs -RKZjn0T&~UuwfKt;-(P=yV>&Nz-00{oNu&6lWd?E1-a2H8_o`%OVCJn|M5~Kx--|J*%*HRI+Pz0+qUS -X_Qavcu4X?LychqW->3%z7Ld$<`wrZ2^fBz^z1z$G%t56f4# -N3Z(xAgdf%+@Nx)7yuo<4XHgCjbyRh#(!^OFO?fC~p6GnFzMtN|M9fddF?Xc6hMgx7ueZf<%;UF{bkQ -JmOJ@?9Kgc!KPBKPQ;1y|4T;sT6@cMjjNVkY0i!#;bYJe(Uo1L-6av0>CFR=dmm%Du*{CdON*e47WnJ -Hs1*8_a$oufwy@=*i{v_Bt#$ZPJGg4-qLa#DK8=HbAJQFkE!QUDPtS`DzqGSP`bqpTef?p9_VxDe+H^-knz=E~<=4_Z&>)&BcI+B(ogEr2xAEX;6AIs|F{e<9({s1`JCS%hf8v_CW@?#6qz; -6xk(X(C2kWu0Y$8A{J+S_@b)Os_J7=!Kk^&p*$`@OamgpORMgfjBn74=2E9jA*?E64!S*Xjfep%Y{{* -_Pa5{3FK3efCu*8Q+?k51ADjeql=Uorpg>?UjXHUgI?iksjmTH_Ss9W}fxyX8EVxy1F4)rWu0zZ}mPA -*4Fv%U&M#f@*ypIZxUDq_GhX;SK~IL-D7ZrQ*krsK0?k+y);cDi;=&#@aj=G2ErP6+Oru>=YCP4<;k{ -4JO)7n|kRm6h1n?LLPHYNm#_$P{g;iK7MI(*$LFWa`_kC;pRmT!%caPQ(UGgpV3XuU)~`9)7zetGb?5 -~^5Ar6@zPyzywO~vAdB%SC|)>g_JiZ1;V%j$*BI$*#A$2-CqQk0yO*G5&!PLg3uz34S0pN9~ -b8<_LV#1_PmRIBrSq~pe5R7Ba1pxTCbbtfA4Qdy)AEY1$wA($jan!W;pK|T@VBp!b*0jw_B0MEJ}p6Q -%0{wCL)*ktBYKv(gr~=8m)}pGR{{;UKq(aQ`=0N9Pp{mMXq#eN278r(d)HJ*S()%q|=qES9O+W!dO}V -w$+>6*RQz|kz9)CvnOAjsffQ9Ng=J@%l8?GR6Ca6yZnUI(lhVZt) -n;0o9+ZcR10SyB}Fir&=yyWsFukYZOFa7GT>SdlD!{DT1nuBzwTSxTH?M&HI7DG -$v`bw@uk>vJgnPl&@*@_%!j-j>CY+=DyENz8+JYsmR@9%H#r|$Lc{`&p=_UVCNJk4*e_|^QLw|1Wvzb -tM)EZR%&=l73K=Qr1j_k3~t$lu*QEv}}c$q*CrC6ZotUGf(qE~Vp9mOrkI(Z;V_DX~tq%~f=#vex=M# -id{ygtWE1koiuV&yZXyqu$<~k@6ut3`?ICD -R0l7N{w&jnJccjc_$^PLS(z6ZuYBz&zOAxRg={Tzo*BKKvXoSxsDDYv)A7@|LdIiNo7N+KLj;hKHv7k -YChz51+1%{OFUrko>HD#+6TK7(r)8{is8X!SXsZSDO^naV$GJ*ve!DeSyA12E7*`Y4+tWWwi^|T|5(Z`XgKDRC-?Mu|O5Q -9uslQHvr&17|)!Xx*$YtM*nn|5&R;f{3*vh=dq4B??P3Im{l*$c3;wcvp(y44*R!L9%M>?n$sth-C1T -gx=3QW6)Up~%Qee4%9g*`8tFT$k7Cg+@KVi?bglI;5}C?fI3THe4cef~G}0J}&f;v9hC>he6X6}VY+> -4^Hfdkr91Q0hiBN_DTk%_e7L@h -Q%5{?hk1N^jS&2#^(442ML|9U!fR;1tZx`4j}P|nOeCGg;4zB1$vT{?62Y8U+z1QD -HskW-lm)5g4rS1iJi`94%uw2zHGU=Vu#a%EPfmzPBaIA=P^c{zI-hT|L~g-|^d&_rECWB^8Ie&i^>h; -XEEAp<+od#Tgz?Qkm;ABHH54*}?*P|;a%Ors1iskcTtT8oEhPCDQ#g@l<5;BbeJnqY~sW%|%(Ah=%CF?R|BMK+%D3LzLE|tRtUBa_rAcNw$Ne{e -HdU>(7B=lFA)yb{X;&L?{Vh9K5l?lwjx|2x9gnAejUR_PF=g$Jces@qkuNC?9O#UxKGI;-XgY#Wn*5>zzvU#jq ->GFEQF7}lEm@Z7ok()d0z`VrU9yGQ>QTfic(o(HR)lCnF%~9AJwv>@55Vox&icA5FDPbg4+pu#eGw^# -{n^-?%@MTFwQ+aX+u`V6|5CC;>GqiA_r_vQ7!e2Y+vmctC;p+P0>8Ha0+XO<#0*AQ92PD2*%D>3{QVS -E$sRuGemVd((Zjr2<#mffTMYP&T(d?d{t8o_^nZ~2b}HX^I)tVg9gQci7YN+Z> -{bSYRY7IXc2mGRc4@n~<-dpV6ZS!}ZDEmx(RM1=WcW5^DIrDa3d6D?Vs+!Mv1)cyjp3z*tD4q%Yu>pQ -h-$+n!bTJ!RH$Vl$+jI)SO*hJ)%vd80^ug`=xa%3QaBUKGe9C6;2rWXG)xsd}&_eBDXM -!o0ep{WEbVRAAltLQ_oE7nA4`+YoD;VoVzGQLn@ZVvrulx={r9OO+(Du*V;6*>s_hK`tZz!F3|27n^L -k8z_2}g4j*tu)Q(GS;S_b-p>m2lq$5O}m`U{AJE*tMUUP6(SC@WGveSCyw?fJM_Y|TBxc}OzV-LFu8a -_ahmM;auqf1N`jrJCs%~&9cTnAlx!>}ySX+ZRP?EE-me|8c9z-kqYT9KrlOpnp$L^{!CR^hG|8e{9^q -gi)0*sYyv+OsYzy-~<9l3g%LAw-6|WLCjqVQ}~%lAYLFE>kkvn6IRuZwcPA@=60?wOqkX*q_|~v5x=z -=EM0v9{Ba*{Nm>Niod%)e|);Xei-QRA^syQ4CbPpFzJE#=m{R-U@M&@dPgNneGli9jsPnOoUu1HWW52 -FLjgkz|EVdCYJ)a&e1x${Z;I7%x2E}aenxJ8dYbL?4PqmFv!D(Z%}@F^XEu|OS}C}nSRo4ZLZK~|)9#mAv5Q!Ex+?OEX|t+ -pr|`di^qNG_?@R8J$av2#i^R`Y>*%L>i(rYH_2_+(LDCPmjST3cG&^22nv)Hn&_vhm~j_-}T?;504N0{PJ;ryYNUW-yOs@JSS -<-{M}v2o?HYT`Y6zAb>;|M%$LU!ql$weM% -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 -?0!Ucz2J~hSaoL9+)oe9#aHF;U({ -91YP9N4B}g2Vhtjr{%YOWhV6V@j1#+4Bz3^mh*aYW`N3o)|(@1TzVk28V@`cW!-!VmymWNY`3-GKF|l -Pxn18EZ_Y1XAN_RlASUrsTHSCMDRzYT#?P^YS7sGOE$~o=O5bWkWx2jysI2j^H!A_ph@?e&tML#O8Er -);&S?Fdw^8Or5PSm0rr~yRK(?^w!GH9A@f<`BZ^xzs<6Sk85J+j)86wJ$l0X;%xsmLXuq)q@xrAX*n6 -veN`)HFxB2>ga1#eY|HxJ4d;`|;y59TEPMsX;u#g^akAB8Rrf(<3jC!k7)s4@_?CAd6zJd%*lebK>_T -hTcNZK-8WKzn8s~cl5qXDmgWw!#=u-%jxAqw%Hg)#`yoA9YMMg@08 -R3sQY(T=Kcah6*VmM*f7s)I&gB0SPbjh`mg0;|9*)21=ev+=D25ArZ#TqEff2MYt-+w_38PMPM2ulDZ -|NS3ZLmNtRPf&tX7_HM(_)iJaahF|vwtpgUA+yvAZ;|i6Zhu#u}Zd@Y@ZKNWq#g?3e7*KRJrP+wR3ui -?JC&{|FMc-!hEuLKy0e$xcqAgav?S?3s`5uNjTCKV9bX`?walzE3dCzHA2~Ix@1Y~OP0!KpBc4wQ`o|_5LX9PwP_!8P!1(y`gz7a_X>)&TwVV_$*zS4##N=T-O`-s&s3wqSM^K47l^U19faXp=a)q#)Dq9s+HZXg&sp@odq(G(- -BY9*1S5}Ay(ot*2*VGA8wwuUt{A)wR-o#7j{hcS8-rh+%U9HfZcKV=T8;DF*)%Yr=SOW*FqkUY6`hS# --Hj1%X(Wua}e#10I!81@3)YqR&bWPoI^U_j{&bN6z<{CCrvcuU=|;`dEXOLshuOi}c$!vLvzAqo0k -v$aSN;zFob>EP_c2^if&sQ9GQl|&z< -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 -YZq8Z!>z?py;_CbVR$6cL1lIB*r%F0 -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 -`F}8^%9|visvwH-HWnVbjH!Pra7$%^OhT1 ->$if3ug!s~x)Od_{yImx4ZX~aD3{)J3~L&=^vkv*rVCx5;(f_`em(V!LnTH3IDF> -C4%IcHa4ltQZ}54;bt4pLp8W4y2wtIxG+j1vxjsg*ydrtRv~*>+(z{U3<)r%3Vwq%X6+<_uf0A-pRjU -KOo*QS>-SS5Y74Op`j0<^3l{p)vphg7J-1uIOF+T>HC>JSV20W6pa#E4$MsLxIq}3)a9tM^D1ejudt( -^&>O_2l#@TsCtBa>j-_iZCJKoY9 -B(kZr-Z{jfij))G{=d~^A -Y5nXCyr&x=fQl}8G)hmch|wy?)(S!gi47BUJW#Orb{=+HoETnqF818Ot3d^ur7?hIz|JN+)$N_xsJfqn`I5jaYV`JcyT?vOx3(26ptlt;gXlIspE}Wl5H$Q;8@x@(~V(th -vJ?7Sx2X{#gx!+WpyX$W~EdI7xGRfMo5_DuDsUwy1^{@2T)4`1QY-O00;nWrv6&p(Y%5b1pom53IG5Z -0001RX>c!JX>N37a&BR4FJo+JFKuCIZZ2?neO7I6+cpsXu3y0^7$y(4f_@F)0Y%$k0fMgRI_yIb2()y -PxX`3PQg*zT|Gwu)$%@l-0g_1My*~Hco$@^Y3K^{I3Q*)igvDW}Q!J_~4v%uSt(V>9b%%gQEm`mN8febUUL~b -`OlGgGmY}K9i8l`5xWzI -`Gb#E0)EN)Qhe!2u;X`U`5?a(?Q+iQEL54JV*pKI1#z -igz%j89ujR!4m9Mb!~wxDyd@`pix`?lv9q;sqhanuDP=Z;vo%2-c1PpKfz2Yhh>-EJB~D54Jq(eSJL@ -D1D1%BMYb_*4(}ytlo9k;X+;AFXsq8hGYac_r2G*|XM%B>u)yNQUAh+R4Z%%-dgXo;xmoW6Dm&n%Ef#rN}N8N~{b&sM2xQ3*Qbpmi)U^mH0N1}!nthfkhqyaxACh*e>E=1sdu}1=uY -jGs;XzVdrtI2V>CkICbUkD0+kJ7{vDs!-p#0bO2f^jKsTY-E5EaCt(CgCh=ZU)#U4s4z%9#ok^n+-ww -qDbg2!_l$k4#4n0a#q$oR}*nC#Zm6z#4qw-Rl^jfflR|@K|R)_ClGvLE4)T}#(^f%M9X4f2pDT^kE{z -No+Dg=qZd6x^TOW(0!Plk!ALDV-DFpI;vY=3Qo8Pd{=Ls3n6DWgB!ol17Cq?vFo{O9CH_`>+l-ouNuE -!*-L`pE#nDyet7!kD4V20ivx!xrCzW8HNiSPWn7Jd?z*Z}SEGH7gc%g=5*mNo0dTzzkfr5L3uNpvWPP -56#sc3a-UKnBJbqZr5EbH%x%c;B{i2)iMkC~c)b=FXu=*T$S2(5g$_rX1ge~^|Tz{USoV)GL;!%fBjs -Wzg<(eY(E2<(PCg3B#Mst41i{PEHT4>ODQ -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 -$nGV?)Ds5V7P2oFte9h -U&Yi(I^zj8dK_Sg`hUGd?*~S=UT>2o|2J7;AUd@w%VK^OBI-EMX9;ikQZbpo>BAfE3QikS$W6@CJF6h -wy8ez2vu)a4?TK97VRIv~ldV9bUA9Bzwo|TyJski}50n8jQoa-XBLP`#RV>!jx8L5OjMByD2ZnvVlj{ -0aeUYlNgCrZcx#Y0WrGxEN!mWZk@us3l{F7rPF%G=W$F8VG8H$&s&Ap{!$)whCH0b2JTp@HgcW24tVu -GC-sZLu++pP%l%gavX9)YD0;W%_}e?y+>;3?bbhi9V~-M*%5}4(l(kO6v^s=PLflqZI8i%`y}Kh%m;;R)*g`r{aVnEa))7i%cfqlHr^AzvR}PsM@6aWAK2mo!S{#xtoA!B|8002A^001HY003}la4% -nJZggdGZeeUMV{BFa%FRKUt(c$b1ras#aK&k+%^!t>sJu!Me8U+8X!FwP!DyRG(Z{@_M(@~LZ -GFQmkpOxNXpxdO1HLZf8AZ*`AWe62Q8fusnI*PokZbfON< -T?oSQAf31UrNrQ>}F^5x(rI*fB;2}R4I9D@adX^e?pmCX^ssOH(|jA<=oAB$LutolJ&8Hzo?4b^oBKA -aSNi7DpRc7WsQ9a3PRJgvwP!z369#Waa>v$D~C|+VRL4 -m^Ykp0ajpk=e(ZU@BJw+K^f_26_5kxeW`oRV19wf&>$A>Kw&R6$J&3z4Go*)IZCifc5Yi!t;2ocH+LT -ptbStxLg~`-9U9#3|15n>(>a^qdn63i(3bOW~!MN5d%ZHyh7Dqus<6dH(aj4!39O#=7tAd8dnN4H((5 -Kgo0Aon7ykCVD4ZUJ)tykz5055m~qyF*_jWJ#D!mds#siN6xaEAg7(#oxpw!vYew`HAcuJF%vcBCJdG -x>d*z#A}2O8;FB!NwzxFu=TBju#G4R=tO8vs)>tfT?+n!+$CCSal}>a_*p$Sa2|%Z{B|`pV_G&hJ%H( -7=dV)raDRX9yC%9+;*C&EzLpcxDcL_*e&*r92E8DmDKC;f)Q8VC`-WUu@F}8AH)QQTKfD2=B*nuc&9M -L<>*;=(Q32FD(m+rhTsmYm-VoDU$DA^Sas6qo4mMVYca$&73ScS{_Xws@Gtaylgj6Y{2nbo0WMV^tDt -JGyM@4oS6=4u{;p>Z^1ADJ_!H9`JaHd(d~z~~KXn+of1;!RBnt!}9(A)l*p`VY+P`n7|$S}IcRTJh1c -x-V6e92{-3+@y*8QA0S0q+7OHJ#PInKv*QkCDxxJPph!YGX`t?na@dAE?s@>%l?T4E()h8_aLAh45lz -X$*W}-Fdkj%KSkV?x34=6(Z12)n0WYOh@Oo6pVFW!?~hnx$<5FuN3Txj59D`N*)!Rm0XN^ --~51yxgW%QQp8y#zf$vn&vYr_c9DH$VK)$xbTo$ZDM-NXGh9(EnVI%OM9Ug-C<>NXgtK -@Z>DbA12i!kTzcXicvz!L%!kf2VcK$4=w4(qZeLX;aD8WSx?vjsuj*o!4_uWKHfmHTNIHsIM3;u6e6> -ff9Zlda@|DnB*4@SfjIdkfN(laM$kQ;HddcUSuy{dHtu|^`1KO9;vy(!e==dDri57=fv0$Y_Mr{#52e -046fxqt`i77!Laa|ZJ@ys3uEN%_vcEibsto;T%iEAet1vz&+QeC~Ae`>=4tvOs>eLTc-> -jj^dOz*){^O#N&TB=tQ63(q!#3u*{hEsB5nr?s%XW3{l)V;xy;rjW_PvZ9_txIDj>vfa2>Dz#DY5+|Y -3zNd>Qid}hcJMA(Ske$At)t8JyWb2+Nu)&WuGYNn~}-h!UDpYRHi-tw<(FAu2-iGi -}tYU@RjyG`*t-_AM_mCP_+5y5l*o8W>wVuSi`KEM-bj$@7NrQz>MHjAuUGiKa(hFxe1XE6sDlD@E>Ek -qHTtWUPcoR-~M%6=78x&F1s@Y_^x;M3Q7*SG8nG((+w*nN*hQ4Qj1pc{Ocpecb~VqlgcbMg!1wj8lmZ3u4R&pddoD$R=kuy -i73`PE5T6vp(kTkj-e5{g`i6f(RH; -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 -GH$&yg=}oc6nyt -q-))Bh$>f7?y^vh=whReA`^&&5Hc-D8tpqIhp=HZ*yg=`7op6&rB0Lr1^3*wi3$Ddd1x+F#j!>dCx=IM_}y6~kiR&8_;P*8C@4n!` -QXHXoKf`Gt~E}=*9|WaIVK|Rx)g=0eU$q83C$hWHol*RS#jnuzx)5Qm*-Kx17U$DTWQpVTpG01s0jcl -9cD&X12knQO9N5~0#gRBLymZa_jp{b9SCb$L|+tUY*AB-PqiZy2s}`RSEx6NU0%Juo&_#45eZfwqdFC -kAeXjOda&Bk79Mk?3I -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 -Oth8dxXjR_TOI4UH*|AKFsgG1Mv77z -d|g03mj3FI`8q0TBbdgkqM42Qm8S$C3w8j%WH;J1v23)lZIXpSZvx?PWzrdiAt-{q(uo*tr`7hz^~c5 -womrP@EE)-(|}~)=CV511Y$*SP05YJQ+Yg*c+E&^aPp8+@A$*Nf*rY -;2qI%SBe-jyncm|+94EyhxMWp7q?`qtT@n29&0|XQR000O8ZKnQOX6zHCjbBdaA|NaUu -kZ1WpZv|Y%gPPZEaz0WOFZLZ*FF3XLWL6bZKvHE^v9h8f$Ocy7jw%1*f8j49L3eJ{s2-=+d+c*4q|Mb -N9hB1lgi&t+J$%QW7J}f8RNWq(thGG`;8ow6;W^_nUMo$|H-Sw5c1xqljh4suVSgi=wRKIx7n~o9TCX -x!-5S-aMAle3Q+#66KCdIZF{;S=Ci6Bsa+Zi1UW)wfa;64zu#6I0fuH&hvPi^O@RFWmOnuMa@MK=ix5 -T!fh;hv@4ItxJV4-xq6%ek-&T-0Vv_pfLwg}^65*!uCC9|FRrdeAmum4FY=f`!F@pSV$1BpZq4qheE`%t>*zTszav5qf7y!V|i#me0(;&x8?#zDV6|A)n*s -aKF9+gd9HT7(UXt`i3W9ASxexP@L6czCioMS$l&31V#rK=5X3SWQDmn635dGz^vT*P}0Sa)&G(182A+ -`?B!U*U}QUNu{7X1P>v7A((Xy<{KYe^wtji+6Q)!(C|Rmx3wpf*q)OCM&+n(u^l_jfN)~pt*qmX_E_2 -AXx$I(LT_=0s<8**?X=)ZqSPvNMG)X~NiumTn>=A#&XN*>n?o!UFD3%`>R0T>kQlh6+ou|HWgs#IsR?QZ -TYZa17y6pahx5kd^`4HuZp9Qr;GM87E<#9XZ4Xd;LoJagP9tVFe)IMZ(P9vdB+Fhp-w{UQ1HD`DMzAv -f!*t*<#U1E&}+}VS&nBEF?rGi-Af7zQB_JsNH0uZsME}@B*I7;s}91>dBG;R!Eg8Lo)EFqXv_=;svWqf<=B6neB;ifM>-`UWaYe)0b>tlu%Fv|4);_=MAt7x#g? -SzTZep3ad6t;NMG3OKklsB3~#6`)xz<|HPK{Vxhv8{i;3}Ra_rn0iR}107HzA1=uuz900H`rl-Lu)}K -d-Lflm=D4WGe5~)4Xmpo7BZ1EfW2$o*9`0#bOJJLQGb^w06gRmOCd4cmDYG%FiMwif$Ac=VrmiPdS9A -Sm5xGM*qS1Xq`(Z5hu1J44l!VhlpGb$ov*Ag<Cck%Y}>!&Zw8zQm;;wBauwi6QlAG#RGM -CDDE@Fa8r=N*sp+4D80^V*o^)DraokEt)JAsQxh_%6=nm=e8fR1~J!ldSo1aSMWtr2x!C-k%c?1ASCH -T===dQI7dJQekEtl?oSru2j252z1aVviB20QfDoo9>V{%mNMFlvZ?%6fvbmkmopBKbaF7;F|N9tkz~7 -iEo;%W;On*O)dNbqxVG5@lv_5NF1rX0!$QICUELytw^SV9_egafu0|-Um|Z256=+o>m1%k#AupSK*s^ -w=M!Q3HJEY#BX!iC|<~2L%VGJ83TZeyTn}HY)omNFtkTwI|56LAn`isRLo?0!EzM(7clxSH5>L5|Ijb -xeq1$)b(oj*eVroFLU(iskkVS>cZ^{WlNgLc-!w{(P6y?)4X&=^U}A34V^PZMMQOF75nTyX)t%`xoxK -Y;|y3;$hapbnTZc;Z{6=W}=e+jUT -ax-3=S14{}AEJ5(PF{VW$iN5_bnDa&G^eO5kVbIYyQ3TorGRwcc@UT*$BrqOTBX-1oq`d&DZpE?&@vT -a%Bl%ViLgZ;PHOClE2Z-Qyp4sS3pI?p)#z`H{fAF~d-I_Kv -D&O@psUNT7ad4j3?r`Dx-DHo->?VA-l=j;^j9{ -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 -=W4D0-}DU -9KG(ywCCVu$MaRTbRPLx6N7;}%9`~_2rhE;F*xLs51;wSnjBrxUhgkSqza(Ih5{E(>{rU}F5KdIKI2n -T?bNGhzA~$y_2Lq92Pn4+G#h_aFUZcDbmNwAK`!xU5B^x~CXTjFJ8c;Mm=RFG -jP0#Y|-Wr+!3h4VTpeXcwRI*^KwLq86ymC7zap>%0hfC^vrT3S^McaW&rYPG6cETxG|>%DXqrrBDDNR -lM_`Gv_IFhMFbM5n_9;d@MVt|6nU0Uc?_5LtMB{e$iqhe81vGE#U#O5TH>-^57t_9zu=>CaQ2*S+9-W -OG%bKkWhUW5EsCA>1^TP0bX1y35d;_vn%{vi8EJ{q_D6Qx;%U&b} -;_#&`s=U6GhwJY3-2%k4aq1ki4?^I -3h!1?52ij#XVOt6Z6>bN}kCuq4!~GO)4XuX_6u+qjZ8oM?<*0?KsP?woT!-a$I|Zg7&_Nk`jBHL^)F* -4Bg$h=mZ< -|nC`Y;5AT|GB%w|b4T@Q8I}yD4G?Q4J%G7LCj$Aq|Wf_Ar#bmKhp2>WNU5sYvoUOOBG_JEs5#luXj3! -O>Y3(`V6}^^I=Blgakv(peOOq1&9XO-&{T-1h`(*8#dn?kpb;4yrd+6I(3RXAs92jzHOs;o7Fwp~jJ$ -ckAt>wrSA5<032(c?MrnSR;PNaJ%Zp5g%#Q?MU0EihFSyW!i-DX)p$4y6>%n9yo1A^?Dt3SM*Eg6!=3 -uCYI%byhgX2w@W-a3!=$k!Yyn|7lPmcJm?#qojt(Sg2PCqHX$IDmv!Aic19}}r9qh7RnzoS#)D5$>%& -rujZfv$KBGjuQe7Qt4}YkRnB9@4{ESsZbzU -;1Yd3muB*zk;8=m3l}Ai}(iUuVrH1g1QBkBOroEu*)Y!YXf`FY3Qhyy3>PU6^&C@V11diZDZel;~Yt0 -0gEG72({TCjapfF;!V1Sk$CPcSk5CP;c5JQi%ex#*VNd3Gj~R-g(=P_ER6Rj{V0-#Br&LKG5M{dX6nD -Hu}8?Rn%Oyh%B?2SVr9E%98t>q}05B*rQ5$T9jVocki6#IDdYOJZ)0Zs|%R5zd3L0jdlxA%nh!=?X-c -c@b_TooYSgmQ3k6*J#+^9O_^%9WzXqj`+kt=>zNL(&^77!JwZWjm=XEWf60n~KR=jD_quGV_rOf3$+e|h@v=p`QHm;s335Y&^A-4gCefUXU*6o6FOFrMCGk=lM?5W^u)K}#XnVO70uZ7&@@iv -hqp8WO?rJ^%r3E!h0;f3?^Bj!=joFV{vUv;20cC{g@HDm2Jbo6Sr!X^?!Tzig0JDtR{sM~O9KQH0000 -80BxrJTBDHp_(TK%0BH;W03iSX0B~t=FJEbHbY*gGVQepBZ*6U1Ze(*WX>N0HWn*+MaCxOx-*4MC5Pt -VxaZoW#0vus`HQ*In+5sJgWQa6oI&E -{Zq3%YY1Hx@w*%73}+(GEIy>IfG#zpK+`kawah*$b*#o3i88GxzSwmYZiZHo|K>@LoEG>O~l>T2$`R+ -KB2H4LT!~V_&RRQ<$h@WMHh&im{m!!lO}KmX#C{S+zBlL`ZuDSUz!!tkP}ERUNHvho9F>tmCe?WK?6S -@BZ`c?fd)tHIo({uesw9`UgdPC#*B#srRXEp}}j7NlpkUZAp1{+(k#$)lxjM?zvg5ZZPSMOi7aMkvYv -sw%Pc(qk(-SKuKBr9b`FUinqvEvDs`Y$*qMC%63?}u`&7hS?o4Ie_57=&k+Rfh)`)tLpjg~;PB7M9kq -g4iQv248C~}k62M@{Pbm27{_Z0jv}w63lk@OJG)fy}2EX=#K8Z3fl>8^tV^H^&H%Nk*48Tw1zk!Ew{* -TEKQ4x4uvr&;-l!pRteuIx%;cher@ZysA<8ii}4Dy5s>D}B&U2&Oh7h+y{=svT~=j1h+oAiMN5pylaCDB`k|NV5nJ7j3zO -7gK7*k9VfGXfEouZlA3v{&&Z+J0(w`d)M_Yl><|S)H@&c#tMgJ_Cs||Tcq?YW&R!I#hGE)o2I0IXxwP -Y8l(Fz|7E-MT}(tYy$S@}!WdyU?%XYK)6g#;@PU@aW1uKK?untZw5lUk&nzVu6Cc%6rZpify^^zy6M> -~SE=uZW~*L=i~dn1bN94W1mI0x9IZOR$FD?IN?2fTheVj3E2*^U(cwM(P -Cties(_2aH(cOJ%o{k;{DF7Mdfn;qwwn~G}2QlH1zSz&8)NJXyx?f#nR#pe7bvg2aY%PEm#_6i*FO)d --^ur#M~d0{m_c3H3nU8RJuipWXmE3x_Qy=;uZyxd6cKz)*t!D-ON4lajCoY3$1=|u -Hw-5j33bp(xLxVQ8^Rg7HU5;9w74A(yYLlh_o<)#Qhf1WUzOCnX5Js<+^>UhoyIu__cNcv87jaEMQ2I -SfNj{L6iJ(U*dfzGiP@L4*A;W3zJx+1Ndk$(95+T-o}RJU57`9F9)rtNa~$S0e5AFwJ7d-Rwx=R-9?+ -9lV#BKwU@aVflBpfGT$o2SqdWS=o5z8~;;sS~Zm8btp01XEtp(pMP=QHay_^4klV6Q__Vkhq3KV!ke% -6BoRCG%y|F6foukV-QIdvn2%*2T(iLG6M9L_W=bV<6ApigXaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZR -ZgX^DY-}!YdCfg*bKEwP-}Ni7bh*UYouT5?CAX#8Nu6UW8}E5-m1QTlrKlJViIEs{$O#{k=e)k(e*FM -IfCNVmyY_A>6>CVK(dce8`UT9ot~Mf$*InDyGLA*Q*;aKclCrGYq|K|cIXFmHO{$E9< -1KYV*C(e>GQKn-}I=-pYeD4p?p5ZBjQ<4|n$N^z_Zi*;)MK$;;EZ_$etm*>KZs(q0w$O1C_PuLtT)zK -!B*S!PuoZIkqUa+#Nx5f0es)@f2!Wu7KQ{@*e#lZ_nrtmW@8R@+4KLn^m4W8M2jc?~_Y)4KYwd-vw0r -*EEK>BieSFWZ>Na_~r;B$u)-cKT%_i*;OeZI-k$i_ -Mr^9AE2Oxox*`)RO)i;iMdW0OKZauc#{k2G*`=$o48}4;zsbwM?>|L2E9m8Xgwa4U8`m?0qF=fm4+qWZJb-B=st9>!jX^w92 -HYN*J$cp^sSY)GQcx50;S@B;o;(Th?V#M85fmN208LPmaY8|Mt}ri*cq?nm|L%o}UrRXZXb1w8)J-ZL -*4GWTIizx6pky$$J^MNqs5Xc)M$_KtLw?f+)s7`%O}2x^s9S;PVD#G_U1GmhCfsKd0x&n>a0GQg+++e$?Nb>YLbeOG3VhJGgmJurCWs>gVMz- -A+WII-pKfz+s%n>UWv+`cNdj9%nul#ZIbzaD3*R*n@yR@K(;+r~e6HribmU}77or~%c_XLY4FX4%w7u -;i$H>pbjo~iu6&5B(N!Vk(_tw32exNia${GAqgzJ(2zoBXhW|iaX4Orj7 -IjjWalVf0u8fl=uB%GX9Oo=i(QUhSFNuLzTSQ4urRa?SN>gM2}Al8Xx&&DvOJw1+R2L}gfku;5#SEZx -EvErN^6V^cx*zzsdegO4|XdfTYyM;L2oxTtus^bc`W>`=e>|@PLbPbAC-9s16R;IvHV0)xG|Mct(IsY -4wU37I}p1%Z5SC}`M;!7?E4PYk)&>%RXsk%DFoin|Hg$&JDs9+mTil!1xw?#rWdbuNJMEtREG%AFmQMBrX@{`YWHYe;g*MBebCf6@4z2kEV}Jwoq%TqH7Z3Pzubt==OBs(vKLEy5#wh2#WaTzDG6(fbu}zfbQrHCi&>6A5i -)nda6<%o5sQM)nkH->XvqWtR1PIY=cv54}R7~iR-DDu@@z@{U5Q@O3Sivq}0_$8OsVFG0lE_v4rifiw&5l9(Z85zzQfulh!$q(Lw`vG_;-@W%mNsfN+`e;`4w~>ydmGm?QjO#k}SO6?Hrz -2@ik$RdeOnU%k5ou5mKaaV%<0z<>Q!FpqUm4hJjwL4#uC0hN0g!~SW~BWv)~5u{BCRosGZNRalG#yyfwU2n=F!4yCGRmT(x7sCW- -sU(gEhM1RtbK!Lw6e8r&5oEGZ~N`rsPfo-=qE} -kpz3TBIV|p4x_mnIR*PL`l5mhhE6IMwNw=J$}P2tr`p_2Q3P+Kam8$13)`61)jOi0&UH+JQ(^Y$%TJnYL&Uz#c;?{roQrn)SUup2Bn9fzp4~Ofb>1V3%Q`(~l3F7UgUhR6GIdHU_HMBP8CH -t#H2fYpSkoV3ml|r*D7!Ja1A@-=q>hXX42U5R0ck-Dlzi2{w%CJrKaQ!0tEOflAHGq~6_IC4qVLz^+^ -JG9TDkZ6Wa$)ot8==;{*opC(=<^}R>NcnO7dnEQ_IqkIbW7E3!?NYxGeCBCeyZtIGgy+slPYU7Fl;YQ -^^_4JzN_n5txb?XlJ)!f2vcAsva9nj?Z0q@GdN+4BV5U+mu;U^1Y6uT9!*%6gJ|kapM^mpqq=r8llZBV9Oz_M2ZbaaYELK -X9dZufkbocR#Lr=1t-VL$8I{7=PPCnZzL6jt_U1^DFuL>B+fY9jgU -B&JQo@-4DDO6bhHl^QG?+icH7DH1yz!(pkgf#kwO?3&M9c5(&}UuNDCZxKKZY=CvRRoefjbqi@!g8_4 -KbN&slOYv=7z~${h$F2>sJin0hQhLFT4%rU_PonjhyU5JI2t6h>9i3)rf`rOh!88d(}%lN2j9s#rxX? -6N9Q{jTys-iBI8RC?4AoS_z+Io<7+CR&fvL+P4q=?dfCrdDKp)OKbs0e4z=AjN1%B*h)vnSOY8^-Pr+ -h1Sv-#^$ipqOBGxKC?iJCGf6m?&l8Z0XArFUiBD^Ts~L|ZH$$nb$VqTH$XHA4lc_N_F3S>M -C-cdQtV=PY@6j0eVj9*PHK?9~pYkH(O=d#n=(cM@rL0Ni)$8FpT;BqXJZc`)G%aNSB4cX(D8PyIif%4MCcsDj#p2#w`8fG=&_f^!-y1sLgsthS_>J34+suuM9Hebf5UMjS9 -gpSEq)$}^`<=*)p|X8rQuW1squlwWjyLe%UX5K8)$YbVm2P7yPY488hSR(Q5*aOZ)r3tG~&2fBUWHQs -NFc}(~R@J7y;8_dD+QmY@ApU#P~j|CEJfYtwCm->)nj)quMYfd`rVrATC{b4BvyLu8TT?*}kr3kcgCU0zWIau+7*FJyUEtFpRI5#SrcAU^&D2H-2@Sh6V%B8 -&{mlYe$?#x&k{V4+$h<+m;#4l_<*SmowEProoQE_VW&>%N<`dl%5u36_U{Z@rhXMHT4Z{x;$RRGb51aPV3aP-=-nU;63r0~OrUEc1 -NL?-jqZ8;k(8uIvuwB?6mdTL59^&j`1*{yW8*3i=W`wP)%XebT!sd+Kk^`Q#c(8o=gn*M|5p7{>^gXQ -pnpG2Hma_Ga4#?3*?DF*=Pu&#QOoC^}2(-rzNP-G-TxGZG6N}4<+Kp_jmpsBrrLCb;!YPpNlA7lALeW -INlRe=v?I_nN+=jqMFeb`zS_RwYaX)QWAvGg2R3I}s>>cL}hWffD$ChHyy#1b6MMKDH$SY^&VhNz2|+ -%JNmGH?lP|4n~@LY_(ChYZYWD~(|^LS-(38}JfknO0c*u?)I)z4&u5qnMu`D1H!3ly#eJI7AP2U=AOb -?egonKF^ahX`XXM_cWtaykk5nRco6AEU@}FWDbys_|5yUHYeK70}NXR7qG2c&y>Um7h`hbhc-I3&E19 -l9)Do?1Saa4#{}Q3lS%#w;wSi-3Uzes`SCDO=vE{ZNg`dwl*Ox^HzIe}^KYRu!qWLRR@o%aWc>gx=VK20_PftpB(NlIJEj&6f -&mvU+VNajZ|fv4K+&LHpa=d9#?0_Dj1$ltKt)gX6+euOwuH_v#w}dGc+c?;h+NJoWn*jp-h;n7ecdOn0atli0OJfJia>-RKt%FCtntQ6l=$iZCEH -X&#hCmV&6WXd+hICxP2YRyt5JIU`3{jHT!O%ZE*~!&WE6^v_DrlnEk;z5xyRD)d?C5)0nKx6_@}k32v -*&^SBO>BMquKZ`Fr03$uzMC|W6oqKTQu8h79_ -jGRJwqym<$Nq*V%$LI}8$yR@V{MT)VB6rSn>SoC_| -IoKcQJR8$rA%O><>9LA%Z+kGN)We%OKzrg&O{aSSzA*E@Vm;DIi!4>TZG+L0{DWaQshHZ77@6v-Z?)2 -g4a-Yx!51t)vw_e9af$=*t7>9;I*TF{{+^5Ay)$7wZn=k3AU;-M8ns98o!Z8ZK1W%#jWpv2({8d2i%>(;Y-siQ(;8=A^+ozF|&q9QTX)C{WzP857Erx*r0 -b4@)8@=wV?#ETW{?8Lr1c_{?+5OV|VSg(tAkYu~)M0L-EsCiF*B8Waf3bujl>tg4D#UeC=F{WUkCo%S -~Gl@EoHgH;9l@3zD8rxp1lczPQDwr)D%Fpgea78Lsg-b#v1b2S4( -w^`g#`)|NfDLec_(`ZRHEdfN2KnuV%q@zcjZycQ)3E+PFpQCu -yRgAK-|DtOV>rx+_;jbha7O3HizgQzRP{$!kc_=f7Gp#t_MmF(R6nv~whcf|Uu*&nq%p_&B -^FddY}k1nq1%L^X&cN;QxrcTsT=<9%TF7TjCn!d(^k6llPyzw)j!E|Fk_wh5yGW=Ln&zmAI-`|ny`1! -&VC2at<>@D<61fm)7MVGp;De2m31CAn>y6S6EZh?ew8dJ}WcWYfsB#6bHJKzI`Aj~QIf5k{^TfPGy`A -*iT-n6xpqki6Q)BX*9Dm=7;)Eoozn4vT{wRSXy(n?BQ%!dS=SvHU_&N#G!Oi)bKzK`!Qyy$Hmf4sJzu -&2O>%(IDG8AD&1RXGSJx`=zQHz{LUzx5pPV8`O-3iGk4Hf)@jbtFz%wkcF7+C?3Is}9EK{81p7s -q1rl2OPWTTPCn>EA%9elJQd=e(2r!&Hh_}Gtp{^a7*e8LuRavwXex(!T6Ke`BcX;0a2x2aK=-ZC -`(v8K0#zx|E$be4uAlg;#?Hpje;fr!g6fd2=R+l-|mNx2GaepnQNH*Pz8a*F^fTPDS$lwhET!7rtX>{ -3Up=RQEIV1H5!Luee6(rHpg4TC72qbmFoqiB;ab$ZGNl@ndwu4G!6ZJd{YFT!KyA6TvdrSm%HU^7L5@ -PGXy%FgxoJln`F^&kB!m80W-_%ZWH*Xgjw2jb*KhtfBoXAtyvUiC*{E4-a -4aj&jiB@JETq%P^f)5JOk&1><++9d&MM;;W9rzTmJW9y_~Z-Vc*$^hwTvNzQbVU`px_Q6ho0DB;;aq| -MR7+l1Oh!Y@#)D%>fGMt6JQnfdS2KS~}>0&0WQTyh^x=Pvd@dMyq$EU_Map_+=pg@KXovsxjUm3%5!f+>#zbHJW^>xcPrr#`L!d5b+W+R7RY3=jh|Cxsc^@5|S^E{sRu^BPnw=5>xYI!Z1?13~EVo%EA -j0eE+B=Jy9)R9z|Q1a=5)QEJ#mLoBJ_N<*etoT{BTqN@*7B1O4t-leYxbY#`G;`BulEEGq&_DjpCeZ( -72&3iJ3w=9gTqhPHmB%`7-=ZNFpb@E7QHH$o&hqA;iFvD{K6&~hK@BouDxSj>JrW=1xUOoN(ZrtT}qZlA`DWY*G|C_eIt3Vw|D9Csx2XXmPOrGL9ShpoFxEGE#1egrVsecKN}u`9$>MC4lygR!V) -Iu6;L-M?3K0ZhOk^Hk-q-qkvQAtMQcyJ%ye9ajFW_J0Fph2LQeS2`pKU!o?M7Ofs+SrmjT;>R5zr6*I -l75_cGVqlB=a!itvg4brX*w0gS@0XR4%JGrZRojiw+WAJOl{p}$FgL?=(Nn4u>ENu|1x2{FeSpl3Ql$ -tcjh$-Iy1JCpm>WR2VR=U-NHu?n25H=UE1_(hv=W&*v#mE#E|;$h -7zq>cFZk%}>8p$!a5xQ{H{7lP-ox-WBt?Q}Axc2wo-f8x)OQG)cpR?3AqR8J%aq{BTPfuSWGf!TSSvJkc3dF1ESDeD+i*|% -xaIcL4r7L2i7*gV~Qlr#CJp_u0ExoJELER4Bh^-h{I0z%BlSL;?>(@CX3n;^UEu=b_H$pT1!zINC!anfJV?EGbz>T^6EB;0^DNWyQw`VyoevylY62J-N2Sl-*^Fz3Lx0Gqr -D_0$0(`0`nd*g?nY-q@dwN>1sdhmk*a)Xj=1 -{*`LZCs{8IYJt}MF1iGD^{OTC4S=1;ss|1VHW0|XQR000O8ZKnQOX){rSd<_5q$}|7~9smFUaA|NaUu -kZ1WpZv|Y%gPPZEaz0WOFZUX>)WgaCzlg-EZSI5`Xt!L8vGq9UP&34d6aBm)$NdXcxD8Ee=7Tr7hZ4B -TFhtC2{xmf4>=iiIgZOyV>5$`602%;cz%#{LM&P-X2I%Y_DqJ?Q7cQ^uSK7yk} -g%0qxp1%{{Cuf7PQ)V!rsBSB+%6p>@8gE4SsLgu^MV#tHMPDcNJOX%7e5l*4W?_?owUcc~}lRtvCMFJ -O7e+%ez%>+E -pow(*AyLR+d*DC{1!$TN4JS;OW869O?6b-wL+G4wJy55-H9cKN;n^tblulfvL$)om1ITR%dQ6>s=f;2eX7$%mHTfi{HiU~jALogP`Jm7AIhK?>CMZ@6##4eMcD)u8df^5?*dHKLMeX91oAlPxc;5}g98 -UDHB>=Wy-{{zHDdhts|1JyX-YfE{4Kib=?~9dCE!#-yuT$EajmLd&u+6DdUTHY4Xhke}Oz4X%g7Iix$ -JgImEZNF9X|@4QWxqmgs#O`{pAlENwx5)UFl@(}?@M*CW>UeQ{xaB9!$Zj~tATkcNv9+&L1#qX!$_nm -BUhrdRA%l+$!Kb8T+x8C>Hh;O+Q@GeY2B7c$m^;G`4g&X~A3m@*Qc6zhbK?paU>}iehB=>L=?WrKUx? -R&6w#B5gBxOqgO{vf&$&{0(u8-%_w2+77*-LbF-UGmzoLk6M5}A0pxqoTuBhlBXJC2w5KzF$9Ib<&On -LaRps2o*V!2Q2ZvoBBxz>fcgqo~A=9?#A>U>iu<1hHMo?pfV-2$G1o(?m{wJlZ{$F<$}fKw8u+A`JmI -oC+ql{DixFzIwJTKTvr}Ls4&1Hi@pa%1fYN=nst;O>g#X1p%{4hk250Ib(k^{D$R6l08r78bc~{VO&r -PMChSyb{E8KKvE-8wj_F^C0w4W(8*)k2QWm=G~t=$xKX7Nf+O(f5|EPIt9^%ijA;FSUE{!>=jJm1f{P -OE1RRPP4qn1`p{tS`A`m=n$vP;I06$pTT_@Yc~jOHg}UC-h|K$jjpmI8)Nu|v4*Gt-OI@KW!tn -+0P>(xW*-U@k*$-z~kZv(H%;oHiRk?;etN%?ilCwgf3Qd$ze#2zY6NTwIuLMFbK|sMqldCuIw -11S&&?giMbkk8=BlT+=^~u1be|x@YwMsBKCPZQ0jY5DEAy46>@4dw_nroAJ;#XOc7wG5OAm5D46Un%m3iW+^C?k0fJRm;2eF=%Y>I~z -LNJ^hF8X3XG@QV@3RIFS9L~2l&omaq9YEt1*j=Vx7ch%y#avTjlxcwhmmNpit)^JEm~`Zf0iRBvfpm~G>MAy@h&;^LI;pWt=ozVSs#^gv3Z916H__HjmKtN)u -3ZUQJCpM6ufCUhYV96bMgzgJAc6Vakrtz1bpjE?OnlAS#2u}PeF`msvKOi4Fw$&(H;=KaU8Rr<#THLx -&E55kBb=Q_)gJ43292?{lqZE&)EW;vsDx>z -YIdhYGIy1IsN(L9;7B8^HJcPB7tMgUMwAz00m5X0u|7>18Wri2s^nk=DNL)pf^;KJoz_?vIptydo&2C -j??+Ae+fXIW6#ow@fy?wcItk+Huha|Vr{azsF*ZJq?w;}W2BbNDiCjEJWt05l2v~>nsXDsSsh=0q3~L -;}BK+hhW>+=9y^Tp15@37JNZEHa$mg}WmkPq*rAaD9NfjFNSi#sG16lOrCFK$L7znatBk-XKDd-U}lsm;Lt*TZDP+2>C^04L!=lcpS;U2xmo^LHxU8?`y4&I`aqXNSek -fs1}SPO3{TP22Gpo^YWuzFN}8YZsJ^fIwkiKm1qTtV-;h&}%t-Tx{erc~rVqhj2CuvP)9l@;0?BSp)b -v^^j$}#9$*CAXV;liIZB)3@=AE6UUd2 -GVlvF`!C$GaeT}|VKgh8!9L3ZV@ME4HDHE(BW|`u}siD^BY=bUVgH$;_M=iqnG*nd7?jw$snDIIMn5= ->XQ~h8Tlx(peT)yQ;9o>VN` -LB>pS`mVv`f9LGUU>1dL(nFqt(#U=Ph@{S0ucRF?G%0$ghyeLpgl3tDg0T;o&aJU=*&NRZS1;lA0`fG -GL+=^SEVTAJC{v*&Qp)=v2}rcJ1`lpcf;L!YqCYWSs!$j)lL;Oxf_PZbuC0Osy~ZaW0tuD6rUWc6id{ -qf#)Z4@$M4R;!ObEc_G+GW8zT=%unR+iEq8%M}i^afT8DfNE-3D?5P-bt~QEg^gPoLbX~z)>xy2da4wR+A^P!y?QLU}s4m0>d0@ -J8jexdk9~6ZEq>c~Tv@`^bE&M>OS)+m>gSzayX&{|2Fs9B?si7&h|XXi%r5r5IL$PM*s(cMmI -}FdRAJ5hnF4tmTDiVol7uAr?`_qj$$3KlMjlm6KB{63Wy(Aua0Vhp1?#};gdYGgzO98;j^v?*5$ZX4S -y@uzJ99S^+YjHvSH3RW=H6P`J{-~SP&Qy`h{t==i|gXUpRTU16EAJ(jQR?we{SWgwl_`eQIp#PVWTnqK60ArhlL0ZR@f@f+Be4cX&f^-?ugrqnAyiI{)MV8Ttfu(X2upQ9%7wdNdZcE`Z}`7^J2#B=OOJ -RyACxPI+2f8CrR}bOdZ&aVd$V|Y{!f{k_pB^azO$(J9@X%Faj#`ZJ7V`c~(RbZW5O^ZmqbXYebefa}R -6WP)vk)#G37c7*!0N>PPWAzuE?^cZP6cFy3rbnT4|i{=sI6Vkev>R?0|XQR000O8ZKnQO)EF~i2L}KE9Txxq9smFUaA|NaUukZ1WpZv|Y%gPPZEaz0WOFZUZ)0mNaCx0rZ -ExE)5dQ98L1-u-1G3tEST_V!u_4QV09hL}X*VD!2u7l1E)=PflwJM%9bY7ijvUlBU|SOJj(5);??tR> -r7}qOda+irL9#V1QJNy=7rqeQ(XiGMv*GM%Gp3zHWh5 -nq|iqQb49=5QZ@|{)xeZ45PUw-Zyi&#p~77H^~^orF|)xXr$lsZwpbpMs|_)o2GJGOgW$W*mzP&}cXK -Qa&~cr&J(uOv2!^}M5Yu=|IIFN<#%snY7NnWAUba*bXLA17F<$v!Y!oSY1;X -K9%zz0=0tx4HqO-`asyB1Rqz}Q(`CH7)gLJFmMRS!zPO*EO^Yiw+I|oBe -}ZdEBUQgxaJM-s_KKv~p^Ncyu~?LxXpL?F_Yel@^T=3UIHi&#asOBU-d=sW{`=~~{U0}I<#-{^%O_Gp -EZ9IZ8w%=Fw56h=qGa&cYJahC>??5m?KMg*r36w?9LT;?y+ob1z^T0gNVl|PYX&gnf%(hj9cq>Q59~v -o;^T8rgYa>s3IM>~F@aPdzmbL_Ejv|GL{zz9TTABX0AWP9mPmK4g;OINlU6O~fMlW^SOt-)lML-PtlZ -eWx2yt*0#TxSf1F5W(M^fd -GqMfIC8H4fX-1sO*#i;ohX)Y@|RTmYj!LP7IJX`RG`8(P{7@gv#J)gkFJBHF^kf{}nx~=vVZPM_Ah&_ -a5bWeuU@UtV7?UhY&O?=l|76e_YZ^MZ&xT&Jpx3c-=rwv^hl;KdsjA_)r<(Hh60P2&4^q&JAQK_` -IENf;>2$tlNJk2nNya3d-?H}O42AQ-Y9%| -@IPBr`a&Bb9XUH~`w?%R@MySm#C>@w5yvrPYhAO#oL8?s!s^tu>{BIln&OtWzx@2`8GE14>}$W*ee -mz8A+=_;Z5s?!j??)qa!e4_pQ^bn4QH}KP_C&ulQ~2R-VnW^XPgIFLzrp&NK}NC%jb?0iqeFkAx|{O! -L^cIdo<&E363VtG)^zqwySKCbQUw~AOwC*yy2T{qTTWowg{214ZAcO5|lW+TSDEg05(=_?)TB}#KTRCVhBg6ySjwk{2F|3L0jeG;IhXw;W5!>#3hpwF2>GzdXx*7k*);$Gs>A`a#RuvI^c)O|M^Gr6H9gtH&o3hWnSFoK9;oCxCJa{CnFKVFW24Gqh_o}0_Cl4&wQ|Cps-NyZR9 -J1iHYyf -TlDtrn|#84Ey5gBGF5gBNDw8AOsPF?PNU3CC6(L>E=jdSyZvY|5u<3`aiUPla+)InCt&CqmaY%1h#{` -|cQW-}gSDH9zS(1F@wkZmW`|^N1Rlg;FTe&3_;3#dR1nB~`%rXQdg47i5X9l-=GRbcnJUJh%zpg5MIl -~9cPAW7A52Avsc6ae67W@6GdKxVQc-MsbVKXDNQ_)*Xeaf$U)g>qd9Jo^`^IhdkB*mm8!i+Y5Y0-kXf0C@Y -MhtGNQR68-Wzti+z?Sv}VqWg5bRSM2o5u7L -dDzZ&fiIoeZyCF5jck@b`a&)}l)Q{Lwx5?eh~bmG(l$TZy-0X*!K3-{a4tM%*>kGpG2{+eZI?E@A+eu -es~uD=UVdz~?|g;;j|+z5*RrL{Y}BS0F80xw6YaL1`1OPU(LKz0exTkdIIZc#m{S79b7R -*!Ohjaeic(hK&LZi|di7fp9Si8&#Q5^}e^5&U1QY-O00;nWrv6%W=?2nj2LJ$;761Ss0001RX>c!JX> -N37a&BR4FJo_QZDDR?b1!pcVRB<=E^v9RSX*z~HWYr(uOL(ukXBphudPPPKBp|Tcjb -CoHTq>^}<|NGA2MG_^Y-NO_hk;rrBcP`9EmV1(An_AbBWf|f7s+5{gAxcd(FNHceacf1n-Ey(@pK4cY -UNm0^eZU-7l0DJxx3shB7DgBCSYc26HFu*C0 -NA1cRacPn0{*)0=!DbtFszfd$gy=78?LeFGnQ)OQ=x7(G#g=SLF0xeHJQy^NG`#lx87jbGoA7hd1YoB -Am6l!sd7yO=!$G8=Qzg8e1XHASQ-(?psuTS5cEwb0|ZZ2O<$l~Vo^lY&h6T#7L3aE&K+*-{!YfQ=1hvB$np> -{+eDk#Gy$aD?MgU6R6$_RnG5+UH06M#-@evN9$us%;>5_R6|jM!xIoPFw36kPEK7^Mm1pq;d@D+*G>{ -xMmza0r7TB+4&8$EG9HDuhIsRC&Vv~^R3vyWsHgh^4kg%Mt_l8cTEkoQ;7*D4>n)Ftt25Y@#NHRT-#& -!_dw^YufoA>7fCX*X;j9^+pNy=(jgd{swRPzWEfdD@#HxO>UgDP}o15Vx=;KxoBdFo!0j;iD&^x70G2 -`K}=wR1b$%CfHF=aVonNgGpH3lqx;agm+5iaH@wZoBw1(S|H&gUvSt+JU7D+>?}pXWhCIM;9D$2RY_W -I7hY`>jnY+26YAyNg5@MEVBYIKn=~JiRg}N1sldI%N`rz#*J2pBh@}14R72?>4Xsye9s}pvpb&a9q=H -wy52%Jg94mGrl(v{tq*Y$IzEtKyCLRj*6b<%H;a?y^Hr<4k`Pz`zk)MXF`6~$$(o0sN!P5#m)Np`?yg%@#2#JM8FlyAnAfhsJmCPygziA8#PHs^C6720~w0-czlSkTt+kEfEl@}=~s@hRLTz8ZD;P;^ -xEV<%p)ckvtA8SuFQtz4WCW);o|)L)v3uhsA9m+3&^@9P7X8jV8o^7_bIF#S06=pZ2{d`Y>!bOH~C(Y -JA+y}=tB$}Dr7zkE@I6bwh=E$ACb5P-3uYuOICyJQINIh3;XS=X=&!~^^O@TV*5EBK3AZZ7R7;3y9Gt -qcioiy$;cgr+SKeBF>O2Kkloq~?QnusB^Ea>3NBa*ia`&wN!SN~0Edy&H$~`@K&c^jjI61E=E>5)e6M -##TK9YY0th;za%_W(eH>y33P@NUOe3G;;?kmY)n=mW?QYkDwk7Zbvm%tc2&0R&bBU9%?WcqdS6Ku{ouZ`z>Gy%2vji66JF7wR!rn&ZXFYu~L8E+-MGjfV8rRQxzeT$<#?61*tP-4H%DWeo3NrWt7`1Zif2$)EgVT5NBQ19y=egN6tS2RxH`?SS5gljBW0@p5(yLeZVt;`-(J`Pr)(d8&vr9>%ReTvz~yK>3c?Z%T942iHz-Mq0@cO#^MC6!=8vFR#xQ*E4)a??BvCvVy^J{7T -%(MUQjapnrmEkS8Y7DaiPgM8jP3k2QUKf9UymEC(KjjzBzJfE*riY;YE`+~Cq9EpF~x25gT2(OO`y9* -^8|U1fFY0ULH!>8lDe#1D3cwbRUcHGIuX)O%rPy%{zLXQ3NPEoC?oj*<;i3sWj=2G*BmzpoziF -wDUrNq+*_)B7PBm!s!fFcEk9P(q%$rLf8Y2}U|@%iDx3qbV>XLp0&j>wN>7Rj?Auct(EiU$g$tR*F~X -sLeNvVKS`j-|)HHYkQZD(Xxq;hHDgH)t^|8Z8~zSQ}Ro{x&}gCkRP89v-SzWfAg7MW(#!_2pDrVTsN8 -roQ^KT*~#45+`kM{{1yB59@sEE{5I-w^if1`m=XN^K;of)&&W?nlKxe45eKR}ecUiLC$xzkaN$Ve2fK -DmI_Jnu;yD_}GVSeMd-yf2cpW -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 -=ln@CEYR|C->UKg^YGk-#(M6?T-wPUKW4c7 -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

i!2%O9KQH00 -0080BxrJTJ}LVvYY_`0HFf_04M+e0B~t=FJEbHbY*gGVQepCX>)XPX<~JBX>V?GFKKRbbYX04Wn?aJd -3933irX*{z3VFmpR%y^2jo!NZo_sdET!zFCm~zoWQZ)OBYDBlkMG#3?Iff+*?Mo@ym_OJLl0VaLmn8l -2I^zr495GAO{U+?TGq*!b68QB|GTS}R!8D@>TF$tI2&y66a&0jA -V_Y``}#+q!Fx-X(}Rimi8HnLYB=Z;h}kh2>7`>Hiv9Q-?R@ICtc;!`%xAuFX?XHtUY#$LAE-ShrX+E! -Kd6C-01xg-GsEea4#dp1X`)l7UlQ34w}-f!E-%%rhNEc@}=YRI9`hvtD+iFW01mrl!BXgwTC7Ix54cR+p{hti!oi|b8gq^M+r;)1yD-^1QY-O00;nWrv6&0Y3Ef<2LJ#l8vp<#0001 -RX>c!JX>N37a&BR4FJx(RbaH88b#!TOZgVelWNCABE^v9(S#6KgMiBm{Eo_S_A-N<^!vTW1pRzj9Bv1@9n8Lz5Zb1 -mvh&1UYqvfge*wViF~NM4sE2zppl@^qhg#XbDD*TN@r&0s|#SbvEkA{&6I -|QZEDH%1)gRVK%rQNvs+*d&o2=e#dOWqU<_>m5#t()B?H6|FV-re-iy;0?BRoNS?#}|^CfI~zGrV0=( -b6lJ%jBQLEO}DQy;$#?+46M15)qJrvf(2B1{**#0-gIuAtm3*!}M@bjki==XC`Z1ZU{1KoG=ckB)_66 -5h8$0;Phrm3ZGmxo7J=6NpnOs>_sI`_l+K`ex)I#e*qr=oTf$E~|JkZDnaFRJS_YNWL{C)5VSn&ODY` -n2i1sLkh1B`XggxW4`De*i1rXyYo2jOp%Rc_o?G7X26kib(puqb3)a&N>^-M*JZz)UqF7D*-ZsBDqJ^ -LjWczRJ+FDenmuOx9qL-D>s`4df)#+pwG$Pv>kBZ<@|+TmfXFN;qTAVwpuwy&n2|vbmi71&kOYlkq=} -Ptmsv6_`4M3h<`s9LL%T6_YL*-tfJNO!fw5%`G!!qdiXm#90rVH!=%ML?#aMaO`DW0k$C!5M3+}U~D! -~;+og^O^3~q)hh<0@v?JzgUJf&>h&ft84GmC-w$CQ7Jg=>o?L{K8KPzX((3bEE+GgRAqMo7U+%!+hQ-K^~z-1}W_!@GK*Nyg3ue7JN4k~9P(?Lu@IbhoH5 -7W6zYEBZJnYjPQeam^75w9c<;(dCas()tfGG%!mSD6u`2`;;vsn#mf6v;MejSEI)*aRFKa1WFWy9USBBC*D5xgXedV%R5)Q0ntFCLE -QFXLtv0rg$avJ~Ns!A2RJM;C->j&MEQ<0H?_95O>$X-Rd~AJ*yp|^HxeCb6AL|HdmqmM-LRQ1{nlZE< -g>%!YU+b;}N|W%IJh(^mby+=%vr+F7z{AjSjosH(>({jH-*|J35iq)ut3Vk!#6zLYZzKIP8#3ZNOo2= -8>i8;G-=u2M~+E`)Xv&W?OQB*!qoXo|sB^H6A7^S%&6DuH4;@yK$Q=soaOfxJP+;?V5DZ%@1gA+{Qn# -eIIVWXRdV#D(B((>HczGiyZ_erpxWlH2h~29AnxK;U}Yu9Ix*Qu6l_$%+_&yOP18wFiO5hCS8iEI7!B -8fJ>yLc;>{r?acNO{d+~#9SOJKP-Nv}!u7GDI#5tPahQ -w+pO_qvlji;Ml}eN4bm`*hi?#seNf9iH|(F= -gCvh#E>|nH0>J@O*l!M9=)6*dzW@ut&{8Am+^;=%;8GO)^Pt26|ffWuibPrHr1HNZpMY~q@ZxPew6`&U5n;XG -KIa2ngok%eQ8@FT#;$$% -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 -HkCiwEh%w7NZYvA#*N{g!uvK%wH#D3A -y+tEtUMC5A5m7-)%33S6SNc8wgLqR%QmDgvmdNAJVUcv{AEQC;|8gNIrl?;$R|p2M^p?!SwkMm&D+7L -=dJh0L4E-l1W2gc9=S4sPR2=IQKL@G=n!t<=>BrBnd1@6=A849daP;o~V)td&wV27(ozL5>deqg)GrQ -qWoqMs|#6@^Cv%taJ3Op17R6e5t+2Vs+9Ps#K}o1Dg9VdV=zV!&jd1?_guj!12aP{dL~%Px4XnBOAW9H3dmCU?7E)A_97*5PML9-*UnVCf~D~k~JoOvP-INpCmV=ZnW&tyU)=p -~g1%tlM|#dG7wX+NU7LvzqP+p`S;ho5DoOL**uTs5;g^{w22c_zKU+|tIRAsm;q9m|3>ke@1Ss7`&qX -+fcg<=BEOx8)b)l*|emz9tW2c;qgpTA(Snk!RU&iflteRD#-r!uBz*w|2tfVG4(?oM@t$Upd32R2L5g -7Q=A&o{!qp9nf|qb+Q|avjc?fWYKWF(lZ`{L-8l=jbR*+g`8AGf+g=#G6s`2XaT;`r1GO)mZvNWCk{5 -!-p`t%5{6A6uP7gGtOHBjPzQdF9NlDEK%tKp^$e7}zbi2J?Ro!zgN_?1cN_z7DHRn&y#4m|8?x#txgt -v7Ame7;VoGT1{tVJ`NUv9YQf#R@5y5(W*N3}+kesy%#?XyioAS6H!5V;E$M>HZRd<;M^DNKoL}Ogo+2 -=*p+7_~2c7=(<?a!Qeo*;0kg7Dn85`}iSND;D-r&LyN65g*o -<)o3=56gUem9g6~uF1_-D-3TiMXkC%-Yb->N`F{#jf2zcZP&WnLoq%u#&k$+M=lu}9*v(IsR{Hm4KhpfrGJD`g$3j_2F8R~j)!cWrZ3C*barG){Q -h_Y&po@RB&@{<1+j~{y%{_KPQRF6KEEiX?2*{GieZ*au+EOj(8Sh+XUh| -h&eFYU!0yImYRyYy5MFAw|ncR(4mHDR41lKo$$Yj-ZXug>P@3v!!6lm)G9_uaiL(Z(7(QjU_QsEXb(g -DGI!FKCnC*rtkoAKs-l9>VnfCgsHy{3toQ>AY-kb1vCjh$z}A7WF0i<+B(>OG?}lJd2(jGE8QWBdwnLp_{$3i5PwvADErvk-t2OD<~3RL)v -XCQK<%{2IXRSfpnG)Lk5ZV2Q_(Rz?4>-zeh_3?C@NXK#Wofv1ATqF<{B!*=&@^tCIp7sj+6lfy@Wb7O -utFW6fpNTjGP`K^w-q9qQ)tQow&Mf4hzqi8rwZ_|6rm`i>tU@ZSPDx(i)s=cV%nojun{fRd9_$_Yndg -*h+UxxK@^O8{O?n3~k1gZE()f_rB5cz=PjaR!^@D?ivMzk<lXbAW -b4Q3|xvHM#-^)&6g8+zlX-uQ3!UbaZ_!qu*+Atc -0G<`raRTkgY2p;Rd{$cc(IasU_~4B>#~#J+>w{$WgcVMIaVQp+dyYdrMrmF?d~-Zge(^m-u?z(Rb4^` -C<2-7bo)th@HH-~((4g7@o8mqwOKj`HcLSQZV{RN%U#uxUJgPeX^_3y}G_e&wORX0II`ILDmJu?zcBz --50Y+GITJ;0a5tecV6S?C!mN;phE2J}=B2Jo5qrh?ZNO)x8GrK_q+E;AwL(TdeR(Cobs?E;DzF?9G1= -26S9lGopFaheo`>d(sOed=Ct6jKC#1ub5d7#mpTz`Os8P -XqguIcy8)mh1w0Mlr_qs+1Ym4iu)>l4GPOAEZ%X@q}&+hLxT)+f-)|rbw(q*3(*?96Gz2;!$MiVc#qg -UcHL(v%gRFfwC0ykhabIei5vzZjbKcw&ECa(ZkMBgD1xHAq*C1v3g*Q|m_UmQDnHGou^>Oz%sw>cu=XuWqXW`OP&lv=C-8~0N=5RM2s)`SN*?*0W9BhOtmz82WI5|M#jx -yPj7LlM$`xJT&FXBfXfm0mmToqDkR;0!HArC3>4z(8|EDHW;ZxQSWIiQao-aa5{;IdV)S;|DLW25&Jk -|{1V+O`Zya$w*D2|k_nBA^P5*lImQ!zahY^9BY@>PorpY1j%U=~)EAVOpBwjM_ILHQV)bCS4Wc`zR((PyC>i9iIF_Gd1X1yHeeXTv3x{h#h;J -hVOh}a@o*a{HeGHr?5(?c^hrMA3x+gd{>gfTP|YzOVEqsiM-1?gx3W?EX2M#0bouvb;V;4PjJ7x)R0a -R4ba2GDIIPp2Vw8#<3nlor&4||Xqk#o27&F3kuB#0IQb(03`qb0B -~t=FJEbHbY*gGVQepHZe(S6FK}UFYhh<)UuJ1;WMy(LaCzlDYjfL1lHc_!u=J%2CKNVKZtHHjp>G{WP -F(9au2|XJmDdGB(_)30evCX*lQT~}7BO&YZ+I<-o= -I(c#P;^{0YtCf1E+R0>cbhK{j%Oua&yKdL0JWtBYZQXQ9QB`$UbY)$&M@Mo^Rl7_5eZ8xeU0rWleWC1 -B<6YR)=jUa0u75U)yIR(pjam}mtTYC0eg5r?F8#2@o{A0s`l*5+v!khudRuO@{7O};y2-Z1@@;YMfNZyFS*}afv?GnPt~fUso)+h0O< -^)NrtPw*>Z)88oAOtcSH-0od-4j90K^Tcsw;SI^>@BHOY*OaR>`khfY}yJtCFJSFS*=xNmEuZDL}kTy -;~|GWa|_BYt?qr@uN+niq*~2s_hDx;1v`6o3ibi@@$9CKCinc^{$dTGKilql-fKgHwtN>C#l_Su@l92 -alRrr5f5j=O?9IYdsbF&AM1-V8QTUfwlN@=^-68p3}$0h0)r__Vj2eV|6_e~1i-p(E{iT-)>YTk8x7? -NU46_KN1jIXT>M9xwyJ2B7kRs^w+7jlbnPWx8huihUA}G9y0s6>OHL6;wF-mnsbFR94d(&N5PCB -KFFX-CHv)4GA`Wc$qSmQ?h63oJn>X%*Fs7qCKu?J{fWjTl&{Tv|k41|wrhMTW}{?!^<=ebry0tP}! -_V(3gy1~?@~0Ose)#e9Wdn+`9(ppmI3NVjJ4`snCrLlX-?%p|8d3D7(TvHmsE3*<1|Ct3fh+if@MRXTk=OZY1ceU^Y -wdj0yy^!E}l1@!->Zs0kmnf=cdu`{!&U@HKuJ4N(XYMtaveq7bz9I)8LJ9Ap;Eb&D2!nCaGc?^Nq+^# -&YYS^O#nDgWewq3ydNwH}`hE9{?hjfYO2RC{A`=f#lzDi&bb{9ZE_%AsxVUI|7-6m#tXkm-K+=A$X-8 -i`}yNjeCjPJJ$PnKhQ%nwcy5ac_R9nsx62;AZ_7}2BT%VQc -b9PELU9ZIefP)2=%&SPW&Do?UW_RqSk(#v9-!aARMB%4mX$28zL`*=+qIsw45x>afKK2Z#pb)rGR(pi -;j+E=^8P^{%%fkA~ir?H%*f@cGLc5=aB?O6)BY%np8H9#xm}FhRw%X?r;{0&jV@xwT_`?R?AM -}*RB%?fSb}+~zvM5Lt<4%>Lg_E7Ye^Z`)s~9fG`pJr2SEo!mGqIph)&B$~D$?ersNMoW`Zq*j+*w_Bx -RG9Z{RUgmP_a=CIs>#^UPRgilp1R4lHvsAU;+AbxP!D?*xE^);p8c9h7(T;HeD=GYW;faV}_9Hjnb@m3`*7`BNjV8~!^Z3IvY_A1D67!GI_t=Sd@5LnErK;yH3N2v?7%97Iy*c@SXz!p -^O;I4q`E($PDL8|Hv>P$h$fS!7{-IU7`@ueLFyGLp*K_8w$mnEQdp*C9$Xsc@6BmtKyL2F8Hj@X`QNa -_VJMA_xJU5KFCuPv_&(Mc(qqpD+X8LKmPlew*(qek~DsBx-$g?f^0;OYKzPwTgh-1%V5#NHd#U;aCJU -RTHs_=le%O$L8jQLK3pKeGAz<2?m>!Q3fL6Ayive}b)IMb~nQG@DH9{&3N6vD=vXgL8GocKrH6j%JYEC(93YGHXHhj+)YgfbU*xP;h(7NJaZbEjb2 -@zsnrSy4TNF!G+B!m!2^^czUQzrHN`m^kW-KutE4zDO0X?aetdWO{WDQ-lUnrp8nEMfI -8N^d%P9px9Hfx(dQ3;C1_O?v1~y+G$>|(93pU15fLpeJg;9+nF*age=#L#v{lVz*@-*vOxZ1p%!l-WjQN#b_t=ui%Sl4mWmn9o@)LtBw8#k3(OzzneMK&zbMehJ!E@+n{clweovl6JYlvn*X>SBp -8I-x}SJE@rc&=0>pVwt*Dn7v+47B9@+sr{Dadq?cV}dA`chIq1Oi7(Gjx(x=Y -K)Y54!^UAMMSmLy6S`#`qHIECUPlF~jouWd|zb`X;`>thI!z$z6A(TZ5e?>jPyaV$xqa^B5Wnex6(8a -uFyy`KrQUkwN`%5y2W$fX1;qcJCcSVWbAq460Wyl!gPhO~cxcH|U!Ji>v~J`1|UGBoMA9V{3202r3ZD -NebFX%iWK1@5x#(kOutZ6=9jkadO-P)?jlTwsxBXQFQf?T_BXj0<9hQ<^yThGBX!faNnV0xN>vTksXZOPUc!n_R#D2bk-TTu_`@DS=t+Zv`ruMc3-FIAL0_%wz#W))?b&=_LF{W53T#AfeDG~W!92pwsq{-y-`j5>3iP2pw&1*BAgpH -pci9icNk8RnITigA6UrPq4gdMHiQ%2iyQR_ixxKyR}T_5+x)G%Nw3HU>zLaV@y)XA^{DDZZBuBQwj4* -Ht@MgbkaUtwrifPymfWg5hbz;FxbBa2GL!u<+BGW(!vce)MT~BT>UG -m0vGrace(1%j-UsOI6Inyym5A{2qG%J!g<`Aylh#BjIT*%kC^4e!Xs~<7g<)G$76RxOooZkQ5|?z&i7 -PyT)@eA0zS{0pG~r2n|NP)wa$Uqx&$lA624dzBn0`#@31OMkyUulFUSFU_Bbhd!9AV5yhB2d!UFc9Gm -AWQ4*dZ!f7Kk{P&{5MR7|rR(w_<%Z0NwS|x>!PVx%|;cBcyc8v59TR!18*rGtbv= -RTLKjCInxOy2s)sjiGH=D3sG<+0Y}6b8LgFc?*7?;<+XDRL -lcoF*UzhA=ExQ$(;WH#eY!nxR3HveAMOm8+Hv1|Bj#zfx`4$h1%W)DPKSWUa*4ydO{amgZk5saliS0u -sQ9r)tIOlV%>+1(Q|TE{le^qpBT(hZz{EO6|=++PVukwrk!?zBeJ%Yjq#~$5A$Bs?Q2Q*mAR^0tMm<7 -pIHDk;?#i-Q-oB!$zfA^rgLG#p_-oS)2qNG}dFyFqAhSX?5FOvjtHh)?S73S6UfBbRl1Yv_ITZ(}W|zc_m#y@B&}yD56af~8<_k)@Phs|?_y -+iMTRo|`1=no|IYtWC2(mX#!lK>tIkXYi*NX~BXHh~QE;3*Y -G2hksg}jI-EC-3Z0k$o7~0w=h}hf>ZKR;;w49tO>Oe%JO6oJDSQ}*7FjVoV_&K6hA)u68XsPpEf_CmW -Q%(z6VB*wN1|7-THDi9{ob>QDvzgIy(Eanq%kj4O_S&>Sv%KQk-`d@Z9V>mKYcZ#c`+~D~YKOT>@81;bsqL{<)~h8pd6WLGzMNBXPAKpur+>A;-6s -O%ZYG2vkP8ORMzov1Op98`32aHBCpUc7*p{z`c9A~SQi1Y5at5^9&5_Mm*Wk&LuhKG`0z7$&ie%0}DR -_&MgT%L5M#7`Mj|GEvLAp8_{Di9*=NL_M(E`t4+Ia1$*)V&<61;tEp6z~-ibq---$0?-r;cmJS}3yIM -*ITM(d*)_W*`QPb$2}X%_73qcicrU@pnTAJ|P302E7&;=84k5f@E`Q^pzy{+Rdt7e7fAwaH^k4{%q>D -K`+a8j7!d(pgZPN8DxKXSiK`JbJ7O*d02poGab46X;yicz%?3I%t)iJ9khUUSvQnK3}Y3wojjXc*%}J -+Wl~pv)Q#C5a6lns?43i?WMts1=4MtAkt`T8HO3pcYoaZZ_ko-}pC{$&9jivGcQZXw%uQ4cDmFo$gn9 -gfUF;|pqpIA{>c5o>|1hU~$jowNup6>Ycmj&3^F0RdUh)$Ki)pEs)fx|8nC;!!gaAcVtg=9`_)BSQp@ -l||$KF1}yV(gp1CQ$Q>WHaUA1Yv|0%dGMZt^olCqM!}G=m`@JUg_|w^FUp9nGl>-EK=I68IS5Ov@cod -s^iJ>7ClozH -*6h+E1@IbXw5zuOLp$F+R8bmTm1w#5<;^{|JunO}kHa%beO=8EDll2HY5=mt6gtX$>6+A$$d(av_@={ -$8jJ9E1jmTIZ>_WWCZNxm^! -uC`zEx+BpU1`U6D+kNN6Rp%fU6Oi{|$8b1`O@<9U3+XNhmyGMhk*RHPzn@uqZw)>PB{tbIe8F0&w+nT -7J7*UT`3eez(R&063kK9oop6W!ZM$DhB#+9vO>GNx9)$+#m7OV55Slql3e#=Od*MCHf&Kpy?!AJVCXL -L_aih2{c(M^L&4djDSYo^cRSErnQn^J?GzT8Ju#ZO{0%anp22kvLQtwQX5DfUir5Zr2A|l!Lgkg -OO+j1vJKINv*JmnZ0YHMa++Ua*e0S&yWzPnGf6?z$@Hx|8ejXrhGhU-l9RL`h&Z$<&VYAyR-?lo& -ubJ;Wk*0XSFj(Qj@I$v#Z7%)E?N0Onmuy^pfauf#oTh@CLv`Hzqcz?61fnM55h#(TeMTmG;pAw7*Y

Thank you very much for your comments on this site

\n" -" You can visit %(article_title)s\n" -" to review your comments,\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this " -"link to your browser.\n" -" %(article_url)s" -msgstr "" -"

Thank you very much for your comments on this site

\n" -" You can visit %(article_title)s\n" -" to review your comments,\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this " -"link to your browser.\n" -" %(article_url)s" - -#: .\comments\utils.py:26 -#, python-format -msgid "" -"Your comment on " -"%(article_title)s
has \n" -" received a reply.
%(comment_body)s\n" -"
\n" -" go check it out!\n" -"
\n" -" If the link above cannot be opened, please copy this " -"link to your browser.\n" -" %(article_url)s\n" -" " -msgstr "" -"Your comment on " -"%(article_title)s
has \n" -" received a reply.
%(comment_body)s\n" -"
\n" -" go check it out!\n" -"
\n" -" If the link above cannot be opened, please copy this " -"link to your browser.\n" -" %(article_url)s\n" -" " - -#: .\djangoblog\logentryadmin.py:63 -msgid "object" -msgstr "object" - -#: .\djangoblog\settings.py:140 -msgid "English" -msgstr "English" - -#: .\djangoblog\settings.py:141 -msgid "Simplified Chinese" -msgstr "Simplified Chinese" - -#: .\djangoblog\settings.py:142 -msgid "Traditional Chinese" -msgstr "Traditional Chinese" - -#: .\oauth\models.py:30 -msgid "oauth user" -msgstr "oauth user" - -#: .\oauth\models.py:37 -msgid "weibo" -msgstr "weibo" - -#: .\oauth\models.py:38 -msgid "google" -msgstr "google" - -#: .\oauth\models.py:48 -msgid "callback url" -msgstr "callback url" - -#: .\oauth\models.py:59 -msgid "already exists" -msgstr "already exists" - -#: .\oauth\views.py:154 -#, python-format -msgid "" -"\n" -"

Congratulations, you have successfully bound your email address. You " -"can use\n" -" %(oauthuser_type)s to directly log in to this website without a " -"password.

\n" -" You are welcome to continue to follow this site, the address is\n" -" %(site)s\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this link to your " -"browser.\n" -" %(site)s\n" -" " -msgstr "" -"\n" -"

Congratulations, you have successfully bound your email address. You " -"can use\n" -" %(oauthuser_type)s to directly log in to this website without a " -"password.

\n" -" You are welcome to continue to follow this site, the address is\n" -" %(site)s\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this link to your " -"browser.\n" -" %(site)s\n" -" " - -#: .\oauth\views.py:165 -msgid "Congratulations on your successful binding!" -msgstr "Congratulations on your successful binding!" - -#: .\oauth\views.py:217 -#, python-format -msgid "" -"\n" -"

Please click the link below to bind your email

\n" -"\n" -" %(url)s\n" -"\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this link " -"to your browser.\n" -"
\n" -" %(url)s\n" -" " -msgstr "" -"\n" -"

Please click the link below to bind your email

\n" -"\n" -" %(url)s\n" -"\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this link " -"to your browser.\n" -"
\n" -" %(url)s\n" -" " - -#: .\oauth\views.py:228 .\oauth\views.py:240 -msgid "Bind your email" -msgstr "Bind your email" - -#: .\oauth\views.py:242 -msgid "" -"Congratulations, the binding is just one step away. Please log in to your " -"email to check the email to complete the binding. Thank you." -msgstr "" -"Congratulations, the binding is just one step away. Please log in to your " -"email to check the email to complete the binding. Thank you." - -#: .\oauth\views.py:245 -msgid "Binding successful" -msgstr "Binding successful" - -#: .\oauth\views.py:247 -#, python-format -msgid "" -"Congratulations, you have successfully bound your email address. You can use " -"%(oauthuser_type)s to directly log in to this website without a password. " -"You are welcome to continue to follow this site." -msgstr "" -"Congratulations, you have successfully bound your email address. You can use " -"%(oauthuser_type)s to directly log in to this website without a password. " -"You are welcome to continue to follow this site." - -#: .\templates\account\forget_password.html:7 -msgid "forget the password" -msgstr "forget the password" - -#: .\templates\account\forget_password.html:18 -msgid "get verification code" -msgstr "get verification code" - -#: .\templates\account\forget_password.html:19 -msgid "submit" -msgstr "submit" - -#: .\templates\account\login.html:36 -msgid "Create Account" -msgstr "Create Account" - -#: .\templates\account\login.html:42 -#, fuzzy -#| msgid "forget the password" -msgid "Forget Password" -msgstr "forget the password" - -#: .\templates\account\result.html:18 .\templates\blog\tags\sidebar.html:126 -msgid "login" -msgstr "login" - -#: .\templates\account\result.html:22 -msgid "back to the homepage" -msgstr "back to the homepage" - -#: .\templates\blog\article_archives.html:7 -#: .\templates\blog\article_archives.html:24 -msgid "article archive" -msgstr "article archive" - -#: .\templates\blog\article_archives.html:32 -msgid "year" -msgstr "year" - -#: .\templates\blog\article_archives.html:36 -msgid "month" -msgstr "month" - -#: .\templates\blog\tags\article_info.html:12 -msgid "pin to top" -msgstr "pin to top" - -#: .\templates\blog\tags\article_info.html:28 -msgid "comments" -msgstr "comments" - -#: .\templates\blog\tags\article_info.html:58 -msgid "toc" -msgstr "toc" - -#: .\templates\blog\tags\article_meta_info.html:6 -msgid "posted in" -msgstr "posted in" - -#: .\templates\blog\tags\article_meta_info.html:14 -msgid "and tagged" -msgstr "and tagged" - -#: .\templates\blog\tags\article_meta_info.html:25 -msgid "by " -msgstr "by" - -#: .\templates\blog\tags\article_meta_info.html:29 -#, python-format -msgid "" -"\n" -" title=\"View all articles published by " -"%(article.author.username)s\"\n" -" " -msgstr "" -"\n" -" title=\"View all articles published by " -"%(article.author.username)s\"\n" -" " - -#: .\templates\blog\tags\article_meta_info.html:44 -msgid "on" -msgstr "on" - -#: .\templates\blog\tags\article_meta_info.html:54 -msgid "edit" -msgstr "edit" - -#: .\templates\blog\tags\article_pagination.html:4 -msgid "article navigation" -msgstr "article navigation" - -#: .\templates\blog\tags\article_pagination.html:9 -msgid "earlier articles" -msgstr "earlier articles" - -#: .\templates\blog\tags\article_pagination.html:12 -msgid "newer articles" -msgstr "newer articles" - -#: .\templates\blog\tags\article_tag_list.html:5 -msgid "tags" -msgstr "tags" - -#: .\templates\blog\tags\sidebar.html:7 -msgid "search" -msgstr "search" - -#: .\templates\blog\tags\sidebar.html:50 -msgid "recent comments" -msgstr "recent comments" - -#: .\templates\blog\tags\sidebar.html:57 -msgid "published on" -msgstr "published on" - -#: .\templates\blog\tags\sidebar.html:65 -msgid "recent articles" -msgstr "recent articles" - -#: .\templates\blog\tags\sidebar.html:77 -msgid "bookmark" -msgstr "bookmark" - -#: .\templates\blog\tags\sidebar.html:96 -msgid "Tag Cloud" -msgstr "Tag Cloud" - -#: .\templates\blog\tags\sidebar.html:107 -msgid "Welcome to star or fork the source code of this site" -msgstr "Welcome to star or fork the source code of this site" - -#: .\templates\blog\tags\sidebar.html:118 -msgid "Function" -msgstr "Function" - -#: .\templates\blog\tags\sidebar.html:120 -msgid "management site" -msgstr "management site" - -#: .\templates\blog\tags\sidebar.html:122 -msgid "logout" -msgstr "logout" - -#: .\templates\blog\tags\sidebar.html:129 -msgid "Track record" -msgstr "Track record" - -#: .\templates\blog\tags\sidebar.html:135 -msgid "Click me to return to the top" -msgstr "Click me to return to the top" - -#: .\templates\oauth\oauth_applications.html:5 -#| msgid "login" -msgid "quick login" -msgstr "quick login" - -#: .\templates\share_layout\nav.html:26 -msgid "Article archive" -msgstr "Article archive" diff --git a/DjangoBlog-master/locale/zh_Hans/LC_MESSAGES/django.mo b/DjangoBlog-master/locale/zh_Hans/LC_MESSAGES/django.mo deleted file mode 100644 index a2d36e98a180a2d9f413841d0cfa0c5f85654f63..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10321 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, YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-13 16:02+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -#: .\accounts\admin.py:12 -msgid "password" -msgstr "密码" - -#: .\accounts\admin.py:13 -msgid "Enter password again" -msgstr "再次输入密码" - -#: .\accounts\admin.py:24 .\accounts\forms.py:89 -msgid "passwords do not match" -msgstr "密码不匹配" - -#: .\accounts\forms.py:36 -msgid "email already exists" -msgstr "邮箱已存在" - -#: .\accounts\forms.py:46 .\accounts\forms.py:50 -msgid "New password" -msgstr "新密码" - -#: .\accounts\forms.py:60 -msgid "Confirm password" -msgstr "确认密码" - -#: .\accounts\forms.py:70 .\accounts\forms.py:116 -msgid "Email" -msgstr "邮箱" - -#: .\accounts\forms.py:76 .\accounts\forms.py:80 -msgid "Code" -msgstr "验证码" - -#: .\accounts\forms.py:100 .\accounts\tests.py:194 -msgid "email does not exist" -msgstr "邮箱不存在" - -#: .\accounts\models.py:12 .\oauth\models.py:17 -msgid "nick name" -msgstr "昵称" - -#: .\accounts\models.py:13 .\blog\models.py:29 .\blog\models.py:266 -#: .\blog\models.py:284 .\comments\models.py:13 .\oauth\models.py:23 -#: .\oauth\models.py:53 -msgid "creation time" -msgstr "创建时间" - -#: .\accounts\models.py:14 .\comments\models.py:14 .\oauth\models.py:24 -#: .\oauth\models.py:54 -msgid "last modify time" -msgstr "最后修改时间" - -#: .\accounts\models.py:15 -msgid "create source" -msgstr "来源" - -#: .\accounts\models.py:33 .\djangoblog\logentryadmin.py:81 -msgid "user" -msgstr "用户" - -#: .\accounts\tests.py:216 .\accounts\utils.py:39 -msgid "Verification code error" -msgstr "验证码错误" - -#: .\accounts\utils.py:13 -msgid "Verify Email" -msgstr "验证邮箱" - -#: .\accounts\utils.py:21 -#, python-format -msgid "" -"You are resetting the password, the verification code is:%(code)s, valid " -"within 5 minutes, please keep it properly" -msgstr "您正在重置密码,验证码为:%(code)s,5分钟内有效 请妥善保管." - -#: .\blog\admin.py:13 .\blog\models.py:92 .\comments\models.py:17 -#: .\oauth\models.py:12 -msgid "author" -msgstr "作者" - -#: .\blog\admin.py:53 -msgid "Publish selected articles" -msgstr "发布选中的文章" - -#: .\blog\admin.py:54 -msgid "Draft selected articles" -msgstr "选中文章设为草稿" - -#: .\blog\admin.py:55 -msgid "Close article comments" -msgstr "关闭文章评论" - -#: .\blog\admin.py:56 -msgid "Open article comments" -msgstr "打开文章评论" - -#: .\blog\admin.py:89 .\blog\models.py:101 .\blog\models.py:183 -#: .\templates\blog\tags\sidebar.html:40 -msgid "category" -msgstr "分类目录" - -#: .\blog\models.py:20 .\blog\models.py:179 .\templates\share_layout\nav.html:8 -msgid "index" -msgstr "首页" - -#: .\blog\models.py:21 -msgid "list" -msgstr "列表" - -#: .\blog\models.py:22 -msgid "post" -msgstr "文章" - -#: .\blog\models.py:23 -msgid "all" -msgstr "所有" - -#: .\blog\models.py:24 -msgid "slide" -msgstr "侧边栏" - -#: .\blog\models.py:30 .\blog\models.py:267 .\blog\models.py:285 -msgid "modify time" -msgstr "修改时间" - -#: .\blog\models.py:63 -msgid "Draft" -msgstr "草稿" - -#: .\blog\models.py:64 -msgid "Published" -msgstr "发布" - -#: .\blog\models.py:67 -msgid "Open" -msgstr "打开" - -#: .\blog\models.py:68 -msgid "Close" -msgstr "关闭" - -#: .\blog\models.py:71 .\comments\admin.py:47 -msgid "Article" -msgstr "文章" - -#: .\blog\models.py:72 -msgid "Page" -msgstr "页面" - -#: .\blog\models.py:74 .\blog\models.py:280 -msgid "title" -msgstr "标题" - -#: .\blog\models.py:75 -msgid "body" -msgstr "内容" - -#: .\blog\models.py:77 -msgid "publish time" -msgstr "发布时间" - -#: .\blog\models.py:79 -msgid "status" -msgstr "状态" - -#: .\blog\models.py:84 -msgid "comment status" -msgstr "评论状态" - -#: .\blog\models.py:88 .\oauth\models.py:43 -msgid "type" -msgstr "类型" - -#: .\blog\models.py:89 -msgid "views" -msgstr "阅读量" - -#: .\blog\models.py:97 .\blog\models.py:258 .\blog\models.py:282 -msgid "order" -msgstr "排序" - -#: .\blog\models.py:98 -msgid "show toc" -msgstr "显示目录" - -#: .\blog\models.py:105 .\blog\models.py:249 -msgid "tag" -msgstr "标签" - -#: .\blog\models.py:115 .\comments\models.py:21 -msgid "article" -msgstr "文章" - -#: .\blog\models.py:171 -msgid "category name" -msgstr "分类名" - -#: .\blog\models.py:174 -msgid "parent category" -msgstr "上级分类" - -#: .\blog\models.py:234 -msgid "tag name" -msgstr "标签名" - -#: .\blog\models.py:256 -msgid "link name" -msgstr "链接名" - -#: .\blog\models.py:257 .\blog\models.py:271 -msgid "link" -msgstr "链接" - -#: .\blog\models.py:260 -msgid "is show" -msgstr "是否显示" - -#: .\blog\models.py:262 -msgid "show type" -msgstr "显示类型" - -#: .\blog\models.py:281 -msgid "content" -msgstr "内容" - -#: .\blog\models.py:283 .\oauth\models.py:52 -msgid "is enable" -msgstr "是否启用" - -#: .\blog\models.py:289 -msgid "sidebar" -msgstr "侧边栏" - -#: .\blog\models.py:299 -msgid "site name" -msgstr "站点名称" - -#: .\blog\models.py:305 -msgid "site description" -msgstr "站点描述" - -#: .\blog\models.py:311 -msgid "site seo description" -msgstr "站点SEO描述" - -#: .\blog\models.py:313 -msgid "site keywords" -msgstr "关键字" - -#: .\blog\models.py:318 -msgid "article sub length" -msgstr "文章摘要长度" - -#: .\blog\models.py:319 -msgid "sidebar article count" -msgstr "侧边栏文章数目" - -#: .\blog\models.py:320 -msgid "sidebar comment count" -msgstr "侧边栏评论数目" - -#: .\blog\models.py:321 -msgid "article comment count" -msgstr "文章页面默认显示评论数目" - -#: .\blog\models.py:322 -msgid "show adsense" -msgstr "是否显示广告" - -#: .\blog\models.py:324 -msgid "adsense code" -msgstr "广告内容" - -#: .\blog\models.py:325 -msgid "open site comment" -msgstr "公共头部" - -#: .\blog\models.py:352 -msgid "Website configuration" -msgstr "网站配置" - -#: .\blog\models.py:360 -msgid "There can only be one configuration" -msgstr "只能有一个配置" - -#: .\blog\views.py:348 -msgid "" -"Sorry, the page you requested is not found, please click the home page to " -"see other?" -msgstr "抱歉,你所访问的页面找不到,请点击首页看看别的?" - -#: .\blog\views.py:356 -msgid "Sorry, the server is busy, please click the home page to see other?" -msgstr "抱歉,服务出错了,请点击首页看看别的?" - -#: .\blog\views.py:369 -msgid "Sorry, you do not have permission to access this page?" -msgstr "抱歉,你没用权限访问此页面。" - -#: .\comments\admin.py:15 -msgid "Disable comments" -msgstr "禁用评论" - -#: .\comments\admin.py:16 -msgid "Enable comments" -msgstr "启用评论" - -#: .\comments\admin.py:46 -msgid "User" -msgstr "用户" - -#: .\comments\models.py:25 -msgid "parent comment" -msgstr "上级评论" - -#: .\comments\models.py:29 -msgid "enable" -msgstr "启用" - -#: .\comments\models.py:34 .\templates\blog\tags\article_info.html:30 -msgid "comment" -msgstr "评论" - -#: .\comments\utils.py:13 -msgid "Thanks for your comment" -msgstr "感谢你的评论" - -#: .\comments\utils.py:15 -#, python-format -msgid "" -"

Thank you very much for your comments on this site

\n" -" You can visit %(article_title)s\n" -" to review your comments,\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this " -"link to your browser.\n" -" %(article_url)s" -msgstr "" -"

非常感谢您对此网站的评论

\n" -" 您可以访问%(article_title)s\n" -"查看您的评论,\n" -"再次感谢您!\n" -"
\n" -" 如果上面的链接打不开,请复制此链接链接到您的浏览器。\n" -"%(article_url)s" - -#: .\comments\utils.py:26 -#, python-format -msgid "" -"Your comment on " -"%(article_title)s
has \n" -" received a reply.
%(comment_body)s\n" -"
\n" -" go check it out!\n" -"
\n" -" If the link above cannot be opened, please copy this " -"link to your browser.\n" -" %(article_url)s\n" -" " -msgstr "" -"您对 %(article_title)s
" -"的评论有\n" -" 收到回复。
%(comment_body)s\n" -"
\n" -"快去看看吧!\n" -"
\n" -" 如果上面的链接打不开,请复制此链接链接到您的浏览器。\n" -" %(article_url)s\n" -" " - -#: .\djangoblog\logentryadmin.py:63 -msgid "object" -msgstr "对象" - -#: .\djangoblog\settings.py:140 -msgid "English" -msgstr "英文" - -#: .\djangoblog\settings.py:141 -msgid "Simplified Chinese" -msgstr "简体中文" - -#: .\djangoblog\settings.py:142 -msgid "Traditional Chinese" -msgstr "繁体中文" - -#: .\oauth\models.py:30 -msgid "oauth user" -msgstr "第三方用户" - -#: .\oauth\models.py:37 -msgid "weibo" -msgstr "微博" - -#: .\oauth\models.py:38 -msgid "google" -msgstr "谷歌" - -#: .\oauth\models.py:48 -msgid "callback url" -msgstr "回调地址" - -#: .\oauth\models.py:59 -msgid "already exists" -msgstr "已经存在" - -#: .\oauth\views.py:154 -#, python-format -msgid "" -"\n" -"

Congratulations, you have successfully bound your email address. You " -"can use\n" -" %(oauthuser_type)s to directly log in to this website without a " -"password.

\n" -" You are welcome to continue to follow this site, the address is\n" -" %(site)s\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this link to your " -"browser.\n" -" %(site)s\n" -" " -msgstr "" -"\n" -"

恭喜你已经绑定成功 你可以使用\n" -" %(oauthuser_type)s 来免密登录本站

\n" -" 欢迎继续关注本站, 地址是\n" -" %(site)s\n" -" 再次感谢你\n" -"
\n" -" 如果上面链接无法打开,请复制此链接到你的浏览器 \n" -" %(site)s\n" -" " - -#: .\oauth\views.py:165 -msgid "Congratulations on your successful binding!" -msgstr "恭喜你绑定成功" - -#: .\oauth\views.py:217 -#, python-format -msgid "" -"\n" -"

Please click the link below to bind your email

\n" -"\n" -" %(url)s\n" -"\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this link " -"to your browser.\n" -"
\n" -" %(url)s\n" -" " -msgstr "" -"\n" -"

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

\n" -"\n" -" %(url)s\n" -"\n" -"再次感谢您!\n" -"
\n" -"如果上面的链接打不开,请复制此链接到您的浏览器。\n" -"%(url)s\n" -" " - -#: .\oauth\views.py:228 .\oauth\views.py:240 -msgid "Bind your email" -msgstr "绑定邮箱" - -#: .\oauth\views.py:242 -msgid "" -"Congratulations, the binding is just one step away. Please log in to your " -"email to check the email to complete the binding. Thank you." -msgstr "恭喜您,还差一步就绑定成功了,请登录您的邮箱查看邮件完成绑定,谢谢。" - -#: .\oauth\views.py:245 -msgid "Binding successful" -msgstr "绑定成功" - -#: .\oauth\views.py:247 -#, python-format -msgid "" -"Congratulations, you have successfully bound your email address. You can use " -"%(oauthuser_type)s to directly log in to this website without a password. " -"You are welcome to continue to follow this site." -msgstr "" -"恭喜您绑定成功,您以后可以使用%(oauthuser_type)s来直接免密码登录本站啦,感谢" -"您对本站对关注。" - -#: .\templates\account\forget_password.html:7 -msgid "forget the password" -msgstr "忘记密码" - -#: .\templates\account\forget_password.html:18 -msgid "get verification code" -msgstr "获取验证码" - -#: .\templates\account\forget_password.html:19 -msgid "submit" -msgstr "提交" - -#: .\templates\account\login.html:36 -msgid "Create Account" -msgstr "创建账号" - -#: .\templates\account\login.html:42 -#| msgid "forget the password" -msgid "Forget Password" -msgstr "忘记密码" - -#: .\templates\account\result.html:18 .\templates\blog\tags\sidebar.html:126 -msgid "login" -msgstr "登录" - -#: .\templates\account\result.html:22 -msgid "back to the homepage" -msgstr "返回首页吧" - -#: .\templates\blog\article_archives.html:7 -#: .\templates\blog\article_archives.html:24 -msgid "article archive" -msgstr "文章归档" - -#: .\templates\blog\article_archives.html:32 -msgid "year" -msgstr "年" - -#: .\templates\blog\article_archives.html:36 -msgid "month" -msgstr "月" - -#: .\templates\blog\tags\article_info.html:12 -msgid "pin to top" -msgstr "置顶" - -#: .\templates\blog\tags\article_info.html:28 -msgid "comments" -msgstr "评论" - -#: .\templates\blog\tags\article_info.html:58 -msgid "toc" -msgstr "目录" - -#: .\templates\blog\tags\article_meta_info.html:6 -msgid "posted in" -msgstr "发布于" - -#: .\templates\blog\tags\article_meta_info.html:14 -msgid "and tagged" -msgstr "并标记为" - -#: .\templates\blog\tags\article_meta_info.html:25 -msgid "by " -msgstr "由" - -#: .\templates\blog\tags\article_meta_info.html:29 -#, python-format -msgid "" -"\n" -" title=\"View all articles published by " -"%(article.author.username)s\"\n" -" " -msgstr "" -"\n" -" title=\"查看所有由 %(article.author.username)s\"发布的文章\n" -" " - -#: .\templates\blog\tags\article_meta_info.html:44 -msgid "on" -msgstr "在" - -#: .\templates\blog\tags\article_meta_info.html:54 -msgid "edit" -msgstr "编辑" - -#: .\templates\blog\tags\article_pagination.html:4 -msgid "article navigation" -msgstr "文章导航" - -#: .\templates\blog\tags\article_pagination.html:9 -msgid "earlier articles" -msgstr "早期文章" - -#: .\templates\blog\tags\article_pagination.html:12 -msgid "newer articles" -msgstr "较新文章" - -#: .\templates\blog\tags\article_tag_list.html:5 -msgid "tags" -msgstr "标签" - -#: .\templates\blog\tags\sidebar.html:7 -msgid "search" -msgstr "搜索" - -#: .\templates\blog\tags\sidebar.html:50 -msgid "recent comments" -msgstr "近期评论" - -#: .\templates\blog\tags\sidebar.html:57 -msgid "published on" -msgstr "发表于" - -#: .\templates\blog\tags\sidebar.html:65 -msgid "recent articles" -msgstr "近期文章" - -#: .\templates\blog\tags\sidebar.html:77 -msgid "bookmark" -msgstr "书签" - -#: .\templates\blog\tags\sidebar.html:96 -msgid "Tag Cloud" -msgstr "标签云" - -#: .\templates\blog\tags\sidebar.html:107 -msgid "Welcome to star or fork the source code of this site" -msgstr "欢迎您STAR或者FORK本站源代码" - -#: .\templates\blog\tags\sidebar.html:118 -msgid "Function" -msgstr "功能" - -#: .\templates\blog\tags\sidebar.html:120 -msgid "management site" -msgstr "管理站点" - -#: .\templates\blog\tags\sidebar.html:122 -msgid "logout" -msgstr "登出" - -#: .\templates\blog\tags\sidebar.html:129 -msgid "Track record" -msgstr "运动轨迹记录" - -#: .\templates\blog\tags\sidebar.html:135 -msgid "Click me to return to the top" -msgstr "点我返回顶部" - -#: .\templates\oauth\oauth_applications.html:5 -#| msgid "login" -msgid "quick login" -msgstr "快捷登录" - -#: .\templates\share_layout\nav.html:26 -msgid "Article archive" -msgstr "文章归档" diff --git a/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.mo b/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.mo deleted file mode 100644 index fe2ea17dc2636742b9c3b8d4eddb6293ee3b7290..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10268 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 diff --git a/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.po b/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.po deleted file mode 100644 index a2920ce..0000000 --- a/DjangoBlog-master/locale/zh_Hant/LC_MESSAGES/django.po +++ /dev/null @@ -1,668 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-13 16:02+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=1; plural=0;\n" - -#: .\accounts\admin.py:12 -msgid "password" -msgstr "密碼" - -#: .\accounts\admin.py:13 -msgid "Enter password again" -msgstr "再次輸入密碼" - -#: .\accounts\admin.py:24 .\accounts\forms.py:89 -msgid "passwords do not match" -msgstr "密碼不匹配" - -#: .\accounts\forms.py:36 -msgid "email already exists" -msgstr "郵箱已存在" - -#: .\accounts\forms.py:46 .\accounts\forms.py:50 -msgid "New password" -msgstr "新密碼" - -#: .\accounts\forms.py:60 -msgid "Confirm password" -msgstr "確認密碼" - -#: .\accounts\forms.py:70 .\accounts\forms.py:116 -msgid "Email" -msgstr "郵箱" - -#: .\accounts\forms.py:76 .\accounts\forms.py:80 -msgid "Code" -msgstr "驗證碼" - -#: .\accounts\forms.py:100 .\accounts\tests.py:194 -msgid "email does not exist" -msgstr "郵箱不存在" - -#: .\accounts\models.py:12 .\oauth\models.py:17 -msgid "nick name" -msgstr "昵稱" - -#: .\accounts\models.py:13 .\blog\models.py:29 .\blog\models.py:266 -#: .\blog\models.py:284 .\comments\models.py:13 .\oauth\models.py:23 -#: .\oauth\models.py:53 -msgid "creation time" -msgstr "創建時間" - -#: .\accounts\models.py:14 .\comments\models.py:14 .\oauth\models.py:24 -#: .\oauth\models.py:54 -msgid "last modify time" -msgstr "最後修改時間" - -#: .\accounts\models.py:15 -msgid "create source" -msgstr "來源" - -#: .\accounts\models.py:33 .\djangoblog\logentryadmin.py:81 -msgid "user" -msgstr "用戶" - -#: .\accounts\tests.py:216 .\accounts\utils.py:39 -msgid "Verification code error" -msgstr "驗證碼錯誤" - -#: .\accounts\utils.py:13 -msgid "Verify Email" -msgstr "驗證郵箱" - -#: .\accounts\utils.py:21 -#, python-format -msgid "" -"You are resetting the password, the verification code is:%(code)s, valid " -"within 5 minutes, please keep it properly" -msgstr "您正在重置密碼,驗證碼為:%(code)s,5分鐘內有效 請妥善保管." - -#: .\blog\admin.py:13 .\blog\models.py:92 .\comments\models.py:17 -#: .\oauth\models.py:12 -msgid "author" -msgstr "作者" - -#: .\blog\admin.py:53 -msgid "Publish selected articles" -msgstr "發布選中的文章" - -#: .\blog\admin.py:54 -msgid "Draft selected articles" -msgstr "選中文章設為草稿" - -#: .\blog\admin.py:55 -msgid "Close article comments" -msgstr "關閉文章評論" - -#: .\blog\admin.py:56 -msgid "Open article comments" -msgstr "打開文章評論" - -#: .\blog\admin.py:89 .\blog\models.py:101 .\blog\models.py:183 -#: .\templates\blog\tags\sidebar.html:40 -msgid "category" -msgstr "分類目錄" - -#: .\blog\models.py:20 .\blog\models.py:179 .\templates\share_layout\nav.html:8 -msgid "index" -msgstr "首頁" - -#: .\blog\models.py:21 -msgid "list" -msgstr "列表" - -#: .\blog\models.py:22 -msgid "post" -msgstr "文章" - -#: .\blog\models.py:23 -msgid "all" -msgstr "所有" - -#: .\blog\models.py:24 -msgid "slide" -msgstr "側邊欄" - -#: .\blog\models.py:30 .\blog\models.py:267 .\blog\models.py:285 -msgid "modify time" -msgstr "修改時間" - -#: .\blog\models.py:63 -msgid "Draft" -msgstr "草稿" - -#: .\blog\models.py:64 -msgid "Published" -msgstr "發布" - -#: .\blog\models.py:67 -msgid "Open" -msgstr "打開" - -#: .\blog\models.py:68 -msgid "Close" -msgstr "關閉" - -#: .\blog\models.py:71 .\comments\admin.py:47 -msgid "Article" -msgstr "文章" - -#: .\blog\models.py:72 -msgid "Page" -msgstr "頁面" - -#: .\blog\models.py:74 .\blog\models.py:280 -msgid "title" -msgstr "標題" - -#: .\blog\models.py:75 -msgid "body" -msgstr "內容" - -#: .\blog\models.py:77 -msgid "publish time" -msgstr "發布時間" - -#: .\blog\models.py:79 -msgid "status" -msgstr "狀態" - -#: .\blog\models.py:84 -msgid "comment status" -msgstr "評論狀態" - -#: .\blog\models.py:88 .\oauth\models.py:43 -msgid "type" -msgstr "類型" - -#: .\blog\models.py:89 -msgid "views" -msgstr "閱讀量" - -#: .\blog\models.py:97 .\blog\models.py:258 .\blog\models.py:282 -msgid "order" -msgstr "排序" - -#: .\blog\models.py:98 -msgid "show toc" -msgstr "顯示目錄" - -#: .\blog\models.py:105 .\blog\models.py:249 -msgid "tag" -msgstr "標簽" - -#: .\blog\models.py:115 .\comments\models.py:21 -msgid "article" -msgstr "文章" - -#: .\blog\models.py:171 -msgid "category name" -msgstr "分類名" - -#: .\blog\models.py:174 -msgid "parent category" -msgstr "上級分類" - -#: .\blog\models.py:234 -msgid "tag name" -msgstr "標簽名" - -#: .\blog\models.py:256 -msgid "link name" -msgstr "鏈接名" - -#: .\blog\models.py:257 .\blog\models.py:271 -msgid "link" -msgstr "鏈接" - -#: .\blog\models.py:260 -msgid "is show" -msgstr "是否顯示" - -#: .\blog\models.py:262 -msgid "show type" -msgstr "顯示類型" - -#: .\blog\models.py:281 -msgid "content" -msgstr "內容" - -#: .\blog\models.py:283 .\oauth\models.py:52 -msgid "is enable" -msgstr "是否啟用" - -#: .\blog\models.py:289 -msgid "sidebar" -msgstr "側邊欄" - -#: .\blog\models.py:299 -msgid "site name" -msgstr "站點名稱" - -#: .\blog\models.py:305 -msgid "site description" -msgstr "站點描述" - -#: .\blog\models.py:311 -msgid "site seo description" -msgstr "站點SEO描述" - -#: .\blog\models.py:313 -msgid "site keywords" -msgstr "關鍵字" - -#: .\blog\models.py:318 -msgid "article sub length" -msgstr "文章摘要長度" - -#: .\blog\models.py:319 -msgid "sidebar article count" -msgstr "側邊欄文章數目" - -#: .\blog\models.py:320 -msgid "sidebar comment count" -msgstr "側邊欄評論數目" - -#: .\blog\models.py:321 -msgid "article comment count" -msgstr "文章頁面默認顯示評論數目" - -#: .\blog\models.py:322 -msgid "show adsense" -msgstr "是否顯示廣告" - -#: .\blog\models.py:324 -msgid "adsense code" -msgstr "廣告內容" - -#: .\blog\models.py:325 -msgid "open site comment" -msgstr "公共頭部" - -#: .\blog\models.py:352 -msgid "Website configuration" -msgstr "網站配置" - -#: .\blog\models.py:360 -msgid "There can only be one configuration" -msgstr "只能有一個配置" - -#: .\blog\views.py:348 -msgid "" -"Sorry, the page you requested is not found, please click the home page to " -"see other?" -msgstr "抱歉,你所訪問的頁面找不到,請點擊首頁看看別的?" - -#: .\blog\views.py:356 -msgid "Sorry, the server is busy, please click the home page to see other?" -msgstr "抱歉,服務出錯了,請點擊首頁看看別的?" - -#: .\blog\views.py:369 -msgid "Sorry, you do not have permission to access this page?" -msgstr "抱歉,你沒用權限訪問此頁面。" - -#: .\comments\admin.py:15 -msgid "Disable comments" -msgstr "禁用評論" - -#: .\comments\admin.py:16 -msgid "Enable comments" -msgstr "啟用評論" - -#: .\comments\admin.py:46 -msgid "User" -msgstr "用戶" - -#: .\comments\models.py:25 -msgid "parent comment" -msgstr "上級評論" - -#: .\comments\models.py:29 -msgid "enable" -msgstr "啟用" - -#: .\comments\models.py:34 .\templates\blog\tags\article_info.html:30 -msgid "comment" -msgstr "評論" - -#: .\comments\utils.py:13 -msgid "Thanks for your comment" -msgstr "感謝你的評論" - -#: .\comments\utils.py:15 -#, python-format -msgid "" -"

Thank you very much for your comments on this site

\n" -" You can visit %(article_title)s\n" -" to review your comments,\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this " -"link to your browser.\n" -" %(article_url)s" -msgstr "" -"

非常感謝您對此網站的評論

\n" -" 您可以訪問%(article_title)s\n" -"查看您的評論,\n" -"再次感謝您!\n" -"
\n" -" 如果上面的鏈接打不開,請復製此鏈接鏈接到您的瀏覽器。\n" -"%(article_url)s" - -#: .\comments\utils.py:26 -#, python-format -msgid "" -"Your comment on " -"%(article_title)s
has \n" -" received a reply.
%(comment_body)s\n" -"
\n" -" go check it out!\n" -"
\n" -" If the link above cannot be opened, please copy this " -"link to your browser.\n" -" %(article_url)s\n" -" " -msgstr "" -"您對 %(article_title)s
" -"的評論有\n" -" 收到回復。
%(comment_body)s\n" -"
\n" -"快去看看吧!\n" -"
\n" -" 如果上面的鏈接打不開,請復製此鏈接鏈接到您的瀏覽器。\n" -" %(article_url)s\n" -" " - -#: .\djangoblog\logentryadmin.py:63 -msgid "object" -msgstr "對象" - -#: .\djangoblog\settings.py:140 -msgid "English" -msgstr "英文" - -#: .\djangoblog\settings.py:141 -msgid "Simplified Chinese" -msgstr "簡體中文" - -#: .\djangoblog\settings.py:142 -msgid "Traditional Chinese" -msgstr "繁體中文" - -#: .\oauth\models.py:30 -msgid "oauth user" -msgstr "第三方用戶" - -#: .\oauth\models.py:37 -msgid "weibo" -msgstr "微博" - -#: .\oauth\models.py:38 -msgid "google" -msgstr "谷歌" - -#: .\oauth\models.py:48 -msgid "callback url" -msgstr "回調地址" - -#: .\oauth\models.py:59 -msgid "already exists" -msgstr "已經存在" - -#: .\oauth\views.py:154 -#, python-format -msgid "" -"\n" -"

Congratulations, you have successfully bound your email address. You " -"can use\n" -" %(oauthuser_type)s to directly log in to this website without a " -"password.

\n" -" You are welcome to continue to follow this site, the address is\n" -" %(site)s\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this link to your " -"browser.\n" -" %(site)s\n" -" " -msgstr "" -"\n" -"

恭喜你已經綁定成功 你可以使用\n" -" %(oauthuser_type)s 來免密登錄本站

\n" -" 歡迎繼續關註本站, 地址是\n" -" %(site)s\n" -" 再次感謝你\n" -"
\n" -" 如果上面鏈接無法打開,請復製此鏈接到你的瀏覽器 \n" -" %(site)s\n" -" " - -#: .\oauth\views.py:165 -msgid "Congratulations on your successful binding!" -msgstr "恭喜你綁定成功" - -#: .\oauth\views.py:217 -#, python-format -msgid "" -"\n" -"

Please click the link below to bind your email

\n" -"\n" -" %(url)s\n" -"\n" -" Thank you again!\n" -"
\n" -" If the link above cannot be opened, please copy this link " -"to your browser.\n" -"
\n" -" %(url)s\n" -" " -msgstr "" -"\n" -"

請點擊下面的鏈接綁定您的郵箱

\n" -"\n" -" %(url)s\n" -"\n" -"再次感謝您!\n" -"
\n" -"如果上面的鏈接打不開,請復製此鏈接到您的瀏覽器。\n" -"%(url)s\n" -" " - -#: .\oauth\views.py:228 .\oauth\views.py:240 -msgid "Bind your email" -msgstr "綁定郵箱" - -#: .\oauth\views.py:242 -msgid "" -"Congratulations, the binding is just one step away. Please log in to your " -"email to check the email to complete the binding. Thank you." -msgstr "恭喜您,還差一步就綁定成功了,請登錄您的郵箱查看郵件完成綁定,謝謝。" - -#: .\oauth\views.py:245 -msgid "Binding successful" -msgstr "綁定成功" - -#: .\oauth\views.py:247 -#, python-format -msgid "" -"Congratulations, you have successfully bound your email address. You can use " -"%(oauthuser_type)s to directly log in to this website without a password. " -"You are welcome to continue to follow this site." -msgstr "" -"恭喜您綁定成功,您以後可以使用%(oauthuser_type)s來直接免密碼登錄本站啦,感謝" -"您對本站對關註。" - -#: .\templates\account\forget_password.html:7 -msgid "forget the password" -msgstr "忘記密碼" - -#: .\templates\account\forget_password.html:18 -msgid "get verification code" -msgstr "獲取驗證碼" - -#: .\templates\account\forget_password.html:19 -msgid "submit" -msgstr "提交" - -#: .\templates\account\login.html:36 -msgid "Create Account" -msgstr "創建賬號" - -#: .\templates\account\login.html:42 -#, fuzzy -#| msgid "forget the password" -msgid "Forget Password" -msgstr "忘記密碼" - -#: .\templates\account\result.html:18 .\templates\blog\tags\sidebar.html:126 -msgid "login" -msgstr "登錄" - -#: .\templates\account\result.html:22 -msgid "back to the homepage" -msgstr "返回首頁吧" - -#: .\templates\blog\article_archives.html:7 -#: .\templates\blog\article_archives.html:24 -msgid "article archive" -msgstr "文章歸檔" - -#: .\templates\blog\article_archives.html:32 -msgid "year" -msgstr "年" - -#: .\templates\blog\article_archives.html:36 -msgid "month" -msgstr "月" - -#: .\templates\blog\tags\article_info.html:12 -msgid "pin to top" -msgstr "置頂" - -#: .\templates\blog\tags\article_info.html:28 -msgid "comments" -msgstr "評論" - -#: .\templates\blog\tags\article_info.html:58 -msgid "toc" -msgstr "目錄" - -#: .\templates\blog\tags\article_meta_info.html:6 -msgid "posted in" -msgstr "發布於" - -#: .\templates\blog\tags\article_meta_info.html:14 -msgid "and tagged" -msgstr "並標記為" - -#: .\templates\blog\tags\article_meta_info.html:25 -msgid "by " -msgstr "由" - -#: .\templates\blog\tags\article_meta_info.html:29 -#, python-format -msgid "" -"\n" -" title=\"View all articles published by " -"%(article.author.username)s\"\n" -" " -msgstr "" -"\n" -" title=\"查看所有由 %(article.author.username)s\"發布的文章\n" -" " - -#: .\templates\blog\tags\article_meta_info.html:44 -msgid "on" -msgstr "在" - -#: .\templates\blog\tags\article_meta_info.html:54 -msgid "edit" -msgstr "編輯" - -#: .\templates\blog\tags\article_pagination.html:4 -msgid "article navigation" -msgstr "文章導航" - -#: .\templates\blog\tags\article_pagination.html:9 -msgid "earlier articles" -msgstr "早期文章" - -#: .\templates\blog\tags\article_pagination.html:12 -msgid "newer articles" -msgstr "較新文章" - -#: .\templates\blog\tags\article_tag_list.html:5 -msgid "tags" -msgstr "標簽" - -#: .\templates\blog\tags\sidebar.html:7 -msgid "search" -msgstr "搜索" - -#: .\templates\blog\tags\sidebar.html:50 -msgid "recent comments" -msgstr "近期評論" - -#: .\templates\blog\tags\sidebar.html:57 -msgid "published on" -msgstr "發表於" - -#: .\templates\blog\tags\sidebar.html:65 -msgid "recent articles" -msgstr "近期文章" - -#: .\templates\blog\tags\sidebar.html:77 -msgid "bookmark" -msgstr "書簽" - -#: .\templates\blog\tags\sidebar.html:96 -msgid "Tag Cloud" -msgstr "標簽雲" - -#: .\templates\blog\tags\sidebar.html:107 -msgid "Welcome to star or fork the source code of this site" -msgstr "歡迎您STAR或者FORK本站源代碼" - -#: .\templates\blog\tags\sidebar.html:118 -msgid "Function" -msgstr "功能" - -#: .\templates\blog\tags\sidebar.html:120 -msgid "management site" -msgstr "管理站點" - -#: .\templates\blog\tags\sidebar.html:122 -msgid "logout" -msgstr "登出" - -#: .\templates\blog\tags\sidebar.html:129 -msgid "Track record" -msgstr "運動軌跡記錄" - -#: .\templates\blog\tags\sidebar.html:135 -msgid "Click me to return to the top" -msgstr "點我返回頂部" - -#: .\templates\oauth\oauth_applications.html:5 -#| msgid "login" -msgid "quick login" -msgstr "快捷登錄" - -#: .\templates\share_layout\nav.html:26 -msgid "Article archive" -msgstr "文章歸檔" diff --git a/DjangoBlog-master/manage.py b/DjangoBlog-master/manage.py deleted file mode 100644 index 919ba74..0000000 --- a/DjangoBlog-master/manage.py +++ /dev/null @@ -1,22 +0,0 @@ -#!/usr/bin/env python -import os -import sys - -if __name__ == "__main__": - os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoblog.settings") - try: - from django.core.management import execute_from_command_line - except ImportError: - # The above import may fail for some other reason. Ensure that the - # issue is really that Django is missing to avoid masking other - # exceptions on Python 2. - try: - import django - except ImportError: - 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?" - ) - raise - execute_from_command_line(sys.argv) diff --git a/DjangoBlog-master/oauth/__init__.py b/DjangoBlog-master/oauth/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/oauth/admin.py b/DjangoBlog-master/oauth/admin.py deleted file mode 100644 index 57eab5f..0000000 --- a/DjangoBlog-master/oauth/admin.py +++ /dev/null @@ -1,54 +0,0 @@ -import logging - -from django.contrib import admin -# Register your models here. -from django.urls import reverse -from django.utils.html import format_html - -logger = logging.getLogger(__name__) - - -class OAuthUserAdmin(admin.ModelAdmin): - search_fields = ('nickname', 'email') - list_per_page = 20 - list_display = ( - 'id', - 'nickname', - 'link_to_usermodel', - 'show_user_image', - 'type', - 'email', - ) - list_display_links = ('id', 'nickname') - list_filter = ('author', 'type',) - readonly_fields = [] - - def get_readonly_fields(self, request, obj=None): - return list(self.readonly_fields) + \ - [field.name for field in obj._meta.fields] + \ - [field.name for field in obj._meta.many_to_many] - - def has_add_permission(self, request): - return False - - def link_to_usermodel(self, obj): - if obj.author: - info = (obj.author._meta.app_label, obj.author._meta.model_name) - link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,)) - return format_html( - u'%s' % - (link, obj.author.nickname if obj.author.nickname else obj.author.email)) - - def show_user_image(self, obj): - img = obj.picture - return format_html( - u'' % - (img)) - - link_to_usermodel.short_description = '用户' - show_user_image.short_description = '用户头像' - - -class OAuthConfigAdmin(admin.ModelAdmin): - list_display = ('type', 'appkey', 'appsecret', 'is_enable') - list_filter = ('type',) diff --git a/DjangoBlog-master/oauth/apps.py b/DjangoBlog-master/oauth/apps.py deleted file mode 100644 index 17fcea2..0000000 --- a/DjangoBlog-master/oauth/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class OauthConfig(AppConfig): - name = 'oauth' diff --git a/DjangoBlog-master/oauth/forms.py b/DjangoBlog-master/oauth/forms.py deleted file mode 100644 index 0e4ede3..0000000 --- a/DjangoBlog-master/oauth/forms.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.contrib.auth.forms import forms -from django.forms import widgets - - -class RequireEmailForm(forms.Form): - email = forms.EmailField(label='电子邮箱', required=True) - oauthid = forms.IntegerField(widget=forms.HiddenInput, required=False) - - def __init__(self, *args, **kwargs): - super(RequireEmailForm, self).__init__(*args, **kwargs) - self.fields['email'].widget = widgets.EmailInput( - attrs={'placeholder': "email", "class": "form-control"}) diff --git a/DjangoBlog-master/oauth/migrations/0001_initial.py b/DjangoBlog-master/oauth/migrations/0001_initial.py deleted file mode 100644 index 3aa3e03..0000000 --- a/DjangoBlog-master/oauth/migrations/0001_initial.py +++ /dev/null @@ -1,57 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-07 09:53 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='OAuthConfig', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('type', models.CharField(choices=[('weibo', '微博'), ('google', '谷歌'), ('github', 'GitHub'), ('facebook', 'FaceBook'), ('qq', 'QQ')], default='a', max_length=10, verbose_name='类型')), - ('appkey', models.CharField(max_length=200, verbose_name='AppKey')), - ('appsecret', models.CharField(max_length=200, verbose_name='AppSecret')), - ('callback_url', models.CharField(default='http://www.baidu.com', max_length=200, verbose_name='回调地址')), - ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ], - options={ - 'verbose_name': 'oauth配置', - 'verbose_name_plural': 'oauth配置', - 'ordering': ['-created_time'], - }, - ), - migrations.CreateModel( - name='OAuthUser', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('openid', models.CharField(max_length=50)), - ('nickname', models.CharField(max_length=50, verbose_name='昵称')), - ('token', models.CharField(blank=True, max_length=150, null=True)), - ('picture', models.CharField(blank=True, max_length=350, null=True)), - ('type', models.CharField(max_length=50)), - ('email', models.CharField(blank=True, max_length=50, null=True)), - ('metadata', models.TextField(blank=True, null=True)), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')), - ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')), - ], - options={ - 'verbose_name': 'oauth用户', - 'verbose_name_plural': 'oauth用户', - 'ordering': ['-created_time'], - }, - ), - ] diff --git a/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py b/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py deleted file mode 100644 index d5cc70e..0000000 --- a/DjangoBlog-master/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py +++ /dev/null @@ -1,86 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-06 13:13 - -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion -import django.utils.timezone - - -class Migration(migrations.Migration): - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ('oauth', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='oauthconfig', - options={'ordering': ['-creation_time'], 'verbose_name': 'oauth配置', 'verbose_name_plural': 'oauth配置'}, - ), - migrations.AlterModelOptions( - name='oauthuser', - options={'ordering': ['-creation_time'], 'verbose_name': 'oauth user', 'verbose_name_plural': 'oauth user'}, - ), - migrations.RemoveField( - model_name='oauthconfig', - name='created_time', - ), - migrations.RemoveField( - model_name='oauthconfig', - name='last_mod_time', - ), - migrations.RemoveField( - model_name='oauthuser', - name='created_time', - ), - migrations.RemoveField( - model_name='oauthuser', - name='last_mod_time', - ), - migrations.AddField( - model_name='oauthconfig', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='oauthconfig', - name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'), - ), - migrations.AddField( - model_name='oauthuser', - name='creation_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'), - ), - migrations.AddField( - model_name='oauthuser', - name='last_modify_time', - field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'), - ), - migrations.AlterField( - model_name='oauthconfig', - name='callback_url', - field=models.CharField(default='', max_length=200, verbose_name='callback url'), - ), - migrations.AlterField( - model_name='oauthconfig', - name='is_enable', - field=models.BooleanField(default=True, verbose_name='is enable'), - ), - migrations.AlterField( - model_name='oauthconfig', - name='type', - field=models.CharField(choices=[('weibo', 'weibo'), ('google', 'google'), ('github', 'GitHub'), ('facebook', 'FaceBook'), ('qq', 'QQ')], default='a', max_length=10, verbose_name='type'), - ), - migrations.AlterField( - model_name='oauthuser', - name='author', - field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'), - ), - migrations.AlterField( - model_name='oauthuser', - name='nickname', - field=models.CharField(max_length=50, verbose_name='nickname'), - ), - ] diff --git a/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py b/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py deleted file mode 100644 index 6af08eb..0000000 --- a/DjangoBlog-master/oauth/migrations/0003_alter_oauthuser_nickname.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.7 on 2024-01-26 02:41 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('oauth', '0002_alter_oauthconfig_options_alter_oauthuser_options_and_more'), - ] - - operations = [ - migrations.AlterField( - model_name='oauthuser', - name='nickname', - field=models.CharField(max_length=50, verbose_name='nick name'), - ), - ] diff --git a/DjangoBlog-master/oauth/migrations/__init__.py b/DjangoBlog-master/oauth/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/oauth/models.py b/DjangoBlog-master/oauth/models.py deleted file mode 100644 index be838ed..0000000 --- a/DjangoBlog-master/oauth/models.py +++ /dev/null @@ -1,67 +0,0 @@ -# Create your models here. -from django.conf import settings -from django.core.exceptions import ValidationError -from django.db import models -from django.utils.timezone import now -from django.utils.translation import gettext_lazy as _ - - -class OAuthUser(models.Model): - author = models.ForeignKey( - settings.AUTH_USER_MODEL, - verbose_name=_('author'), - blank=True, - null=True, - on_delete=models.CASCADE) - openid = models.CharField(max_length=50) - nickname = models.CharField(max_length=50, verbose_name=_('nick name')) - token = models.CharField(max_length=150, null=True, blank=True) - picture = models.CharField(max_length=350, blank=True, null=True) - type = models.CharField(blank=False, null=False, max_length=50) - email = models.CharField(max_length=50, null=True, blank=True) - metadata = models.TextField(null=True, blank=True) - creation_time = models.DateTimeField(_('creation time'), default=now) - last_modify_time = models.DateTimeField(_('last modify time'), default=now) - - def __str__(self): - return self.nickname - - class Meta: - verbose_name = _('oauth user') - verbose_name_plural = verbose_name - ordering = ['-creation_time'] - - -class OAuthConfig(models.Model): - TYPE = ( - ('weibo', _('weibo')), - ('google', _('google')), - ('github', 'GitHub'), - ('facebook', 'FaceBook'), - ('qq', 'QQ'), - ) - type = models.CharField(_('type'), max_length=10, choices=TYPE, default='a') - appkey = models.CharField(max_length=200, verbose_name='AppKey') - appsecret = models.CharField(max_length=200, verbose_name='AppSecret') - callback_url = models.CharField( - max_length=200, - verbose_name=_('callback url'), - blank=False, - default='') - is_enable = models.BooleanField( - _('is enable'), default=True, blank=False, null=False) - creation_time = models.DateTimeField(_('creation time'), default=now) - last_modify_time = models.DateTimeField(_('last modify time'), default=now) - - def clean(self): - if OAuthConfig.objects.filter( - type=self.type).exclude(id=self.id).count(): - raise ValidationError(_(self.type + _('already exists'))) - - def __str__(self): - return self.type - - class Meta: - verbose_name = 'oauth配置' - verbose_name_plural = verbose_name - ordering = ['-creation_time'] diff --git a/DjangoBlog-master/oauth/oauthmanager.py b/DjangoBlog-master/oauth/oauthmanager.py deleted file mode 100644 index 2e7ceef..0000000 --- a/DjangoBlog-master/oauth/oauthmanager.py +++ /dev/null @@ -1,504 +0,0 @@ -import json -import logging -import os -import urllib.parse -from abc import ABCMeta, abstractmethod - -import requests - -from djangoblog.utils import cache_decorator -from oauth.models import OAuthUser, OAuthConfig - -logger = logging.getLogger(__name__) - - -class OAuthAccessTokenException(Exception): - ''' - oauth授权失败异常 - ''' - - -class BaseOauthManager(metaclass=ABCMeta): - """获取用户授权""" - AUTH_URL = None - """获取token""" - TOKEN_URL = None - """获取用户信息""" - API_URL = None - '''icon图标名''' - ICON_NAME = None - - def __init__(self, access_token=None, openid=None): - self.access_token = access_token - self.openid = openid - - @property - def is_access_token_set(self): - return self.access_token is not None - - @property - def is_authorized(self): - return self.is_access_token_set and self.access_token is not None and self.openid is not None - - @abstractmethod - def get_authorization_url(self, nexturl='/'): - pass - - @abstractmethod - def get_access_token_by_code(self, code): - pass - - @abstractmethod - def get_oauth_userinfo(self): - pass - - @abstractmethod - def get_picture(self, metadata): - pass - - def do_get(self, url, params, headers=None): - rsp = requests.get(url=url, params=params, headers=headers) - logger.info(rsp.text) - return rsp.text - - def do_post(self, url, params, headers=None): - rsp = requests.post(url, params, headers=headers) - logger.info(rsp.text) - return rsp.text - - def get_config(self): - value = OAuthConfig.objects.filter(type=self.ICON_NAME) - return value[0] if value else None - - -class WBOauthManager(BaseOauthManager): - AUTH_URL = 'https://api.weibo.com/oauth2/authorize' - TOKEN_URL = 'https://api.weibo.com/oauth2/access_token' - API_URL = 'https://api.weibo.com/2/users/show.json' - ICON_NAME = 'weibo' - - def __init__(self, access_token=None, openid=None): - config = self.get_config() - self.client_id = config.appkey if config else '' - self.client_secret = config.appsecret if config else '' - self.callback_url = config.callback_url if config else '' - super( - WBOauthManager, - self).__init__( - access_token=access_token, - openid=openid) - - def get_authorization_url(self, nexturl='/'): - params = { - 'client_id': self.client_id, - 'response_type': 'code', - 'redirect_uri': self.callback_url + '&next_url=' + nexturl - } - url = self.AUTH_URL + "?" + urllib.parse.urlencode(params) - return url - - def get_access_token_by_code(self, code): - - params = { - 'client_id': self.client_id, - 'client_secret': self.client_secret, - 'grant_type': 'authorization_code', - 'code': code, - 'redirect_uri': self.callback_url - } - rsp = self.do_post(self.TOKEN_URL, params) - - obj = json.loads(rsp) - if 'access_token' in obj: - self.access_token = str(obj['access_token']) - self.openid = str(obj['uid']) - return self.get_oauth_userinfo() - else: - raise OAuthAccessTokenException(rsp) - - def get_oauth_userinfo(self): - if not self.is_authorized: - return None - params = { - 'uid': self.openid, - 'access_token': self.access_token - } - rsp = self.do_get(self.API_URL, params) - try: - datas = json.loads(rsp) - user = OAuthUser() - user.metadata = rsp - user.picture = datas['avatar_large'] - user.nickname = datas['screen_name'] - user.openid = datas['id'] - user.type = 'weibo' - user.token = self.access_token - if 'email' in datas and datas['email']: - user.email = datas['email'] - return user - except Exception as e: - logger.error(e) - logger.error('weibo oauth error.rsp:' + rsp) - return None - - def get_picture(self, metadata): - datas = json.loads(metadata) - return datas['avatar_large'] - - -class ProxyManagerMixin: - def __init__(self, *args, **kwargs): - if os.environ.get("HTTP_PROXY"): - self.proxies = { - "http": os.environ.get("HTTP_PROXY"), - "https": os.environ.get("HTTP_PROXY") - } - else: - self.proxies = None - - def do_get(self, url, params, headers=None): - rsp = requests.get(url=url, params=params, headers=headers, proxies=self.proxies) - logger.info(rsp.text) - return rsp.text - - def do_post(self, url, params, headers=None): - rsp = requests.post(url, params, headers=headers, proxies=self.proxies) - logger.info(rsp.text) - return rsp.text - - -class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager): - AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth' - TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token' - API_URL = 'https://www.googleapis.com/oauth2/v3/userinfo' - ICON_NAME = 'google' - - def __init__(self, access_token=None, openid=None): - config = self.get_config() - self.client_id = config.appkey if config else '' - self.client_secret = config.appsecret if config else '' - self.callback_url = config.callback_url if config else '' - super( - GoogleOauthManager, - self).__init__( - access_token=access_token, - openid=openid) - - def get_authorization_url(self, nexturl='/'): - params = { - 'client_id': self.client_id, - 'response_type': 'code', - 'redirect_uri': self.callback_url, - 'scope': 'openid email', - } - url = self.AUTH_URL + "?" + urllib.parse.urlencode(params) - return url - - def get_access_token_by_code(self, code): - params = { - 'client_id': self.client_id, - 'client_secret': self.client_secret, - 'grant_type': 'authorization_code', - 'code': code, - - 'redirect_uri': self.callback_url - } - rsp = self.do_post(self.TOKEN_URL, params) - - obj = json.loads(rsp) - - if 'access_token' in obj: - self.access_token = str(obj['access_token']) - self.openid = str(obj['id_token']) - logger.info(self.ICON_NAME + ' oauth ' + rsp) - return self.access_token - else: - raise OAuthAccessTokenException(rsp) - - def get_oauth_userinfo(self): - if not self.is_authorized: - return None - params = { - 'access_token': self.access_token - } - rsp = self.do_get(self.API_URL, params) - try: - - datas = json.loads(rsp) - user = OAuthUser() - user.metadata = rsp - user.picture = datas['picture'] - user.nickname = datas['name'] - user.openid = datas['sub'] - user.token = self.access_token - user.type = 'google' - if datas['email']: - user.email = datas['email'] - return user - except Exception as e: - logger.error(e) - logger.error('google oauth error.rsp:' + rsp) - return None - - def get_picture(self, metadata): - datas = json.loads(metadata) - return datas['picture'] - - -class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager): - AUTH_URL = 'https://github.com/login/oauth/authorize' - TOKEN_URL = 'https://github.com/login/oauth/access_token' - API_URL = 'https://api.github.com/user' - ICON_NAME = 'github' - - def __init__(self, access_token=None, openid=None): - config = self.get_config() - self.client_id = config.appkey if config else '' - self.client_secret = config.appsecret if config else '' - self.callback_url = config.callback_url if config else '' - super( - GitHubOauthManager, - self).__init__( - access_token=access_token, - openid=openid) - - def get_authorization_url(self, next_url='/'): - params = { - 'client_id': self.client_id, - 'response_type': 'code', - 'redirect_uri': f'{self.callback_url}&next_url={next_url}', - 'scope': 'user' - } - url = self.AUTH_URL + "?" + urllib.parse.urlencode(params) - return url - - def get_access_token_by_code(self, code): - params = { - 'client_id': self.client_id, - 'client_secret': self.client_secret, - 'grant_type': 'authorization_code', - 'code': code, - - 'redirect_uri': self.callback_url - } - rsp = self.do_post(self.TOKEN_URL, params) - - from urllib import parse - r = parse.parse_qs(rsp) - if 'access_token' in r: - self.access_token = (r['access_token'][0]) - return self.access_token - else: - raise OAuthAccessTokenException(rsp) - - def get_oauth_userinfo(self): - - rsp = self.do_get(self.API_URL, params={}, headers={ - "Authorization": "token " + self.access_token - }) - try: - datas = json.loads(rsp) - user = OAuthUser() - user.picture = datas['avatar_url'] - user.nickname = datas['name'] - user.openid = datas['id'] - user.type = 'github' - user.token = self.access_token - user.metadata = rsp - if 'email' in datas and datas['email']: - user.email = datas['email'] - return user - except Exception as e: - logger.error(e) - logger.error('github oauth error.rsp:' + rsp) - return None - - def get_picture(self, metadata): - datas = json.loads(metadata) - return datas['avatar_url'] - - -class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager): - AUTH_URL = 'https://www.facebook.com/v16.0/dialog/oauth' - TOKEN_URL = 'https://graph.facebook.com/v16.0/oauth/access_token' - API_URL = 'https://graph.facebook.com/me' - ICON_NAME = 'facebook' - - def __init__(self, access_token=None, openid=None): - config = self.get_config() - self.client_id = config.appkey if config else '' - self.client_secret = config.appsecret if config else '' - self.callback_url = config.callback_url if config else '' - super( - FaceBookOauthManager, - self).__init__( - access_token=access_token, - openid=openid) - - def get_authorization_url(self, next_url='/'): - params = { - 'client_id': self.client_id, - 'response_type': 'code', - 'redirect_uri': self.callback_url, - 'scope': 'email,public_profile' - } - url = self.AUTH_URL + "?" + urllib.parse.urlencode(params) - return url - - def get_access_token_by_code(self, code): - params = { - 'client_id': self.client_id, - 'client_secret': self.client_secret, - # 'grant_type': 'authorization_code', - 'code': code, - - 'redirect_uri': self.callback_url - } - rsp = self.do_post(self.TOKEN_URL, params) - - obj = json.loads(rsp) - if 'access_token' in obj: - token = str(obj['access_token']) - self.access_token = token - return self.access_token - else: - raise OAuthAccessTokenException(rsp) - - def get_oauth_userinfo(self): - params = { - 'access_token': self.access_token, - 'fields': 'id,name,picture,email' - } - try: - rsp = self.do_get(self.API_URL, params) - datas = json.loads(rsp) - user = OAuthUser() - user.nickname = datas['name'] - user.openid = datas['id'] - user.type = 'facebook' - user.token = self.access_token - user.metadata = rsp - if 'email' in datas and datas['email']: - user.email = datas['email'] - if 'picture' in datas and datas['picture'] and datas['picture']['data'] and datas['picture']['data']['url']: - user.picture = str(datas['picture']['data']['url']) - return user - except Exception as e: - logger.error(e) - return None - - def get_picture(self, metadata): - datas = json.loads(metadata) - return str(datas['picture']['data']['url']) - - -class QQOauthManager(BaseOauthManager): - AUTH_URL = 'https://graph.qq.com/oauth2.0/authorize' - TOKEN_URL = 'https://graph.qq.com/oauth2.0/token' - API_URL = 'https://graph.qq.com/user/get_user_info' - OPEN_ID_URL = 'https://graph.qq.com/oauth2.0/me' - ICON_NAME = 'qq' - - def __init__(self, access_token=None, openid=None): - config = self.get_config() - self.client_id = config.appkey if config else '' - self.client_secret = config.appsecret if config else '' - self.callback_url = config.callback_url if config else '' - super( - QQOauthManager, - self).__init__( - access_token=access_token, - openid=openid) - - def get_authorization_url(self, next_url='/'): - params = { - 'response_type': 'code', - 'client_id': self.client_id, - 'redirect_uri': self.callback_url + '&next_url=' + next_url, - } - url = self.AUTH_URL + "?" + urllib.parse.urlencode(params) - return url - - def get_access_token_by_code(self, code): - params = { - 'grant_type': 'authorization_code', - 'client_id': self.client_id, - 'client_secret': self.client_secret, - 'code': code, - 'redirect_uri': self.callback_url - } - rsp = self.do_get(self.TOKEN_URL, params) - if rsp: - d = urllib.parse.parse_qs(rsp) - if 'access_token' in d: - token = d['access_token'] - self.access_token = token[0] - return token - else: - raise OAuthAccessTokenException(rsp) - - def get_open_id(self): - if self.is_access_token_set: - params = { - 'access_token': self.access_token - } - rsp = self.do_get(self.OPEN_ID_URL, params) - if rsp: - rsp = rsp.replace( - 'callback(', '').replace( - ')', '').replace( - ';', '') - obj = json.loads(rsp) - openid = str(obj['openid']) - self.openid = openid - return openid - - def get_oauth_userinfo(self): - openid = self.get_open_id() - if openid: - params = { - 'access_token': self.access_token, - 'oauth_consumer_key': self.client_id, - 'openid': self.openid - } - rsp = self.do_get(self.API_URL, params) - logger.info(rsp) - obj = json.loads(rsp) - user = OAuthUser() - user.nickname = obj['nickname'] - user.openid = openid - user.type = 'qq' - user.token = self.access_token - user.metadata = rsp - if 'email' in obj: - user.email = obj['email'] - if 'figureurl' in obj: - user.picture = str(obj['figureurl']) - return user - - def get_picture(self, metadata): - datas = json.loads(metadata) - return str(datas['figureurl']) - - -@cache_decorator(expiration=100 * 60) -def get_oauth_apps(): - configs = OAuthConfig.objects.filter(is_enable=True).all() - if not configs: - return [] - configtypes = [x.type for x in configs] - applications = BaseOauthManager.__subclasses__() - apps = [x() for x in applications if x().ICON_NAME.lower() in configtypes] - return apps - - -def get_manager_by_type(type): - applications = get_oauth_apps() - if applications: - finds = list( - filter( - lambda x: x.ICON_NAME.lower() == type.lower(), - applications)) - if finds: - return finds[0] - return None diff --git a/DjangoBlog-master/oauth/templatetags/__init__.py b/DjangoBlog-master/oauth/templatetags/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/DjangoBlog-master/oauth/templatetags/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/DjangoBlog-master/oauth/templatetags/oauth_tags.py b/DjangoBlog-master/oauth/templatetags/oauth_tags.py deleted file mode 100644 index 7b687d5..0000000 --- a/DjangoBlog-master/oauth/templatetags/oauth_tags.py +++ /dev/null @@ -1,22 +0,0 @@ -from django import template -from django.urls import reverse - -from oauth.oauthmanager import get_oauth_apps - -register = template.Library() - - -@register.inclusion_tag('oauth/oauth_applications.html') -def load_oauth_applications(request): - applications = get_oauth_apps() - if applications: - baseurl = reverse('oauth:oauthlogin') - path = request.get_full_path() - - apps = list(map(lambda x: (x.ICON_NAME, '{baseurl}?type={type}&next_url={next}'.format( - baseurl=baseurl, type=x.ICON_NAME, next=path)), applications)) - else: - apps = [] - return { - 'apps': apps - } diff --git a/DjangoBlog-master/oauth/tests.py b/DjangoBlog-master/oauth/tests.py deleted file mode 100644 index bb23b9b..0000000 --- a/DjangoBlog-master/oauth/tests.py +++ /dev/null @@ -1,249 +0,0 @@ -import json -from unittest.mock import patch - -from django.conf import settings -from django.contrib import auth -from django.test import Client, RequestFactory, TestCase -from django.urls import reverse - -from djangoblog.utils import get_sha256 -from oauth.models import OAuthConfig -from oauth.oauthmanager import BaseOauthManager - - -# Create your tests here. -class OAuthConfigTest(TestCase): - def setUp(self): - self.client = Client() - self.factory = RequestFactory() - - def test_oauth_login_test(self): - c = OAuthConfig() - c.type = 'weibo' - c.appkey = 'appkey' - c.appsecret = 'appsecret' - c.save() - - response = self.client.get('/oauth/oauthlogin?type=weibo') - self.assertEqual(response.status_code, 302) - self.assertTrue("api.weibo.com" in response.url) - - response = self.client.get('/oauth/authorize?type=weibo&code=code') - self.assertEqual(response.status_code, 302) - self.assertEqual(response.url, '/') - - -class OauthLoginTest(TestCase): - def setUp(self) -> None: - self.client = Client() - self.factory = RequestFactory() - self.apps = self.init_apps() - - def init_apps(self): - applications = [p() for p in BaseOauthManager.__subclasses__()] - for application in applications: - c = OAuthConfig() - c.type = application.ICON_NAME.lower() - c.appkey = 'appkey' - c.appsecret = 'appsecret' - c.save() - return applications - - def get_app_by_type(self, type): - for app in self.apps: - if app.ICON_NAME.lower() == type: - return app - - @patch("oauth.oauthmanager.WBOauthManager.do_post") - @patch("oauth.oauthmanager.WBOauthManager.do_get") - def test_weibo_login(self, mock_do_get, mock_do_post): - weibo_app = self.get_app_by_type('weibo') - assert weibo_app - url = weibo_app.get_authorization_url() - mock_do_post.return_value = json.dumps({"access_token": "access_token", - "uid": "uid" - }) - mock_do_get.return_value = json.dumps({ - "avatar_large": "avatar_large", - "screen_name": "screen_name", - "id": "id", - "email": "email", - }) - userinfo = weibo_app.get_access_token_by_code('code') - self.assertEqual(userinfo.token, 'access_token') - self.assertEqual(userinfo.openid, 'id') - - @patch("oauth.oauthmanager.GoogleOauthManager.do_post") - @patch("oauth.oauthmanager.GoogleOauthManager.do_get") - def test_google_login(self, mock_do_get, mock_do_post): - google_app = self.get_app_by_type('google') - assert google_app - url = google_app.get_authorization_url() - mock_do_post.return_value = json.dumps({ - "access_token": "access_token", - "id_token": "id_token", - }) - mock_do_get.return_value = json.dumps({ - "picture": "picture", - "name": "name", - "sub": "sub", - "email": "email", - }) - token = google_app.get_access_token_by_code('code') - userinfo = google_app.get_oauth_userinfo() - self.assertEqual(userinfo.token, 'access_token') - self.assertEqual(userinfo.openid, 'sub') - - @patch("oauth.oauthmanager.GitHubOauthManager.do_post") - @patch("oauth.oauthmanager.GitHubOauthManager.do_get") - def test_github_login(self, mock_do_get, mock_do_post): - github_app = self.get_app_by_type('github') - assert github_app - url = github_app.get_authorization_url() - self.assertTrue("github.com" in url) - self.assertTrue("client_id" in url) - mock_do_post.return_value = "access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a&scope=repo%2Cgist&token_type=bearer" - mock_do_get.return_value = json.dumps({ - "avatar_url": "avatar_url", - "name": "name", - "id": "id", - "email": "email", - }) - token = github_app.get_access_token_by_code('code') - userinfo = github_app.get_oauth_userinfo() - self.assertEqual(userinfo.token, 'gho_16C7e42F292c6912E7710c838347Ae178B4a') - self.assertEqual(userinfo.openid, 'id') - - @patch("oauth.oauthmanager.FaceBookOauthManager.do_post") - @patch("oauth.oauthmanager.FaceBookOauthManager.do_get") - def test_facebook_login(self, mock_do_get, mock_do_post): - facebook_app = self.get_app_by_type('facebook') - assert facebook_app - url = facebook_app.get_authorization_url() - self.assertTrue("facebook.com" in url) - mock_do_post.return_value = json.dumps({ - "access_token": "access_token", - }) - mock_do_get.return_value = json.dumps({ - "name": "name", - "id": "id", - "email": "email", - "picture": { - "data": { - "url": "url" - } - } - }) - token = facebook_app.get_access_token_by_code('code') - userinfo = facebook_app.get_oauth_userinfo() - self.assertEqual(userinfo.token, 'access_token') - - @patch("oauth.oauthmanager.QQOauthManager.do_get", side_effect=[ - 'access_token=access_token&expires_in=3600', - 'callback({"client_id":"appid","openid":"openid"} );', - json.dumps({ - "nickname": "nickname", - "email": "email", - "figureurl": "figureurl", - "openid": "openid", - }) - ]) - def test_qq_login(self, mock_do_get): - qq_app = self.get_app_by_type('qq') - assert qq_app - url = qq_app.get_authorization_url() - self.assertTrue("qq.com" in url) - token = qq_app.get_access_token_by_code('code') - userinfo = qq_app.get_oauth_userinfo() - self.assertEqual(userinfo.token, 'access_token') - - @patch("oauth.oauthmanager.WBOauthManager.do_post") - @patch("oauth.oauthmanager.WBOauthManager.do_get") - def test_weibo_authoriz_login_with_email(self, mock_do_get, mock_do_post): - - mock_do_post.return_value = json.dumps({"access_token": "access_token", - "uid": "uid" - }) - mock_user_info = { - "avatar_large": "avatar_large", - "screen_name": "screen_name1", - "id": "id", - "email": "email", - } - mock_do_get.return_value = json.dumps(mock_user_info) - - response = self.client.get('/oauth/oauthlogin?type=weibo') - self.assertEqual(response.status_code, 302) - self.assertTrue("api.weibo.com" in response.url) - - response = self.client.get('/oauth/authorize?type=weibo&code=code') - self.assertEqual(response.status_code, 302) - self.assertEqual(response.url, '/') - - user = auth.get_user(self.client) - assert user.is_authenticated - self.assertTrue(user.is_authenticated) - self.assertEqual(user.username, mock_user_info['screen_name']) - self.assertEqual(user.email, mock_user_info['email']) - self.client.logout() - - response = self.client.get('/oauth/authorize?type=weibo&code=code') - self.assertEqual(response.status_code, 302) - self.assertEqual(response.url, '/') - - user = auth.get_user(self.client) - assert user.is_authenticated - self.assertTrue(user.is_authenticated) - self.assertEqual(user.username, mock_user_info['screen_name']) - self.assertEqual(user.email, mock_user_info['email']) - - @patch("oauth.oauthmanager.WBOauthManager.do_post") - @patch("oauth.oauthmanager.WBOauthManager.do_get") - def test_weibo_authoriz_login_without_email(self, mock_do_get, mock_do_post): - - mock_do_post.return_value = json.dumps({"access_token": "access_token", - "uid": "uid" - }) - mock_user_info = { - "avatar_large": "avatar_large", - "screen_name": "screen_name1", - "id": "id", - } - mock_do_get.return_value = json.dumps(mock_user_info) - - response = self.client.get('/oauth/oauthlogin?type=weibo') - self.assertEqual(response.status_code, 302) - self.assertTrue("api.weibo.com" in response.url) - - response = self.client.get('/oauth/authorize?type=weibo&code=code') - - self.assertEqual(response.status_code, 302) - - oauth_user_id = int(response.url.split('/')[-1].split('.')[0]) - self.assertEqual(response.url, f'/oauth/requireemail/{oauth_user_id}.html') - - response = self.client.post(response.url, {'email': 'test@gmail.com', 'oauthid': oauth_user_id}) - - self.assertEqual(response.status_code, 302) - sign = get_sha256(settings.SECRET_KEY + - str(oauth_user_id) + settings.SECRET_KEY) - - url = reverse('oauth:bindsuccess', kwargs={ - 'oauthid': oauth_user_id, - }) - self.assertEqual(response.url, f'{url}?type=email') - - path = reverse('oauth:email_confirm', kwargs={ - 'id': oauth_user_id, - 'sign': sign - }) - response = self.client.get(path) - self.assertEqual(response.status_code, 302) - self.assertEqual(response.url, f'/oauth/bindsuccess/{oauth_user_id}.html?type=success') - user = auth.get_user(self.client) - from oauth.models import OAuthUser - oauth_user = OAuthUser.objects.get(author=user) - self.assertTrue(user.is_authenticated) - self.assertEqual(user.username, mock_user_info['screen_name']) - self.assertEqual(user.email, 'test@gmail.com') - self.assertEqual(oauth_user.pk, oauth_user_id) diff --git a/DjangoBlog-master/oauth/urls.py b/DjangoBlog-master/oauth/urls.py deleted file mode 100644 index c4a12a0..0000000 --- a/DjangoBlog-master/oauth/urls.py +++ /dev/null @@ -1,25 +0,0 @@ -from django.urls import path - -from . import views - -app_name = "oauth" -urlpatterns = [ - path( - r'oauth/authorize', - views.authorize), - path( - r'oauth/requireemail/.html', - views.RequireEmailView.as_view(), - name='require_email'), - path( - r'oauth/emailconfirm//.html', - views.emailconfirm, - name='email_confirm'), - path( - r'oauth/bindsuccess/.html', - views.bindsuccess, - name='bindsuccess'), - path( - r'oauth/oauthlogin', - views.oauthlogin, - name='oauthlogin')] diff --git a/DjangoBlog-master/oauth/views.py b/DjangoBlog-master/oauth/views.py deleted file mode 100644 index 12e3a6e..0000000 --- a/DjangoBlog-master/oauth/views.py +++ /dev/null @@ -1,253 +0,0 @@ -import logging -# Create your views here. -from urllib.parse import urlparse - -from django.conf import settings -from django.contrib.auth import get_user_model -from django.contrib.auth import login -from django.core.exceptions import ObjectDoesNotExist -from django.db import transaction -from django.http import HttpResponseForbidden -from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404 -from django.shortcuts import render -from django.urls import reverse -from django.utils import timezone -from django.utils.translation import gettext_lazy as _ -from django.views.generic import FormView - -from djangoblog.blog_signals import oauth_user_login_signal -from djangoblog.utils import get_current_site -from djangoblog.utils import send_email, get_sha256 -from oauth.forms import RequireEmailForm -from .models import OAuthUser -from .oauthmanager import get_manager_by_type, OAuthAccessTokenException - -logger = logging.getLogger(__name__) - - -def get_redirecturl(request): - nexturl = request.GET.get('next_url', None) - if not nexturl or nexturl == '/login/' or nexturl == '/login': - nexturl = '/' - return nexturl - p = urlparse(nexturl) - if p.netloc: - site = get_current_site().domain - if not p.netloc.replace('www.', '') == site.replace('www.', ''): - logger.info('非法url:' + nexturl) - return "/" - return nexturl - - -def oauthlogin(request): - type = request.GET.get('type', None) - if not type: - return HttpResponseRedirect('/') - manager = get_manager_by_type(type) - if not manager: - return HttpResponseRedirect('/') - nexturl = get_redirecturl(request) - authorizeurl = manager.get_authorization_url(nexturl) - return HttpResponseRedirect(authorizeurl) - - -def authorize(request): - type = request.GET.get('type', None) - if not type: - return HttpResponseRedirect('/') - manager = get_manager_by_type(type) - if not manager: - return HttpResponseRedirect('/') - code = request.GET.get('code', None) - try: - rsp = manager.get_access_token_by_code(code) - except OAuthAccessTokenException as e: - logger.warning("OAuthAccessTokenException:" + str(e)) - return HttpResponseRedirect('/') - except Exception as e: - logger.error(e) - rsp = None - nexturl = get_redirecturl(request) - if not rsp: - return HttpResponseRedirect(manager.get_authorization_url(nexturl)) - user = manager.get_oauth_userinfo() - if user: - if not user.nickname or not user.nickname.strip(): - user.nickname = "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S') - try: - temp = OAuthUser.objects.get(type=type, openid=user.openid) - temp.picture = user.picture - temp.metadata = user.metadata - temp.nickname = user.nickname - user = temp - except ObjectDoesNotExist: - pass - # facebook的token过长 - if type == 'facebook': - user.token = '' - if user.email: - with transaction.atomic(): - author = None - try: - author = get_user_model().objects.get(id=user.author_id) - except ObjectDoesNotExist: - pass - if not author: - result = get_user_model().objects.get_or_create(email=user.email) - author = result[0] - if result[1]: - try: - get_user_model().objects.get(username=user.nickname) - except ObjectDoesNotExist: - author.username = user.nickname - else: - author.username = "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S') - author.source = 'authorize' - author.save() - - user.author = author - user.save() - - oauth_user_login_signal.send( - sender=authorize.__class__, id=user.id) - login(request, author) - return HttpResponseRedirect(nexturl) - else: - user.save() - url = reverse('oauth:require_email', kwargs={ - 'oauthid': user.id - }) - - return HttpResponseRedirect(url) - else: - return HttpResponseRedirect(nexturl) - - -def emailconfirm(request, id, sign): - if not sign: - return HttpResponseForbidden() - if not get_sha256(settings.SECRET_KEY + - str(id) + - settings.SECRET_KEY).upper() == sign.upper(): - return HttpResponseForbidden() - oauthuser = get_object_or_404(OAuthUser, pk=id) - with transaction.atomic(): - if oauthuser.author: - author = get_user_model().objects.get(pk=oauthuser.author_id) - else: - result = get_user_model().objects.get_or_create(email=oauthuser.email) - author = result[0] - if result[1]: - author.source = 'emailconfirm' - author.username = oauthuser.nickname.strip() if oauthuser.nickname.strip( - ) else "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S') - author.save() - oauthuser.author = author - oauthuser.save() - oauth_user_login_signal.send( - sender=emailconfirm.__class__, - id=oauthuser.id) - login(request, author) - - site = 'http://' + get_current_site().domain - content = _(''' -

Congratulations, you have successfully bound your email address. You can use - %(oauthuser_type)s to directly log in to this website without a password.

- You are welcome to continue to follow this site, the address is - %(site)s - Thank you again! -
- If the link above cannot be opened, please copy this link to your browser. - %(site)s - ''') % {'oauthuser_type': oauthuser.type, 'site': site} - - send_email(emailto=[oauthuser.email, ], title=_('Congratulations on your successful binding!'), content=content) - url = reverse('oauth:bindsuccess', kwargs={ - 'oauthid': id - }) - url = url + '?type=success' - return HttpResponseRedirect(url) - - -class RequireEmailView(FormView): - form_class = RequireEmailForm - template_name = 'oauth/require_email.html' - - def get(self, request, *args, **kwargs): - oauthid = self.kwargs['oauthid'] - oauthuser = get_object_or_404(OAuthUser, pk=oauthid) - if oauthuser.email: - pass - # return HttpResponseRedirect('/') - - return super(RequireEmailView, self).get(request, *args, **kwargs) - - def get_initial(self): - oauthid = self.kwargs['oauthid'] - return { - 'email': '', - 'oauthid': oauthid - } - - def get_context_data(self, **kwargs): - oauthid = self.kwargs['oauthid'] - oauthuser = get_object_or_404(OAuthUser, pk=oauthid) - if oauthuser.picture: - kwargs['picture'] = oauthuser.picture - return super(RequireEmailView, self).get_context_data(**kwargs) - - def form_valid(self, form): - email = form.cleaned_data['email'] - oauthid = form.cleaned_data['oauthid'] - oauthuser = get_object_or_404(OAuthUser, pk=oauthid) - oauthuser.email = email - oauthuser.save() - sign = get_sha256(settings.SECRET_KEY + - str(oauthuser.id) + settings.SECRET_KEY) - site = get_current_site().domain - if settings.DEBUG: - site = '127.0.0.1:8000' - path = reverse('oauth:email_confirm', kwargs={ - 'id': oauthid, - 'sign': sign - }) - url = "http://{site}{path}".format(site=site, path=path) - - content = _(""" -

Please click the link below to bind your email

- - %(url)s - - Thank you again! -
- If the link above cannot be opened, please copy this link to your browser. -
- %(url)s - """) % {'url': url} - send_email(emailto=[email, ], title=_('Bind your email'), content=content) - url = reverse('oauth:bindsuccess', kwargs={ - 'oauthid': oauthid - }) - url = url + '?type=email' - return HttpResponseRedirect(url) - - -def bindsuccess(request, oauthid): - type = request.GET.get('type', None) - oauthuser = get_object_or_404(OAuthUser, pk=oauthid) - if type == 'email': - title = _('Bind your email') - content = _( - 'Congratulations, the binding is just one step away. ' - 'Please log in to your email to check the email to complete the binding. Thank you.') - else: - title = _('Binding successful') - content = _( - "Congratulations, you have successfully bound your email address. You can use %(oauthuser_type)s" - " to directly log in to this website without a password. You are welcome to continue to follow this site." % { - 'oauthuser_type': oauthuser.type}) - return render(request, 'oauth/bindsuccess.html', { - 'title': title, - 'content': content - }) diff --git a/DjangoBlog-master/owntracks/__init__.py b/DjangoBlog-master/owntracks/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/owntracks/admin.py b/DjangoBlog-master/owntracks/admin.py deleted file mode 100644 index 655b535..0000000 --- a/DjangoBlog-master/owntracks/admin.py +++ /dev/null @@ -1,7 +0,0 @@ -from django.contrib import admin - -# Register your models here. - - -class OwnTrackLogsAdmin(admin.ModelAdmin): - pass diff --git a/DjangoBlog-master/owntracks/apps.py b/DjangoBlog-master/owntracks/apps.py deleted file mode 100644 index 1bc5f12..0000000 --- a/DjangoBlog-master/owntracks/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class OwntracksConfig(AppConfig): - name = 'owntracks' diff --git a/DjangoBlog-master/owntracks/migrations/0001_initial.py b/DjangoBlog-master/owntracks/migrations/0001_initial.py deleted file mode 100644 index 9eee55c..0000000 --- a/DjangoBlog-master/owntracks/migrations/0001_initial.py +++ /dev/null @@ -1,31 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-02 07:14 - -from django.db import migrations, models -import django.utils.timezone - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='OwnTrackLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('tid', models.CharField(max_length=100, verbose_name='用户')), - ('lat', models.FloatField(verbose_name='纬度')), - ('lon', models.FloatField(verbose_name='经度')), - ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')), - ], - options={ - 'verbose_name': 'OwnTrackLogs', - 'verbose_name_plural': 'OwnTrackLogs', - 'ordering': ['created_time'], - 'get_latest_by': 'created_time', - }, - ), - ] diff --git a/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py b/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py deleted file mode 100644 index b4f8dec..0000000 --- a/DjangoBlog-master/owntracks/migrations/0002_alter_owntracklog_options_and_more.py +++ /dev/null @@ -1,22 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-06 13:19 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('owntracks', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='owntracklog', - options={'get_latest_by': 'creation_time', 'ordering': ['creation_time'], 'verbose_name': 'OwnTrackLogs', 'verbose_name_plural': 'OwnTrackLogs'}, - ), - migrations.RenameField( - model_name='owntracklog', - old_name='created_time', - new_name='creation_time', - ), - ] diff --git a/DjangoBlog-master/owntracks/migrations/__init__.py b/DjangoBlog-master/owntracks/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/owntracks/models.py b/DjangoBlog-master/owntracks/models.py deleted file mode 100644 index 760942c..0000000 --- a/DjangoBlog-master/owntracks/models.py +++ /dev/null @@ -1,20 +0,0 @@ -from django.db import models -from django.utils.timezone import now - - -# Create your models here. - -class OwnTrackLog(models.Model): - tid = models.CharField(max_length=100, null=False, verbose_name='用户') - lat = models.FloatField(verbose_name='纬度') - lon = models.FloatField(verbose_name='经度') - creation_time = models.DateTimeField('创建时间', default=now) - - def __str__(self): - return self.tid - - class Meta: - ordering = ['creation_time'] - verbose_name = "OwnTrackLogs" - verbose_name_plural = verbose_name - get_latest_by = 'creation_time' diff --git a/DjangoBlog-master/owntracks/tests.py b/DjangoBlog-master/owntracks/tests.py deleted file mode 100644 index 3b4b9d8..0000000 --- a/DjangoBlog-master/owntracks/tests.py +++ /dev/null @@ -1,64 +0,0 @@ -import json - -from django.test import Client, RequestFactory, TestCase - -from accounts.models import BlogUser -from .models import OwnTrackLog - - -# Create your tests here. - -class OwnTrackLogTest(TestCase): - def setUp(self): - self.client = Client() - self.factory = RequestFactory() - - def test_own_track_log(self): - o = { - 'tid': 12, - 'lat': 123.123, - 'lon': 134.341 - } - - self.client.post( - '/owntracks/logtracks', - json.dumps(o), - content_type='application/json') - length = len(OwnTrackLog.objects.all()) - self.assertEqual(length, 1) - - o = { - 'tid': 12, - 'lat': 123.123 - } - - self.client.post( - '/owntracks/logtracks', - json.dumps(o), - content_type='application/json') - length = len(OwnTrackLog.objects.all()) - self.assertEqual(length, 1) - - rsp = self.client.get('/owntracks/show_maps') - self.assertEqual(rsp.status_code, 302) - - user = BlogUser.objects.create_superuser( - email="liangliangyy1@gmail.com", - username="liangliangyy1", - password="liangliangyy1") - - self.client.login(username='liangliangyy1', password='liangliangyy1') - s = OwnTrackLog() - s.tid = 12 - s.lon = 123.234 - s.lat = 34.234 - s.save() - - rsp = self.client.get('/owntracks/show_dates') - self.assertEqual(rsp.status_code, 200) - rsp = self.client.get('/owntracks/show_maps') - self.assertEqual(rsp.status_code, 200) - rsp = self.client.get('/owntracks/get_datas') - self.assertEqual(rsp.status_code, 200) - rsp = self.client.get('/owntracks/get_datas?date=2018-02-26') - self.assertEqual(rsp.status_code, 200) diff --git a/DjangoBlog-master/owntracks/urls.py b/DjangoBlog-master/owntracks/urls.py deleted file mode 100644 index c19ada8..0000000 --- a/DjangoBlog-master/owntracks/urls.py +++ /dev/null @@ -1,12 +0,0 @@ -from django.urls import path - -from . import views - -app_name = "owntracks" - -urlpatterns = [ - path('owntracks/logtracks', views.manage_owntrack_log, name='logtracks'), - path('owntracks/show_maps', views.show_maps, name='show_maps'), - path('owntracks/get_datas', views.get_datas, name='get_datas'), - path('owntracks/show_dates', views.show_log_dates, name='show_dates') -] diff --git a/DjangoBlog-master/owntracks/views.py b/DjangoBlog-master/owntracks/views.py deleted file mode 100644 index 4c72bdd..0000000 --- a/DjangoBlog-master/owntracks/views.py +++ /dev/null @@ -1,127 +0,0 @@ -# Create your views here. -import datetime -import itertools -import json -import logging -from datetime import timezone -from itertools import groupby - -import django -import requests -from django.contrib.auth.decorators import login_required -from django.http import HttpResponse -from django.http import JsonResponse -from django.shortcuts import render -from django.views.decorators.csrf import csrf_exempt - -from .models import OwnTrackLog - -logger = logging.getLogger(__name__) - - -@csrf_exempt -def manage_owntrack_log(request): - try: - s = json.loads(request.read().decode('utf-8')) - tid = s['tid'] - lat = s['lat'] - lon = s['lon'] - - logger.info( - 'tid:{tid}.lat:{lat}.lon:{lon}'.format( - tid=tid, lat=lat, lon=lon)) - if tid and lat and lon: - m = OwnTrackLog() - m.tid = tid - m.lat = lat - m.lon = lon - m.save() - return HttpResponse('ok') - else: - return HttpResponse('data error') - except Exception as e: - logger.error(e) - return HttpResponse('error') - - -@login_required -def show_maps(request): - if request.user.is_superuser: - defaultdate = str(datetime.datetime.now(timezone.utc).date()) - date = request.GET.get('date', defaultdate) - context = { - 'date': date - } - return render(request, 'owntracks/show_maps.html', context) - else: - from django.http import HttpResponseForbidden - return HttpResponseForbidden() - - -@login_required -def show_log_dates(request): - dates = OwnTrackLog.objects.values_list('creation_time', flat=True) - results = list(sorted(set(map(lambda x: x.strftime('%Y-%m-%d'), dates)))) - - context = { - 'results': results - } - return render(request, 'owntracks/show_log_dates.html', context) - - -def convert_to_amap(locations): - convert_result = [] - it = iter(locations) - - item = list(itertools.islice(it, 30)) - while item: - datas = ';'.join( - set(map(lambda x: str(x.lon) + ',' + str(x.lat), item))) - - key = '8440a376dfc9743d8924bf0ad141f28e' - api = 'http://restapi.amap.com/v3/assistant/coordinate/convert' - query = { - 'key': key, - 'locations': datas, - 'coordsys': 'gps' - } - rsp = requests.get(url=api, params=query) - result = json.loads(rsp.text) - if "locations" in result: - convert_result.append(result['locations']) - item = list(itertools.islice(it, 30)) - - return ";".join(convert_result) - - -@login_required -def get_datas(request): - now = django.utils.timezone.now().replace(tzinfo=timezone.utc) - querydate = django.utils.timezone.datetime( - now.year, now.month, now.day, 0, 0, 0) - if request.GET.get('date', None): - date = list(map(lambda x: int(x), request.GET.get('date').split('-'))) - querydate = django.utils.timezone.datetime( - date[0], date[1], date[2], 0, 0, 0) - nextdate = querydate + datetime.timedelta(days=1) - models = OwnTrackLog.objects.filter( - creation_time__range=(querydate, nextdate)) - result = list() - if models and len(models): - for tid, item in groupby( - sorted(models, key=lambda k: k.tid), key=lambda k: k.tid): - - d = dict() - d["name"] = tid - paths = list() - # 使用高德转换后的经纬度 - # locations = convert_to_amap( - # sorted(item, key=lambda x: x.creation_time)) - # for i in locations.split(';'): - # paths.append(i.split(',')) - # 使用GPS原始经纬度 - for location in sorted(item, key=lambda x: x.creation_time): - paths.append([str(location.lon), str(location.lat)]) - d["path"] = paths - result.append(d) - return JsonResponse(result, safe=False) diff --git a/DjangoBlog-master/plugins/__init__.py b/DjangoBlog-master/plugins/__init__.py deleted file mode 100644 index e88afca..0000000 --- a/DjangoBlog-master/plugins/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# This file makes this a Python package diff --git a/DjangoBlog-master/plugins/article_copyright/__init__.py b/DjangoBlog-master/plugins/article_copyright/__init__.py deleted file mode 100644 index e88afca..0000000 --- a/DjangoBlog-master/plugins/article_copyright/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# This file makes this a Python package diff --git a/DjangoBlog-master/plugins/article_copyright/plugin.py b/DjangoBlog-master/plugins/article_copyright/plugin.py deleted file mode 100644 index 317fed2..0000000 --- a/DjangoBlog-master/plugins/article_copyright/plugin.py +++ /dev/null @@ -1,32 +0,0 @@ -from djangoblog.plugin_manage.base_plugin import BasePlugin -from djangoblog.plugin_manage import hooks -from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME - - -class ArticleCopyrightPlugin(BasePlugin): - PLUGIN_NAME = '文章结尾版权声明' - PLUGIN_DESCRIPTION = '一个在文章正文末尾添加版权声明的插件。' - PLUGIN_VERSION = '0.2.0' - PLUGIN_AUTHOR = 'liangliangyy' - - # 2. 实现 register_hooks 方法,专门用于注册钩子 - def register_hooks(self): - # 在这里将插件的方法注册到指定的钩子上 - hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.add_copyright_to_content) - - def add_copyright_to_content(self, content, *args, **kwargs): - """ - 这个方法会被注册到 'the_content' 过滤器钩子上。 - 它接收原始内容,并返回添加了版权信息的新内容。 - """ - article = kwargs.get('article') - if not article: - return content - - copyright_info = f"\n

本文由 {article.author.username} 原创,转载请注明出处。

" - return content + copyright_info - - -# 3. 实例化插件。 -# 这会自动调用 BasePlugin.__init__,然后 BasePlugin.__init__ 会调用我们上面定义的 register_hooks 方法。 -plugin = ArticleCopyrightPlugin() diff --git a/DjangoBlog-master/plugins/external_links/__init__.py b/DjangoBlog-master/plugins/external_links/__init__.py deleted file mode 100644 index e88afca..0000000 --- a/DjangoBlog-master/plugins/external_links/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# This file makes this a Python package diff --git a/DjangoBlog-master/plugins/external_links/plugin.py b/DjangoBlog-master/plugins/external_links/plugin.py deleted file mode 100644 index 5b2ef14..0000000 --- a/DjangoBlog-master/plugins/external_links/plugin.py +++ /dev/null @@ -1,48 +0,0 @@ -import re -from urllib.parse import urlparse -from djangoblog.plugin_manage.base_plugin import BasePlugin -from djangoblog.plugin_manage import hooks -from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME - - -class ExternalLinksPlugin(BasePlugin): - PLUGIN_NAME = '外部链接处理器' - PLUGIN_DESCRIPTION = '自动为文章中的外部链接添加 target="_blank" 和 rel="noopener noreferrer" 属性。' - PLUGIN_VERSION = '0.1.0' - PLUGIN_AUTHOR = 'liangliangyy' - - def register_hooks(self): - hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.process_external_links) - - def process_external_links(self, content, *args, **kwargs): - from djangoblog.utils import get_current_site - site_domain = get_current_site().domain - - # 正则表达式查找所有 标签 - link_pattern = re.compile(r'(]*?\s+)?href=")([^"]*)(".*?/a>)', re.IGNORECASE) - - def replacer(match): - # match.group(1) 是 ... - href = match.group(2) - - # 如果链接已经有 target 属性,则不处理 - if 'target=' in match.group(0).lower(): - return match.group(0) - - # 解析链接 - parsed_url = urlparse(href) - - # 如果链接是外部的 (有域名且域名不等于当前网站域名) - if parsed_url.netloc and parsed_url.netloc != site_domain: - # 添加 target 和 rel 属性 - return f'{match.group(1)}{href}" target="_blank" rel="noopener noreferrer"{match.group(3)}' - - # 否则返回原样 - return match.group(0) - - return link_pattern.sub(replacer, content) - - -plugin = ExternalLinksPlugin() diff --git a/DjangoBlog-master/plugins/reading_time/__init__.py b/DjangoBlog-master/plugins/reading_time/__init__.py deleted file mode 100644 index e88afca..0000000 --- a/DjangoBlog-master/plugins/reading_time/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# This file makes this a Python package diff --git a/DjangoBlog-master/plugins/reading_time/plugin.py b/DjangoBlog-master/plugins/reading_time/plugin.py deleted file mode 100644 index 35f9db1..0000000 --- a/DjangoBlog-master/plugins/reading_time/plugin.py +++ /dev/null @@ -1,43 +0,0 @@ -import math -import re -from djangoblog.plugin_manage.base_plugin import BasePlugin -from djangoblog.plugin_manage import hooks -from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME - - -class ReadingTimePlugin(BasePlugin): - PLUGIN_NAME = '阅读时间预测' - PLUGIN_DESCRIPTION = '估算文章阅读时间并显示在文章开头。' - PLUGIN_VERSION = '0.1.0' - PLUGIN_AUTHOR = 'liangliangyy' - - def register_hooks(self): - hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.add_reading_time) - - def add_reading_time(self, content, *args, **kwargs): - """ - 计算阅读时间并添加到内容开头。 - """ - # 移除HTML标签和空白字符,以获得纯文本 - clean_content = re.sub(r'<[^>]*>', '', content) - clean_content = clean_content.strip() - - # 中文和英文单词混合计数的一个简单方法 - # 匹配中文字符或连续的非中文字符(视为单词) - words = re.findall(r'[\u4e00-\u9fa5]|\w+', clean_content) - word_count = len(words) - - # 按平均每分钟200字的速度计算 - reading_speed = 200 - reading_minutes = math.ceil(word_count / reading_speed) - - # 如果阅读时间少于1分钟,则显示为1分钟 - if reading_minutes < 1: - reading_minutes = 1 - - reading_time_html = f'

预计阅读时间:{reading_minutes} 分钟

' - - return reading_time_html + content - - -plugin = ReadingTimePlugin() \ No newline at end of file diff --git a/DjangoBlog-master/plugins/seo_optimizer/__init__.py b/DjangoBlog-master/plugins/seo_optimizer/__init__.py deleted file mode 100644 index e88afca..0000000 --- a/DjangoBlog-master/plugins/seo_optimizer/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# This file makes this a Python package diff --git a/DjangoBlog-master/plugins/seo_optimizer/plugin.py b/DjangoBlog-master/plugins/seo_optimizer/plugin.py deleted file mode 100644 index b5b19a3..0000000 --- a/DjangoBlog-master/plugins/seo_optimizer/plugin.py +++ /dev/null @@ -1,142 +0,0 @@ -import json -from django.utils.html import strip_tags -from django.template.defaultfilters import truncatewords -from djangoblog.plugin_manage.base_plugin import BasePlugin -from djangoblog.plugin_manage import hooks -from blog.models import Article, Category, Tag -from djangoblog.utils import get_blog_setting - - -class SeoOptimizerPlugin(BasePlugin): - PLUGIN_NAME = 'SEO 优化器' - PLUGIN_DESCRIPTION = '为文章、页面等提供 SEO 优化,动态生成 meta 标签和 JSON-LD 结构化数据。' - PLUGIN_VERSION = '0.2.0' - PLUGIN_AUTHOR = 'liuangliangyy' - - def register_hooks(self): - hooks.register('head_meta', self.dispatch_seo_generation) - - def _get_article_seo_data(self, context, request, blog_setting): - article = context.get('article') - if not isinstance(article, Article): - return None - - description = strip_tags(article.body)[:150] - keywords = ",".join([tag.name for tag in article.tags.all()]) or blog_setting.site_keywords - - meta_tags = f''' - - - - - - - - - ''' - for tag in article.tags.all(): - meta_tags += f'' - meta_tags += f'' - - structured_data = { - "@context": "https://schema.org", - "@type": "Article", - "mainEntityOfPage": {"@type": "WebPage", "@id": request.build_absolute_uri()}, - "headline": article.title, - "description": description, - "image": request.build_absolute_uri(article.get_first_image_url()), - "datePublished": article.pub_time.isoformat(), - "dateModified": article.last_modify_time.isoformat(), - "author": {"@type": "Person", "name": article.author.username}, - "publisher": {"@type": "Organization", "name": blog_setting.site_name} - } - if not structured_data.get("image"): - del structured_data["image"] - - return { - "title": f"{article.title} | {blog_setting.site_name}", - "description": description, - "keywords": keywords, - "meta_tags": meta_tags, - "json_ld": structured_data - } - - def _get_category_seo_data(self, context, request, blog_setting): - category_name = context.get('tag_name') - if not category_name: - return None - - category = Category.objects.filter(name=category_name).first() - if not category: - return None - - title = f"{category.name} | {blog_setting.site_name}" - description = strip_tags(category.name) or blog_setting.site_description - keywords = category.name - - # BreadcrumbList structured data for category page - breadcrumb_items = [{"@type": "ListItem", "position": 1, "name": "首页", "item": request.build_absolute_uri('/')}] - breadcrumb_items.append({"@type": "ListItem", "position": 2, "name": category.name, "item": request.build_absolute_uri()}) - - structured_data = { - "@context": "https://schema.org", - "@type": "BreadcrumbList", - "itemListElement": breadcrumb_items - } - - return { - "title": title, - "description": description, - "keywords": keywords, - "meta_tags": "", - "json_ld": structured_data - } - - def _get_default_seo_data(self, context, request, blog_setting): - # Homepage and other default pages - structured_data = { - "@context": "https://schema.org", - "@type": "WebSite", - "url": request.build_absolute_uri('/'), - "potentialAction": { - "@type": "SearchAction", - "target": f"{request.build_absolute_uri('/search/')}?q={{search_term_string}}", - "query-input": "required name=search_term_string" - } - } - return { - "title": f"{blog_setting.site_name} | {blog_setting.site_description}", - "description": blog_setting.site_description, - "keywords": blog_setting.site_keywords, - "meta_tags": "", - "json_ld": structured_data - } - - def dispatch_seo_generation(self, metas, context): - request = context.get('request') - if not request: - return metas - - view_name = request.resolver_match.view_name - blog_setting = get_blog_setting() - - seo_data = None - if view_name == 'blog:detailbyid': - seo_data = self._get_article_seo_data(context, request, blog_setting) - elif view_name == 'blog:category_detail': - seo_data = self._get_category_seo_data(context, request, blog_setting) - - if not seo_data: - seo_data = self._get_default_seo_data(context, request, blog_setting) - - json_ld_script = f'' - - return f""" - {seo_data.get("title", "")} - - - {seo_data.get("meta_tags", "")} - {json_ld_script} - """ - -plugin = SeoOptimizerPlugin() diff --git a/DjangoBlog-master/plugins/view_count/__init__.py b/DjangoBlog-master/plugins/view_count/__init__.py deleted file mode 100644 index 8804fdf..0000000 --- a/DjangoBlog-master/plugins/view_count/__init__.py +++ /dev/null @@ -1 +0,0 @@ -# This file makes this a Python package \ No newline at end of file diff --git a/DjangoBlog-master/plugins/view_count/plugin.py b/DjangoBlog-master/plugins/view_count/plugin.py deleted file mode 100644 index 15e9d94..0000000 --- a/DjangoBlog-master/plugins/view_count/plugin.py +++ /dev/null @@ -1,18 +0,0 @@ -from djangoblog.plugin_manage.base_plugin import BasePlugin -from djangoblog.plugin_manage import hooks - - -class ViewCountPlugin(BasePlugin): - PLUGIN_NAME = '文章浏览次数统计' - PLUGIN_DESCRIPTION = '统计文章的浏览次数' - PLUGIN_VERSION = '0.1.0' - PLUGIN_AUTHOR = 'liangliangyy' - - def register_hooks(self): - hooks.register('after_article_body_get', self.record_view) - - def record_view(self, article, *args, **kwargs): - article.viewed() - - -plugin = ViewCountPlugin() \ No newline at end of file diff --git a/DjangoBlog-master/requirements.txt b/DjangoBlog-master/requirements.txt deleted file mode 100644 index 9dc5c935191f166db408850beb747475a262f65f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2554 zcmZ{mOK)0H41~|RQhy3jxa83;x~aOVRHt-$MBCMc>m$SHk)3NsCx724O z&svob%XMU|V{iQC@?R zZ~0G!W+z_|B@^!EYb0|a8|zFJJ(1fNe%^|{En8uk>3b5kw*sb!x=F1+XweA^2Zpom&^VXcC#kB7{YUtyk1AW8lRCsH7kc&9|2FJZDW6fPxwcK<4UuCUBdsXLVs`_5F zp~0oR=sQS>?<%PjW*zBU@cbYY3-!J}L^}_3Y27N;PkG)pk*uJsc&?t#R3o*J(X1SA zka;NaQGO#GYHJxbBbSkJp-k9@@&PlNs$w3EcPR@sF|TltTB6Tpl&WX?P!AFM`gAq# ziDP5!b%x?N`7!_PG~o%JQRKX9Y6p=$>EBXOM*7a}6;HOi-71ev8|^_li^@^eSj@w__0A3aLAaD3lW$cY-+ejf+0D0n;c*g?m`=Y9 z#D{NVKaQOf&bHqN|K6p!G41^vmi8TR=TIe^a2}m??@)Ypmc4|+%#Zt&bBpa!Y_Lna z95ZK`d!>na86B&$`)xt#Oe-&k^ISIS%h_8C0ec(O{{Ln{w6Q!`KVv#@hd&K=)9UJoM~W6el-zM7!{x**T=1X z^wa1z+L2b5chAc%ZKb?k2fY03=7b3E0}!J+D^ z`Om~yc~RJ~yuIGH-QLq%`|ZTuPLXcJX{%xE{f6^yk|#Rdx2k=` SS`X?#O)JeKo3nk{t@?jbHFwMa diff --git a/DjangoBlog-master/servermanager/MemcacheStorage.py b/DjangoBlog-master/servermanager/MemcacheStorage.py deleted file mode 100644 index 38a7990..0000000 --- a/DjangoBlog-master/servermanager/MemcacheStorage.py +++ /dev/null @@ -1,32 +0,0 @@ -from werobot.session import SessionStorage -from werobot.utils import json_loads, json_dumps - -from djangoblog.utils import cache - - -class MemcacheStorage(SessionStorage): - def __init__(self, prefix='ws_'): - self.prefix = prefix - self.cache = cache - - @property - def is_available(self): - value = "1" - self.set('checkavaliable', value=value) - return value == self.get('checkavaliable') - - def key_name(self, s): - return '{prefix}{s}'.format(prefix=self.prefix, s=s) - - def get(self, id): - id = self.key_name(id) - session_json = self.cache.get(id) or '{}' - return json_loads(session_json) - - def set(self, id, value): - id = self.key_name(id) - self.cache.set(id, json_dumps(value)) - - def delete(self, id): - id = self.key_name(id) - self.cache.delete(id) diff --git a/DjangoBlog-master/servermanager/__init__.py b/DjangoBlog-master/servermanager/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/servermanager/admin.py b/DjangoBlog-master/servermanager/admin.py deleted file mode 100644 index f26f4f6..0000000 --- a/DjangoBlog-master/servermanager/admin.py +++ /dev/null @@ -1,19 +0,0 @@ -from django.contrib import admin -# Register your models here. - - -class CommandsAdmin(admin.ModelAdmin): - list_display = ('title', 'command', 'describe') - - -class EmailSendLogAdmin(admin.ModelAdmin): - list_display = ('title', 'emailto', 'send_result', 'creation_time') - readonly_fields = ( - 'title', - 'emailto', - 'send_result', - 'creation_time', - 'content') - - def has_add_permission(self, request): - return False diff --git a/DjangoBlog-master/servermanager/api/__init__.py b/DjangoBlog-master/servermanager/api/__init__.py deleted file mode 100644 index 8b13789..0000000 --- a/DjangoBlog-master/servermanager/api/__init__.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/DjangoBlog-master/servermanager/api/blogapi.py b/DjangoBlog-master/servermanager/api/blogapi.py deleted file mode 100644 index 8a4d6ac..0000000 --- a/DjangoBlog-master/servermanager/api/blogapi.py +++ /dev/null @@ -1,27 +0,0 @@ -from haystack.query import SearchQuerySet - -from blog.models import Article, Category - - -class BlogApi: - def __init__(self): - self.searchqueryset = SearchQuerySet() - self.searchqueryset.auto_query('') - self.__max_takecount__ = 8 - - def search_articles(self, query): - sqs = self.searchqueryset.auto_query(query) - sqs = sqs.load_all() - return sqs[:self.__max_takecount__] - - def get_category_lists(self): - return Category.objects.all() - - def get_category_articles(self, categoryname): - articles = Article.objects.filter(category__name=categoryname) - if articles: - return articles[:self.__max_takecount__] - return None - - def get_recent_articles(self): - return Article.objects.all()[:self.__max_takecount__] diff --git a/DjangoBlog-master/servermanager/api/commonapi.py b/DjangoBlog-master/servermanager/api/commonapi.py deleted file mode 100644 index 83ad9ff..0000000 --- a/DjangoBlog-master/servermanager/api/commonapi.py +++ /dev/null @@ -1,64 +0,0 @@ -import logging -import os - -import openai - -from servermanager.models import commands - -logger = logging.getLogger(__name__) - -openai.api_key = os.environ.get('OPENAI_API_KEY') -if os.environ.get('HTTP_PROXY'): - openai.proxy = os.environ.get('HTTP_PROXY') - - -class ChatGPT: - - @staticmethod - def chat(prompt): - try: - completion = openai.ChatCompletion.create(model="gpt-3.5-turbo", - messages=[{"role": "user", "content": prompt}]) - return completion.choices[0].message.content - except Exception as e: - logger.error(e) - return "服务器出错了" - - -class CommandHandler: - def __init__(self): - self.commands = commands.objects.all() - - def run(self, title): - """ - 运行命令 - :param title: 命令 - :return: 返回命令执行结果 - """ - cmd = list( - filter( - lambda x: x.title.upper() == title.upper(), - self.commands)) - if cmd: - return self.__run_command__(cmd[0].command) - else: - return "未找到相关命令,请输入hepme获得帮助。" - - def __run_command__(self, cmd): - try: - res = os.popen(cmd).read() - return res - except BaseException: - return '命令执行出错!' - - def get_help(self): - rsp = '' - for cmd in self.commands: - rsp += '{c}:{d}\n'.format(c=cmd.title, d=cmd.describe) - return rsp - - -if __name__ == '__main__': - chatbot = ChatGPT() - prompt = "写一篇1000字关于AI的论文" - print(chatbot.chat(prompt)) diff --git a/DjangoBlog-master/servermanager/apps.py b/DjangoBlog-master/servermanager/apps.py deleted file mode 100644 index 03cc38d..0000000 --- a/DjangoBlog-master/servermanager/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class ServermanagerConfig(AppConfig): - name = 'servermanager' diff --git a/DjangoBlog-master/servermanager/migrations/0001_initial.py b/DjangoBlog-master/servermanager/migrations/0001_initial.py deleted file mode 100644 index bbdbf77..0000000 --- a/DjangoBlog-master/servermanager/migrations/0001_initial.py +++ /dev/null @@ -1,45 +0,0 @@ -# Generated by Django 4.1.7 on 2023-03-02 07:14 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - ] - - operations = [ - migrations.CreateModel( - name='commands', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('title', models.CharField(max_length=300, verbose_name='命令标题')), - ('command', models.CharField(max_length=2000, verbose_name='命令')), - ('describe', models.CharField(max_length=300, verbose_name='命令描述')), - ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ('last_mod_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')), - ], - options={ - 'verbose_name': '命令', - 'verbose_name_plural': '命令', - }, - ), - migrations.CreateModel( - name='EmailSendLog', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('emailto', models.CharField(max_length=300, verbose_name='收件人')), - ('title', models.CharField(max_length=2000, verbose_name='邮件标题')), - ('content', models.TextField(verbose_name='邮件内容')), - ('send_result', models.BooleanField(default=False, verbose_name='结果')), - ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')), - ], - options={ - 'verbose_name': '邮件发送log', - 'verbose_name_plural': '邮件发送log', - 'ordering': ['-created_time'], - }, - ), - ] diff --git a/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py b/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py deleted file mode 100644 index 4858857..0000000 --- a/DjangoBlog-master/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py +++ /dev/null @@ -1,32 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-06 13:19 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('servermanager', '0001_initial'), - ] - - operations = [ - migrations.AlterModelOptions( - name='emailsendlog', - options={'ordering': ['-creation_time'], 'verbose_name': '邮件发送log', 'verbose_name_plural': '邮件发送log'}, - ), - migrations.RenameField( - model_name='commands', - old_name='created_time', - new_name='creation_time', - ), - migrations.RenameField( - model_name='commands', - old_name='last_mod_time', - new_name='last_modify_time', - ), - migrations.RenameField( - model_name='emailsendlog', - old_name='created_time', - new_name='creation_time', - ), - ] diff --git a/DjangoBlog-master/servermanager/migrations/__init__.py b/DjangoBlog-master/servermanager/migrations/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/DjangoBlog-master/servermanager/models.py b/DjangoBlog-master/servermanager/models.py deleted file mode 100644 index 4326c65..0000000 --- a/DjangoBlog-master/servermanager/models.py +++ /dev/null @@ -1,33 +0,0 @@ -from django.db import models - - -# Create your models here. -class commands(models.Model): - title = models.CharField('命令标题', max_length=300) - command = models.CharField('命令', max_length=2000) - describe = models.CharField('命令描述', max_length=300) - creation_time = models.DateTimeField('创建时间', auto_now_add=True) - last_modify_time = models.DateTimeField('修改时间', auto_now=True) - - def __str__(self): - return self.title - - class Meta: - verbose_name = '命令' - verbose_name_plural = verbose_name - - -class EmailSendLog(models.Model): - emailto = models.CharField('收件人', max_length=300) - title = models.CharField('邮件标题', max_length=2000) - content = models.TextField('邮件内容') - send_result = models.BooleanField('结果', default=False) - creation_time = models.DateTimeField('创建时间', auto_now_add=True) - - def __str__(self): - return self.title - - class Meta: - verbose_name = '邮件发送log' - verbose_name_plural = verbose_name - ordering = ['-creation_time'] diff --git a/DjangoBlog-master/servermanager/robot.py b/DjangoBlog-master/servermanager/robot.py deleted file mode 100644 index 7b45736..0000000 --- a/DjangoBlog-master/servermanager/robot.py +++ /dev/null @@ -1,187 +0,0 @@ -import logging -import os -import re - -import jsonpickle -from django.conf import settings -from werobot import WeRoBot -from werobot.replies import ArticlesReply, Article -from werobot.session.filestorage import FileStorage - -from djangoblog.utils import get_sha256 -from servermanager.api.blogapi import BlogApi -from servermanager.api.commonapi import ChatGPT, CommandHandler -from .MemcacheStorage import MemcacheStorage - -robot = WeRoBot(token=os.environ.get('DJANGO_WEROBOT_TOKEN') - or 'lylinux', enable_session=True) -memstorage = MemcacheStorage() -if memstorage.is_available: - robot.config['SESSION_STORAGE'] = memstorage -else: - if os.path.exists(os.path.join(settings.BASE_DIR, 'werobot_session')): - os.remove(os.path.join(settings.BASE_DIR, 'werobot_session')) - robot.config['SESSION_STORAGE'] = FileStorage(filename='werobot_session') - -blogapi = BlogApi() -cmd_handler = CommandHandler() -logger = logging.getLogger(__name__) - - -def convert_to_article_reply(articles, message): - reply = ArticlesReply(message=message) - from blog.templatetags.blog_tags import truncatechars_content - for post in articles: - imgs = re.findall(r'(?:http\:|https\:)?\/\/.*\.(?:png|jpg)', post.body) - imgurl = '' - if imgs: - imgurl = imgs[0] - article = Article( - title=post.title, - description=truncatechars_content(post.body), - img=imgurl, - url=post.get_full_url() - ) - reply.add_article(article) - return reply - - -@robot.filter(re.compile(r"^\?.*")) -def search(message, session): - s = message.content - searchstr = str(s).replace('?', '') - result = blogapi.search_articles(searchstr) - if result: - articles = list(map(lambda x: x.object, result)) - reply = convert_to_article_reply(articles, message) - return reply - else: - return '没有找到相关文章。' - - -@robot.filter(re.compile(r'^category\s*$', re.I)) -def category(message, session): - categorys = blogapi.get_category_lists() - content = ','.join(map(lambda x: x.name, categorys)) - return '所有文章分类目录:' + content - - -@robot.filter(re.compile(r'^recent\s*$', re.I)) -def recents(message, session): - articles = blogapi.get_recent_articles() - if articles: - reply = convert_to_article_reply(articles, message) - return reply - else: - return "暂时还没有文章" - - -@robot.filter(re.compile('^help$', re.I)) -def help(message, session): - return '''欢迎关注! - 默认会与图灵机器人聊天~~ - 你可以通过下面这些命令来获得信息 - ?关键字搜索文章. - 如?python. - category获得文章分类目录及文章数. - category-***获得该分类目录文章 - 如category-python - recent获得最新文章 - help获得帮助. - weather:获得天气 - 如weather:西安 - idcard:获得身份证信息 - 如idcard:61048119xxxxxxxxxx - music:音乐搜索 - 如music:阴天快乐 - PS:以上标点符号都不支持中文标点~~ - ''' - - -@robot.filter(re.compile(r'^weather\:.*$', re.I)) -def weather(message, session): - return "建设中..." - - -@robot.filter(re.compile(r'^idcard\:.*$', re.I)) -def idcard(message, session): - return "建设中..." - - -@robot.handler -def echo(message, session): - handler = MessageHandler(message, session) - return handler.handler() - - -class MessageHandler: - def __init__(self, message, session): - userid = message.source - self.message = message - self.session = session - self.userid = userid - try: - info = session[userid] - self.userinfo = jsonpickle.decode(info) - except Exception as e: - userinfo = WxUserInfo() - self.userinfo = userinfo - - @property - def is_admin(self): - return self.userinfo.isAdmin - - @property - def is_password_set(self): - return self.userinfo.isPasswordSet - - def save_session(self): - info = jsonpickle.encode(self.userinfo) - self.session[self.userid] = info - - def handler(self): - info = self.message.content - - if self.userinfo.isAdmin and info.upper() == 'EXIT': - self.userinfo = WxUserInfo() - self.save_session() - return "退出成功" - if info.upper() == 'ADMIN': - self.userinfo.isAdmin = True - self.save_session() - return "输入管理员密码" - if self.userinfo.isAdmin and not self.userinfo.isPasswordSet: - passwd = settings.WXADMIN - if settings.TESTING: - passwd = '123' - if passwd.upper() == get_sha256(get_sha256(info)).upper(): - self.userinfo.isPasswordSet = True - self.save_session() - return "验证通过,请输入命令或者要执行的命令代码:输入helpme获得帮助" - else: - if self.userinfo.Count >= 3: - self.userinfo = WxUserInfo() - self.save_session() - return "超过验证次数" - self.userinfo.Count += 1 - self.save_session() - return "验证失败,请重新输入管理员密码:" - if self.userinfo.isAdmin and self.userinfo.isPasswordSet: - if self.userinfo.Command != '' and info.upper() == 'Y': - return cmd_handler.run(self.userinfo.Command) - else: - if info.upper() == 'HELPME': - return cmd_handler.get_help() - self.userinfo.Command = info - self.save_session() - return "确认执行: " + info + " 命令?" - - return ChatGPT.chat(info) - - -class WxUserInfo(): - def __init__(self): - self.isAdmin = False - self.isPasswordSet = False - self.Count = 0 - self.Command = '' diff --git a/DjangoBlog-master/servermanager/tests.py b/DjangoBlog-master/servermanager/tests.py deleted file mode 100644 index 22a6689..0000000 --- a/DjangoBlog-master/servermanager/tests.py +++ /dev/null @@ -1,79 +0,0 @@ -from django.test import Client, RequestFactory, TestCase -from django.utils import timezone -from werobot.messages.messages import TextMessage - -from accounts.models import BlogUser -from blog.models import Category, Article -from servermanager.api.commonapi import ChatGPT -from .models import commands -from .robot import MessageHandler, CommandHandler -from .robot import search, category, recents - - -# Create your tests here. -class ServerManagerTest(TestCase): - def setUp(self): - self.client = Client() - self.factory = RequestFactory() - - def test_chat_gpt(self): - content = ChatGPT.chat("你好") - self.assertIsNotNone(content) - - def test_validate_comment(self): - user = BlogUser.objects.create_superuser( - email="liangliangyy1@gmail.com", - username="liangliangyy1", - password="liangliangyy1") - - self.client.login(username='liangliangyy1', password='liangliangyy1') - - c = Category() - c.name = "categoryccc" - c.save() - - article = Article() - article.title = "nicetitleccc" - article.body = "nicecontentccc" - article.author = user - article.category = c - article.type = 'a' - article.status = 'p' - article.save() - s = TextMessage([]) - s.content = "nice" - rsp = search(s, None) - rsp = category(None, None) - self.assertIsNotNone(rsp) - rsp = recents(None, None) - self.assertTrue(rsp != '暂时还没有文章') - - cmd = commands() - cmd.title = "test" - cmd.command = "ls" - cmd.describe = "test" - cmd.save() - - cmdhandler = CommandHandler() - rsp = cmdhandler.run('test') - self.assertIsNotNone(rsp) - s.source = 'u' - s.content = 'test' - msghandler = MessageHandler(s, {}) - - # msghandler.userinfo.isPasswordSet = True - # msghandler.userinfo.isAdmin = True - msghandler.handler() - s.content = 'y' - msghandler.handler() - s.content = 'idcard:12321233' - msghandler.handler() - s.content = 'weather:上海' - msghandler.handler() - s.content = 'admin' - msghandler.handler() - s.content = '123' - msghandler.handler() - - s.content = 'exit' - msghandler.handler() diff --git a/DjangoBlog-master/servermanager/urls.py b/DjangoBlog-master/servermanager/urls.py deleted file mode 100644 index 8d134d2..0000000 --- a/DjangoBlog-master/servermanager/urls.py +++ /dev/null @@ -1,10 +0,0 @@ -from django.urls import path -from werobot.contrib.django import make_view - -from .robot import robot - -app_name = "servermanager" -urlpatterns = [ - path(r'robot', make_view(robot)), - -] diff --git a/DjangoBlog-master/servermanager/views.py b/DjangoBlog-master/servermanager/views.py deleted file mode 100644 index 60f00ef..0000000 --- a/DjangoBlog-master/servermanager/views.py +++ /dev/null @@ -1 +0,0 @@ -# Create your views here. diff --git a/DjangoBlog-master/templates/account/forget_password.html b/DjangoBlog-master/templates/account/forget_password.html deleted file mode 100644 index 3384531..0000000 --- a/DjangoBlog-master/templates/account/forget_password.html +++ /dev/null @@ -1,30 +0,0 @@ -{% extends 'share_layout/base_account.html' %} -{% load i18n %} -{% load static %} -{% block content %} -
- - - - - -

- Home Page - | - login page -

- -
-{% endblock %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/account/login.html b/DjangoBlog-master/templates/account/login.html deleted file mode 100644 index cff8d33..0000000 --- a/DjangoBlog-master/templates/account/login.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends 'share_layout/base_account.html' %} -{% load static %} -{% load i18n %} -{% block content %} -
- - - - - -

- - {% trans 'Create Account' %} - - | - Home Page - | - - {% trans 'Forget Password' %} - -

- -
-{% endblock %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/account/registration_form.html b/DjangoBlog-master/templates/account/registration_form.html deleted file mode 100644 index 65e7549..0000000 --- a/DjangoBlog-master/templates/account/registration_form.html +++ /dev/null @@ -1,29 +0,0 @@ -{% extends 'share_layout/base_account.html' %} -{% load static %} -{% block content %} -
- - - - - -

- Sign In -

- -
-{% endblock %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/account/result.html b/DjangoBlog-master/templates/account/result.html deleted file mode 100644 index 23c9094..0000000 --- a/DjangoBlog-master/templates/account/result.html +++ /dev/null @@ -1,27 +0,0 @@ -{% extends 'share_layout/base.html' %} -{% load i18n %} -{% block header %} - {{ title }} -{% endblock %} -{% block content %} -
- -
-{% endblock %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/blog/article_archives.html b/DjangoBlog-master/templates/blog/article_archives.html deleted file mode 100644 index 959319e..0000000 --- a/DjangoBlog-master/templates/blog/article_archives.html +++ /dev/null @@ -1,60 +0,0 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} -{% load cache %} -{% load i18n %} -{% block header %} - - {% trans 'article archive' %} | {{ SITE_DESCRIPTION }} - - - - - - - - - -{% endblock %} -{% block content %} -
-
- -
- -

{% trans 'article archive' %}

-
- -
- - {% regroup article_list by pub_time.year as year_post_group %} -
    - {% for year in year_post_group %} -
  • {{ year.grouper }} {% trans 'year' %} - {% regroup year.list by pub_time.month as month_post_group %} -
      - {% for month in month_post_group %} -
    • {{ month.grouper }} {% trans 'month' %} - -
    • - {% endfor %} -
    -
  • - {% endfor %} -
-
-
-
- -{% endblock %} - - -{% block sidebar %} - {% load_sidebar user 'i' %} -{% endblock %} - - diff --git a/DjangoBlog-master/templates/blog/article_detail.html b/DjangoBlog-master/templates/blog/article_detail.html deleted file mode 100644 index a74a0db..0000000 --- a/DjangoBlog-master/templates/blog/article_detail.html +++ /dev/null @@ -1,52 +0,0 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} - -{% block header %} -{% endblock %} -{% block content %} -
-
- {% load_article_detail article False user %} - - {% if article.type == 'a' %} - - {% endif %} - -
- {% if article.comment_status == "o" and OPEN_SITE_COMMENT %} - - - {% include 'comments/tags/comment_list.html' %} - {% if user.is_authenticated %} - {% include 'comments/tags/post_comment.html' %} - {% else %} -
-

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

- - {% load oauth_tags %} - {% load_oauth_applications request %} - -
- {% endif %} - {% endif %} -
- -{% endblock %} - -{% block sidebar %} - {% load_sidebar user "p" %} -{% endblock %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/blog/article_index.html b/DjangoBlog-master/templates/blog/article_index.html deleted file mode 100644 index 0ee6150..0000000 --- a/DjangoBlog-master/templates/blog/article_index.html +++ /dev/null @@ -1,42 +0,0 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} -{% load cache %} -{% block header %} - {% if tag_name %} - {{ page_type }}:{{ tag_name }} | {{ SITE_DESCRIPTION }} - {% comment %}{% endcomment %} - {% else %} - {{ SITE_NAME }} | {{ SITE_DESCRIPTION }} - {% endif %} - - - - - - - -{% endblock %} -{% block content %} -
-
- {% if page_type and tag_name %} -
- -

{{ page_type }}:{{ tag_name }}

-
- {% endif %} - - {% for article in article_list %} - {% load_article_detail article True user %} - {% endfor %} - {% if is_paginated %} - {% load_pagination_info page_obj page_type tag_name %} - - {% endif %} -
-
- -{% endblock %} -{% block sidebar %} - {% load_sidebar user linktype %} -{% endblock %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/blog/error_page.html b/DjangoBlog-master/templates/blog/error_page.html deleted file mode 100644 index d41cfb6..0000000 --- a/DjangoBlog-master/templates/blog/error_page.html +++ /dev/null @@ -1,45 +0,0 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} -{% load cache %} -{% block header %} - {% if tag_name %} - {% if statuscode == '404' %} - 404 NotFound - {% elif statuscode == '403' %} - Permission Denied - {% elif statuscode == '500' %} - 500 Error - {% else %} - - {% endif %} - {% comment %}{% endcomment %} - {% else %} - {{ SITE_NAME }} | {{ SITE_DESCRIPTION }} - {% endif %} - - - - - - - -{% endblock %} -{% block content %} -
-
- -
-

{{ message }}

-
- -
-
- -{% endblock %} - - -{% block sidebar %} - {% load_sidebar user 'i' %} -{% endblock %} - - diff --git a/DjangoBlog-master/templates/blog/links_list.html b/DjangoBlog-master/templates/blog/links_list.html deleted file mode 100644 index ccecbea..0000000 --- a/DjangoBlog-master/templates/blog/links_list.html +++ /dev/null @@ -1,44 +0,0 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} -{% load cache %} -{% block header %} - - 友情链接 | {{ SITE_DESCRIPTION }} - - - - - - - - - -{% endblock %} -{% block content %} -
-
- -
- -

友情链接

-
- -
- -
-
-
- -{% endblock %} - - -{% block sidebar %} - {% load_sidebar user 'i' %} -{% endblock %} - - diff --git a/DjangoBlog-master/templates/blog/tags/article_info.html b/DjangoBlog-master/templates/blog/tags/article_info.html deleted file mode 100644 index 3deec44..0000000 --- a/DjangoBlog-master/templates/blog/tags/article_info.html +++ /dev/null @@ -1,74 +0,0 @@ -{% load blog_tags %} -{% load cache %} -{% load i18n %} -
-
- -

- {% if isindex %} - {% if article.article_order > 0 %} - 【{% trans 'pin to top' %}】{{ article.title }} - {% else %} - {{ article.title }} - {% endif %} - - {% else %} - {{ article.title }} - {% endif %} -

- -
- {% if article.type == 'a' %} - {% if not isindex %} - {% cache 36000 breadcrumb article.pk %} - {% load_breadcrumb article %} - {% endcache %} - {% endif %} - {% endif %} -
- -
- {% if isindex %} - {{ article.body|custom_markdown|escape|truncatechars_content }} -

Read more

- {% else %} - - {% if article.show_toc %} - {% get_markdown_toc article.body as toc %} - {% trans 'toc' %}: - {{ toc|safe }} - -
- {% endif %} -
- - {{ article.body|custom_markdown|escape }} - -
- {% endif %} - -
- - {% load_article_metas article user %} - -
\ No newline at end of file diff --git a/DjangoBlog-master/templates/blog/tags/article_meta_info.html b/DjangoBlog-master/templates/blog/tags/article_meta_info.html deleted file mode 100644 index cb6111c..0000000 --- a/DjangoBlog-master/templates/blog/tags/article_meta_info.html +++ /dev/null @@ -1,59 +0,0 @@ -{% load i18n %} -{% load blog_tags %} - - - - - diff --git a/DjangoBlog-master/templates/blog/tags/article_pagination.html b/DjangoBlog-master/templates/blog/tags/article_pagination.html deleted file mode 100644 index 95514ff..0000000 --- a/DjangoBlog-master/templates/blog/tags/article_pagination.html +++ /dev/null @@ -1,17 +0,0 @@ -{% load i18n %} - \ No newline at end of file diff --git a/DjangoBlog-master/templates/blog/tags/article_tag_list.html b/DjangoBlog-master/templates/blog/tags/article_tag_list.html deleted file mode 100644 index c8ba474..0000000 --- a/DjangoBlog-master/templates/blog/tags/article_tag_list.html +++ /dev/null @@ -1,19 +0,0 @@ -{% load i18n %} -{% if article_tags_list %} -
-
- {% trans 'tags' %} -
-
- - {% for url,count,tag,color in article_tags_list %} - - {{ tag.name }} - {{ count }} - - {% endfor %} - -
-
-{% endif %} diff --git a/DjangoBlog-master/templates/blog/tags/breadcrumb.html b/DjangoBlog-master/templates/blog/tags/breadcrumb.html deleted file mode 100644 index 67087d5..0000000 --- a/DjangoBlog-master/templates/blog/tags/breadcrumb.html +++ /dev/null @@ -1,19 +0,0 @@ - - diff --git a/DjangoBlog-master/templates/blog/tags/sidebar.html b/DjangoBlog-master/templates/blog/tags/sidebar.html deleted file mode 100644 index f70544c..0000000 --- a/DjangoBlog-master/templates/blog/tags/sidebar.html +++ /dev/null @@ -1,136 +0,0 @@ -{% load blog_tags %} -{% load i18n %} - diff --git a/DjangoBlog-master/templates/comments/tags/comment_item.html b/DjangoBlog-master/templates/comments/tags/comment_item.html deleted file mode 100644 index ebb0388..0000000 --- a/DjangoBlog-master/templates/comments/tags/comment_item.html +++ /dev/null @@ -1,34 +0,0 @@ -{% load blog_tags %} -
  • -
    - - - -

    {{ comment_item.body|escape|comment_markdown }}

    - -
    - -
  • \ No newline at end of file diff --git a/DjangoBlog-master/templates/comments/tags/comment_item_tree.html b/DjangoBlog-master/templates/comments/tags/comment_item_tree.html deleted file mode 100644 index a9decd1..0000000 --- a/DjangoBlog-master/templates/comments/tags/comment_item_tree.html +++ /dev/null @@ -1,54 +0,0 @@ -{% load blog_tags %} -
  • -
    - - - -

    - {% if comment_item.parent_comment %} -

    - {% endif %} -

    - -

    {{ comment_item.body|escape|comment_markdown }}

    - - -
    - -
  • -{% query article_comments parent_comment=comment_item as cc_comments %} -{% for cc in cc_comments %} - {% with comment_item=cc template_name="comments/tags/comment_item_tree.html" %} - {% if depth >= 1 %} - {% include template_name %} - {% else %} - {% with depth=depth|add:1 %} - {% include template_name %} - {% endwith %} - {% endif %} - {% endwith %} -{% endfor %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/comments/tags/comment_list.html b/DjangoBlog-master/templates/comments/tags/comment_list.html deleted file mode 100644 index 4092161..0000000 --- a/DjangoBlog-master/templates/comments/tags/comment_list.html +++ /dev/null @@ -1,45 +0,0 @@ - -
    - {% load blog_tags %} - {% load comments_tags %} - {% load cache %} - - - {% if article_comments %} -
    -
      - {# {% query article_comments parent_comment=None as parent_comments %}#} - {% for comment_item in p_comments %} - - {% with 0 as depth %} - {% include "comments/tags/comment_item_tree.html" %} - {% endwith %} - {% endfor %} - -
    - -
    -
    - {% endif %} -
    - -
    \ No newline at end of file diff --git a/DjangoBlog-master/templates/comments/tags/post_comment.html b/DjangoBlog-master/templates/comments/tags/post_comment.html deleted file mode 100644 index 3ae5a27..0000000 --- a/DjangoBlog-master/templates/comments/tags/post_comment.html +++ /dev/null @@ -1,33 +0,0 @@ -
    - -
    -

    发表评论 - -

    -
    {% csrf_token %} -

    - {{ form.body.label_tag }} - - {{ form.body }} - {{ form.body.errors }} -

    - {{ form.parent_comment_id }} -
    - {% if COMMENT_NEED_REVIEW %} - 支持markdown,评论经审核后才会显示。 - {% else %} - 支持markdown。 - {% endif %} - - -
    -
    -
    - -
    - - diff --git a/DjangoBlog-master/templates/oauth/bindsuccess.html b/DjangoBlog-master/templates/oauth/bindsuccess.html deleted file mode 100644 index 4bee77c..0000000 --- a/DjangoBlog-master/templates/oauth/bindsuccess.html +++ /dev/null @@ -1,22 +0,0 @@ -{% extends 'share_layout/base.html' %} -{% block header %} - {{ title }} -{% endblock %} -{% block content %} -
    -
    - -
    - -

    {{ content }}

    -
    -
    -
    - - 登录 - | - 回到首页 -
    -
    -
    -{% endblock %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/oauth/oauth_applications.html b/DjangoBlog-master/templates/oauth/oauth_applications.html deleted file mode 100644 index a841ad2..0000000 --- a/DjangoBlog-master/templates/oauth/oauth_applications.html +++ /dev/null @@ -1,13 +0,0 @@ -{% load i18n %} - diff --git a/DjangoBlog-master/templates/oauth/require_email.html b/DjangoBlog-master/templates/oauth/require_email.html deleted file mode 100644 index 3adef12..0000000 --- a/DjangoBlog-master/templates/oauth/require_email.html +++ /dev/null @@ -1,46 +0,0 @@ -{% extends 'share_layout/base_account.html' %} - -{% load static %} -{% block content %} -
    - - - - - -

    - 登录 -

    - -
    -{% endblock %} \ No newline at end of file diff --git a/DjangoBlog-master/templates/owntracks/show_log_dates.html b/DjangoBlog-master/templates/owntracks/show_log_dates.html deleted file mode 100644 index 7dbba21..0000000 --- a/DjangoBlog-master/templates/owntracks/show_log_dates.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - 记录日期 - - - -
      - {% for date in results %} -
    • - {{ date }} -
    • - {% endfor %} -
    - - \ No newline at end of file diff --git a/DjangoBlog-master/templates/owntracks/show_maps.html b/DjangoBlog-master/templates/owntracks/show_maps.html deleted file mode 100644 index 3aeda36..0000000 --- a/DjangoBlog-master/templates/owntracks/show_maps.html +++ /dev/null @@ -1,135 +0,0 @@ - - - - - - - 运动轨迹 - - - -
    - - - - - - - - \ No newline at end of file diff --git a/DjangoBlog-master/templates/search/indexes/blog/article_text.txt b/DjangoBlog-master/templates/search/indexes/blog/article_text.txt deleted file mode 100644 index 4f9ca76..0000000 --- a/DjangoBlog-master/templates/search/indexes/blog/article_text.txt +++ /dev/null @@ -1,3 +0,0 @@ -{{ object.title }} -{{ object.author.username }} -{{ object.body }} \ No newline at end of file diff --git a/DjangoBlog-master/templates/search/search.html b/DjangoBlog-master/templates/search/search.html deleted file mode 100644 index 1404c60..0000000 --- a/DjangoBlog-master/templates/search/search.html +++ /dev/null @@ -1,66 +0,0 @@ -{% extends 'share_layout/base.html' %} -{% load blog_tags %} -{% block header %} - {{ SITE_NAME }} | {{ SITE_DESCRIPTION }} - - - - - - - -{% endblock %} -{% block content %} -
    -
    - {% if query %} -
    - {% if suggestion %} -

    - 已显示 “{{ suggestion }}” 的搜索结果。   - 仍然搜索:{{ query }}
    -

    - {% else %} -

    - 搜索:{{ query }}    -

    - {% endif %} -
    - {% endif %} - {% if query and page.object_list %} - {% for article in page.object_list %} - {% load_article_detail article.object True user %} - {% endfor %} - {% if page.has_previous or page.has_next %} - - - {% endif %} - {% else %} -
    - -

    哎呀,关键字:{{ query }}没有找到结果,要不换个词再试试?

    -
    - {% endif %} -
    -
    -{% endblock %} - - -{% block sidebar %} - {% load_sidebar request.user 'i' %} -{% endblock %} - - diff --git a/DjangoBlog-master/templates/share_layout/adsense.html b/DjangoBlog-master/templates/share_layout/adsense.html deleted file mode 100644 index 8f99c55..0000000 --- a/DjangoBlog-master/templates/share_layout/adsense.html +++ /dev/null @@ -1,6 +0,0 @@ - \ No newline at end of file diff --git a/DjangoBlog-master/templates/share_layout/base.html b/DjangoBlog-master/templates/share_layout/base.html deleted file mode 100644 index 75d0df5..0000000 --- a/DjangoBlog-master/templates/share_layout/base.html +++ /dev/null @@ -1,123 +0,0 @@ -{% load static %} -{% load cache %} -{% load i18n %} -{% load compress %} - - - - - - - - - - {% block header %} - {% block title %}{{ SITE_NAME }}{% endblock %} - - - {% endblock %} - {% load blog_tags %} - {% head_meta %} - - - - - - - - - - - {% compress css %} - - - - {% comment %}{% endcomment %} - - - - {% block compress_css %} - {% endblock %} - {% endcompress %} - {% if GLOBAL_HEADER %} - {{ GLOBAL_HEADER|safe }} - {% endif %} - - - -
    - -
    - - {% block content %} - {% endblock %} - - - {% block sidebar %} - {% endblock %} - - -
    - {% include 'share_layout/footer.html' %} -
    - - -
    - - {% compress js %} - - - - - - {% block compress_js %} - {% endblock %} - {% endcompress %} - {% block footer %} - {% endblock %} -
    - diff --git a/DjangoBlog-master/templates/share_layout/base_account.html b/DjangoBlog-master/templates/share_layout/base_account.html deleted file mode 100644 index c00d842..0000000 --- a/DjangoBlog-master/templates/share_layout/base_account.html +++ /dev/null @@ -1,47 +0,0 @@ - - - - {% load static %} - - - - - - - - - {{ SITE_NAME }} | {{ SITE_DESCRIPTION }} - - {% load compress %} - {% compress css %} - - - - - - - - - - {% endcompress %} - {% compress js %} - - - {% endcompress %} - - - - - -{% block content %} -{% endblock %} - - - - - - - \ No newline at end of file diff --git a/DjangoBlog-master/templates/share_layout/footer.html b/DjangoBlog-master/templates/share_layout/footer.html deleted file mode 100644 index cd86a29..0000000 --- a/DjangoBlog-master/templates/share_layout/footer.html +++ /dev/null @@ -1,56 +0,0 @@ - - - diff --git a/DjangoBlog-master/templates/share_layout/nav.html b/DjangoBlog-master/templates/share_layout/nav.html deleted file mode 100644 index 24d4da6..0000000 --- a/DjangoBlog-master/templates/share_layout/nav.html +++ /dev/null @@ -1,30 +0,0 @@ -{% load i18n %} - - \ No newline at end of file diff --git a/DjangoBlog-master/templates/share_layout/nav_node.html b/DjangoBlog-master/templates/share_layout/nav_node.html deleted file mode 100644 index c266880..0000000 --- a/DjangoBlog-master/templates/share_layout/nav_node.html +++ /dev/null @@ -1,19 +0,0 @@ - - - diff --git a/main.py b/main.py deleted file mode 100644 index eb389a0..0000000 --- a/main.py +++ /dev/null @@ -1,16 +0,0 @@ -# 这是一个示例 Python 脚本。 - -# 按 Shift+F10 执行或将其替换为您的代码。 -# 按 双击 Shift 在所有地方搜索类、文件、工具窗口、操作和设置。 - - -def print_hi(name): - # 在下面的代码行中使用断点来调试脚本。 - print(f'Hi, {name}') # 按 Ctrl+F8 切换断点。 - - -# 按装订区域中的绿色按钮以运行脚本。 -if __name__ == '__main__': - print_hi('PyCharm') - -# 访问 https://www.jetbrains.com/help/pycharm/ 获取 PyCharm 帮助 diff --git a/setup.py b/setup.py deleted file mode 100644 index b737f29..0000000 --- a/setup.py +++ /dev/null @@ -1,12 +0,0 @@ -from distutils.core import setup - -setup( - name='DjangoBlog-master', - version='', - packages=[''], - url='', - license='', - author='鹿与溪', - author_email='', - description='' -)

    f@hE8$z7u+({MhRrRHqRoQgibFOy4-9Q++a!Y57R`a|`H=@!bfMPOJ1%T1CX9c_tgv@kVt!oDSe -CYE{Q%&=$22N5vLG`|V -8#JD*gS?yU}k?{-}h=IR72LBKn(#Lb*|MV8CgigJ@1QRtS4Iei{Q`(_*(-Yv|r4j|54=aGdev^ydh^K -$l@}eq#*(u3mt0{m6-wB2WIYW(9mjKz~72*e{ITE6&LHyu+@L79};^7elO6E5u6z4(lMAPgy7yj_eBh -LN8h(p0qGdWzaa2`#u8G865mm1;J6CIVT$z}lBUxMlj#+;e)GsTW(i8M%9d&)^G1{epw*-|C%C$M7M5 -7CB1dlZn3U6V8J5tB!y&ZNuqa<vOh)AoZ@4t1i) -Oc`<;c<@I8*s)nD*I{q*0A|=|5oIMe5*gIf+bbPY~tX<%vQD27v8@|isUgtO&uWS*EnK?Zr^&iEdJ9U -wHTr4k4ULIGPz+c*n5XspwGwh{0t;!`;PnwNs2Z+J@4*?{9U(%rbRY+qZ=z9`QW`20}wZ9`q_F&sUkU -L_!{684W7bp2CttY7hSn4B|M$|9K -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 -!=klJV~JI3T)ycg`6n&VX3 -%4(N10Vtis=*D$lnZ)?hgx+$b9HpN!8;(el07%ro8)&z|U0_kc&FqU&HpT&HNSmXQS&`XYYF%p-dFG{ -^D(EBu(+ENc6J~X2P%i;dIuJin?XpSFbpWinvdYP`+?iiZcCVQA5oPXxBf7cU##LMu7`*v7dc6!lVg6 -T-bW0)`aTsf+YD3#Dk01GZlbR1it)yw6s(TD5S)JC;`(Y42Ps#}F-L`8C}?2OmXn+t)*j*L!X0$jh$p -rnn89_mPWu}3ITY{rVYt%#~glc -dkv-Ta86pc?zw?A~CdP6c514a_Nm>58wY-R&S!h%rbaCr7n0zLsl17S1g~eyAm%(tEhsK=4mP7AM4Z+ -#NGqj`r4^T@31QY-O00;nWrv6&rz}3a-2LJ%SApig#0001RX>c!JX>N37a&BR4FKKRMWq2=hZ*_8GWp -gfYdF>h7Zre8W-Cset7-|=`wr6iHR&2`_ARC4@-5vr%Q6$>tLW=@P=d#Pc@0>$P6eZeCnzqA+1w~<#= -YBu*TB{vP)Aio&HBVC}c9qhWWo4;sW`!!vXjGp`wb_Vr(|p~D9k=%tH_d@Eqcu{?m6SXu+=g7i*V$g$ -LgaP;lVn$U19xh<&aNbPKtLV%HFw`+QqrBftVEThH@qyAPO2=w&QP{wZ-s2RPDTv=PRpB2isDTnj6D& -Om)VZbx;MXWIhWy0o|UQ;Io$rvQ-54#+VIpMrF0!2xSLy~)VY~l3vvi&h5%;EiW5qFSyT25J!iFZ&7t73NsYNVC?Fi0vN^0N|xz-D{eS -QXe=K6aQ@>N!=Qe=c!-svrhUoC(}7UV_pLmp;<;FprD}N$D};ucA@3h9#|VW;A$GTR=Mr;5B0(rGn6a -7!Lbp3nG`Y%Gwq&#C#-#YU#hW1>)UoABCJmone6eE7B^z6QXFo?S<2HcO6NtfYNoJ+*&(&MVLdOT>e* -;cyG=f{OdjVA^--4Q&8>p2Am23@}LQ)Ewreww$C9zi^3^{~ID5W9BDYN@GLC7q~&lf-o6k!*uKD1W9f -ru`(UD*H>EaZqX^T>$ZDf1J&nD&SnU|aGc^=dB}nX#iN!gv55HiNwCTTqobCo@*6+DnS=D+NaA>D!VZ -(1>;{vy`g_&X|Qz=rV0Tuc3rMtN~w9Lgz~ianKWQ{i&B0|A2JukDAeh&=Ud;AVoi90k}u(pf{7Os-Pz -l$^au*B_#B|ws!OPjA8F%yS=e&n?c@ZjM^fjZpJ`QYIw{yJCCWbc->+G!nnY0p;2s+ixE)fD0RV_%yn -lTpn~Q@jT&z>4V3IP?5nlm@FS*QK!)Gtyt2{GKI9h_;;0~D>pv#QP?%rl|O^BMb|Tc}C(R3~ss#+C`O5VIoYg{a|8H(6-15M^i1@Sc -g)E#L8Qw2xq2;5T1AVYT58AQY2%f-uWrM?J_i5Xk3fyvRIY+^_Q=51%rl<8XoVLSW@ayre(C1*+Z)E( -=Dx+~Gi@LPI^`J!bWH2p4Np!@ym|q;941Rq{+hWMk7jQX!*-Q0X~PfX-t__Ni -g~(*k>%~{GuU{U*;kc>ay!FJpaJO$$0JwE7C;-|5{Kck!lN*&h#ZzRRpBa1L;wP6@D0Qjfz+(lLK~a7 -V*Or47`8UQr5&rd=UW`r%6ShuzH5B2dWnv4LVvWXNRJ^);`H?V^(3E?#r0qj&hnmgG92q2`}XKzx$G& -vM=}p~jmOK^HCB_hulY_{{>Z$2XTAn``kze}(%`0fr&_S!+3J(#R4558LEVFYO$Nra>88y;orH(;=oE -whAzQ++4(E0PN1~TM@XuCc -*jwc+l{$A5wMszwdmksQ&~JTg%%%v0m+kEK;|nI`>H!+;HmY>_RWv-&P*s#s^eGepdl9F60Y(%Wc@?T -c=A}zioGKBkLL|YxkApxLVdOnst5#sE_K=wcP>hV+aBo`|+-MUXENrNm7O${SvoYO&Uv07r3LH;)@v` -N#c;SCLg{Zwur%AN}#pPA=~BQ~YdyjZYt2m$1amo))sG61t4dN0xO9KQH000080BxrJTAugY$L03!eZ0B~t=FJEbHbY*gGVQepKZ)0I}X>V?GFJE7 -2ZfSI1UoLQY&07C++qM<|U4I2ew>N2%sW{2f##d!?61VZqNqup$w3qoPN`xfV6rmBM9j)vCzV`(HL5h --{uDdO_)iww`y!Y^VUx9g{Rw9b#Raq4>ibT3vtD+Qfo~tr0QdEGSi`+E#*qu$(Ju)oxVPe{(Ac3< -xpJGr>?bCnZnYUS^-aX>4&p7FTekAxqEsd*M*#s_;!sQlF9C6H)&B;aVGN{yQ--290ZK2Jds6Ie>Y-= -c3#xdOE`_SleFIx@bCTQ>D%bV2}m8iJbOE&v4t$7Rs54GqE(v1e{W==f#>?8E@lz%Jx@RJSgIqXKSlE -*UddZkT=S?-OBt=>*>${-nujV4{NnhqH|X`ED9$nf$5?!%uR)WUfY$`L<2239j_pw->5p|V?tflqaS0 -e|3;{%2LyU>Wh!fe4HpH`oPCyvA>h*evWm$lP2Gk+YI;XLI6y>0gOAnKaZ=d93rgo>~(FZ -0?1u#$|d}G6dpbJW5{c&(vBu6=pmg}r7E0G^aD^dx&7^014aj0Y18MF(9C|b+ -H2o;GO3Je07T!tN1{i8Se#WL_SJn@jugSBT6OW8Q&;XZdjQ_RZOw?|Kl^Wg3p>(Ku79Sh$guL9fjuD?NOh-xqNqWej -afMzvKsTDtB=<223JHwewxX3DgG)qr$wQPny@j`+M}3(jt$2m_yR{LN -OT-QA(IHq03u5XK`5~cZItSBXgUUU%*v18B8M0+u3E=ci^tR3f&lh{(HC`483kx?=I>gpnv6ZgLc(t| -*>9~Lig_^l*^6Soj37X{onYFN$FK*2x@(^;^;NkMzjJFtZpf7FPEO@!X}y40#ngw^e8ImB@F<*iObUP -@`!6mb4QDZY-+4lK8rx>_7MOk&Q?d$+wbNm0(7;Iqy9WGwsaBHR2x`YL5;lVi23wVbZ@G;(;*6r9215 -c9EG6JS7G~2!t2K_HznKvk)xxyCJF{z)s6E?$MCm>U)NLcYz8W}M7*1)ke&8t(PtxhnAYgEx(@W$ujd -PRXb)4Tz`Yz%m0lnWU{3Ye=z@SBkznw+{;0D8PDOW*x{e^lUl!T2DA+oR1OOVdi+LUvY2m<%Rma#za^QB%{OFr?i=X2{`vBRCJ -Jir5B4%#mRZM!{cWf4(MKEATfv?WN6*VOKtd*(y24QWrd?q0#pz^#=nxEu5zya{5mhVX6YzXush=YZJ -%0p32c-&V!$o#v@#YP+(#1wCuNVG5E!tMZ+5)t-Hx|QMi{?Yw+~fkZ8)?7{9br9Lnor1l{jE#`FwMkU -o1E8?ukl+NLm$jzzGe1DHk38%Ke(;7>#+MJxa(*9HK1Y-3P_1-x07Yl~kE*V -hWQ+J}I}}X -LCCU)pw{ba{9mNs3bld+U~}|(?>^74gbtGeFwO+tx##7fndcfc7R8?n_>rh1h`qHD+r?%IBlB91tF>rpu2+XpX -4AJG28+fJU_<;I`;o!ojDx?k;WHyy7d=U828pf_|b1wxj?kT-TPRz{MX6nHmk#r!&ZP~v$EI;gpDA{Z#g~OYh^%f6B8 -yvtHod5auX=X^1ppR=KY<7B6TgEY>1-&tEW>?K=y^E(>T97Wf!5)a`dt4uw{KfU4jy|B2-Lu)T3=Bg?%ZC1#m)85@Kjbyd(dpQcgGL+ -E%Uh^xe}M@MM?^svQ&=+kadO11zRAQR$Y}~zdX!C~_FcZic`*@AlvCQLW6l3##S}icM^2ph+k4I@(^j@IgPW3QY1fq(ljW3ptQ?C?3^!1Ly`j9g}6Fs%*JiWFQy!7-`M*Df5P?dgkx(i;Q;Ef0(N(BF -n{lujO>L_XM)q@oQUP`AkM@(=7U)E?17KVZmr#oPrnkIOd5BRRH*a_ -BVBlza?M%-?X(1F$wC|Qq4*$w4<&sjOOMzKd$9!q!M3+c3FHgYF1Hv2Z!iaB!a?~^d-}MIsBO{q;|ph -O9ZdV$w!eLL*MvYL-Zlt*+Z8Tua2R68)UR30 -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 -fEe|~anukuf>TnRdf_HT%WhZ@6aWAK2mo!S{# -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 -U!`W}eyJ;uPf7G9(EKS#fGQRdl_p?3j^QZTz%C -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 -Dz*PN^XDqGoot!(q+Ni>zxeslY{FSIbK* -QGGCUBFTK$dQX&i+}f0i-Tx7;}8FBT)T9%?_@okd8hP&*Ya6ZNa325yituTX{l&n=@O(Tp$&+{!}^DP -{S_xjLyQ~yP`Iz5cw_@IZg`!GUrN!EUi{(f<_XBGK0XLhuYr+9+=z}>yyI`QMy33{F@1|$h269SmV}X -u{l2JW^XNhQ_oC(9LxIPNT9ssZ=Ybrg8SJH!rmgvj&aYYHg-q|~Asms)NH)dMa;F8a>~nE&OcrFc7Ff -RJAjinyeuS0R(sWvGd$QYM^-a_1(|ZND!?h==RfDEu7q6S0dXL1@o0TydZc*^M29LaAx5>L#H*bI* -RbEjVO=*j;-K`&n~Q~#B+0d3d9T%ztbpk5fp;W)!@S%X%H1%MI|oHmRN(rQuL#VC2Wc3ujbaDh2B#`f -stUtGV91?fz$f!oFn+=iB63fb1zd?8ghbiMt|(F?>fL$(zVY$r7T8>~?Wq&yZq2N{4ARNi%Ri8tSbp& -b!Q_KQf%yI7WXA{z_KYthVGrC4U>tEV-mOygh*t{;>J$h9%6>X53lJn#SsioA_n||z3TU9-e4aBDdl|)L4$T5W%oeKN=vYIrgrp(Mw7?XaS1*AopUsb$+aAogYSBMa6*QakHcErgjN+=SsVorYx>!WNtxQb@O% -$Tm88&YkffxGDY`zPzw;3I4UJ?Q0I^#%lg7>(d&e5Pwn<78IWw7=(K`V~hm#2jHrJ67UbGy>xxbCzKT -8mrnEIV1v^brKUv(>V*A=m;u7^msQ6fTdi85yVEG62qH;}~ZZ^dpBQj^Z%M;yEs5ZewAttd>FN+Z*e&pLjBoa}7L!^@${H5m8P1S`px5^AR;i>-0_9VN22(wYcWU{d#J#H2gnN?Npn*Oo)fb3S9_8G6Ee!L_lQ_gsTDBSPEV -FhivLe2nP>jUvi(Qz%EJ!)fqHdV`4_9n%Us6Z)3}^aR)*fbH|t>0!D&QUvSR>9oC(*Xq4Q%`S9xXs}H -Y|&780i_2;RazpUf9Eb+b2mFI08eI9f%Ews~!{PiH3EIuFUID8qmeY!&I~!ki$tTb3=kJsGvC$4)@$F=n|H!L<%JwZhE)?IkZVsFO)C%8Qr4yn} -KVphwT5zAmFssag9a~XDv%`1;Vj&WkYtfDO7EK0Wgf>^kpYLUq?=XSK@Bu -aRnEYb4cJp%i>mlK48tJj>e39y_9dDhi`d=3hW)9{qFMs5LBjQArQdb-Y_#h_#&&X|LU^(kkCM_sx90 -^24*0FwL0%ESm$kD!(Fq!3jacH&4Z3DFYxYkc;$RSyR5=j{EEH -nYK>R^vB*r@a71m2(pNjnG5sdo>6Z3Q^2Y&xw>YbU)puaf1O0W6%>5tgkG4XYHk0v$F~)FHt}e3yryJ -UlmyK#`<@hzrj|9tUCoxfus0~gvoS*MkzL -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 -j2T8g>(1#AnQVw;-o-=nJP<>5 -&DF0GDr2N{y1irMl^g=itd|HP0T*l%>F4pwW7*8Y)U%>S3vXc^1eGa2ZVO-!V?C68P_I0iB2p; -E$bxxEUH{JNzJo+eg_wl2C&OU>_laH%>Nk9p1ax_S@w56s%FPa`5^L-dd7}m6+I~s9w`0L&1)8mRnTRi$sL2}c@@4fI*vx<33Q@-FmrA!ao(p@jgu|IX*>E81>HJ -4Hc`LPPg7obzz}+8)L5tdj1nUK_!4`#p8)ODRY!vLwya4uqL&bI=hopM!4G-nq!L>`O}wpHp7t& -IM9L4onsbg|XaINStIP*V?LmY_D)0pLsrT{7`cO&1s&%jKAGD523n3fA+NV`#+HDL+LQ-Uj2`24!n58|47^Jr2qD{voju -bAuEvnJbL0>5rO;NxX3I_D>|;5|MFA;aR??)Y^(p+*qAcS636-f=F5}*G$vnA&DsiQ>{6ZecHV2@y9& -V9Sl`12tR&4neK}YQD&yXN1vGN#5$p1Avw+#62JnR7CJzLTKLM{wi+p4^mmHfSPkU`$8)C1!W#mZHvq -%-;O5(8+(z@Wp?AY*?_63t>~3B&|4j}F=GLUTqv_TcRMo99+KF(-}9|S9V6nSGVfJeZBo_JA!UUAen_c -{y6i3&`bulr9gTxDiQ2r9|(He@3p&P@;I%3{w-VmQ^;+=$HX|Op -#|1Wb%2FB;16PygrEpEC7gWlK&@J^63>`pbqKgn(iEmXs$Qv63zz>UbEoCyX%X$7uOfTGIT=Xq@?C>rvGT9tDj&*Hcguu>uEJku2ih4BZnz -s50yNFQV2O`saSu#fBFnMM?31KOW-l(h8Mll_MjA-^mWzE-OR7v$6aVvMX_V-<~pwX#yi6L2z|B(I#S -B4bxyp#-8IP_m&rVKyTa_+_#b)Rb@B^^*%pcMmLv4hb{)B$p~P{;NjuJi;DENlwtQd%XmK2fHvtb91*VJ`*bECSZ1cc -InL40r|0dkH}q|kDxbqR8j?oSnFfzB)|0&XlJ3wBG1XZkYg^m(z<)nuViP{D=@+cO>LQ_cWnS+2L9?K -Mqkx4-8VbEg?h+>M?yw)-?&)1B51)qi3#v}3(y78_KNJ)dv4#gAn-8U -Y*I*eqn;%jf}CB`*LS7l-;Im8|-(URK@O%;qG*;bbMFf98S@i(aT6wX1%Ob{h*2{1u@kOJ@!Hd?V9AC -ebb$n(jr2k8gmqfRtdjJvd)ww|5lC!X73ns}warF}}h&(s$;rMU6;`kq_P`yTM30hZa`3aTd4pmbeGn -Dcye$uq$<=4-U*>{}z;xeok#zh)&L?B|%-_gML_4$v@F><`JC-+dP^@Q*Wk$#EXNqMsGS2ls;Oeo1Tb -xgq;di^LAakfe;tREd^q!3P@CR?s=(G6;R2Yfve;`JP(toz!}?5IqGLgS%zu;?UhREqd8r&?*}Tm8@) -Oc>omwdoMtNwV~#RQt>>D685Xl9gtPF;h!w02{E4AJvQYKuoAZBz(l7xaGC1KL -J?wqt`6Jz-%@Dt?{W&(k0N_`~NxTR9Q}Z~o+OwrI -Apq?RBv*b8AK9eIIa=Z&0|1xf)&Fh6N*J8gJ`e46xxVSDb@)5b?vCo9V_|_yL~IOpQKKaw5-8xHzMOL -B&1#{5%*AA|@S|^X)m5CICx -zytx<6eB!YM0wDbLU)f!?ilvYpup|Ozl@`)NI9MYHo}zEU4plW=LfN@NWXqs^AZ>N-@hp}90Z>Z=1QY --O00;nWrv6$D2vauy1^@un6aWAz0001RX>c!JX>N37a&BR4FKuOXVPs)+VJ~TIaBp&SY-wUIUuAA~b1 -rasrC9B5+cp&a@2B8A6qNy&56~gQHY~*oZ0WET=&&FVXz9x4LW`P6C3Uff-?^l|rDhoxAc-uIhxg;$b -FW;~_AJu0Xna#snhJfcZ0&_AOY4=_wsf0KdoTVo`XJBbRj$11o)~*P>hjoqw643Vls{oZ_t9PYC!cjy -XhWNV&(Ewel!eozI%J&@Z#~t@+j=kF(_amhS-ad{DxB7>dROsAWj66)r7M{}P?_6WRw}z!Y?*AlHm=) -|Yy^JZmJiD4{Nstpyn!jDI@3Npxv}*bk3Lg=)8ETfX-l1j+v%{hQnjP>kulPu2ic8n(Iwn~eSOsAK{@ -Jd`2p8IYUgWxXxNW^xN}`T=-QTND*YN#@J8}^2nF?ief;VA`seie_Yc=M@87<=X^oI+X3t2(aAN*TYe -_upS0rgKj^xuEhNQix(arEcmxWD@%>pAjSYF+qJ@#RP9)Asm!fLw%7Ur35dNf{i1@g`{xh8|DnS&sDf21JOTpf_y>K?*+0tC_XL=aJM6W0ZNG(WZClTwF0)Q -0XbDljP5B5VN@)!&l(EvZ%lpH8i4VRN=NC(KugDvi*69+n}2W{)!9&(n4oadH>!^1;|Bf7#CmG!7Jtq -d%UaB6042~!>sx~is(a+ac?Z5#^wKy|Hi>P+#CQ~&PjcMb44bk#Vu;CHtw -LPcr6M0?(FYJVfw -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 --NQ@l5)*30Zr -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 -#$YGl(q7#9{H-9N;;tR -A8d;IEHnb&Vuq(MoJvIPFvwd$SkwH=tSycG?M&|0<@{rf@78H1&2Yc=d#o@el2{LiZPVd75Lsj0Rxcb --WFW9qn|vdxbi^+JFWkPk`CJ43jjZ3tQQJZ1S6RP)zOl9aJKPaN=NV?oxL^{f?M6$uBn+C{0yAV$ICn -N*?*|N}U6>EH;N$2y@^?NMq>c?fcdTQ+LXbMa%UMu&$g{$kb`b -A&-OFfGR)rpDgI;SMPzSbpj6AWbr9Oc$Y<3-4R8>AVXfioQP#0NlkNn)TgIBpB!jqDwXK8@7|pV)c7+ -SqOu>KZNdh%6mEE3-IiRK6LMD -OJaaOjOWFR`nEif;*b|ffv}mFiwR7ieQgs^ymo0XKnz{lw+|)6A6j~}toQAD)u>^)HY8vzd)=~JtF)Q -RQ&HLM+2|A9t&e2rHI9oxcVqg;ck}106gok|)^<7b^qj%O-xP?IjAVrmxT`tjY1AnlwcSqI`V%*ftz< -B73kkmEBF1XIg%aXc^pT5Ua45P6b5@a%iO}BsgIEJg>*`zN*ECu>tdE#H1JItu(9oBq-Ei`*Ix_E(&y -Ed3hnRarIbD8sgrs1-lkKt@5Q_q98fpjQ62I&Syd>au0FIz`EV3Fx~Y8zJW){U{cpKkq;MX#L9wc37U -ix-f0-086+CS+(*_TsI3@B|?(l4f_DXeGVV@qKhRvHhvqHu7a#REed_By!_w=7&!^ujuXy*i -BXz1ak@<-Lke31{j*b98IDbRf_8(r?8E9X)m$Bz;eDqT0;dNZ>L{}#P_dhkCF557M8*?iIeCh5W1=cX -2?39B*%`IpGSHq`!Zff%$6xX&)YMnafD>k;3 -UO|&;KxfqL!Zk#K76Bqg-z*m5qn}idg%S4#@(YIRdSbYYl!<;D#aAzm_7!;CSC*c|sBv9mHFuF)P>S6 -AR&Ua7iU$edMR_x#!>(z1a?)r=}e^>gloqGUpuH4;iWoR2#ZRKClnFPrSXIn=VgxjzQk=Dx54NR~X@2RnEezk7IQ8vbY+woYCs+Sz_?Kg9DK8!k)7^cGdIdsmJr-v)Lj1 -vT+8!1TPNe{01~SfR||#JbkI5WiVUS$@o9uq}x8@C7p4Azx=|8{CQau0U_|t`+z?()2FzfYG5#BFZx5 -7PSKlP8NO40{yhVb1`@aU#GqdO#;V8pO^nh_smIeFX^0s3WShNiuRv%;~eZjwB^=0s;l^3#fA3@`1U| -bSLoi*#g3?dV^VBy6qxfazD0n?UGKXB)RU~T4ZkYDeOHXBn=I^gx4OLMw|ww>L+oo-ZfdM`Jm_nhbq< -f}{s!8>0%-2)d}W( -L#W1cytjz(`9ucGFIt0{XlI(`vAobgLp`HZq2K3o*@;E~IBE7<*~@B#H6HHtTQy@W4JfK?RE#Vz6u$e -T$8wY%qLI;l^# -(HN??$W_zY2B&S6(O!GxlIRgZnrRXM$9$YMuKeI++WY8Wii=H;um=1$|k2Xy0`*i9h2A%np$<~O%c7V -KPEy24-mB -WDOrQd2lgo-T146RO{NdbJTs0bs|q{diwz9`bE+cmh~x{w94x)!7x(n!+f&c-<#(O_&wq*?h&0&Q3)+dLq&8~t(Ii0fyLksI>}~Hc6Fy%DH0PJXjVT -^)$EPL_XyRB3k9&`y|}D&sqd=4nMPkYopS6aPv~MNt90r_{Nxr&lz$?GVfqBe1W@9D -H4!?em)q9=akMhs1~-*NxblF6@7%hg54;PW!=rTGHdb={4NRbMG^;&zJdgSuVhiYkRa1|XB{*Km9b1l --%f3}J7vFuccJ?rJcBTR%`|JAh|X^5oOl-eRc`2;leZZ_wbz=bm+h#iUIDY_wXdAAbkLA`YJ3C@$P07 -o=KE4@5DvTKD_kYJXVb5}0mis;nS$TlqWwP4S?d#I#9(vHXZA)fbL`w?O-TvN!?4fE!jHJm0`c>mEKF -UTA)ZBWe+=&ldR1eCJFWbRG1Mu@$PlPmcYqYjA!`<9^CL&i^zOPL&Ojv{#wgaq)}wgu{{T=+0|XQR00 -0O8ZKnQO);oVH)CB+lLl^)69smFUaA|NaUukZ1WpZv|Y%gtZWMyn~FK~HmZ)0mNaCzkzYj4{&@VkEn; -qHTkxx)6m)t}czDuhaMoq@7RCJoBYE2CPZ6O( -S_7S4*bp0O9w)IWYpE&aHIl9I($OXD1Whd>Qn(;seFCsfDstp0op*A -3*-@9SnhVYmhwh!Z^ViQ`@*mI6Px-5pUrzbkQ?@|!CaKO#sUR}bkL6D3^y)(rU -3^TI-^L#j((O0Xcruw}ux8Totxz)KH5^|f8ZSf+5eYZ<){NQHAJ|H3RZC_7y{_ykoHD7wb7xxjS=7D2dytt7*bh@pjZ>A1{#p0--mL4kVPgFi0)iw@pXsO -4$tK_(sLfw?#4kohAxHNiRm0@@ivTKNeEkQjK!Akl@i$hedF5CLTd+N!ZO!fwFM2d>qze#FJ!W$3&0w -aM3e^*&Cxucf*pN;rPc-!}m2e{j#x6}MmxgB0`d9dhTwT!Fct~LWl&;4g(8($_PvOh -rv^DIAmzZZ53@r6S~T%|5a0K@y+p}wL-(*gR`wz%K=6h<%{E10=zYt3aTm^EZuMOfrcW%{(n<1wPMmY -1`(m%IERX5gQB~|w53nX4>zVhT9MF1Mt!XTERNmWef}6g7h*D)|3_q^tf$n0)R>A^SNHeI+)C<&JNvj -1wCA2@W2wJVS5XBvoSM9>{NiM;0`W_zZo5IJa#MY^W+uQ6wsWYUWFnCzj)nyB4yex?rs>D;Figj;H&2&v%w4)a5coI{2jP>(*}g -&RY-qncb4M-LTMPXQ)9FV7_xQdrabnp+z1vn;scULEodm#>yv-k0v5(Pdcy2RS1Mx#U?YSQme_lYpzf -o^G5s!U*But>6RT_FYe=)TFX4aRL;P#Q!apiMY!k~(H(|6d1t5s9o8X@_MJF;TmFda`Rw0ybt%; -`~pzQWGct_OH=yB_Dww8A-bIob`oEUi{&w&{h2UFUJdJVYrERLJ9$@pU7#d;9Ua3XVzsA*hzYzbNeuM -+-LnKKX8Xq??9fD~&FQ^{64IA>oUTNO0?nKsFt93Y*W)Q#*cXbXA7&QUiiIw$-k?Z7IuX^S0tub}+XP -^5BIPoeESI;ea42)jfhO2qXRn69^3)*nQCJT4Zrr@M8l4%@6I`yt^h@m6tXe3MMQ~$R2yfhA(uB@uMB -R=Mm8d4L*ZLydHr9AoK2R`eHmAEf0qyPcJYbHFu?nBvfnc!g|$?b3iyNG?q+`PINTU^sKLP*%9smFUaA|NaUu -kZ1WpZv|Y%gtZWMyn~FLPsPWo>0HaCv1>OH0Hs5WeSE4E3V0wg+zu3yQ1=A_(h6M9OSB-2~I7%*?+0^ -`_mfU9gu-zBJz}S(cfn$e_oL1W+Vek4hEny*lu%D29OQV+&#(H6*ziE+A~ECB^`8N4)d2hgbvd&<9)x -I{yvWR-leNgN?1yL1!RD#;JBPFN!K!$-ASv*yfQp#a%IKrnRC&?z9P47?ik8wr(#CTxOylSXD`M$KvFtk?#-1koGREBh5ndtRIEb-cJ+) -#d&HN#Mr5~503rmz>$PlsdnIf$NQ#s(%}>|p}NHYT8N;@6aWAK2mo!S{#pv*y3AMx005g20 -01Na003}la4%nJZggdGZeeUMZEs{{Y;!MjWnpq-XkT+Pqa3-Een<~TYirla*_I>8d$%dgZ|^hG`rX$SYVxv{Mx%Lo=9#f7Yi%4;P8z3mVOJ_F6#8&b)uDa -W-A1jgdZ>lV+l9syh1&627%NwMqf3?vC(=S#D=j+UpY5nBU6g!X^%GKPp}-o3_H0Hf6Af0UXBuUn*)#_yzlNe_|%WC`1G;+Q2OYBApljb+6uI1&5;fbQi|NnE9i -rO}XaEPwUC2mQDQ@Wxz_yr%BvMklrUL6`($ed=@YvY|F|079kp`A?4y4>C=0k7n2fnED3Hzy0MKtNfhirzkiy -E?g?0_x*HKYSzv`@(r|zz$J#$s#rg+qXS@v|%IxpbVkZ?RyOEy$BDHr|aVAOSB#fc{Is;e;!wii6N%d -^-La$wdEwhLv-U-!-8^pTIFY{k<8R(F6tU|dcJxPQ{d?y7|_)PksUJe?&^lgO6&@mZ78)B`~B71U&%T -IyDmg3ap1xbvFN)oLI5rW*3lIy>BEnsmF}oCo_I5idDmumnhRCMeZ6jG9xVjL_m~VJ8TK5G7ysk&u!n -Qprny8tY_}}RovUs1NZ37B^Lt?`xF0Xd&=bw6)`N3G$VPr@P=PFZToI!A?B?cbgL=J}C@)`e))oxitw -B*Ea@VK>#2wdBsQ*EiI0{uzcKNcae`sfc>V?)TZ*PSdp+7g3kk?I6b^k(P|N?T>!@3 -}EU8=$*-)s>?Zvo7&LERu+3t-6--tjj#mx9F=_N(Ky@NF%|$mNEsjO6Aez+zQQ3IEV#umP)J(5odX@K -l(InL%*!KkHiBF5iAJ-5#jc1@V}K>6+M)x1)I#%Z0{x==2jU%q$)^b+0gXeBhUTpjA>FrcafDizF`(!iW&+g9JinhJA&=dPEylk5=~7?wyX8i=if -g6;k!-v`TdJ;U-0&VOiK&;TTms6dvUUXA3FD73l+7Yt_iPw=d!tO(5Q`2Rxap~52>s}%@YdH3kb6^=v -rbVDq_kJ9|p2r3laT)wr*}1P%PkfZS+xPG}#1O_{HW?t#jlo%P!z)p$^-g^ -{;sJW!*~8G)GsVQTYZ#8YBjQKmUq&;Ek9jSpHF?-7MI!9`oVg;+7X4n-l)KQRkUtVA5jHrgkn6!Hi=D -?Q*7b>RF!&Pwvql)%iN_$%J$J2u(F*MG`f690N0?k29$`25!2HT_W8>jro~DlsL>KKie7ncjt -S*zkAJ+(GXpCI=SpmBIL^!V+D+KEoZQ)M*LRD+r_0fw36&XAPzta9VHEXg=>=C>rG*9j;HcYSYj=J!( -E46kx(_|BW2tJIf5!&6t}%H5%VMvrpc580#5Shi^~VFP?ZFRN0g)eB-K}0^Oei*`n4@$SS7V0+-9mVq -o#H^WXAgRX{ZH1Sr9Q@d`hG<4Y~)jJ+yAsm&n|VO7MdZ4zuP(}|*(c#XOyea&M)#B?u-PRSzOxNwX#u -R;2hVQLg1j}uM4>CzX`8A9hz2^w3dQgQAS#?y>oGIaLBPE_qA=3u1_AFZEB842X%ewh28EzSKQ$Mo+v -3=*ULb8TUBHcY*r18H8*eJR|o{svG>0|XQR000O8ZKnQO=H8j>3j+WE;0FKzCjbBdaA|NaUukZ1WpZv -|Y%gtZWMyn~FLPyVWn*+{Z*E_3a%E<7E^v8uRZDN%Fbux?R}k)@Ezo)|?63eGF!XU;gRR33!(b>j-Dr -^|Pn6RZ*pDA2*>U71ImC&?$47o2>BgEC%ChNQZ?P;vwwU3*TN8JtO*3Wh7?#AQ5iy!;Xu8oI)-v<;W%|A$+Hp3Zw14{=T6B=db|U9k%5Y3dB -G_qLt3Ni#y}mKPNB!589^X0`muc^j0&HO}NPf5jZlyvJW=U5-@%NATqr@PlH8!6AVZfsR{ -&0g~x$#n42x5swVy4MbX|lOQ!e1w1w5y4VjVb&bzqfv|8l^a}or?XjB-Fy8nHHLAXVRUf8O`k%o#NmW -bY8n@n_H}BI3+#kP6c}zQdxuc2S5~l6=^{`SYclo!$`7$o}JT46Ka~kV}o485HhYR8@_LVmYxjBLdcv -GZ;J+Aw4KC)z>i)6E1CAvLiI2-Dn$Lu_HoxK^aW_nDG6SCPh`Jb8|E%fk3_8(A70|XQR000O8ZKnQOC -YogT9t8jZlMny^B>(^baA|NaUukZ1WpZv|Y%gtZWMyn~FLYsYXJvF>aCvlSZ*DGddA(ObZ|gP;zUNnP ->0t@rCt&YwmjD^iLkH}#6!=T+-lEAkv -<-5BeEYi&ID(rC9@b!+Z|_Z7)EvSIm)(511g;l(pQN_}LWAKk0|13!*srS`&Vu2}1(a;z|QgT@b1Nq@ -=5dPm`N*EV!7$_!qFb?5`?S0?u+*QPZ7y4_m3;?PN_d?gC1yKsE=erwY>?mw>Bi5P1SM*r;~9?MG#D6qRwv?v<>l4ce)$XLwhTviWjS}k -cdwpeaHXg)K&hqA^G&bggsKJ$y>~lM()mZ{?GyY9zfpqs;cq0Hy0-)0V$hFxE*UV!uIf>)-eEpX;SNx -CNpuSzeh!bjAq^dp=-M^h@_HQCZP=QigccDih$hz{U%n?llOpYwI0(!Q<;S% -6lwAP~6L{~3r2EXH?JxhmX&r+$xTqSimu~~2p2LXpxDs)mu2E2NYvd-aFQZsl!|HQI$v=8d43t4)_JE -{>QN=acK+L%kf#olg6XC$lg&Chcea3RVdWq@; -0?4+z(%kmgU^^yHs0}Z_vz{LmLPFMygg(vHC-@ -sQe^aGWi`}+YPKhao-#h5e`azCFujUU?wbadd)Q_oxLqxjMATlhHT%m*y{UP#!6VG3J3Q__K5a+ZAwh -&eRHnc5MG6ZPNf%P#pM;=+`#L;a6Z+;k-Sn_*BPbCLSVIGJuuHcVInGrZssdqT)9S4YAc+{A;A -H4zU9Z5AX3{IJYEbXPh9MS}`d%38Oa$bHOoUc~2esTuehK0O6EcK{{tzs4A4_U%7*t|0=a*$hbuUM`P -eMVozr_>GE*0lF`sZeQ_$6{7UEXf6)c9@imhQ0XzO`*)VFcJ`ammA(x@2CWYcn2nCYao26VY_anZ_~` -kHgkLmzltH`dT8;NNFev9ZuJ%tLZ*V){Fci#z2m=& -**e?U}kLWA`au`DH@9yEpk3T^e>WPCVQ9M(D7K-{}Da$Zd^CPE$m!_1v+Mv8Z&;H!Dk=hT1{y?l}jns>P3>hc8P>zcYpK3t2oa)UJDwLCN#}(JoeqrNE)(}uqn2}_%Y1! -b0CyjVMEhF$iAB^)XSFPv?xia^&vVott;bE<4VTJ#Ogm#O9KQH000080BxrJTI3cWzv~770G1U10384 -T0B~t=FJEbHbY*gGVQepLZ)9a`b1!#jWo2wGaCya8ZExE)5dQ98!Kni#w~ny3E6^Epx^+m0e%XSyXg? -&Wz(};sRu(lXO##corq6st}%+)WZE@N=dd@IS)^_fRj@Z2PmFMnak6SrN?}g -e*R_CpNu`s?B*`QK%);iZKH~OE2M-)ZTF=x|w~emlTxa^YrYk-+#iZ#jfS5<;Y7tSSd1Dwo8rV^XPh=eLRm+?aCvaFU -|B@s5)yESJCT}7f=6qdE(cm-t<&00b-r&GLA#1vgJ(F*q94f+Nn4?7QdTZ%^OKX71cy=Q-h_3;*NM_# -74eH%Iu0AlBd95SExiUccR=CN~|>{C#O~-b0h{Nah#P#;@OU*G1LlUIcZ1ZwvlDBj{4d*=Jc0ijzf&p -nm2hOKC-#6i~hAXm3G&ww-a9^;J&g6^z4SJTro4$KK)NkL|Pvk4oJ~0;3%WQ{l@Kpvy(?|rP0)uDVSw -t00Zc3SXfZE_zJ29i-(gJk|qQ4papET@< -?+>At{c#(Ge_ThPmNj~82T7QwW2*(ec&5f%AlTnNOo7_xf|bw2~_g|g+YY3EHB;eO5yyl!r7u|zo?^q -jgz^_x9CFn??FA$_;qpHll{4DmMM>PunuH65Zifwa!Yv> -zrE<$3z{Rv5tOx^y*qnjOXdSX&BwbP&R9&?y2rs|Gl~~t48?t(~$ofw&x9FzYqlVa43K}f+ -eIB3WzPGMYharEyDZ&GvdK&NmmeA$bNP)s5T~835#mH#Aa-&*K3TUx_@C6r`hNJG@mmd`541?c -(n{C(khm%T47eB0oJj6+b+9oI+>@fks66&dPr5U; -o&rG|c!JX>N37a&BR4FK%UYcW-iQFJE72ZfSI1UoLQYQ&LiL&d)1J%*-oRC@3vT&8bw#OD!qSFUr;hauQ2 -YQxr-|GIKIZGEc!JX>N37a&BR4FK% -UYcW-iQFJX0bXfAMh#XM_s+sKjM^(zp#u9n=RVLRT9W7RRWUiq=Ab+kU)n^elBfFMcr_cWNM=^5*uFcf`uy+6rUDMR7Y+rPONqmY`sS$bdGjYTE9QfRoIilmDOET6LjU1mTRyy-u!X4I4&)C(%@MLzBz^4!w3 -w;5j_d{>L?m=8Z+A4XcV>^Dy2&Wtu$bsYXiP$%FFCPHj4KJPN^B6LhrN}NpeLyf#wmaUqL_clU{OG8m -tt3mkty!M8z{NbDA07St>>saB&tBwDPv1QG@#V9pFzz{M3^4boYyj=##H!KE@_v3y2m@xv2?(S7A`qz -^vv>5qytX&{Q#i83miRWPwxK3P*UAHPO>b!ej!n4_TIeG!Y2}~=q%%t>}+TnL$U}N$0TfFh9 -O~$aF*X+nM?#viaXfSmTn$>T@uBY@JaUS0IX?0k7(8dU2W@fi`WP-_hr#E2c=)&cit-szMmlL+iq|+o -Wn*zw{zNRWD81d)$}8CABSfasQ3I|!L2*Vn_=&w@{OQ1tT!EzN5}~@u1Dl7EY}BL)xe1Xe5t`IOWtm4 -u+}2sEl{?&Eb69MHPVq=APSMcEFXNI{V}_rS(>ep)=XQVikrpwl*lO6!(iakI4_7j -^n(BZ}@?Rn)y3Rd7I0cyEE}r>I&(L<84g0eJeF6achv*{ULm=8XFw4Ny?^{dpvDL&x#7eTC`EfAd -Gf3v-U-KQEY(oFbp1p#1##1Hv%AII6)w;7)>>ExDJ?iI}Eo0rq=eW8iui!^b}?%J=e}(4Pck#tZ6M7; -{#qAE=ZrF=5;4}7)k(MJ$d_6rZ@hX_7oIicPoAY7#R7lw4U0cA@yK|0x}r1?G4+3K?una7z+o?w(s}i -_};w@7|mgo0Uz%DRzRr1@2RQxRt;a>d;I86e}4S%kKaLtu)hCbRXuq0_+j%icmNL?>VhQ5O!kJi+vOAZ4(T>vJG84&d!pfkYkcVj1;`Wv%*IyZA!dkJ$P1%->c<1NVY{x -F-n&)=o5{_*ywU(QZ{d3JX4%Tq{4(sP)T4*mM>pHqh=0)@`x(^)sPGv6QYREniPqOa<9=}dY0>~Ckkz -I-_v1%Zh>?A7V3XZ{#Ka|!?K&ACfgKrNG$!$QO~MT2`dO)d^@7iZ4Yp6778tM@Y?8=79h_O5t|&lj(o -1^%{jCceI$eSJ~6PDnyA7T1ZfF9ejKIu-u&`pRO{! -X;kC!$ZBqFRcSZOdz0~__MR8MoV|KwdNlO(_@1v}5va|@*J4!(S$00YE$7U(;s8)*U(a;y@xbRLspT+ -B-8hm!640q(J7ANH)oOqA)_!7K>s1f*O|m0T5_g -n)wWjoJweiqcaP8=}s+5gRgACU15f6pfK@H*8!g_KZEdTnzvIcLH1lrctcQAri!zu33|PVCB$Lj0p57 -0Eec*$R&;O4fh62ag?CBNCigR1N( -!l>}D;`;fm#Cj=T|QZ7?R!AqbaHbAp;z4ATD{+Pu8(XF6^fGn-1zo;ioz&QgR4M5S1IG>2kDtQ^N&hW -?CnJ-#ZE7!aGKCvS3Ebi5pXlm^b8^x$zW{}tO>DBJI3(XU7o@y~2;hJRU76=2y@ylrBo -n*D=cOQQL==<-Flc&7>rl)Mn&OoxEa?`?!*vR(evgm5kqFO|tf>}e|16#s&`~HAj^h!_qql_Hk=EqG^ -W&wLVF}Ojjj{+muZ*k(gbMH3R737ZETM1uA74w-o;J`2`B8M7395R-HNofM2A_w=GfIwgk-fgBfbB#O -@7Fn_1Lyk4Eo|3oc0Pu|W&-u_+5hq~*g0mP=PRfhga`qdqTUty6M83{WEeOz3;uzShXkF+A_h>Dq_7% -p2Qyc+cqpmYj5}~2Di&ki96Ew&Le867G+*V>@CyO(EP^B2`l^MKpf(_XK6VM%V0HMkh_Br@9_5lxY#B -tKpqJIZUcCKxhf!C_!0>kitT%5hT#4uc4cH`o3UiwlggU!sMQ+=^X5lRM{=K^a!7`uL?$Es -FISxE_Pw0HfQm0fv;yvs7gAwXc0%6W1Ci4bIv7#x6L@&Ip)cA++J|>a95~N9lZ0<3YvSPK3LEOWbjfY -~`QLZAh63DVU>KZ^J=F6v-6`K*NeoB}Q73wZQ@rDJ -~ez$3qp4A`1S%1qKHBzz2xmTarzAbS&i-G`Q{$h#?; -pmV2SUs_iu`8*Fwc`mo79Dy!0Dh+fVM2g6cDW5DQr)m0H?g;HjkB9DLU?@f^l1&Y{Ag<~i%w%pI<_-j -lxXUeBPmE@0{1qkcLmTo`6 -QtLDnJ>9Hi+R^bc)w2*80^zDkC@6ya`YTY37MsDLBEK9tOx;~$d!Y%5&iqK2riN-FRrDyY`zrA|hQ(Z -M{x;z@H=9Pog7g9@_6)8!T*slE=(#VPmK;GziWBzP1=-V#Sy?HSF+vnzu#zeYI= -s3rN6S3UV8s}t0XVLi4KLa3jHK9##25?=8-BC5()tkDdb8di>{QCN3vMc&>tB=*JwSq^P)ru2DwL*ha -9YDBi*)FgFUP_PS#uU5akWl+svB91(OJAh}u4G?PJIN2iPV1V9GYc{F63OlwKPi%_{6hYV -%9`s0&lNEF@ctY5*qB@ueD!>VWBfua|YTbPtj1R2avKbI3)rO~7M}e1vzT|<-{FS{jgqO>r=~`pPv_L -Y$5ao8@nSp|1l$Sw=axej4=JGpY_z)ys2C?!j>>*BGM%%j3+|9F27W314=^P7R_w4G_HOiG8e47(D3t50em<2P?H?ZLJ06$@|Fj -J;b|-!h@kN)(FO~!NYQ5j9=UVMZh{Q$(noU5k_z!`G;0Az|!R1{qyj+wPjasb~F{HX*-sigJJXNTYCw -Xn_tP+(TTiXj_{ffbthdUxbs}ibEpBS%MOu9NZi($aLNYR8AxT*$f3nMlu~*MX5{zHi14^rfHFj%J&4Ikg&f)(x(U)NV_kXlQNGV -*E2^@cj*3t^|X$XIx?xa9Xy)sF+8lOp0L#=JD1?D74}fDJ^iw)-D&clxz=eUop#(8QAlEcTk5!P9(aN -Xdv|tSSPuHtPPfJ>Ya-d0I@$vTijSFq|RwV22PR -Yr+KA%c`y>yO~)ai)C=}uuWU0bU0TN7C=M1$RpW639Ugs0#bEjMKGrv6jsyr*0hveyZ#Regh5a%#bI{M)V;*l^| -op?+IcH+SOax*ufc8pxY$hA`(qTem>qK=Kw>(W$Yai)1zQK0c6DG5<{g&pyvwk4@g1&bIAlWH+%Bx9h -~+o~ZfJLkkSN(yTLs-pSeLf%!X?g?J7G!!uaY0(l@sRVpua=$I-hkA->>fHmYS@!c!~P*<9;*t2O-2( -r-d#>iprEg{Zt$2K{h5D*=1e7PYccRV(_lbrAtvgzJuzaLnU-8g&$WKx!t`)58{evFjM%9pM#gS@4Z? -5Xo2YE`3rf~poPei>AmA!`^q)QI4fNr)Smz{o+KWm`Uw8mcG6`3fr=9Z1NhBMt8nBd= -_R+BH@w74xYea@M=2DIdvk|loWG9M}PIW5Z?7O4mGLFHb{Q7q~KI9ce2-mqS$uljIApTMi*dphRQubY9q#WFetSymWswkEerwGcTNh&ZfJY2}HXDu -hJHq7VTjJUx#IIg-L7!;`)Ttp=;T<@^G74nchdrvAJ853sSE-jw#Y+p$e8ddSpp#w^u?sQVe1Am`^We -_WG;>AC^OqqAD -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 -X(MZ@X8kjqzI*KCH%GnS03Oo3)@C(KQ# -puB*yS0;eImLOLpG2uEDDk_!Kyxk>huEKrT9YY?i`t9aD)o9`wZ9Zl&k9S4S+zFmOA^HE4p%~&}?lXg -A%-YOSBgac&RpDc)p!})ZLnw?rASS4alK1-mNGo;UCngA%`3;PpV4udig;#8k&L9N|@_p2^x3J)L;&E -tv!hRKy8GyfQD~A=@zM~2+>IU2j#K>GW;AX1k0J#;vgOJV(j}oK{8CQeZxM_%nip`T`&QSBHV5o!KL~ -r}Y!3t{ZffC@}O@~xx2!rE^m?Jz$GQ0$11fQP;R<}&%f*1X=x^4uPx8QzDDy^K0iP?rjB?Ufw&9hsOq -)tp=FqIKLV$kn^q2B@ZJcNoTAIc8gZxA_>N76H`fw!O(0B})Nw~V*6&~Y$h$_|6qqJ>0S -X1r#Y$XR@P<9eh`nd+3rf0wbxtoIe<`ybk+mnkEFOo`EB<@56!-?ihEKHYd|(PnJpAt4#0@KlsoOGU? -dCYy{wMTY;Zs>xK -`Carx$4QkjO~aABdDFHe}B0swzeS9Usz=1owar8}&noLIu&Aa3ky=%#C04i{3Y&9i3JXy_i~0OU(tX- -xqYm@&skj6R0J^iHmTcC%S}IGK1Xmpc|pz_Y8*%f>vG&ln5|(0B?&6A`#+~ruXeWaQk0GQzh07P3}Vp -aT`55vC+2a$_$_qElzSxx-;0buoK4fk_S&Q8A{VApb~q(sQa<>IwAeb6E&hn0VL}J_R=;|Xe{ZM69UA -(V$%)^qmTgm!2*~YyGdO1YJEd>%=jP@nz$wHn2&Yn%nPA|bcN -JwQuAbUMn3uJ2J1PJvhBe)#E6p}X9ub{s>|zu;a}sE=i3-}9Wn&S{JV;S!e;u=Env7?SDHKsf0g&wlW -*Qfvlt@4WS%emAdFGle@<|xzWy?Cu`5nDL3>YE|SBXx3FoGe4o-0?ckmFmOY_=KJd=Yw)=|7b$BfF3I -yBU|3hZj8QsG+LC^YwzA->>Hj>~;IuV5m%ohgBG|j2``oama0`T4y|&F52{-BRfO)vBD`3ZC$hrqs=1 -RV^812D`R>J8-b0mMAT?CM`Zxiaav-Nyla=13}f6hTs^docsBve<^Z95<3V%oNUU+lwU9MMxGA*kS^; -idoj%bK^syaJ0)hJT@w9IHYI}Mr^{&Hp`ht7qhJ~T4pU}%ZXguQwT@vv{H4-s6#)wE -S618p*&ip0EpDhB_1RlbhQTp2qmTq`HN5wHt`#iY?)iqmZ}MLZ$bDlTZkXS@6QNtL}MI!Wx8Hb3nsdU -?#`pklpWKS${9d+;^(qF`+?}I*Q(nY!ngkFnlcL12|vmRWSnTh0ozXL`5iJUH4d??m;PeQ`NQb(qa6B -EGB&(YtzbMY)12WoUbwY$w*5!p`8Z4w*yE&pPy3NNYhp}gc>Uz*6*;FPV`irKUFa^Yq6*KVjhp^`7PZ -eCbxLxEOPVXu@h{EgBBrdN7jrVr=f`;Yt-lYbG56wB2V|2Nv(S9Ekoz)&eZup1NR`Nv@5#V{G1l4=7bv<6$0%7>7WOFhQ@PL!_=04>bTCh< -V3`W0P>*k+s;$5+Rg~XkrSS+Sw~#w27vjW>qe*KJJg}lTR-%uPm{P_O-v)!AtDV{($|wd~>L-~>D##f_MKyc(Wk+7IRlvg|w`{sdeIV5kw+%B}e%?2`OF -FPWflBnP1fJdkQ-QFwl#hry#0DabV&v9Int6ZcEazAt1yItm@RC*X)Id=4RK-h<6`847*V6~%$s*?Gv -*o#aLiG1PS>)ND9bpZK>PMTi32(Qqjb`btQLp(bMb0u4>YTP#yz@f`FI>{%!k4HH_}IG()En(~Q==ZD -iH(;gc}t44xV^TMI?QPI?We;>rEo?&N|7K_G;m)sloji>gVX}!}D$)v#bF=~)2Paifr_Vo=D7vfCQfle9BJMY9<1O64r|JJ$yOF(MD -Ox#}4Va&pxj!Vcc@}RSdI*=e0aOR++h})Yl69fld!z3`dSa(zD%hj!<+hZ0{&c4ltmaw$Znn6`zTIX~ -uYtQoSXW)RS_!rl*KF%|3T>$k=9nkJ5#acniXrsl1U;=dJ#*qqw|DPapeD~{P&knAz8vsDzuKi54C~zzIzE5clDe6WK{F)A3+M_vR -a2A@l*O9w!fgihMx-P2q^h_{22oNA?a2nmdR-pt(u#YFy5VN)Cbr(Z-sy7DI^8`DqQERVYq+*OJ&(HB8vF~H;oxjpIXiQqsyHslB4HF@>Ws?GRm_Ur&s-Le6ORU -ExDt-Yrm7~}vL7sA$NZzl!xaZv6b`PUb?L(-l68*}hKnoUOhP3YJhGJ -Y+fFL3-t+ -U@lthtJXS<9nhmZqC5lP-$U6OR2m=)C~KeyZrF0NikrTn>EmY*N}Pb~JxP*Lzs|4|J7~yiv@Y2GlC)|g88%B(OU*YYoiBiqGg&c)dt*L0IZ+1^~KTY -x^66a=)HNlJ<%NhV=#*2vzk<4G$wNI64-T{ZLyPDUp;`b}GrcNb?$np>!)C%|?P)h>@6aWAK2mo!S{# -x@5Ww_o600841001KZ003}la4%nJZggdGZeeUMZe?_LZ*prdY+-tNUw3F_Wo#~Rd6ijTQ{y-if9F%Ey -bqh~IWE}8+-B=I25zXE8>SehwrWz8OR?odgChGZdm!xXcfanIY{`iO3=iPM{a@X`?iNuLec;bK_VxM` -E5D1Hy}!Hr6h+a=Nl}+umZnA9w6#c6Cbv~tH;ijtHoTFgHYX=(%9Vn53$~1q@s{hgxE23xg=xNM(^kk -L@+f9eE=`l7%=B?1gi705MfXn2N*a+hod)|URibF(?n5oto5m7nrBd(}8j$c+)^YtyXi@W~tUD^*h^I -!$RY%~y*>cU-!cjG1TNP4?j^-oZiu_LC2VU>4;Eki(RZ_1zs&l=wUq8zM_dj|5RaVFqGxy*etYnp>-- -XW0I;nW}z}FxyX&b3bZywjBmKj&_S)>~5IOMEtU`?_ubKyBJZa&^!e!NRB-hcV{@AUKa*UOmQ3R6LV2 -KpuVUu&+Ct!Q}88}98k;vF1MEmtkZHD>=2+p^yMfd>_{wP@U#x0Sr|4}uD)Te+qBvUww7?ZjC8Xvw;z%dV!zu+!&*>DZb1 -~lFV>wuYf4sXwPS^7vJFW=KS_E)A4wbu!mQ^~WA=8(9dU{lz;)k#-Wt-cYubR{A8;g!}8I1oDfk^v&CvdVrRV#PD$+ed?D)1AvmINRptKy-(gc3PkAco-`%&8USH|K_IIV=fpC)usJdNKqS%qbdQxXa52)vCwr6tZ93zPn1m|#rQ!TB -X9{X)38viRjB~$7LQ~068d&5;S4GIe@Wq{4 -mzHiw^WDhBfFAy1e^$)AlFo|3p?HL?bdY>{5gm0!ma?*k`y|Ftb_U@N!VeusYE6V3E&Q+!R*X8dRS1Lt`_>GVwX2p -)D^52aGS9!@kkNj+DF`*xaB+SH61;wMH!MN3MiyJ3Ek|U^YT%vlII=#ZTZs#9}lrHD9(o0Fb4DxXu%4 -GlF2k4z+=EsRzT=Dx`_P7P8lo+f^DUqkWluwp+TLJJ>AaE)7T204Vm^)q(ZFSJSrkSzl*2TIL4I`!eM$zw^RQSq1h@cHg2O^n8cE~#FnwVFePn!PO4T-^Ckk@DM{C1V6n_%h8$^YHioEjF-h>M$j?X2z0Rza)YwJ&x`$Ixb?Bl_WXDS&TLf$ -dcu()gtY$=QsY>rwe24GZ|Omd)D=_=*E8jU5fW2#08a_X99R_aa9Z6Z{Db^lYOTG?4#9L%KcgU&K>3$9tWT);t -t%xsbl=!F%^gy#@fqowvTfo?4>xnEBuLV{zgK8{&fybO&X({R1dDw>zt_g^7QxVb-u6EtV_3^L;aAucdSVIT7h0$0H_ -X=oF-dY$HY};}2`lSKbD`x7Mp<>m -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 -j!{R67yTg^t;m&o#wVjoUF)xLUs%h -gnb1TqQ__&H^yA7Nhs#bdy;Vz2gB`I>Vz&Dv2@7X&b4jU-(r)Z!$VGy+-_V=rh=lN9K$P=oL?Y?3}LK -ycdKs)|_~6=s>kF{?&ZoHufW7O`+d}rgN9Xh=M~~S0UQp4Cw;{0-GN`!FRkNM3Op-Vt-s#y>R -UzOb#1t8uPRx2?0uY<2auS+RetuIX`vFdHavb_LQ5A3B=uFegrE`%Paf#*~26S;JLUmLjDRg$eF9ZLz -l5wN6A;MIm~b=(eUdNins~swB3>1Q?XJ;{~UEv>&I^k2_F#D(H -`bO#2TZujHhXxK?<5Q;-5m!=Mk1TD3fk01E)&Y$JW6iz+^~rbv_eGLW&?2%+hr9w3wk8#H^F0YBCr~L -i{Ty)z>!3X1ona%;WegRx*{ -aDt*E0C6V%@6aWAK2mo!S{#t6N^GMJI008?B0015U003}la4%nJZg -gdGZeeUMZe?_LZ*prdb#!TLb1rasrB~Z-+eQ$5*H;Xb2U~?C$nsr?3%Icx2Q8c;b`YRJAS}tDv=zl=c -9&L^!2jNvU0y^=j+;~uGQG>0IdeaXwOT@`k_1C5RceMJ2th70r7gV16t%EQcRDe@mdR{5eZVwQdZ_Vp -j>ekdQbm}UQsf<)jI`jUgiI~!R3tfzt)<~y@2~ULQJsKN~SXcxLjgt4Tv; -?oBR8}V2&a}ZD55OL1{UYWFc9Dh$4(2Sd)q?MMn|b**uOd$P{MMI!*LvJPwaf#z*6GadHp@r>6&}y%LrT5`RNVz(w52CMX`TmJ*3dvADH!dH`HzgqCt?DJoTzehdkz -r3ggp%@~mlhvZ2Vea<>ZMQmB0!0fBcifY*bMX~Dz7Ll&)+^L~xkg3XZD;9KHhlD}z)ldV(w1v0&7pT_ -6OvtqFjt$yE13nA&w>7`+3dVA2SXEXQWuGdYbyNk2g%awAJu1by%6#Za2ot%a=CD3$q)HP!VQ>g=qcV -*1#497QNTK0q0Dh5%OTx#A^sMGGoYOaQ7tshP!qLbjx%y-RUCc<&TIGz7Z3weqz_HRBwMTFJyjHPilM -!yjle7d#Qk+HK3}xfVxJ(Uu%09vy@`+Q-iCvKJ9;rlbmw;25siN8`OiNC(3p7upVdv)h^6L8C-2@^T+ -K8qRfw|`9U-u!({V&mfz_yrC|y$%Dv^5&= -2UWaTkhdA!04ROSV(y?r1cEm&KJYgQ-i(OE?~lY8_kI-*3Q+yWiRx9^q9YsV2dD1K+&iM;F>$C+>tq= -<%^zDeH9#2MF5nlh987FSI+qvwj8|0c%>^2sAg;6P#kdDb%kv?*%{f%9bn+r{O$L7sh*19@eed2X{X; -)BwZ+D99JEqv<=Yg)0{I(e-2B^|b%~Im#(Bkxd>2q+-8#GYT~P5i3DFAu6mH3{1O=`@=O!@EcSk -1JRl?&37>m!aE$rxJO@G|PSz%*Sq=v($;+AA(kvGeV_e9F;h?L0KZ`E*);lkw0mSE}5<|}?l1#HHjS9 -`3Dm$up_#}l(+dAeP$!Z6o0opm)S@%np82(1pHV&@E9<7tIc`xDuOeanh3eQRdhZhixYLeHOVT{ -~uW8Pb1zowwti*K)XY<=Dyxo#*^ILAh -nv{s#s-+;u-LJY;+b|JaY?50VlT>ZXjR|5%Q(AD{hWM_DRS&mbvmMqF=|`lSf_^o6!f -U#Ka<@J&_$Gl9HiH}^Wn$@Z)*h(a}#Idl8<6z(-7Xk>8Q+;vkIf83;b(wlz)P)h>@6aW -AK2mo!S{#w(#bqmP?001Tj0018V003}la4%nJZggdGZeeUMZe?_LZ*prdcx`NQaAPiTd3{w)kJB&^z2 -{e0kyc8ithrYz2UZJeRB5&@#p=!&>=jYXtO*nZjEv%81;yASUl-$fy# -7Byu;IWUS#3a1=uf3)pu9>Kk6^W#n{1-ak3H%6OlwJKy%3)W&@53mvSQ;7VpsBuCLXBsXnuu>alk%-1 -33FW-`RYdS<@n%!^2<$;yw>{U85%vi=I&vxbUP3h1G2{m95sJR%N&JH1O;eU&qR7(?hOB$u=>gowHkL -4^cm<55Q55BpTkA*s+;%YtnasKx1x`cbDD1}QXHfv*WOu~~y9+0fXTg2yNan5u();hI3hhC|cmcs2mdCZ06e;nd+%crj%t -QWOfr|t#;GTu^wr^B21%#G%fm=?96#=ucZih0mh&IJH`vcp?hyz7LgeTC-(}S9{-t@7(l-(3$0cY?P# -s1Doy&!7D4nGUr&WtrgWXHyYFQJvIx9dZ%qU9&2--G#m$uT{m;|dQ99LevhM9-aMu_WGFx_84>e#?!( -T?+a5ewqK1O%=gt;7@V=zv8$k~3l<7jS#6FHYlw®c0-^&;Kq`dqmV%GB6R*g<;KQK8mDK`KoQGz0 -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 -=cA8)u#|Nabfe7baS9;~u#GS;ej -3`Tw`>UHP6W}giSBTzh9{n$A(zFC@$_5b&4-)KxYS%Vf+Wgy=Q{+U&`otzNu%j-g~%77&azXv8AYL27 -00GmlHW|k$H=LXWqzc1NM_qc3dBWD}r!%eiZ-n38VWu*VlxzKb0ONPF6yArgGQX6s_#! -`&Jw)9M@x-k%T4-Z+l`5)+btrtf-f6#fAxxV&N)u*{IAK5EAX_9CMC};iHZrLl1vlf>r9!zAI20U=0icD3XACNg&N`2&Rdm+&85vFm`v?JX$5e2m;Yxa`MDT# -Q-bN^NF#VC;<_f@SHp$;X5i}YuL$d?o-DNZ-BtChad$0(FZVUB&|@Fl1N;wr`oj^S7Jp3QGhAu0`M3{ --Y!F$igag)C)DT2vhC3Xiu2Gs6pv^Ogr;c&j>X6m6_q~i@&O`R*Nj4dbP^=0*fSaXR;lA}&+Xb^fh_o -EHAFB9h!8R?d)A7E?{FpPfk^+TJ>hsN0D#x53VUlJ0{U??{S|HxJWed?2`FpJ;pht-kho|xF$RR%Q_p -bN?HnBZr9w%hJq#{_bJn%+ldw4E-{J^XB3PaQwelo>)`I*zw0TiwE8yvqJPW=!4=W&L&nq~r&jYUoI} -Z>N7YPoz+HrZF6aY0(nUt%HflX$RBpkkg+ZB5TkN#3_;P_bOjAB-OV!^bbjlYS;eFBH}o{h0iamxI`i -8MU=OHmz#xmlVAz%k9T5RpyqhU0+QyWVsXtUcqV{C?-&?D -g+c0cKE@K_WRHH*Z4sB|A=u0WB*c=K{9`hGpASMNi=TsVIpoGYOoNXTIxmws7VzCX`CU~J%Vrw}!BQi -PoD~0ytWDXaJ9Uuk7?ZCVUUexF7g%r15*GXh>8Y-Ay<@RG`gK`#_eS1B%9B!vHzb(HrV|{bGc6%!Fc} -jhKuaXv??|5q^^eytwHLkqY)Qc>3o(dXu!o1udtIb6Eafmy#FX>zw?E?}edI-ULP`J+*_ -lnaqG{l-vGSy!r(+g2>^+1%V>%hd$FSPK-mku9nZFI{h( -^4>+9p+uqI8>Xx-u0w$KhFgYYdKbpr!z1d!R@GbrKawc;2BHJ)4Tz-*i5g8kv9T`))VF#DFlu$@H98R -E)u%YllJK)^QZpl!w|=ms$;Bhs%%h5e8tIYk`NF_*e{r~=m!0Q(-f?zE|q8a?VNPpsy&`Pc&3E9xXRZ}u`AWL+4_AQd -u*$dBZ^V@00h);%|VBPZEL=bmuHX|u;vGya_yRVcZDR3XF;fT1`VaV}3o#_pi9ydWRoM9QJ;=1+LG_o -S#51t%GhyFFlc)OHltLDzut&CIkBG$lRjiyvv(Fc9&Qd}4=9N%z}~lIJyf*7g*`-b{pkri>jB4?{eDn -CMnukIiv^^1Q}0Mw1-7?F#$0x&v$b=ejAjK5As^wj%sATPbnq_QVBlH5{Dy(>3E=LzC6RhP>bID%k@~ -55!-CLojT1kvw?OC2r -ssEIJzCm1f4ZMQno)`N+n?QYFAE3GHHJz6^99RD5L?buCKo(d)b^m4q<5o#Js+Xc5Mxr{Nu#T6J$3gQ -Ue1l>yf8%|=1tjmquE1|d8oTR74o3Il=g3vU(N3|co{M1 -(CFZPV3X2YCRt#e2qFm-GM#=lr8Pd2Ia4*tQu&zsnm=K3@F>Dh7tMqT8;rUo-@e-h=uf%@>#baAis9P -dawG*=T6*{ri*NYH#DsF(w?&G&gs7a6ud<7l=$BvEt5Gbt)!!Nx~OP?Q0a=`?R&1vT3zC)GYKg5XJ5S -XmK1_O!?_SYYj`0CLSZLhL@!0=8+$KM$GF!*ZKcY&M!_>uSX&mnuTdek8#`i4K{k&^ZyIa$DQUALh!g -Y0+Z^UJq!w?Nl}pMn|z=fg5V -yz2+o&y-bDz>8%^4<4TU@F$nbs?uObDebczYsan^KsJ#SK5Y+*-Ep#XKKf@cDF9cN=s> -uk%^>lLR7v?hWVGD&yQat_iPX&@^~KFx#n?A?XAA+*R$Oh8ji4TW?1ZXZ;lJS3j}z7{hs{r-M6Is>_w -7VFbP{PO&+U`7Mer+tPe%P4r=r;pXT3Ch>)9&i>G4bQoK*p{Ly!0T%`SADQW8({5?eQmq9*WU0y2oD#z4!xAO9KQH0 -00080BxrJTEF+inrsJp8mTP?Dwkfh2AxXY9m -%kbZXyRtBsKGWwTeJZAy4v)}qIbhyKv10*2Wi+vA16>DK*u)8YiOT0E1LR%hF;*^A|p8!wll+_z2F3t -87qPfO8fXXd+oxmW$MRoZ^hxZm^+_EGU*Ygn*aQ`bVS)^@}fz3SwuGBa*uFV~gS8qUHra9?qYR`$E9T --m2@;3GePCE#h8?=S!B&C})M=T9I1{l)9Qotds>TP&|ty=l6lmFxHN3OW?HLO0o3)=gcmWmW#HmbKig -uqzy*>fnfq)uF65OI2UnZeQXTPwVTlYigv$3#OUsa=k+eyNUPmy;_D(mO%TZq5ZN`KOIV-Dhz$=h=u6 -{^tVTBzjzX~%Z;NxtW_H=YkDT&=LyWc?wjuTuB)C)y?a;dLyOPVW=;+8%U7k|%YMD{-@L1VBDHPvBH- -hvUDtH{^()m&B=L)S4fDbRVUPUewd!x0?)~Ei##kr4zFF(RLT0%wmGwq_Fw|DdEq?4g@6fl;=*wK^y; -++hv(zd)!@0Op{nECI$d?_i*{DhbweU2Thpw{YKI+!H@>;#9x6O%uRaw8cJzm1^Cz{^ulyakgiyv~0Y -d+v&)7;cmBOSjyF`x5?VtZBc=i@S(h?(X2<>i~Vvc6JpfoYZQ2h4)Uw8VkAHy*?Y@$6Kf4K%7lOSiK9 -n!m|)YFbbzED0p1Mq2w0sO@P_5_`$t%jW7TKNmOmY_Z&_3O11 -lVelMPgW8lm>dpD!A)YMq%E+PeY&(!1uVbInr?~(b)}Ryl<9KJ-*j1owQGV(kw>#ctU+Q(bAKg~+xI~ -4{o4_@Jh}u2v%8g>?z)zjpNZ6q~^gG^Spc7B-v{(BUMAK3J5R+)K)A}5w(mWHjyHln$!E~B -E!6Vw2^>ZM4}Nk0DEV}!8Oa-ys%HH8`k(2V%0Q;l>gX9Jq)oS}}CeHtTpkw*mCJrOr29XNHNZ3WL -G;XP<(}2UfvxS|)?=u?09LciWK>ujGSgAk*Y7aCIx|CV~SQ4+BTJa|E=cep;@qrigf1Gq4^q%=HgUu9 -%(D81p8Jb2NN60r%=`$ZtB^qXCxCqc-oh)Y51Wqk;8p#2%AiXrje=f*`%|+F$4Szt#xwux{s?lW+xD* -707{Z?)4zR)ls*xV>iRL!n5H&SyVy|d|f`8|QBcNo#bOQv`Ne4HwKecO{3(!IftB!bd_+=I>cj11+P5 -+0-tn{yV21<)&l?;(w(Tbm#&r-g3Sy4;pXiqa -ET$f{Fvb9BwYL4aqS`YVeFKVAB#WSK1|8vcro!JYMZ-z966up7zGJQ`_kPHuK)=NTE%Pe~BKOfu5gwM -`hz?FW!@AQ$&UrFxJ6cbXs`QuO;4k99cjJRR2dGyAjTWv&fn)No0}sr+5RrzNi>Dr%Z$>LxUYV{|a`( -R5@nvw+``@>zbrm7`B`bIQzMbpNJ$!)KsfZ8V`evRx5nuW3MMo_`(h62sCA*WjaX{$I0h%${K{vOq*x -YV6PR{M_J}bBph_XtvwA_<=wFfM2K*Pf-AW_QyG%sOgWR0{^T1N^MX%V3)Z7colgCe*seBC}tuQ6Vso -(|6+6*U`B6%bU;20R#hX7#p(#OJevO47K2o7tu!D^d;xs&?(NI-6+pucE=bmf!T2lFA87p-u<3%4wd& -7xbLiG)BLKX>ivuGSOi);or4o53X*?l}$aV?%88taE0J_Br#qj7KZ<)*kC)4j}@q!}{H7G<6iR`rGA# -EKy6wHkmfKTP#P5a1B+^l{CD>p}40b`1rU87aZanNXg(7gy^N%#X)4}(mOoSiIz0~}a+?)c5HH}C^>z -}RZ1+;DcebJ8V;8fQjqL(m6hMg$Il79jZVmH6iI*SK)-lW%tA+S`{Kin}%)oQ3R4P!`^2{%V;X>rh}h1iJ^+>n>XPD{StUc-IJda -1vjwEBu-PKEK;z1D5$RCS1OU?fB#9pMBga_I{!~u1?2nx?|1B*fV~owB{3EwKK6>Ie$&ow63iy${Co* -N05QR|Jss@IEf10{FhH_(uGh=Ad#YdQ(#{=f(iAcu+j_rV_z%f%8ZCRJt-e6w}qd9_vG^8g3p4pPRf` -1BDSDtC;%LGaU{6eu06L82_$CHrSsizYPi67m_u3myeOW~ls5qORfAXWzSYc!VEbxvVDCa@9CNdW?In -6Jy`psNV|5-X8IzHT~1-?SkJDcj}%%EFul0va;M$etc(Sdn&1FebavblAm@O<7N48F1Dd -LpmNuARTQll0`j@1`Q$v194_>HNg8QeW)D`7Gur$S7U}Su2huuwwX@I6x>Lia~baI$vocOY|M-fT)uI -;ni-HppBX&V{PBn|Yo1*OexTVPwg%A~21$r`ZycYTr+jjs&P=Q%o2f#)Rp)^$7n6B*$Y^83CbQ_92E& -e|%}357C3f&R>H$K^frlX0XX0mF8c@TeW6+FJjI?qJJD7+A+o+%9g?RJw?eG8m*%xBkH)vYxqaahS0C -Q=-Q1sXRZvVyqYT`?gE{M)4K{39C?$()pC&G-G$Da_t#GkQ1Wjy+cz$qUEr7A9hw1s~d27WJW#MeLOe -APvqshS6JPh?wKWg4&Upuz5fSzm(fSLs8ay-Agv&>Hln`7M6zm7|9GRr1{VsEYuQPfgo-M8kP<4zWZOCjYo -Fg!Q|=hgmvwje5Yf=f>(=U=U>XZ@RJv;|zd+ATK-W1RFwhdykBV1|VSoE+Ws3sz69F$+rijDSHLRP;N -nNg-dl5@oFP8x`;$HT=@%aaE}u+i*o;(qls^|NX0YPJPKP6yZC3}lD_5x;p@fVqqUCyya8SVti@3~1~r)!Ud90rd*XU1l%h|3KO94G^DTQVaDIo?H}G -Qx46H#bIQD9fh@>M?0S^yCOykZB%E2xgQuA%*6;+$at*e&}i8NRnU`Kw&&Hn}T -#GByboK%rK}Z&qY#P4SP-ylx3{VVXfX{_%u}}n7dV>*Kr -`|&lQ|!o%B5T9Je8xKnu>y%Jr4MfeFt1Ar5r<@x@G298{2jcb7AZxI&_Y3QPlBTmK8-0rv0we!B8BXu -t=UFVC(9DEMG>Gm>PoWI7}8sCcA7@5069 -rn7X#W1C4S`>$ZICxRk;;-l~M%WQHMp4HUw%slKo6Te&BF1_*mcM|sOWN{=U@agicQ%c_|iR3N&dXH& -_s*xx8jwF=^GOlAJhJnXR>Ppob%MI~4?s{p^-!xK#qsnVet_a9##g^M3;xQBoD(WfCXT*qLPO~*CIjf -E%wFWj7L@DY`ac;m>TCtLJA-yn~Sd1ygBwE9Be2bPE6Rfr3K#++(u^|W1YB0u0580mZa*|OtHoF)nW! -ZOhUzsQ%t|{J>b2c-pj^l*s@)>+#gsF0iC@}_6pZA?o$!el`7h6>mz6yD-*NPo@T?1tQH2i^##|(y!ysd3NdOq|h?QLzq0Z*ePea+kh$u5I -i4LR7J6QYYNz~?^_>%H$qA&OG4;T~VfHf76&Qr7hKyz|HTTFZ%dLb(Xt_m#{DbS#`SxazU6oA{Co$Lj -sx(C5_G&hk2BilnEb*g6GH=Xio>{?Ju~L)d?jVO*lHAyeNSsJw$SS0=?DGxj=gX<#85GozjE#pejiK+ka5Zmkih`J -_{FjOY`>yshe~1D?I_jYUB{j$Pv!5pD>#uc#^wP_jM;%&}Xd257uyG+V0~Wn&ECbKZGOWdj&DYIVrO|gdmB?pN^u3t39Qgl=v;j9Zt{~0bII8?f21bjvWMm%| -rDCFEVz!l+e=w)q1+kgz>$;Ljz^s|}3QYm6sgia=P9MG;Z0~j-iRWg)ZHr1(aFn2UGP5_sq2eA!|`(% -qlb9@$f1`N;Q;@LnlOe`?wipf{2-dKDWhdo9vHfrkon$H;h#DQagA&?|M6YL}hpy8}PpSg%V9id5WQ- -KG5e)aV7(UV7)kIbQK3|>ia8$7A8cD(F~iHGNYYc0aa8ZE>6IAXQ{A?jU#TVmAyEVo-SVGW$^G+LfX@ -lU$dWQM9|n?|`zTX!-GfeKKD&$0bv>9XH#&% -QGHSXbBd#r2MhcTKfF#7-d-Lhmv1!&w7E_=S4l4~RGU(*>({G->1#WFy1h(}NJnsnf4Q^9TBgz+cMv9 -RQ8;4ZPLiF6-!J$#6;OPX{?m-)5Ui4!H>zMrx|=jgzlZVC%+F2wD0>6wbS@ji5~xJ#x^W2l_Rf -tS~}CM!m_E2l|B(>5Y;%CR=}F5<>v;Pg|iC!5Wo~sT(dCOl -b&oAX8D8(4vb5n{zBuJRtn?YbJbHpfCik_cyXgQb0)`jfzyo4VD#nZ-n8b)f$Oj@K}kiJ}A70D8a -w*(PMn|fL5;hcHOjC#@%4XRfbUrj7OVL8XgE}G9~xofjyiDbMb(<;{n$vJTPYM0lmxsz6k)tZ@VW`_z -HPm*(=!!CE;K~@EuxHGH|$NFp&iZVyK(}LoL`|t3DX+2SOqk-d?&O^$&2~TpikiCT%+`wuq)MQD{j{d -KkNbQ?-79##m|vj6>n{;~`05jXJk)mCH_PoIWIvOZ=BpkV&f_$-9R%q_xV}EY$&u^?4gszpmd*T< -zO#+LXJ5rrWrdpy%WPtBT+s9B$gstGbwo_;7-bf1i>r>Q+?Ap5a)#TCr#z)wp7Impk^!cBvrH7fIjH6&x%%gRB@!ez>nHKr98j*rkjeIG72nS<^GaQXtyiZVbH{ZBG!r4 -`7aoYRHElOe4KEY$tOPf(!n&nO3(7hNd0#SUHlqaC_mT;QF!VSsyPC2nf3eQ7Uj@`Cg;i1CZgFdYBJCY$A4*6Ba -M(z$h{@hY -Ea8GdAez`TqxKh`$_I3BRcEtrNhYBtf9S>KccAaUcMLsWS~NixiM3X~fVpqbmdi9%gPvhoP@%OT-QQ80XUy6<64@z%pOLD9 -Nt4Q7uxK*yNKb=EokRgXyJTf8s9#Phb_T*DXFI4CmF&GnlV5UyW<0GHdg$o!51sCLuRj6EKZc}O?NX>$&^#XMPWpz?V2-@d(>*V-ZuwE9aX3^Nc -TVKVyP#^VsidjWb2Gylqol7P;!U77Ez0-yE4<0{aVr92>$H`q~sVeV^dvojo7`DkQCshK -M>YTjNN=P%#hCTG7lKDx{N>7aMlS(cf8g|-POJ}#onW&t*~e3j73808V75ldh-RWJ~=(Rg7Ml -^c~&x%N5;_;7G+<8ozH5b{3}1*OzLHUZ!kbCml{6#B#L35#mj1J| -GfyhUQcjo~hO5fl(&665o_zC`5ECRSb|;bfRheQ3dfE`05I#5K8LQwL*Xl -aQhF6{YOLg$_LrCyN7d$SdhP-R{2hn>1TCF)2LhI(EH`p -6vGZf0kGQ3UU#8sjpi{S%0XD3&S^~fvCbR2e{qY63o^W?>S}$UIVG&B>=2lJMD{4_0g8$T%f3%8njwA -jPVOx7QI9UVh;SmfGE%=2wQ;u_|0zM51i^3?HUH*hbdjM!Cb&z*6odCO=TB`rc}%q*Or&8*`XhY!#fd -%08K!hcgkU@23i6MztX~NHy?gsod_=cO6JvU3- -f9y+mVtK100|gCn~NQD)e^6Kdewl*ax-ogF$j{eSB)}BWa`8uQ0XHHHWJm+p+DTCVml9tx{`mv7Eir6 -r#B#b`-o#2$yDiBp!ac=0)}`#^$v_u}IF6wY`$>m7t7*1vApUPAshR5XrarR}b1lg$|cn7^x -Vntpl8}A!>lOTa^_k=OF|!=_{f>-tEFt8wR_>ft4QiKXE`90hj;;{QQr9T71MWewlP=xRAj9k`$KOP4mdixlmR)4{~y(Q>7?=F54I`bMo6q -mDcdTK8*w7&^ALn*mkZA>sO%3lSP8B8@#X79$nc6IKLZ(qj4n7B?&XwWTNR}FTdzg`lc7d0y|5L+v^8 -YgPjW7h{=B4#P2)5i4q;z8&26fzFu-IA?$14(zlak!lEPPjkmm|%ubLHj{hi3fy9|+4q`rBVSJqFtJR -{nJlxVZx}5$sz91+2LNliTPJy0(+~t#eroPTzA0JE0yT1n^CTsdDJuc58plJhsg`|&M<}9qv+>AJ77{B%%W`y{~ -7WE`trt`2tEhRgqo+zjL|NUA60JfJ7VkI>b2!&N&8f@9U;(##}}S?M5`9x%^Ix$Gn!m-fG|cE-roD-` -FTNZ-p;WRxRL4()G%}rEwm-9Sy70N3}b>|5-r4{vZLjT;D-ElelL8SQ@2$PX48n{{c`-0|XQR000O8Z -KnQO000000ssI200000DgXcgaA|NaUukZ1WpZv|Y%gzcWpZJ3X>V?GFJg6RY-BHAUukY>bYEXCaCrj& -P)h>@6aWAK2mo!S{#vVU?XZpp005&B001ul003}la4%nJZggdGZeeUMZ*XODVRUJ4ZgVeUb!lv5FJg6 -RY-C?_a$#d@WpXZXd97I6ZreBzefL)oE{c^4R}aNLEDU6^y`+nvMX^Xz>_bo}v_#u%WKt!mB;IcSea{ -Sajg>YJYamH2&h>ES%%~e@_ae(Sb*P=nGNJdCbwNmDY>+`)<7czxS#C{GccIkl_Q_8A-Qa6!w_9zt?N -{q(8?v6;vQ# -$&<`Yp3?ggem1Z=+dV_?W?TNu5*g-Z=jVUro(JT*(fJY+O*QG3h((@l4VBjf!1O+oBh$lk~aU`OCupR -E7V5RRUw1Ql)1^8)2um8o;}NV(rlGq2_M`-JpaAN1yX6ss)MHavg$Sb+mGOy_HBA!jOj75QA1B}D1^db*;JxI6-whsKs;TXNfFBISy1l00CGUA) -$TASiz&Ivwm5}Ypgi@2p7!dBD_6YZ$mnbgL=$;h`6;jlg&*z_v{#vW@AasGO_2xi#Q^gYMZ -QPnzAoyb=Ejh5M=Kh4;h^wn%&?#q?EUvCnafWz?7KGo~TQAsbUOTUEfY%Z>wU31_wTE{^7)cX@xQYA% -A^_7?p1Ss0tnGg7h1#!GL9imQJt`sBwnkF$FNwt&*T^CpYz`Xrl4S|O6f2{jX5(HUi8O1vf&Sb(K_*U -jW2P&I<-|-MjGW^Fe(e<6p%_=gDpqbE!(y02?5CInM}Ax{$9_&p=@EW99>C2z!$%|EcZ}Z%jOB35Wwa -Tsct5&)CA~UrFYz}}Be_YjuwIR~tdZ11*O!+{nz~Ac=+V6$GUBPG2(Hy()weU7LPhkJXl -n#+C++%}`kj37;@1RvV7A;ADgp8B)Lo%bZ|y2FfDjZ;?$R$-o1}+&*;pO~CyS!3O!aj&QAv~0juS_{z -bp!BpJR1mo-s-G?LnU>7*Bz@X`@R;(zgz?9G!)J+w*=c3s__z@wr?p~nmR*8ZIGcezL?ug-I1n9xakox+j|#UCesAq{UGV}&f=o0;ANkRG$CJW -V$|QXTJSR5Q@y5YiNc?z$~B=50>5I3yqQAW_tb3s&^mNaWes02Bi>&HSgkfxfyrc(_rTKj -ehltIu*RGi*!vm$9NL!eyTLeyj8=X4j@68MEI}>FD75y?<=(3c%QW}K-p4dHox%M<>!t?!4_+eLq~a> -|m~wgaYk5;Y!;SI^7OvWLvio0(N34_dSv5P`fK*n*AkpZp>=~-tfdRi1w00VDcVqK7UF_zFSVx~&mT~ -LUxHDVSIbnT>q_J0FXA7?LP9t7GrfG!tTRY$2=zeMV^5ym#y+r>SJy{P!mHz-xO9KQH000080BxrJTG -j4RKZDn*}WMOn+E^v8;lud8jF -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 -T!bBBV3Z1BSI+N|W>EHDPSi2+%HKAV-n1InDd2 -B*Wx12cfQTsKkKuXL}Xlt7t|orYb7+B^WsPAOa?d2oHdB{JrT`2VX_kvXoW+E@e&R$WQA)miA^(LPza -?Qg6Gns#Lhe;nyMdIqa%KeoW0}r7xNdNWm2?1dNQ`C(0^2UVsA59GLC=3%d^JoIy6fe?k#8uv)PAxTX55G%5Rxa^LJh81{0btg-#t9l6U5 -lf{^T?nhnmD)~QNBYP#l8U9g&P}(+qQ|5WQi~-J_RY@lz<@`aP8=wnDZL)?-cGR8@zfy#~z$(jYcQz|2^KJ#G{@rWv9PL?Qq= -c-$U5V<5=#u(ONm66EPb9f5r4VBAOsCd&y;rn&;**2ZWo%m#LbC+gguMtL_?;cwaGV8^!=C^ci(9jU8 -pwn{en@FB`VnnOyHz-^AxNYLs{y%%ll@fL8S_bNGUs!dI+>x~_S?AUwz%7=lR^BSh*KfB0^jmNje&SA -Zk^@jA`e8g}<+unSqJ~SbrI1XtxuQr`|m%>&}t1^A|;Kg5Jc{a0t2NLWx5f#3u|MK)Q2%|?D@V>@<1& -htftuh458ch-aWFCSgkCDAW5Y*y3C7#@C-D73Fn-z4|3Mjs=0Nf7HZ0%Ga?3>HH)il3QSc)(*EA#d=p -$$9iiB$cbXk`(uaR`V_b1jHHUxu5b~>^)8cXaK -mBaahMTSNri~-<`D2HFj9I@%y7bvL+PtzVf)g2web-Z%Jy-u>_GWOt248DHSYFU-H&>e=&2zm!;$SV; -mHV4hrogvu)bwV;G4{{wg#}ROczp@iCa|OR=4PK*;{Rj9Z&L`FYfp;LdJyDOL3dA40(B(zmR3!^L99b -k>TNim}pxWNAWl@ijm6g*ps?EEW1%0T3roa$({j%=7O9TvlUYzBi!7Fs$h?Q_Ps#+;9N-kiI7uM@T=& -&UfSo!${nphlH!FD6qO3*PWm+?nntj{muN+zN!MW^97u$Wz}d^N(7GsCX|A>8|^-Bm!Ekke3juiMaic ->jVN=*fikH^2>$MBuJ{31HWClL+|ENGTCR^myH#req=@pT6E3TORzAdP_88nZl0*I@KTdJ$D4{ov -KSI|(snZBAG+yy=InTkjmpas@z0Ph26B4q;n4>+Gl5xs!Da^>cG9SCW4>&g-8`W6^a`Qi-L?rV|6Awq -@)hqt3K)72Ug;D(GI!j8?C;D>@(#GMK#0ntZ#4kLCzsBN_Y_e8ItjRPD(qjpon^A@E6Oh7c=6g)ev4i -3ud0G}K%LJmw!Nai*%tc+Y>5z=RX0j7d=7^+1yyp1$Ovi(CI_+U_AIs5V+p~+@?umqnC@oWtQJ0`Q2$ -kpEixs!Hzu|%Z>9UR(oU|BMsevke+A&*nYLsW(%x~0?4RLGM8A_4{=Vm^8L;&`Tz@g$nV41i(j47iW1 -h8W00%S0}rg}(Xy&D3KFg2eo|&bGvm;JN8{HEa>!T`)j^i4~x~$VF2GjJlIjgZW#E7;_7?3sVrbpjv} -9l9PYrOFfZ}(J!U!XvcM8Po^l2DI&T|L47sVWy@%iA2S=$e$@-Myq@lnQAyG-jEn^C0T^*l%ov_WkHA -rpX2uCdQzqd>K1PX)%%L;DE3_or0|*0ZxFd;x8?kE=^&Fw#O8{JoIKr*G&dwPFq=V7UW)l>)E^0(F3X -lamu%TKR_1FxSqb>XS0j)`iSBNe;EDHlLB>ff@uuNN5<&q*9nc@fegIJ8fZbdsu6lyM-Fye;5V9<;gMB9C}DarnvQY)Mc#);9m>{VrUIKSN?@|H(FRAo|vKxT(9eH -D=+#n2_{srr2)`l2a~3NMU}-CM?qsA5malAXH7{@67!Q?1f2v>3)p~l;#y(mfEQh84*RgRwWu&ujAX0 -}*fy7jdUj6x>M!8LUSRMR#z5$;i3nZ7*1#Sxzl=@$b$)u*0f}(yAhN0j>}b_p<_ig>?)!Eg+PR26N+Z ->nr2&)@j+>l&FPIB%2b9+ -jgVd!!N(O|CKK}#GJ8mD)s`!5=!SlA?*x~bdEMbUPWooQblCAh#=|ERq3jwZt;$A^b~~6Tx?Z|*1_K( -JYuX&l6KO1>QBjz3S+^D-Wxv*JQ&AKVkX#pTB@J&atW&e)3GASe+K*x+r7~kW4&O%vqddMzUN#Q6;pM -YufVX-mZOU56w^`|2sv+!W-}`xEx2p|9*6rtiTVMak!CV^zoqYkQheETgSpzZ+hvQck*3m)0-@H52Xz -c;@GuM=V}h_3ruR{f;U|nBBL|Sqwl(`QGF+Iey@p;32m&P_rfv(Ts?I~w%@x2{|-} -uD$m0Vu|&I0K<0~0k{`!jq1YN%F}tjU_i8FGYZ1rA75rZGz(eism`7X~q)eGK&{75X=%&dLz8eSZWVP -SiK*^U!zTL`@N>sK)o3OKU1H!Kz9V{oLd;)+h`a|8FC8r0ETs)N`2YuWY?e6 -RUP+$PWz*TvFsi&)G5Lp!K%Z|4U8!nq7fO@1L-!88+hoE*}M&|_U`K*3GPx46@})dn+x>0*Y9(0&?jU -QV^9v~+sjh97}knBe7imRqGLpIt~}B4K2)wI@0;lj(-T1xb_7S%g`Q&Xoxj?MoeIAYCTxO>b{s}FqX} -ue_61lFqfOgdFWOih&UyiICpv<3A^rWHtY9=7KuA#e2A7A}@Vuh99|(zsDb+BmCle9&+l8@7WOxbCD4 -J8>w0lI>&wA4VO$nrKZner3CHPQ|%r~$Q>TN8_z=WEC@`g27)Cl|&7&HeRMus3*6lD8>;N8?*%K{6r{ -bW?IY%e)O&q{A!K%q-U_6S6{<`$RfmI3&>go%){=v<_%^AxgwaOhG$K2X -i1DsC^Pmn8=Dp+(6kBi!zI_GHXuQvp&)qu(zotrei$s09A=?r#5WqtRWey$*doBWtOj*j;+wu=sDHUa -b_qqmh)1oS|AEa&GdXW)yNCjG^Zd*uLTfW80_q -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 -TKOk`&wr6^V -x4-U1S0ipO#;4n{*Em%}P56uWuf1S{7g4T1$8Vmhgu#ei3P_#t)E#;)?8@O_^@x3JCl1{hO;RbziB>1 -uL{8#w;mi6#P71N}=udrg -l_2biR5M$n&f;BIK_@gMQP(py{j0}a|o4W(W5Uj%(=Ly;*Lv|_t -i_ZQ83YUneOoa>xSQ7@a=Z>R)}@lT(8p~Z+|JfHRK5#tBu17q$gp5MBVIh#n)0mz$ChEd;dXSAf|&{U -2e{_HG*498Rb$KMNr>NR+%ejXo3nwRY>DiWuU#HK|YQ?Fr6QW0V;sk& -l>LB@m8$lk@&+BPak^^`-Z<(Re*Y@K*ygG2|G-xnqPndVxM64K+nx>V=jxa!8ErZC7&zPcd%2r? -!OmYXHZu-p8wOa%Y(nqmaGu5JsV{`l -iZAA}m2K_gq@mHE97pa#VNu;s&x^TYWNNz=X>wZx*v)fa9t)4xaze`7 -)T^#e|z&Dz1#>@xaN?ZS7?dk6dui@pQ6~pYGw@#f|xe40L@VscKNAHdZVHlpvfVD$=Z?oCOw<`yqqrv -s{>QukPT|TcfdSQEs~t@L3B;xQt2-Dl;-#PuCj1+Pf#N^(oc4aiVDXNe-ID)Acuji4`gp}IPy$Yy^`W -2X#n~`FSZa!tR(RPgFgJ*@$|kZ4>s%l{`>lF3xr5mUB=NFW7u`keETH2Fb0Y>e(rvGV3-7+pq0UVd@gOYJ3LCP_lnGwHDT3?ngu`4GyZXvdNByvu -lkja;yfWG}a&mkY!1-b+ZSv?@qW145(W -5!2sEr%Hw`R+W1JQ1ulSbyzYD -S;ieHk@@n;=>A;fcmaC!f&?hc60w(}DAi9q16Tl_qf9_q~>R;+6zx!y|lJY9qQK$4sNjpuilT4^t5&` -LKgZn(IQ>C(#wPF_JG!d*Wc`C1&GsP5`Y3Q;LsoTXoVg-zvVcB4oBdHyvtJSC%v}BJs*MBrm@C{Hs1y -fdmy(Ha-J(0p|t$9I17P^-(;524%7ZB+n){&iMZ3kJiyZZ-GvxF*zV6^a$Hp-Rhj&wkIUx=$*6 -8v&vTLR?a^_7TP8uvo|0^>_+UjOFv|5~!OfQo7Yw2-32eM1egVsBI6rFyn4#X2n$-MzrLE89V<(IUdA -Z$a}SYgJplA2c~iu`Ml7v||Eb0Zn2WuTuauZ}vsoGc%zbBRK^L6K#qu_Li}I;8CcUc$`kM$1(ety0!t -G>5cESb^D4p#^|*F5Uaa8XbW$v6D#}GKK59)@~$k9A&NYty*OUNHxM;*{>d{T0K -|xt9@)Vb7j8RN4{dN{7Vbws^rSO@JMvkVl}vSSVm4jFVk&?^t()@5O*>ui#u2f)th9%NG -ii=djWDqD5e9#!VpVfYID6Qfl*f;pa9vs#ay2osm#Vj3AsNwYVDw)*i$Lj0`S4b$1dLjPTc!5yBI15z -}~9M0hEVBih*OG7-|n$fvRP`Fyu>)nIiZ_e7+GKWMQH1KbT|?KrquZmucC;o#=Nx -o?Nuf;D=q2?QjzX%2moo=ww3IH0Trirs!bD#OX2OY>-b3ItWN|Rge)|F -Xnw*$XGK{>~MI$FeYS!;(E|{-zHa7TsU~e&xU0?#7`D&QjG|{wvp{8|qrY&_L4^KJv7vFpupRWpRL1Hn=QBYxPGT7np4H(jOnG&IRWL^dVW^SJ;!vzb=0*R<}3qAWpEIVP{In( -y7$@7S+@{TcPTynVbXH*&CR8|c-fAo^*)Ms1#@AqKfCpINUj{s$F>K=T*>CI=Y&l?X;AzdfgO_81`wRY!Gp7D7rwySfDV?OvKb -N{PxJUFH+Aj%kT%%cvsw|jN$oEhW@DhXY*qHGyS5L6gPwr1_iT4V2JP6PgeI#pY`)mYO?3cIR8w?xL} -I!`Q&x@BkQ;Dnbt)7Sj1?;u{7&ogpq}?(dKcr|AH^BP99@qlP^jos!c#blpw~M)pFe7J&-Afe$Az390 -s3Cc9ENjF#Ecj01FBj&93h@;eT>oT{RJImEzKxhcZK}`GLltq;^0p9WM6$~UrX!IBe#|u6#HZJr**-t -NNw>vIOemMLOwpsWaXl^xTbOhl^b{xMBvFjz>q#$F<*e`&t<<+gm$>VoM3^;8y+RosHtP(xRO7qws^x -aCYWFkUPHR{(5{f$9b+p{erkN__zNi;dhM_YZsMExRiNr~Jkb^%r`xsoXC+Mdh?`O5##MC$k&f~WX$8 -sBQ=bAgV5N>LDzwIfQTrXY2~3!z_67mI)gZ(EizoU`@JQHWgnU}9^p)PvbapVuCWdE!T78PqSvG~N4YTi{yPd<0^rjzR!@BeA-@{e2ehgqjPJR?sxbLgqdder&1IAC9=ZWswU$uUSCoI9y5ieIqHa(_Rp>zc|lvcrrsy?HFN_KpnLX4L|`(NsQf4I~eH?a- -mEOa&8r#DDq_7z-VXC5~?(ck?c7%X9E!UNPkxVq^o^0D@*x4cmw)se2AYfOX|$tJj|{zQGdM-4hSLvg -Ga07%=8_PIS7kB+yYpD~wnTEk~Ffpwx`qH%Prqf4n{pNAaj;;IyKvX}dB+r>SSLwgvW?ezX!{aftD>9 -=9hsh+HAk)=AzR;?#hMdLo1NiXf0iZ9e_Tfr`zdInqz?SvB&wVmqo>dQ)>9E4;`TS(4j))Z?xc -S99D26DAQFPYy7rmcH*h@RD5Qww^XMeqKK4IP3+gY^3uNBY@)UF4NS|HBRKGg`{p81*w_nl=Oe|I|`` -ragYN<2JChe%RfZ?_*o;X -a#Nm51;(_P=n>6^r7pJIL01TqRvtw$6mAZMo>NwP`39O;ZlGqey_@QGEy?l?{CbK*BEUtJ9Wgt3~iFG -LzbZfw`e*np5MOz-gMM?r=^`S28hCekNb55smM1|^4FC$@LDW8>xmw5#53{I{6z-V&g$UAeM#m>}IFz -7BSXOFL7PzqN{!FEEn>cAX{;iej%VR&@dL}i+uN8%WD0Dp8m@(lNh>BvckdBU?j;}d_nCsD_NJVP@fM -BiVIU&Q-g@D_?kKTo<)Y@Fp`3&8A(4qebQ8~*0wAe#)1zPwis_>>l$A#Tka6L>s?;~4aeYMrG&&aCIf -MQs*SiTjD9`~Q@q&+urFTudG1@2Sv0_}*g}_ydClyS|M^gMFQJ-!*YR^eWD^e^xT`$@G$&^NGY^9Ep2 -;il&KClRrM4n*7t0(n&&HaOd{Vuci1?`|bwcpE+6<-X#z~6MW4rGzF19QMRC$-==b`({AXzkg+mv)=i -!sDURuUe>#L9%+(+{pH9nZ#Hp;D08Xdo%CSC^pA*zsNjgEEm#J%x91*y$LS`Z7L`ytxZ2FMLIr&vTf!S>JPgkU}&Cl#GyfHGW#G4oE-X+Yobl-N3YYL -#soBGZ$xSsqblyz5d(lvU-hzJdOM2Xn!_?e2ur|6dl$7PgMFs`wzTI_A7cDL))F}n<9=V{@rBZG{SMv>{o1Vyt8y)Cmd~4{H;K2+IAx -+%#XM6TL{!8$`xj740|XQR000O8ZKnQOF7-zkX#)TNItl;)DF6TfaA|NaUukZ1WpZv|Y%g+UaW8UZab -IL*aAj^}Wo~16UuSY}b#N|ld9_x-ZreBzz3VFmx>y1neSm=$ShUR|Kz3VXgS{ArLrYZ5mL^$}N-6~T_ -njd{*@|T>P_#b8v@{&PdGqEmtr2gd3r(eMVI1Ovb4@Zt7m2|@vbhc&|7uQp-F1Grv!_M^%|+B+s3cjr|jA1yG|`#m+{QDe-r)#|aDfU -*O(4&K3b*9qBd$43Pn3az#fR!i8H1Si5ShwZ+zDpXvgja!6~$QpAk)3$P;cP@I0Xa$X^eI)mSA~q@Qg -?412A4!=rJ-O?UUMb_D9dWlrr7KMBi8Z7TIFSiKLJt>UJRmJzhlEU>cVfyo@fqn1&=ouIBdZbJDGk_{ -k&&Q+%^2cse~hP3FSu+X)B|mJY3KjDN!{niV;Csj)~$xLGdq>9S``IaNnD>yw&M -HZjuJ!<8^m)uGcT``rV`6B*Lb{W$2Ui5Pr#wzP;DL0#;2x7Gx&;jOH(N|YqsN)0JS{Lo}clUgVH`gNx -Ob&lRQxb&)EEfaE|6=LVq>Wq5K0=glS*^7FRLb0(3C%>P@?jW5AxzJs4RTD_hT$zd5BRP=P41SXJ|4H -sLb#x%^!$F^T#(pqQ~Gk)&V3(V15a27&ZkFsk|LCssb@N~1C(zZJRb#qmf+cfS`7_FNh?F-qd_A{+Bm -blH-B+<*S-cDTuIeB|8Cx+wLMZbfa#&67j8sku%p@mb*5VYnKuuiWnP;90GC)9G0HaQ{~XwrEYHWw66 -*G^4+@zt){<0-O`4zMzvfkG&2GY-wW!|J&lpp$QOi^pV%Pi6;EUb}yeFqv;=V! -88&YFwa{{v7<0|XQR000O8ZKnQOiL~?JrWXJJ!A<}G9RL6TaA|NaUukZ1WpZv|Y%g+UaW8UZabIR>Y- -KKRdA&UQd)vm5zw@uyk4iq12?bep&#l=qS3l@fSKCr8C2fyVVUS#ks6haS1w``{{qLQbeFIBb&OMNP7 -O{`no!QxW?JWBJe(wYSuO_YdhUc|lt2E=ROe&G)*S$W}_EuG~VR5`_>Zamx%+gI+R5eTTyr`2pEppN8 -sj@|}mZkGZ_NVDBiY(&`noN|;7kZ9A@|;& -mT~s4>Qp2x#rrIx)I$30i5M1cCrbxEX^V8KoCt1T)UA-#-6J0atF?cTiem0F?{b~B@&nNGH98uAWd?$ -arO0o>cmT&Ki4;)CDk9tEz+%herIL%>(d6GqoEY+YFn5grp}0f1?~2TIFN`%}qOPu>B{m@vRnSha -+iZrlBPN@}?~!5$>~Rh>csGqdOrb-?qeZbPVW>gX|Hoim*X1wxkN5?*)o|P&VUv?#@73wMchmP5fnI} -u{BkM2A09;CebWd2IAHIRda;IS^ZX{QiX3rbH%XP^#12KhgY9}llVNGj9({BB?(gx>FFu^Sc=>i3AKy -J<+q7P@dd&fp?#A+D!(q?lA{8}qa=O=#yf+qPgsg`WXVcGc-r*|@)PT6_AWXF#mKaGw@0PD%)aR|4X*XRy0YRcUhS(@lZ@Ai0GA_)SU@V;luIt^M<~H?FH -2z%CQtl&dVYb>q3xg;3?uA0gw0#A0{3UI)72MnGC*m{4~Fc5P|IGSasXNM^7P#~OSM)@@_MW+iwJ7sp -Dx}UKa1Z^u|>j=U(?Un%Wv}Z2{Zwqq9Dy4fjFvafx>X$0vWPx(PT^HGhkFs6V|<{V-F{zmQBk4`K|`m -mkPt6J@7~W-H7#@dUg1_{%{nkfBHlZ#G&-E~{q5d5?XrFCj-RPU$Ite*up=Hn+dm@F&9;* -x!UCR6&z?Mc8c*N9I(-c$L05*)`v3@7bT*z`U5f7pv%|}61fm<{_Uq}J7eBqdFrA^NqF=9vh9jwCWkE -*Qynah%gE&St5XT_+dw(T=mBGxk9Ee2X~5u+L8muj`)mMY2M7 -J(w*u_&5qp`ik0@Q*W#F>)>yI&&Dac~N9i=9nqimUaS8LpA;wOoT-(ts<~^uh=hO;pUv0IP)Q3y6$1C -g|^tZOWQu$Qn>D5O%cXt$A@l~59ibD|Ft~EdCuB2pi*IZHs8fl>oI6_sRXfXMA>h)6vRA%@)fF&-(E+z*#tF{A+hfiEBTdbTHfPmhP2``EUg(MXYXA*h`QeNUS}`x`mWaVevZigRH!d -jDssPRuU_5{;uhb504%Zns8{oXm(i*3k)i^)o`hONcCqzfccO@PYCTUp99oB0xj2sKiV)cR?iLV_-=< -opuy}SXf*NPTkA&$j0Dv`j+Qjft94MDIhC&$AU9O#J{#Il_&5420Kb8w|&(7aN{-P@RdE`b{Gb`xuVz -S~35$`-42QQYIu!r>(RHFk9G*NH`1+r4HMFa+@XjWn=vIbdSo6_U{o>{GApZnOpnT7kEB4E`qs;m5Ea -0f0?XI~LaAqL18)EgT0Mv>8a${$b_VN7Te@HosV!0-n)?ekTSlvAS}_uH6hhg&qhF!peBA>!fB$S!TP -A->NnjK@I9zrC4D=lSa(R}v$3Ma#QAMl+7$q@v$OYITUXmn9UIFrm;VoKtAHgJlr>fv0S6L~$f%U%D(N{!+oa0DV -DQyt|63sy3H*hA?C=nKvw2g)=5`lfDxYndWf&xoqIwxQQpF{fU+T&fwFq{)L=RI15|USoA_FyJgh*4u -%$M#yBP{(GoWF;{aXO$|pl1iL3c{hnK=xgRLAP6BG?eE{pSna7k{&CR3qkNW?usO-Y!QU+K^>r>W&ij`iSH?i4O2ePRrE=6 -K$Xybd}_+7hDTLEtIVBne=f>4HCjx#IKb~E3c{O#QoBu#DOb9ssZnu2}eyaZPOM2Y^iZ+|ol;s8!~&=|9;DsSm;ui$@|siBRqr!9rt99~gMxdRq8Cw!2*-@z4F$(QzbA8ne~nKt9JT<+N -yT-6Rvai1bbKs0G&u)ql#!g34**H8+Van-0BbV{ayahnXN_X8eM=f@{3Eb|kRQ}U{OE)f^WFGD&Skj2 -DVHb?aTSvWyjbEyG(=iX3qGnWl6@Se8@T*ujU)GfbK;(D4)$O&X`i$hR1Exy{W%J5@QHsggwN2ER4su -5Gjm^io&5t=^|&)%0#f6dXtAY}QD_Bg#uI`5N)U)W@!1Q`Yc+VlQljw3Gan=W-Gd-HP@G+LnJj3{X+y -4M!-dvASuSIDpaC5_DXH~7*QSBTf|AWmTYZq3;p7@)%h0P9iCGgqsR$v_QpRj_Y?z!l~zCZDv7Bs7~=@H~{R0yiis10<(mr5lM+2XF&g%R=5Jp4odY=Q_E;kRju%70gC@u@#qj6+!<`TDJgfM-55NknqKf0qpLK8nIJ -~ujvuPP}wHg$pr_GAI)bpsR6dM(3JTcM08n@jqxk+mF_R$d=I`b|9fYS^uXB@v;YQX_Q*YgtEnTpdde -mi?pmdTB&*j&bG>FDeE`V?9c&^dgpOns<6IBrih$gv=4vBBkz7oPqq`5495AlMec_7xV~U-qkQYm|no -9Z3PSB6Y??oT>`I-7Lj(yi-3Pfm3RokPoGS@HbILG)1_gDa*4od3iK^{Vdew2Y%7M5M#8_{fklZBdDpJO#p_XT=wdH+{MO-RSv<&2INv)MJ|J-Ic=!c2Vyc%wOI>d<361?MCe -_hy6q>YOoMgT7f-3wFh}uSEFA=^_3E-(oVM2!%@F<&VbJz0YZie%B8NGZ$osR&7tNYYn;3%-=XgsfCr -LJz%?pD9FE(E=G>V?7dk%Z9U((_;YGJw+iBB}oKWnhZrKA$zN2ihqF(DGdVy#ozQtAozRHFddi5IV3@D@CFI={-UwdEZ?*iXTMoa79z`Z3r^xq8O -(U=Q|8WcYqoRvm8CT*9KzZ->bFR-J$Q%q_MbT~<=M -Ur@NL20;B1=+#*@Wb?(kJwQB+}(HA_k>?!du}(tB755!VzPJIQa7 -EcL9Tu?&{oUFNquf_8yz_eB8!-G;tN;pQH_?YMD7;-I^xmq`G-3f={h+nP -OWqmri4UF?^lUBM{Kispsmra_X8M4(`LdbR`v54JZy7d+71IX9a4S|)IY=5>_m7qnL^%6rp?YF@eJA#dv-Zuo-uR6O|_nTLKo`Sg8qUB0Yw=WjkCtlRo|BuQyFCZ1{wq3jLJdq~*f8a-GumB$D8;^GgcIws*sg|B27soX= -*ZC0!Byurd2&Xl?%HTeETuN_`yK)YSr&8EO~m*mc4STI1S)ep;*!IY{h}!?uM|WKPh}`twM{@|Lt}X} -4*?F0k*mYwLpKvIvzz#!<9hF5ncR__pces*z2X{e@CvI`@11DlLz-!}PVpA3!1N$PGEx>vwoP_;5`#Bofo@=5RY7AE6 -H4K~@4L1&;-U!?bPNegCBnP5K0Yd4G(`UU+W- -Lmd?9uHp)~rSF6-eLY3NqGJ~s -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 -6FoeM{a^YEO@pwlL(L^pcI3H^@Gwl& -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 -I62d-Il!Vv;7&gF`z7ATLxjvE0EFnphqt -%t8>84DVbayF?3S!+>N!AVCSO4RQ-;pi%5sYwG!<=S3k~wDl@cR8Mranks!!#$H>M?Qz@MqJ -$XT4)Q?uj5UH$ZzhP@yr|0B!S7gyV=R;9lbaibC!7uKZ$sWN6fSJq{ZwdS&)tD -ELh66Sdmpe{ZpY3bs`R5<&9j&5NfhK#a`{x|0?(VjE+Z?13NDC{{>-Qg|W8xuwZLNlKJ}QymHc1|x7e -5en19JWPJml$NYP4!QNWyDK$FzzLdEZ`7~b;sz5LGyLz%#WK4TA8u2}iT>h0{_qUcI+|0I2SF~K$Qem -X4KqUFtHMkS;>_=o#pRB};(oDR*4l6G^qVA88D+^yA`1=4BkiHe*xp8Ze8)S`?om4-GjCkL -L}4Hycz!PEw46RKX3AyL0r>B(GLV|#~W=$+!d!Z@=r!cc6OI;f2)y>OU2lMu~zgE^FQPD!jaB059kUc -z>`#w3MnL|-KQYlhP9EL0+1P?WCIWm|xwUC_@Y1un=~IMtlp6x#MPm2~K -0ztfO9#lXd_eh`?>Sb}!r^G?6pVP7J#=FY0+QGMM-w19q6yu97-x~1ar(x%}skUG$4Xig}!ekNmeu_P -P}+Q+sFNixQGCx)^HSt&SEAs(THpKbaV3}IxUpSvuT2MW-|CKDBsg$7Y99FN>$XdrCAM|GA&9$Z(TVR -&u`LcMWEG?dpY+Mfi7ejc}8BCD-w)%sqMm)*FrZRr_*{fL`oLje6r3>E9jSd2Wx`#6 -!KS0Kq?||x=r-_nIeKe^j$2Um%6chM`mlMlskrJiMEkbR!->nFEKNd%#Ci)IcLb~k{nvUaT -2;f2?6b9Mb18zTWJF6K%^oq8(6Q53D>}6j1WSKh4ix_nqu~0Stu`LYh+^U67F4n4zrvd=ZUt@{L0`R@$)?=-K$G -1JqUamRUE1W`1*-Uy(isiCYGnJ<~=Q`fiGITFgqtrma{_)4x@&X=FfwE*Pe~pg{otx<$jR98U72uU=6 -nBn^t)b+S*m$`Pe@o?v$*Ga -vVd+D>skG^pNTQGA*3^ggo0a$u)NiHalCdt%(9kGbuzxwKP4H#om$#bO+W55TACi`<5abzjZLs?gbp1S*d`H|HW3%00&C&=DcTid3uq~(X%m0X;M?&>_GKGiCaG@@2*Q?+$q{ -r5F903^8&-5}7137!?0C9i8#-D(VK5iDKCU=p){-l#=?2qg#e0H)#LDq4|qSP-mpDpGBsB8FIH(aKL` -tdh)7E4`pvHUN2en)xxcM!4YgSA7)RiM{yDa-{mHm?ZbBBtUD!y+IXUGd5f%*`pP4bUOu;rk>I@xl2x -(W#&PY{L!#;2~Y1ovD>Zj(1W`JIzLuWl3AU~5=16RD7H;G2|^3#wwo?YGMo3pd$fKo#wDO0_?f35)QX -}aGbirMmcK7Wi3EE%W^cn`$e=fO>|xw$qodMuH%LA1F_6=jN2WAhK`v`9|K62LcfxFNdbIyv203$@#r -*~+k9Q2mc>nP^94Lq8F(f3o< -#brP?ww^73jIDD65_j7?_9`PFWAzPnwd7KNt8M8+BrFEVF($@_w6yTbOAp{?MXU8QjI|AGB!xfZ6xhg -mjA_4<7Gs>wrd*3r|ate`A3+9xj~W4{g{Dn{+Qgw3>bU0bRMU7(2b_X%78b|Lgu`bULV7wdczIt9mu< -gb!{1Ll)HSH{?5_8Bgyc48cSR(+@=w$qa_`-oe@e5jbfpd?TcZaAoTX(M5Yf)dNK2nMN*-fT=CK@kgP%0*jnEuS2U -y34jJDKQIgQ#AVYUDJ_Sk&sB$5?tAMCl!xi1s#5QRhQOIC`zLq7QX=A0J&MS*NrE#t!lUg=&)#^+-oTa0q+O$3aa;P(dr -JTg4^^q5EkK=jEY&gnMKUAnIM?4S2S^vSMs!YH}Az9piiWG>)EbSxHy3KMgXr%tczWTh=g@g5xiTskj=isX~%M -GZ0Q6;couF33`Whfbn#(nP44Y7(^23}{T}l$L!hZ{u$SX;59A{< -jWa}tQ{qtS*y%Otg}^@XF`Xj?$tV*cpy^>x;b?qli;QbUuS?9fmA;}-7*(w1EF?S#u-uh(8#iJxt2B2 -iSR*i;@z7VcrD1Mwo_4?AA!0mXiyk)x)6sWh&9Wy-5x=#c*aw{)TH;GHl94Sc&d5Ue@nWZJ@(>tm;g4M9NFLA)RLQo8gxHdB~**E(>E4%nkp=gOVH|GN2#-{EUZoaIqy -c`zSeoJ69Cehw`r$R^L`h`dJ!9HFIo`HU5*9JQTL$1_*kC2Y*{!4?l#vbbK>y@)x@)5XP-uB3uO{=>;nqMjOck<1Ikf5kNkZdMXnFHOrWpsAT{NmQB%ryWkytx`_0t65~XwK8(v;`E>$pWFWzN{G>m -~dsGfRGGW-_QA;ZIVRq#AD6a<)DIE5kFAL3rsau(exN8>6$Sk5H2gp1v){or%g>>7+pVQO>X*0-({Q> -0zd1-0(pLLI37!+E+! -06W&O9BNSPyb_>VZxwToF~_GZAZwqP{Mh_lwY+WI^DQLRt+YoHQJo -U1X-7L^P+kD6TZ6QVVKU~PCAEVRK}Nf!3VJ`F_>w?5uhL082UbWmiTeE#9lv!}_o-zJZ~LM`$0?(4^o -=juz`$gYPPPsAJ+3=_z3q)l8MwU~4OkXqRI`jE5n*DsKQ{=d0GdbA#=)>y6z%r1~K!3#YmXL2k8VWxnkJj@lLz=m-%)cvIW8qNV>?j?9$(q+Wkb;O -jr+Pag^0Qr^K!pf{>-P;?91XaSfi(}Z|-08Uc9;YzJ|J=to^*R3MKE6-j3(tAZe}eE{g5u?ex$&s+cm -;F+X}D{FV~$;$$G7t~eaXRpzRee9ei;a$PikCt8wh4ub7vhrjDA1L}2R!)#Pf?kHh}L%w -Gz|}?H-URyC2~$oo$Z>m?W6q_`o_*LGpwrH&_yWR(Jk4{2Io@Hdo9flc3DxkdHTiU?4KjzW<8Dvkw1F -$<>RmKPzz#N2*3J10Dt1_8v&YmWd0=TB_Wt52)N%3rMeug)LyV|YtQc7+QveKlLevJ@_%yCz|3vW;rz -H9-ne!WIcesL)90|v1+l?34gD3WNlY{FBx@sa>npHniC)`KHBJRp=fh=d?V06u6*uUls#bHvmR5-YxL+?#KJX_wFXNpg*x -L4VBpt4a -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 -jt5a^E#tb{tA)@*rP^Uh<%x)Tl -CWu|&D&w7o`6nYfB)Msd$sS-EO@z{Q?Veo>L}b?Lz+EOFaNkUI%*Ut-=sFdsaOf=2s#pNL5awgI;98pv9Yx=f}9Mt$Ca{gA1RkE57bDHU^vU<>|cIUwXvo`&BLswX5W@K=yry2EZ -V#atGMkfZV}^GL3PKrEAJ#s7okk@rj?h=7nbNsN=uyez^+-iafb5N3|AYhX(c;#o(|49RY}&^Z=GRC& -^yH558PiZF*)he(u1Yxf8drLx1`Z1S7PbI>`k*_`LzaV;Pe+3+lgUw_>Lcx}@SJt3+BV%=glgKYGw-s -Gjqj?s#^fPqfk&LNSdI^0;`k=<7<{llPDkbCXQekv;DbBZ0RiF1M(Esu;ORh(3i!ug>s?^GDq){IVa9 -TR2Brr~HHT~#8l_if^PeUdZ|$lM1>KyL{iMe -llVIZ3S#wCzE=mDPh~uhp0}1-AyCb*`EW!swL;Lls$~nY+;5WjjcBWUg-bt`)~fCz!F16}42^)H26?t ->LVxuKM(=BP;AotvoFz|K1^waPpc5cJq#(4c6QZKY7k85+rK9R3{w4={kcH^Lx-?*&9FmR^aXIozx1U -|`JaOmSRi&Wn@s|)hLSon-2L;aSY%Jf{Gc-Crz=w$?KQoh|J~A~m<;kU~;F+k@ZEnDVqsowf+k=_hL3 -VH=A)QAjx-DE?W&BSaC=R1Kv4&{*gTq=3CWaJrAV$A^#pmN*BFfXKZG%tn0|u&K9AUsj`k&ZEe$}>xa -Y)6jjYvYb6%HkAUqNiaqsaxA1Cfu>!2)au^QXsxm+j=bYf#3)h>af-^gzh>DXB -yT`@5v*s3lUX`=g9}vtZ>M=Trzm`0SXeA%P1;bpGuuMZ8^yj -OYg@SvJw@7{JdszPGKTU(jc&+D%-lS2U-_gG01$qkN6WgwKYiDGvSmyZ#w!lL~w17QA9FD2k>|T4`_3 -5f*W$a?Kdp#biP^QZw_pI6wTi?&^hFLpEoo~pZ+TA8NAS&uadP?-CA*z6>~^q^6bqgll?I%;n -;u9E1VF7+gINdZq4z-eMW5z%YtdW@(T_(OP2)=`l;-}g;M?f%`4dzS%GGMMw|R&>h-9kTs69FM=@w2g -geiG9bGkY8VOd!Sl|LyiQ(N@3M`uCHxWUKAP&w|ta_O#)=Qp1={ -q$PaF9V)umA4P4o+k|DGj$)|)sk1yGB?+Fjb}+S*MEGMO#nzveYJa4?0f3Gayxp&`<&ZBG7)b^u5K2= -5L19~fvurCIdk~J{UaCe2jx&{3d0h_N#0QR`?THAsS#Wfnuu-iQ$xd#RE12x*aIF%4l(h)h3HWblNSpHaKCs=E*>j{k7gr>fSQRpbs*Bq}Y#?c@RDO4_hUF%h`d@AOq&&BO<;>Hm$otsA0Nk -)Y^-QY5Xyh`_j0!=34Nv+6gkmNa_83$U4sKGX%;na(R=)KeAFs$-mC)*?&g>D2b0Tw!Y&(@oa{N1@Olo -D#*Q2Lr5%E+q~X@U;uWvC1kINa}LDNWk@hzPCd?X>Xvs%?P^KT&vRL4%F*Lgs{Vo1`%g$QxY6ITh9Aw -bduXu@Mw*~P5C%ig0U|+0IIvQvbgF4#nBaC!KGKDd>lh$BzM!lkJ3PN^I_zM_s%=E)${HRQKTHl&mUwvhWnHiV+-icPHdmzyz4e%AX@>xt^vesOr6|n9g73Wa8f3!cOrtkH{a(zNpG&JexUtHGsw_jENcRdVUR%!n`oOhGxw`soVr?_cqZGbo7gsqNu$G -V3LS@w;^2y-cnu7TBfQm3EuVJ%#T->~SLIIRwY2O)PVpFQcxV`n?1DaUr?$Ys3e;Ov(OvmS6x{S+SAF -|^ONHGGk;YInFeXISPOw^&& ->eQ^|hh+_4V9?rmD)=If%EtlxTZ&ql+Zz2x~Uj7OF$HD5Um8HJi>5I;%U)3LC3I#1Sgv*Jxb{jKpTWq -Jfx-`u}sIg7`c4sjC@M^@Cf`=rBOFhtKgUJsreLxAscF_8r!BA$BVubYEfOrjkXsd^RmP<7>iG5)kSE -a@3Zu4o}O$Adb4N`GYq#9lYGxCo!XY(ar=UzXxI>e-CWR>jbv#odZnZb|!L3n-oQJeDlb^*riF+`vbW; -I~&Y`a9;AuF1=olEp6(bP7>8p8*SC%p=LC51kyv_)sIx@yv7U0>w!Ajpm51O;K*S#=o)X&(0GXvOB^& -H#_2;+?q*n%TpaCJ2(ZiakWd$U1ly9|q0@TmzYe5!d&v5^WFzA%j7;*BnW5BnTVxSCdhasRs`_0Dd?n -1mE*x \ No newline at end of file diff --git a/DjangoBlog-master/locale/en/LC_MESSAGES/django.mo b/DjangoBlog-master/locale/en/LC_MESSAGES/django.mo deleted file mode 100644 index f63669f46b3283a84e04098a7338b55f204e7b9d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 11097 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 diff --git a/DjangoBlog-master/locale/en/LC_MESSAGES/django.po b/DjangoBlog-master/locale/en/LC_MESSAGES/django.po deleted file mode 100644 index c80b30a..0000000 --- a/DjangoBlog-master/locale/en/LC_MESSAGES/django.po +++ /dev/null @@ -1,685 +0,0 @@ -# SOME DESCRIPTIVE TITLE. -# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER -# This file is distributed under the same license as the PACKAGE package. -# FIRST AUTHOR , YEAR. -# -#, fuzzy -msgid "" -msgstr "" -"Project-Id-Version: PACKAGE VERSION\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-09-13 16:02+0800\n" -"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" -"Last-Translator: FULL NAME \n" -"Language-Team: LANGUAGE \n" -"Language: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: 8bit\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" - -#: .\accounts\admin.py:12 -msgid "password" -msgstr "password" - -#: .\accounts\admin.py:13 -msgid "Enter password again" -msgstr "Enter password again" - -#: .\accounts\admin.py:24 .\accounts\forms.py:89 -msgid "passwords do not match" -msgstr "passwords do not match" - -#: .\accounts\forms.py:36 -msgid "email already exists" -msgstr "email already exists" - -#: .\accounts\forms.py:46 .\accounts\forms.py:50 -msgid "New password" -msgstr "New password" - -#: .\accounts\forms.py:60 -msgid "Confirm password" -msgstr "Confirm password" - -#: .\accounts\forms.py:70 .\accounts\forms.py:116 -msgid "Email" -msgstr "Email" - -#: .\accounts\forms.py:76 .\accounts\forms.py:80 -msgid "Code" -msgstr "Code" - -#: .\accounts\forms.py:100 .\accounts\tests.py:194 -msgid "email does not exist" -msgstr "email does not exist" - -#: .\accounts\models.py:12 .\oauth\models.py:17 -msgid "nick name" -msgstr "nick name" - -#: .\accounts\models.py:13 .\blog\models.py:29 .\blog\models.py:266 -#: .\blog\models.py:284 .\comments\models.py:13 .\oauth\models.py:23 -#: .\oauth\models.py:53 -msgid "creation time" -msgstr "creation time" - -#: .\accounts\models.py:14 .\comments\models.py:14 .\oauth\models.py:24 -#: .\oauth\models.py:54 -msgid "last modify time" -msgstr "last modify time" - -#: .\accounts\models.py:15 -msgid "create source" -msgstr "create source" - -#: .\accounts\models.py:33 .\djangoblog\logentryadmin.py:81 -msgid "user" -msgstr "user" - -#: .\accounts\tests.py:216 .\accounts\utils.py:39 -msgid "Verification code error" -msgstr "Verification code error" - -#: .\accounts\utils.py:13 -msgid "Verify Email" -msgstr "Verify Email" - -#: .\accounts\utils.py:21 -#, python-format -msgid "" -"You are resetting the password, the verification code is:%(code)s, valid " -"within 5 minutes, please keep it properly" -msgstr "" -"You are resetting the password, the verification code is:%(code)s, valid " -"within 5 minutes, please keep it properly" - -#: .\blog\admin.py:13 .\blog\models.py:92 .\comments\models.py:17 -#: .\oauth\models.py:12 -msgid "author" -msgstr "author" - -#: .\blog\admin.py:53 -msgid "Publish selected articles" -msgstr "Publish selected articles" - -#: .\blog\admin.py:54 -msgid "Draft selected articles" -msgstr "Draft selected articles" - -#: .\blog\admin.py:55 -msgid "Close article comments" -msgstr "Close article comments" - -#: .\blog\admin.py:56 -msgid "Open article comments" -msgstr "Open article comments" - -#: .\blog\admin.py:89 .\blog\models.py:101 .\blog\models.py:183 -#: .\templates\blog\tags\sidebar.html:40 -msgid "category" -msgstr "category" - -#: .\blog\models.py:20 .\blog\models.py:179 .\templates\share_layout\nav.html:8 -msgid "index" -msgstr "index" - -#: .\blog\models.py:21 -msgid "list" -msgstr "list" - -#: .\blog\models.py:22 -msgid "post" -msgstr "post" - -#: .\blog\models.py:23 -msgid "all" -msgstr "all" - -#: .\blog\models.py:24 -msgid "slide" -msgstr "slide" - -#: .\blog\models.py:30 .\blog\models.py:267 .\blog\models.py:285 -msgid "modify time" -msgstr "modify time" - -#: .\blog\models.py:63 -msgid "Draft" -msgstr "Draft" - -#: .\blog\models.py:64 -msgid "Published" -msgstr "Published" - -#: .\blog\models.py:67 -msgid "Open" -msgstr "Open" - -#: .\blog\models.py:68 -msgid "Close" -msgstr "Close" - -#: .\blog\models.py:71 .\comments\admin.py:47 -msgid "Article" -msgstr "Article" - -#: .\blog\models.py:72 -msgid "Page" -msgstr "Page" - -#: .\blog\models.py:74 .\blog\models.py:280 -msgid "title" -msgstr "title" - -#: .\blog\models.py:75 -msgid "body" -msgstr "body" - -#: .\blog\models.py:77 -msgid "publish time" -msgstr "publish time" - -#: .\blog\models.py:79 -msgid "status" -msgstr "status" - -#: .\blog\models.py:84 -msgid "comment status" -msgstr "comment status" - -#: .\blog\models.py:88 .\oauth\models.py:43 -msgid "type" -msgstr "type" - -#: .\blog\models.py:89 -msgid "views" -msgstr "views" - -#: .\blog\models.py:97 .\blog\models.py:258 .\blog\models.py:282 -msgid "order" -msgstr "order" - -#: .\blog\models.py:98 -msgid "show toc" -msgstr "show toc" - -#: .\blog\models.py:105 .\blog\models.py:249 -msgid "tag" -msgstr "tag" - -#: .\blog\models.py:115 .\comments\models.py:21 -msgid "article" -msgstr "article" - -#: .\blog\models.py:171 -msgid "category name" -msgstr "category name" - -#: .\blog\models.py:174 -msgid "parent category" -msgstr "parent category" - -#: .\blog\models.py:234 -msgid "tag name" -msgstr "tag name" - -#: .\blog\models.py:256 -msgid "link name" -msgstr "link name" - -#: .\blog\models.py:257 .\blog\models.py:271 -msgid "link" -msgstr "link" - -#: .\blog\models.py:260 -msgid "is show" -msgstr "is show" - -#: .\blog\models.py:262 -msgid "show type" -msgstr "show type" - -#: .\blog\models.py:281 -msgid "content" -msgstr "content" - -#: .\blog\models.py:283 .\oauth\models.py:52 -msgid "is enable" -msgstr "is enable" - -#: .\blog\models.py:289 -msgid "sidebar" -msgstr "sidebar" - -#: .\blog\models.py:299 -msgid "site name" -msgstr "site name" - -#: .\blog\models.py:305 -msgid "site description" -msgstr "site description" - -#: .\blog\models.py:311 -msgid "site seo description" -msgstr "site seo description" - -#: .\blog\models.py:313 -msgid "site keywords" -msgstr "site keywords" - -#: .\blog\models.py:318 -msgid "article sub length" -msgstr "article sub length" - -#: .\blog\models.py:319 -msgid "sidebar article count" -msgstr "sidebar article count" - -#: .\blog\models.py:320 -msgid "sidebar comment count" -msgstr "sidebar comment count" - -#: .\blog\models.py:321 -msgid "article comment count" -msgstr "article comment count" - -#: .\blog\models.py:322 -msgid "show adsense" -msgstr "show adsense" - -#: .\blog\models.py:324 -msgid "adsense code" -msgstr "adsense code" - -#: .\blog\models.py:325 -msgid "open site comment" -msgstr "open site comment" - -#: .\blog\models.py:352 -msgid "Website configuration" -msgstr "Website configuration" - -#: .\blog\models.py:360 -msgid "There can only be one configuration" -msgstr "There can only be one configuration" - -#: .\blog\views.py:348 -msgid "" -"Sorry, the page you requested is not found, please click the home page to " -"see other?" -msgstr "" -"Sorry, the page you requested is not found, please click the home page to " -"see other?" - -#: .\blog\views.py:356 -msgid "Sorry, the server is busy, please click the home page to see other?" -msgstr "Sorry, the server is busy, please click the home page to see other?" - -#: .\blog\views.py:369 -msgid "Sorry, you do not have permission to access this page?" -msgstr "Sorry, you do not have permission to access this page?" - -#: .\comments\admin.py:15 -msgid "Disable comments" -msgstr "Disable comments" - -#: .\comments\admin.py:16 -msgid "Enable comments" -msgstr "Enable comments" - -#: .\comments\admin.py:46 -msgid "User" -msgstr "User" - -#: .\comments\models.py:25 -msgid "parent comment" -msgstr "parent comment" - -#: .\comments\models.py:29 -msgid "enable" -msgstr "enable" - -#: .\comments\models.py:34 .\templates\blog\tags\article_info.html:30 -msgid "comment" -msgstr "comment" - -#: .\comments\utils.py:13 -msgid "Thanks for your comment" -msgstr "Thanks for your comment" - -#: .\comments\utils.py:15 -#, python-format -msgid "" -"