From 630744767be3cc78872ff11e93601dbf77f5797a Mon Sep 17 00:00:00 2001 From: YishengDu Date: Sun, 5 Oct 2025 22:50:16 +0800 Subject: [PATCH] Add doc and src folder --- doc/README.md | 158 + doc/zijin-ultraclass | 1 + src/Dockerfile | 15 + src/LICENSE | 20 + src/accounts/__init__.py | 0 src/accounts/admin.py | 60 + src/accounts/apps.py | 5 + src/accounts/forms.py | 117 + src/accounts/migrations/0001_initial.py | 49 + ...s_remove_bloguser_created_time_and_more.py | 46 + src/accounts/migrations/__init__.py | 0 src/accounts/models.py | 35 + src/accounts/templatetags/__init__.py | 0 src/accounts/tests.py | 207 ++ src/accounts/urls.py | 28 + src/accounts/user_login_backend.py | 26 + src/accounts/utils.py | 49 + src/accounts/views.py | 204 ++ src/blog/__init__.py | 0 src/blog/admin.py | 114 + src/blog/apps.py | 5 + src/blog/context_processors.py | 43 + src/blog/documents.py | 213 ++ src/blog/forms.py | 19 + src/blog/management/__init__.py | 0 src/blog/management/commands/__init__.py | 0 src/blog/management/commands/build_index.py | 18 + .../management/commands/build_search_words.py | 13 + src/blog/management/commands/clear_cache.py | 11 + .../management/commands/create_testdata.py | 40 + src/blog/management/commands/ping_baidu.py | 50 + .../management/commands/sync_user_avatar.py | 47 + src/blog/middleware.py | 42 + src/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 + src/blog/migrations/__init__.py | 0 src/blog/models.py | 376 +++ src/blog/search_indexes.py | 13 + src/blog/static/account/css/account.css | 9 + src/blog/static/account/js/account.js | 47 + src/blog/static/assets/css/bootstrap.min.css | 6 + src/blog/static/assets/css/docs.min.css | 11 + .../css/ie10-viewport-bug-workaround.css | 13 + src/blog/static/assets/css/signin.css | 58 + .../static/assets/css/todc-bootstrap.min.css | 6 + src/blog/static/assets/img/checkmark.png | Bin 0 -> 221 bytes .../assets/js/ie-emulation-modes-warning.js | 51 + .../assets/js/ie10-viewport-bug-workaround.js | 23 + src/blog/static/blog/css/ie.css | 273 ++ src/blog/static/blog/css/nprogress.css | 74 + src/blog/static/blog/css/oauth_style.css | 305 ++ src/blog/static/blog/css/style.css | 2706 +++++++++++++++++ src/blog/static/blog/fonts/fonts.css | 378 +++ .../mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2 | Bin 0 -> 14088 bytes .../fonts/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2 | Bin 0 -> 27131 bytes .../mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2 | Bin 0 -> 17304 bytes .../mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2 | Bin 0 -> 31604 bytes .../mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2 | Bin 0 -> 20844 bytes .../mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2 | Bin 0 -> 10957 bytes .../mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2 | Bin 0 -> 4250 bytes .../mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2 | Bin 0 -> 14269 bytes .../fonts/mem5YaGs126MiZpBA-UNirkOUuhp.woff2 | Bin 0 -> 27159 bytes .../mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2 | Bin 0 -> 17452 bytes .../mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2 | Bin 0 -> 31596 bytes .../mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2 | Bin 0 -> 21360 bytes .../mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2 | Bin 0 -> 11354 bytes .../mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2 | Bin 0 -> 4181 bytes .../mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2 | Bin 0 -> 29070 bytes .../mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2 | Bin 0 -> 4095 bytes .../mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2 | Bin 0 -> 10790 bytes .../mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2 | Bin 0 -> 19681 bytes .../fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2 | Bin 0 -> 25060 bytes .../mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2 | Bin 0 -> 13133 bytes .../mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2 | Bin 0 -> 16985 bytes .../fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2 | Bin 0 -> 16994 bytes .../blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2 | Bin 0 -> 26166 bytes .../fonts/mem8YaGs126MiZpBA-UFVp0bbck.woff2 | Bin 0 -> 13949 bytes .../fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 | Bin 0 -> 20653 bytes .../fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2 | Bin 0 -> 30070 bytes .../fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2 | Bin 0 -> 4171 bytes .../fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2 | Bin 0 -> 11091 bytes .../memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2 | Bin 0 -> 10841 bytes .../memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2 | Bin 0 -> 19853 bytes .../memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2 | Bin 0 -> 28801 bytes .../memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2 | Bin 0 -> 4247 bytes .../memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2 | Bin 0 -> 13474 bytes .../memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2 | Bin 0 -> 25055 bytes .../memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2 | Bin 0 -> 17036 bytes .../memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2 | Bin 0 -> 11233 bytes .../memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2 | Bin 0 -> 20235 bytes .../memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2 | Bin 0 -> 29777 bytes .../memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2 | Bin 0 -> 4096 bytes .../memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2 | Bin 0 -> 13423 bytes .../memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2 | Bin 0 -> 25389 bytes .../memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2 | Bin 0 -> 17652 bytes src/blog/static/blog/img/avatar.png | Bin 0 -> 1493 bytes src/blog/static/blog/img/icon-sn.svg | 1 + src/blog/static/blog/js/blog.js | 91 + src/blog/static/blog/js/html5.js | 8 + src/blog/static/blog/js/jquery-3.6.0.min.js | 2 + src/blog/static/blog/js/mathjax-loader.js | 142 + src/blog/static/blog/js/navigation.js | 55 + src/blog/static/blog/js/nprogress.js | 480 +++ src/blog/static/pygments/default.css | 293 ++ src/blog/templatetags/__init__.py | 0 src/blog/templatetags/blog_tags.py | 401 +++ src/blog/tests.py | 232 ++ src/blog/urls.py | 62 + src/blog/views.py | 375 +++ src/codecov.yml | 87 + src/comments/__init__.py | 0 src/comments/admin.py | 49 + src/comments/apps.py | 5 + src/comments/forms.py | 13 + src/comments/migrations/0001_initial.py | 38 + .../0002_alter_comment_is_enable.py | 18 + ...ns_remove_comment_created_time_and_more.py | 60 + src/comments/migrations/__init__.py | 0 src/comments/models.py | 39 + src/comments/templatetags/__init__.py | 0 src/comments/templatetags/comments_tags.py | 30 + src/comments/tests.py | 109 + src/comments/urls.py | 11 + src/comments/utils.py | 38 + src/comments/views.py | 63 + .../docker-compose/docker-compose.es.yml | 48 + src/deploy/docker-compose/docker-compose.yml | 60 + src/deploy/entrypoint.sh | 31 + src/deploy/k8s/configmap.yaml | 119 + src/deploy/k8s/deployment.yaml | 274 ++ src/deploy/k8s/gateway.yaml | 17 + src/deploy/k8s/pv.yaml | 94 + src/deploy/k8s/pvc.yaml | 60 + src/deploy/k8s/service.yaml | 80 + src/deploy/k8s/storageclass.yaml | 10 + src/deploy/nginx.conf | 50 + src/djangoblog/__init__.py | 1 + src/djangoblog/admin_site.py | 64 + src/djangoblog/apps.py | 11 + src/djangoblog/blog_signals.py | 122 + src/djangoblog/elasticsearch_backend.py | 183 ++ src/djangoblog/feeds.py | 40 + src/djangoblog/logentryadmin.py | 91 + src/djangoblog/plugin_manage/base_plugin.py | 41 + .../plugin_manage/hook_constants.py | 7 + src/djangoblog/plugin_manage/hooks.py | 44 + src/djangoblog/plugin_manage/loader.py | 19 + src/djangoblog/settings.py | 360 +++ src/djangoblog/sitemap.py | 59 + src/djangoblog/spider_notify.py | 21 + src/djangoblog/tests.py | 32 + src/djangoblog/urls.py | 64 + src/djangoblog/utils.py | 272 ++ src/djangoblog/whoosh_cn_backend.py | 1044 +++++++ src/djangoblog/wsgi.py | 16 + src/docs/README-en.md | 158 + src/docs/config-en.md | 64 + src/docs/config.md | 58 + src/docs/docker-en.md | 114 + src/docs/docker.md | 114 + src/docs/es.md | 28 + src/docs/imgs/alipay.jpg | Bin 0 -> 17961 bytes src/docs/imgs/pycharm_logo.png | Bin 0 -> 132045 bytes src/docs/imgs/wechat.jpg | Bin 0 -> 24722 bytes src/docs/k8s-en.md | 141 + src/docs/k8s.md | 141 + src/locale/en/LC_MESSAGES/django.mo | Bin 0 -> 11097 bytes src/locale/en/LC_MESSAGES/django.po | 685 +++++ src/locale/zh_Hans/LC_MESSAGES/django.mo | Bin 0 -> 10321 bytes src/locale/zh_Hans/LC_MESSAGES/django.po | 667 ++++ src/locale/zh_Hant/LC_MESSAGES/django.mo | Bin 0 -> 10268 bytes src/locale/zh_Hant/LC_MESSAGES/django.po | 668 ++++ src/manage.py | 22 + src/oauth/__init__.py | 0 src/oauth/admin.py | 54 + src/oauth/apps.py | 5 + src/oauth/forms.py | 12 + src/oauth/migrations/0001_initial.py | 57 + ...ptions_alter_oauthuser_options_and_more.py | 86 + .../0003_alter_oauthuser_nickname.py | 18 + src/oauth/migrations/__init__.py | 0 src/oauth/models.py | 67 + src/oauth/oauthmanager.py | 504 +++ src/oauth/templatetags/__init__.py | 1 + src/oauth/templatetags/oauth_tags.py | 22 + src/oauth/tests.py | 249 ++ src/oauth/urls.py | 25 + src/oauth/views.py | 253 ++ src/owntracks/__init__.py | 0 src/owntracks/admin.py | 7 + src/owntracks/apps.py | 5 + src/owntracks/migrations/0001_initial.py | 31 + ...0002_alter_owntracklog_options_and_more.py | 22 + src/owntracks/migrations/__init__.py | 0 src/owntracks/models.py | 20 + src/owntracks/tests.py | 64 + src/owntracks/urls.py | 12 + src/owntracks/views.py | 127 + src/plugins/__init__.py | 1 + src/plugins/article_copyright/__init__.py | 1 + src/plugins/article_copyright/plugin.py | 37 + src/plugins/external_links/__init__.py | 1 + src/plugins/external_links/plugin.py | 48 + src/plugins/image_lazy_loading/__init__.py | 1 + src/plugins/image_lazy_loading/plugin.py | 182 ++ src/plugins/reading_time/__init__.py | 1 + src/plugins/reading_time/plugin.py | 43 + src/plugins/seo_optimizer/__init__.py | 1 + src/plugins/seo_optimizer/plugin.py | 147 + src/plugins/view_count/__init__.py | 1 + src/plugins/view_count/plugin.py | 18 + src/requirements.txt | Bin 0 -> 1690 bytes src/servermanager/MemcacheStorage.py | 32 + src/servermanager/__init__.py | 0 src/servermanager/admin.py | 19 + src/servermanager/api/__init__.py | 1 + src/servermanager/api/blogapi.py | 27 + src/servermanager/api/commonapi.py | 64 + src/servermanager/apps.py | 5 + src/servermanager/migrations/0001_initial.py | 45 + ...002_alter_emailsendlog_options_and_more.py | 32 + src/servermanager/migrations/__init__.py | 0 src/servermanager/models.py | 33 + src/servermanager/robot.py | 187 ++ src/servermanager/tests.py | 79 + src/servermanager/urls.py | 10 + src/servermanager/views.py | 1 + src/templates/account/forget_password.html | 30 + src/templates/account/login.html | 46 + src/templates/account/registration_form.html | 29 + src/templates/account/result.html | 27 + src/templates/blog/article_archives.html | 60 + src/templates/blog/article_detail.html | 52 + src/templates/blog/article_index.html | 42 + src/templates/blog/error_page.html | 45 + src/templates/blog/links_list.html | 44 + src/templates/blog/tags/article_info.html | 74 + .../blog/tags/article_meta_info.html | 59 + .../blog/tags/article_pagination.html | 17 + src/templates/blog/tags/article_tag_list.html | 19 + src/templates/blog/tags/breadcrumb.html | 19 + src/templates/blog/tags/sidebar.html | 136 + src/templates/comments/tags/comment_item.html | 34 + .../comments/tags/comment_item_tree.html | 54 + src/templates/comments/tags/comment_list.html | 45 + src/templates/comments/tags/post_comment.html | 33 + src/templates/oauth/bindsuccess.html | 22 + src/templates/oauth/oauth_applications.html | 13 + src/templates/oauth/require_email.html | 46 + src/templates/owntracks/show_log_dates.html | 17 + src/templates/owntracks/show_maps.html | 135 + .../search/indexes/blog/article_text.txt | 3 + src/templates/search/search.html | 66 + src/templates/share_layout/adsense.html | 6 + src/templates/share_layout/base.html | 113 + src/templates/share_layout/base_account.html | 47 + src/templates/share_layout/footer.html | 56 + src/templates/share_layout/nav.html | 30 + src/templates/share_layout/nav_node.html | 19 + 263 files changed, 19497 insertions(+) create mode 100644 doc/README.md create mode 160000 doc/zijin-ultraclass create mode 100644 src/Dockerfile create mode 100644 src/LICENSE create mode 100644 src/accounts/__init__.py create mode 100644 src/accounts/admin.py create mode 100644 src/accounts/apps.py create mode 100644 src/accounts/forms.py create mode 100644 src/accounts/migrations/0001_initial.py create mode 100644 src/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py create mode 100644 src/accounts/migrations/__init__.py create mode 100644 src/accounts/models.py create mode 100644 src/accounts/templatetags/__init__.py create mode 100644 src/accounts/tests.py create mode 100644 src/accounts/urls.py create mode 100644 src/accounts/user_login_backend.py create mode 100644 src/accounts/utils.py create mode 100644 src/accounts/views.py create mode 100644 src/blog/__init__.py create mode 100644 src/blog/admin.py create mode 100644 src/blog/apps.py create mode 100644 src/blog/context_processors.py create mode 100644 src/blog/documents.py create mode 100644 src/blog/forms.py create mode 100644 src/blog/management/__init__.py create mode 100644 src/blog/management/commands/__init__.py create mode 100644 src/blog/management/commands/build_index.py create mode 100644 src/blog/management/commands/build_search_words.py create mode 100644 src/blog/management/commands/clear_cache.py create mode 100644 src/blog/management/commands/create_testdata.py create mode 100644 src/blog/management/commands/ping_baidu.py create mode 100644 src/blog/management/commands/sync_user_avatar.py create mode 100644 src/blog/middleware.py create mode 100644 src/blog/migrations/0001_initial.py create mode 100644 src/blog/migrations/0002_blogsettings_global_footer_and_more.py create mode 100644 src/blog/migrations/0003_blogsettings_comment_need_review.py create mode 100644 src/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py create mode 100644 src/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py create mode 100644 src/blog/migrations/0006_alter_blogsettings_options.py create mode 100644 src/blog/migrations/__init__.py create mode 100644 src/blog/models.py create mode 100644 src/blog/search_indexes.py create mode 100644 src/blog/static/account/css/account.css create mode 100644 src/blog/static/account/js/account.js create mode 100644 src/blog/static/assets/css/bootstrap.min.css create mode 100644 src/blog/static/assets/css/docs.min.css create mode 100644 src/blog/static/assets/css/ie10-viewport-bug-workaround.css create mode 100644 src/blog/static/assets/css/signin.css create mode 100644 src/blog/static/assets/css/todc-bootstrap.min.css create mode 100644 src/blog/static/assets/img/checkmark.png create mode 100644 src/blog/static/assets/js/ie-emulation-modes-warning.js create mode 100644 src/blog/static/assets/js/ie10-viewport-bug-workaround.js create mode 100644 src/blog/static/blog/css/ie.css create mode 100644 src/blog/static/blog/css/nprogress.css create mode 100644 src/blog/static/blog/css/oauth_style.css create mode 100644 src/blog/static/blog/css/style.css create mode 100644 src/blog/static/blog/fonts/fonts.css create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUuhp.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2 create mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2 create mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2 create mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2 create mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2 create mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2 create mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2 create mode 100644 src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2 create mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2 create mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2 create mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFVp0bbck.woff2 create mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 create mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2 create mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2 create mode 100644 src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2 create mode 100644 src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2 create mode 100644 src/blog/static/blog/img/avatar.png create mode 100644 src/blog/static/blog/img/icon-sn.svg create mode 100644 src/blog/static/blog/js/blog.js create mode 100644 src/blog/static/blog/js/html5.js create mode 100644 src/blog/static/blog/js/jquery-3.6.0.min.js create mode 100644 src/blog/static/blog/js/mathjax-loader.js create mode 100644 src/blog/static/blog/js/navigation.js create mode 100644 src/blog/static/blog/js/nprogress.js create mode 100755 src/blog/static/pygments/default.css create mode 100644 src/blog/templatetags/__init__.py create mode 100644 src/blog/templatetags/blog_tags.py create mode 100644 src/blog/tests.py create mode 100644 src/blog/urls.py create mode 100644 src/blog/views.py create mode 100644 src/codecov.yml create mode 100644 src/comments/__init__.py create mode 100644 src/comments/admin.py create mode 100644 src/comments/apps.py create mode 100644 src/comments/forms.py create mode 100644 src/comments/migrations/0001_initial.py create mode 100644 src/comments/migrations/0002_alter_comment_is_enable.py create mode 100644 src/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py create mode 100644 src/comments/migrations/__init__.py create mode 100644 src/comments/models.py create mode 100644 src/comments/templatetags/__init__.py create mode 100644 src/comments/templatetags/comments_tags.py create mode 100644 src/comments/tests.py create mode 100644 src/comments/urls.py create mode 100644 src/comments/utils.py create mode 100644 src/comments/views.py create mode 100644 src/deploy/docker-compose/docker-compose.es.yml create mode 100644 src/deploy/docker-compose/docker-compose.yml create mode 100644 src/deploy/entrypoint.sh create mode 100644 src/deploy/k8s/configmap.yaml create mode 100644 src/deploy/k8s/deployment.yaml create mode 100644 src/deploy/k8s/gateway.yaml create mode 100644 src/deploy/k8s/pv.yaml create mode 100644 src/deploy/k8s/pvc.yaml create mode 100644 src/deploy/k8s/service.yaml create mode 100644 src/deploy/k8s/storageclass.yaml create mode 100644 src/deploy/nginx.conf create mode 100644 src/djangoblog/__init__.py create mode 100644 src/djangoblog/admin_site.py create mode 100644 src/djangoblog/apps.py create mode 100644 src/djangoblog/blog_signals.py create mode 100644 src/djangoblog/elasticsearch_backend.py create mode 100644 src/djangoblog/feeds.py create mode 100644 src/djangoblog/logentryadmin.py create mode 100644 src/djangoblog/plugin_manage/base_plugin.py create mode 100644 src/djangoblog/plugin_manage/hook_constants.py create mode 100644 src/djangoblog/plugin_manage/hooks.py create mode 100644 src/djangoblog/plugin_manage/loader.py create mode 100644 src/djangoblog/settings.py create mode 100644 src/djangoblog/sitemap.py create mode 100644 src/djangoblog/spider_notify.py create mode 100644 src/djangoblog/tests.py create mode 100644 src/djangoblog/urls.py create mode 100644 src/djangoblog/utils.py create mode 100644 src/djangoblog/whoosh_cn_backend.py create mode 100644 src/djangoblog/wsgi.py create mode 100644 src/docs/README-en.md create mode 100644 src/docs/config-en.md create mode 100644 src/docs/config.md create mode 100644 src/docs/docker-en.md create mode 100644 src/docs/docker.md create mode 100644 src/docs/es.md create mode 100644 src/docs/imgs/alipay.jpg create mode 100644 src/docs/imgs/pycharm_logo.png create mode 100644 src/docs/imgs/wechat.jpg create mode 100644 src/docs/k8s-en.md create mode 100644 src/docs/k8s.md create mode 100644 src/locale/en/LC_MESSAGES/django.mo create mode 100644 src/locale/en/LC_MESSAGES/django.po create mode 100644 src/locale/zh_Hans/LC_MESSAGES/django.mo create mode 100644 src/locale/zh_Hans/LC_MESSAGES/django.po create mode 100644 src/locale/zh_Hant/LC_MESSAGES/django.mo create mode 100644 src/locale/zh_Hant/LC_MESSAGES/django.po create mode 100755 src/manage.py create mode 100644 src/oauth/__init__.py create mode 100644 src/oauth/admin.py create mode 100644 src/oauth/apps.py create mode 100644 src/oauth/forms.py create mode 100644 src/oauth/migrations/0001_initial.py create mode 100644 src/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py create mode 100644 src/oauth/migrations/0003_alter_oauthuser_nickname.py create mode 100644 src/oauth/migrations/__init__.py create mode 100644 src/oauth/models.py create mode 100644 src/oauth/oauthmanager.py create mode 100644 src/oauth/templatetags/__init__.py create mode 100644 src/oauth/templatetags/oauth_tags.py create mode 100644 src/oauth/tests.py create mode 100644 src/oauth/urls.py create mode 100644 src/oauth/views.py create mode 100644 src/owntracks/__init__.py create mode 100644 src/owntracks/admin.py create mode 100644 src/owntracks/apps.py create mode 100644 src/owntracks/migrations/0001_initial.py create mode 100644 src/owntracks/migrations/0002_alter_owntracklog_options_and_more.py create mode 100644 src/owntracks/migrations/__init__.py create mode 100644 src/owntracks/models.py create mode 100644 src/owntracks/tests.py create mode 100644 src/owntracks/urls.py create mode 100644 src/owntracks/views.py create mode 100644 src/plugins/__init__.py create mode 100644 src/plugins/article_copyright/__init__.py create mode 100644 src/plugins/article_copyright/plugin.py create mode 100644 src/plugins/external_links/__init__.py create mode 100644 src/plugins/external_links/plugin.py create mode 100644 src/plugins/image_lazy_loading/__init__.py create mode 100644 src/plugins/image_lazy_loading/plugin.py create mode 100644 src/plugins/reading_time/__init__.py create mode 100644 src/plugins/reading_time/plugin.py create mode 100644 src/plugins/seo_optimizer/__init__.py create mode 100644 src/plugins/seo_optimizer/plugin.py create mode 100644 src/plugins/view_count/__init__.py create mode 100644 src/plugins/view_count/plugin.py create mode 100644 src/requirements.txt create mode 100644 src/servermanager/MemcacheStorage.py create mode 100644 src/servermanager/__init__.py create mode 100644 src/servermanager/admin.py create mode 100644 src/servermanager/api/__init__.py create mode 100644 src/servermanager/api/blogapi.py create mode 100644 src/servermanager/api/commonapi.py create mode 100644 src/servermanager/apps.py create mode 100644 src/servermanager/migrations/0001_initial.py create mode 100644 src/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py create mode 100644 src/servermanager/migrations/__init__.py create mode 100644 src/servermanager/models.py create mode 100644 src/servermanager/robot.py create mode 100644 src/servermanager/tests.py create mode 100644 src/servermanager/urls.py create mode 100644 src/servermanager/views.py create mode 100644 src/templates/account/forget_password.html create mode 100644 src/templates/account/login.html create mode 100644 src/templates/account/registration_form.html create mode 100644 src/templates/account/result.html create mode 100644 src/templates/blog/article_archives.html create mode 100755 src/templates/blog/article_detail.html create mode 100644 src/templates/blog/article_index.html create mode 100644 src/templates/blog/error_page.html create mode 100644 src/templates/blog/links_list.html create mode 100644 src/templates/blog/tags/article_info.html create mode 100644 src/templates/blog/tags/article_meta_info.html create mode 100644 src/templates/blog/tags/article_pagination.html create mode 100644 src/templates/blog/tags/article_tag_list.html create mode 100644 src/templates/blog/tags/breadcrumb.html create mode 100755 src/templates/blog/tags/sidebar.html create mode 100644 src/templates/comments/tags/comment_item.html create mode 100644 src/templates/comments/tags/comment_item_tree.html create mode 100644 src/templates/comments/tags/comment_list.html create mode 100644 src/templates/comments/tags/post_comment.html create mode 100644 src/templates/oauth/bindsuccess.html create mode 100644 src/templates/oauth/oauth_applications.html create mode 100644 src/templates/oauth/require_email.html create mode 100644 src/templates/owntracks/show_log_dates.html create mode 100644 src/templates/owntracks/show_maps.html create mode 100644 src/templates/search/indexes/blog/article_text.txt create mode 100644 src/templates/search/search.html create mode 100644 src/templates/share_layout/adsense.html create mode 100644 src/templates/share_layout/base.html create mode 100644 src/templates/share_layout/base_account.html create mode 100644 src/templates/share_layout/footer.html create mode 100644 src/templates/share_layout/nav.html create mode 100644 src/templates/share_layout/nav_node.html diff --git a/doc/README.md b/doc/README.md new file mode 100644 index 0000000..56aa4cc --- /dev/null +++ b/doc/README.md @@ -0,0 +1,158 @@ +# 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/doc/zijin-ultraclass b/doc/zijin-ultraclass new file mode 160000 index 0000000..6ceca84 --- /dev/null +++ b/doc/zijin-ultraclass @@ -0,0 +1 @@ +Subproject commit 6ceca848326e41c50cd3245846901c4aaafd0fea diff --git a/src/Dockerfile b/src/Dockerfile new file mode 100644 index 0000000..80b46ac --- /dev/null +++ b/src/Dockerfile @@ -0,0 +1,15 @@ +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/src/LICENSE b/src/LICENSE new file mode 100644 index 0000000..3b08474 --- /dev/null +++ b/src/LICENSE @@ -0,0 +1,20 @@ +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/src/accounts/__init__.py b/src/accounts/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/accounts/admin.py b/src/accounts/admin.py new file mode 100644 index 0000000..29d162a --- /dev/null +++ b/src/accounts/admin.py @@ -0,0 +1,60 @@ +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',) + search_fields = ('username', 'nickname', 'email') diff --git a/src/accounts/apps.py b/src/accounts/apps.py new file mode 100644 index 0000000..9b3fc5a --- /dev/null +++ b/src/accounts/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class AccountsConfig(AppConfig): + name = 'accounts' diff --git a/src/accounts/forms.py b/src/accounts/forms.py new file mode 100644 index 0000000..fce4137 --- /dev/null +++ b/src/accounts/forms.py @@ -0,0 +1,117 @@ +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/src/accounts/migrations/0001_initial.py b/src/accounts/migrations/0001_initial.py new file mode 100644 index 0000000..d2fbcab --- /dev/null +++ b/src/accounts/migrations/0001_initial.py @@ -0,0 +1,49 @@ +# 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/src/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py b/src/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py new file mode 100644 index 0000000..1a9f509 --- /dev/null +++ b/src/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py @@ -0,0 +1,46 @@ +# 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/src/accounts/migrations/__init__.py b/src/accounts/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/accounts/models.py b/src/accounts/models.py new file mode 100644 index 0000000..3baddbb --- /dev/null +++ b/src/accounts/models.py @@ -0,0 +1,35 @@ +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/src/accounts/templatetags/__init__.py b/src/accounts/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/accounts/tests.py b/src/accounts/tests.py new file mode 100644 index 0000000..6893411 --- /dev/null +++ b/src/accounts/tests.py @@ -0,0 +1,207 @@ +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/src/accounts/urls.py b/src/accounts/urls.py new file mode 100644 index 0000000..107a801 --- /dev/null +++ b/src/accounts/urls.py @@ -0,0 +1,28 @@ +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/src/accounts/user_login_backend.py b/src/accounts/user_login_backend.py new file mode 100644 index 0000000..73cdca1 --- /dev/null +++ b/src/accounts/user_login_backend.py @@ -0,0 +1,26 @@ +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/src/accounts/utils.py b/src/accounts/utils.py new file mode 100644 index 0000000..4b94bdf --- /dev/null +++ b/src/accounts/utils.py @@ -0,0 +1,49 @@ +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/src/accounts/views.py b/src/accounts/views.py new file mode 100644 index 0000000..ae67aec --- /dev/null +++ b/src/accounts/views.py @@ -0,0 +1,204 @@ +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/src/blog/__init__.py b/src/blog/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/blog/admin.py b/src/blog/admin.py new file mode 100644 index 0000000..69d7f8e --- /dev/null +++ b/src/blog/admin.py @@ -0,0 +1,114 @@ +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, Category, Tag, Links, SideBar, BlogSettings + + +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') + date_hierarchy = 'creation_time' + 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] + raw_id_fields = ('author', 'category',) + + 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/src/blog/apps.py b/src/blog/apps.py new file mode 100644 index 0000000..7930587 --- /dev/null +++ b/src/blog/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class BlogConfig(AppConfig): + name = 'blog' diff --git a/src/blog/context_processors.py b/src/blog/context_processors.py new file mode 100644 index 0000000..73e3088 --- /dev/null +++ b/src/blog/context_processors.py @@ -0,0 +1,43 @@ +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/src/blog/documents.py b/src/blog/documents.py new file mode 100644 index 0000000..0f1db7b --- /dev/null +++ b/src/blog/documents.py @@ -0,0 +1,213 @@ +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/src/blog/forms.py b/src/blog/forms.py new file mode 100644 index 0000000..715be76 --- /dev/null +++ b/src/blog/forms.py @@ -0,0 +1,19 @@ +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/src/blog/management/__init__.py b/src/blog/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/blog/management/commands/__init__.py b/src/blog/management/commands/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/blog/management/commands/build_index.py b/src/blog/management/commands/build_index.py new file mode 100644 index 0000000..3c4acd7 --- /dev/null +++ b/src/blog/management/commands/build_index.py @@ -0,0 +1,18 @@ +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/src/blog/management/commands/build_search_words.py b/src/blog/management/commands/build_search_words.py new file mode 100644 index 0000000..cfe7e0d --- /dev/null +++ b/src/blog/management/commands/build_search_words.py @@ -0,0 +1,13 @@ +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/src/blog/management/commands/clear_cache.py b/src/blog/management/commands/clear_cache.py new file mode 100644 index 0000000..0d66172 --- /dev/null +++ b/src/blog/management/commands/clear_cache.py @@ -0,0 +1,11 @@ +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/src/blog/management/commands/create_testdata.py b/src/blog/management/commands/create_testdata.py new file mode 100644 index 0000000..675d2ba --- /dev/null +++ b/src/blog/management/commands/create_testdata.py @@ -0,0 +1,40 @@ +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/src/blog/management/commands/ping_baidu.py b/src/blog/management/commands/ping_baidu.py new file mode 100644 index 0000000..2c7fbdd --- /dev/null +++ b/src/blog/management/commands/ping_baidu.py @@ -0,0 +1,50 @@ +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/src/blog/management/commands/sync_user_avatar.py b/src/blog/management/commands/sync_user_avatar.py new file mode 100644 index 0000000..d0f4612 --- /dev/null +++ b/src/blog/management/commands/sync_user_avatar.py @@ -0,0 +1,47 @@ +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/src/blog/middleware.py b/src/blog/middleware.py new file mode 100644 index 0000000..94dd70c --- /dev/null +++ b/src/blog/middleware.py @@ -0,0 +1,42 @@ +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/src/blog/migrations/0001_initial.py b/src/blog/migrations/0001_initial.py new file mode 100644 index 0000000..3d391b6 --- /dev/null +++ b/src/blog/migrations/0001_initial.py @@ -0,0 +1,137 @@ +# 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/src/blog/migrations/0002_blogsettings_global_footer_and_more.py b/src/blog/migrations/0002_blogsettings_global_footer_and_more.py new file mode 100644 index 0000000..adbaa36 --- /dev/null +++ b/src/blog/migrations/0002_blogsettings_global_footer_and_more.py @@ -0,0 +1,23 @@ +# 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/src/blog/migrations/0003_blogsettings_comment_need_review.py b/src/blog/migrations/0003_blogsettings_comment_need_review.py new file mode 100644 index 0000000..e9f5502 --- /dev/null +++ b/src/blog/migrations/0003_blogsettings_comment_need_review.py @@ -0,0 +1,17 @@ +# 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/src/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py b/src/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py new file mode 100644 index 0000000..ceb1398 --- /dev/null +++ b/src/blog/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py @@ -0,0 +1,27 @@ +# 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/src/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py b/src/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py new file mode 100644 index 0000000..d08e853 --- /dev/null +++ b/src/blog/migrations/0005_alter_article_options_alter_category_options_and_more.py @@ -0,0 +1,300 @@ +# 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/src/blog/migrations/0006_alter_blogsettings_options.py b/src/blog/migrations/0006_alter_blogsettings_options.py new file mode 100644 index 0000000..e36feb4 --- /dev/null +++ b/src/blog/migrations/0006_alter_blogsettings_options.py @@ -0,0 +1,17 @@ +# 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/src/blog/migrations/__init__.py b/src/blog/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/blog/models.py b/src/blog/models.py new file mode 100644 index 0000000..083788b --- /dev/null +++ b/src/blog/models.py @@ -0,0 +1,376 @@ +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/src/blog/search_indexes.py b/src/blog/search_indexes.py new file mode 100644 index 0000000..7f1dfac --- /dev/null +++ b/src/blog/search_indexes.py @@ -0,0 +1,13 @@ +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/src/blog/static/account/css/account.css b/src/blog/static/account/css/account.css new file mode 100644 index 0000000..7d4cec7 --- /dev/null +++ b/src/blog/static/account/css/account.css @@ -0,0 +1,9 @@ +.button { + border: none; + padding: 4px 80px; + text-align: center; + text-decoration: none; + display: inline-block; + font-size: 16px; + margin: 4px 2px; +} \ No newline at end of file diff --git a/src/blog/static/account/js/account.js b/src/blog/static/account/js/account.js new file mode 100644 index 0000000..f1a8771 --- /dev/null +++ b/src/blog/static/account/js/account.js @@ -0,0 +1,47 @@ +let wait = 60; + +function time(o) { + if (wait == 0) { + o.removeAttribute("disabled"); + o.value = "获取验证码"; + wait = 60 + return false + } else { + o.setAttribute("disabled", true); + o.value = "重新发送(" + wait + ")"; + wait--; + setTimeout(function () { + time(o) + }, + 1000) + } +} + +document.getElementById("btn").onclick = function () { + let id_email = $("#id_email") + let token = $("*[name='csrfmiddlewaretoken']").val() + let ts = this + let myErr = $("#myErr") + $.ajax( + { + url: "/forget_password_code/", + type: "POST", + data: { + "email": id_email.val(), + "csrfmiddlewaretoken": token + }, + success: function (result) { + if (result != "ok") { + myErr.remove() + id_email.after("") + return + } + myErr.remove() + time(ts) + }, + error: function (e) { + alert("发送失败,请重试") + } + } + ); +} diff --git a/src/blog/static/assets/css/bootstrap.min.css b/src/blog/static/assets/css/bootstrap.min.css new file mode 100644 index 0000000..ed3905e --- /dev/null +++ b/src/blog/static/assets/css/bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * Bootstrap v3.3.7 (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + *//*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */html{font-family:sans-serif;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{margin:0}article,aside,details,figcaption,figure,footer,header,hgroup,main,menu,nav,section,summary{display:block}audio,canvas,progress,video{display:inline-block;vertical-align:baseline}audio:not([controls]){display:none;height:0}[hidden],template{display:none}a{background-color:transparent}a:active,a:hover{outline:0}abbr[title]{border-bottom:1px dotted}b,strong{font-weight:700}dfn{font-style:italic}h1{margin:.67em 0;font-size:2em}mark{color:#000;background:#ff0}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sup{top:-.5em}sub{bottom:-.25em}img{border:0}svg:not(:root){overflow:hidden}figure{margin:1em 40px}hr{height:0;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box}pre{overflow:auto}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}button,input,optgroup,select,textarea{margin:0;font:inherit;color:inherit}button{overflow:visible}button,select{text-transform:none}button,html input[type=button],input[type=reset],input[type=submit]{-webkit-appearance:button;cursor:pointer}button[disabled],html input[disabled]{cursor:default}button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}input{line-height:normal}input[type=checkbox],input[type=radio]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:0}input[type=number]::-webkit-inner-spin-button,input[type=number]::-webkit-outer-spin-button{height:auto}input[type=search]{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;-webkit-appearance:textfield}input[type=search]::-webkit-search-cancel-button,input[type=search]::-webkit-search-decoration{-webkit-appearance:none}fieldset{padding:.35em .625em .75em;margin:0 2px;border:1px solid silver}legend{padding:0;border:0}textarea{overflow:auto}optgroup{font-weight:700}table{border-spacing:0;border-collapse:collapse}td,th{padding:0}/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */@media print{*,:after,:before{color:#000!important;text-shadow:none!important;background:0 0!important;-webkit-box-shadow:none!important;box-shadow:none!important}a,a:visited{text-decoration:underline}a[href]:after{content:" (" attr(href) ")"}abbr[title]:after{content:" (" attr(title) ")"}a[href^="javascript:"]:after,a[href^="#"]:after{content:""}blockquote,pre{border:1px solid #999;page-break-inside:avoid}thead{display:table-header-group}img,tr{page-break-inside:avoid}img{max-width:100%!important}h2,h3,p{orphans:3;widows:3}h2,h3{page-break-after:avoid}.navbar{display:none}.btn>.caret,.dropup>.btn>.caret{border-top-color:#000!important}.label{border:1px solid #000}.table{border-collapse:collapse!important}.table td,.table th{background-color:#fff!important}.table-bordered td,.table-bordered th{border:1px solid #ddd!important}}@font-face{font-family:'Glyphicons Halflings';src:url(../fonts/glyphicons-halflings-regular.eot);src:url(../fonts/glyphicons-halflings-regular.eot?#iefix) format('embedded-opentype'),url(../fonts/glyphicons-halflings-regular.woff2) format('woff2'),url(../fonts/glyphicons-halflings-regular.woff) format('woff'),url(../fonts/glyphicons-halflings-regular.ttf) format('truetype'),url(../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular) format('svg')}.glyphicon{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.glyphicon-asterisk:before{content:"\002a"}.glyphicon-plus:before{content:"\002b"}.glyphicon-eur:before,.glyphicon-euro:before{content:"\20ac"}.glyphicon-minus:before{content:"\2212"}.glyphicon-cloud:before{content:"\2601"}.glyphicon-envelope:before{content:"\2709"}.glyphicon-pencil:before{content:"\270f"}.glyphicon-glass:before{content:"\e001"}.glyphicon-music:before{content:"\e002"}.glyphicon-search:before{content:"\e003"}.glyphicon-heart:before{content:"\e005"}.glyphicon-star:before{content:"\e006"}.glyphicon-star-empty:before{content:"\e007"}.glyphicon-user:before{content:"\e008"}.glyphicon-film:before{content:"\e009"}.glyphicon-th-large:before{content:"\e010"}.glyphicon-th:before{content:"\e011"}.glyphicon-th-list:before{content:"\e012"}.glyphicon-ok:before{content:"\e013"}.glyphicon-remove:before{content:"\e014"}.glyphicon-zoom-in:before{content:"\e015"}.glyphicon-zoom-out:before{content:"\e016"}.glyphicon-off:before{content:"\e017"}.glyphicon-signal:before{content:"\e018"}.glyphicon-cog:before{content:"\e019"}.glyphicon-trash:before{content:"\e020"}.glyphicon-home:before{content:"\e021"}.glyphicon-file:before{content:"\e022"}.glyphicon-time:before{content:"\e023"}.glyphicon-road:before{content:"\e024"}.glyphicon-download-alt:before{content:"\e025"}.glyphicon-download:before{content:"\e026"}.glyphicon-upload:before{content:"\e027"}.glyphicon-inbox:before{content:"\e028"}.glyphicon-play-circle:before{content:"\e029"}.glyphicon-repeat:before{content:"\e030"}.glyphicon-refresh:before{content:"\e031"}.glyphicon-list-alt:before{content:"\e032"}.glyphicon-lock:before{content:"\e033"}.glyphicon-flag:before{content:"\e034"}.glyphicon-headphones:before{content:"\e035"}.glyphicon-volume-off:before{content:"\e036"}.glyphicon-volume-down:before{content:"\e037"}.glyphicon-volume-up:before{content:"\e038"}.glyphicon-qrcode:before{content:"\e039"}.glyphicon-barcode:before{content:"\e040"}.glyphicon-tag:before{content:"\e041"}.glyphicon-tags:before{content:"\e042"}.glyphicon-book:before{content:"\e043"}.glyphicon-bookmark:before{content:"\e044"}.glyphicon-print:before{content:"\e045"}.glyphicon-camera:before{content:"\e046"}.glyphicon-font:before{content:"\e047"}.glyphicon-bold:before{content:"\e048"}.glyphicon-italic:before{content:"\e049"}.glyphicon-text-height:before{content:"\e050"}.glyphicon-text-width:before{content:"\e051"}.glyphicon-align-left:before{content:"\e052"}.glyphicon-align-center:before{content:"\e053"}.glyphicon-align-right:before{content:"\e054"}.glyphicon-align-justify:before{content:"\e055"}.glyphicon-list:before{content:"\e056"}.glyphicon-indent-left:before{content:"\e057"}.glyphicon-indent-right:before{content:"\e058"}.glyphicon-facetime-video:before{content:"\e059"}.glyphicon-picture:before{content:"\e060"}.glyphicon-map-marker:before{content:"\e062"}.glyphicon-adjust:before{content:"\e063"}.glyphicon-tint:before{content:"\e064"}.glyphicon-edit:before{content:"\e065"}.glyphicon-share:before{content:"\e066"}.glyphicon-check:before{content:"\e067"}.glyphicon-move:before{content:"\e068"}.glyphicon-step-backward:before{content:"\e069"}.glyphicon-fast-backward:before{content:"\e070"}.glyphicon-backward:before{content:"\e071"}.glyphicon-play:before{content:"\e072"}.glyphicon-pause:before{content:"\e073"}.glyphicon-stop:before{content:"\e074"}.glyphicon-forward:before{content:"\e075"}.glyphicon-fast-forward:before{content:"\e076"}.glyphicon-step-forward:before{content:"\e077"}.glyphicon-eject:before{content:"\e078"}.glyphicon-chevron-left:before{content:"\e079"}.glyphicon-chevron-right:before{content:"\e080"}.glyphicon-plus-sign:before{content:"\e081"}.glyphicon-minus-sign:before{content:"\e082"}.glyphicon-remove-sign:before{content:"\e083"}.glyphicon-ok-sign:before{content:"\e084"}.glyphicon-question-sign:before{content:"\e085"}.glyphicon-info-sign:before{content:"\e086"}.glyphicon-screenshot:before{content:"\e087"}.glyphicon-remove-circle:before{content:"\e088"}.glyphicon-ok-circle:before{content:"\e089"}.glyphicon-ban-circle:before{content:"\e090"}.glyphicon-arrow-left:before{content:"\e091"}.glyphicon-arrow-right:before{content:"\e092"}.glyphicon-arrow-up:before{content:"\e093"}.glyphicon-arrow-down:before{content:"\e094"}.glyphicon-share-alt:before{content:"\e095"}.glyphicon-resize-full:before{content:"\e096"}.glyphicon-resize-small:before{content:"\e097"}.glyphicon-exclamation-sign:before{content:"\e101"}.glyphicon-gift:before{content:"\e102"}.glyphicon-leaf:before{content:"\e103"}.glyphicon-fire:before{content:"\e104"}.glyphicon-eye-open:before{content:"\e105"}.glyphicon-eye-close:before{content:"\e106"}.glyphicon-warning-sign:before{content:"\e107"}.glyphicon-plane:before{content:"\e108"}.glyphicon-calendar:before{content:"\e109"}.glyphicon-random:before{content:"\e110"}.glyphicon-comment:before{content:"\e111"}.glyphicon-magnet:before{content:"\e112"}.glyphicon-chevron-up:before{content:"\e113"}.glyphicon-chevron-down:before{content:"\e114"}.glyphicon-retweet:before{content:"\e115"}.glyphicon-shopping-cart:before{content:"\e116"}.glyphicon-folder-close:before{content:"\e117"}.glyphicon-folder-open:before{content:"\e118"}.glyphicon-resize-vertical:before{content:"\e119"}.glyphicon-resize-horizontal:before{content:"\e120"}.glyphicon-hdd:before{content:"\e121"}.glyphicon-bullhorn:before{content:"\e122"}.glyphicon-bell:before{content:"\e123"}.glyphicon-certificate:before{content:"\e124"}.glyphicon-thumbs-up:before{content:"\e125"}.glyphicon-thumbs-down:before{content:"\e126"}.glyphicon-hand-right:before{content:"\e127"}.glyphicon-hand-left:before{content:"\e128"}.glyphicon-hand-up:before{content:"\e129"}.glyphicon-hand-down:before{content:"\e130"}.glyphicon-circle-arrow-right:before{content:"\e131"}.glyphicon-circle-arrow-left:before{content:"\e132"}.glyphicon-circle-arrow-up:before{content:"\e133"}.glyphicon-circle-arrow-down:before{content:"\e134"}.glyphicon-globe:before{content:"\e135"}.glyphicon-wrench:before{content:"\e136"}.glyphicon-tasks:before{content:"\e137"}.glyphicon-filter:before{content:"\e138"}.glyphicon-briefcase:before{content:"\e139"}.glyphicon-fullscreen:before{content:"\e140"}.glyphicon-dashboard:before{content:"\e141"}.glyphicon-paperclip:before{content:"\e142"}.glyphicon-heart-empty:before{content:"\e143"}.glyphicon-link:before{content:"\e144"}.glyphicon-phone:before{content:"\e145"}.glyphicon-pushpin:before{content:"\e146"}.glyphicon-usd:before{content:"\e148"}.glyphicon-gbp:before{content:"\e149"}.glyphicon-sort:before{content:"\e150"}.glyphicon-sort-by-alphabet:before{content:"\e151"}.glyphicon-sort-by-alphabet-alt:before{content:"\e152"}.glyphicon-sort-by-order:before{content:"\e153"}.glyphicon-sort-by-order-alt:before{content:"\e154"}.glyphicon-sort-by-attributes:before{content:"\e155"}.glyphicon-sort-by-attributes-alt:before{content:"\e156"}.glyphicon-unchecked:before{content:"\e157"}.glyphicon-expand:before{content:"\e158"}.glyphicon-collapse-down:before{content:"\e159"}.glyphicon-collapse-up:before{content:"\e160"}.glyphicon-log-in:before{content:"\e161"}.glyphicon-flash:before{content:"\e162"}.glyphicon-log-out:before{content:"\e163"}.glyphicon-new-window:before{content:"\e164"}.glyphicon-record:before{content:"\e165"}.glyphicon-save:before{content:"\e166"}.glyphicon-open:before{content:"\e167"}.glyphicon-saved:before{content:"\e168"}.glyphicon-import:before{content:"\e169"}.glyphicon-export:before{content:"\e170"}.glyphicon-send:before{content:"\e171"}.glyphicon-floppy-disk:before{content:"\e172"}.glyphicon-floppy-saved:before{content:"\e173"}.glyphicon-floppy-remove:before{content:"\e174"}.glyphicon-floppy-save:before{content:"\e175"}.glyphicon-floppy-open:before{content:"\e176"}.glyphicon-credit-card:before{content:"\e177"}.glyphicon-transfer:before{content:"\e178"}.glyphicon-cutlery:before{content:"\e179"}.glyphicon-header:before{content:"\e180"}.glyphicon-compressed:before{content:"\e181"}.glyphicon-earphone:before{content:"\e182"}.glyphicon-phone-alt:before{content:"\e183"}.glyphicon-tower:before{content:"\e184"}.glyphicon-stats:before{content:"\e185"}.glyphicon-sd-video:before{content:"\e186"}.glyphicon-hd-video:before{content:"\e187"}.glyphicon-subtitles:before{content:"\e188"}.glyphicon-sound-stereo:before{content:"\e189"}.glyphicon-sound-dolby:before{content:"\e190"}.glyphicon-sound-5-1:before{content:"\e191"}.glyphicon-sound-6-1:before{content:"\e192"}.glyphicon-sound-7-1:before{content:"\e193"}.glyphicon-copyright-mark:before{content:"\e194"}.glyphicon-registration-mark:before{content:"\e195"}.glyphicon-cloud-download:before{content:"\e197"}.glyphicon-cloud-upload:before{content:"\e198"}.glyphicon-tree-conifer:before{content:"\e199"}.glyphicon-tree-deciduous:before{content:"\e200"}.glyphicon-cd:before{content:"\e201"}.glyphicon-save-file:before{content:"\e202"}.glyphicon-open-file:before{content:"\e203"}.glyphicon-level-up:before{content:"\e204"}.glyphicon-copy:before{content:"\e205"}.glyphicon-paste:before{content:"\e206"}.glyphicon-alert:before{content:"\e209"}.glyphicon-equalizer:before{content:"\e210"}.glyphicon-king:before{content:"\e211"}.glyphicon-queen:before{content:"\e212"}.glyphicon-pawn:before{content:"\e213"}.glyphicon-bishop:before{content:"\e214"}.glyphicon-knight:before{content:"\e215"}.glyphicon-baby-formula:before{content:"\e216"}.glyphicon-tent:before{content:"\26fa"}.glyphicon-blackboard:before{content:"\e218"}.glyphicon-bed:before{content:"\e219"}.glyphicon-apple:before{content:"\f8ff"}.glyphicon-erase:before{content:"\e221"}.glyphicon-hourglass:before{content:"\231b"}.glyphicon-lamp:before{content:"\e223"}.glyphicon-duplicate:before{content:"\e224"}.glyphicon-piggy-bank:before{content:"\e225"}.glyphicon-scissors:before{content:"\e226"}.glyphicon-bitcoin:before{content:"\e227"}.glyphicon-btc:before{content:"\e227"}.glyphicon-xbt:before{content:"\e227"}.glyphicon-yen:before{content:"\00a5"}.glyphicon-jpy:before{content:"\00a5"}.glyphicon-ruble:before{content:"\20bd"}.glyphicon-rub:before{content:"\20bd"}.glyphicon-scale:before{content:"\e230"}.glyphicon-ice-lolly:before{content:"\e231"}.glyphicon-ice-lolly-tasted:before{content:"\e232"}.glyphicon-education:before{content:"\e233"}.glyphicon-option-horizontal:before{content:"\e234"}.glyphicon-option-vertical:before{content:"\e235"}.glyphicon-menu-hamburger:before{content:"\e236"}.glyphicon-modal-window:before{content:"\e237"}.glyphicon-oil:before{content:"\e238"}.glyphicon-grain:before{content:"\e239"}.glyphicon-sunglasses:before{content:"\e240"}.glyphicon-text-size:before{content:"\e241"}.glyphicon-text-color:before{content:"\e242"}.glyphicon-text-background:before{content:"\e243"}.glyphicon-object-align-top:before{content:"\e244"}.glyphicon-object-align-bottom:before{content:"\e245"}.glyphicon-object-align-horizontal:before{content:"\e246"}.glyphicon-object-align-left:before{content:"\e247"}.glyphicon-object-align-vertical:before{content:"\e248"}.glyphicon-object-align-right:before{content:"\e249"}.glyphicon-triangle-right:before{content:"\e250"}.glyphicon-triangle-left:before{content:"\e251"}.glyphicon-triangle-bottom:before{content:"\e252"}.glyphicon-triangle-top:before{content:"\e253"}.glyphicon-console:before{content:"\e254"}.glyphicon-superscript:before{content:"\e255"}.glyphicon-subscript:before{content:"\e256"}.glyphicon-menu-left:before{content:"\e257"}.glyphicon-menu-right:before{content:"\e258"}.glyphicon-menu-down:before{content:"\e259"}.glyphicon-menu-up:before{content:"\e260"}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}:after,:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}html{font-size:10px;-webkit-tap-highlight-color:rgba(0,0,0,0)}body{font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;line-height:1.42857143;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#337ab7;text-decoration:none}a:focus,a:hover{color:#23527c;text-decoration:underline}a:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}figure{margin:0}img{vertical-align:middle}.carousel-inner>.item>a>img,.carousel-inner>.item>img,.img-responsive,.thumbnail a>img,.thumbnail>img{display:block;max-width:100%;height:auto}.img-rounded{border-radius:6px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:4px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:all .2s ease-in-out;-o-transition:all .2s ease-in-out;transition:all .2s ease-in-out}.img-circle{border-radius:50%}hr{margin-top:20px;margin-bottom:20px;border:0;border-top:1px solid #eee}.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);border:0}.sr-only-focusable:active,.sr-only-focusable:focus{position:static;width:auto;height:auto;margin:0;overflow:visible;clip:auto}[role=button]{cursor:pointer}.h1,.h2,.h3,.h4,.h5,.h6,h1,h2,h3,h4,h5,h6{font-family:inherit;font-weight:500;line-height:1.1;color:inherit}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-weight:400;line-height:1;color:#777}.h1,.h2,.h3,h1,h2,h3{margin-top:20px;margin-bottom:10px}.h1 .small,.h1 small,.h2 .small,.h2 small,.h3 .small,.h3 small,h1 .small,h1 small,h2 .small,h2 small,h3 .small,h3 small{font-size:65%}.h4,.h5,.h6,h4,h5,h6{margin-top:10px;margin-bottom:10px}.h4 .small,.h4 small,.h5 .small,.h5 small,.h6 .small,.h6 small,h4 .small,h4 small,h5 .small,h5 small,h6 .small,h6 small{font-size:75%}.h1,h1{font-size:36px}.h2,h2{font-size:30px}.h3,h3{font-size:24px}.h4,h4{font-size:18px}.h5,h5{font-size:14px}.h6,h6{font-size:12px}p{margin:0 0 10px}.lead{margin-bottom:20px;font-size:16px;font-weight:300;line-height:1.4}@media (min-width:768px){.lead{font-size:21px}}.small,small{font-size:85%}.mark,mark{padding:.2em;background-color:#fcf8e3}.text-left{text-align:left}.text-right{text-align:right}.text-center{text-align:center}.text-justify{text-align:justify}.text-nowrap{white-space:nowrap}.text-lowercase{text-transform:lowercase}.text-uppercase{text-transform:uppercase}.text-capitalize{text-transform:capitalize}.text-muted{color:#777}.text-primary{color:#337ab7}a.text-primary:focus,a.text-primary:hover{color:#286090}.text-success{color:#3c763d}a.text-success:focus,a.text-success:hover{color:#2b542c}.text-info{color:#31708f}a.text-info:focus,a.text-info:hover{color:#245269}.text-warning{color:#8a6d3b}a.text-warning:focus,a.text-warning:hover{color:#66512c}.text-danger{color:#a94442}a.text-danger:focus,a.text-danger:hover{color:#843534}.bg-primary{color:#fff;background-color:#337ab7}a.bg-primary:focus,a.bg-primary:hover{background-color:#286090}.bg-success{background-color:#dff0d8}a.bg-success:focus,a.bg-success:hover{background-color:#c1e2b3}.bg-info{background-color:#d9edf7}a.bg-info:focus,a.bg-info:hover{background-color:#afd9ee}.bg-warning{background-color:#fcf8e3}a.bg-warning:focus,a.bg-warning:hover{background-color:#f7ecb5}.bg-danger{background-color:#f2dede}a.bg-danger:focus,a.bg-danger:hover{background-color:#e4b9b9}.page-header{padding-bottom:9px;margin:40px 0 20px;border-bottom:1px solid #eee}ol,ul{margin-top:0;margin-bottom:10px}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}.list-unstyled{padding-left:0;list-style:none}.list-inline{padding-left:0;margin-left:-5px;list-style:none}.list-inline>li{display:inline-block;padding-right:5px;padding-left:5px}dl{margin-top:0;margin-bottom:20px}dd,dt{line-height:1.42857143}dt{font-weight:700}dd{margin-left:0}@media (min-width:768px){.dl-horizontal dt{float:left;width:160px;overflow:hidden;clear:left;text-align:right;text-overflow:ellipsis;white-space:nowrap}.dl-horizontal dd{margin-left:180px}}abbr[data-original-title],abbr[title]{cursor:help;border-bottom:1px dotted #777}.initialism{font-size:90%;text-transform:uppercase}blockquote{padding:10px 20px;margin:0 0 20px;font-size:17.5px;border-left:5px solid #eee}blockquote ol:last-child,blockquote p:last-child,blockquote ul:last-child{margin-bottom:0}blockquote .small,blockquote footer,blockquote small{display:block;font-size:80%;line-height:1.42857143;color:#777}blockquote .small:before,blockquote footer:before,blockquote small:before{content:'\2014 \00A0'}.blockquote-reverse,blockquote.pull-right{padding-right:15px;padding-left:0;text-align:right;border-right:5px solid #eee;border-left:0}.blockquote-reverse .small:before,.blockquote-reverse footer:before,.blockquote-reverse small:before,blockquote.pull-right .small:before,blockquote.pull-right footer:before,blockquote.pull-right small:before{content:''}.blockquote-reverse .small:after,.blockquote-reverse footer:after,.blockquote-reverse small:after,blockquote.pull-right .small:after,blockquote.pull-right footer:after,blockquote.pull-right small:after{content:'\00A0 \2014'}address{margin-bottom:20px;font-style:normal;line-height:1.42857143}code,kbd,pre,samp{font-family:Menlo,Monaco,Consolas,"Courier New",monospace}code{padding:2px 4px;font-size:90%;color:#c7254e;background-color:#f9f2f4;border-radius:4px}kbd{padding:2px 4px;font-size:90%;color:#fff;background-color:#333;border-radius:3px;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.25);box-shadow:inset 0 -1px 0 rgba(0,0,0,.25)}kbd kbd{padding:0;font-size:100%;font-weight:700;-webkit-box-shadow:none;box-shadow:none}pre{display:block;padding:9.5px;margin:0 0 10px;font-size:13px;line-height:1.42857143;color:#333;word-break:break-all;word-wrap:break-word;background-color:#f5f5f5;border:1px solid #ccc;border-radius:4px}pre code{padding:0;font-size:inherit;color:inherit;white-space:pre-wrap;background-color:transparent;border-radius:0}.pre-scrollable{max-height:340px;overflow-y:scroll}.container{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}@media (min-width:768px){.container{width:750px}}@media (min-width:992px){.container{width:970px}}@media (min-width:1200px){.container{width:1170px}}.container-fluid{padding-right:15px;padding-left:15px;margin-right:auto;margin-left:auto}.row{margin-right:-15px;margin-left:-15px}.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9,.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9,.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9,.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{position:relative;min-height:1px;padding-right:15px;padding-left:15px}.col-xs-1,.col-xs-10,.col-xs-11,.col-xs-12,.col-xs-2,.col-xs-3,.col-xs-4,.col-xs-5,.col-xs-6,.col-xs-7,.col-xs-8,.col-xs-9{float:left}.col-xs-12{width:100%}.col-xs-11{width:91.66666667%}.col-xs-10{width:83.33333333%}.col-xs-9{width:75%}.col-xs-8{width:66.66666667%}.col-xs-7{width:58.33333333%}.col-xs-6{width:50%}.col-xs-5{width:41.66666667%}.col-xs-4{width:33.33333333%}.col-xs-3{width:25%}.col-xs-2{width:16.66666667%}.col-xs-1{width:8.33333333%}.col-xs-pull-12{right:100%}.col-xs-pull-11{right:91.66666667%}.col-xs-pull-10{right:83.33333333%}.col-xs-pull-9{right:75%}.col-xs-pull-8{right:66.66666667%}.col-xs-pull-7{right:58.33333333%}.col-xs-pull-6{right:50%}.col-xs-pull-5{right:41.66666667%}.col-xs-pull-4{right:33.33333333%}.col-xs-pull-3{right:25%}.col-xs-pull-2{right:16.66666667%}.col-xs-pull-1{right:8.33333333%}.col-xs-pull-0{right:auto}.col-xs-push-12{left:100%}.col-xs-push-11{left:91.66666667%}.col-xs-push-10{left:83.33333333%}.col-xs-push-9{left:75%}.col-xs-push-8{left:66.66666667%}.col-xs-push-7{left:58.33333333%}.col-xs-push-6{left:50%}.col-xs-push-5{left:41.66666667%}.col-xs-push-4{left:33.33333333%}.col-xs-push-3{left:25%}.col-xs-push-2{left:16.66666667%}.col-xs-push-1{left:8.33333333%}.col-xs-push-0{left:auto}.col-xs-offset-12{margin-left:100%}.col-xs-offset-11{margin-left:91.66666667%}.col-xs-offset-10{margin-left:83.33333333%}.col-xs-offset-9{margin-left:75%}.col-xs-offset-8{margin-left:66.66666667%}.col-xs-offset-7{margin-left:58.33333333%}.col-xs-offset-6{margin-left:50%}.col-xs-offset-5{margin-left:41.66666667%}.col-xs-offset-4{margin-left:33.33333333%}.col-xs-offset-3{margin-left:25%}.col-xs-offset-2{margin-left:16.66666667%}.col-xs-offset-1{margin-left:8.33333333%}.col-xs-offset-0{margin-left:0}@media (min-width:768px){.col-sm-1,.col-sm-10,.col-sm-11,.col-sm-12,.col-sm-2,.col-sm-3,.col-sm-4,.col-sm-5,.col-sm-6,.col-sm-7,.col-sm-8,.col-sm-9{float:left}.col-sm-12{width:100%}.col-sm-11{width:91.66666667%}.col-sm-10{width:83.33333333%}.col-sm-9{width:75%}.col-sm-8{width:66.66666667%}.col-sm-7{width:58.33333333%}.col-sm-6{width:50%}.col-sm-5{width:41.66666667%}.col-sm-4{width:33.33333333%}.col-sm-3{width:25%}.col-sm-2{width:16.66666667%}.col-sm-1{width:8.33333333%}.col-sm-pull-12{right:100%}.col-sm-pull-11{right:91.66666667%}.col-sm-pull-10{right:83.33333333%}.col-sm-pull-9{right:75%}.col-sm-pull-8{right:66.66666667%}.col-sm-pull-7{right:58.33333333%}.col-sm-pull-6{right:50%}.col-sm-pull-5{right:41.66666667%}.col-sm-pull-4{right:33.33333333%}.col-sm-pull-3{right:25%}.col-sm-pull-2{right:16.66666667%}.col-sm-pull-1{right:8.33333333%}.col-sm-pull-0{right:auto}.col-sm-push-12{left:100%}.col-sm-push-11{left:91.66666667%}.col-sm-push-10{left:83.33333333%}.col-sm-push-9{left:75%}.col-sm-push-8{left:66.66666667%}.col-sm-push-7{left:58.33333333%}.col-sm-push-6{left:50%}.col-sm-push-5{left:41.66666667%}.col-sm-push-4{left:33.33333333%}.col-sm-push-3{left:25%}.col-sm-push-2{left:16.66666667%}.col-sm-push-1{left:8.33333333%}.col-sm-push-0{left:auto}.col-sm-offset-12{margin-left:100%}.col-sm-offset-11{margin-left:91.66666667%}.col-sm-offset-10{margin-left:83.33333333%}.col-sm-offset-9{margin-left:75%}.col-sm-offset-8{margin-left:66.66666667%}.col-sm-offset-7{margin-left:58.33333333%}.col-sm-offset-6{margin-left:50%}.col-sm-offset-5{margin-left:41.66666667%}.col-sm-offset-4{margin-left:33.33333333%}.col-sm-offset-3{margin-left:25%}.col-sm-offset-2{margin-left:16.66666667%}.col-sm-offset-1{margin-left:8.33333333%}.col-sm-offset-0{margin-left:0}}@media (min-width:992px){.col-md-1,.col-md-10,.col-md-11,.col-md-12,.col-md-2,.col-md-3,.col-md-4,.col-md-5,.col-md-6,.col-md-7,.col-md-8,.col-md-9{float:left}.col-md-12{width:100%}.col-md-11{width:91.66666667%}.col-md-10{width:83.33333333%}.col-md-9{width:75%}.col-md-8{width:66.66666667%}.col-md-7{width:58.33333333%}.col-md-6{width:50%}.col-md-5{width:41.66666667%}.col-md-4{width:33.33333333%}.col-md-3{width:25%}.col-md-2{width:16.66666667%}.col-md-1{width:8.33333333%}.col-md-pull-12{right:100%}.col-md-pull-11{right:91.66666667%}.col-md-pull-10{right:83.33333333%}.col-md-pull-9{right:75%}.col-md-pull-8{right:66.66666667%}.col-md-pull-7{right:58.33333333%}.col-md-pull-6{right:50%}.col-md-pull-5{right:41.66666667%}.col-md-pull-4{right:33.33333333%}.col-md-pull-3{right:25%}.col-md-pull-2{right:16.66666667%}.col-md-pull-1{right:8.33333333%}.col-md-pull-0{right:auto}.col-md-push-12{left:100%}.col-md-push-11{left:91.66666667%}.col-md-push-10{left:83.33333333%}.col-md-push-9{left:75%}.col-md-push-8{left:66.66666667%}.col-md-push-7{left:58.33333333%}.col-md-push-6{left:50%}.col-md-push-5{left:41.66666667%}.col-md-push-4{left:33.33333333%}.col-md-push-3{left:25%}.col-md-push-2{left:16.66666667%}.col-md-push-1{left:8.33333333%}.col-md-push-0{left:auto}.col-md-offset-12{margin-left:100%}.col-md-offset-11{margin-left:91.66666667%}.col-md-offset-10{margin-left:83.33333333%}.col-md-offset-9{margin-left:75%}.col-md-offset-8{margin-left:66.66666667%}.col-md-offset-7{margin-left:58.33333333%}.col-md-offset-6{margin-left:50%}.col-md-offset-5{margin-left:41.66666667%}.col-md-offset-4{margin-left:33.33333333%}.col-md-offset-3{margin-left:25%}.col-md-offset-2{margin-left:16.66666667%}.col-md-offset-1{margin-left:8.33333333%}.col-md-offset-0{margin-left:0}}@media (min-width:1200px){.col-lg-1,.col-lg-10,.col-lg-11,.col-lg-12,.col-lg-2,.col-lg-3,.col-lg-4,.col-lg-5,.col-lg-6,.col-lg-7,.col-lg-8,.col-lg-9{float:left}.col-lg-12{width:100%}.col-lg-11{width:91.66666667%}.col-lg-10{width:83.33333333%}.col-lg-9{width:75%}.col-lg-8{width:66.66666667%}.col-lg-7{width:58.33333333%}.col-lg-6{width:50%}.col-lg-5{width:41.66666667%}.col-lg-4{width:33.33333333%}.col-lg-3{width:25%}.col-lg-2{width:16.66666667%}.col-lg-1{width:8.33333333%}.col-lg-pull-12{right:100%}.col-lg-pull-11{right:91.66666667%}.col-lg-pull-10{right:83.33333333%}.col-lg-pull-9{right:75%}.col-lg-pull-8{right:66.66666667%}.col-lg-pull-7{right:58.33333333%}.col-lg-pull-6{right:50%}.col-lg-pull-5{right:41.66666667%}.col-lg-pull-4{right:33.33333333%}.col-lg-pull-3{right:25%}.col-lg-pull-2{right:16.66666667%}.col-lg-pull-1{right:8.33333333%}.col-lg-pull-0{right:auto}.col-lg-push-12{left:100%}.col-lg-push-11{left:91.66666667%}.col-lg-push-10{left:83.33333333%}.col-lg-push-9{left:75%}.col-lg-push-8{left:66.66666667%}.col-lg-push-7{left:58.33333333%}.col-lg-push-6{left:50%}.col-lg-push-5{left:41.66666667%}.col-lg-push-4{left:33.33333333%}.col-lg-push-3{left:25%}.col-lg-push-2{left:16.66666667%}.col-lg-push-1{left:8.33333333%}.col-lg-push-0{left:auto}.col-lg-offset-12{margin-left:100%}.col-lg-offset-11{margin-left:91.66666667%}.col-lg-offset-10{margin-left:83.33333333%}.col-lg-offset-9{margin-left:75%}.col-lg-offset-8{margin-left:66.66666667%}.col-lg-offset-7{margin-left:58.33333333%}.col-lg-offset-6{margin-left:50%}.col-lg-offset-5{margin-left:41.66666667%}.col-lg-offset-4{margin-left:33.33333333%}.col-lg-offset-3{margin-left:25%}.col-lg-offset-2{margin-left:16.66666667%}.col-lg-offset-1{margin-left:8.33333333%}.col-lg-offset-0{margin-left:0}}table{background-color:transparent}caption{padding-top:8px;padding-bottom:8px;color:#777;text-align:left}th{text-align:left}.table{width:100%;max-width:100%;margin-bottom:20px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{padding:8px;line-height:1.42857143;vertical-align:top;border-top:1px solid #ddd}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table>caption+thead>tr:first-child>td,.table>caption+thead>tr:first-child>th,.table>colgroup+thead>tr:first-child>td,.table>colgroup+thead>tr:first-child>th,.table>thead:first-child>tr:first-child>td,.table>thead:first-child>tr:first-child>th{border-top:0}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-condensed>tbody>tr>td,.table-condensed>tbody>tr>th,.table-condensed>tfoot>tr>td,.table-condensed>tfoot>tr>th,.table-condensed>thead>tr>td,.table-condensed>thead>tr>th{padding:5px}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border-bottom-width:2px}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#f5f5f5}table col[class*=col-]{position:static;display:table-column;float:none}table td[class*=col-],table th[class*=col-]{position:static;display:table-cell;float:none}.table>tbody>tr.active>td,.table>tbody>tr.active>th,.table>tbody>tr>td.active,.table>tbody>tr>th.active,.table>tfoot>tr.active>td,.table>tfoot>tr.active>th,.table>tfoot>tr>td.active,.table>tfoot>tr>th.active,.table>thead>tr.active>td,.table>thead>tr.active>th,.table>thead>tr>td.active,.table>thead>tr>th.active{background-color:#f5f5f5}.table-hover>tbody>tr.active:hover>td,.table-hover>tbody>tr.active:hover>th,.table-hover>tbody>tr:hover>.active,.table-hover>tbody>tr>td.active:hover,.table-hover>tbody>tr>th.active:hover{background-color:#e8e8e8}.table>tbody>tr.success>td,.table>tbody>tr.success>th,.table>tbody>tr>td.success,.table>tbody>tr>th.success,.table>tfoot>tr.success>td,.table>tfoot>tr.success>th,.table>tfoot>tr>td.success,.table>tfoot>tr>th.success,.table>thead>tr.success>td,.table>thead>tr.success>th,.table>thead>tr>td.success,.table>thead>tr>th.success{background-color:#dff0d8}.table-hover>tbody>tr.success:hover>td,.table-hover>tbody>tr.success:hover>th,.table-hover>tbody>tr:hover>.success,.table-hover>tbody>tr>td.success:hover,.table-hover>tbody>tr>th.success:hover{background-color:#d0e9c6}.table>tbody>tr.info>td,.table>tbody>tr.info>th,.table>tbody>tr>td.info,.table>tbody>tr>th.info,.table>tfoot>tr.info>td,.table>tfoot>tr.info>th,.table>tfoot>tr>td.info,.table>tfoot>tr>th.info,.table>thead>tr.info>td,.table>thead>tr.info>th,.table>thead>tr>td.info,.table>thead>tr>th.info{background-color:#d9edf7}.table-hover>tbody>tr.info:hover>td,.table-hover>tbody>tr.info:hover>th,.table-hover>tbody>tr:hover>.info,.table-hover>tbody>tr>td.info:hover,.table-hover>tbody>tr>th.info:hover{background-color:#c4e3f3}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#fcf8e3}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#faf2cc}.table>tbody>tr.danger>td,.table>tbody>tr.danger>th,.table>tbody>tr>td.danger,.table>tbody>tr>th.danger,.table>tfoot>tr.danger>td,.table>tfoot>tr.danger>th,.table>tfoot>tr>td.danger,.table>tfoot>tr>th.danger,.table>thead>tr.danger>td,.table>thead>tr.danger>th,.table>thead>tr>td.danger,.table>thead>tr>th.danger{background-color:#f2dede}.table-hover>tbody>tr.danger:hover>td,.table-hover>tbody>tr.danger:hover>th,.table-hover>tbody>tr:hover>.danger,.table-hover>tbody>tr>td.danger:hover,.table-hover>tbody>tr>th.danger:hover{background-color:#ebcccc}.table-responsive{min-height:.01%;overflow-x:auto}@media screen and (max-width:767px){.table-responsive{width:100%;margin-bottom:15px;overflow-y:hidden;-ms-overflow-style:-ms-autohiding-scrollbar;border:1px solid #ddd}.table-responsive>.table{margin-bottom:0}.table-responsive>.table>tbody>tr>td,.table-responsive>.table>tbody>tr>th,.table-responsive>.table>tfoot>tr>td,.table-responsive>.table>tfoot>tr>th,.table-responsive>.table>thead>tr>td,.table-responsive>.table>thead>tr>th{white-space:nowrap}.table-responsive>.table-bordered{border:0}.table-responsive>.table-bordered>tbody>tr>td:first-child,.table-responsive>.table-bordered>tbody>tr>th:first-child,.table-responsive>.table-bordered>tfoot>tr>td:first-child,.table-responsive>.table-bordered>tfoot>tr>th:first-child,.table-responsive>.table-bordered>thead>tr>td:first-child,.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.table-responsive>.table-bordered>tbody>tr>td:last-child,.table-responsive>.table-bordered>tbody>tr>th:last-child,.table-responsive>.table-bordered>tfoot>tr>td:last-child,.table-responsive>.table-bordered>tfoot>tr>th:last-child,.table-responsive>.table-bordered>thead>tr>td:last-child,.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.table-responsive>.table-bordered>tbody>tr:last-child>td,.table-responsive>.table-bordered>tbody>tr:last-child>th,.table-responsive>.table-bordered>tfoot>tr:last-child>td,.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;padding:0;margin-bottom:20px;font-size:21px;line-height:inherit;color:#333;border:0;border-bottom:1px solid #e5e5e5}label{display:inline-block;max-width:100%;margin-bottom:5px;font-weight:700}input[type=search]{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}input[type=checkbox],input[type=radio]{margin:4px 0 0;margin-top:1px\9;line-height:normal}input[type=file]{display:block}input[type=range]{display:block;width:100%}select[multiple],select[size]{height:auto}input[type=file]:focus,input[type=checkbox]:focus,input[type=radio]:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}output{display:block;padding-top:7px;font-size:14px;line-height:1.42857143;color:#555}.form-control{display:block;width:100%;height:34px;padding:6px 12px;font-size:14px;line-height:1.42857143;color:#555;background-color:#fff;background-image:none;border:1px solid #ccc;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075);-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.form-control:focus{border-color:#66afe9;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.form-control::-moz-placeholder{color:#999;opacity:1}.form-control:-ms-input-placeholder{color:#999}.form-control::-webkit-input-placeholder{color:#999}.form-control::-ms-expand{background-color:transparent;border:0}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#eee;opacity:1}.form-control[disabled],fieldset[disabled] .form-control{cursor:not-allowed}textarea.form-control{height:auto}input[type=search]{-webkit-appearance:none}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:34px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:30px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:46px}}.form-group{margin-bottom:15px}.checkbox,.radio{position:relative;display:block;margin-top:10px;margin-bottom:10px}.checkbox label,.radio label{min-height:20px;padding-left:20px;margin-bottom:0;font-weight:400;cursor:pointer}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio]{position:absolute;margin-top:4px\9;margin-left:-20px}.checkbox+.checkbox,.radio+.radio{margin-top:-5px}.checkbox-inline,.radio-inline{position:relative;display:inline-block;padding-left:20px;margin-bottom:0;font-weight:400;vertical-align:middle;cursor:pointer}.checkbox-inline+.checkbox-inline,.radio-inline+.radio-inline{margin-top:0;margin-left:10px}fieldset[disabled] input[type=checkbox],fieldset[disabled] input[type=radio],input[type=checkbox].disabled,input[type=checkbox][disabled],input[type=radio].disabled,input[type=radio][disabled]{cursor:not-allowed}.checkbox-inline.disabled,.radio-inline.disabled,fieldset[disabled] .checkbox-inline,fieldset[disabled] .radio-inline{cursor:not-allowed}.checkbox.disabled label,.radio.disabled label,fieldset[disabled] .checkbox label,fieldset[disabled] .radio label{cursor:not-allowed}.form-control-static{min-height:34px;padding-top:7px;padding-bottom:7px;margin-bottom:0}.form-control-static.input-lg,.form-control-static.input-sm{padding-right:0;padding-left:0}.input-sm{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-sm{height:30px;line-height:30px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.form-group-sm select.form-control{height:30px;line-height:30px}.form-group-sm select[multiple].form-control,.form-group-sm textarea.form-control{height:auto}.form-group-sm .form-control-static{height:30px;min-height:32px;padding:6px 10px;font-size:12px;line-height:1.5}.input-lg{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-lg{height:46px;line-height:46px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.form-group-lg select.form-control{height:46px;line-height:46px}.form-group-lg select[multiple].form-control,.form-group-lg textarea.form-control{height:auto}.form-group-lg .form-control-static{height:46px;min-height:38px;padding:11px 16px;font-size:18px;line-height:1.3333333}.has-feedback{position:relative}.has-feedback .form-control{padding-right:42.5px}.form-control-feedback{position:absolute;top:0;right:0;z-index:2;display:block;width:34px;height:34px;line-height:34px;text-align:center;pointer-events:none}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:46px;height:46px;line-height:46px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:30px;height:30px;line-height:30px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#8a6d3b}.has-warning .form-control{border-color:#8a6d3b;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#66512c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #c0a16b}.has-warning .input-group-addon{color:#8a6d3b;background-color:#fcf8e3;border-color:#8a6d3b}.has-warning .form-control-feedback{color:#8a6d3b}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#a94442}.has-error .form-control{border-color:#a94442;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#843534;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ce8483}.has-error .input-group-addon{color:#a94442;background-color:#f2dede;border-color:#a94442}.has-error .form-control-feedback{color:#a94442}.has-feedback label~.form-control-feedback{top:25px}.has-feedback label.sr-only~.form-control-feedback{top:0}.help-block{display:block;margin-top:5px;margin-bottom:10px;color:#737373}@media (min-width:768px){.form-inline .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static{display:inline-block}.form-inline .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control{width:100%}.form-inline .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio]{position:relative;margin-left:0}.form-inline .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:7px;margin-top:0;margin-bottom:0}.form-horizontal .checkbox,.form-horizontal .radio{min-height:27px}.form-horizontal .form-group{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.form-horizontal .control-label{padding-top:7px;margin-bottom:0;text-align:right}}.form-horizontal .has-feedback .form-control-feedback{right:15px}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:11px;font-size:18px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:6px;font-size:12px}}.btn{display:inline-block;padding:6px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:4px}.btn.active.focus,.btn.active:focus,.btn.focus,.btn:active.focus,.btn:active:focus,.btn:focus{outline:5px auto -webkit-focus-ring-color;outline-offset:-2px}.btn.focus,.btn:focus,.btn:hover{color:#333;text-decoration:none}.btn.active,.btn:active{background-image:none;outline:0;-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn.disabled,.btn[disabled],fieldset[disabled] .btn{cursor:not-allowed;filter:alpha(opacity=65);-webkit-box-shadow:none;box-shadow:none;opacity:.65}a.btn.disabled,fieldset[disabled] a.btn{pointer-events:none}.btn-default{color:#333;background-color:#fff;border-color:#ccc}.btn-default.focus,.btn-default:focus{color:#333;background-color:#e6e6e6;border-color:#8c8c8c}.btn-default:hover{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e6e6e6;border-color:#adadad}.btn-default.active.focus,.btn-default.active:focus,.btn-default.active:hover,.btn-default:active.focus,.btn-default:active:focus,.btn-default:active:hover,.open>.dropdown-toggle.btn-default.focus,.open>.dropdown-toggle.btn-default:focus,.open>.dropdown-toggle.btn-default:hover{color:#333;background-color:#d4d4d4;border-color:#8c8c8c}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{background-image:none}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#fff;border-color:#ccc}.btn-default .badge{color:#fff;background-color:#333}.btn-primary{color:#fff;background-color:#337ab7;border-color:#2e6da4}.btn-primary.focus,.btn-primary:focus{color:#fff;background-color:#286090;border-color:#122b40}.btn-primary:hover{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#286090;border-color:#204d74}.btn-primary.active.focus,.btn-primary.active:focus,.btn-primary.active:hover,.btn-primary:active.focus,.btn-primary:active:focus,.btn-primary:active:hover,.open>.dropdown-toggle.btn-primary.focus,.open>.dropdown-toggle.btn-primary:focus,.open>.dropdown-toggle.btn-primary:hover{color:#fff;background-color:#204d74;border-color:#122b40}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{background-image:none}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#337ab7;border-color:#2e6da4}.btn-primary .badge{color:#337ab7;background-color:#fff}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success.focus,.btn-success:focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}.btn-success.active.focus,.btn-success.active:focus,.btn-success.active:hover,.btn-success:active.focus,.btn-success:active:focus,.btn-success:active:hover,.open>.dropdown-toggle.btn-success.focus,.open>.dropdown-toggle.btn-success:focus,.open>.dropdown-toggle.btn-success:hover{color:#fff;background-color:#398439;border-color:#255625}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{background-image:none}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#5cb85c;border-color:#4cae4c}.btn-success .badge{color:#5cb85c;background-color:#fff}.btn-info{color:#fff;background-color:#5bc0de;border-color:#46b8da}.btn-info.focus,.btn-info:focus{color:#fff;background-color:#31b0d5;border-color:#1b6d85}.btn-info:hover{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#31b0d5;border-color:#269abc}.btn-info.active.focus,.btn-info.active:focus,.btn-info.active:hover,.btn-info:active.focus,.btn-info:active:focus,.btn-info:active:hover,.open>.dropdown-toggle.btn-info.focus,.open>.dropdown-toggle.btn-info:focus,.open>.dropdown-toggle.btn-info:hover{color:#fff;background-color:#269abc;border-color:#1b6d85}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{background-image:none}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;border-color:#46b8da}.btn-info .badge{color:#5bc0de;background-color:#fff}.btn-warning{color:#fff;background-color:#f0ad4e;border-color:#eea236}.btn-warning.focus,.btn-warning:focus{color:#fff;background-color:#ec971f;border-color:#985f0d}.btn-warning:hover{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#ec971f;border-color:#d58512}.btn-warning.active.focus,.btn-warning.active:focus,.btn-warning.active:hover,.btn-warning:active.focus,.btn-warning:active:focus,.btn-warning:active:hover,.open>.dropdown-toggle.btn-warning.focus,.open>.dropdown-toggle.btn-warning:focus,.open>.dropdown-toggle.btn-warning:hover{color:#fff;background-color:#d58512;border-color:#985f0d}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{background-image:none}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#f0ad4e;border-color:#eea236}.btn-warning .badge{color:#f0ad4e;background-color:#fff}.btn-danger{color:#fff;background-color:#d9534f;border-color:#d43f3a}.btn-danger.focus,.btn-danger:focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger.active.focus,.btn-danger.active:focus,.btn-danger.active:hover,.btn-danger:active.focus,.btn-danger:active:focus,.btn-danger:active:hover,.open>.dropdown-toggle.btn-danger.focus,.open>.dropdown-toggle.btn-danger:focus,.open>.dropdown-toggle.btn-danger:hover{color:#fff;background-color:#ac2925;border-color:#761c19}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{background-image:none}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#d9534f;border-color:#d43f3a}.btn-danger .badge{color:#d9534f;background-color:#fff}.btn-link{font-weight:400;color:#337ab7;border-radius:0}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link:focus,.btn-link:hover{color:#23527c;text-decoration:underline;background-color:transparent}.btn-link[disabled]:focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus,fieldset[disabled] .btn-link:hover{color:#777;text-decoration:none}.btn-group-lg>.btn,.btn-lg{padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}.btn-group-sm>.btn,.btn-sm{padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}.btn-group-xs>.btn,.btn-xs{padding:1px 5px;font-size:12px;line-height:1.5;border-radius:3px}.btn-block{display:block;width:100%}.btn-block+.btn-block{margin-top:5px}input[type=button].btn-block,input[type=reset].btn-block,input[type=submit].btn-block{width:100%}.fade{opacity:0;-webkit-transition:opacity .15s linear;-o-transition:opacity .15s linear;transition:opacity .15s linear}.fade.in{opacity:1}.collapse{display:none}.collapse.in{display:block}tr.collapse.in{display:table-row}tbody.collapse.in{display:table-row-group}.collapsing{position:relative;height:0;overflow:hidden;-webkit-transition-timing-function:ease;-o-transition-timing-function:ease;transition-timing-function:ease;-webkit-transition-duration:.35s;-o-transition-duration:.35s;transition-duration:.35s;-webkit-transition-property:height,visibility;-o-transition-property:height,visibility;transition-property:height,visibility}.caret{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}.dropdown,.dropup{position:relative}.dropdown-toggle:focus{outline:0}.dropdown-menu{position:absolute;top:100%;left:0;z-index:1000;display:none;float:left;min-width:160px;padding:5px 0;margin:2px 0 0;font-size:14px;text-align:left;list-style:none;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.15);border-radius:4px;-webkit-box-shadow:0 6px 12px rgba(0,0,0,.175);box-shadow:0 6px 12px rgba(0,0,0,.175)}.dropdown-menu.pull-right{right:0;left:auto}.dropdown-menu .divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.dropdown-menu>li>a{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.42857143;color:#333;white-space:nowrap}.dropdown-menu>li>a:focus,.dropdown-menu>li>a:hover{color:#262626;text-decoration:none;background-color:#f5f5f5}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#fff;text-decoration:none;background-color:#337ab7;outline:0}.dropdown-menu>.disabled>a,.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{color:#777}.dropdown-menu>.disabled>a:focus,.dropdown-menu>.disabled>a:hover{text-decoration:none;cursor:not-allowed;background-color:transparent;background-image:none;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false)}.open>.dropdown-menu{display:block}.open>a{outline:0}.dropdown-menu-right{right:0;left:auto}.dropdown-menu-left{right:auto;left:0}.dropdown-header{display:block;padding:3px 20px;font-size:12px;line-height:1.42857143;color:#777;white-space:nowrap}.dropdown-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:990}.pull-right>.dropdown-menu{right:0;left:auto}.dropup .caret,.navbar-fixed-bottom .dropdown .caret{content:"";border-top:0;border-bottom:4px dashed;border-bottom:4px solid\9}.dropup .dropdown-menu,.navbar-fixed-bottom .dropdown .dropdown-menu{top:auto;bottom:100%;margin-bottom:2px}@media (min-width:768px){.navbar-right .dropdown-menu{right:0;left:auto}.navbar-right .dropdown-menu-left{right:auto;left:0}}.btn-group,.btn-group-vertical{position:relative;display:inline-block;vertical-align:middle}.btn-group-vertical>.btn,.btn-group>.btn{position:relative;float:left}.btn-group-vertical>.btn.active,.btn-group-vertical>.btn:active,.btn-group-vertical>.btn:focus,.btn-group-vertical>.btn:hover,.btn-group>.btn.active,.btn-group>.btn:active,.btn-group>.btn:focus,.btn-group>.btn:hover{z-index:2}.btn-group .btn+.btn,.btn-group .btn+.btn-group,.btn-group .btn-group+.btn,.btn-group .btn-group+.btn-group{margin-left:-1px}.btn-toolbar{margin-left:-5px}.btn-toolbar .btn,.btn-toolbar .btn-group,.btn-toolbar .input-group{float:left}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:5px}.btn-group>.btn:not(:first-child):not(:last-child):not(.dropdown-toggle){border-radius:0}.btn-group>.btn:first-child{margin-left:0}.btn-group>.btn:first-child:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn:last-child:not(:first-child),.btn-group>.dropdown-toggle:not(:first-child){border-top-left-radius:0;border-bottom-left-radius:0}.btn-group>.btn-group{float:left}.btn-group>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-top-right-radius:0;border-bottom-right-radius:0}.btn-group>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-bottom-left-radius:0}.btn-group .dropdown-toggle:active,.btn-group.open .dropdown-toggle{outline:0}.btn-group>.btn+.dropdown-toggle{padding-right:8px;padding-left:8px}.btn-group>.btn-lg+.dropdown-toggle{padding-right:12px;padding-left:12px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn-group.open .dropdown-toggle.btn-link{-webkit-box-shadow:none;box-shadow:none}.btn .caret{margin-left:0}.btn-lg .caret{border-width:5px 5px 0;border-bottom-width:0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn,.btn-group-vertical>.btn-group,.btn-group-vertical>.btn-group>.btn{display:block;float:none;width:100%;max-width:100%}.btn-group-vertical>.btn-group>.btn{float:none}.btn-group-vertical>.btn+.btn,.btn-group-vertical>.btn+.btn-group,.btn-group-vertical>.btn-group+.btn,.btn-group-vertical>.btn-group+.btn-group{margin-top:-1px;margin-left:0}.btn-group-vertical>.btn:not(:first-child):not(:last-child){border-radius:0}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.btn-group-vertical>.btn-group:not(:first-child):not(:last-child)>.btn{border-radius:0}.btn-group-vertical>.btn-group:first-child:not(:last-child)>.btn:last-child,.btn-group-vertical>.btn-group:first-child:not(:last-child)>.dropdown-toggle{border-bottom-right-radius:0;border-bottom-left-radius:0}.btn-group-vertical>.btn-group:last-child:not(:first-child)>.btn:first-child{border-top-left-radius:0;border-top-right-radius:0}.btn-group-justified{display:table;width:100%;table-layout:fixed;border-collapse:separate}.btn-group-justified>.btn,.btn-group-justified>.btn-group{display:table-cell;float:none;width:1%}.btn-group-justified>.btn-group .btn{width:100%}.btn-group-justified>.btn-group .dropdown-menu{left:auto}[data-toggle=buttons]>.btn input[type=checkbox],[data-toggle=buttons]>.btn input[type=radio],[data-toggle=buttons]>.btn-group>.btn input[type=checkbox],[data-toggle=buttons]>.btn-group>.btn input[type=radio]{position:absolute;clip:rect(0,0,0,0);pointer-events:none}.input-group{position:relative;display:table;border-collapse:separate}.input-group[class*=col-]{float:none;padding-right:0;padding-left:0}.input-group .form-control{position:relative;z-index:2;float:left;width:100%;margin-bottom:0}.input-group .form-control:focus{z-index:3}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:46px;padding:10px 16px;font-size:18px;line-height:1.3333333;border-radius:6px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:46px;line-height:46px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:30px;padding:5px 10px;font-size:12px;line-height:1.5;border-radius:3px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:30px;line-height:30px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{display:table-cell}.input-group .form-control:not(:first-child):not(:last-child),.input-group-addon:not(:first-child):not(:last-child),.input-group-btn:not(:first-child):not(:last-child){border-radius:0}.input-group-addon,.input-group-btn{width:1%;white-space:nowrap;vertical-align:middle}.input-group-addon{padding:6px 12px;font-size:14px;font-weight:400;line-height:1;color:#555;text-align:center;background-color:#eee;border:1px solid #ccc;border-radius:4px}.input-group-addon.input-sm{padding:5px 10px;font-size:12px;border-radius:3px}.input-group-addon.input-lg{padding:10px 16px;font-size:18px;border-radius:6px}.input-group-addon input[type=checkbox],.input-group-addon input[type=radio]{margin-top:0}.input-group .form-control:first-child,.input-group-addon:first-child,.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group>.btn,.input-group-btn:first-child>.dropdown-toggle,.input-group-btn:last-child>.btn-group:not(:last-child)>.btn,.input-group-btn:last-child>.btn:not(:last-child):not(.dropdown-toggle){border-top-right-radius:0;border-bottom-right-radius:0}.input-group-addon:first-child{border-right:0}.input-group .form-control:last-child,.input-group-addon:last-child,.input-group-btn:first-child>.btn-group:not(:first-child)>.btn,.input-group-btn:first-child>.btn:not(:first-child),.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group>.btn,.input-group-btn:last-child>.dropdown-toggle{border-top-left-radius:0;border-bottom-left-radius:0}.input-group-addon:last-child{border-left:0}.input-group-btn{position:relative;font-size:0;white-space:nowrap}.input-group-btn>.btn{position:relative}.input-group-btn>.btn+.btn{margin-left:-1px}.input-group-btn>.btn:active,.input-group-btn>.btn:focus,.input-group-btn>.btn:hover{z-index:2}.input-group-btn:first-child>.btn,.input-group-btn:first-child>.btn-group{margin-right:-1px}.input-group-btn:last-child>.btn,.input-group-btn:last-child>.btn-group{z-index:2;margin-left:-1px}.nav{padding-left:0;margin-bottom:0;list-style:none}.nav>li{position:relative;display:block}.nav>li>a{position:relative;display:block;padding:10px 15px}.nav>li>a:focus,.nav>li>a:hover{text-decoration:none;background-color:#eee}.nav>li.disabled>a{color:#777}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#777;text-decoration:none;cursor:not-allowed;background-color:transparent}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{background-color:#eee;border-color:#337ab7}.nav .nav-divider{height:1px;margin:9px 0;overflow:hidden;background-color:#e5e5e5}.nav>li>a>img{max-width:none}.nav-tabs{border-bottom:1px solid #ddd}.nav-tabs>li{float:left;margin-bottom:-1px}.nav-tabs>li>a{margin-right:2px;line-height:1.42857143;border:1px solid transparent;border-radius:4px 4px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{color:#555;cursor:default;background-color:#fff;border:1px solid #ddd;border-bottom-color:transparent}.nav-tabs.nav-justified{width:100%;border-bottom:0}.nav-tabs.nav-justified>li{float:none}.nav-tabs.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-tabs.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-tabs.nav-justified>li{display:table-cell;width:1%}.nav-tabs.nav-justified>li>a{margin-bottom:0}}.nav-tabs.nav-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs.nav-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs.nav-justified>.active>a,.nav-tabs.nav-justified>.active>a:focus,.nav-tabs.nav-justified>.active>a:hover{border-bottom-color:#fff}}.nav-pills>li{float:left}.nav-pills>li>a{border-radius:4px}.nav-pills>li+li{margin-left:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#337ab7}.nav-stacked>li{float:none}.nav-stacked>li+li{margin-top:2px;margin-left:0}.nav-justified{width:100%}.nav-justified>li{float:none}.nav-justified>li>a{margin-bottom:5px;text-align:center}.nav-justified>.dropdown .dropdown-menu{top:auto;left:auto}@media (min-width:768px){.nav-justified>li{display:table-cell;width:1%}.nav-justified>li>a{margin-bottom:0}}.nav-tabs-justified{border-bottom:0}.nav-tabs-justified>li>a{margin-right:0;border-radius:4px}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border:1px solid #ddd}@media (min-width:768px){.nav-tabs-justified>li>a{border-bottom:1px solid #ddd;border-radius:4px 4px 0 0}.nav-tabs-justified>.active>a,.nav-tabs-justified>.active>a:focus,.nav-tabs-justified>.active>a:hover{border-bottom-color:#fff}}.tab-content>.tab-pane{display:none}.tab-content>.active{display:block}.nav-tabs .dropdown-menu{margin-top:-1px;border-top-left-radius:0;border-top-right-radius:0}.navbar{position:relative;min-height:50px;margin-bottom:20px;border:1px solid transparent}@media (min-width:768px){.navbar{border-radius:4px}}@media (min-width:768px){.navbar-header{float:left}}.navbar-collapse{padding-right:15px;padding-left:15px;overflow-x:visible;-webkit-overflow-scrolling:touch;border-top:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1)}.navbar-collapse.in{overflow-y:auto}@media (min-width:768px){.navbar-collapse{width:auto;border-top:0;-webkit-box-shadow:none;box-shadow:none}.navbar-collapse.collapse{display:block!important;height:auto!important;padding-bottom:0;overflow:visible!important}.navbar-collapse.in{overflow-y:visible}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse,.navbar-static-top .navbar-collapse{padding-right:0;padding-left:0}}.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:340px}@media (max-device-width:480px) and (orientation:landscape){.navbar-fixed-bottom .navbar-collapse,.navbar-fixed-top .navbar-collapse{max-height:200px}}.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:-15px;margin-left:-15px}@media (min-width:768px){.container-fluid>.navbar-collapse,.container-fluid>.navbar-header,.container>.navbar-collapse,.container>.navbar-header{margin-right:0;margin-left:0}}.navbar-static-top{z-index:1000;border-width:0 0 1px}@media (min-width:768px){.navbar-static-top{border-radius:0}}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;z-index:1030}@media (min-width:768px){.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}}.navbar-fixed-top{top:0;border-width:0 0 1px}.navbar-fixed-bottom{bottom:0;margin-bottom:0;border-width:1px 0 0}.navbar-brand{float:left;height:50px;padding:15px 15px;font-size:18px;line-height:20px}.navbar-brand:focus,.navbar-brand:hover{text-decoration:none}.navbar-brand>img{display:block}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{position:relative;float:right;padding:9px 10px;margin-top:8px;margin-right:15px;margin-bottom:8px;background-color:transparent;background-image:none;border:1px solid transparent;border-radius:4px}.navbar-toggle:focus{outline:0}.navbar-toggle .icon-bar{display:block;width:22px;height:2px;border-radius:1px}.navbar-toggle .icon-bar+.icon-bar{margin-top:4px}@media (min-width:768px){.navbar-toggle{display:none}}.navbar-nav{margin:7.5px -15px}.navbar-nav>li>a{padding-top:10px;padding-bottom:10px;line-height:20px}@media (max-width:767px){.navbar-nav .open .dropdown-menu{position:static;float:none;width:auto;margin-top:0;background-color:transparent;border:0;-webkit-box-shadow:none;box-shadow:none}.navbar-nav .open .dropdown-menu .dropdown-header,.navbar-nav .open .dropdown-menu>li>a{padding:5px 15px 5px 25px}.navbar-nav .open .dropdown-menu>li>a{line-height:20px}.navbar-nav .open .dropdown-menu>li>a:focus,.navbar-nav .open .dropdown-menu>li>a:hover{background-image:none}}@media (min-width:768px){.navbar-nav{float:left;margin:0}.navbar-nav>li{float:left}.navbar-nav>li>a{padding-top:15px;padding-bottom:15px}}.navbar-form{padding:10px 15px;margin-top:8px;margin-right:-15px;margin-bottom:8px;margin-left:-15px;border-top:1px solid transparent;border-bottom:1px solid transparent;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 0 rgba(255,255,255,.1),0 1px 0 rgba(255,255,255,.1)}@media (min-width:768px){.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.navbar-form .form-control-static{display:inline-block}.navbar-form .input-group{display:inline-table;vertical-align:middle}.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.navbar-form .input-group>.form-control{width:100%}.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-left:0}.navbar-form .has-feedback .form-control-feedback{top:0}}@media (max-width:767px){.navbar-form .form-group{margin-bottom:5px}.navbar-form .form-group:last-child{margin-bottom:0}}@media (min-width:768px){.navbar-form{width:auto;padding-top:0;padding-bottom:0;margin-right:0;margin-left:0;border:0;-webkit-box-shadow:none;box-shadow:none}}.navbar-nav>li>.dropdown-menu{margin-top:0;border-top-left-radius:0;border-top-right-radius:0}.navbar-fixed-bottom .navbar-nav>li>.dropdown-menu{margin-bottom:0;border-top-left-radius:4px;border-top-right-radius:4px;border-bottom-right-radius:0;border-bottom-left-radius:0}.navbar-btn{margin-top:8px;margin-bottom:8px}.navbar-btn.btn-sm{margin-top:10px;margin-bottom:10px}.navbar-btn.btn-xs{margin-top:14px;margin-bottom:14px}.navbar-text{margin-top:15px;margin-bottom:15px}@media (min-width:768px){.navbar-text{float:left;margin-right:15px;margin-left:15px}}@media (min-width:768px){.navbar-left{float:left!important}.navbar-right{float:right!important;margin-right:-15px}.navbar-right~.navbar-right{margin-right:0}}.navbar-default{background-color:#f8f8f8;border-color:#e7e7e7}.navbar-default .navbar-brand{color:#777}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#5e5e5e;background-color:transparent}.navbar-default .navbar-text{color:#777}.navbar-default .navbar-nav>li>a{color:#777}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-default .navbar-toggle{border-color:#ddd}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#ddd}.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#e7e7e7}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#555;background-color:#e7e7e7}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#555;background-color:#e7e7e7}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-default .navbar-link{color:#777}.navbar-default .navbar-link:hover{color:#333}.navbar-default .btn-link{color:#777}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#333}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#ccc}.navbar-inverse{background-color:#222;border-color:#080808}.navbar-inverse .navbar-brand{color:#9d9d9d}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-text{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#444;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#333}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#101010}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#080808}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#9d9d9d}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#080808}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#444;background-color:transparent}}.navbar-inverse .navbar-link{color:#9d9d9d}.navbar-inverse .navbar-link:hover{color:#fff}.navbar-inverse .btn-link{color:#9d9d9d}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#fff}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#444}.breadcrumb{padding:8px 15px;margin-bottom:20px;list-style:none;background-color:#f5f5f5;border-radius:4px}.breadcrumb>li{display:inline-block}.breadcrumb>li+li:before{padding:0 5px;color:#ccc;content:"/\00a0"}.breadcrumb>.active{color:#777}.pagination{display:inline-block;padding-left:0;margin:20px 0;border-radius:4px}.pagination>li{display:inline}.pagination>li>a,.pagination>li>span{position:relative;float:left;padding:6px 12px;margin-left:-1px;line-height:1.42857143;color:#337ab7;text-decoration:none;background-color:#fff;border:1px solid #ddd}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:4px;border-bottom-left-radius:4px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:4px;border-bottom-right-radius:4px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{z-index:2;color:#23527c;background-color:#eee;border-color:#ddd}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{z-index:3;color:#fff;cursor:default;background-color:#337ab7;border-color:#337ab7}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#777;cursor:not-allowed;background-color:#fff;border-color:#ddd}.pagination-lg>li>a,.pagination-lg>li>span{padding:10px 16px;font-size:18px;line-height:1.3333333}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:6px;border-bottom-left-radius:6px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:6px;border-bottom-right-radius:6px}.pagination-sm>li>a,.pagination-sm>li>span{padding:5px 10px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:3px;border-bottom-left-radius:3px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:3px;border-bottom-right-radius:3px}.pager{padding-left:0;margin:20px 0;text-align:center;list-style:none}.pager li{display:inline}.pager li>a,.pager li>span{display:inline-block;padding:5px 14px;background-color:#fff;border:1px solid #ddd;border-radius:15px}.pager li>a:focus,.pager li>a:hover{text-decoration:none;background-color:#eee}.pager .next>a,.pager .next>span{float:right}.pager .previous>a,.pager .previous>span{float:left}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#777;cursor:not-allowed;background-color:#fff}.label{display:inline;padding:.2em .6em .3em;font-size:75%;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:baseline;border-radius:.25em}a.label:focus,a.label:hover{color:#fff;text-decoration:none;cursor:pointer}.label:empty{display:none}.btn .label{position:relative;top:-1px}.label-default{background-color:#777}.label-default[href]:focus,.label-default[href]:hover{background-color:#5e5e5e}.label-primary{background-color:#337ab7}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#286090}.label-success{background-color:#5cb85c}.label-success[href]:focus,.label-success[href]:hover{background-color:#449d44}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#f0ad4e}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#ec971f}.label-danger{background-color:#d9534f}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#c9302c}.badge{display:inline-block;min-width:10px;padding:3px 7px;font-size:12px;font-weight:700;line-height:1;color:#fff;text-align:center;white-space:nowrap;vertical-align:middle;background-color:#777;border-radius:10px}.badge:empty{display:none}.btn .badge{position:relative;top:-1px}.btn-group-xs>.btn .badge,.btn-xs .badge{top:0;padding:1px 5px}a.badge:focus,a.badge:hover{color:#fff;text-decoration:none;cursor:pointer}.list-group-item.active>.badge,.nav-pills>.active>a>.badge{color:#337ab7;background-color:#fff}.list-group-item>.badge{float:right}.list-group-item>.badge+.badge{margin-right:5px}.nav-pills>li>a>.badge{margin-left:3px}.jumbotron{padding-top:30px;padding-bottom:30px;margin-bottom:30px;color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{margin-bottom:15px;font-size:21px;font-weight:200}.jumbotron>hr{border-top-color:#d5d5d5}.container .jumbotron,.container-fluid .jumbotron{padding-right:15px;padding-left:15px;border-radius:6px}.jumbotron .container{max-width:100%}@media screen and (min-width:768px){.jumbotron{padding-top:48px;padding-bottom:48px}.container .jumbotron,.container-fluid .jumbotron{padding-right:60px;padding-left:60px}.jumbotron .h1,.jumbotron h1{font-size:63px}}.thumbnail{display:block;padding:4px;margin-bottom:20px;line-height:1.42857143;background-color:#fff;border:1px solid #ddd;border-radius:4px;-webkit-transition:border .2s ease-in-out;-o-transition:border .2s ease-in-out;transition:border .2s ease-in-out}.thumbnail a>img,.thumbnail>img{margin-right:auto;margin-left:auto}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#337ab7}.thumbnail .caption{padding:9px;color:#333}.alert{padding:15px;margin-bottom:20px;border:1px solid transparent;border-radius:4px}.alert h4{margin-top:0;color:inherit}.alert .alert-link{font-weight:700}.alert>p,.alert>ul{margin-bottom:0}.alert>p+p{margin-top:5px}.alert-dismissable,.alert-dismissible{padding-right:35px}.alert-dismissable .close,.alert-dismissible .close{position:relative;top:-2px;right:-21px;color:inherit}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.alert-success hr{border-top-color:#c9e2b3}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.alert-info hr{border-top-color:#a6e1ec}.alert-info .alert-link{color:#245269}.alert-warning{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.alert-warning hr{border-top-color:#f7e1b5}.alert-warning .alert-link{color:#66512c}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.alert-danger hr{border-top-color:#e4b9c0}.alert-danger .alert-link{color:#843534}@-webkit-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@-o-keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}@keyframes progress-bar-stripes{from{background-position:40px 0}to{background-position:0 0}}.progress{height:20px;margin-bottom:20px;overflow:hidden;background-color:#f5f5f5;border-radius:4px;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.progress-bar{float:left;width:0;height:100%;font-size:12px;line-height:20px;color:#fff;text-align:center;background-color:#337ab7;-webkit-box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);box-shadow:inset 0 -1px 0 rgba(0,0,0,.15);-webkit-transition:width .6s ease;-o-transition:width .6s ease;transition:width .6s ease}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar.active,.progress.active .progress-bar{-webkit-animation:progress-bar-stripes 2s linear infinite;-o-animation:progress-bar-stripes 2s linear infinite;animation:progress-bar-stripes 2s linear infinite}.progress-bar-success{background-color:#5cb85c}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#5bc0de}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#f0ad4e}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#d9534f}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.media{margin-top:15px}.media:first-child{margin-top:0}.media,.media-body{overflow:hidden;zoom:1}.media-body{width:10000px}.media-object{display:block}.media-object.img-thumbnail{max-width:none}.media-right,.media>.pull-right{padding-left:10px}.media-left,.media>.pull-left{padding-right:10px}.media-body,.media-left,.media-right{display:table-cell;vertical-align:top}.media-middle{vertical-align:middle}.media-bottom{vertical-align:bottom}.media-heading{margin-top:0;margin-bottom:5px}.media-list{padding-left:0;list-style:none}.list-group{padding-left:0;margin-bottom:20px}.list-group-item{position:relative;display:block;padding:10px 15px;margin-bottom:-1px;background-color:#fff;border:1px solid #ddd}.list-group-item:first-child{border-top-left-radius:4px;border-top-right-radius:4px}.list-group-item:last-child{margin-bottom:0;border-bottom-right-radius:4px;border-bottom-left-radius:4px}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}a.list-group-item:focus,a.list-group-item:hover,button.list-group-item:focus,button.list-group-item:hover{color:#555;text-decoration:none;background-color:#f5f5f5}button.list-group-item{width:100%;text-align:left}.list-group-item.disabled,.list-group-item.disabled:focus,.list-group-item.disabled:hover{color:#777;cursor:not-allowed;background-color:#eee}.list-group-item.disabled .list-group-item-heading,.list-group-item.disabled:focus .list-group-item-heading,.list-group-item.disabled:hover .list-group-item-heading{color:inherit}.list-group-item.disabled .list-group-item-text,.list-group-item.disabled:focus .list-group-item-text,.list-group-item.disabled:hover .list-group-item-text{color:#777}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{z-index:2;color:#fff;background-color:#337ab7;border-color:#337ab7}.list-group-item.active .list-group-item-heading,.list-group-item.active .list-group-item-heading>.small,.list-group-item.active .list-group-item-heading>small,.list-group-item.active:focus .list-group-item-heading,.list-group-item.active:focus .list-group-item-heading>.small,.list-group-item.active:focus .list-group-item-heading>small,.list-group-item.active:hover .list-group-item-heading,.list-group-item.active:hover .list-group-item-heading>.small,.list-group-item.active:hover .list-group-item-heading>small{color:inherit}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{color:#c7ddef}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#8a6d3b;background-color:#fcf8e3}a.list-group-item-warning,button.list-group-item-warning{color:#8a6d3b}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#8a6d3b;background-color:#faf2cc}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#8a6d3b;border-color:#8a6d3b}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-heading{margin-top:0;margin-bottom:5px}.list-group-item-text{margin-bottom:0;line-height:1.3}.panel{margin-bottom:20px;background-color:#fff;border:1px solid transparent;border-radius:4px;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.05);box-shadow:0 1px 1px rgba(0,0,0,.05)}.panel-body{padding:15px}.panel-heading{padding:10px 15px;border-bottom:1px solid transparent;border-top-left-radius:3px;border-top-right-radius:3px}.panel-heading>.dropdown .dropdown-toggle{color:inherit}.panel-title{margin-top:0;margin-bottom:0;font-size:16px;color:inherit}.panel-title>.small,.panel-title>.small>a,.panel-title>a,.panel-title>small,.panel-title>small>a{color:inherit}.panel-footer{padding:10px 15px;background-color:#f5f5f5;border-top:1px solid #ddd;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group,.panel>.panel-collapse>.list-group{margin-bottom:0}.panel>.list-group .list-group-item,.panel>.panel-collapse>.list-group .list-group-item{border-width:1px 0;border-radius:0}.panel>.list-group:first-child .list-group-item:first-child,.panel>.panel-collapse>.list-group:first-child .list-group-item:first-child{border-top:0;border-top-left-radius:3px;border-top-right-radius:3px}.panel>.list-group:last-child .list-group-item:last-child,.panel>.panel-collapse>.list-group:last-child .list-group-item:last-child{border-bottom:0;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.panel-heading+.panel-collapse>.list-group .list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.panel-heading+.list-group .list-group-item:first-child{border-top-width:0}.list-group+.panel-footer{border-top-width:0}.panel>.panel-collapse>.table,.panel>.table,.panel>.table-responsive>.table{margin-bottom:0}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px;padding-left:15px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:3px;border-top-right-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:3px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:3px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:3px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:3px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel>.table>tbody:first-child>tr:first-child td,.panel>.table>tbody:first-child>tr:first-child th{border-top:0}.panel>.table-bordered,.panel>.table-responsive>.table-bordered{border:0}.panel>.table-bordered>tbody>tr>td:first-child,.panel>.table-bordered>tbody>tr>th:first-child,.panel>.table-bordered>tfoot>tr>td:first-child,.panel>.table-bordered>tfoot>tr>th:first-child,.panel>.table-bordered>thead>tr>td:first-child,.panel>.table-bordered>thead>tr>th:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:first-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:first-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:first-child,.panel>.table-responsive>.table-bordered>thead>tr>td:first-child,.panel>.table-responsive>.table-bordered>thead>tr>th:first-child{border-left:0}.panel>.table-bordered>tbody>tr>td:last-child,.panel>.table-bordered>tbody>tr>th:last-child,.panel>.table-bordered>tfoot>tr>td:last-child,.panel>.table-bordered>tfoot>tr>th:last-child,.panel>.table-bordered>thead>tr>td:last-child,.panel>.table-bordered>thead>tr>th:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>td:last-child,.panel>.table-responsive>.table-bordered>tbody>tr>th:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>td:last-child,.panel>.table-responsive>.table-bordered>tfoot>tr>th:last-child,.panel>.table-responsive>.table-bordered>thead>tr>td:last-child,.panel>.table-responsive>.table-bordered>thead>tr>th:last-child{border-right:0}.panel>.table-bordered>tbody>tr:first-child>td,.panel>.table-bordered>tbody>tr:first-child>th,.panel>.table-bordered>thead>tr:first-child>td,.panel>.table-bordered>thead>tr:first-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:first-child>th,.panel>.table-responsive>.table-bordered>thead>tr:first-child>td,.panel>.table-responsive>.table-bordered>thead>tr:first-child>th{border-bottom:0}.panel>.table-bordered>tbody>tr:last-child>td,.panel>.table-bordered>tbody>tr:last-child>th,.panel>.table-bordered>tfoot>tr:last-child>td,.panel>.table-bordered>tfoot>tr:last-child>th,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>td,.panel>.table-responsive>.table-bordered>tbody>tr:last-child>th,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>td,.panel>.table-responsive>.table-bordered>tfoot>tr:last-child>th{border-bottom:0}.panel>.table-responsive{margin-bottom:0;border:0}.panel-group{margin-bottom:20px}.panel-group .panel{margin-bottom:0;border-radius:4px}.panel-group .panel+.panel{margin-top:5px}.panel-group .panel-heading{border-bottom:0}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:1px solid #ddd}.panel-group .panel-footer{border-top:0}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:1px solid #ddd}.panel-default{border-color:#ddd}.panel-default>.panel-heading{color:#333;background-color:#f5f5f5;border-color:#ddd}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ddd}.panel-default>.panel-heading .badge{color:#f5f5f5;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ddd}.panel-primary{border-color:#337ab7}.panel-primary>.panel-heading{color:#fff;background-color:#337ab7;border-color:#337ab7}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#337ab7}.panel-primary>.panel-heading .badge{color:#337ab7;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#337ab7}.panel-success{border-color:#d6e9c6}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#d6e9c6}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d6e9c6}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d6e9c6}.panel-info{border-color:#bce8f1}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#bce8f1}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#bce8f1}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#bce8f1}.panel-warning{border-color:#faebcc}.panel-warning>.panel-heading{color:#8a6d3b;background-color:#fcf8e3;border-color:#faebcc}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#faebcc}.panel-warning>.panel-heading .badge{color:#fcf8e3;background-color:#8a6d3b}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#faebcc}.panel-danger{border-color:#ebccd1}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#ebccd1}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#ebccd1}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#ebccd1}.embed-responsive{position:relative;display:block;height:0;padding:0;overflow:hidden}.embed-responsive .embed-responsive-item,.embed-responsive embed,.embed-responsive iframe,.embed-responsive object,.embed-responsive video{position:absolute;top:0;bottom:0;left:0;width:100%;height:100%;border:0}.embed-responsive-16by9{padding-bottom:56.25%}.embed-responsive-4by3{padding-bottom:75%}.well{min-height:20px;padding:19px;margin-bottom:20px;background-color:#f5f5f5;border:1px solid #e3e3e3;border-radius:4px;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.05);box-shadow:inset 0 1px 1px rgba(0,0,0,.05)}.well blockquote{border-color:#ddd;border-color:rgba(0,0,0,.15)}.well-lg{padding:24px;border-radius:6px}.well-sm{padding:9px;border-radius:3px}.close{float:right;font-size:21px;font-weight:700;line-height:1;color:#000;text-shadow:0 1px 0 #fff;filter:alpha(opacity=20);opacity:.2}.close:focus,.close:hover{color:#000;text-decoration:none;cursor:pointer;filter:alpha(opacity=50);opacity:.5}button.close{-webkit-appearance:none;padding:0;cursor:pointer;background:0 0;border:0}.modal-open{overflow:hidden}.modal{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1050;display:none;overflow:hidden;-webkit-overflow-scrolling:touch;outline:0}.modal.fade .modal-dialog{-webkit-transition:-webkit-transform .3s ease-out;-o-transition:-o-transform .3s ease-out;transition:transform .3s ease-out;-webkit-transform:translate(0,-25%);-ms-transform:translate(0,-25%);-o-transform:translate(0,-25%);transform:translate(0,-25%)}.modal.in .modal-dialog{-webkit-transform:translate(0,0);-ms-transform:translate(0,0);-o-transform:translate(0,0);transform:translate(0,0)}.modal-open .modal{overflow-x:hidden;overflow-y:auto}.modal-dialog{position:relative;width:auto;margin:10px}.modal-content{position:relative;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #999;border:1px solid rgba(0,0,0,.2);border-radius:6px;outline:0;-webkit-box-shadow:0 3px 9px rgba(0,0,0,.5);box-shadow:0 3px 9px rgba(0,0,0,.5)}.modal-backdrop{position:fixed;top:0;right:0;bottom:0;left:0;z-index:1040;background-color:#000}.modal-backdrop.fade{filter:alpha(opacity=0);opacity:0}.modal-backdrop.in{filter:alpha(opacity=50);opacity:.5}.modal-header{padding:15px;border-bottom:1px solid #e5e5e5}.modal-header .close{margin-top:-2px}.modal-title{margin:0;line-height:1.42857143}.modal-body{position:relative;padding:15px}.modal-footer{padding:15px;text-align:right;border-top:1px solid #e5e5e5}.modal-footer .btn+.btn{margin-bottom:0;margin-left:5px}.modal-footer .btn-group .btn+.btn{margin-left:-1px}.modal-footer .btn-block+.btn-block{margin-left:0}.modal-scrollbar-measure{position:absolute;top:-9999px;width:50px;height:50px;overflow:scroll}@media (min-width:768px){.modal-dialog{width:600px;margin:30px auto}.modal-content{-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5)}.modal-sm{width:300px}}@media (min-width:992px){.modal-lg{width:900px}}.tooltip{position:absolute;z-index:1070;display:block;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:12px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;filter:alpha(opacity=0);opacity:0;line-break:auto}.tooltip.in{filter:alpha(opacity=90);opacity:.9}.tooltip.top{padding:5px 0;margin-top:-3px}.tooltip.right{padding:0 5px;margin-left:3px}.tooltip.bottom{padding:5px 0;margin-top:3px}.tooltip.left{padding:0 5px;margin-left:-3px}.tooltip-inner{max-width:200px;padding:3px 8px;color:#fff;text-align:center;background-color:#000;border-radius:4px}.tooltip-arrow{position:absolute;width:0;height:0;border-color:transparent;border-style:solid}.tooltip.top .tooltip-arrow{bottom:0;left:50%;margin-left:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-left .tooltip-arrow{right:5px;bottom:0;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.top-right .tooltip-arrow{bottom:0;left:5px;margin-bottom:-5px;border-width:5px 5px 0;border-top-color:#000}.tooltip.right .tooltip-arrow{top:50%;left:0;margin-top:-5px;border-width:5px 5px 5px 0;border-right-color:#000}.tooltip.left .tooltip-arrow{top:50%;right:0;margin-top:-5px;border-width:5px 0 5px 5px;border-left-color:#000}.tooltip.bottom .tooltip-arrow{top:0;left:50%;margin-left:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-left .tooltip-arrow{top:0;right:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.tooltip.bottom-right .tooltip-arrow{top:0;left:5px;margin-top:-5px;border-width:0 5px 5px;border-bottom-color:#000}.popover{position:absolute;top:0;left:0;z-index:1060;display:none;max-width:276px;padding:1px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:1.42857143;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;-webkit-background-clip:padding-box;background-clip:padding-box;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:6px;-webkit-box-shadow:0 5px 10px rgba(0,0,0,.2);box-shadow:0 5px 10px rgba(0,0,0,.2);line-break:auto}.popover.top{margin-top:-10px}.popover.right{margin-left:10px}.popover.bottom{margin-top:10px}.popover.left{margin-left:-10px}.popover-title{padding:8px 14px;margin:0;font-size:14px;background-color:#f7f7f7;border-bottom:1px solid #ebebeb;border-radius:5px 5px 0 0}.popover-content{padding:9px 14px}.popover>.arrow,.popover>.arrow:after{position:absolute;display:block;width:0;height:0;border-color:transparent;border-style:solid}.popover>.arrow{border-width:11px}.popover>.arrow:after{content:"";border-width:10px}.popover.top>.arrow{bottom:-11px;left:50%;margin-left:-11px;border-top-color:#999;border-top-color:rgba(0,0,0,.25);border-bottom-width:0}.popover.top>.arrow:after{bottom:1px;margin-left:-10px;content:" ";border-top-color:#fff;border-bottom-width:0}.popover.right>.arrow{top:50%;left:-11px;margin-top:-11px;border-right-color:#999;border-right-color:rgba(0,0,0,.25);border-left-width:0}.popover.right>.arrow:after{bottom:-10px;left:1px;content:" ";border-right-color:#fff;border-left-width:0}.popover.bottom>.arrow{top:-11px;left:50%;margin-left:-11px;border-top-width:0;border-bottom-color:#999;border-bottom-color:rgba(0,0,0,.25)}.popover.bottom>.arrow:after{top:1px;margin-left:-10px;content:" ";border-top-width:0;border-bottom-color:#fff}.popover.left>.arrow{top:50%;right:-11px;margin-top:-11px;border-right-width:0;border-left-color:#999;border-left-color:rgba(0,0,0,.25)}.popover.left>.arrow:after{right:1px;bottom:-10px;content:" ";border-right-width:0;border-left-color:#fff}.carousel{position:relative}.carousel-inner{position:relative;width:100%;overflow:hidden}.carousel-inner>.item{position:relative;display:none;-webkit-transition:.6s ease-in-out left;-o-transition:.6s ease-in-out left;transition:.6s ease-in-out left}.carousel-inner>.item>a>img,.carousel-inner>.item>img{line-height:1}@media all and (transform-3d),(-webkit-transform-3d){.carousel-inner>.item{-webkit-transition:-webkit-transform .6s ease-in-out;-o-transition:-o-transform .6s ease-in-out;transition:transform .6s ease-in-out;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-perspective:1000px;perspective:1000px}.carousel-inner>.item.active.right,.carousel-inner>.item.next{left:0;-webkit-transform:translate3d(100%,0,0);transform:translate3d(100%,0,0)}.carousel-inner>.item.active.left,.carousel-inner>.item.prev{left:0;-webkit-transform:translate3d(-100%,0,0);transform:translate3d(-100%,0,0)}.carousel-inner>.item.active,.carousel-inner>.item.next.left,.carousel-inner>.item.prev.right{left:0;-webkit-transform:translate3d(0,0,0);transform:translate3d(0,0,0)}}.carousel-inner>.active,.carousel-inner>.next,.carousel-inner>.prev{display:block}.carousel-inner>.active{left:0}.carousel-inner>.next,.carousel-inner>.prev{position:absolute;top:0;width:100%}.carousel-inner>.next{left:100%}.carousel-inner>.prev{left:-100%}.carousel-inner>.next.left,.carousel-inner>.prev.right{left:0}.carousel-inner>.active.left{left:-100%}.carousel-inner>.active.right{left:100%}.carousel-control{position:absolute;top:0;bottom:0;left:0;width:15%;font-size:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6);background-color:rgba(0,0,0,0);filter:alpha(opacity=50);opacity:.5}.carousel-control.left{background-image:-webkit-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.5)),to(rgba(0,0,0,.0001)));background-image:linear-gradient(to right,rgba(0,0,0,.5) 0,rgba(0,0,0,.0001) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#80000000', endColorstr='#00000000', GradientType=1);background-repeat:repeat-x}.carousel-control.right{right:0;left:auto;background-image:-webkit-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-o-linear-gradient(left,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);background-image:-webkit-gradient(linear,left top,right top,from(rgba(0,0,0,.0001)),to(rgba(0,0,0,.5)));background-image:linear-gradient(to right,rgba(0,0,0,.0001) 0,rgba(0,0,0,.5) 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#00000000', endColorstr='#80000000', GradientType=1);background-repeat:repeat-x}.carousel-control:focus,.carousel-control:hover{color:#fff;text-decoration:none;filter:alpha(opacity=90);outline:0;opacity:.9}.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{position:absolute;top:50%;z-index:5;display:inline-block;margin-top:-10px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{left:50%;margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{right:50%;margin-right:-10px}.carousel-control .icon-next,.carousel-control .icon-prev{width:20px;height:20px;font-family:serif;line-height:1}.carousel-control .icon-prev:before{content:'\2039'}.carousel-control .icon-next:before{content:'\203a'}.carousel-indicators{position:absolute;bottom:10px;left:50%;z-index:15;width:60%;padding-left:0;margin-left:-30%;text-align:center;list-style:none}.carousel-indicators li{display:inline-block;width:10px;height:10px;margin:1px;text-indent:-999px;cursor:pointer;background-color:#000\9;background-color:rgba(0,0,0,0);border:1px solid #fff;border-radius:10px}.carousel-indicators .active{width:12px;height:12px;margin:0;background-color:#fff}.carousel-caption{position:absolute;right:15%;bottom:20px;left:15%;z-index:10;padding-top:20px;padding-bottom:20px;color:#fff;text-align:center;text-shadow:0 1px 2px rgba(0,0,0,.6)}.carousel-caption .btn{text-shadow:none}@media screen and (min-width:768px){.carousel-control .glyphicon-chevron-left,.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next,.carousel-control .icon-prev{width:30px;height:30px;margin-top:-10px;font-size:30px}.carousel-control .glyphicon-chevron-left,.carousel-control .icon-prev{margin-left:-10px}.carousel-control .glyphicon-chevron-right,.carousel-control .icon-next{margin-right:-10px}.carousel-caption{right:20%;left:20%;padding-bottom:30px}.carousel-indicators{bottom:20px}}.btn-group-vertical>.btn-group:after,.btn-group-vertical>.btn-group:before,.btn-toolbar:after,.btn-toolbar:before,.clearfix:after,.clearfix:before,.container-fluid:after,.container-fluid:before,.container:after,.container:before,.dl-horizontal dd:after,.dl-horizontal dd:before,.form-horizontal .form-group:after,.form-horizontal .form-group:before,.modal-footer:after,.modal-footer:before,.modal-header:after,.modal-header:before,.nav:after,.nav:before,.navbar-collapse:after,.navbar-collapse:before,.navbar-header:after,.navbar-header:before,.navbar:after,.navbar:before,.pager:after,.pager:before,.panel-body:after,.panel-body:before,.row:after,.row:before{display:table;content:" "}.btn-group-vertical>.btn-group:after,.btn-toolbar:after,.clearfix:after,.container-fluid:after,.container:after,.dl-horizontal dd:after,.form-horizontal .form-group:after,.modal-footer:after,.modal-header:after,.nav:after,.navbar-collapse:after,.navbar-header:after,.navbar:after,.pager:after,.panel-body:after,.row:after{clear:both}.center-block{display:block;margin-right:auto;margin-left:auto}.pull-right{float:right!important}.pull-left{float:left!important}.hide{display:none!important}.show{display:block!important}.invisible{visibility:hidden}.text-hide{font:0/0 a;color:transparent;text-shadow:none;background-color:transparent;border:0}.hidden{display:none!important}.affix{position:fixed}@-ms-viewport{width:device-width}.visible-lg,.visible-md,.visible-sm,.visible-xs{display:none!important}.visible-lg-block,.visible-lg-inline,.visible-lg-inline-block,.visible-md-block,.visible-md-inline,.visible-md-inline-block,.visible-sm-block,.visible-sm-inline,.visible-sm-inline-block,.visible-xs-block,.visible-xs-inline,.visible-xs-inline-block{display:none!important}@media (max-width:767px){.visible-xs{display:block!important}table.visible-xs{display:table!important}tr.visible-xs{display:table-row!important}td.visible-xs,th.visible-xs{display:table-cell!important}}@media (max-width:767px){.visible-xs-block{display:block!important}}@media (max-width:767px){.visible-xs-inline{display:inline!important}}@media (max-width:767px){.visible-xs-inline-block{display:inline-block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm{display:block!important}table.visible-sm{display:table!important}tr.visible-sm{display:table-row!important}td.visible-sm,th.visible-sm{display:table-cell!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-block{display:block!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline{display:inline!important}}@media (min-width:768px) and (max-width:991px){.visible-sm-inline-block{display:inline-block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md{display:block!important}table.visible-md{display:table!important}tr.visible-md{display:table-row!important}td.visible-md,th.visible-md{display:table-cell!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-block{display:block!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline{display:inline!important}}@media (min-width:992px) and (max-width:1199px){.visible-md-inline-block{display:inline-block!important}}@media (min-width:1200px){.visible-lg{display:block!important}table.visible-lg{display:table!important}tr.visible-lg{display:table-row!important}td.visible-lg,th.visible-lg{display:table-cell!important}}@media (min-width:1200px){.visible-lg-block{display:block!important}}@media (min-width:1200px){.visible-lg-inline{display:inline!important}}@media (min-width:1200px){.visible-lg-inline-block{display:inline-block!important}}@media (max-width:767px){.hidden-xs{display:none!important}}@media (min-width:768px) and (max-width:991px){.hidden-sm{display:none!important}}@media (min-width:992px) and (max-width:1199px){.hidden-md{display:none!important}}@media (min-width:1200px){.hidden-lg{display:none!important}}.visible-print{display:none!important}@media print{.visible-print{display:block!important}table.visible-print{display:table!important}tr.visible-print{display:table-row!important}td.visible-print,th.visible-print{display:table-cell!important}}.visible-print-block{display:none!important}@media print{.visible-print-block{display:block!important}}.visible-print-inline{display:none!important}@media print{.visible-print-inline{display:inline!important}}.visible-print-inline-block{display:none!important}@media print{.visible-print-inline-block{display:inline-block!important}}@media print{.hidden-print{display:none!important}} +/*# sourceMappingURL=bootstrap.min.css.map */ \ No newline at end of file diff --git a/src/blog/static/assets/css/docs.min.css b/src/blog/static/assets/css/docs.min.css new file mode 100644 index 0000000..3945197 --- /dev/null +++ b/src/blog/static/assets/css/docs.min.css @@ -0,0 +1,11 @@ +/*! + * IE10 viewport hack for Surface/desktop Windows 8 bug + * Copyright 2014-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */@-ms-viewport{width:device-width}@-o-viewport{width:device-width}@viewport{width:device-width}.hll{background-color:#ffc}.c{color:#999}.err{color:#A00;background-color:#FAA}.k{color:#069}.o{color:#555}.cm{color:#999}.cp{color:#099}.c1{color:#999}.cs{color:#999}.gd{background-color:#FCC;border:1px solid #C00}.ge{font-style:italic}.gr{color:red}.gh{color:#030}.gi{background-color:#CFC;border:1px solid #0C0}.go{color:#AAA}.gp{color:#009}.gu{color:#030}.gt{color:#9C6}.kc{color:#069}.kd{color:#069}.kn{color:#069}.kp{color:#069}.kr{color:#069}.kt{color:#078}.m{color:#F60}.s{color:#d44950}.na{color:#4f9fcf}.nb{color:#366}.nc{color:#0A8}.no{color:#360}.nd{color:#99F}.ni{color:#999}.ne{color:#C00}.nf{color:#C0F}.nl{color:#99F}.nn{color:#0CF}.nt{color:#2f6f9f}.nv{color:#033}.ow{color:#000}.w{color:#bbb}.mf{color:#F60}.mh{color:#F60}.mi{color:#F60}.mo{color:#F60}.sb{color:#C30}.sc{color:#C30}.sd{color:#C30;font-style:italic}.s2{color:#C30}.se{color:#C30}.sh{color:#C30}.si{color:#A00}.sx{color:#C30}.sr{color:#3AA}.s1{color:#C30}.ss{color:#FC3}.bp{color:#366}.vc{color:#033}.vg{color:#033}.vi{color:#033}.il{color:#F60}.css .nt+.nt,.css .o,.css .o+.nt{color:#999}.select2-container{position:relative;display:inline-block;zoom:1;*display:inline;vertical-align:top;padding:0;border:0}.select2-container:hover{border:0;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none}.select2-container,.select2-drop,.select2-search,.select2-search input{-moz-box-sizing:border-box;-ms-box-sizing:border-box;-webkit-box-sizing:border-box;-khtml-box-sizing:border-box;box-sizing:border-box}.select2-container .select2-choice{display:block;overflow:hidden;text-decoration:none;padding:4px 12px;margin:0;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap;font-family:Arial,Helvetica,sans-serif;font-weight:700;font-size:13px;cursor:default;height:18px;background-color:#f3f3f3;background-image:-moz-linear-gradient(top,#f5f5f5,#f1f1f1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f5f5f5),to(#f1f1f1));background-image:-webkit-linear-gradient(top,#f5f5f5,#f1f1f1);background-image:-o-linear-gradient(top,#f5f5f5,#f1f1f1);background-image:linear-gradient(to bottom,#f5f5f5,#f1f1f1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff1f1f1', GradientType=0);-webkit-background-clip:padding;-moz-background-clip:padding;background-clip:padding;border:1px solid #dcdcdc;-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;-moz-box-sizing:content-box;-ms-box-sizing:content-box;-webkit-box-sizing:content-box;-khtml-box-sizing:content-box;box-sizing:content-box}.select2-container .select2-choice:hover{color:#333;text-shadow:none;border-color:#c6c6c6;background-color:#f5f5f5;background-image:-moz-linear-gradient(top,#f8f8f8,#f1f1f1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f8f8f8),to(#f1f1f1));background-image:-webkit-linear-gradient(top,#f8f8f8,#f1f1f1);background-image:-o-linear-gradient(top,#f8f8f8,#f1f1f1);background-image:linear-gradient(to bottom,#f8f8f8,#f1f1f1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8f8f8', endColorstr='#fff1f1f1', GradientType=0);-webkit-box-shadow:0 1px 1px rgba(0,0,0,.1);-moz-box-shadow:0 1px 1px rgba(0,0,0,.1);box-shadow:0 1px 1px rgba(0,0,0,.1);background-position:0 0;-webkit-transition:none;-moz-transition:none;-o-transition:none;transition:none;z-index:2}.select2-container-active .select2-choice:hover{border:1px solid #4D90FE}.select2-container.select2-drop-above .select2-choice{background-image:-webkit-gradient(linear,left bottom,left top,color-stop(0,#eee),color-stop(.9,#fff));background-image:-webkit-linear-gradient(center bottom,#eee 0,#fff 90%);background-image:-moz-linear-gradient(center bottom,#eee 0,#fff 90%);background-image:-o-linear-gradient(bottom,#eee 0,#fff 90%);background-image:-ms-linear-gradient(top,#eee 0,#fff 90%);filter:progid:DXImageTransform.Microsoft.gradient( startColorstr='#eeeeee', endColorstr='#ffffff', GradientType=0 );background-image:linear-gradient(top,#eee 0,#fff 90%)}.select2-container .select2-choice span{margin-right:26px;display:block;overflow:hidden;white-space:nowrap;-o-text-overflow:ellipsis;-ms-text-overflow:ellipsis;text-overflow:ellipsis}.select2-container .select2-choice abbr{display:block;position:absolute;right:26px;top:8px;width:12px;height:12px;font-size:17px;line-height:16px;color:#595959;font-weight:700;cursor:pointer;text-decoration:none;border:0;outline:0}.select2-container .select2-choice abbr:hover{color:#222;cursor:pointer}.select2-drop-mask{position:absolute;left:0;top:0;z-index:9998;opacity:0}.select2-drop{background:#fff;color:#000;border:1px solid #aaa;position:absolute;top:100%;-webkit-box-shadow:0 2px 4px rgba(0,0,0,.2);-moz-box-shadow:0 2px 4px rgba(0,0,0,.2);-o-box-shadow:0 2px 4px rgba(0,0,0,.2);box-shadow:0 2px 4px rgba(0,0,0,.2);z-index:9999;width:100%;margin-top:1px}.select2-drop.select2-drop-above{margin-top:-1px;-webkit-box-shadow:0 -2px 4px rgba(0,0,0,.2);-moz-box-shadow:0 -2px 4px rgba(0,0,0,.2);-o-box-shadow:0 -2px 4px rgba(0,0,0,.2);box-shadow:0 -2px 4px rgba(0,0,0,.2)}.select2-container .select2-choice div{-webkit-border-radius:0 2px 2px 0;-moz-border-radius:0 2px 2px 0;border-radius:0 2px 2px 0;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;position:absolute;right:0;top:0;display:block;height:100%;width:18px}.select2-container .select2-choice div b{background:url(/assets/img/select2.png) no-repeat -30px 2px;display:block;width:100%;height:100%}.select2-search{display:inline-block;white-space:nowrap;z-index:10000;min-height:26px;width:100%;margin:0;padding:4px 4px 0 4px}.select2-search-hidden{display:block;position:absolute;left:-10000px}.select2-search input{background:#fff url(/assets/img/select2.png) no-repeat 100% -22px;background:url(/assets/img/select2.png) no-repeat 100% -22px,-webkit-gradient(linear,left bottom,left top,color-stop(.85,#fff),color-stop(.99,#eee));background:url(/assets/img/select2.png) no-repeat 100% -22px,-webkit-linear-gradient(center bottom,#fff 85%,#eee 99%);background:url(/assets/img/select2.png) no-repeat 100% -22px,-moz-linear-gradient(center bottom,#fff 85%,#eee 99%);background:url(/assets/img/select2.png) no-repeat 100% -22px,-o-linear-gradient(bottom,#fff 85%,#eee 99%);background:url(/assets/img/select2.png) no-repeat 100% -22px,-ms-linear-gradient(top,#fff 85%,#eee 99%);background:url(/assets/img/select2.png) no-repeat 100% -22px,linear-gradient(top,#fff 85%,#eee 99%);padding:4px 20px 4px 5px;outline:0;border:1px solid #aaa;font-family:sans-serif;font-size:1em;width:100%;margin:0;height:auto!important;min-height:26px;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;border-radius:0;-moz-border-radius:0;-webkit-border-radius:0}.select2-drop.select2-drop-above .select2-search input{margin-top:4px}.select2-search input.select2-active{background:#fff url(../img/spinner.gif) no-repeat 100%;background:url(../img/spinner.gif) no-repeat 100%,-webkit-gradient(linear,left bottom,left top,color-stop(.85,#fff),color-stop(.99,#eee));background:url(../img/spinner.gif) no-repeat 100%,-webkit-linear-gradient(center bottom,#fff 85%,#eee 99%);background:url(../img/spinner.gif) no-repeat 100%,-moz-linear-gradient(center bottom,#fff 85%,#eee 99%);background:url(../img/spinner.gif) no-repeat 100%,-o-linear-gradient(bottom,#fff 85%,#eee 99%);background:url(../img/spinner.gif) no-repeat 100%,-ms-linear-gradient(top,#fff 85%,#eee 99%);background:url(../img/spinner.gif) no-repeat 100%,linear-gradient(top,#fff 85%,#eee 99%)}.select2-container-active .select2-choice,.select2-container-active .select2-choices{border:1px solid #4D90FE;outline:0}.select2-dropdown-open .select2-choice,.select2-dropdown-open .select2-choice:hover{background-color:#f4f4f4;background-image:-moz-linear-gradient(top,#f6f6f6,#f1f1f1);background-image:-webkit-gradient(linear,0 0,0 100%,from(#f6f6f6),to(#f1f1f1));background-image:-webkit-linear-gradient(top,#f6f6f6,#f1f1f1);background-image:-o-linear-gradient(top,#f6f6f6,#f1f1f1);background-image:linear-gradient(to bottom,#f6f6f6,#f1f1f1);background-repeat:repeat-x;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff6f6f6', endColorstr='#fff1f1f1', GradientType=0);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.select2-dropdown-open .select2-choice div{background:0 0;border-left:none}.select2-results{margin:4px 1px 4px 0;padding:0;position:relative;overflow-x:hidden;overflow-y:auto;max-height:200px}.select2-results ul.select2-result-sub{margin:0}.select2-results ul.select2-result-sub>li .select2-result-label{padding-left:20px}.select2-results ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:40px}.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:60px}.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:80px}.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:100px}.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:110px}.select2-results ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub ul.select2-result-sub>li .select2-result-label{padding-left:120px}.select2-results li{list-style:none;display:list-item}.select2-results li.select2-result-with-children>.select2-result-label{font-weight:700}.select2-results .select2-result-label{padding:3px 7px 4px;margin:0;cursor:pointer}.select2-results .select2-highlighted{background:#eee}.select2-results li em{background:#feffde;font-style:normal}.select2-results .select2-highlighted em{background:0 0}.select2-results .select2-no-results,.select2-results .select2-searching,.select2-results .select2-selection-limit{background:#f4f4f4;display:list-item;padding-left:4px}.select2-results .select2-disabled{display:none}.select2-more-results.select2-active{background:#f4f4f4 url(../img/spinner.gif) no-repeat 100%}.select2-more-results{background:#f4f4f4;display:list-item}.select2-container.select2-container-disabled .select2-choice{color:#b3b3b3;border-color:#d9d9d9;background-color:#e6e6e6;background-image:none;-webkit-box-shadow:none;-moz-box-shadow:none;box-shadow:none;text-shadow:none;cursor:default}.select2-container.select2-container-disabled .select2-choice div{opacity:.5;filter:alpha(opacity=50)}.select2-container-multi .select2-choices{background-color:#fff;border:1px solid #d9d9d9;border-top:1px solid silver;-webkit-border-radius:1px;-moz-border-radius:1px;border-radius:1px;margin:0;padding:0;cursor:text;overflow:hidden;height:auto!important;height:1%;position:relative}.select2-container-multi .select2-choices:hover{border:1px solid #b9b9b9;border-top:1px solid #a0a0a0;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.select2-container-multi .select2-choices{min-height:26px}.select2-container-multi.select2-container-active .select2-choices{border:1px solid #4D90FE;outline:0}.select2-container-multi .select2-choices li{float:left;list-style:none}.select2-container-multi .select2-choices .select2-search-field{white-space:nowrap;margin:0;padding:0}.select2-container-multi .select2-choices .select2-search-field input{color:#666;background:0 0!important;font-family:sans-serif;font-size:100%;height:23px;padding:5px;margin:1px 0;outline:0;border:0;-webkit-box-shadow:none;-moz-box-shadow:none;-o-box-shadow:none;box-shadow:none}.select2-container-multi .select2-choices .select2-search-field input.select2-active{background:#fff url(../img/spinner.gif) no-repeat 100%!important}.select2-default{color:#999!important}.select2-container-multi .select2-choices .select2-search-choice{-webkit-border-radius:2px;-moz-border-radius:2px;border-radius:2px;-moz-background-clip:padding;-webkit-background-clip:padding-box;background-clip:padding-box;background-color:#DAE4F6;color:#222;font-family:Arial;border:1px solid #DAE4F6;line-height:23px;padding:0 19px 0 5px;margin:1px;position:relative;cursor:default}.select2-container-multi .select2-choices .select2-search-choice span{cursor:default}.select2-container-multi .select2-choices .select2-search-choice-focus{background:#A6D7F5}.select2-search-choice-close{display:block;position:absolute;right:3px;top:4px;width:12px;height:13px;font-size:17px;line-height:16px;color:#444;font-weight:700;outline:0}.select2-search-choice-close:hover{text-decoration:none;color:#222;cursor:pointer}.select2-container-multi.select2-container-disabled .select2-choices{background-color:#f4f4f4;background-image:none;border:1px solid #ddd;cursor:default}.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice{background-image:none;background-color:#f4f4f4;border:1px solid #ddd;padding:3px 5px 3px 5px}.select2-container-multi.select2-container-disabled .select2-choices .select2-search-choice .select2-search-choice-close{display:none}.select2-result-selectable .select2-match,.select2-result-unselectable .select2-result-selectable .select2-match{font-weight:700}.select2-result-unselectable .select2-match{text-decoration:none}.select2-offscreen{position:absolute;left:-10000px}.select2-results::-webkit-scrollbar{height:16px;width:10px}.select2-results::-webkit-scrollbar-button:end:increment,.select2-results::-webkit-scrollbar-button:start:decrement{background-color:transparent;display:block;height:0}.select2-results::-webkit-scrollbar-track{background-clip:padding-box;border:solid transparent;border-width:0 0 0 4px}.select2-results::-webkit-scrollbar-track-piece{background-color:transparent;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.select2-results::-webkit-scrollbar:hover{background-color:#f3f3f3;border:1px solid #dbdbdb}.select2-results::-webkit-scrollbar-thumb:horizontal,.select2-results::-webkit-scrollbar-thumb:vertical{background-color:#c6c6c6;-webkit-border-radius:0;-moz-border-radius:0;border-radius:0}.select2-results::-webkit-scrollbar-thumb{background-color:rgba(0,0,0,.2);border:solid transparent;border-width:0;-webkit-box-shadow:inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07);-moz-box-shadow:inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07);box-shadow:inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07);background-clip:padding-box}.select2-results::-webkit-scrollbar-thumb:hover{background-color:#949494}.select2-results::-webkit-scrollbar-thumb:active{background-color:rgba(0,0,0,.5);-webkit-box-shadow:inset 1px 1px 3px rgba(0,0,0,.35);-moz-box-shadow:inset 1px 1px 3px rgba(0,0,0,.35);box-shadow:inset 1px 1px 3px rgba(0,0,0,.35)}@media only screen and (-webkit-min-device-pixel-ratio:1.5){.select2-container .select2-choice div b,.select2-search input{background-image:url(/assets/img/select2x2.png)!important;background-repeat:no-repeat!important;background-size:60px 40px!important}.select2-search input{background-position:100% -21px!important}}/*! + * Bootstrap Docs (http://getbootstrap.com) + * Copyright 2011-2016 Twitter, Inc. + * Licensed under the Creative Commons Attribution 3.0 Unported License. For + * details, see https://creativecommons.org/licenses/by/3.0/. + */body{position:relative;padding-top:94px}.table code{font-size:13px;font-weight:400}h2 code,h3 code,h4 code{background-color:inherit}.btn-outline{color:#4d90fe;background-color:transparent;border-color:#4d90fe}.btn-outline:active,.btn-outline:focus,.btn-outline:hover{color:#fff;background-color:#4d90fe;border-color:#4d90fe}.btn-outline-inverse{color:#fff;background-color:transparent;border-color:#fff}.btn-outline-inverse:active,.btn-outline-inverse:focus,.btn-outline-inverse:hover{color:#2d87e2;text-shadow:none;background-color:#fff;border-color:#fff}#skippy{display:block;padding:1em;color:#777;background-color:#f1f1f1;outline:0}#skippy .skiplink-text{padding:.5em;outline:1px dotted}#content:focus{outline:0}.bs-docs-footer{padding-top:40px;padding-bottom:30px;margin-top:100px;color:#777;text-align:center;border-top:1px solid #e5e5e5}.bs-docs-footer-links{padding-left:0;margin-bottom:20px}.bs-docs-footer-links li{display:inline-block}.bs-docs-footer-links li+li{margin-left:15px}@media (min-width:768px){.bs-docs-footer{text-align:left}.bs-docs-footer p{margin-bottom:0}}.bs-docs-header,.bs-docs-masthead{position:relative;padding:30px 0;color:#b3d4f4;text-align:center;text-shadow:0 1px 0 rgba(0,0,0,.1);background-color:#2d87e2;background-image:-webkit-linear-gradient(top,#1b6ec1 0,#2d87e2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#1b6ec1),to(#2d87e2));background-image:-o-linear-gradient(top,#1b6ec1 0,#2d87e2 100%);background-image:linear-gradient(to bottom,#1b6ec1 0,#2d87e2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#1b6ec1', endColorstr='#2d87e2', GradientType=0);background-repeat:repeat-x}.bs-docs-masthead .bs-docs-booticon{margin:0 auto 30px}.bs-docs-masthead h1{font-weight:300;line-height:1;color:#fff}.bs-docs-masthead .lead{margin:0 auto 30px;font-size:20px;color:#fff}.bs-docs-masthead .version{margin-top:-15px;color:#b3d4f4}.bs-docs-masthead .btn{width:100%;padding:15px 30px;font-size:20px}@media (min-width:480px){.bs-docs-masthead .btn{width:auto}}@media (min-width:768px){.bs-docs-masthead{padding:80px 0}.bs-docs-masthead h1{font-size:60px}.bs-docs-masthead .lead{font-size:24px}}@media (min-width:992px){.bs-docs-masthead .lead{width:80%;font-size:30px}}.bs-docs-header{margin-bottom:40px;font-size:20px}.bs-docs-header h1{margin-top:0;color:#fff}.bs-docs-header p{margin-bottom:0;font-weight:300;line-height:1.4}.bs-docs-header .container{position:relative}@media (min-width:768px){.bs-docs-header{padding-top:60px;padding-bottom:60px;font-size:24px;text-align:left}.bs-docs-header h1{font-size:60px;line-height:1}}@media (min-width:992px){.bs-docs-header h1,.bs-docs-header p{margin-right:380px}}.bs-docs-featurette{padding-top:40px;padding-bottom:40px;font-size:16px;line-height:1.5;color:#555;text-align:center;background-color:#fff;border-bottom:1px solid #e5e5e5}.bs-docs-featurette+.bs-docs-footer{margin-top:0;border-top:0}.bs-docs-featurette-title{margin-bottom:5px;font-size:30px;font-weight:400;color:#333}.half-rule{width:100px;margin:40px auto}.bs-docs-featurette h3{margin-bottom:5px;font-weight:400;color:#333}.bs-docs-featurette-img{display:block;margin-bottom:20px;color:#333}.bs-docs-featurette-img:hover{color:#337ab7;text-decoration:none}.bs-docs-featurette-img img{display:block;margin-bottom:15px}@media (min-width:480px){.bs-docs-featurette .img-responsive{margin-top:30px}}@media (min-width:768px){.bs-docs-featurette{padding-top:100px;padding-bottom:100px}.bs-docs-featurette-title{font-size:40px}.bs-docs-featurette .lead{max-width:80%;margin-right:auto;margin-left:auto}.bs-docs-featurette .img-responsive{margin-top:0}}.bs-docs-featured-sites{margin-right:-1px;margin-left:-1px}.bs-docs-featured-sites .col-xs-6{padding:1px}.bs-docs-featured-sites .img-responsive{margin-top:0}@media (min-width:768px){.bs-docs-featured-sites .col-sm-3:first-child img{border-top-left-radius:4px;border-bottom-left-radius:4px}.bs-docs-featured-sites .col-sm-3:last-child img{border-top-right-radius:4px;border-bottom-right-radius:4px}}.bs-examples .thumbnail{margin-bottom:10px}.bs-examples h4{margin-bottom:5px}.bs-examples p{margin-bottom:20px}@media (max-width:480px){.bs-examples{margin-right:-10px;margin-left:-10px}.bs-examples>[class^=col-]{padding-right:10px;padding-left:10px}}.bs-docs-sidebar.affix{position:static}@media (min-width:768px){.bs-docs-sidebar{padding-left:20px}}.bs-docs-sidenav{margin-top:50px;margin-bottom:20px}.bs-docs-sidebar .nav>li>a{display:block;padding:5px 20px;font-size:13px;font-weight:500;color:#222}.bs-docs-sidebar .nav>li>a:focus,.bs-docs-sidebar .nav>li>a:hover{text-decoration:none;background-color:#eee}.bs-docs-sidebar .nav>.active:focus>a,.bs-docs-sidebar .nav>.active:hover>a,.bs-docs-sidebar .nav>.active>a{color:#dd4b39;background-color:transparent}.bs-docs-sidebar .nav .nav{display:none;margin-bottom:8px}.bs-docs-sidebar .nav .nav>li>a{padding-top:1px;padding-bottom:1px;padding-left:30px;font-size:12px}.back-to-top,.bs-docs-theme-toggle{display:none;padding:4px 10px;margin-top:10px;margin-left:10px;font-size:12px;font-weight:500;color:#999}.back-to-top:hover,.bs-docs-theme-toggle:hover{color:#563d7c;text-decoration:none}.bs-docs-theme-toggle{margin-top:0}@media (min-width:768px){.back-to-top,.bs-docs-theme-toggle{display:block}}@media (min-width:992px){.bs-docs-sidebar .nav>.active>ul{display:block}.bs-docs-sidebar.affix,.bs-docs-sidebar.affix-bottom{width:213px}.bs-docs-sidebar.affix{position:fixed;top:80px}.bs-docs-sidebar.affix-bottom{position:absolute}.bs-docs-sidebar.affix .bs-docs-sidenav,.bs-docs-sidebar.affix-bottom .bs-docs-sidenav{margin-top:0;margin-bottom:0}}@media (min-width:1200px){.bs-docs-sidebar.affix,.bs-docs-sidebar.affix-bottom{width:263px}}.bs-docs-section{margin-bottom:60px}.bs-docs-section:last-child{margin-bottom:0}h1[id]{padding-top:20px;margin-top:0}.bs-callout{padding:20px;margin:20px 0;border:1px solid #eee;border-left-width:5px;border-radius:3px}.bs-callout h4{margin-top:0;margin-bottom:5px}.bs-callout p:last-child{margin-bottom:0}.bs-callout code{border-radius:3px}.bs-callout+.bs-callout{margin-top:-5px}.bs-callout-danger{border-left-color:#dd4b39}.bs-callout-danger h4{color:#c23321}.bs-callout-warning{border-left-color:#f1e7bc}.bs-callout-warning h4{color:#ba9e27}.bs-callout-info{border-left-color:#d0e3f0}.bs-callout-info h4{color:#3b86b9}.color-swatches{margin:0 -5px;overflow:hidden}.color-swatch{float:left;width:60px;height:60px;margin:0 5px;border-radius:3px}@media (min-width:768px){.color-swatch{width:100px;height:100px}}.color-swatches .gray-darker{background-color:#222}.color-swatches .gray-dark{background-color:#333}.color-swatches .gray{background-color:#555}.color-swatches .gray-light{background-color:#999}.color-swatches .gray-lighter{background-color:#eee}.color-swatches .brand-primary{background-color:#4d90fe}.color-swatches .brand-success{background-color:#35aa47}.color-swatches .brand-warning{background-color:#faa937}.color-swatches .brand-danger{background-color:#d84a38}.color-swatches .brand-info{background-color:#5bc0de}.color-swatches .bs-purple{background-color:#1b6ec1}.color-swatches .bs-purple-light{background-color:#c7bfd3}.color-swatches .bs-purple-lighter{background-color:#e5e1ea}.color-swatches .bs-gray{background-color:#f9f9f9}.bs-team .team-member{line-height:32px;color:#555}.bs-team .team-member:hover{color:#333;text-decoration:none}.bs-team .github-btn{float:right;width:180px;height:20px;margin-top:6px;border:none}.bs-team img{float:left;width:32px;margin-right:10px;border-radius:4px}.bs-docs-browser-bugs td p{margin-bottom:0}.bs-docs-browser-bugs th:first-child{width:18%}.show-grid{margin-bottom:15px}.show-grid [class^=col-]{padding-top:10px;padding-bottom:10px;background-color:#f9f9f9;border:1px solid #ddd}.bs-example{position:relative;padding:45px 15px 15px;margin:0 -15px 15px;border-color:#e5e5e5 #eee #eee;border-style:solid;border-width:1px 0;-webkit-box-shadow:inset 0 3px 6px rgba(0,0,0,.05);box-shadow:inset 0 3px 6px rgba(0,0,0,.05)}.bs-example:after{position:absolute;top:15px;left:15px;font-size:12px;font-weight:700;color:#959595;text-transform:uppercase;letter-spacing:1px;content:"Example"}.bs-example-padded-bottom{padding-bottom:24px}.bs-example+.highlight,.bs-example+.zero-clipboard+.highlight{margin:-15px -15px 15px;border-width:0 0 1px;border-radius:0}@media (min-width:768px){.bs-example{margin-right:0;margin-left:0;background-color:#fff;border-color:#ddd;border-width:1px;border-radius:4px 4px 0 0;-webkit-box-shadow:none;box-shadow:none}.bs-example+.highlight,.bs-example+.zero-clipboard+.highlight{margin-top:-16px;margin-right:0;margin-left:0;border-width:1px;border-bottom-right-radius:4px;border-bottom-left-radius:4px}.bs-example-standalone{border-radius:4px}}.bs-example .container{width:auto}.bs-example>.alert:last-child,.bs-example>.form-control:last-child,.bs-example>.jumbotron:last-child,.bs-example>.list-group:last-child,.bs-example>.navbar:last-child,.bs-example>.panel:last-child,.bs-example>.progress:last-child,.bs-example>.table-responsive:last-child>.table,.bs-example>.table:last-child,.bs-example>.well:last-child,.bs-example>blockquote:last-child,.bs-example>ol:last-child,.bs-example>p:last-child,.bs-example>ul:last-child{margin-bottom:0}.bs-example>p>.close{float:none}.bs-example-type .table .type-info{color:#999;vertical-align:middle}.bs-example-type .table td{padding:15px 0;border-color:#eee}.bs-example-type .table tr:first-child td{border-top:0}.bs-example-type h1,.bs-example-type h2,.bs-example-type h3,.bs-example-type h4,.bs-example-type h5,.bs-example-type h6{margin:0}.bs-example-bg-classes p{padding:15px}.bs-example>.img-circle,.bs-example>.img-rounded,.bs-example>.img-thumbnail{margin:5px}.bs-example>.table-responsive>.table{background-color:#fff}.bs-example>.btn,.bs-example>.btn-group{margin-top:5px;margin-bottom:5px}.bs-example>.btn-toolbar+.btn-toolbar{margin-top:10px}.bs-example .select2-container.form-control,.bs-example-control-sizing input[type=text]+input[type=text],.bs-example-control-sizing select{margin-top:10px}.bs-example-form .input-group{margin-bottom:10px}.bs-example>textarea.form-control{resize:vertical}.bs-example>.list-group{max-width:400px}.bs-example .navbar:last-child{margin-bottom:0}.bs-navbar-bottom-example,.bs-navbar-top-example{z-index:1;padding:0;overflow:hidden}.bs-navbar-bottom-example .navbar-header,.bs-navbar-top-example .navbar-header{margin-left:0}.bs-navbar-bottom-example .navbar-fixed-bottom,.bs-navbar-top-example .navbar-fixed-top{position:relative;margin-right:0;margin-left:0}.bs-navbar-top-example{padding-bottom:90px}.bs-navbar-top-example:after{top:auto;bottom:15px}.bs-navbar-top-example .navbar-fixed-top{top:-1px}.bs-navbar-bottom-example{padding-top:90px}.bs-navbar-bottom-example .navbar-fixed-bottom{bottom:-1px}.bs-navbar-bottom-example .navbar{margin-bottom:0}@media (min-width:768px){.bs-navbar-bottom-example .navbar-fixed-bottom,.bs-navbar-top-example .navbar-fixed-top{position:absolute}}.bs-example .pagination{margin-top:10px;margin-bottom:10px}.bs-example>.pager{margin-top:0}.bs-example>.scrollable{height:200px;overflow-y:auto}.bs-example-modal{background-color:#f5f5f5}.bs-example-modal .modal{position:relative;top:auto;right:auto;bottom:auto;left:auto;z-index:1;display:block}.bs-example-modal .modal-dialog{left:auto;margin-right:auto;margin-left:auto}.bs-example .dropup>.dropdown-toggle,.bs-example>.dropdown>.dropdown-toggle{float:left}.bs-example-submenu .dropdown>.dropdown-menu,.bs-example-submenu .dropup>.dropdown-menu,.bs-example>.dropdown>.dropdown-menu{position:static;display:block;margin-bottom:5px;clear:left}.bs-example-submenu .dropdown-menu{margin-right:20px}.bs-example-tabs .nav-tabs{margin-bottom:15px}.bs-example-tooltips{text-align:center}.bs-example-tooltips>.btn{margin-top:5px;margin-bottom:5px}.bs-example-tooltip .tooltip{position:relative;display:inline-block;margin:10px 20px;opacity:1}.bs-example-popover{padding-bottom:24px;background-color:#f9f9f9}.bs-example-popover .popover{position:relative;display:block;float:left;width:260px;margin:20px}.scrollspy-example{position:relative;height:200px;margin-top:10px;overflow:auto}.bs-example>.nav-pills-stacked-example{max-width:300px}#collapseExample .well{margin-bottom:0}.bs-events-table>tbody>tr>td:first-child,.bs-events-table>thead>tr>th:first-child{white-space:nowrap}.bs-events-table>thead>tr>th:first-child{width:150px}.js-options-table>thead>tr>th:nth-child(1),.js-options-table>thead>tr>th:nth-child(2){width:100px}.js-options-table>thead>tr>th:nth-child(3){width:50px}.highlight{padding:9px 14px;margin-bottom:14px;background-color:#f7f7f9;border:1px solid #e1e1e8;border-radius:4px}.highlight pre{padding:0;margin-top:0;margin-bottom:0;word-break:normal;white-space:nowrap;background-color:transparent;border:0}.highlight pre code{font-size:inherit;color:#333}.highlight pre code:first-child{display:inline-block;padding-right:45px}.table-responsive .highlight pre{white-space:normal}.bs-table th small,.responsive-utilities th small{display:block;font-weight:400;color:#999}.responsive-utilities tbody th{font-weight:400}.responsive-utilities td{text-align:center}.responsive-utilities td.is-visible{color:#468847;background-color:#dff0d8!important}.responsive-utilities td.is-hidden{color:#ccc;background-color:#f9f9f9!important}.responsive-utilities-test{margin-top:5px}.responsive-utilities-test .col-xs-6{margin-bottom:10px}.responsive-utilities-test span{display:block;padding:15px 10px;font-size:14px;font-weight:700;line-height:1.1;text-align:center;border-radius:4px}.hidden-on .col-xs-6 .hidden-lg,.hidden-on .col-xs-6 .hidden-md,.hidden-on .col-xs-6 .hidden-sm,.hidden-on .col-xs-6 .hidden-xs,.visible-on .col-xs-6 .hidden-lg,.visible-on .col-xs-6 .hidden-md,.visible-on .col-xs-6 .hidden-sm,.visible-on .col-xs-6 .hidden-xs{color:#999;border:1px solid #ddd}.hidden-on .col-xs-6 .visible-lg-block,.hidden-on .col-xs-6 .visible-md-block,.hidden-on .col-xs-6 .visible-sm-block,.hidden-on .col-xs-6 .visible-xs-block,.visible-on .col-xs-6 .visible-lg-block,.visible-on .col-xs-6 .visible-md-block,.visible-on .col-xs-6 .visible-sm-block,.visible-on .col-xs-6 .visible-xs-block{color:#468847;background-color:#dff0d8;border:1px solid #d6e9c6}.bs-glyphicons{margin:0 -10px 20px;overflow:hidden}.bs-glyphicons-list{padding-left:0;list-style:none}.bs-glyphicons li{float:left;width:25%;height:115px;padding:10px;margin:0 -1px -1px 0;font-size:10px;line-height:1.4;text-align:center;border:1px solid #ddd}.bs-glyphicons .glyphicon{margin-top:5px;margin-bottom:10px;font-size:24px}.bs-glyphicons .glyphicon-class{display:block;text-align:center;word-wrap:break-word}.bs-glyphicons li:hover{background-color:#eee}@media (min-width:768px){.bs-glyphicons{margin-right:0;margin-left:0}.bs-glyphicons li{width:12.5%;font-size:12px}}.bs-customizer .toggle{float:right;margin-top:25px}.bs-customizer label{margin-top:10px;font-weight:500;color:#555}.bs-customizer h2{padding-top:30px;margin-top:0;margin-bottom:5px}.bs-customizer h3{margin-bottom:0}.bs-customizer h4{margin-top:15px;margin-bottom:0}.bs-customizer .bs-callout h4{margin-top:0;margin-bottom:5px}.bs-customizer input[type=text]{font-family:Menlo,Monaco,Consolas,"Courier New",monospace;background-color:#fafafa}.bs-customizer .help-block{margin-bottom:5px;font-size:12px}#less-section label{font-weight:400}.bs-customize-download .btn-outline{padding:20px}.bs-customizer-alert{position:fixed;top:0;right:0;left:0;z-index:1030;padding:15px 0;color:#fff;background-color:#d9534f;border-bottom:1px solid #b94441;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25);box-shadow:inset 0 1px 0 rgba(255,255,255,.25)}.bs-customizer-alert .close{margin-top:-4px;font-size:24px}.bs-customizer-alert p{margin-bottom:0}.bs-customizer-alert .glyphicon{margin-right:5px}.bs-customizer-alert pre{margin:10px 0 0;color:#fff;background-color:#a83c3a;border-color:#973634;-webkit-box-shadow:inset 0 2px 4px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 2px 4px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)}.bs-dropzone{position:relative;padding:20px;margin-bottom:20px;color:#777;text-align:center;border:2px dashed #eee;border-radius:4px}.bs-dropzone .import-header{margin-bottom:5px}.bs-dropzone .glyphicon-download-alt{font-size:40px}.bs-dropzone hr{width:100px}.bs-dropzone .lead{margin-bottom:10px;font-weight:400;color:#333}#import-manual-trigger{cursor:pointer}.bs-dropzone p:last-child{margin-bottom:0}.bs-brand-logos{display:table;width:100%;margin-bottom:15px;overflow:hidden;color:#1b6ec1;background-color:#f9f9f9;border-radius:4px}.bs-brand-item{padding:60px 0;text-align:center}.bs-brand-item+.bs-brand-item{border-top:1px solid #fff}.bs-brand-logos .inverse{color:#fff;background-color:#1b6ec1}.bs-brand-item h1,.bs-brand-item h3{margin-top:0;margin-bottom:0}.bs-brand-item .bs-docs-booticon{margin-right:auto;margin-left:auto}.bs-brand-item .glyphicon{width:30px;height:30px;margin:10px auto -10px;line-height:30px;color:#fff;border-radius:50%}.bs-brand-item .glyphicon-ok{background-color:#5cb85c}.bs-brand-item .glyphicon-remove{background-color:#d9534f}@media (min-width:768px){.bs-brand-item{display:table-cell;width:1%}.bs-brand-item+.bs-brand-item{border-top:0;border-left:1px solid #fff}.bs-brand-item h1{font-size:60px}}.zero-clipboard{position:relative;display:none}.btn-clipboard{position:absolute;top:0;right:0;z-index:10;display:block;padding:5px 8px;font-size:12px;color:#777;cursor:pointer;background-color:#fff;border:1px solid #e1e1e8;border-radius:0 4px 0 4px}.btn-clipboard-hover{color:#fff;background-color:#563d7c;border-color:#563d7c}@media (min-width:768px){.zero-clipboard{display:block}.bs-example+.zero-clipboard .btn-clipboard{top:-16px;border-top-right-radius:0}}.anchorjs-link{color:inherit}@media (max-width:480px){.anchorjs-link{display:none}}:hover>.anchorjs-link{opacity:.75;-webkit-transition:color .16s linear;-o-transition:color .16s linear;transition:color .16s linear}.anchorjs-link:focus,:hover>.anchorjs-link:hover{text-decoration:none;opacity:1}#focusedInput{border:1px solid #4d90fe!important;outline:0;outline:thin dotted\9;-webkit-box-shadow:none;box-shadow:none}.v4-tease{position:fixed;top:0;right:0;left:0;z-index:1030;display:block;padding:15px 20px;font-weight:700;color:#fff;text-align:center;background-color:#1b6ec1}.v4-tease:hover{color:#fff;text-decoration:none;background-color:#2d87e2}@media print{a[href]:after{content:""!important}}.bs-docs-navbar-masthead{top:48px}.bs-docs-dl-options h4{margin-top:15px;margin-bottom:5px} +/*# sourceMappingURL=docs.min.css.map */ \ No newline at end of file diff --git a/src/blog/static/assets/css/ie10-viewport-bug-workaround.css b/src/blog/static/assets/css/ie10-viewport-bug-workaround.css new file mode 100644 index 0000000..4b9518e --- /dev/null +++ b/src/blog/static/assets/css/ie10-viewport-bug-workaround.css @@ -0,0 +1,13 @@ +/*! + * IE10 viewport hack for Surface/desktop Windows 8 bug + * Copyright 2014-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +/* + * See the Getting Started docs for more information: + * http://getbootstrap.com/getting-started/#support-ie10-width + */ +@-ms-viewport { width: device-width; } +@-o-viewport { width: device-width; } +@viewport { width: device-width; } diff --git a/src/blog/static/assets/css/signin.css b/src/blog/static/assets/css/signin.css new file mode 100644 index 0000000..121fb0d --- /dev/null +++ b/src/blog/static/assets/css/signin.css @@ -0,0 +1,58 @@ +body { + padding-top: 40px; + padding-bottom: 40px; + background-color: #fff; +} + +.form-signin { + max-width: 330px; + padding: 15px; + margin: 0 auto; +} +.form-signin-heading { + margin: 0 0 15px; + font-size: 18px; + font-weight: 400; + color: #555; +} +.form-signin .checkbox { + margin-bottom: 10px; + font-weight: normal; +} +.form-signin .form-control { + position: relative; + height: auto; + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: 10px; + font-size: 16px; +} +.form-signin .form-control:focus { + z-index: 2; +} +.form-signin input[type="email"] { + margin-bottom: 10px; +} +.form-signin input[type="password"] { + margin-bottom: 10px; +} +.card { + width: 304px; + padding: 20px 25px 30px; + margin: 0 auto 25px; + background-color: #f7f7f7; + border-radius: 2px; + -webkit-box-shadow: 0 2px 2px rgba(0, 0, 0, .3); + box-shadow: 0 2px 2px rgba(0, 0, 0, .3); +} +.card-signin { + width: 354px; + padding: 40px; +} +.card-signin .profile-img { + display: block; + width: 96px; + height: 96px; + margin: 0 auto 10px; +} diff --git a/src/blog/static/assets/css/todc-bootstrap.min.css b/src/blog/static/assets/css/todc-bootstrap.min.css new file mode 100644 index 0000000..66c9cb2 --- /dev/null +++ b/src/blog/static/assets/css/todc-bootstrap.min.css @@ -0,0 +1,6 @@ +/*! + * TODC Bootstrap v3.3.7-3.3.7 (http://todc.github.com/todc-bootstrap/) + * Copyright 2011-2016 Tim O'Donnell + * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license + */.panel-group .panel-heading a.collapsed:before,.panel-group .panel-heading a:before{position:relative;top:1px;display:inline-block;font-family:'Glyphicons Halflings';font-style:normal;font-weight:400;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.caret-left,.caret-right,.collapse-caret.collapsed:before,.collapse-caret:before,.dropdown-submenu>a:after{display:inline-block;width:0;height:0;margin-left:2px;vertical-align:middle;border-top:4px dashed;border-top:4px solid\9;border-right:4px solid transparent;border-left:4px solid transparent}body{font-family:Arial,Helvetica,sans-serif;font-size:13px;line-height:1.4;color:#333;background-color:#fff}button,input,select,textarea{font-family:inherit;font-size:inherit;line-height:inherit}a{color:#15c}a:focus,a:hover{color:#15c}.img-rounded{border-radius:1px}.img-thumbnail{display:inline-block;max-width:100%;height:auto;padding:0;line-height:1.4;background-color:#fff;border:3px solid #fff;border-radius:0;-webkit-box-shadow:0 0 0 1px #aaa;box-shadow:0 0 0 1px #aaa;-webkit-transition:none;-o-transition:none;transition:none}.caret-left,.caret-right,.collapse-caret.collapsed:before,.dropdown-submenu>a:after{vertical-align:baseline;border-top:4px solid transparent;border-right:0 dotted;border-bottom:4px solid transparent;border-left:4px solid}.caret-left{margin-right:2px;margin-left:0;border-right:4px solid;border-left:0 dotted}.scrollable-shadow{background:-webkit-gradient(linear,left top,left bottom,color-stop(30%,#fff),to(rgba(255,255,255,0))),-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,0)),color-stop(70%,#fff)) 0 100%,radial-gradient(50% 0,farthest-side,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(50% 100%,farthest-side,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background:-webkit-linear-gradient(white 30%,rgba(255,255,255,0)),-webkit-linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,-webkit-radial-gradient(50% 0,farthest-side,rgba(0,0,0,.2),rgba(0,0,0,0)),-webkit-radial-gradient(50% 100%,farthest-side,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background:-o-linear-gradient(white 30%,rgba(255,255,255,0)),-o-linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,-o-radial-gradient(50% 0,farthest-side,rgba(0,0,0,.2),rgba(0,0,0,0)),-o-radial-gradient(50% 100%,farthest-side,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background:linear-gradient(white 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(50% 0,farthest-side,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(50% 100%,farthest-side,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background:-webkit-gradient(linear,left top,left bottom,color-stop(30%,#fff),to(rgba(255,255,255,0))),-webkit-gradient(linear,left top,left bottom,from(rgba(255,255,255,0)),color-stop(70%,#fff)) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background:linear-gradient(white 30%,rgba(255,255,255,0)),linear-gradient(rgba(255,255,255,0),#fff 70%) 0 100%,radial-gradient(farthest-side at 50% 0,rgba(0,0,0,.2),rgba(0,0,0,0)),radial-gradient(farthest-side at 50% 100%,rgba(0,0,0,.2),rgba(0,0,0,0)) 0 100%;background-repeat:no-repeat;background-attachment:local,local,scroll,scroll;-webkit-background-size:100% 40px,100% 40px,100% 6px,100% 6px;background-size:100% 40px,100% 40px,100% 6px,100% 6px}.mark,mark{background-color:#f9edbe}.text-primary{color:#4d90fe}a.text-primary:focus,a.text-primary:hover{color:#1a70fe}.text-warning{color:#333}a.text-warning:focus,a.text-warning:hover{color:#1a1a1a}.bg-primary{color:#fff;background-color:#4d90fe}a.bg-primary:focus,a.bg-primary:hover{background-color:#1a70fe}.bg-warning{background-color:#f9edbe}a.bg-warning:focus,a.bg-warning:hover{background-color:#f5e08f}code{padding:2px 4px;border-radius:0}kbd{border-radius:1px}pre{padding:9px;margin:0 0 9px;font-size:12px;line-height:1.4;border-radius:0}table{background-color:transparent}caption{color:#999}.table{margin-bottom:18px}.table>tbody>tr>td,.table>tbody>tr>th,.table>tfoot>tr>td,.table>tfoot>tr>th,.table>thead>tr>td,.table>thead>tr>th{line-height:1.4;border-top:1px solid #ddd}.table>thead>tr>th{border-bottom:2px solid #ddd}.table>tbody+tbody{border-top:2px solid #ddd}.table .table{background-color:#fff}.table-bordered{border:1px solid #ddd}.table-bordered>tbody>tr>td,.table-bordered>tbody>tr>th,.table-bordered>tfoot>tr>td,.table-bordered>tfoot>tr>th,.table-bordered>thead>tr>td,.table-bordered>thead>tr>th{border:1px solid #ddd}.table-striped>tbody>tr:nth-of-type(odd){background-color:#f9f9f9}.table-hover>tbody>tr:hover{background-color:#ffc}.table>tbody>tr.warning>td,.table>tbody>tr.warning>th,.table>tbody>tr>td.warning,.table>tbody>tr>th.warning,.table>tfoot>tr.warning>td,.table>tfoot>tr.warning>th,.table>tfoot>tr>td.warning,.table>tfoot>tr>th.warning,.table>thead>tr.warning>td,.table>thead>tr.warning>th,.table>thead>tr>td.warning,.table>thead>tr>th.warning{background-color:#f9edbe}.table-hover>tbody>tr.warning:hover>td,.table-hover>tbody>tr.warning:hover>th,.table-hover>tbody>tr:hover>.warning,.table-hover>tbody>tr>td.warning:hover,.table-hover>tbody>tr>th.warning:hover{background-color:#f7e7a7}@media screen and (max-width:767px){.table-responsive{margin-bottom:13.5px;border:1px solid #ddd}}legend{margin-bottom:18px;font-size:19.5px}input[type=radio],input[type=checkbox]{margin:2px 0 0}output{padding-top:6px;font-size:13px;line-height:1.4;color:#555}.form-control{height:30px;-webkit-appearance:none;padding:5px 8px;font-size:13px;line-height:1.4;background-color:#fff;border:1px solid #d9d9d9;border-top-color:silver;border-radius:2px;-webkit-box-shadow:none;box-shadow:none;-webkit-transition:none;-o-transition:none;transition:none}.form-control:hover{border:1px solid #b9b9b9;border-top-color:#a0a0a0;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.form-control:focus{border-color:#4d90fe;outline:0;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(77,144,254,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(77,144,254,.6)}.form-control:focus{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.form-control::-ms-expand{background-color:transparent}.form-control[disabled],.form-control[readonly],fieldset[disabled] .form-control{background-color:#f1f1f1;border:1px solid #e5e5e5}.form-control[disabled]:active,.form-control[disabled]:focus,.form-control[disabled]:hover,.form-control[readonly]:active,.form-control[readonly]:focus,.form-control[readonly]:hover,fieldset[disabled] .form-control:active,fieldset[disabled] .form-control:focus,fieldset[disabled] .form-control:hover{border:1px solid #e5e5e5;-webkit-box-shadow:none;box-shadow:none}.form-control[readonly] .form-control{border:1px solid #d9d9d9}.form-control[readonly] .form-control:active,.form-control[readonly] .form-control:focus,.form-control[readonly] .form-control:hover{border:1px solid #d9d9d9}textarea.form-control{padding-right:4px}@media screen and (-webkit-min-device-pixel-ratio:0){input[type=date].form-control,input[type=time].form-control,input[type=datetime-local].form-control,input[type=month].form-control{line-height:30px}.input-group-sm input[type=date],.input-group-sm input[type=time],.input-group-sm input[type=datetime-local],.input-group-sm input[type=month],input[type=date].input-sm,input[type=time].input-sm,input[type=datetime-local].input-sm,input[type=month].input-sm{line-height:26px}.input-group-lg input[type=date],.input-group-lg input[type=time],.input-group-lg input[type=datetime-local],.input-group-lg input[type=month],input[type=date].input-lg,input[type=time].input-lg,input[type=datetime-local].input-lg,input[type=month].input-lg{line-height:38px}}.checkbox label,.radio label{min-height:18px}.checkbox input[type=checkbox],.checkbox-inline input[type=checkbox],.radio input[type=radio],.radio-inline input[type=radio],input[type=radio],input[type=checkbox]{position:relative;width:13px;width:16px\9;height:13px;height:16px\9;-webkit-appearance:none;background:#fff;border:1px solid #dcdcdc;border:1px solid transparent\9;border-radius:1px}.checkbox input[type=checkbox]:focus,.checkbox-inline input[type=checkbox]:focus,.radio input[type=radio]:focus,.radio-inline input[type=radio]:focus,input[type=radio]:focus,input[type=checkbox]:focus{border-color:#4d90fe;outline:0}.checkbox input[type=checkbox]:active,.checkbox-inline input[type=checkbox]:active,.radio input[type=radio]:active,.radio-inline input[type=radio]:active,input[type=radio]:active,input[type=checkbox]:active{background-color:#ebebeb;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffffffff', GradientType=0);border-color:#c6c6c6}.checkbox input[type=checkbox]:checked,.checkbox-inline input[type=checkbox]:checked,.radio input[type=radio]:checked,.radio-inline input[type=radio]:checked,input[type=radio]:checked,input[type=checkbox]:checked{background:#fff}.radio input[type=radio],.radio-inline input[type=radio],input[type=radio]{width:15px;width:18px\9;height:15px;height:18px\9;border-radius:1em}.radio input[type=radio]:checked::after,.radio-inline input[type=radio]:checked::after,input[type=radio]:checked::after{position:relative;top:3px;left:3px;display:block;width:7px;height:7px;content:'';background:#666;border-radius:1em}.checkbox input[type=checkbox]:hover,.checkbox-inline input[type=checkbox]:hover,input[type=checkbox]:hover{border-color:#c6c6c6;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.1);-webkit-box-shadow:none\9;box-shadow:inset 0 1px 1px rgba(0,0,0,.1);box-shadow:none\9}.checkbox input[type=checkbox]:checked::after,.checkbox-inline input[type=checkbox]:checked::after,input[type=checkbox]:checked::after{position:absolute;top:-6px;left:-5px;display:block;content:url(../img/checkmark.png)}.form-control-static{min-height:31px;padding-top:6px;padding-bottom:6px}.input-sm{height:26px;padding:3px 8px;font-size:12px;line-height:1.5;border-radius:1px}select.input-sm{height:26px;line-height:26px}select[multiple].input-sm,textarea.input-sm{height:auto}.form-group-sm .form-control{height:26px;padding:3px 8px;font-size:12px;line-height:1.5;border-radius:1px}.form-group-sm select.form-control{height:26px;line-height:26px}.form-group-sm .form-control-static{height:26px;min-height:30px;padding:4px 8px;font-size:12px;line-height:1.5}.input-lg{height:38px;padding:9px 14px;font-size:14px;line-height:1.3;border-radius:1px}select.input-lg{height:38px;line-height:38px}select[multiple].input-lg,textarea.input-lg{height:auto}.form-group-lg .form-control{height:38px;padding:9px 14px;font-size:14px;line-height:1.3;border-radius:1px}.form-group-lg select.form-control{height:38px;line-height:38px}.form-group-lg .form-control-static{height:38px;min-height:32px;padding:10px 14px;font-size:14px;line-height:1.3}.has-feedback .form-control{padding-right:37.5px}.form-control-feedback{top:23px;width:30px;height:30px;line-height:30px}.form-group-lg .form-control+.form-control-feedback,.input-group-lg+.form-control-feedback,.input-lg+.form-control-feedback{width:38px;height:38px;line-height:38px}.form-group-sm .form-control+.form-control-feedback,.input-group-sm+.form-control-feedback,.input-sm+.form-control-feedback{width:26px;height:26px;line-height:26px}.has-success .checkbox,.has-success .checkbox-inline,.has-success .control-label,.has-success .help-block,.has-success .radio,.has-success .radio-inline,.has-success.checkbox label,.has-success.checkbox-inline label,.has-success.radio label,.has-success.radio-inline label{color:#3c763d}.has-success .form-control{border-color:#3c763d;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-success .form-control:focus{border-color:#2b542c;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #67b168}.has-success .input-group-addon{color:#3c763d;background-color:#dff0d8;border-color:#3c763d}.has-success .form-control-feedback{color:#3c763d}.has-success .form-control{-webkit-box-shadow:none;box-shadow:none}.has-success .form-control:hover{border-color:#3c763d;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.1) inset;box-shadow:0 1px 2px rgba(0,0,0,.1) inset}.has-success .form-control:focus{border-color:#3c763d;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.3) inset;box-shadow:0 1px 2px rgba(0,0,0,.3) inset}.has-warning .checkbox,.has-warning .checkbox-inline,.has-warning .control-label,.has-warning .help-block,.has-warning .radio,.has-warning .radio-inline,.has-warning.checkbox label,.has-warning.checkbox-inline label,.has-warning.radio label,.has-warning.radio-inline label{color:#e09b17}.has-warning .form-control{border-color:#e09b17;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-warning .form-control:focus{border-color:#b27b12;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #f0c36d;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #f0c36d}.has-warning .input-group-addon{color:#e09b17;background-color:#f9edbe;border-color:#e09b17}.has-warning .form-control-feedback{color:#e09b17}.has-warning .form-control{-webkit-box-shadow:none;box-shadow:none}.has-warning .form-control:hover{border-color:#e09b17;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.1) inset;box-shadow:0 1px 2px rgba(0,0,0,.1) inset}.has-warning .form-control:focus{border-color:#e09b17;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.3) inset;box-shadow:0 1px 2px rgba(0,0,0,.3) inset}.has-error .checkbox,.has-error .checkbox-inline,.has-error .control-label,.has-error .help-block,.has-error .radio,.has-error .radio-inline,.has-error.checkbox label,.has-error.checkbox-inline label,.has-error.radio label,.has-error.radio-inline label{color:#dd4b39}.has-error .form-control{border-color:#dd4b39;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 1px rgba(0,0,0,.075)}.has-error .form-control:focus{border-color:#c23321;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ec9a90;box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 6px #ec9a90}.has-error .input-group-addon{color:#dd4b39;background-color:#f2dede;border-color:#dd4b39}.has-error .form-control-feedback{color:#dd4b39}.has-error .form-control{-webkit-box-shadow:none;box-shadow:none}.has-error .form-control:hover{border-color:#dd4b39;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.1) inset;box-shadow:0 1px 2px rgba(0,0,0,.1) inset}.has-error .form-control:focus{border-color:#dd4b39;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.3) inset;box-shadow:0 1px 2px rgba(0,0,0,.3) inset}.has-feedback label~.form-control-feedback{top:23px}.help-block{color:#777}.form-horizontal .checkbox-inline,.form-horizontal .control-label,.form-horizontal .radio-inline{padding-top:5px}@media (min-width:768px){.form-inline .form-group,.navbar-form .form-group{display:inline-block;margin-bottom:0;vertical-align:middle}.form-inline .form-control,.navbar-form .form-control{display:inline-block;width:auto;vertical-align:middle}.form-inline .form-control-static,.navbar-form .form-control-static{display:inline-block}.form-inline .input-group,.navbar-form .input-group{display:inline-table;vertical-align:middle}.form-inline .input-group .form-control,.form-inline .input-group .input-group-addon,.form-inline .input-group .input-group-btn,.navbar-form .input-group .form-control,.navbar-form .input-group .input-group-addon,.navbar-form .input-group .input-group-btn{width:auto}.form-inline .input-group>.form-control,.navbar-form .input-group>.form-control{width:100%}.form-inline .control-label,.navbar-form .control-label{margin-bottom:0;vertical-align:middle}.form-inline .checkbox,.form-inline .radio,.navbar-form .checkbox,.navbar-form .radio{display:inline-block;margin-top:0;margin-bottom:0;vertical-align:middle}.form-inline .checkbox label,.form-inline .radio label,.navbar-form .checkbox label,.navbar-form .radio label{padding-left:0}.form-inline .checkbox input[type=checkbox],.form-inline .radio input[type=radio],.navbar-form .checkbox input[type=checkbox],.navbar-form .radio input[type=radio]{position:relative;margin-bottom:-2px;margin-left:0}.form-inline .has-feedback .form-control-feedback,.navbar-form .has-feedback .form-control-feedback{top:0}}.form-horizontal .checkbox,.form-horizontal .checkbox-inline,.form-horizontal .radio,.form-horizontal .radio-inline{padding-top:6px}.form-horizontal .checkbox,.form-horizontal .radio{min-height:24px}@media (min-width:768px){.form-horizontal .control-label{padding-top:6px}.form-horizontal .has-feedback .form-control-feedback{top:0}}@media (min-width:768px){.form-horizontal .form-group-lg .control-label{padding-top:10px;font-size:14px}}@media (min-width:768px){.form-horizontal .form-group-sm .control-label{padding-top:4px;font-size:12px}}.btn{padding:5px 12px;font-size:13px;font-weight:700;line-height:18px;cursor:default;-webkit-background-clip:border-box;background-clip:border-box;border-radius:2px;-webkit-box-shadow:none;box-shadow:none}.btn:hover{-webkit-box-shadow:0 1px 1px rgba(0,0,0,.1);box-shadow:0 1px 1px rgba(0,0,0,.1)}.btn.active,.btn:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.btn-default{color:#333;text-shadow:0 1px rgba(0,0,0,.1);text-shadow:0 1px 0 #fff;background-color:#f3f3f3;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#f1f1f1 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#f1f1f1 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#f1f1f1));background-image:linear-gradient(to bottom,#f5f5f5 0,#f1f1f1 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff1f1f1', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #dcdcdc}.btn-default:hover{text-shadow:0 1px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 1px rgba(0,0,0,.2);box-shadow:0 1px 1px rgba(0,0,0,.2)}.btn-default.active,.btn-default.focus,.btn-default:active,.btn-default:focus,.btn-default:hover,.open>.dropdown-toggle.btn-default{color:#333;background-color:#e4e4e4;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e4e4e4 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e4e4e4 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e4e4e4));background-image:linear-gradient(to bottom,#f5f5f5 0,#e4e4e4 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe4e4e4', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #cfcfcf}.btn-default.active,.btn-default:active,.open>.dropdown-toggle.btn-default{text-shadow:0 1px rgba(0,0,0,.3);background-image:-webkit-linear-gradient(top,#f5f5f5 0,#d8d8d8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#d8d8d8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#d8d8d8));background-image:linear-gradient(to bottom,#f5f5f5 0,#d8d8d8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffd8d8d8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #c3c3c3;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-default.focus,.btn-default:focus{border:1px solid #dcdcdc;-webkit-box-shadow:inset 0 0 0 1px #fff;box-shadow:inset 0 0 0 1px #fff}.btn-default.disabled.focus,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{background-color:#f5f5f5;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#f1f1f1 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#f1f1f1 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#f1f1f1));background-image:linear-gradient(to bottom,#f5f5f5 0,#f1f1f1 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff1f1f1', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #dcdcdc;-webkit-box-shadow:none;box-shadow:none}.btn-default .badge{color:#dcdcdc;background-color:#333}.btn-default:hover{text-shadow:none;background-image:-webkit-linear-gradient(top,#f8f8f8 0,#f1f1f1 100%);background-image:-o-linear-gradient(top,#f8f8f8 0,#f1f1f1 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f8f8f8),to(#f1f1f1));background-image:linear-gradient(to bottom,#f8f8f8 0,#f1f1f1 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff8f8f8', endColorstr='#fff1f1f1', GradientType=0);background-repeat:repeat-x;background-position:0 0;border-color:#c6c6c6;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.1);box-shadow:0 1px 1px rgba(0,0,0,.1);-webkit-transition:none;-o-transition:none;transition:none}.btn-default.active,.btn-default:active,.open .dropdown-toggle.btn-default{text-shadow:0 1px 0 #fff;background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f6f6f6 0,#f1f1f1 100%);background-image:-o-linear-gradient(top,#f6f6f6 0,#f1f1f1 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f6f6f6),to(#f1f1f1));background-image:linear-gradient(to bottom,#f6f6f6 0,#f1f1f1 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff6f6f6', endColorstr='#fff1f1f1', GradientType=0);background-repeat:repeat-x;border:1px solid #dcdcdc;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.btn-default.focus,.btn-default:focus{background-color:#f3f3f3;border-color:#4d90fe;outline-style:none}.btn-default.disabled.focus,.btn-default.disabled:active,.btn-default.disabled:focus,.btn-default.disabled:hover,.btn-default[disabled].focus,.btn-default[disabled]:active,.btn-default[disabled]:focus,.btn-default[disabled]:hover,fieldset[disabled] .btn-default.focus,fieldset[disabled] .btn-default:active,fieldset[disabled] .btn-default:focus,fieldset[disabled] .btn-default:hover{text-shadow:none;background-color:#f3f3f3}.btn-default .badge{color:#f3f3f3;text-shadow:none}.btn-primary{color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-image:-webkit-linear-gradient(top,#4d90fe 0,#4787ed 100%);background-image:-o-linear-gradient(top,#4d90fe 0,#4787ed 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#4787ed));background-image:linear-gradient(to bottom,#4d90fe 0,#4787ed 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d90fe', endColorstr='#ff4787ed', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #3079ed}.btn-primary:hover{text-shadow:0 1px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 1px rgba(0,0,0,.2);box-shadow:0 1px 1px rgba(0,0,0,.2)}.btn-primary.active,.btn-primary.focus,.btn-primary:active,.btn-primary:focus,.btn-primary:hover,.open>.dropdown-toggle.btn-primary{color:#fff;background-color:#3078eb;background-image:-webkit-linear-gradient(top,#4d90fe 0,#3078eb 100%);background-image:-o-linear-gradient(top,#4d90fe 0,#3078eb 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#3078eb));background-image:linear-gradient(to bottom,#4d90fe 0,#3078eb 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d90fe', endColorstr='#ff3078eb', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #196aeb}.btn-primary.active,.btn-primary:active,.open>.dropdown-toggle.btn-primary{text-shadow:0 1px rgba(0,0,0,.3);background-image:-webkit-linear-gradient(top,#4d90fe 0,#1969e8 100%);background-image:-o-linear-gradient(top,#4d90fe 0,#1969e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#1969e8));background-image:linear-gradient(to bottom,#4d90fe 0,#1969e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d90fe', endColorstr='#ff1969e8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #135fd7;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-primary.focus,.btn-primary:focus{border:1px solid #3079ed;-webkit-box-shadow:inset 0 0 0 1px #fff;box-shadow:inset 0 0 0 1px #fff}.btn-primary.disabled.focus,.btn-primary.disabled:focus,.btn-primary.disabled:hover,.btn-primary[disabled].focus,.btn-primary[disabled]:focus,.btn-primary[disabled]:hover,fieldset[disabled] .btn-primary.focus,fieldset[disabled] .btn-primary:focus,fieldset[disabled] .btn-primary:hover{background-color:#4d90fe;background-image:-webkit-linear-gradient(top,#4d90fe 0,#4787ed 100%);background-image:-o-linear-gradient(top,#4d90fe 0,#4787ed 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#4d90fe),to(#4787ed));background-image:linear-gradient(to bottom,#4d90fe 0,#4787ed 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff4d90fe', endColorstr='#ff4787ed', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #3079ed;-webkit-box-shadow:none;box-shadow:none}.btn-primary .badge{color:#3079ed;background-color:#fff}.btn-success{color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-image:-webkit-linear-gradient(top,#35aa47 0,#35aa47 100%);background-image:-o-linear-gradient(top,#35aa47 0,#35aa47 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#35aa47),to(#35aa47));background-image:linear-gradient(to bottom,#35aa47 0,#35aa47 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff35aa47', endColorstr='#ff35aa47', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #359947}.btn-success:hover{text-shadow:0 1px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 1px rgba(0,0,0,.2);box-shadow:0 1px 1px rgba(0,0,0,.2)}.btn-success.active,.btn-success.focus,.btn-success:active,.btn-success:focus,.btn-success:hover,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#2f973f;background-image:-webkit-linear-gradient(top,#35aa47 0,#2f973f 100%);background-image:-o-linear-gradient(top,#35aa47 0,#2f973f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#35aa47),to(#2f973f));background-image:linear-gradient(to bottom,#35aa47 0,#2f973f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff35aa47', endColorstr='#ff2f973f', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #2e863e}.btn-success.active,.btn-success:active,.open>.dropdown-toggle.btn-success{text-shadow:0 1px rgba(0,0,0,.3);background-image:-webkit-linear-gradient(top,#35aa47 0,#298337 100%);background-image:-o-linear-gradient(top,#35aa47 0,#298337 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#35aa47),to(#298337));background-image:linear-gradient(to bottom,#35aa47 0,#298337 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff35aa47', endColorstr='#ff298337', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #287335;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-success.focus,.btn-success:focus{border:1px solid #359947;-webkit-box-shadow:inset 0 0 0 1px #fff;box-shadow:inset 0 0 0 1px #fff}.btn-success.disabled.focus,.btn-success.disabled:focus,.btn-success.disabled:hover,.btn-success[disabled].focus,.btn-success[disabled]:focus,.btn-success[disabled]:hover,fieldset[disabled] .btn-success.focus,fieldset[disabled] .btn-success:focus,fieldset[disabled] .btn-success:hover{background-color:#35aa47;background-image:-webkit-linear-gradient(top,#35aa47 0,#35aa47 100%);background-image:-o-linear-gradient(top,#35aa47 0,#35aa47 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#35aa47),to(#35aa47));background-image:linear-gradient(to bottom,#35aa47 0,#35aa47 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff35aa47', endColorstr='#ff35aa47', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #359947;-webkit-box-shadow:none;box-shadow:none}.btn-success .badge{color:#359947;background-color:#fff}.btn-info{color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-image:-webkit-linear-gradient(top,#5bc0de 0,#5bc0de 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#5bc0de 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#5bc0de));background-image:linear-gradient(to bottom,#5bc0de 0,#5bc0de 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff5bc0de', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #46b8da}.btn-info:hover{text-shadow:0 1px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 1px rgba(0,0,0,.2);box-shadow:0 1px 1px rgba(0,0,0,.2)}.btn-info.active,.btn-info.focus,.btn-info:active,.btn-info:focus,.btn-info:hover,.open>.dropdown-toggle.btn-info{color:#fff;background-color:#46b8da;background-image:-webkit-linear-gradient(top,#5bc0de 0,#46b8da 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#46b8da 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#46b8da));background-image:linear-gradient(to bottom,#5bc0de 0,#46b8da 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff46b8da', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #31b0d5}.btn-info.active,.btn-info:active,.open>.dropdown-toggle.btn-info{text-shadow:0 1px rgba(0,0,0,.3);background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #28a1c5;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-info.focus,.btn-info:focus{border:1px solid #46b8da;-webkit-box-shadow:inset 0 0 0 1px #fff;box-shadow:inset 0 0 0 1px #fff}.btn-info.disabled.focus,.btn-info.disabled:focus,.btn-info.disabled:hover,.btn-info[disabled].focus,.btn-info[disabled]:focus,.btn-info[disabled]:hover,fieldset[disabled] .btn-info.focus,fieldset[disabled] .btn-info:focus,fieldset[disabled] .btn-info:hover{background-color:#5bc0de;background-image:-webkit-linear-gradient(top,#5bc0de 0,#5bc0de 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#5bc0de 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#5bc0de));background-image:linear-gradient(to bottom,#5bc0de 0,#5bc0de 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff5bc0de', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #46b8da;-webkit-box-shadow:none;box-shadow:none}.btn-info .badge{color:#46b8da;background-color:#fff}.btn-warning{color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-image:-webkit-linear-gradient(top,#fbb450 0,#faa937 100%);background-image:-o-linear-gradient(top,#fbb450 0,#faa937 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fbb450),to(#faa937));background-image:linear-gradient(to bottom,#fbb450 0,#faa937 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fffaa937', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #faa328}.btn-warning:hover{text-shadow:0 1px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 1px rgba(0,0,0,.2);box-shadow:0 1px 1px rgba(0,0,0,.2)}.btn-warning.active,.btn-warning.focus,.btn-warning:active,.btn-warning:focus,.btn-warning:hover,.open>.dropdown-toggle.btn-warning{color:#fff;background-color:#f99e1e;background-image:-webkit-linear-gradient(top,#fbb450 0,#f99e1e 100%);background-image:-o-linear-gradient(top,#fbb450 0,#f99e1e 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fbb450),to(#f99e1e));background-image:linear-gradient(to bottom,#fbb450 0,#f99e1e 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff99e1e', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #f9980f}.btn-warning.active,.btn-warning:active,.open>.dropdown-toggle.btn-warning{text-shadow:0 1px rgba(0,0,0,.3);background-image:-webkit-linear-gradient(top,#fbb450 0,#f89306 100%);background-image:-o-linear-gradient(top,#fbb450 0,#f89306 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fbb450),to(#f89306));background-image:linear-gradient(to bottom,#fbb450 0,#f89306 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fff89306', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #e98b06;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-warning.focus,.btn-warning:focus{border:1px solid #faa328;-webkit-box-shadow:inset 0 0 0 1px #fff;box-shadow:inset 0 0 0 1px #fff}.btn-warning.disabled.focus,.btn-warning.disabled:focus,.btn-warning.disabled:hover,.btn-warning[disabled].focus,.btn-warning[disabled]:focus,.btn-warning[disabled]:hover,fieldset[disabled] .btn-warning.focus,fieldset[disabled] .btn-warning:focus,fieldset[disabled] .btn-warning:hover{background-color:#fbb450;background-image:-webkit-linear-gradient(top,#fbb450 0,#faa937 100%);background-image:-o-linear-gradient(top,#fbb450 0,#faa937 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fbb450),to(#faa937));background-image:linear-gradient(to bottom,#fbb450 0,#faa937 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffbb450', endColorstr='#fffaa937', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #faa328;-webkit-box-shadow:none;box-shadow:none}.btn-warning .badge{color:#faa328;background-color:#fff}.btn-danger{color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-image:-webkit-linear-gradient(top,#dd4b39 0,#d14836 100%);background-image:-o-linear-gradient(top,#dd4b39 0,#d14836 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#d14836));background-image:linear-gradient(to bottom,#dd4b39 0,#d14836 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd4b39', endColorstr='#ffd14836', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #c6322a}.btn-danger:hover{text-shadow:0 1px rgba(0,0,0,.3);-webkit-box-shadow:0 1px 1px rgba(0,0,0,.2);box-shadow:0 1px 1px rgba(0,0,0,.2)}.btn-danger.active,.btn-danger.focus,.btn-danger:active,.btn-danger:focus,.btn-danger:hover,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c13e2c;background-image:-webkit-linear-gradient(top,#dd4b39 0,#c13e2c 100%);background-image:-o-linear-gradient(top,#dd4b39 0,#c13e2c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#c13e2c));background-image:linear-gradient(to bottom,#dd4b39 0,#c13e2c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd4b39', endColorstr='#ffc13e2c', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #b12d26}.btn-danger.active,.btn-danger:active,.open>.dropdown-toggle.btn-danger{text-shadow:0 1px rgba(0,0,0,.3);background-image:-webkit-linear-gradient(top,#dd4b39 0,#ad3727 100%);background-image:-o-linear-gradient(top,#dd4b39 0,#ad3727 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#ad3727));background-image:linear-gradient(to bottom,#dd4b39 0,#ad3727 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd4b39', endColorstr='#ffad3727', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #9c2721;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-danger.focus,.btn-danger:focus{border:1px solid #c6322a;-webkit-box-shadow:inset 0 0 0 1px #fff;box-shadow:inset 0 0 0 1px #fff}.btn-danger.disabled.focus,.btn-danger.disabled:focus,.btn-danger.disabled:hover,.btn-danger[disabled].focus,.btn-danger[disabled]:focus,.btn-danger[disabled]:hover,fieldset[disabled] .btn-danger.focus,fieldset[disabled] .btn-danger:focus,fieldset[disabled] .btn-danger:hover{background-color:#dd4b39;background-image:-webkit-linear-gradient(top,#dd4b39 0,#d14836 100%);background-image:-o-linear-gradient(top,#dd4b39 0,#d14836 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dd4b39),to(#d14836));background-image:linear-gradient(to bottom,#dd4b39 0,#d14836 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdd4b39', endColorstr='#ffd14836', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border:1px solid #c6322a;-webkit-box-shadow:none;box-shadow:none}.btn-danger .badge{color:#c6322a;background-color:#fff}.btn-link{color:#15c}.btn-link,.btn-link.active,.btn-link:active,.btn-link[disabled],fieldset[disabled] .btn-link{background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link,.btn-link.focus,.btn-link:active,.btn-link:focus,.btn-link:hover{border-color:transparent}.btn-link.focus,.btn-link:focus,.btn-link:hover{color:#15c;background-color:transparent;-webkit-box-shadow:none;box-shadow:none}.btn-link[disabled]:focus .btn-link[disabled].focus,.btn-link[disabled]:focus fieldset[disabled] .btn-link.focus,.btn-link[disabled]:hover,fieldset[disabled] .btn-link:focus .btn-link[disabled].focus,fieldset[disabled] .btn-link:focus fieldset[disabled] .btn-link.focus,fieldset[disabled] .btn-link:hover{color:#333}.btn-group-lg>.btn,.btn-lg{padding:9px 14px;font-size:14px;line-height:1.3;border-radius:2px}.btn-group-sm>.btn,.btn-sm{padding:3px 8px;font-size:12px;line-height:1.5;border-radius:2px}.btn-group-xs>.btn,.btn-xs{padding:2px 6px;font-size:11px;line-height:1.25;border-radius:1px}.dropdown-menu{padding:6px 0;margin:1px 0 0;font-size:13px;background-color:#fff;border:1px solid #ccc;border:1px solid rgba(0,0,0,.2);border-radius:0;-webkit-box-shadow:0 2px 4px rgba(0,0,0,.2);box-shadow:0 2px 4px rgba(0,0,0,.2)}.dropdown-menu .divider{height:1px;margin:8px 0;overflow:hidden;background-color:#ebebeb}.dropdown-menu>li>a{position:relative;padding:3px 30px}.dropdown-menu>li>a .glyphicon{position:absolute;top:4px;left:7px}.dropdown-menu li>a:focus,.dropdown-menu li>a:hover,.dropdown-submenu:focus>a,.dropdown-submenu:hover>a{color:#333;background-color:#eee;background-image:-webkit-linear-gradient(top,#eee 0,#eee 100%);background-image:-o-linear-gradient(top,#eee 0,#eee 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#eee));background-image:linear-gradient(to bottom,#eee 0,#eee 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffeeeeee', endColorstr='#ffeeeeee', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:focus,.dropdown-menu>.active>a:hover{color:#333;background-color:#eee;background-image:-webkit-linear-gradient(top,#eee 0,#eee 100%);background-image:-o-linear-gradient(top,#eee 0,#eee 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#eee));background-image:linear-gradient(to bottom,#eee 0,#eee 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffeeeeee', endColorstr='#ffeeeeee', GradientType=0);background-repeat:repeat-x}.dropdown-header{color:#999}.dropdown-submenu{position:relative}.dropdown-submenu>.dropdown-menu{top:0;left:100%;margin-left:-1px;border-radius:0}.dropdown-submenu:hover>.dropdown-menu{display:block}.dropup .dropdown-submenu>.dropdown-menu{top:auto;bottom:0;margin-top:0;margin-bottom:-2px;border-radius:0}.dropdown-submenu>a:after{position:absolute;right:10px;margin-top:5px;content:""}.dropdown-submenu.dropdown-menu-left,.dropdown-submenu.pull-left{float:none!important}.dropdown-submenu.dropdown-menu-left>.dropdown-menu,.dropdown-submenu.pull-left>.dropdown-menu{left:-100%;margin-left:18px;border-radius:0}.btn-group-vertical>.btn:focus,.btn-group>.btn:focus{z-index:3}.btn-toolbar>.btn,.btn-toolbar>.btn-group,.btn-toolbar>.input-group{margin-left:16px}.btn-group>.btn+.dropdown-toggle{-webkit-box-shadow:none;box-shadow:none}.btn-group>.dropdown-toggle:hover{-webkit-box-shadow:0 1px 1px rgba(0,0,0,.1);box-shadow:0 1px 1px rgba(0,0,0,.1)}.btn-group>.btn-danger.dropdown-toggle:hover,.btn-group>.btn-info.dropdown-toggle:hover,.btn-group>.btn-primary.dropdown-toggle:hover,.btn-group>.btn-success.dropdown-toggle:hover,.btn-group>.btn-warning.dropdown-toggle:hover{-webkit-box-shadow:0 1px 1px rgba(0,0,0,.2);box-shadow:0 1px 1px rgba(0,0,0,.2)}.btn-group>.btn.dropdown-toggle.active,.btn-group>.btn.dropdown-toggle:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.btn-group>.btn-danger.dropdown-toggle.active,.btn-group>.btn-danger.dropdown-toggle:active,.btn-group>.btn-info.dropdown-toggle.active,.btn-group>.btn-info.dropdown-toggle:active,.btn-group>.btn-primary.dropdown-toggle.active,.btn-group>.btn-primary.dropdown-toggle:active,.btn-group>.btn-success.dropdown-toggle.active,.btn-group>.btn-success.dropdown-toggle:active,.btn-group>.btn-warning.dropdown-toggle.active,.btn-group>.btn-warning.dropdown-toggle:active{-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-group>.btn-sm.dropdown-toggle{padding:5px 7px}.btn-group>.btn-lg.dropdown-toggle{padding:9px 9px}.btn-group.open .dropdown-toggle{-webkit-box-shadow:inset 0 1px 6px rgba(0,0,0,.15);box-shadow:inset 0 1px 6px rgba(0,0,0,.15)}.btn-group.open .btn.dropdown-toggle{background-color:#f3f3f3;background-image:none;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.btn-group.open .btn-primary.dropdown-toggle{background-color:#4d90fe;background-image:none;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-group.open .btn-warning.dropdown-toggle{background-color:#faa937;background-image:none;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-group.open .btn-danger.dropdown-toggle{background-color:#d84a38;background-image:none;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-group.open .btn-success.dropdown-toggle{background-color:#35aa47;background-image:none;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-group.open .btn-info.dropdown-toggle{background-color:#5bc0de;background-image:none;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.3);box-shadow:inset 0 1px 2px rgba(0,0,0,.3)}.btn-lg .caret{border-width:5px 5px 0}.dropup .btn-lg .caret{border-width:0 5px 5px}.btn-group-vertical>.btn:first-child:not(:last-child){border-top-left-radius:2px;border-top-right-radius:2px}.btn-group-vertical>.btn:last-child:not(:first-child){border-bottom-right-radius:2px;border-bottom-left-radius:2px}.input-group-lg>.form-control,.input-group-lg>.input-group-addon,.input-group-lg>.input-group-btn>.btn{height:38px;padding:9px 14px;font-size:14px;line-height:1.3;border-radius:1px}select.input-group-lg>.form-control,select.input-group-lg>.input-group-addon,select.input-group-lg>.input-group-btn>.btn{height:38px;line-height:38px}select[multiple].input-group-lg>.form-control,select[multiple].input-group-lg>.input-group-addon,select[multiple].input-group-lg>.input-group-btn>.btn,textarea.input-group-lg>.form-control,textarea.input-group-lg>.input-group-addon,textarea.input-group-lg>.input-group-btn>.btn{height:auto}.input-group-sm>.form-control,.input-group-sm>.input-group-addon,.input-group-sm>.input-group-btn>.btn{height:26px;padding:3px 8px;font-size:12px;line-height:1.5;border-radius:1px}select.input-group-sm>.form-control,select.input-group-sm>.input-group-addon,select.input-group-sm>.input-group-btn>.btn{height:26px;line-height:26px}select[multiple].input-group-sm>.form-control,select[multiple].input-group-sm>.input-group-addon,select[multiple].input-group-sm>.input-group-btn>.btn,textarea.input-group-sm>.form-control,textarea.input-group-sm>.input-group-addon,textarea.input-group-sm>.input-group-btn>.btn{height:auto}.input-group .form-control,.input-group-addon,.input-group-btn{margin:0;border-radius:0}.input-group-addon{padding:5px 8px;font-size:13px;color:#555;border:1px solid #d9d9d9;border-top-color:silver;border-radius:2px}.input-group-addon.input-sm{padding:3px 8px;font-size:12px;border-radius:1px}.input-group-addon.input-lg{padding:9px 14px;font-size:14px;border-radius:1px}.input-group-addon input[type=radio],.input-group-addon input[type=checkbox]{margin-bottom:-3px}.nav>li.disabled>a{color:#999}.nav>li.disabled>a:focus,.nav>li.disabled>a:hover{color:#999}.nav .open>a,.nav .open>a:focus,.nav .open>a:hover{color:#fff;background-color:#999;border-color:#999}.nav-tabs>li>a{color:#666;border-radius:2px 2px 0 0}.nav-tabs>li>a:hover{border-color:#eee #eee #ddd}.nav-tabs>li.active>a,.nav-tabs>li.active>a:focus,.nav-tabs>li.active>a:hover{font-weight:700;color:#333}.nav-tabs-google>li{margin:0 -1px 0 0}.nav-tabs-google>li>a{padding:12px 8px;margin:0 8px;line-height:1.4;color:#777;border:3px solid transparent;border-width:3px 0;border-radius:0}.nav-tabs-google>li>a:first-of-type{margin-left:0}.nav-tabs-google>li>a:focus,.nav-tabs-google>li>a:hover{background-color:transparent;border-top-color:transparent}.nav-tabs-google>li>a:hover{color:#000;border-bottom-color:transparent}.nav-tabs-google>li>a:active{color:#dd4b39}.nav-tabs-google>li>a:focus{color:#000;outline:0}.nav-tabs-google>li.active>a,.nav-tabs-google>li.active>a:focus,.nav-tabs-google>li.active>a:hover{color:#dd4b39;border:3px solid transparent;border-width:3px 0;border-bottom-color:#dd4b39}.nav-pills>li>a{border-radius:2px}.nav-pills>li.active>a,.nav-pills>li.active>a:focus,.nav-pills>li.active>a:hover{color:#fff;background-color:#4d90fe}.navbar{min-height:28px;margin-bottom:18px}@media (min-width:768px){.navbar{border-radius:2px}}.navbar-brand{height:28px;padding:5px 15px;font-size:14px;line-height:18px}.navbar-brand>.glyphicon{margin-top:0}@media (min-width:768px){.navbar>.container .navbar-brand,.navbar>.container-fluid .navbar-brand{margin-left:-15px}}.navbar-toggle{padding:5px 10px;margin-top:1px;margin-right:15px;margin-bottom:1px;border-radius:2px}.navbar-nav{margin:2px -15px}.navbar-nav>li>a{padding-top:5px;padding-bottom:5px;line-height:18px}@media (max-width:767px){.navbar-nav .open .dropdown-menu>li>a{line-height:18px}}@media (min-width:768px){.navbar-nav{margin:0}.navbar-nav>li>a{padding-top:5px;padding-bottom:5px}}.navbar-form{padding:10px 15px;margin-top:0;margin-right:-15px;margin-bottom:0;margin-left:-15px;-webkit-box-shadow:none;box-shadow:none}.navbar-form>.input-group .form-control{margin-top:1px;margin-bottom:1px}@media (min-width:768px){.navbar-form{padding-top:0;padding-bottom:0;margin-right:0;margin-left:0}}.navbar-form .form-control{height:26px;padding:3px 8px}.navbar .btn,.navbar-btn{padding:3px 8px;margin-top:1px;margin-bottom:1px}.navbar .btn.btn-sm,.navbar-btn.btn-sm{margin-top:1px;margin-bottom:1px}.navbar .btn.btn-xs,.navbar-btn.btn-xs{padding:2px 6px;margin-top:4px;margin-bottom:4px}.navbar-text{margin-top:5px;margin-bottom:5px}.navbar-default{background-color:#2d2d2d;border-color:#000}.navbar-default .navbar-brand{color:#999}.navbar-default .navbar-brand:focus,.navbar-default .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-default .navbar-brand>.caret{border-top-color:#999;border-bottom-color:#999}.navbar-default .navbar-text{color:#999}.navbar-default .navbar-nav>li>a{color:#999}.navbar-default .navbar-nav>li>a:focus,.navbar-default .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-default .navbar-nav>.active>a,.navbar-default .navbar-nav>.active>a:focus,.navbar-default .navbar-nav>.active>a:hover{color:#fff;background-color:#141414}.navbar-default .navbar-nav>.disabled>a,.navbar-default .navbar-nav>.disabled>a:focus,.navbar-default .navbar-nav>.disabled>a:hover{color:#555;background-color:transparent}.navbar-default .navbar-toggle{border-color:#222}.navbar-default .navbar-toggle:focus,.navbar-default .navbar-toggle:hover{background-color:#333}.navbar-default .navbar-toggle .icon-bar{background-color:#fff}.navbar-default .navbar-collapse,.navbar-default .navbar-form{border-color:#000}.navbar-default .navbar-nav>.open>a,.navbar-default .navbar-nav>.open>a:focus,.navbar-default .navbar-nav>.open>a:hover{color:#fff;background-color:#141414}@media (max-width:767px){.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#141414}.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#555;background-color:transparent}}.navbar-default .navbar-link{color:#999}.navbar-default .navbar-link:hover{color:#fff}.navbar-default .btn-link{color:#999}.navbar-default .btn-link:focus,.navbar-default .btn-link:hover{color:#fff}.navbar-default .btn-link[disabled]:focus,.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-default .btn-link:focus,fieldset[disabled] .navbar-default .btn-link:hover{color:#555}.navbar-inverse{background-color:#fafafa;border-color:#dbdbdb}.navbar-inverse .navbar-brand{color:#999}.navbar-inverse .navbar-brand:focus,.navbar-inverse .navbar-brand:hover{color:grey;background-color:transparent}.navbar-inverse .navbar-brand>.caret{border-top-color:#999;border-bottom-color:#999}.navbar-inverse .navbar-text{color:#999}.navbar-inverse .navbar-nav>li>a{color:#999}.navbar-inverse .navbar-nav>li>a:focus,.navbar-inverse .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-inverse .navbar-nav>.active>a,.navbar-inverse .navbar-nav>.active>a:focus,.navbar-inverse .navbar-nav>.active>a:hover{color:#333;background-color:#e1e1e1}.navbar-inverse .navbar-nav>.disabled>a,.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-inverse .navbar-nav>.disabled>a:hover{color:#ccc;background-color:transparent}.navbar-inverse .navbar-toggle{border-color:#ddd}.navbar-inverse .navbar-toggle:focus,.navbar-inverse .navbar-toggle:hover{background-color:#ddd}.navbar-inverse .navbar-toggle .icon-bar{background-color:#888}.navbar-inverse .navbar-collapse,.navbar-inverse .navbar-form{border-color:#e8e8e8}.navbar-inverse .navbar-nav>.open>a,.navbar-inverse .navbar-nav>.open>a:focus,.navbar-inverse .navbar-nav>.open>a:hover{color:#333;background-color:#e1e1e1}@media (max-width:767px){.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#dbdbdb}.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#dbdbdb}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#999}.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#333;background-color:#e1e1e1}.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#ccc;background-color:transparent}}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#333}.navbar-inverse .btn-link{color:#999}.navbar-inverse .btn-link:focus,.navbar-inverse .btn-link:hover{color:#333}.navbar-inverse .btn-link[disabled]:focus,.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-inverse .btn-link:hover{color:#ccc}.navbar-masthead{min-height:44px;margin-bottom:18px}@media (min-width:768px){.navbar-masthead{border-radius:2px}}.navbar-masthead .navbar-static-top{z-index:1005}.navbar-masthead .navbar-fixed-bottom,.navbar-masthead .navbar-fixed-top{z-index:1029}.navbar-masthead .navbar-brand{height:44px;padding:13px 15px;font-size:20px}.navbar-masthead .navbar-brand>.glyphicon{margin-top:-3px}@media (min-width:768px){.navbar>.container .navbar-masthead .navbar-brand,.navbar>.container-fluid .navbar-masthead .navbar-brand{margin-left:-15px}}.navbar-masthead .navbar-toggle{margin-top:7px;margin-right:15px;margin-bottom:7px}.navbar-masthead .navbar-nav{margin:6px -15px}@media (min-width:768px){.navbar-masthead .navbar-nav{margin:6px 0}.navbar-masthead .navbar-nav>li>a{padding-top:8px;padding-bottom:6px}}.navbar-masthead .navbar-form{padding:10px 15px;margin-top:0;margin-right:-15px;margin-bottom:0;margin-left:-15px}.navbar-masthead .navbar-form>.input-group .form-control{margin-top:7px;margin-bottom:7px}@media (max-width:767px){.navbar-masthead .navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-masthead .navbar-form{padding-top:0;padding-bottom:0;margin-right:0;margin-left:0}}.navbar-masthead .navbar-form .form-control{height:30px;padding:5px 8px}.navbar-masthead.navbar .btn,.navbar-masthead.navbar-btn{padding:5px 8px;margin-top:7px;margin-bottom:7px}.navbar-masthead.navbar .btn.btn-sm,.navbar-masthead.navbar-btn.btn-sm{padding:3px 8px;margin-top:9px;margin-bottom:9px}.navbar-masthead.navbar .btn.btn-xs,.navbar-masthead.navbar-btn.btn-xs{padding:2px 6px;margin-top:12px;margin-bottom:12px}.navbar-masthead .navbar-text{margin-top:13px;margin-bottom:13px}.navbar-masthead.navbar-default{background-color:#f1f1f1;border-color:#e5e5e5}.navbar-masthead.navbar-default .navbar-brand{color:#777}.navbar-masthead.navbar-default .navbar-brand:focus,.navbar-masthead.navbar-default .navbar-brand:hover{color:#777;background-color:transparent}.navbar-masthead.navbar-default .navbar-brand>.caret{border-top-color:#777;border-bottom-color:#777}.navbar-masthead.navbar-default .navbar-text{color:#777}.navbar-masthead.navbar-default .navbar-nav>li>a{color:#777}.navbar-masthead.navbar-default .navbar-nav>li>a:focus,.navbar-masthead.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-masthead.navbar-default .navbar-nav>.active>a,.navbar-masthead.navbar-default .navbar-nav>.active>a:focus,.navbar-masthead.navbar-default .navbar-nav>.active>a:hover{color:#333;background-color:#f1f1f1}.navbar-masthead.navbar-default .navbar-nav>.disabled>a,.navbar-masthead.navbar-default .navbar-nav>.disabled>a:focus,.navbar-masthead.navbar-default .navbar-nav>.disabled>a:hover{color:#bbb;background-color:transparent}.navbar-masthead.navbar-default .navbar-toggle{border-color:#dcdcdc}.navbar-masthead.navbar-default .navbar-toggle:focus,.navbar-masthead.navbar-default .navbar-toggle:hover{background-color:#e4e4e4}.navbar-masthead.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-masthead.navbar-default .navbar-collapse,.navbar-masthead.navbar-default .navbar-form{border-color:#dfdfdf}.navbar-masthead.navbar-default .navbar-nav>.open>a,.navbar-masthead.navbar-default .navbar-nav>.open>a:focus,.navbar-masthead.navbar-default .navbar-nav>.open>a:hover{color:#333;background-color:#f1f1f1}@media (max-width:767px){.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#333;background-color:#f1f1f1}.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-masthead.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#bbb;background-color:transparent}}.navbar-masthead.navbar-default .navbar-link{color:#777}.navbar-masthead.navbar-default .navbar-link:hover{color:#333}.navbar-masthead.navbar-default .btn-link{color:#777}.navbar-masthead.navbar-default .btn-link:focus,.navbar-masthead.navbar-default .btn-link:hover{color:#333}.navbar-masthead.navbar-default .btn-link[disabled]:focus,.navbar-masthead.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-masthead.navbar-default .btn-link:focus,fieldset[disabled] .navbar-masthead.navbar-default .btn-link:hover{color:#bbb}.navbar-masthead.navbar-inverse{background-color:#444;border-color:#333}.navbar-masthead.navbar-inverse .navbar-brand{color:#fff}.navbar-masthead.navbar-inverse .navbar-brand:focus,.navbar-masthead.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-masthead.navbar-inverse .navbar-brand>.caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-masthead.navbar-inverse .navbar-text{color:#999}.navbar-masthead.navbar-inverse .navbar-nav>li>a{color:#fff}.navbar-masthead.navbar-inverse .navbar-nav>li>a:focus,.navbar-masthead.navbar-inverse .navbar-nav>li>a:hover{color:#bbb;background-color:transparent}.navbar-masthead.navbar-inverse .navbar-nav>.active>a,.navbar-masthead.navbar-inverse .navbar-nav>.active>a:focus,.navbar-masthead.navbar-inverse .navbar-nav>.active>a:hover{color:#bbb;background-color:#444}.navbar-masthead.navbar-inverse .navbar-nav>.disabled>a,.navbar-masthead.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-masthead.navbar-inverse .navbar-nav>.disabled>a:hover{color:#777;background-color:transparent}.navbar-masthead.navbar-inverse .navbar-toggle{border-color:#222}.navbar-masthead.navbar-inverse .navbar-toggle:focus,.navbar-masthead.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-masthead.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-masthead.navbar-inverse .navbar-collapse,.navbar-masthead.navbar-inverse .navbar-form{border-color:#323232}.navbar-masthead.navbar-inverse .navbar-nav>.open>a,.navbar-masthead.navbar-inverse .navbar-nav>.open>a:focus,.navbar-masthead.navbar-inverse .navbar-nav>.open>a:hover{color:#bbb;background-color:#444}@media (max-width:767px){.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#333}.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#333}.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#fff}.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#bbb;background-color:transparent}.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#bbb;background-color:#444}.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-masthead.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#777;background-color:transparent}}.navbar-masthead.navbar-inverse .navbar-link{color:#fff}.navbar-masthead.navbar-inverse .navbar-link:hover{color:#bbb}.navbar-masthead.navbar-inverse .btn-link{color:#fff}.navbar-masthead.navbar-inverse .btn-link:focus,.navbar-masthead.navbar-inverse .btn-link:hover{color:#bbb}.navbar-masthead.navbar-inverse .btn-link[disabled]:focus,.navbar-masthead.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-masthead.navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-masthead.navbar-inverse .btn-link:hover{color:#777}.navbar-toolbar{min-height:36px;margin-bottom:18px}@media (min-width:768px){.navbar-toolbar{border-radius:2px}}.navbar-toolbar .navbar-static-top{z-index:1008}.navbar-toolbar .navbar-fixed-bottom,.navbar-toolbar .navbar-fixed-top{z-index:1028}.navbar-toolbar .navbar-brand{height:36px;padding:9px 15px;font-size:16px;font-weight:700}@media (min-width:768px){.navbar>.container .navbar-toolbar .navbar-brand,.navbar>.container-fluid .navbar-toolbar .navbar-brand{margin-left:-15px}}.navbar-toolbar .navbar-toggle{margin-top:3px;margin-right:15px;margin-bottom:3px}.navbar-toolbar .navbar-nav{margin:4px -15px}.navbar-toolbar .navbar-nav>li{position:relative}.navbar-toolbar .navbar-nav>li>a{padding:9px 15px}.navbar-toolbar .navbar-nav>li>a:focus,.navbar-toolbar .navbar-nav>li>a:hover{text-decoration:underline}.navbar-toolbar .navbar-nav>li>.dropdown-menu{margin-top:1px}.navbar-toolbar .navbar-nav>.active>a{font-weight:700}.navbar-toolbar .navbar-nav>.active>a:before{position:absolute;bottom:-1px;left:50%;display:inline-block;margin-left:-8px;content:'';border-right:8px solid transparent;border-bottom:8px solid transparent;border-left:8px solid transparent}.navbar-toolbar .navbar-nav>.active>a:after{position:absolute;bottom:-1px;left:50%;display:inline-block;margin-left:-7px;content:'';border-right:7px solid transparent;border-bottom:7px solid transparent;border-left:7px solid transparent}@media (min-width:768px){.navbar-toolbar .navbar-nav{margin:0}.navbar-toolbar .navbar-nav>li>a{padding-top:9px;padding-bottom:9px}}.navbar-toolbar .navbar-form{padding:10px 15px;margin-top:0;margin-right:-15px;margin-bottom:0;margin-left:-15px}.navbar-toolbar .navbar-form>.input-group .form-control{margin-top:3px;margin-bottom:3px}@media (max-width:767px){.navbar-toolbar .navbar-form .form-group{margin-bottom:5px}}@media (min-width:768px){.navbar-toolbar .navbar-form{padding-top:0;padding-bottom:0;margin-right:0;margin-left:0}}.navbar-toolbar .navbar-form .form-control{height:30px;padding:5px 8px}.navbar-toolbar .dropdown-menu{border-top:1px none}.navbar-toolbar.navbar .btn,.navbar-toolbar.navbar-btn{padding:5px 8px;margin-top:3px;margin-bottom:3px}.navbar-toolbar.navbar .btn.btn-sm,.navbar-toolbar.navbar-btn.btn-sm{padding:3px 8px;margin-top:5px;margin-bottom:5px}.navbar-toolbar.navbar .btn.btn-xs,.navbar-toolbar.navbar-btn.btn-xs{padding:2px 6px;margin-top:8px;margin-bottom:8px}.navbar-toolbar .navbar-text{margin-top:9px;margin-bottom:9px}.navbar-toolbar.navbar-default{background-color:#fff;border-color:#ebebeb}.navbar-toolbar.navbar-default .navbar-brand{color:#dd4b39}.navbar-toolbar.navbar-default .navbar-brand:focus,.navbar-toolbar.navbar-default .navbar-brand:hover{color:#dd4b39;background-color:transparent}.navbar-toolbar.navbar-default .navbar-brand>.caret{border-top-color:#dd4b39;border-bottom-color:#dd4b39}.navbar-toolbar.navbar-default .navbar-text{color:#777}.navbar-toolbar.navbar-default .navbar-nav>li>a{color:#777}.navbar-toolbar.navbar-default .navbar-nav>li>a:focus,.navbar-toolbar.navbar-default .navbar-nav>li>a:hover{color:#333;background-color:transparent}.navbar-toolbar.navbar-default .navbar-nav>.active>a,.navbar-toolbar.navbar-default .navbar-nav>.active>a:focus,.navbar-toolbar.navbar-default .navbar-nav>.active>a:hover{color:#333;background-color:#f2f2f2}.navbar-toolbar.navbar-default .navbar-nav>.active>a:before{border-bottom:8px solid #ebebeb}.navbar-toolbar.navbar-default .navbar-nav>.active>a:after{border-bottom:7px solid #fff}.navbar-toolbar.navbar-default .navbar-nav>.disabled>a,.navbar-toolbar.navbar-default .navbar-nav>.disabled>a:focus,.navbar-toolbar.navbar-default .navbar-nav>.disabled>a:hover{color:#bbb;background-color:transparent}.navbar-toolbar.navbar-default .navbar-toggle{border-color:#dcdcdc}.navbar-toolbar.navbar-default .navbar-toggle:focus,.navbar-toolbar.navbar-default .navbar-toggle:hover{background-color:#e4e4e4}.navbar-toolbar.navbar-default .navbar-toggle .icon-bar{background-color:#888}.navbar-toolbar.navbar-default .navbar-collapse,.navbar-toolbar.navbar-default .navbar-form{border-color:#ededed}.navbar-toolbar.navbar-default .navbar-nav>.open>a,.navbar-toolbar.navbar-default .navbar-nav>.open>a:focus,.navbar-toolbar.navbar-default .navbar-nav>.open>a:hover{color:#333;background-color:#f2f2f2}@media (max-width:767px){.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>li>a{color:#777}.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>li>a:hover{color:#333;background-color:transparent}.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>.active>a,.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>.active>a:hover{color:#333;background-color:#f2f2f2}.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-toolbar.navbar-default .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#bbb;background-color:transparent}}.navbar-toolbar.navbar-default .navbar-link{color:#777}.navbar-toolbar.navbar-default .navbar-link:hover{color:#333}.navbar-toolbar.navbar-default .btn-link{color:#777}.navbar-toolbar.navbar-default .btn-link:focus,.navbar-toolbar.navbar-default .btn-link:hover{color:#333}.navbar-toolbar.navbar-default .btn-link[disabled]:focus,.navbar-toolbar.navbar-default .btn-link[disabled]:hover,fieldset[disabled] .navbar-toolbar.navbar-default .btn-link:focus,fieldset[disabled] .navbar-toolbar.navbar-default .btn-link:hover{color:#bbb}.navbar-toolbar.navbar-inverse{background-color:#444;border-color:#333}.navbar-toolbar.navbar-inverse .navbar-brand{color:#fff}.navbar-toolbar.navbar-inverse .navbar-brand:focus,.navbar-toolbar.navbar-inverse .navbar-brand:hover{color:#fff;background-color:transparent}.navbar-toolbar.navbar-inverse .navbar-brand>.caret{border-top-color:#fff;border-bottom-color:#fff}.navbar-toolbar.navbar-inverse .navbar-text{color:#999}.navbar-toolbar.navbar-inverse .navbar-nav>li>a{color:#fff}.navbar-toolbar.navbar-inverse .navbar-nav>li>a:focus,.navbar-toolbar.navbar-inverse .navbar-nav>li>a:hover{color:#fff;background-color:transparent}.navbar-toolbar.navbar-inverse .navbar-nav>.active>a,.navbar-toolbar.navbar-inverse .navbar-nav>.active>a:focus,.navbar-toolbar.navbar-inverse .navbar-nav>.active>a:hover{color:#fff;background-color:#444}.navbar-toolbar.navbar-inverse .navbar-nav>.active>a:before{border-bottom:8px solid #333}.navbar-toolbar.navbar-inverse .navbar-nav>.active>a:after{border-bottom:7px solid #fff}.navbar-toolbar.navbar-inverse .navbar-nav>.disabled>a,.navbar-toolbar.navbar-inverse .navbar-nav>.disabled>a:focus,.navbar-toolbar.navbar-inverse .navbar-nav>.disabled>a:hover{color:#777;background-color:transparent}.navbar-toolbar.navbar-inverse .navbar-toggle{border-color:#222}.navbar-toolbar.navbar-inverse .navbar-toggle:focus,.navbar-toolbar.navbar-inverse .navbar-toggle:hover{background-color:#333}.navbar-toolbar.navbar-inverse .navbar-toggle .icon-bar{background-color:#fff}.navbar-toolbar.navbar-inverse .navbar-collapse,.navbar-toolbar.navbar-inverse .navbar-form{border-color:#323232}.navbar-toolbar.navbar-inverse .navbar-nav>.open>a,.navbar-toolbar.navbar-inverse .navbar-nav>.open>a:focus,.navbar-toolbar.navbar-inverse .navbar-nav>.open>a:hover{color:#fff;background-color:#444}@media (max-width:767px){.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>.dropdown-header{border-color:#333}.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu .divider{background-color:#333}.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>li>a{color:#fff}.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:focus,.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>li>a:hover{color:#fff;background-color:transparent}.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a,.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:focus,.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>.active>a:hover{color:#fff;background-color:#444}.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a,.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:focus,.navbar-toolbar.navbar-inverse .navbar-nav .open .dropdown-menu>.disabled>a:hover{color:#777;background-color:transparent}}.navbar-toolbar.navbar-inverse .navbar-link{color:#fff}.navbar-toolbar.navbar-inverse .navbar-link:hover{color:#fff}.navbar-toolbar.navbar-inverse .btn-link{color:#fff}.navbar-toolbar.navbar-inverse .btn-link:focus,.navbar-toolbar.navbar-inverse .btn-link:hover{color:#fff}.navbar-toolbar.navbar-inverse .btn-link[disabled]:focus,.navbar-toolbar.navbar-inverse .btn-link[disabled]:hover,fieldset[disabled] .navbar-toolbar.navbar-inverse .btn-link:focus,fieldset[disabled] .navbar-toolbar.navbar-inverse .btn-link:hover{color:#777}.navbar-static-top{border-radius:0}.navbar-fixed-top,.navbar-static-top{border-width:1px 0}.navbar-fixed-bottom{border-width:1px 0}.navbar-fixed-bottom,.navbar-fixed-top{position:fixed;right:0;left:0;border-radius:0}.navbar-fixed-bottom,.navbar-fixed-top{border-radius:0}.navbar-fixed-top{top:0}.navbar-fixed-bottom{bottom:0;margin-bottom:0}.navbar-btn{padding:3px 8px;margin-top:1px}.btn.navbar-masthead-btn{margin-top:7px}.btn.navbar-toolbar-btn{margin-top:3px}.navbar-link{color:#999}.navbar-link:hover{color:#fff}.navbar-inverse .navbar-link{color:#999}.navbar-inverse .navbar-link:hover{color:#333}.navbar-form .checkbox-inline,.navbar-form .radio-inline{color:#999}.breadcrumb{padding:13px 15px;margin-bottom:18px;background-color:#f3f3f3;border-radius:2px}.breadcrumb>li+li{position:relative;display:inline-block;margin-left:20px}.breadcrumb>li+li:before{border-radius:5px}.breadcrumb>li+li:after,.breadcrumb>li+li:before{position:absolute;width:0;height:0;content:""}.breadcrumb>li+li:before{border:7px solid transparent}.breadcrumb>li+li:after{border:5px solid transparent}.breadcrumb>li+li:after,.breadcrumb>li+li:before{top:9px;left:100%}.breadcrumb>li+li:before{margin-top:-7px;border-left:7px solid;border-left-color:#777}.breadcrumb>li+li:after{margin-top:-5px;border-left:5px solid #f3f3f3}.breadcrumb>li+li:after,.breadcrumb>li+li:before{left:-16px}.breadcrumb>li+li:before{color:#999;content:""}.breadcrumb>li>a{color:#999}.breadcrumb>li>a:hover{color:#000}.breadcrumb>.active,.breadcrumb>.active>a{color:#000}.breadcrumb-inverse{background-color:#393832}.breadcrumb-inverse>li+li{position:relative;display:inline-block}.breadcrumb-inverse>li+li:before{border-radius:5px}.breadcrumb-inverse>li+li:after,.breadcrumb-inverse>li+li:before{position:absolute;width:0;height:0;content:""}.breadcrumb-inverse>li+li:before{border:7px solid transparent}.breadcrumb-inverse>li+li:after{border:5px solid transparent}.breadcrumb-inverse>li+li:after,.breadcrumb-inverse>li+li:before{top:9px;left:100%}.breadcrumb-inverse>li+li:before{margin-top:-7px;border-left:7px solid;border-left-color:#666}.breadcrumb-inverse>li+li:after{margin-top:-5px;border-left:5px solid #393832}.breadcrumb-inverse>li+li:after,.breadcrumb-inverse>li+li:before{left:-16px}.breadcrumb-inverse>li>a{color:#999}.breadcrumb-inverse>li>a:hover{color:#fff}.breadcrumb-inverse>.active,.breadcrumb-inverse>.active>a{color:#fff}.breadcrumb-sm{padding:4px 15px;background-color:#fff;border-bottom:1px solid #ebebeb}.breadcrumb-sm.breadcrumb-inverse{background-color:#393832}.pagination{margin:18px 0;border-radius:2px}.pagination>li>a,.pagination>li>span{padding:5px 12px;line-height:1.4;color:#333;background-color:#f3f3f3;border:1px solid #dcdcdc}.pagination>li:first-child>a,.pagination>li:first-child>span{margin-left:0;border-top-left-radius:2px;border-bottom-left-radius:2px}.pagination>li:last-child>a,.pagination>li:last-child>span{border-top-right-radius:2px;border-bottom-right-radius:2px}.pagination>li>a:focus,.pagination>li>a:hover,.pagination>li>span:focus,.pagination>li>span:hover{color:#333;background-color:#f5f5f5;border-color:#c6c6c6;-webkit-box-shadow:0 1px 1px rgba(0,0,0,.1);box-shadow:0 1px 1px rgba(0,0,0,.1)}.pagination>li>a:active{background-color:#f4f4f4;background-image:-webkit-linear-gradient(top,#f6f6f6 0,#f1f1f1 100%);background-image:-o-linear-gradient(top,#f6f6f6 0,#f1f1f1 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f6f6f6),to(#f1f1f1));background-image:linear-gradient(to bottom,#f6f6f6 0,#f1f1f1 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff6f6f6', endColorstr='#fff1f1f1', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.pagination>.active>a,.pagination>.active>a:focus,.pagination>.active>a:hover,.pagination>.active>span,.pagination>.active>span:focus,.pagination>.active>span:hover{color:#4d90fe;background-color:#f5f5f5;border-color:#c6c6c6;-webkit-box-shadow:none;box-shadow:none}.pagination>.disabled>a,.pagination>.disabled>a:focus,.pagination>.disabled>a:hover,.pagination>.disabled>span,.pagination>.disabled>span:focus,.pagination>.disabled>span:hover{color:#b3b3b3;text-shadow:none;background-color:#f3f3f3;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#f1f1f1 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#f1f1f1 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#f1f1f1));background-image:linear-gradient(to bottom,#f5f5f5 0,#f1f1f1 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#fff1f1f1', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#d9d9d9;-webkit-box-shadow:none;box-shadow:none}.pagination-lg>li>a,.pagination-lg>li>span{padding:9px 14px;font-size:14px;line-height:1.3}.pagination-lg>li:first-child>a,.pagination-lg>li:first-child>span{border-top-left-radius:1px;border-bottom-left-radius:1px}.pagination-lg>li:last-child>a,.pagination-lg>li:last-child>span{border-top-right-radius:1px;border-bottom-right-radius:1px}.pagination-sm>li>a,.pagination-sm>li>span{padding:3px 8px;font-size:12px;line-height:1.5}.pagination-sm>li:first-child>a,.pagination-sm>li:first-child>span{border-top-left-radius:1px;border-bottom-left-radius:1px}.pagination-sm>li:last-child>a,.pagination-sm>li:last-child>span{border-top-right-radius:1px;border-bottom-right-radius:1px}.pager{margin:18px 0}.pager li>a,.pager li>span{padding:11px 24px;overflow:visible;font-size:14px;color:#777;text-decoration:none;white-space:nowrap;cursor:default;background-color:#fff;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border:1px solid #5b5b5b;border:1px solid rgba(0,0,0,.1);border-radius:2px;outline:0;-webkit-box-shadow:0 2px 1px rgba(0,0,0,.1),0 0 1px rgba(0,0,0,.1);box-shadow:0 2px 1px rgba(0,0,0,.1),0 0 1px rgba(0,0,0,.1)}.pager li>a:focus,.pager li>a:hover{color:#444;background-color:#fff}.pager li>a:active{color:#444;background-color:#fff}.pager li .icon-prev{position:relative;display:inline-block;padding-right:8px}.pager li .icon-prev:before{border-radius:5px}.pager li .icon-prev:after,.pager li .icon-prev:before{position:absolute;width:0;height:0;content:""}.pager li .icon-prev:before{border:7px solid transparent}.pager li .icon-prev:after{border:4px solid transparent}.pager li .icon-prev:after,.pager li .icon-prev:before{top:-5px;right:100%}.pager li .icon-prev:before{margin-top:-7px;border-right:7px solid;border-right-color:inherit}.pager li .icon-prev:after{margin-top:-4px;border-right:4px solid #fff}.pager li .icon-next{position:relative;display:inline-block;padding-left:8px}.pager li .icon-next:before{border-radius:5px}.pager li .icon-next:after,.pager li .icon-next:before{position:absolute;width:0;height:0;content:""}.pager li .icon-next:before{border:7px solid transparent}.pager li .icon-next:after{border:4px solid transparent}.pager li .icon-next:after,.pager li .icon-next:before{top:-5px;left:100%}.pager li .icon-next:before{margin-top:-7px;border-left:7px solid;border-left-color:inherit}.pager li .icon-next:after{margin-top:-4px;border-left:4px solid #fff}.pager .disabled>a,.pager .disabled>a:focus,.pager .disabled>a:hover,.pager .disabled>span{color:#b3b3b3;background-color:#fafafa;filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);border-color:#d9d9d9;-webkit-box-shadow:none;box-shadow:none}.pager .disabled .icon-prev{position:relative;display:inline-block;padding-right:8px}.pager .disabled .icon-prev:before{border-radius:5px}.pager .disabled .icon-prev:after,.pager .disabled .icon-prev:before{position:absolute;width:0;height:0;content:""}.pager .disabled .icon-prev:before{border:7px solid transparent}.pager .disabled .icon-prev:after{border:4px solid transparent}.pager .disabled .icon-prev:after,.pager .disabled .icon-prev:before{top:-5px;right:100%}.pager .disabled .icon-prev:before{margin-top:-7px;border-right:7px solid;border-right-color:#b3b3b3}.pager .disabled .icon-prev:after{margin-top:-4px;border-right:4px solid #fafafa}.pager .disabled .icon-next{position:relative;display:inline-block;padding-left:8px}.pager .disabled .icon-next:before{border-radius:5px}.pager .disabled .icon-next:after,.pager .disabled .icon-next:before{position:absolute;width:0;height:0;content:""}.pager .disabled .icon-next:before{border:7px solid transparent}.pager .disabled .icon-next:after{border:4px solid transparent}.pager .disabled .icon-next:after,.pager .disabled .icon-next:before{top:-5px;left:100%}.pager .disabled .icon-next:before{margin-top:-7px;border-left:7px solid;border-left-color:#b3b3b3}.pager .disabled .icon-next:after{margin-top:-4px;border-left:4px solid #fafafa}.label{font-size:80%;border-radius:0}.label-default{background-color:#999}.label-default[href]:focus,.label-default[href]:hover{background-color:grey}.label-primary{background-color:#4d90fe}.label-primary[href]:focus,.label-primary[href]:hover{background-color:#1a70fe}.label-success{background-color:#35aa47}.label-success[href]:focus,.label-success[href]:hover{background-color:#298337}.label-info{background-color:#5bc0de}.label-info[href]:focus,.label-info[href]:hover{background-color:#31b0d5}.label-warning{background-color:#faa937}.label-warning[href]:focus,.label-warning[href]:hover{background-color:#f89306}.label-danger{background-color:#d84a38}.label-danger[href]:focus,.label-danger[href]:hover{background-color:#b93524}.badge{font-size:12px}.btn-group-xs>.btn .badge,.btn-xs .badge{font-size:11px}.list-group-item.active>.badge,li.list-group-item.active a>.badge{color:#fff;background-color:#dd4b39}.nav-pills>.active>a>.badge{color:#15c;background-color:#fff}.jumbotron{color:inherit;background-color:#eee}.jumbotron .h1,.jumbotron h1{color:inherit}.jumbotron p{font-size:20px}.container .jumbotron,.container-fluid .jumbotron{border-radius:1px}@media screen and (min-width:768px){.jumbotron .h1,.jumbotron h1{font-size:59px}}.thumbnail{display:block;padding:0;margin-bottom:18px;line-height:1.4;background-color:#fff;border:1px solid #fff;border-radius:0}a.thumbnail.active,a.thumbnail:focus,a.thumbnail:hover{border-color:#fff;-webkit-box-shadow:0 0 0 1px #dedede;box-shadow:0 0 0 1px #dedede}.thumbnail .caption{padding:9px 4px;color:#000}.alert{padding:8px;margin-bottom:18px;border-radius:2px}.alert .alert-link{font-weight:700}.alert-dismissable,.alert-dismissible{padding-right:28px}.alert-success{color:#3c763d;background-color:#dff0d8;border-color:#a3d48e}.alert-success hr{border-top-color:#93cd7c}.alert-success .alert-link{color:#2b542c}.alert-info{color:#31708f;background-color:#d9edf7;border-color:#85c5e5}.alert-info hr{border-top-color:#70bbe1}.alert-info .alert-link{color:#245269}.alert-warning{color:#333;background-color:#f9edbe;border-color:#f0c36d}.alert-warning hr{border-top-color:#eeb956}.alert-warning .alert-link{color:#1a1a1a}.alert-danger{color:#a94442;background-color:#f2dede;border-color:#d59595}.alert-danger hr{border-top-color:#ce8383}.alert-danger .alert-link{color:#843534}.alert-danger,.alert-info,.alert-success,.alert-warning{text-shadow:0 1px 0 rgba(255,255,255,.5)}.progress{height:14px;height:18px;padding:1px;margin-bottom:18px;font-size:12px;background-color:transparent;background-image:none;border:1px solid #999;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.progress-bar{line-height:1.25;background-color:#6188f5;background-image:none;-webkit-box-shadow:none;box-shadow:none}.progress-bar-striped,.progress-striped .progress-bar{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);-webkit-background-size:40px 40px;background-size:40px 40px}.progress-bar-success{background-color:#2f973f}.progress-striped .progress-bar-success{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-info{background-color:#53bddc}.progress-striped .progress-bar-info{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-warning{background-color:#fbb450}.progress-striped .progress-bar-warning{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.progress-bar-danger{background-color:#c13e2c}.progress-striped .progress-bar-danger{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group-item{color:#222;background-color:#fff;border:1px solid #e5e5e5}.list-group-item:first-child{border-top-left-radius:0;border-top-right-radius:0}.list-group-item:last-child{border-bottom-right-radius:0;border-bottom-left-radius:0}.list-group-item .dropdown{display:none}.list-group-item .dropdown-toggle{display:inline-block;padding:5px 6px 5px 5px;color:#222}a.list-group-item,button.list-group-item{color:#555}a.list-group-item .list-group-item-heading,button.list-group-item .list-group-item-heading{color:#333}.list-group-item.active,.list-group-item.active:focus,.list-group-item.active:hover{font-weight:700;color:#dd4b39;background-color:transparent;border-color:#e5e5e5;border-left:4px solid #dd4b39;border-left-color:#dd4b39}.list-group-item.active .list-group-item-text,.list-group-item.active:focus .list-group-item-text,.list-group-item.active:hover .list-group-item-text{font-weight:400;color:#888}.list-group-item.active:focus,.list-group-item.active:hover{background-color:#eee}a.list-group-item:focus,a.list-group-item:hover,li.list-group-item a:focus,li.list-group-item a:hover{color:#555;text-decoration:none;background-color:#eee}li.list-group-item{padding:0;margin-bottom:0;border:0 none}li.list-group-item>a{display:block;padding:5px 17px;margin:0 0 0 14px;color:#222}li.list-group-item.active,li.list-group-item.active:focus,li.list-group-item.active:hover{background-color:transparent}li.list-group-item.active:focus>a,li.list-group-item.active:hover>a,li.list-group-item.active>a{margin-left:10px;color:#dd4b39}.list-group-item-success{color:#3c763d;background-color:#dff0d8}a.list-group-item-success,button.list-group-item-success{color:#3c763d}a.list-group-item-success .list-group-item-heading,button.list-group-item-success .list-group-item-heading{color:inherit}a.list-group-item-success:focus,a.list-group-item-success:hover,button.list-group-item-success:focus,button.list-group-item-success:hover{color:#3c763d;background-color:#d0e9c6}a.list-group-item-success.active,a.list-group-item-success.active:focus,a.list-group-item-success.active:hover,button.list-group-item-success.active,button.list-group-item-success.active:focus,button.list-group-item-success.active:hover{color:#fff;background-color:#3c763d;border-color:#3c763d}.list-group-item-info{color:#31708f;background-color:#d9edf7}a.list-group-item-info,button.list-group-item-info{color:#31708f}a.list-group-item-info .list-group-item-heading,button.list-group-item-info .list-group-item-heading{color:inherit}a.list-group-item-info:focus,a.list-group-item-info:hover,button.list-group-item-info:focus,button.list-group-item-info:hover{color:#31708f;background-color:#c4e3f3}a.list-group-item-info.active,a.list-group-item-info.active:focus,a.list-group-item-info.active:hover,button.list-group-item-info.active,button.list-group-item-info.active:focus,button.list-group-item-info.active:hover{color:#fff;background-color:#31708f;border-color:#31708f}.list-group-item-warning{color:#333;background-color:#f9edbe}a.list-group-item-warning,button.list-group-item-warning{color:#333}a.list-group-item-warning .list-group-item-heading,button.list-group-item-warning .list-group-item-heading{color:inherit}a.list-group-item-warning:focus,a.list-group-item-warning:hover,button.list-group-item-warning:focus,button.list-group-item-warning:hover{color:#333;background-color:#f7e7a7}a.list-group-item-warning.active,a.list-group-item-warning.active:focus,a.list-group-item-warning.active:hover,button.list-group-item-warning.active,button.list-group-item-warning.active:focus,button.list-group-item-warning.active:hover{color:#fff;background-color:#333;border-color:#333}.list-group-item-danger{color:#a94442;background-color:#f2dede}a.list-group-item-danger,button.list-group-item-danger{color:#a94442}a.list-group-item-danger .list-group-item-heading,button.list-group-item-danger .list-group-item-heading{color:inherit}a.list-group-item-danger:focus,a.list-group-item-danger:hover,button.list-group-item-danger:focus,button.list-group-item-danger:hover{color:#a94442;background-color:#ebcccc}a.list-group-item-danger.active,a.list-group-item-danger.active:focus,a.list-group-item-danger.active:hover,button.list-group-item-danger.active,button.list-group-item-danger.active:focus,button.list-group-item-danger.active:hover{color:#fff;background-color:#a94442;border-color:#a94442}.list-group-item-wrapper{margin-left:14px}.list-group-item-wrapper:hover>.dropdown{display:block}.list-group-item-wrapper>a{display:block;padding:5px 17px;margin:0;color:#222}.list-group-item-wrapper>.dropdown:hover+a{background-color:#eee}.list-group-item-wrapper>.dropdown.open{display:block}.list-group-item-wrapper>.dropdown.open+a{background-color:#eee}.list-group-item-wrapper>.dropdown>.dropdown-menu{margin-top:0}.list-group-header{display:block;padding:10px 30px 10px 15px;font-size:11px;font-weight:700;line-height:1.4;color:#999;text-shadow:0 1px 0 rgba(255,255,255,.5);text-transform:uppercase}li.list-group-header{padding:3px 15px}.list-group .list-group-header{margin-top:9px}.list-group-item-menu{padding:0;margin:0;border:0 none;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.list-group-item-menu .list-group-item-wrapper>a{padding-left:30px}.list-group-item-menu .list-group-item-menu .list-group-item-wrapper>a{padding-left:44px}.list-group-item-menu>.list-group-item .collapse-caret{margin-left:28px}.collapse-caret{position:absolute;z-index:1;display:inline-block;width:17px;height:28px;margin-left:14px}.collapse-caret:before{position:absolute;top:12px;left:5px;margin-left:0;content:'';border-bottom:0 dotted}.collapse-caret:hover{background-color:#eee}.collapse-caret.collapsed:before{top:10px;left:6px}.list-group .divider{height:1px;margin:8px 0;margin-right:15px;margin-left:15px;overflow:hidden;background-color:#e5e5e5}.panel{word-wrap:break-word;background-color:#fff;border:1px solid transparent;border-bottom-width:2px;border-radius:3px;-webkit-box-shadow:none;box-shadow:none}.panel-body{padding:15px 20px}.panel-heading{padding:15px 20px;border-top-left-radius:3px;border-top-right-radius:3px}.panel-title{font-size:16px}.panel-footer{padding:15px 20px;background-color:#f8f8f8;border-top:1px solid #e5e5e5;border-bottom-right-radius:3px;border-bottom-left-radius:3px}.panel>.list-group{padding:15px 20px;padding-top:0}.panel>.list-group:first-child .list-group-item:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.panel>.list-group:last-child .list-group-item:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.panel-collapse>.table caption,.panel>.table caption,.panel>.table-responsive>.table caption{padding-right:15px 20px;padding-left:15px 20px}.panel>.table-responsive:first-child>.table:first-child,.panel>.table:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child,.panel>.table:first-child>thead:first-child>tr:first-child{border-top-left-radius:2px;border-top-right-radius:2px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:first-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:first-child,.panel>.table:first-child>thead:first-child>tr:first-child td:first-child,.panel>.table:first-child>thead:first-child>tr:first-child th:first-child{border-top-left-radius:2px}.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table-responsive:first-child>.table:first-child>thead:first-child>tr:first-child th:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child td:last-child,.panel>.table:first-child>tbody:first-child>tr:first-child th:last-child,.panel>.table:first-child>thead:first-child>tr:first-child td:last-child,.panel>.table:first-child>thead:first-child>tr:first-child th:last-child{border-top-right-radius:2px}.panel>.table-responsive:last-child>.table:last-child,.panel>.table:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child{border-bottom-right-radius:2px;border-bottom-left-radius:2px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:first-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:first-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:first-child{border-bottom-left-radius:2px}.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table-responsive:last-child>.table:last-child>tfoot:last-child>tr:last-child th:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child td:last-child,.panel>.table:last-child>tbody:last-child>tr:last-child th:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child td:last-child,.panel>.table:last-child>tfoot:last-child>tr:last-child th:last-child{border-bottom-right-radius:2px}.panel>.panel-body+.table,.panel>.panel-body+.table-responsive,.panel>.table+.panel-body,.panel>.table-responsive+.panel-body{border-top:1px solid #ddd}.panel-default{border-color:#d8d8d8}.panel-default>.panel-heading{color:#333;background-color:#fff;border-color:#fff}.panel-default>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d8d8d8}.panel-default>.panel-heading .badge{color:#fff;background-color:#333}.panel-default>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d8d8d8}.panel-primary{border-color:#4d90fe}.panel-primary>.panel-heading{color:#fff;background-color:#4d90fe;border-color:#4d90fe}.panel-primary>.panel-heading+.panel-collapse>.panel-body{border-top-color:#4d90fe}.panel-primary>.panel-heading .badge{color:#4d90fe;background-color:#fff}.panel-primary>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#4d90fe}.panel-success{border-color:#a3d48e}.panel-success>.panel-heading{color:#3c763d;background-color:#dff0d8;border-color:#a3d48e}.panel-success>.panel-heading+.panel-collapse>.panel-body{border-top-color:#a3d48e}.panel-success>.panel-heading .badge{color:#dff0d8;background-color:#3c763d}.panel-success>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#a3d48e}.panel-info{border-color:#85c5e5}.panel-info>.panel-heading{color:#31708f;background-color:#d9edf7;border-color:#85c5e5}.panel-info>.panel-heading+.panel-collapse>.panel-body{border-top-color:#85c5e5}.panel-info>.panel-heading .badge{color:#d9edf7;background-color:#31708f}.panel-info>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#85c5e5}.panel-warning{border-color:#f0c36d}.panel-warning>.panel-heading{color:#333;background-color:#f9edbe;border-color:#f0c36d}.panel-warning>.panel-heading+.panel-collapse>.panel-body{border-top-color:#f0c36d}.panel-warning>.panel-heading .badge{color:#f9edbe;background-color:#333}.panel-warning>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#f0c36d}.panel-danger{border-color:#d59595}.panel-danger>.panel-heading{color:#a94442;background-color:#f2dede;border-color:#d59595}.panel-danger>.panel-heading+.panel-collapse>.panel-body{border-top-color:#d59595}.panel-danger>.panel-heading .badge{color:#f2dede;background-color:#a94442}.panel-danger>.panel-footer+.panel-collapse>.panel-body{border-bottom-color:#d59595}.panel-group{margin-bottom:18px}.panel-group .panel{border-color:transparent;border-radius:0}.panel-group .panel+.panel{margin-top:-3px}.panel-group .panel-heading{padding:0 15px;background-color:#fafafa;border-top:1px dashed #ccc;border-bottom:1px dashed #ccc}.panel-group .panel-heading a{display:block;padding:10px 0 9px;color:#444;text-decoration:none}.panel-group .panel-heading a:before{margin-right:7px;content:"\e082"}.panel-group .panel-heading a:hover{background-color:#f5f5f5}.panel-group .panel-heading a:focus{outline:0}.panel-group .panel-heading a.collapsed:before{margin-right:7px;content:"\e081"}.panel-group .panel-heading .panel-title{font-size:13px}.panel-group .panel-heading+.panel-collapse>.list-group,.panel-group .panel-heading+.panel-collapse>.panel-body{border-top:0 none}.panel-group .panel-footer+.panel-collapse .panel-body{border-bottom:0 none}.well{background-color:#f1f1f1;border:1px solid #e5e5e5;border-radius:0;-webkit-box-shadow:none;box-shadow:none}.well-lg{border-radius:0}.well-sm{border-radius:0}.scrollable::-webkit-scrollbar{width:10px;height:16px}.scrollable::-webkit-scrollbar:hover{background-color:#f3f3f3;border:1px solid #dbdbdb}.scrollable::-webkit-scrollbar-button:end:increment,.scrollable::-webkit-scrollbar-button:start:decrement{display:block;height:0;background-color:transparent}.scrollable::-webkit-scrollbar-track{-webkit-background-clip:padding-box;background-clip:padding-box;border:solid transparent;border-width:0 0 0 4px}.scrollable::-webkit-scrollbar-track-piece{background-color:transparent;border-radius:0}.scrollable::-webkit-scrollbar-thumb{background-color:#515151;background-color:rgba(0,0,0,.2);-webkit-background-clip:padding-box;background-clip:padding-box;border:solid transparent;border-width:0;-webkit-box-shadow:inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07);box-shadow:inset 1px 1px 0 rgba(0,0,0,.1),inset 0 -1px 0 rgba(0,0,0,.07)}.scrollable::-webkit-scrollbar-thumb:hover{background-color:#949494}.scrollable::-webkit-scrollbar-thumb:active{background-color:#3b3b3b;background-color:rgba(0,0,0,.5);-webkit-box-shadow:inset 1px 1px 3px rgba(0,0,0,.35);box-shadow:inset 1px 1px 3px rgba(0,0,0,.35)}.scrollable::-webkit-scrollbar-thumb:horizontal,.scrollable::-webkit-scrollbar-thumb:vertical{background-color:#c6c6c6;border-radius:0}.modal-content{color:#222;border:1px solid #aaa;border:1px solid rgba(0,0,0,.333);border-radius:0;-webkit-box-shadow:0 4px 16px rgba(0,0,0,.2);box-shadow:0 4px 16px rgba(0,0,0,.2)}.modal-backdrop{background-color:#fff}.modal-header .close{font-weight:400;filter:alpha(opacity=40);opacity:.4}.modal-body{padding:15px}.tooltip{font-family:Arial,Helvetica,sans-serif;font-size:11px;font-style:normal;font-weight:400;font-weight:700;line-height:1.4;line-height:1.25;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-break:break-word;word-spacing:normal;word-wrap:normal;white-space:normal;line-break:auto}.tooltip.in{filter:alpha(opacity=100);opacity:1}.tooltip-inner{padding:7px 9px;background-color:#2a2a2a;border:1px solid #fff;border-radius:0}.tooltip-arrow:before{position:absolute;z-index:-1;content:" ";border:7px solid transparent}.tooltip.top .tooltip-arrow,.tooltip.top-left .tooltip-arrow,.tooltip.top-right .tooltip-arrow{bottom:1px;border-top-color:#2a2a2a}.tooltip.top .tooltip-arrow:before,.tooltip.top-left .tooltip-arrow:before,.tooltip.top-right .tooltip-arrow:before{top:-5px;left:-7px;border-top-color:#fff;border-bottom:0 dotted}.tooltip.right .tooltip-arrow{left:1px;border-right-color:#2a2a2a}.tooltip.right .tooltip-arrow:before{top:-7px;right:-5px;border-right-color:#fff;border-left:0 dotted}.tooltip.left .tooltip-arrow{right:1px;border-left-color:#2a2a2a}.tooltip.left .tooltip-arrow:before{top:-7px;left:-5px;border-right:0 dotted;border-left-color:#fff}.tooltip.bottom .tooltip-arrow,.tooltip.bottom-left .tooltip-arrow,.tooltip.bottom-right .tooltip-arrow{top:1px;border-bottom-color:#2a2a2a}.tooltip.bottom .tooltip-arrow:before,.tooltip.bottom-left .tooltip-arrow:before,.tooltip.bottom-right .tooltip-arrow:before{bottom:-5px;left:-7px;border-top:0 dotted;border-bottom-color:#fff}.popover{padding:0;font-family:Arial,Helvetica,sans-serif;font-size:13px;font-style:normal;font-weight:400;line-height:1.4;text-align:left;text-align:start;text-decoration:none;text-shadow:none;text-transform:none;letter-spacing:normal;word-break:normal;word-spacing:normal;word-wrap:normal;white-space:normal;background-color:#fff;border-radius:2px;-webkit-box-shadow:0 2px 10px rgba(0,0,0,.2);box-shadow:0 2px 10px rgba(0,0,0,.2);line-break:auto}.popover-footer,.popover-title{padding:10px;font-size:13px;background-color:#f5f5f5;border-bottom:1px solid #ccc;border-bottom:1px solid rgba(0,0,0,.2);border-radius:0}.popover-footer{border-top:1px solid #ccc;border-top:1px solid rgba(0,0,0,.2);border-bottom:none}.popover-content{padding:10px}.carousel{width:100%;padding:50px;overflow:hidden;background-color:#f5f5f5;background-image:-webkit-linear-gradient(top,#eee 0,#f5f5f5 100%),-webkit-linear-gradient(bottom,#eee 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#eee 0,#f5f5f5 100%),-o-linear-gradient(bottom,#eee 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#eee),to(#f5f5f5)),-webkit-gradient(linear,left bottom,left top,from(#eee),to(#f5f5f5));background-image:linear-gradient(to bottom,#eee 0,#f5f5f5 100%),linear-gradient(to top,#eee 0,#f5f5f5 100%);background-repeat:no-repeat;background-position:0 0,0 100%;-webkit-background-size:100% 10px;background-size:100% 10px}.carousel-control{width:100px;color:#777;text-shadow:none;filter:alpha(opacity=33);opacity:.33}.carousel-control.left{background-image:none}.carousel-control.right{background-image:none}.carousel-control:focus,.carousel-control:hover{color:#777}.carousel-control .icon-next:before,.carousel-control .icon-prev:before{content:''}.carousel-control .icon-prev{position:relative;position:absolute;right:0;display:inline-block}.carousel-control .icon-prev:before{border-radius:20px}.carousel-control .icon-prev:after,.carousel-control .icon-prev:before{position:absolute;width:0;height:0;content:""}.carousel-control .icon-prev:before{border:22px solid transparent}.carousel-control .icon-prev:after{border:19px solid transparent}.carousel-control .icon-prev:after,.carousel-control .icon-prev:before{top:8px;right:100%}.carousel-control .icon-prev:before{margin-top:-22px;border-right:22px solid;border-right-color:#777}.carousel-control .icon-prev:after{margin-top:-19px;border-right:19px solid #f5f5f5}.carousel-control .icon-next{position:relative;position:absolute;right:0;left:50%;display:inline-block}.carousel-control .icon-next:before{border-radius:20px}.carousel-control .icon-next:after,.carousel-control .icon-next:before{position:absolute;width:0;height:0;content:""}.carousel-control .icon-next:before{border:22px solid transparent}.carousel-control .icon-next:after{border:19px solid transparent}.carousel-control .icon-next:after,.carousel-control .icon-next:before{top:8px;left:100%}.carousel-control .icon-next:before{margin-top:-22px;border-left:22px solid;border-left-color:#777}.carousel-control .icon-next:after{margin-top:-19px;border-left:19px solid #f5f5f5}.carousel-control .icon-next:after,.carousel-control .icon-next:before{left:50%}.carousel-indicators{bottom:5px;left:0;width:100%;margin-left:0}.carousel-indicators li{background-color:#c2c2c2;border:1px solid #c2c2c2}.carousel-indicators .active{width:10px;height:10px;margin:1px;background-color:#444;border:1px solid #444}.carousel-caption{right:0;bottom:0;left:0;padding:10px;color:#fff;text-shadow:none;background-color:#262626;background-color:rgba(0,0,0,.55)} +/*# sourceMappingURL=todc-bootstrap.min.css.map */ \ No newline at end of file diff --git a/src/blog/static/assets/img/checkmark.png b/src/blog/static/assets/img/checkmark.png new file mode 100644 index 0000000000000000000000000000000000000000..4bd0eb350087ae1c5dedf8dfce9d24272d6db7a8 GIT binary patch literal 221 zcmeAS@N?(olHy`uVBq!ia0vp^Vj#@H1|*Mc$*~4fjKx9jP7LeL$-D$|rhB?LhIn`< zrz{XmSTb=Z&*38p6En+u{{Q{|+S|v#(9kHqZt~J1u~=QV01lshZx0+fvtho3b7I!N zzo`X(CakVvh~#fO=oFl9W?*1+b#juy;s5o$?>AkQcH>Qx;N@~rlJFJ%ut7mhgTe~DWM4f;?+@i literal 0 HcmV?d00001 diff --git a/src/blog/static/assets/js/ie-emulation-modes-warning.js b/src/blog/static/assets/js/ie-emulation-modes-warning.js new file mode 100644 index 0000000..3f97ba5 --- /dev/null +++ b/src/blog/static/assets/js/ie-emulation-modes-warning.js @@ -0,0 +1,51 @@ +// NOTICE!! DO NOT USE ANY OF THIS JAVASCRIPT +// IT'S JUST JUNK FOR OUR DOCS! +// ++++++++++++++++++++++++++++++++++++++++++ +/*! + * Copyright 2014-2015 Twitter, Inc. + * + * Licensed under the Creative Commons Attribution 3.0 Unported License. For + * details, see https://creativecommons.org/licenses/by/3.0/. + */ +// Intended to prevent false-positive bug reports about Bootstrap not working properly in old versions of IE due to folks testing using IE's unreliable emulation modes. +(function () { + 'use strict'; + + function emulatedIEMajorVersion() { + var groups = /MSIE ([0-9.]+)/.exec(window.navigator.userAgent) + if (groups === null) { + return null + } + var ieVersionNum = parseInt(groups[1], 10) + var ieMajorVersion = Math.floor(ieVersionNum) + return ieMajorVersion + } + + function actualNonEmulatedIEMajorVersion() { + // Detects the actual version of IE in use, even if it's in an older-IE emulation mode. + // IE JavaScript conditional compilation docs: https://msdn.microsoft.com/library/121hztk3%28v=vs.94%29.aspx + // @cc_on docs: https://msdn.microsoft.com/library/8ka90k2e%28v=vs.94%29.aspx + var jscriptVersion = new Function('/*@cc_on return @_jscript_version; @*/')() // jshint ignore:line + if (jscriptVersion === undefined) { + return 11 // IE11+ not in emulation mode + } + if (jscriptVersion < 9) { + return 8 // IE8 (or lower; haven't tested on IE<8) + } + return jscriptVersion // IE9 or IE10 in any mode, or IE11 in non-IE11 mode + } + + var ua = window.navigator.userAgent + if (ua.indexOf('Opera') > -1 || ua.indexOf('Presto') > -1) { + return // Opera, which might pretend to be IE + } + var emulated = emulatedIEMajorVersion() + if (emulated === null) { + return // Not IE + } + var nonEmulated = actualNonEmulatedIEMajorVersion() + + if (emulated !== nonEmulated) { + window.alert('WARNING: You appear to be using IE' + nonEmulated + ' in IE' + emulated + ' emulation mode.\nIE emulation modes can behave significantly differently from ACTUAL older versions of IE.\nPLEASE DON\'T FILE BOOTSTRAP BUGS based on testing in IE emulation modes!') + } +})(); diff --git a/src/blog/static/assets/js/ie10-viewport-bug-workaround.js b/src/blog/static/assets/js/ie10-viewport-bug-workaround.js new file mode 100644 index 0000000..479a6eb --- /dev/null +++ b/src/blog/static/assets/js/ie10-viewport-bug-workaround.js @@ -0,0 +1,23 @@ +/*! + * IE10 viewport hack for Surface/desktop Windows 8 bug + * Copyright 2014-2015 Twitter, Inc. + * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) + */ + +// See the Getting Started docs for more information: +// http://getbootstrap.com/getting-started/#support-ie10-width + +(function () { + 'use strict'; + + if (navigator.userAgent.match(/IEMobile\/10\.0/)) { + var msViewportStyle = document.createElement('style') + msViewportStyle.appendChild( + document.createTextNode( + '@-ms-viewport{width:auto!important}' + ) + ) + document.querySelector('head').appendChild(msViewportStyle) + } + +})(); diff --git a/src/blog/static/blog/css/ie.css b/src/blog/static/blog/css/ie.css new file mode 100644 index 0000000..706f510 --- /dev/null +++ b/src/blog/static/blog/css/ie.css @@ -0,0 +1,273 @@ +/* +Styles for older IE versions (previous to IE9). +*/ + +body { + background-color: #e6e6e6; +} +body.custom-background-empty { + background-color: #fff; +} +body.custom-background-empty .site, +body.custom-background-white .site { + box-shadow: none; + margin-bottom: 0; + margin-top: 0; + padding: 0; +} +.assistive-text, +.site .screen-reader-text { + clip: rect(1px 1px 1px 1px); +} +.full-width .site-content { + float: none; + width: 100%; +} +img.size-full, +img.size-large, +img.header-image, +img.wp-post-image, +img[class*="align"], +img[class*="wp-image-"], +img[class*="attachment-"] { + width: auto; /* Prevent stretching of full-size and large-size images with height and width attributes in IE8 */ +} +.author-avatar { + float: left; + margin-top: 8px; + margin-top: 0.571428571rem; +} +.author-description { + float: right; + width: 80%; +} +.site { + box-shadow: 0 2px 6px rgba(100, 100, 100, 0.3); + margin: 48px auto; + max-width: 960px; + overflow: hidden; + padding: 0 40px; +} +.site-content { + float: left; + width: 65.104166667%; +} +body.template-front-page .site-content, +body.attachment .site-content, +body.full-width .site-content { + width: 100%; +} +.widget-area { + float: right; + width: 26.041666667%; +} +.site-header h1, +.site-header h2 { + text-align: left; +} +.site-header h1 { + font-size: 26px; + line-height: 1.846153846; +} +.main-navigation ul.nav-menu, +.main-navigation div.nav-menu > ul { + border-bottom: 1px solid #ededed; + border-top: 1px solid #ededed; + display: inline-block !important; + text-align: left; + width: 100%; +} +.main-navigation ul { + margin: 0; + text-indent: 0; +} +.main-navigation li a, +.main-navigation li { + display: inline-block; + text-decoration: none; +} +.ie7 .main-navigation li a, +.ie7 .main-navigation li { + display: inline; +} +.main-navigation li a { + border-bottom: 0; + color: #6a6a6a; + line-height: 3.692307692; + text-transform: uppercase; +} +.main-navigation li a:hover { + color: #000; +} +.main-navigation li { + margin: 0 40px 0 0; + position: relative; +} +.main-navigation li ul { + margin: 0; + padding: 0; + position: absolute; + top: 100%; + z-index: 1; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px); +} +.ie7 .main-navigation li ul { + clip: inherit; + display: none; + left: 0; + overflow: visible; +} +.main-navigation li ul ul, +.ie7 .main-navigation li ul ul { + top: 0; + left: 100%; +} +.main-navigation ul li:hover > ul, +.main-navigation ul li:focus > ul, +.main-navigation .focus > ul { + border-left: 0; + clip: inherit; + overflow: inherit; + height: inherit; + width: inherit; +} +.ie7 .main-navigation ul li:hover > ul, +.ie7 .main-navigation ul li:focus > ul { + display: block; +} +.main-navigation li ul li a { + background: #efefef; + border-bottom: 1px solid #ededed; + display: block; + font-size: 11px; + line-height: 2.181818182; + padding: 8px 10px; + width: 180px; +} +.main-navigation li ul li a:hover { + background: #e3e3e3; + color: #444; +} +.main-navigation .current-menu-item > a, +.main-navigation .current-menu-ancestor > a, +.main-navigation .current_page_item > a, +.main-navigation .current_page_ancestor > a { + color: #636363; + font-weight: bold; +} +.main-navigation .menu-toggle { + display: none; +} +.entry-header .entry-title { + font-size: 22px; +} +#respond form input[type="text"] { + width: 46.333333333%; +} +#respond form textarea.blog-textarea { + width: 79.666666667%; +} +.template-front-page .site-content, +.template-front-page article { + overflow: hidden; +} +.template-front-page.has-post-thumbnail article { + float: left; + width: 47.916666667%; +} +.entry-page-image { + float: right; + margin-bottom: 0; + width: 47.916666667%; +} +/* IE Front Page Template Widget fix */ +.template-front-page .widget-area { + clear: both; +} +.template-front-page .widget { + width: 100% !important; + border: none; +} +.template-front-page .widget-area .widget, +.template-front-page .first.front-widgets, +.template-front-page.two-sidebars .widget-area .front-widgets { + float: left; + margin-bottom: 24px; + width: 51.875%; +} +.template-front-page .second.front-widgets, +.template-front-page .widget-area .widget:nth-child(odd) { + clear: right; +} +.template-front-page .first.front-widgets, +.template-front-page .second.front-widgets, +.template-front-page.two-sidebars .widget-area .front-widgets + .front-widgets { + float: right; + margin: 0 0 24px; + width: 39.0625%; +} +.template-front-page.two-sidebars .widget, +.template-front-page.two-sidebars .widget:nth-child(even) { + float: none; + width: auto; +} +/* add input font for ul { + text-align: right; +} +.rtl .main-navigation ul li ul li, +.rtl .main-navigation ul li ul li ul li { + margin-left: 40px; + margin-right: auto; +} +.rtl .main-navigation li ul ul { + position: absolute; + bottom: 0; + right: 100%; + z-index: 1; +} +.ie7 .rtl .main-navigation li ul ul { + position: absolute; + bottom: 0; + right: 100%; + z-index: 1; +} +.ie7 .rtl .main-navigation ul li { + z-index: 99; +} +.ie7 .rtl .main-navigation li ul { + position: absolute; + bottom: 100%; + right: 0; + z-index: 1; +} +.ie7 .rtl .main-navigation li { + margin-right: auto; + margin-left: 40px; +} +.ie7 .rtl .main-navigation li ul ul ul { + position: relative; + z-index: 1; +} \ No newline at end of file diff --git a/src/blog/static/blog/css/nprogress.css b/src/blog/static/blog/css/nprogress.css new file mode 100644 index 0000000..90c7b6c --- /dev/null +++ b/src/blog/static/blog/css/nprogress.css @@ -0,0 +1,74 @@ +/* Make clicks pass-through */ +#nprogress { + pointer-events: none; +} + +#nprogress .bar { + background: red; + + position: fixed; + z-index: 1031; + top: 0; + left: 0; + + width: 100%; + height: 2px; +} + +/* Fancy blur effect */ +#nprogress .peg { + display: block; + position: absolute; + right: 0px; + width: 100px; + height: 100%; + box-shadow: 0 0 10px #29d, 0 0 5px #29d; + opacity: 1.0; + + -webkit-transform: rotate(3deg) translate(0px, -4px); + -ms-transform: rotate(3deg) translate(0px, -4px); + transform: rotate(3deg) translate(0px, -4px); +} + +/* Remove these to get rid of the spinner */ +#nprogress .spinner { + display: block; + position: fixed; + z-index: 1031; + top: 15px; + right: 15px; +} + +#nprogress .spinner-icon { + width: 18px; + height: 18px; + box-sizing: border-box; + + border: solid 2px transparent; + border-top-color: red; + border-left-color: red; + border-radius: 50%; + + -webkit-animation: nprogress-spinner 400ms linear infinite; + animation: nprogress-spinner 400ms linear infinite; +} + +.nprogress-custom-parent { + overflow: hidden; + position: relative; +} + +.nprogress-custom-parent #nprogress .spinner, +.nprogress-custom-parent #nprogress .bar { + position: absolute; +} + +@-webkit-keyframes nprogress-spinner { + 0% { -webkit-transform: rotate(0deg); } + 100% { -webkit-transform: rotate(360deg); } +} +@keyframes nprogress-spinner { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + diff --git a/src/blog/static/blog/css/oauth_style.css b/src/blog/static/blog/css/oauth_style.css new file mode 100644 index 0000000..8af78af --- /dev/null +++ b/src/blog/static/blog/css/oauth_style.css @@ -0,0 +1,305 @@ + +.icon-sn-google { + background-position: 0 -28px; +} + +.icon-sn-bg-google { + background-color: #4285f4; + background-position: 0 0; +} + +.fa-sn-google { + color: #4285f4; +} + +.icon-sn-github { + background-position: -28px -28px; +} + +.icon-sn-bg-github { + background-color: #333; + background-position: -28px 0; +} + +.fa-sn-github { + color: #333; +} + +.icon-sn-weibo { + background-position: -56px -28px; +} + +.icon-sn-bg-weibo { + background-color: #e90d24; + background-position: -56px 0; +} + +.fa-sn-weibo { + color: #e90d24; +} + +.icon-sn-qq { + background-position: -84px -28px; +} + +.icon-sn-bg-qq { + background-color: #0098e6; + background-position: -84px 0; +} + +.fa-sn-qq { + color: #0098e6; +} + +.icon-sn-twitter { + background-position: -112px -28px; +} + +.icon-sn-bg-twitter { + background-color: #50abf1; + background-position: -112px 0; +} + +.fa-sn-twitter { + color: #50abf1; +} + +.icon-sn-facebook { + background-position: -140px -28px; +} + +.icon-sn-bg-facebook { + background-color: #4862a3; + background-position: -140px 0; +} + +.fa-sn-facebook { + color: #4862a3; +} + +.icon-sn-renren { + background-position: -168px -28px; +} + +.icon-sn-bg-renren { + background-color: #197bc8; + background-position: -168px 0; +} + +.fa-sn-renren { + color: #197bc8; +} + +.icon-sn-tqq { + background-position: -196px -28px; +} + +.icon-sn-bg-tqq { + background-color: #1f9ed2; + background-position: -196px 0; +} + +.fa-sn-tqq { + color: #1f9ed2; +} + +.icon-sn-douban { + background-position: -224px -28px; +} + +.icon-sn-bg-douban { + background-color: #279738; + background-position: -224px 0; +} + +.fa-sn-douban { + color: #279738; +} + +.icon-sn-weixin { + background-position: -252px -28px; +} + +.icon-sn-bg-weixin { + background-color: #00b500; + background-position: -252px 0; +} + +.fa-sn-weixin { + color: #00b500; +} + +.icon-sn-dotted { + background-position: -280px -28px; +} + +.icon-sn-bg-dotted { + background-color: #eee; + background-position: -280px 0; +} + +.fa-sn-dotted { + color: #eee; +} + +.icon-sn-site { + background-position: -308px -28px; +} + +.icon-sn-bg-site { + background-color: #00b500; + background-position: -308px 0; +} + +.fa-sn-site { + color: #00b500; +} + +.icon-sn-linkedin { + background-position: -336px -28px; +} + +.icon-sn-bg-linkedin { + background-color: #0077b9; + background-position: -336px 0; +} + +.fa-sn-linkedin { + color: #0077b9; +} + +[class*=icon-sn-] { + display: inline-block; + background-image: url('../img/icon-sn.svg'); + background-repeat: no-repeat; + width: 28px; + height: 28px; + vertical-align: middle; + background-size: auto 56px; +} + +[class*=icon-sn-]:hover { + opacity: .8; + filter: alpha(opacity=80); +} + +.btn-sn-google { + background: #4285f4; +} + +.btn-sn-google:active, .btn-sn-google:focus, .btn-sn-google:hover { + background: #2a75f3; +} + +.btn-sn-github { + background: #333; +} + +.btn-sn-github:active, .btn-sn-github:focus, .btn-sn-github:hover { + background: #262626; +} + +.btn-sn-weibo { + background: #e90d24; +} + +.btn-sn-weibo:active, .btn-sn-weibo:focus, .btn-sn-weibo:hover { + background: #d10c20; +} + +.btn-sn-qq { + background: #0098e6; +} + +.btn-sn-qq:active, .btn-sn-qq:focus, .btn-sn-qq:hover { + background: #0087cd; +} + +.btn-sn-twitter { + background: #50abf1; +} + +.btn-sn-twitter:active, .btn-sn-twitter:focus, .btn-sn-twitter:hover { + background: #38a0ef; +} + +.btn-sn-facebook { + background: #4862a3; +} + +.btn-sn-facebook:active, .btn-sn-facebook:focus, .btn-sn-facebook:hover { + background: #405791; +} + +.btn-sn-renren { + background: #197bc8; +} + +.btn-sn-renren:active, .btn-sn-renren:focus, .btn-sn-renren:hover { + background: #166db1; +} + +.btn-sn-tqq { + background: #1f9ed2; +} + +.btn-sn-tqq:active, .btn-sn-tqq:focus, .btn-sn-tqq:hover { + background: #1c8dbc; +} + +.btn-sn-douban { + background: #279738; +} + +.btn-sn-douban:active, .btn-sn-douban:focus, .btn-sn-douban:hover { + background: #228330; +} + +.btn-sn-weixin { + background: #00b500; +} + +.btn-sn-weixin:active, .btn-sn-weixin:focus, .btn-sn-weixin:hover { + background: #009c00; +} + +.btn-sn-dotted { + background: #eee; +} + +.btn-sn-dotted:active, .btn-sn-dotted:focus, .btn-sn-dotted:hover { + background: #e1e1e1; +} + +.btn-sn-site { + background: #00b500; +} + +.btn-sn-site:active, .btn-sn-site:focus, .btn-sn-site:hover { + background: #009c00; +} + +.btn-sn-linkedin { + background: #0077b9; +} + +.btn-sn-linkedin:active, .btn-sn-linkedin:focus, .btn-sn-linkedin:hover { + background: #0067a0; +} + +[class*=btn-sn-], [class*=btn-sn-]:active, [class*=btn-sn-]:focus, [class*=btn-sn-]:hover { + border: none; + color: #fff; +} + +.btn-sn-more { + padding: 0; +} + +.btn-sn-more, .btn-sn-more:active, .btn-sn-more:hover { + box-shadow: none; +} + +[class*=btn-sn-] [class*=icon-sn-] { + background-color: transparent; +} \ No newline at end of file diff --git a/src/blog/static/blog/css/style.css b/src/blog/static/blog/css/style.css new file mode 100644 index 0000000..bd317db --- /dev/null +++ b/src/blog/static/blog/css/style.css @@ -0,0 +1,2706 @@ +html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, big, cite, code, del, dfn, em, img, ins, kbd, q, s, samp, small, strike, strong, sub, sup, tt, var, b, u, i, center, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, canvas, details, embed, figure, figcaption, footer, header, hgroup, menu, nav, output, ruby, section, summary, time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + vertical-align: baseline; +} + +body { + line-height: 1; +} + +ol, +ul { + list-style: none; +} + +blockquote, +q { + quotes: none; +} + +blockquote:before, +blockquote:after, +q:before, +q:after { + content: ''; + content: none; +} + +table { + border-collapse: collapse; + border-spacing: 0; +} + +caption, +th, +td { + font-weight: normal; + text-align: left; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + clear: both; +} + +html { + overflow-y: scroll; + font-size: 100%; + -webkit-text-size-adjust: 100%; + -ms-text-size-adjust: 100%; +} + +a:focus { + outline: thin dotted; +} + +article, +aside, +details, +figcaption, +figure, +footer, +header, +hgroup, +nav, +section { + display: block; +} + +audio, +canvas, +video { + display: inline-block; +} + +audio:not([controls]) { + display: none; +} + +del { + color: #333; +} + +ins { + background: #fff9c0; + text-decoration: none; +} + +hr { + background-color: #ccc; + border: 0; + height: 1px; + margin: 24px; + margin-bottom: 1.714285714rem; +} + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sup { + top: -0.5em; +} + +sub { + bottom: -0.25em; +} + +small { + font-size: smaller; +} + +img { + border: 0; + -ms-interpolation-mode: bicubic; +} + +/* Clearing floats */ +.clear:after, +.wrapper:after, +.format-status .entry-header:after { + clear: both; +} + +.clear:before, +.clear:after, +.wrapper:before, +.wrapper:after, +.format-status .entry-header:before, +.format-status .entry-header:after { + display: table; + content: ""; +} + + +/* =Repeatable patterns +-------------------------------------------------------------- */ + +/* Small headers */ +.archive-title, +.page-title, +.widget-title, +.entry-content th, +.comment-content th { + font-size: 11px; + font-size: 0.785714286rem; + line-height: 2.181818182; + font-weight: bold; + text-transform: uppercase; + color: #636363; +} + +/* Shared Post Format styling */ +article.format-quote footer.entry-meta, +article.format-link footer.entry-meta, +article.format-status footer.entry-meta { + font-size: 11px; + font-size: 0.785714286rem; + line-height: 2.181818182; +} + +/* Form fields, general styles first */ +button, +input, +select, +textarea { + border: 1px solid #ccc; + border-radius: 3px; + font-family: inherit; + padding: 6px; + padding: 0.428571429rem; +} + +button, +input { + line-height: normal; +} + +textarea { + font-size: 100%; + overflow: auto; + vertical-align: top; +} + +/* Reset non-text input types */ +input[type="checkbox"], +input[type="radio"], +input[type="file"], +input[type="hidden"], +input[type="image"], +input[type="color"] { + border: 0; + border-radius: 0; + padding: 0; +} + +/* Buttons */ +.menu-toggle, +input[type="submit"], +input[type="button"], +input[type="reset"], +article.post-password-required input[type=submit], +.bypostauthor cite span { + padding: 6px 10px; + padding: 0.428571429rem 0.714285714rem; + font-size: 11px; + font-size: 0.785714286rem; + line-height: 1.428571429; + font-weight: normal; + color: #7c7c7c; + background-color: #e6e6e6; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(top, #f4f4f4, #e6e6e6); + background-image: -ms-linear-gradient(top, #f4f4f4, #e6e6e6); + background-image: -webkit-linear-gradient(top, #f4f4f4, #e6e6e6); + background-image: -o-linear-gradient(top, #f4f4f4, #e6e6e6); + background-image: linear-gradient(to bottom, #f4f4f4, #e6e6e6); + border: 1px solid #d2d2d2; + border-radius: 3px; + box-shadow: 0 1px 2px rgba(64, 64, 64, 0.1); +} + +.menu-toggle, +button, +input[type="submit"], +input[type="button"], +input[type="reset"] { + cursor: pointer; +} + +button[disabled], +input[disabled] { + cursor: default; +} + +.menu-toggle:hover, +.menu-toggle:focus, +button:hover, +input[type="submit"]:hover, +input[type="button"]:hover, +input[type="reset"]:hover, +article.post-password-required input[type=submit]:hover { + color: #5e5e5e; + background-color: #ebebeb; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(top, #f9f9f9, #ebebeb); + background-image: -ms-linear-gradient(top, #f9f9f9, #ebebeb); + background-image: -webkit-linear-gradient(top, #f9f9f9, #ebebeb); + background-image: -o-linear-gradient(top, #f9f9f9, #ebebeb); + background-image: linear-gradient(to bottom, #f9f9f9, #ebebeb); +} + +.menu-toggle:active, +.menu-toggle.toggled-on, +button:active, +input[type="submit"]:active, +input[type="button"]:active, +input[type="reset"]:active { + color: #757575; + background-color: #e1e1e1; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(top, #ebebeb, #e1e1e1); + background-image: -ms-linear-gradient(top, #ebebeb, #e1e1e1); + background-image: -webkit-linear-gradient(top, #ebebeb, #e1e1e1); + background-image: -o-linear-gradient(top, #ebebeb, #e1e1e1); + background-image: linear-gradient(to bottom, #ebebeb, #e1e1e1); + box-shadow: inset 0 0 8px 2px #c6c6c6, 0 1px 0 0 #f4f4f4; + border-color: transparent; +} + +.bypostauthor cite span { + color: #fff; + background-color: #21759b; + background-image: none; + border: 1px solid #1f6f93; + border-radius: 2px; + box-shadow: none; + padding: 0; +} + +/* Responsive images */ +.entry-content img, +.comment-content img, +.widget img { + max-width: 100%; /* Fluid images for posts, comments, and widgets */ +} + +img[class*="align"], +img[class*="wp-image-"], +img[class*="attachment-"] { + height: auto; /* Make sure images with WordPress-added height and width attributes are scaled correctly */ +} + +img.size-full, +img.size-large, +img.header-image, +img.wp-post-image { + max-width: 100%; + height: auto; /* Make sure images with WordPress-added height and width attributes are scaled correctly */ +} + +/* Make sure videos and embeds fit their containers */ +embed, +iframe, +object, +video { + max-width: 100%; +} + +.entry-content .twitter-tweet-rendered { + max-width: 100% !important; /* Override the Twitter embed fixed width */ +} + +/* Images */ +.alignleft { + float: left; +} + +.alignright { + float: right; +} + +.aligncenter { + display: block; + margin-left: auto; + margin-right: auto; +} + +.entry-content img, +.comment-content img, +.widget img, +img.header-image, +.author-avatar img, +img.wp-post-image { + /* Add fancy borders to all WordPress-added images but not things like badges and icons and the like */ + border-radius: 3px; + box-shadow: 0 1px 4px rgba(0, 0, 0, 0.2); +} + +.wp-caption { + max-width: 100%; /* Keep wide captions from overflowing their container. */ + padding: 4px; +} + +.wp-caption .wp-caption-text, +.gallery-caption, +.entry-caption { + font-style: italic; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 2; + color: #757575; +} + +img.wp-smiley, +.rsswidget img { + border: 0; + border-radius: 0; + box-shadow: none; + margin-bottom: 0; + margin-top: 0; + padding: 0; +} + +.entry-content dl.gallery-item { + margin: 0; +} + +.gallery-item a, +.gallery-caption { + width: 90%; +} + +.gallery-item a { + display: block; +} + +.gallery-caption a { + display: inline; +} + +.gallery-columns-1 .gallery-item a { + max-width: 100%; + width: auto; +} + +.gallery .gallery-icon img { + height: auto; + max-width: 90%; + padding: 5%; +} + +.gallery-columns-1 .gallery-icon img { + padding: 3%; +} + +/* Navigation */ +.site-content nav { + clear: both; + line-height: 2; + overflow: hidden; +} + +#nav-above { + padding: 24px 0; + padding: 1.714285714rem 0; +} + +#nav-above { + display: none; +} + +.paged #nav-above { + display: block; +} + +.nav-previous, +.previous-image { + float: left; + width: 50%; +} + +.nav-next, +.next-image { + float: right; + text-align: right; + width: 50%; +} + +.nav-single + .comments-area, +#comment-nav-above { + margin: 48px 0; + margin: 3.428571429rem 0; +} + +/* Author profiles */ +.author .archive-header { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +.author-info { + border-top: 1px solid #ededed; + margin: 24px 0; + margin: 1.714285714rem 0; + padding-top: 24px; + padding-top: 1.714285714rem; + overflow: hidden; +} + +.author-description p { + color: #757575; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; +} + +.author.archive .author-info { + border-top: 0; + margin: 0 0 48px; + margin: 0 0 3.428571429rem; +} + +.author.archive .author-avatar { + margin-top: 0; +} + + +/* =Basic structure +-------------------------------------------------------------- */ + +/* Body, links, basics */ +html { + font-size: 87.5%; +} + +body { + font-size: 14px; + font-size: 1rem; + font-family: Helvetica, Arial, sans-serif; + text-rendering: optimizeLegibility; + color: #444; +} + +body.custom-font-enabled { + font-family: "Open Sans", Helvetica, Arial, sans-serif; +} + +a { + outline: none; + color: #21759b; +} + +a:hover { + color: #0f3647; +} + +/* Assistive text */ +.assistive-text, +.site .screen-reader-text { + position: absolute !important; + clip: rect(1px, 1px, 1px, 1px); + overflow: hidden; + height: 1px; + width: 1px; +} + +.main-navigation .assistive-text:focus, +.site .screen-reader-text:hover, +.site .screen-reader-text:active, +.site .screen-reader-text:focus { + background: #fff; + border: 2px solid #333; + border-radius: 3px; + clip: auto !important; + color: #000; + display: block; + font-size: 12px; + height: auto; + padding: 12px; + position: absolute; + top: 5px; + left: 5px; + width: auto; + z-index: 100000; /* Above WP toolbar */ +} + +/* Page structure */ +.site { + padding: 0 24px; + padding: 0 1.714285714rem; + background-color: #fff; +} + +.site-content { + margin: 24px 0 0; + margin: 1.714285714rem 0 0; +} + +.widget-area { + margin: 24px 0 0; + margin: 1.714285714rem 0 0; +} + +/* Header */ +.site-header { + padding: 24px 0; + padding: 1.714285714rem 0; +} + +.site-header h1, +.site-header h2 { + text-align: center; +} + +.site-header h1 a, +.site-header h2 a { + color: #515151; + display: inline-block; + text-decoration: none; +} + +.site-header h1 a:hover, +.site-header h2 a:hover { + color: #21759b; +} + +.site-header h1 { + font-size: 24px; + font-size: 1.714285714rem; + line-height: 1.285714286; + margin-bottom: 14px; + margin-bottom: 1rem; +} + +.site-header h2 { + font-weight: normal; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; + color: #757575; +} + +.header-image { + margin-top: 24px; + margin-top: 1.714285714rem; +} + +/* Navigation Menu */ +.main-navigation { + margin-top: 24px; + margin-top: 1.714285714rem; + text-align: center; +} + +.main-navigation li { + margin-top: 24px; + margin-top: 1.714285714rem; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 1.42857143; +} + +.main-navigation a { + color: #5e5e5e; +} + +.main-navigation a:hover, +.main-navigation a:focus { + color: #21759b; +} + +.main-navigation ul.nav-menu, +.main-navigation div.nav-menu > ul { + display: none; +} + +.main-navigation ul.nav-menu.toggled-on, +.menu-toggle { + display: inline-block; +} + +/* Banner */ +section[role="banner"] { + margin-bottom: 48px; + margin-bottom: 3.428571429rem; +} + +/* Sidebar */ +.widget-area .widget { + -webkit-hyphens: auto; + -moz-hyphens: auto; + hyphens: auto; + margin-bottom: 48px; + margin-bottom: 3.428571429rem; + word-wrap: break-word; +} + +.widget-area .widget h3 { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +.widget-area .widget p, +.widget-area .widget li, +.widget-area .widget .textwidget { + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; +} + +.widget-area .widget p { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +.widget-area .textwidget ul, +.widget-area .textwidget ol { + list-style: disc outside; + margin: 0 0 24px; + margin: 0 0 1.714285714rem; +} + +.widget-area .textwidget li > ul, +.widget-area .textwidget li > ol { + margin-bottom: 0; +} + +.widget-area .textwidget ol { + list-style: decimal; +} + +.widget-area .textwidget li { + margin-left: 36px; + margin-left: 2.571428571rem; +} + +.widget-area .widget a { + color: #757575; +} + +.widget-area .widget a:hover { + color: #21759b; +} + +.widget-area .widget a:visited { + color: #9f9f9f; +} + +.widget-area #s { + width: 53.66666666666%; /* define a width to avoid dropping a wider submit button */ +} + +/* Footer */ +footer[role="contentinfo"] { + border-top: 1px solid #ededed; + clear: both; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 2; + max-width: 960px; + max-width: 68.571428571rem; + margin-top: 24px; + margin-top: 1.714285714rem; + margin-left: auto; + margin-right: auto; + padding: 24px 0; + padding: 1.714285714rem 0; +} + +footer[role="contentinfo"] a { + color: #686868; +} + +footer[role="contentinfo"] a:hover { + color: #21759b; +} + +.site-info span[role=separator] { + padding: 0 0.3em 0 0.6em; +} + +.site-info span[role=separator]::before { + content: '\002f'; +} + + +/* =Main content and comment content +-------------------------------------------------------------- */ + +.entry-meta { + clear: both; +} + +.entry-header { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +.entry-header img.wp-post-image { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +.entry-header .entry-title { + font-size: 20px; + font-size: 1.428571429rem; + line-height: 1.2; + font-weight: normal; +} + +.entry-header .entry-title a { + text-decoration: none; +} + +.entry-header .entry-format { + margin-top: 24px; + margin-top: 1.714285714rem; + font-weight: normal; +} + +.entry-header .comments-link { + margin-top: 24px; + margin-top: 1.714285714rem; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; + color: #757575; +} + +.comments-link a, +.entry-meta a { + color: #757575; +} + +.comments-link a:hover, +.entry-meta a:hover { + color: #21759b; +} + +article.sticky .featured-post { + border-top: 4px double #ededed; + border-bottom: 4px double #ededed; + color: #757575; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 3.692307692; + margin-bottom: 24px; + margin-bottom: 1.714285714rem; + text-align: center; +} + +.entry-content, +.entry-summary, +.mu_register { + line-height: 1.714285714; +} + +.entry-content h1, +.comment-content h1, +.entry-content h2, +.comment-content h2, +.entry-content h3, +.comment-content h3, +.entry-content h4, +.comment-content h4, +.entry-content h5, +.comment-content h5, +.entry-content h6, +.comment-content h6 { + margin: 24px 0; + margin: 1.714285714rem 0; + line-height: 1.714285714; +} + +.entry-content h1, +.comment-content h1 { + font-size: 21px; + font-size: 1.5rem; + line-height: 1.5; +} + +.entry-content h2, +.comment-content h2, +.mu_register h2 { + font-size: 18px; + font-size: 1.285714286rem; + line-height: 1.6; +} + +.entry-content h3, +.comment-content h3 { + font-size: 16px; + font-size: 1.142857143rem; + line-height: 1.846153846; +} + +.entry-content h4, +.comment-content h4 { + font-size: 14px; + font-size: 1rem; + line-height: 1.846153846; +} + +.entry-content h5, +.comment-content h5 { + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; +} + +.entry-content h6, +.comment-content h6 { + font-size: 12px; + font-size: 0.857142857rem; + line-height: 1.846153846; +} + +.entry-content p, +.entry-summary p, +.comment-content p, +.mu_register p { + margin: 0 0 24px; + margin: 0 0 1.714285714rem; + line-height: 1.714285714; +} + +.entry-content a:visited, +.comment-content a:visited { + color: #9f9f9f; +} + +.entry-content .more-link { + white-space: nowrap; +} + +.entry-content ol, +.comment-content ol, +.entry-content ul, +.comment-content ul, +.mu_register ul { + margin: 0 0 24px; + margin: 0 0 1.714285714rem; + line-height: 1.714285714; +} + +.entry-content ul ul, +.comment-content ul ul, +.entry-content ol ol, +.comment-content ol ol, +.entry-content ul ol, +.comment-content ul ol, +.entry-content ol ul, +.comment-content ol ul { + margin-bottom: 0; +} + +.entry-content ul, +.comment-content ul, +.mu_register ul { + list-style: disc outside; +} + +.entry-content ol, +.comment-content ol { + list-style: decimal outside; +} + +.entry-content li, +.comment-content li, +.mu_register li { + margin: 0 0 0 36px; + margin: 0 0 0 2.571428571rem; +} + +.entry-content blockquote, +.comment-content blockquote { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; + padding: 24px; + padding: 1.714285714rem; + font-style: italic; +} + +.entry-content blockquote p:last-child, +.comment-content blockquote p:last-child { + margin-bottom: 0; +} + +.entry-content code, +.comment-content code { + font-family: Consolas, Monaco, Lucida Console, monospace; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 2; +} + +.entry-content pre, +.comment-content pre { + border: 1px solid #ededed; + color: #666; + font-family: Consolas, Monaco, Lucida Console, monospace; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 1.714285714; + margin: 24px 0; + margin: 1.714285714rem 0; + overflow: auto; + padding: 24px; + padding: 1.714285714rem; +} + +.entry-content pre code, +.comment-content pre code { + display: block; +} + +.entry-content abbr, +.comment-content abbr, +.entry-content dfn, +.comment-content dfn, +.entry-content acronym, +.comment-content acronym { + border-bottom: 1px dotted #666; + cursor: help; +} + +.entry-content address, +.comment-content address { + display: block; + line-height: 1.714285714; + margin: 0 0 24px; + margin: 0 0 1.714285714rem; +} + +img.alignleft, +.wp-caption.alignleft { + margin: 12px 24px 12px 0; + margin: 0.857142857rem 1.714285714rem 0.857142857rem 0; +} + +img.alignright, +.wp-caption.alignright { + margin: 12px 0 12px 24px; + margin: 0.857142857rem 0 0.857142857rem 1.714285714rem; +} + +img.aligncenter, +.wp-caption.aligncenter { + clear: both; + margin-top: 12px; + margin-top: 0.857142857rem; + margin-bottom: 12px; + margin-bottom: 0.857142857rem; +} + +.entry-content embed, +.entry-content iframe, +.entry-content object, +.entry-content video { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +.entry-content dl, +.comment-content dl { + margin: 0 24px; + margin: 0 1.714285714rem; +} + +.entry-content dt, +.comment-content dt { + font-weight: bold; + line-height: 1.714285714; +} + +.entry-content dd, +.comment-content dd { + line-height: 1.714285714; + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +.entry-content table, +.comment-content table { + border-bottom: 1px solid #ededed; + color: #757575; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 2; + margin: 0 0 24px; + margin: 0 0 1.714285714rem; + width: 100%; +} + +.entry-content table caption, +.comment-content table caption { + font-size: 16px; + font-size: 1.142857143rem; + margin: 24px 0; + margin: 1.714285714rem 0; +} + +.entry-content td, +.comment-content td { + border-top: 1px solid #ededed; + padding: 6px 10px 6px 0; +} + +.site-content article { + border-bottom: 4px double #ededed; + margin-bottom: 72px; + margin-bottom: 5.142857143rem; + padding-bottom: 24px; + padding-bottom: 1.714285714rem; + word-wrap: break-word; + -webkit-hyphens: auto; + -moz-hyphens: auto; + hyphens: auto; +} + +.page-links { + clear: both; + line-height: 1.714285714; +} + +footer.entry-meta { + margin-top: 24px; + margin-top: 1.714285714rem; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; + color: #757575; +} + +.single-author .entry-meta .by-author { + display: none; +} + +.mu_register h2 { + color: #757575; + font-weight: normal; +} + + +/* =Archives +-------------------------------------------------------------- */ + +.archive-header, +.page-header { + margin-bottom: 48px; + margin-bottom: 3.428571429rem; + padding-bottom: 22px; + padding-bottom: 1.571428571rem; + border-bottom: 1px solid #ededed; +} + +.archive-meta { + color: #757575; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 2; + margin-top: 22px; + margin-top: 1.571428571rem; +} + +/* =Single audio/video attachment view +-------------------------------------------------------------- */ + +.attachment .entry-content .mejs-audio { + max-width: 400px; +} + +.attachment .entry-content .mejs-container { + margin-bottom: 24px; +} + + +/* =Single image attachment view +-------------------------------------------------------------- */ + +.article.attachment { + overflow: hidden; +} + +.image-attachment div.attachment { + text-align: center; +} + +.image-attachment div.attachment p { + text-align: center; +} + +.image-attachment div.attachment img { + display: block; + height: auto; + margin: 0 auto; + max-width: 100%; +} + +.image-attachment .entry-caption { + margin-top: 8px; + margin-top: 0.571428571rem; +} + + +/* =Aside post format +-------------------------------------------------------------- */ + +article.format-aside h1 { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +article.format-aside h1 a { + text-decoration: none; + color: #4d525a; +} + +article.format-aside h1 a:hover { + color: #2e3542; +} + +article.format-aside .aside { + padding: 24px 24px 0; + padding: 1.714285714rem; + background: #d2e0f9; + border-left: 22px solid #a8bfe8; +} + +article.format-aside p { + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; + color: #4a5466; +} + +article.format-aside blockquote:last-child, +article.format-aside p:last-child { + margin-bottom: 0; +} + + +/* =Post formats +-------------------------------------------------------------- */ + +/* Image posts */ +article.format-image footer h1 { + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; + font-weight: normal; +} + +article.format-image footer h2 { + font-size: 11px; + font-size: 0.785714286rem; + line-height: 2.181818182; +} + +article.format-image footer a h2 { + font-weight: normal; +} + +/* Link posts */ +article.format-link header { + padding: 0 10px; + padding: 0 0.714285714rem; + float: right; + font-size: 11px; + font-size: 0.785714286rem; + line-height: 2.181818182; + font-weight: bold; + font-style: italic; + text-transform: uppercase; + color: #848484; + background-color: #ebebeb; + border-radius: 3px; +} + +article.format-link .entry-content { + max-width: 80%; + float: left; +} + +article.format-link .entry-content a { + font-size: 22px; + font-size: 1.571428571rem; + line-height: 1.090909091; + text-decoration: none; +} + +/* Quote posts */ +article.format-quote .entry-content p { + margin: 0; + padding-bottom: 24px; + padding-bottom: 1.714285714rem; +} + +article.format-quote .entry-content blockquote { + display: block; + padding: 24px 24px 0; + padding: 1.714285714rem 1.714285714rem 0; + font-size: 15px; + font-size: 1.071428571rem; + line-height: 1.6; + font-style: normal; + color: #6a6a6a; + background: #efefef; +} + +/* Status posts */ +.format-status .entry-header { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +.format-status .entry-header header { + display: inline-block; +} + +.format-status .entry-header h1 { + font-size: 15px; + font-size: 1.071428571rem; + font-weight: normal; + line-height: 1.6; + margin: 0; +} + +.format-status .entry-header h2 { + font-size: 12px; + font-size: 0.857142857rem; + font-weight: normal; + line-height: 2; + margin: 0; +} + +.format-status .entry-header header a { + color: #757575; +} + +.format-status .entry-header header a:hover { + color: #21759b; +} + +.format-status .entry-header img { + float: left; + margin-right: 21px; + margin-right: 1.5rem; +} + + +/* =Comments +-------------------------------------------------------------- */ + +.comments-title { + margin-bottom: 48px; + margin-bottom: 3.428571429rem; + font-size: 16px; + font-size: 1.142857143rem; + line-height: 1.5; + font-weight: normal; +} + +.comments-area article { + margin: 24px 0; + margin: 1.714285714rem 0; +} + +.comments-area article header { + margin: 0 0 48px; + margin: 0 0 3.428571429rem; + overflow: hidden; + position: relative; +} + +.comments-area article header img { + float: left; + padding: 0; + line-height: 0; +} + +.comments-area article header cite, +.comments-area article header time { + display: block; + margin-left: 85px; + margin-left: 6.071428571rem; +} + +.comments-area article header cite { + font-style: normal; + font-size: 15px; + font-size: 1.071428571rem; + line-height: 1.42857143; +} + +.comments-area cite b { + font-weight: normal; +} + +.comments-area article header time { + line-height: 1.714285714; + text-decoration: none; + font-size: 12px; + font-size: 0.857142857rem; + color: #5e5e5e; +} + +.comments-area article header a { + text-decoration: none; + color: #5e5e5e; +} + +.comments-area article header a:hover { + color: #21759b; +} + +.comments-area article header cite a { + color: #444; +} + +.comments-area article header cite a:hover { + text-decoration: underline; +} + +.comments-area article header h4 { + position: absolute; + top: 0; + right: 0; + padding: 6px 12px; + padding: 0.428571429rem 0.857142857rem; + font-size: 12px; + font-size: 0.857142857rem; + font-weight: normal; + color: #fff; + background-color: #0088d0; + background-repeat: repeat-x; + background-image: -moz-linear-gradient(top, #009cee, #0088d0); + background-image: -ms-linear-gradient(top, #009cee, #0088d0); + background-image: -webkit-linear-gradient(top, #009cee, #0088d0); + background-image: -o-linear-gradient(top, #009cee, #0088d0); + background-image: linear-gradient(to bottom, #009cee, #0088d0); + border-radius: 3px; + border: 1px solid #007cbd; +} + +.comments-area .bypostauthor cite span { + position: absolute; + margin-left: 5px; + margin-left: 0.357142857rem; + padding: 2px 5px; + padding: 0.142857143rem 0.357142857rem; + font-size: 10px; + font-size: 0.714285714rem; +} + +.comments-area .bypostauthor cite b { + font-weight: bold; +} + +a.comment-reply-link, +a.comment-edit-link { + color: #686868; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; +} + +a.comment-reply-link:hover, +a.comment-edit-link:hover { + color: #21759b; +} + +.commentlist .pingback { + line-height: 1.714285714; + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +/* Comment form */ +#respond { + margin-top: 48px; + margin-top: 3.428571429rem; +} + +#respond h3#reply-title { + font-size: 16px; + font-size: 1.142857143rem; + line-height: 1.5; +} + +#respond h3#reply-title #cancel-comment-reply-link { + margin-left: 10px; + margin-left: 0.714285714rem; + font-weight: normal; + font-size: 12px; + font-size: 0.857142857rem; +} + +#respond form { + margin: 24px 0; + margin: 1.714285714rem 0; +} + +#respond form p { + margin: 11px 0; + margin: 0.785714286rem 0; +} + +#respond form p.logged-in-as { + margin-bottom: 24px; + margin-bottom: 1.714285714rem; +} + +#respond form label { + display: block; + line-height: 1.714285714; +} + +#respond form input[type="text"], +#respond form textarea { + -moz-box-sizing: border-box; + box-sizing: border-box; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 1.714285714; + padding: 10px; + padding: 0.714285714rem; + width: 100%; +} + +#respond form p.form-allowed-tags { + margin: 0; + font-size: 12px; + font-size: 0.857142857rem; + line-height: 2; + color: #5e5e5e; +} + +#respond #wp-comment-cookies-consent { + margin: 0 10px 0 0; +} + +#respond .comment-form-cookies-consent label { + display: inline; +} + +.required { + color: red; +} + + +/* =Front page template +-------------------------------------------------------------- */ + +.entry-page-image { + margin-bottom: 14px; + margin-bottom: 1rem; +} + +.template-front-page .site-content article { + border: 0; + margin-bottom: 0; +} + +.template-front-page .widget-area { + clear: both; + float: none; + width: auto; + padding-top: 24px; + padding-top: 1.714285714rem; + border-top: 1px solid #ededed; +} + +.template-front-page .widget-area .widget li { + margin: 8px 0 0; + margin: 0.571428571rem 0 0; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.714285714; + list-style-type: square; + list-style-position: inside; +} + +.template-front-page .widget-area .widget li a { + color: #757575; +} + +.template-front-page .widget-area .widget li a:hover { + color: #21759b; +} + +.template-front-page .widget-area .widget_text img { + float: left; + margin: 8px 24px 8px 0; + margin: 0.571428571rem 1.714285714rem 0.571428571rem 0; +} + + +/* =Widgets +-------------------------------------------------------------- */ + +.widget select { + max-width: 100%; +} + +.widget-area .widget ul ul { + margin-left: 12px; + margin-left: 0.857142857rem; +} + +.widget_rss li { + margin: 12px 0; + margin: 0.857142857rem 0; +} + +.widget_recent_entries .post-date, +.widget_rss .rss-date { + color: #aaa; + font-size: 11px; + font-size: 0.785714286rem; + margin-left: 12px; + margin-left: 0.857142857rem; +} + +.wp-calendar-nav, +#wp-calendar { + margin: 0; + width: 100%; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; + color: #686868; +} + +#wp-calendar th, +#wp-calendar td, +#wp-calendar caption { + text-align: left; +} + +.wp-calendar-nav { + display: table; +} + +.wp-calendar-nav span { + display: table-cell; +} + +.wp-calendar-nav-next, +#wp-calendar #next { + padding-right: 24px; + padding-right: 1.714285714rem; + text-align: right; +} + +.widget_search label { + display: block; + font-size: 13px; + font-size: 0.928571429rem; + line-height: 1.846153846; +} + +.widget_twitter li { + list-style-type: none; +} + +.widget_twitter .timesince { + display: block; + text-align: right; +} + +.tagcloud ul { + list-style-type: none; +} + +.tagcloud ul li { + display: inline-block; +} + +.widget-area .widget.widget_tag_cloud li { + line-height: 1; +} + +.template-front-page .widget-area .widget.widget_tag_cloud li { + margin: 0; +} + +.widget-area .gallery-columns-2.gallery-size-full .gallery-icon img, +.widget-area .gallery-columns-3.gallery-size-full .gallery-icon img, +.widget-area .gallery-columns-4.gallery-size-full .gallery-icon img, +.widget-area .gallery-columns-5.gallery-size-full .gallery-icon img, +.widget-area .gallery-columns-6 .gallery-icon img, +.widget-area .gallery-columns-7 .gallery-icon img, +.widget-area .gallery-columns-8 .gallery-icon img, +.widget-area .gallery-columns-9 .gallery-icon img { + height: auto; + max-width: 80%; +} + +/* =Plugins +----------------------------------------------- */ + +img#wpstats { + display: block; + margin: 0 auto 24px; + margin: 0 auto 1.714285714rem; +} + + +/* =Media queries +-------------------------------------------------------------- */ + +/* Does the same thing as , + * but in the future W3C standard way. -ms- prefix is required for IE10+ to + * render responsive styling in Windows 8 "snapped" views; IE10+ does not honor + * the meta tag. See https://core.trac.wordpress.org/ticket/25888. + */ +@-ms-viewport { + width: device-width; +} + +@viewport { + width: device-width; +} + +/* Minimum width of 600 pixels. */ +@media screen and (min-width: 600px) { + .author-avatar { + float: left; + margin-top: 8px; + margin-top: 0.571428571rem; + } + + .author-description { + float: right; + width: 80%; + } + + .site { + margin: 0 auto; + max-width: 960px; + max-width: 68.571428571rem; + overflow: hidden; + } + + .site-content { + float: left; + width: 65.104166667%; + } + + body.template-front-page .site-content, + body.attachment .site-content, + body.full-width .site-content { + width: 100%; + } + + .widget-area { + float: right; + width: 26.041666667%; + } + + .site-header h1, + .site-header h2 { + text-align: left; + } + + .site-header h1 { + font-size: 26px; + font-size: 1.857142857rem; + line-height: 1.846153846; + margin-bottom: 0; + } + + .main-navigation ul.nav-menu, + .main-navigation div.nav-menu > ul { + border-bottom: 1px solid #ededed; + border-top: 1px solid #ededed; + display: inline-block !important; + text-align: left; + width: 100%; + } + + .main-navigation ul { + margin: 0; + text-indent: 0; + } + + .main-navigation li a, + .main-navigation li { + display: inline-block; + text-decoration: none; + } + + .main-navigation li a { + border-bottom: 0; + color: #6a6a6a; + line-height: 3.692307692; + text-transform: uppercase; + white-space: nowrap; + } + + .main-navigation li a:hover, + .main-navigation li a:focus { + color: #000; + } + + .main-navigation li { + margin: 0 40px 0 0; + margin: 0 2.857142857rem 0 0; + position: relative; + } + + .main-navigation li ul { + margin: 0; + padding: 0; + position: absolute; + top: 100%; + z-index: 1; + height: 1px; + width: 1px; + overflow: hidden; + clip: rect(1px, 1px, 1px, 1px); + } + + .main-navigation li ul ul { + top: 0; + left: 100%; + } + + .main-navigation ul li:hover > ul, + .main-navigation ul li:focus > ul, + .main-navigation .focus > ul { + border-left: 0; + clip: inherit; + overflow: inherit; + height: inherit; + width: inherit; + } + + .main-navigation li ul li a { + background: #efefef; + border-bottom: 1px solid #ededed; + display: block; + font-size: 11px; + font-size: 0.785714286rem; + line-height: 2.181818182; + padding: 8px 10px; + padding: 0.571428571rem 0.714285714rem; + width: 180px; + width: 12.85714286rem; + white-space: normal; + } + + .main-navigation li ul li a:hover, + .main-navigation li ul li a:focus { + background: #e3e3e3; + color: #444; + } + + .main-navigation .current-menu-item > a, + .main-navigation .current-menu-ancestor > a, + .main-navigation .current_page_item > a, + .main-navigation .current_page_ancestor > a { + color: #636363; + font-weight: bold; + } + + .menu-toggle { + display: none; + } + + .entry-header .entry-title { + font-size: 22px; + font-size: 1.571428571rem; + } + + #respond form input[type="text"] { + width: 46.333333333%; + } + + #respond form textarea.blog-textarea { + width: 79.666666667%; + } + + .template-front-page .site-content, + .template-front-page article { + overflow: hidden; + } + + .template-front-page.has-post-thumbnail article { + float: left; + width: 47.916666667%; + } + + .entry-page-image { + float: right; + margin-bottom: 0; + width: 47.916666667%; + } + + .template-front-page .widget-area .widget, + .template-front-page.two-sidebars .widget-area .front-widgets { + float: left; + width: 51.875%; + margin-bottom: 24px; + margin-bottom: 1.714285714rem; + } + + .template-front-page .widget-area .widget:nth-child(odd) { + clear: right; + } + + .template-front-page .widget-area .widget:nth-child(even), + .template-front-page.two-sidebars .widget-area .front-widgets + .front-widgets { + float: right; + width: 39.0625%; + margin: 0 0 24px; + margin: 0 0 1.714285714rem; + } + + .template-front-page.two-sidebars .widget, + .template-front-page.two-sidebars .widget:nth-child(even) { + float: none; + width: auto; + } + + .commentlist .children { + margin-left: 48px; + margin-left: 3.428571429rem; + } +} + +/* Minimum width of 960 pixels. */ +@media screen and (min-width: 960px) { + body { + background-color: #e6e6e6; + } + + body .site { + padding: 0 40px; + padding: 0 2.857142857rem; + margin-top: 48px; + margin-top: 3.428571429rem; + margin-bottom: 48px; + margin-bottom: 3.428571429rem; + box-shadow: 0 2px 6px rgba(100, 100, 100, 0.3); + } + + body.custom-background-empty { + background-color: #fff; + } + + body.custom-background-empty .site, + body.custom-background-white .site { + padding: 0; + margin-top: 0; + margin-bottom: 0; + box-shadow: none; + } +} + + +/* =Print +----------------------------------------------- */ + +@media print { + body { + background: none !important; + color: #000; + font-size: 10pt; + } + + footer a[rel=bookmark]:link:after, + footer a[rel=bookmark]:visited:after { + content: " [" attr(href) "] "; /* Show URLs */ + } + + a { + text-decoration: none; + } + + .entry-content img, + .comment-content img, + .author-avatar img, + img.wp-post-image { + border-radius: 0; + box-shadow: none; + } + + .site { + clear: both !important; + display: block !important; + float: none !important; + max-width: 100%; + position: relative !important; + } + + .site-header { + margin-bottom: 72px; + margin-bottom: 5.142857143rem; + text-align: left; + } + + .site-header h1 { + font-size: 21pt; + line-height: 1; + text-align: left; + } + + .site-header h2 { + color: #000; + font-size: 10pt; + text-align: left; + } + + .site-header h1 a, + .site-header h2 a { + color: #000; + } + + .author-avatar, + #colophon, + #respond, + .commentlist .comment-edit-link, + .commentlist .reply, + .entry-header .comments-link, + .entry-meta .edit-link a, + .page-link, + .site-content nav, + .widget-area, + img.header-image, + .main-navigation { + display: none; + } + + .wrapper { + border-top: none; + box-shadow: none; + } + + .site-content { + margin: 0; + width: auto; + } + + .entry-header .entry-title, + .entry-title { + font-size: 21pt; + } + + footer.entry-meta, + footer.entry-meta a { + color: #444; + font-size: 10pt; + } + + .author-description { + float: none; + width: auto; + } + + /* Comments */ + .commentlist > li.comment { + background: none; + position: relative; + width: auto; + } + + .commentlist .avatar { + height: 39px; + left: 2.2em; + top: 2.2em; + width: 39px; + } + + .comments-area article header cite, + .comments-area article header time { + margin-left: 50px; + margin-left: 3.57142857rem; + } +} + +.breadcrumb +div { + display: inline; + font-size: 13px; + margin-left: -3px; +} + +#wp-auto-top { + position: fixed; + top: 45%; + right: 50%; + display: block; + margin-right: -540px; + z-index: 9999; +} + +#wp-auto-top-top, #wp-auto-top-comment, #wp-auto-top-bottom { + background: url(https://www.lylinux.org/wp-content/plugins/wp-auto-top/img/1.png) no-repeat; + position: relative; + cursor: pointer; + height: 25px; + width: 29px; + margin: 10px 0 0; +} + +#wp-auto-top-comment { + background-position: left -30px; + height: 32px; +} + +#wp-auto-top-bottom { + background-position: left -68px; +} + +#wp-auto-top-comment:hover { + background-position: right -30px; +} + +#wp-auto-top-top:hover { + background-position: right 0; +} + +#wp-auto-top-bottom:hover { + background-position: right -68px; +} + +.widget-login { + margin-top: 15px !important; +} + +/* ------------------------------------------------------------------------- * + * Comments +/* ------------------------------------------------------------------------- */ +#comments { + margin-top: 20px; +} + +#pinglist-container { + display: none; +} + +.comment-tabs { + margin-bottom: 20px; + font-size: 15px; + border-bottom: 2px solid #e5e5e5; +} + +.comment-tabs li { + float: left; + margin-bottom: -2px; +} + +.comment-tabs li a { + display: block; + padding: 0 10px 10px; + font-weight: 600; + color: #aaa; + border-bottom: 2px solid #e5e5e5; +} + +.comment-tabs li a:hover { + color: #444; + border-color: #ccc; +} + +.comment-tabs li span { + margin-left: 8px; + padding: 0 6px; + border-radius: 4px; + background-color: #e5e5e5; +} + +.comment-tabs li i { + margin-right: 6px; +} + +.comment-tabs li.active a { + color: #e8554e; + border-bottom-color: #e8554e; +} + +.commentlist, .pinglist { + margin-bottom: 20px; +} + +.commentlist li, .pinglist li { + padding-left: 60px; + font-size: 14px; + line-height: 22px; + font-weight: 400; +} + +.commentlist .comment-body, .pinglist li { + position: relative; + padding-bottom: 20px; + clear: both; + word-break: break-all; +} + +.commentlist .comment-author, +.commentlist .comment-meta, +.commentlist .comment-awaiting-moderation { + float: left; + display: block; + font-size: 13px; + line-height: 22px; +} + +.commentlist .comment-author { + margin-right: 6px; +} + +.commentlist .fn, .pinglist .ping-link { + color: #444; + font-size: 13px; + font-style: normal; + font-weight: 600; +} + +.commentlist .says { + display: none; +} + +.commentlist .avatar { + position: absolute; + left: -60px; + top: 0; + width: 48px; + height: 48px; + border-radius: 100%; +} + +.commentlist .comment-meta:before, .pinglist .ping-meta:before { + + vertical-align: 4%; + margin-right: 3px; + font-size: 10px; + font-family: FontAwesome; + color: #ccc; +} + +.commentlist .comment-meta a, .pinglist .ping-meta { + color: #aaa; +} + +.commentlist .reply { + font-size: 13px; + line-height: 16px; +} + +.commentlist .reply a, +.commentlist .comment-reply-chain { + color: #aaa; +} + +.commentlist .reply a:hover, +.commentlist .comment-reply-chain:hover { + color: #444; +} + +.comment-awaiting-moderation { + color: #e8554e; + font-style: normal; +} + +/* pings */ +.pinglist li { + padding-left: 0; +} + +/* comment text */ +.commentlist .comment-body p { + margin-bottom: 8px; + color: #777; + clear: both; +} + +.commentlist .comment-body strong { + font-weight: 600; +} + +.commentlist .comment-body ol li { + margin-left: 2em; + padding: 0; + list-style: decimal; +} + +.commentlist .comment-body ul li { + margin-left: 2em; + padding: 0; + list-style: square; +} + +/* post author & admin comment */ +.commentlist li.bypostauthor > .comment-body:after, +.commentlist li.comment-author-admin > .comment-body:after { + display: block; + position: absolute; + content: "\f040"; + width: 12px; + line-height: 12px; + font-style: normal; + font-family: FontAwesome; + text-align: center; + color: #fff; + background-color: #e8554e; +} + +.commentlist li.comment-author-admin > .comment-body:after { + content: "\f005"; /* star for admin */ +} + +.commentlist li.bypostauthor > .comment-body:after, +.commentlist li.comment-author-admin > .comment-body:after { + padding: 3px; + top: 32px; + left: -28px; + font-size: 12px; + border-radius: 100%; +} + +.commentlist li li.bypostauthor > .comment-body:after, +.commentlist li li.comment-author-admin > .comment-body:after { + padding: 2px; + top: 22px; + left: -26px; + font-size: 10px; + border-radius: 100%; +} + +/* child comment */ +.commentlist li ul { +} + +.commentlist li li { + margin: 0; + padding-left: 48px; +} + +.commentlist li li .avatar { + top: 0; + left: -48px; + width: 36px; + height: 36px; +} + +.commentlist li li .comment-meta { + left: 70px; +} + +/* comments : nav +/* ------------------------------------ */ +.comments-nav { + margin-bottom: 20px; +} + +.comments-nav a { + font-weight: 600; +} + +.comments-nav .nav-previous { + float: left; +} + +.comments-nav .nav-next { + float: right; +} + +/* comments : form +/* ------------------------------------ */ +.logged-in-as, +.comment-notes, +.form-allowed-tags { + display: none; +} + +#respond { + position: relative; +} + +#reply-title { + margin-bottom: 20px; +} + +li #reply-title { + margin: 0 !important; + padding: 0; + height: 0; + font-size: 0; + border-top: 0; +} + +#cancel-comment-reply-link { + float: right; + bottom: 26px; + right: 20px; + font-size: 12px; + color: #999; +} + +#cancel-comment-reply-link:hover { + color: #777; +} + +#commentform { + margin-bottom: 20px; + padding: 10px 20px 20px; + border-radius: 4px; + background-color: #e5e5e5; +} + +#commentform p.comment-form-author { + float: left; + width: 48%; +} + +#commentform p.comment-form-email { + float: right; + width: 48%; +} + +#commentform p.comment-form-url, +#commentform p.comment-form-comment { + clear: both; +} + +#commentform label { + display: block; + padding: 6px 0; + font-weight: 600; +} + +#commentform input[type="text"], +#commentform textarea { + max-width: 100%; + width: 100%; +} + +#commentform textarea { + height: 100px; +} + +#commentform p.form-submit { + margin-top: 10px; +} + +.logged-in #reply-title { + margin-bottom: 20px; +} + +.logged-in #commentform p.comment-form-comment { + margin-top: 10px; +} + +.logged-in #commentform p.comment-form-comment label { + display: none; +} + +.heading, +#reply-title { + margin-bottom: 1em; + font-size: 18px; + font-weight: 600; + text-transform: uppercase; + color: #222; +} + +.heading i { + margin-right: 6px; + font-size: 22px; +} + +.group:before { + content: ""; + display: table; +} + +.group:after { + content: ""; + display: table; + clear: both; +} + +.cancel-comment { + margin: 0; + padding: 0; + border: 0; + font: inherit; + vertical-align: baseline; +} + +#rocket { + position: fixed; + right: 50px; + bottom: 50px; + display: block; + visibility: hidden; + width: 26px; + height: 48px; + background: url("data:image/png;base64,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") no-repeat 50% 0; + cursor: pointer; + -webkit-transition: all 0s; + transition: all 0s; +} + +#rocket:hover { + background-position: 50% -62px; +} + +#rocket.show { + visibility: visible; + opacity: 1; +} + +#rocket.move { + background-position: 50% -62px; + -webkit-animation: toTop .8s ease-in; + animation: toTop .8s ease-in; + animation-fill-mode: forwards; + -webkit-animation-fill-mode: forwards; +} + +.comment-markdown { + float: right; + font-size: small; +} + +.breadcrumb { + margin-bottom: 20px; + list-style: none; + border-radius: 4px; +} + +.breadcrumb > li { + display: inline-block; +} + +.breadcrumb > li + li:before { + color: #ccc; + content: "/\00a0"; +} + +.breadcrumb > .active { + color: #777; +} + +.break_line { + height: 1px; + border: none; + /*border-top: 1px dashed #f5d6d6;*/ +} + +/* ============================================================================= + 评论内容溢出修复样式 + 解决代码块和长文本撑开页面布局的问题 + ============================================================================= */ + +/* 评论容器基础样式 */ +.comment-body { + overflow-wrap: break-word; + word-wrap: break-word; + word-break: break-word; + max-width: 100%; + box-sizing: border-box; +} + +/* 修复评论中的代码块溢出 */ +.comment-content pre, +.comment-body pre { + white-space: pre-wrap !important; + word-wrap: break-word !important; + overflow-wrap: break-word !important; + max-width: 100% !important; + overflow-x: auto; + padding: 10px; + background-color: #f8f8f8; + border: 1px solid #ddd; + border-radius: 4px; + font-size: 12px; + line-height: 1.4; + margin: 10px 0; +} + +/* 修复评论中的行内代码 */ +.comment-content code, +.comment-body code { + word-wrap: break-word !important; + overflow-wrap: break-word !important; + white-space: pre-wrap; + max-width: 100%; + display: inline-block; + vertical-align: top; +} + +/* 修复评论中的长链接 */ +.comment-content a, +.comment-body a { + word-wrap: break-word !important; + overflow-wrap: break-word !important; + word-break: break-all; + max-width: 100%; +} + +/* 修复评论段落 */ +.comment-content p, +.comment-body p { + word-wrap: break-word !important; + overflow-wrap: break-word !important; + max-width: 100%; + margin: 10px 0; +} + +/* 特殊处理代码高亮块 - 关键修复! */ +.comment-content .codehilite, +.comment-body .codehilite { + max-width: 100% !important; + overflow-x: auto; + margin: 10px 0; + background: #f8f8f8 !important; + border: 1px solid #ddd; + border-radius: 4px; + padding: 10px; + font-size: 12px; + line-height: 1.4; + /* 关键:防止内容撑开容器 */ + width: 100%; + box-sizing: border-box; + display: block; +} + +.comment-content .codehilite pre, +.comment-body .codehilite pre { + white-space: pre-wrap !important; + word-wrap: break-word !important; + overflow-wrap: break-word !important; + margin: 0 !important; + padding: 0 !important; + background: transparent !important; + border: none !important; + font-size: inherit; + line-height: inherit; + /* 确保pre标签不会超出父容器 */ + max-width: 100%; + width: 100%; + box-sizing: border-box; +} + +/* 修复代码高亮中的span标签 */ +.comment-content .codehilite span, +.comment-body .codehilite span { + word-wrap: break-word !important; + overflow-wrap: break-word !important; + /* 防止行内元素导致的溢出 */ + display: inline; + max-width: 100%; +} + +/* 针对特定的代码高亮类 */ +.comment-content .codehilite .kt, +.comment-content .codehilite .nf, +.comment-content .codehilite .n, +.comment-content .codehilite .p, +.comment-content .codehilite .w, +.comment-content .codehilite .o, +.comment-body .codehilite .kt, +.comment-body .codehilite .nf, +.comment-body .codehilite .n, +.comment-body .codehilite .p, +.comment-body .codehilite .w, +.comment-body .codehilite .o { + word-break: break-all; + overflow-wrap: break-word; +} + +/* 修复评论列表项 */ +.commentlist li { + max-width: 100%; + overflow: hidden; + box-sizing: border-box; +} + +/* 确保评论内容不超出容器 */ +.commentlist .comment-body { + max-width: calc(100% - 20px); /* 留出一些边距 */ + margin-left: 10px; + margin-right: 10px; + overflow: hidden; /* 防止内容溢出 */ + word-wrap: break-word; +} + +/* 重要:限制评论列表项的最大宽度 */ +.commentlist li[style*="margin-left"] { + max-width: calc(100% - 2rem) !important; + overflow: hidden; + box-sizing: border-box; +} + +/* 特别处理深层嵌套的评论 */ +.commentlist li[style*="margin-left: 3rem"], +.commentlist li[style*="margin-left: 6rem"], +.commentlist li[style*="margin-left: 9rem"] { + max-width: calc(100% - 1rem) !important; +} + +/* 移动端优化 */ +@media (max-width: 768px) { + .comment-content pre, + .comment-body pre { + font-size: 11px; + padding: 8px; + margin: 8px 0; + } + + .commentlist .comment-body { + max-width: calc(100% - 10px); + margin-left: 5px; + margin-right: 5px; + } + + /* 移动端评论缩进调整 */ + .commentlist li[style*="margin-left"] { + margin-left: 1rem !important; + max-margin-left: 2rem !important; + } +} + +/* 防止表格溢出 */ +.comment-content table, +.comment-body table { + max-width: 100%; + overflow-x: auto; + display: block; + white-space: nowrap; +} + +/* 修复图片溢出 */ +.comment-content img, +.comment-body img { + max-width: 100% !important; + height: auto !important; +} + +/* 修复引用块 */ +.comment-content blockquote, +.comment-body blockquote { + max-width: 100%; + overflow-wrap: break-word; + word-wrap: break-word; + padding: 10px 15px; + margin: 10px 0; + border-left: 4px solid #ddd; + background-color: #f9f9f9; +} \ No newline at end of file diff --git a/src/blog/static/blog/fonts/fonts.css b/src/blog/static/blog/fonts/fonts.css new file mode 100644 index 0000000..c1a29cf --- /dev/null +++ b/src/blog/static/blog/fonts/fonts.css @@ -0,0 +1,378 @@ +/* cyrillic-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 300; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 400; + font-display: fallback; + src: url(mem6YaGs126MiZpBA-UFUK0Zdc0.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: italic; + font-weight: 600; + font-display: fallback; + src: url(memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 300; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UN_r8OUuhp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(mem8YaGs126MiZpBA-UFWJ0bbck.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(mem8YaGs126MiZpBA-UFUZ0bbck.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(mem8YaGs126MiZpBA-UFWZ0bbck.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(mem8YaGs126MiZpBA-UFVp0bbck.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(mem8YaGs126MiZpBA-UFWp0bbck.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(mem8YaGs126MiZpBA-UFW50bbck.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 400; + font-display: fallback; + src: url(mem8YaGs126MiZpBA-UFVZ0b.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} +/* cyrillic-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2) format('woff2'); + unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F; +} +/* cyrillic */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2) format('woff2'); + unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116; +} +/* greek-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2) format('woff2'); + unicode-range: U+1F00-1FFF; +} +/* greek */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2) format('woff2'); + unicode-range: U+0370-03FF; +} +/* vietnamese */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2) format('woff2'); + unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+1EA0-1EF9, U+20AB; +} +/* latin-ext */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2) format('woff2'); + unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF; +} +/* latin */ +@font-face { + font-family: 'Open Sans'; + font-style: normal; + font-weight: 600; + font-display: fallback; + src: url(mem5YaGs126MiZpBA-UNirkOUuhp.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} diff --git a/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2 b/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2c47cc54d7327178d5f488c0f62859dec0176cdd GIT binary patch literal 14088 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 L!c#4!+F$qojU_4^ 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 RFgp&(HCY8fJ^qhQ{|||=U}FFP literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2 b/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..119f1d711fcdbf3d080c6319db2ccd0b91a7dbe8 GIT binary patch literal 17304 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 EKj`>@{r~^~ literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2 b/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d56688ffb5fc6ea607d3eb8c45450375849ff7f6 GIT binary patch literal 31604 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 HpJe|BCCN}= literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2 b/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..50d818328a00610b8a14ba3cf98190f366f111dc GIT binary patch literal 4250 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2 b/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..e293ffceb144b8e93601342178bca2d47abd5beb GIT binary patch literal 17452 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 KHa928)%`DAS^Pc# literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2 b/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..46fd61bf137ae5cd4176f4eb52709f689338153a GIT binary patch literal 31596 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 z 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2 b/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..88a1616ab33f3a796c9352e03d860ae0cb44b3bb GIT binary patch literal 21360 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 V-yi%|+z;QI;93JhA32^!{txzoPK^Kn literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2 b/src/blog/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d54c7c0fdc1654b0f5d9a7c594ad866dd2411e4e GIT binary patch literal 4181 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2 b/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..683014d7361938ee64886118860566deb1065c24 GIT binary patch literal 29070 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 d8d-!kNjoT*j}OxRWQ6LY7+Y>DZdRJi6Xa& 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2 b/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2f22c670e1ad2cf15338858f5dee2934ea253eb8 GIT binary patch literal 19681 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 T#r(?DvXcNlWf7cn>#hF>LCj&V literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2 b/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..28c6c76ee692be74d9dd54d79f139fb19bc833c0 GIT binary patch literal 25060 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 GwfH~bqheD4 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2 b/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..fdeb9a4a8d7b67e14a3d4e6acff2f62483b6085f GIT binary patch literal 13133 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 CPahiq literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2 b/src/blog/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..2a48105cff5f84187e10aa6ee1956cd5e42d2fab GIT binary patch literal 16985 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2 b/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1ddef142a7fd87c0f4d97bd79cfe0cf566cdae8d GIT binary patch literal 16994 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 B$GZRk literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2 b/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..1d5e847b7aae0103d9ae70a04c677b3ae014b922 GIT binary patch literal 26166 zcma)l2a}!Em2Kys?%O%%oO4dMbH1H(&N=5CWWWdqk_k3ZWNcvu1I9K3hANa05)uXp z0RmBew#R-c5z5|ww$JlVKYI}_V9qIlYX$5OrLo2E18G> zljjQ+1Tp80fKju79T?u|e*!N_`{v0O z7-#LAOs?kY(@?r&UJbl;vkO$^9F%%q(`2KE@%YMD_wYER4+4aV`G0FmbKcOcVt*Q@ z{*QM&RG^)I?fbmZ^+%zK`nw0?T105<2pFDdWIq!lAuyV4gje`+%oXD z<8O$Q43iZOZawQF(gYH;-pC6oiakEueL-_{RurdQ>iXR3*^b%CQzQmU+%}e7BV>b>wvOK$Z0QlzN=;A(CCK1Sj zFrpgD9KGNxZo#{9zjjD-_QWG!J1pYXU|2DvP$OidZlnfgmn#R7c|X<`AG1~B)CQA# z=D8M_^Z*rR`2Q_f3kC+-EGY!qn@MIqfK+cNvtqKOEf?zZQ+*aN0M8GA@Wcs-3nK=s zd9pHq&3>`atjulS^zb@TPsPC}RXYr)an^ROQ*F4VtHKyn`-puxWdSA_a{cW_=A#qR z%nm}90n16z+<4?pl8jrKuLiCwBY|5}WhDop(~HF|@a%$<8M?g3*mgje#Rv%Z=6wJK zgyz|kKvofD2PrZ$RLn?(sN#le32~kDXo9KA?DqRVzkCc{Hv}-2G8fA<0(^v^s(Ty` zBU=*bV#`el8rXhFx20l7cbMVC=8lYo5oU5YB7>m>7|3ZzK6g%8>Rr!M>*JourcW0>1LVG4rXi?W)^O&mDp0KYKVYK1p0VmzwWoT{_e!;=>Gir`10_i?%xO@0FQ)6rwvCEcwqnh)pF9l7SEYUkK?Yr+Vmrk^tD=GT(R(q_JfWUpCm8`&60n&i_KTm0oxbwe z*T?z9VK#_=#iUGyGh@U3P}QES3f?`Db*?+6$aCXS#*aZ@g;@1KadDoIJZ^N+{_G1d zEEqw?(4jb9cFnqD*-ID%#&MIpq)85)ev+&Qthw*cPsrfhnUkPX|Lft{Q zLrO4N8hZIuRm||z9!Z}Lh$Cx)j-5O6vhKykZ7Q$|yI_pt(o_MZF|i}u3~0rgK?^K$qus%Bt~@^6u=}1>`f+V6aGF#Jxq=i+Os$W1~I(r za$sEQ^oWdWy*&HH6Rl2Tkofs=`sy9f>?|G;iO?w!uF&1VRI3=~n4a_5a~Sh89|bV= z0~tdvs35s`fAkn@NKw~M)qyXjjB!`ZEuDG(<6W;YX@OjTfNT(0i*bw>yBEJa1CPrs zHt*uI(&Ydn$IijT7+8Fh^tb|nQ**p}y^-v|hM4YBv1%5BM&2qLa^}gVDVWk&sN8QK zYK5tH@Zv(knOmT4C+>o}e=QFi`ec3uMo~}AaP-;zP->5Uq3~7Rx52Gd)L{bytQZf# zLkzUDSq0An*pPtzgZc#+R>x5!V8Am>sma19xlE-cx^5wck7ebSw8urk2yQ&2xp3H| z$d*w^9G8|D+{8ec?#l3mUwwelh?+n1E({QtSqV)J0j4IDPZAW3Fe-xd5zSq#<+BcC zJRl|pW*1_UCS&Lh6{~_04A|6;VngN9)V`8%I2qCcQ4C_4&6*k%Mi~%ba}R3T4vH;J zLrZDTAh)l9^x+d+ODt=R9~52LQ^Fu+5E6S*_4pgdG4O*J%%E;Q2j#DaV)SOY-5TZu zh!o=%%*ws>r8VbWpbk?U2UI}vjK|zrbsoB7+!Vl2GyHJ@%v8@3RP!ixpW>W%TChz9 zUkb$#W0-S0XDV_r0GJ(eFddR#0A=r8Is`M>6`Sb8bXa?ILkqav2)kw$f^TCAcK}2- zxq?N#$CyECAcb66LQF9<8Cy;&0ChRnc_%jU zATpz}9cm{laeC_WyWnk<=Lf*FTR;KaS*Cqb(zR1?)CMl!#Z>}iry!Rssh?%VCz@WX zS$r`oBF!7?5915Jfog-4LKrDNALxgqC-(6fOcikYk&0{g!rpvbag8zOc3eMw0eXKH zYW8nWKL7(*2IWBCe4N2aD{~(wG0NwmO6?lGSq?yD#lASCo?z?z-3x9rmKdZ!$O$s8 z339lxHS_nE?uHl4UzJ2v@K-G%^8}_at;znBHOvTyOOs8wm{xr zgISs)Az#((?&*xLLvufeyq;_)!Ef9X1g(eULsXA|2RLOy=@N^ea~XBP($@BRev*~S zyt`FJvPxm29Pbmu4YWeeyc6aGV;c@tnF}RYbAaW#fbg$h3m9pdfmg&u@_AQIvKKPK zO=K>xJ~{@?_ljyJ@Z3kB1fM()%RTWnj0V8v&j&*t*gNC~MKR0&6N-4_X$VgbfXp)3 zyiYkqFgAVd_bFyx7%&-YK1LoyF?oz*Vm#UXr)S@dfDzR8*1_rd=v0B7?idqy<%Kb_ zZq@VLk6|hyz`;6HKs_l&0$g}qMHW7=zIyGceOTQflpeygHb58~x6&2pUVo@52$#G! zl+>9!HOn&@;VQDn6s6ewkEA>kV2+~jbb{11qQf4?9|H0zp?_R zMUj{#!z!|Z#KrcG!vr!LP2ELfC9oPAV&LvWNM;L_U{Y!OpmZe?mh=X;SWf$Xj?1%_ zrd}Y2psVtr*3DSPl$~uThN=SssQiEN1Jbvo)=XF2X-K~D00ztt**~uq#b5%{kWya7 z)d5`BmAlw=+WKY2`s&+oNX*%Knc+16BRvVps8z``ZVVGV?a~jao`3>Ac+jarJok1> z8}V!%TX}vj8wKgNVjF9fH0aXGT;_GOEWymMYW=wu6&iTzQJ3C?4=`4wcyG$*uffz! z6xqW7SKgh6b~Nari)5;Zj{(+6)7nm`Lz!@_vTr3#flq(R^5|HQ(I%@5IQJ4%4_69! ziK%ANftebpunigHb}x&=+yGxZ1*I1s`ml4#0)~O(8tC^FKnYgVQ?-dYcg_9cq_|UO z?-FNJOe4(Kudu$RdFuoHvr3;Fy+YyA$- zjq>3wHQ3aN8#N7E1(|?Ee)c@HQ#xWPh!0no_ux%>^^J0AEyqYBS7ijLbljv!DM|hBiE7 z!Q^nWL(KaMnlM*=M`-umT4$;WcO8fah2`H%Er3Td-9G1yr$qf%+Sdhy%^b z5+b>dvk$tCq%GHxhb&qZ3VK&maKkZR76GnxYita9Y=;E?;CI!WQ5`;M-hHlc0TTkg zr6O-_!BircWjFv!mf5J2?F5s~RB!=+@r%?|0BJT;w* zNWbizMA)HVdh~|3KZPpve_uKHt zN1v0Kq3d_SiR$Yo`Bvv-vRq7u1FT_?LFi}G0?z$rCvSrRBijs~GC6U;f&iSFCf&t@ zUL=Fs21#@)ZBldM`Kl6>vyxa=0``a2k}3AP<}4d%CYAeP+}VmwzdWK}W}YG=x7+ygGACc1t#T7*AJA#7rEL(u#0GPF-*#73Rqr)EF zU=_mR0$5B*;&wE6j>8Mrvxu8W+N0p@t2VTmW$XFwIyQU(i)Vo)aqX!tXx@j!+lGV% zeSQ+g*wb5b_KRm{B~~0w0PsE#?$XRPh$2kM@}JAitK{z2LJb6FRGf};-`{z zIZVEB)rT?^-2lUebny{y=2{S4%54LSA!N?w`=LS&GIdl(XJe%3rp+h7V>aEUv?(~G zLhymFOic%hC2IxOetOs(o178$?Q1els#{|$DNF{yTx%fH?X{6OeyXpg+%})jN@SQe zU7^@e{uTR}T9j!<$FHn*48{`D2w@DI*?!njr&h@f-YS@7sPJ&!~I|dr4T@dT*%0LNHk|M0(4IG*XoFC1+bXb?Lcx^e) z#4+sKDv`nPd->`w>AiFU=3l{D6_9kmwYQ;y+5FR^bfi9Mx8pEpTs>T%MGSO5L~U7{ zbAe~BjVj<>c>pTEkL+mS6 zSygqDF^mTgoeVX-47Cqoo};@1q3dmK+}RK7U2>@Mh5ui}|Mj_NVEO2VzrXvA97)SR z`_?o`Q858E!J;e4hvMFVOK-C8_wp@jZ>+up_LF?mvp3n8d+neOAssj0;%5Ptiy^fG)?)FPcR zjL(D0V>IHp_E<=;Z{cVkY}287efN}4Tj_HnF?C2nFbcqy!_06XSdcgU0m@Ll(NB#@M%kKuC-C3kqXmp*sGE8E$pl#f7{+&-`0gV~jv`(KH;u#zkRz9W zB39)Y1Fp>Sz~JqjsONfvRZKhJ*DS6mTBh2yVck0S`y*GPKbLKVy&6=@u)vo=77HAdL@sI4??VnWsTKu?Yf!xRDms>MJvz5>IJi8R%A zx=L=Q43d%584KfM7qm{!+y=5HGXTmjXM@4r;?@9C0^mBoxd*DZ7-|D@2x01Y0@DXD zIf4c&W@cmeKkt7~CN`NGAUM#sUp#2;kGWBS4KauBs@vg$8zJSq_38Ve@;7)nbG*es z>LGxQwectWHY8ZTKO^Yg!kt}t<>uWmg%H7~@3&{OeBx!udzn5!V{r4$GIy>QAPsy& z*#m&@-oI0Y9RsGj3i8EMB-Mp`p@x`QlA0H%GfTsNz2mq&*E4X)#k)gzM}U_C+-?dx z?*QLQrx~4X^3b^)N?@sZnxl$E#$+iYw_PCPZRgLPhS%Pwm$7KY0jAPhW=vb*vIOOE zTwd64h3e7_`>0no^yW0lpop>q_v)*Re@S>d^am%-{mPq92uV$cJ_+WOv zQ8=o!zBGQJ>nJA-b?99(oWxHX=PK0_>s&s%V^`*n>aHM+Iw1_*ZTS1~lZ@a}9@Al= zb^|wMZGVx`BRd3T4pEn0)<%4lu;nal1TDZU$pJetg?<@&zh?M05I)pSt?KLx9^4(! z0!a;Bx%3NIJ%r&r^9&RVbgu5BCrAbjTXUNGPXufrGwbBo^j7a8yj94x$DtM*9pnI) znpq1q-X07cKt?ecENZ|a(kAlaqpV1v=>{nQv_TkICRX72-H*O6o*{EZrA5Z$K~v#8 zp9D08IMg{B!3X0%?@p1cAn3+1y*^S1^EV!z9!JAlKsZGAj3XEYrF^X}v?a+Pw)y7{H z8wtp9#gGRDx4wSL!I0^Wh+=*wA=h7p((u)<#r^g^LrYjn2_t_SK73;cGq--TTQ{x8 zXvR&Q7=*#jZ5RCE6ehlUUS_=%@kbIdo(Zo}3H|U{48tHy(j8!$P1SK`oweE zpb1{5j~DdjC--TyN1eot^rJ)Z9mP1nurvhTOw?`IOm(w3|Et+unf6Q;+Yh699*e>b z;y%E(YnQ-r^;&>-bTBgc zzQ>YTT**BpYuY(`WFp|j>lqe6ZL>gis_lNvoxht6VT`ZEt(s}*1V{#?Ha0OH#)FKB zt!UbW=}67qnPFkz zymHBwqwsMI$8hcs|E5hcTfo_O#P09$SXs|ZLX)b zRD35fn_s>P=gh-0N?}kLG8Jjd(tJ-2nIE8E1!DfdCykeF-2o;t~L+q z!q4NNf$=pzL%_WFqN7OreesuJxZx<{uTEHhc`DlY`mn?l1G(itz&_N&M;D|*87|)o z4Fve}F2H;~lnVB;e>Vmy1KbF(!z*thgIw@o4)V}Mli5naC=ik2y zcZ<>Dg2{Fb=bq7AI9WV?AT#C3v6FL^g#}!oAM1K?a>MZ1+w8vrQ@5LETNLb1!rB#A zL6-zT)i~6Q1;Sc#Tm6wERxpB+*!A|kBbzn;_Dpfc{@5ChZ}JAJ-r^Q$9k2t>&=}sn+JhW!DmAWh$I-0b6zt;LzCj1DUI0UTMv1c+a>z)u7?msoL>}FIdq$K5`zi z!<}}@v^8&+t=?d>@X7Xz#;>tfxc2-m2A0Ql5Mbc?{f#h7Eb~k6kM1@1*jLICXECDN zDz~?}zNG~fdYSh1o$Y~VUzt@z=7(+PALx$V`j+P&S;^-EX%L@pf1YdE-3KoQ;t2ur zQpFC55$$eKwhJ$XgaB&Ath9!*cV1S%okr!1W)C-G&)ROpyV!@wY|}RJn4$w}Al!>Qo<_9VMl? zq3(#%@i|!LpTsu)``yIpgAms7U%&Ufdx7a8>qRVHHgmu>SZp}ylWK02V9tKZlk4qw zRrU0QVOXq)v0=d;WdkQLT1S6MV~ zbM}p?p!}XO-$1@p7(s_LE`hh6%Ulbofd!Z=OA9`s&?Ql(T61=F>@PqfOQ`{GA{c&R zVgkn?CA{Yngl!C%hWz1O|35qj+nU2t0;-Mw>|oweM$lYgQE=!%kSm-9GH?A5YH*p_ zW~8Hlf%xu+_do-Tpa#~>gs|{wicsn{@z@;$00tZscO&hX~R9VQ}4oo}4Qx?0p593@p z(a?~c3y&3wKS;nO^+R~h+?Yq!m|oqDGyuYf+*7`KT6|5jLkvQUU$=6ts(z!A$qx2n z&TWddf#HjkmOYpiuC_Y>*DHSvS}+??2FF(hr1OmBP&G^vq<`qU-#uoEi)zz`hQU}l zeP#qUglXtY=dMX$CZq*YZl78`3d`)7B8>r1ET(Xlfo&uMxj+u0AV+ce*BG&xIn*+07w8QaWDKk?40-;d|3eXU8T^MetEKm}{O06rtZ?JhyMi_6FPwh4o%%uT~}8G%|eg96%{NyJVWXnM3uTzeQcC2J)S zu^QqTi_ne@}UbbUZA@qD2V#s`ei(~aNP6EXwwn`u-pqH88 zwwo(u3YxoQGvc)GXc2S}L@=p&;xJOH%zB`VRk_Ikx=Bsv*p4Y$`4xueGhhweEWmgq z&bvV~L?m@>F4TNXA~b^nFsnR+n*}yzTrZqTg|jyS8YMjF-+cM7A7*;?tlz2&;!?g&II(`e76mTck_af6blfb z9WtmSiy2)HfD&vem#O2vxf{R%($2eX$#~?`UvqQgc`QE)na&y8gFQWrhK@c(fC@}y z_e0UKgy_AvJf=B{7Ynw+)>#!m9QhV-`CaJItl$}$Y=yC7ah4wxciC*4%EcFRRuI~@ z;BTLYS@m;BZEe|9#44|-Hb3**0Vd2*55X0xG}|_>*unU4876LSU<9aYwCAG%E@hj-m%TYe5j|*)@f2`@ zK!KWRWxbI7a|KX_4}Soyfs|qcScVL*a0RI;U;fUC{F(OChcRl&3?u2wCPwz;LI9=% z=oS+RW0fubD2!t=R|oV#s(YaLB4e7uA!It^C(s?s5~m-A*#gGIuw?jL<~2{IxE)}7 zrT^2P!tOW^m}RLgS8~e*@Nt^Tu|b!JKnj{iW2v!X$U90tGMH+XK1~!n^@FQ8B)=d|Zihvdj#X ziiO@{*?DAWfaZm=uXO<3nLT7YB>5f_V_5JbLM+?-gJl*yf zvnDSVg5b()u)JWYYD#y^l!#>BxH5de2Zp5PDLQh+({eXy$FKO-w|6lUuOhJoUoi6d z1J2A|OBWQQ$_8WN)3`r|EEuz^CqH87;+YLWgg5=sQEfu!#Q5+=6ZVWvnu?R0kWbbE zJkIC<+&EArrik%c?w7#<%pfO#I>?SCRHJiVApIOD`vo^>1MjBNR}J^`6W4#>vj#_y zOPaY_)k3Ry8Gqr3q!9e^`0wE8cA*ls**g3KLRcgMK1_85F8+XlnC#XKFV?QYn}sOi zE80i#?P!WeEJgquZe0V*y{1)WcoiAVTpMI6ko)8OCqLnl-gc6Yfk`ud8-Z(D@8o;r zHgqvTzQ*59jPdxdw@$u-p^5GoI7G1h`QP>l7E?ge98?J#1Hef7r?J)WVayP_1l!BG z7gZ7fE31(V&-c8gHV+qcg^F&BVCgjnd2O>xt! z3SA>Vem6!&i66fONAWJwJRXBFK02tJ95J$mJ9gb0<^q5+UN{EfS%DBc?i(PV#x}x3 zgJ4g;WLq1Woa2|s)CsO1kL2CLl8xVlg{PJSFV|SFa++vW0Avr}VnBl(_b3R}yqhmt zWhd=3OlG8$MXov(^zvn2!{29~@aW2UMk8J0X({n;z1YbMAHX zC7Iju_3MF`nb>;aNh_IxRxK7Dt4{HP@d<s=e|hYee|_;@46Z#i?9tx8 z?*w<&8L9vhAHsTK)vzDBkp(OE4%Vk%fz#yy#RpF3nZk)-8)Vg;D+T`Y!Ve?b`gH1L z=W^-$GJTu&isK0+8sM`~dgdwZ1Qnu$a@HA;&)O@?BB_9&0==VnJ1!nv^!1;zvjXE{ zM5{izjS(LbXu0|Eor{gkJ1H&T$je3>!fAr&su_Uv{YupdE&4I^XQ>!}xRTVj+Xr7m z0ng5DX5R{8nT0bmTM&Rxrx=P!3|_WnN)p%p-NWCjfN>$cY9P%JU^v0g&YhY2^5oeE zVVDiIeRzBntAl;mayF1fkyG2DXf)t3O8#|2fLUJY!!sDvMLQ_s;rqogaa0|U!$w_% zDZ6i9mjel6&YXzx`;;5Cye1c=`u0dI(Ej z(8*16(fyD(OPPdTELJhH(Wc%JA%O9_O$=MHjC)wq-W$e$`6M&Y z6LkU5O!q*d+624dF0b=WhFYDdXn{F@^1_LC#HnY?VbLovx;WjL^&ULc|F1DLX1(we~ zCr(v&WHtzmZ&3W#apc6~VyFd2GrHT~z4k1noFurnP+LfKkX zdZ5f0V6uHXp;bYLW{@15jp|}Gw2@;6Gpe5koIWl`p>vrIb(Kt>ephVO8n>uO39l*! zzt}xUM=hlgzLg@c?+l+_=Lf~bT=+LApewDth&l079gdu+;TR&|*^FlaWaTgfn!Vfw z0@$vQuo@;-;o3Nn3?xAX#6E;Ms_>uv`j@aqcho%RU{ zt|tti###k-)K&uhF<=UTo@sq5%p@CQ(6s+(9PI20Kc%#?l38fFV+p+9ZE$iXo3~H1 zCshqW71lCu-Fm1dbF+@sfE0KF+l&p~KYu-6#!0DDy!eY(_6FK|jNdwnbdV#^sz6kP z*lqkrq52a8p}7-bKnXN*d3knld5k0?Gn#xZ(^pTK_3ajYzINY zSAs@Z7XX7XQg6!+Yeg!q4nP6pbx^(HL&rea>kqY=xy`@4^ss}knhVWJzjg?9?IvU12D!a@0=Y!$rFt3Xa86@G*tuLrQdk~}O zu+UPko~tjztz>5a5(3z; zU;B}F5z7oI#5KuU3%-tcV&%#5t5fCWaSU4j<3q339~>= zHk9)|6w?Ik_&|Mj+-I?j0RW6oXF&`6NYTO7!E$Y;0bYn7n*bNKn;a`g>SKJ_d|F0i zC^{lsER(dL5)YS!=)*pI8-isVkv`G0>dupR`7+Cw{010yow#aW7>ub5$h>Gja)FTs z8aK+I*|FGy96Wd>7tQ5fdp^Bx7M4-yWSNm~F?@7km^(C33h5X3sJ`)DH!tLp>wIVa z_WUbwvbj{=_WTPQ+;Uc2GUIKw0kvm;>D0%Mz!3z(IEc$Y#AY8bnm`GpOR-_07=y}@ zO?4s$8(*7l5w^yniE@R~ptg*$O4zo6oyZPi9C|3~KMR=Q2W?<#bCS;sPm&>Y9D@p^LPGMOcr9@5-`QRxWuWP1 zc)=-F-r1kL7!Q|sO<#nXfXqT}{qO>Ab6$4biw$YQi_Sg^1+Z)69*8em6)6U4 zx$vS5QP6rh;ZMIe{(bYseiU=R zRIF7ocK}#fk&gX5wTU|e_z0^t@&AcbwJD|7-)6-6`RvL=vS~a-Cug4dA?ydwe)Oo@ zDjaT`V%tixs*+RBwrOebGX6e0jsRRbz!liV29Jh>@dAhT9o`Jl%NqNpIF6;6ISQBp$RcPqQRn{}tVr|9!O& z)67#bq?CKMh6uP)AmG9?(Bbw_ZoFBNkE&uW)U3{)>`|{f9));9iWuE-2;j|q^4`X8 zis9or+%gjSlRKdFkWXJKgAUraab#yX|1GnS`+8k`VtxMfcISyjJogg+VS{!}<5NNL;UZo-7!tgk1|vV^F8BJ+1=C)v4-dVg2#xcc2AB zd@1y`Z9BmjUr!|&a)5hG@nmZPLdR8wv<-jxz>M+7Gjf2n##^RCs)E>`tH1mCk67kh z!BCpw{@^4=j**a#(@1 zHU?d~RRSwpm?)ZN6ae-CfRBiS=%W~gGv9~Wb%z?yPT#)pR&@XQCoxEvg_>^M4c~|D zE>-mlpWFsoAnXBvfxh93N2>MetewVoIU>49Ej5mkNHntkAE)j;e`f@9yLD?WpouCI?K>3~^w{Va#&@zoBZb;Tlpo9* zNZUfTOq#7Z_M>7BiW>d&*$@f$sitTs+Dg7K6*UWTyGTHse7@W zK?%)z@6mLb0{cR8AjNgs)HW>T%5^RQZ9T&)g`H_!s0J~qvLNc9o@=lLcbjuS?o3&@`ULd*5Asp1e9m$3&CN1#O4J?{ugda01P4Q|zXak-XVY)0 zaA6SbX;neR0O-}(CHn@{oPWd~1BQ;#1dI_N1Eh|LNbfX45+Oj>NNa?>D~vjBWR4{W z(4D#&21GCnwKK+BFFe6rc4eqKlV!S@w*#egut{w2x^0V$d?;l6`eR^Q^{C(m2QagM zWl=nBs+e=4gu|eM^Y{*K?droFQ&1Bd=}}O?onIV+)7V9rcgYX|PnrVhj|MI5$J_UN z^HD6P@lsGx638w?<&uHvtRWW`m&^9jOa^VXJYko0s~iQZxJcI2wgWM8OMdtJP$wV; zzWx6Icf*7M7|enH=W@C7+suu#k;cf96{=oLjc1m`WD)>JF_LkWBhv;*9s{+C4enMx zsq-wr$b*$%7R~4XzR!i)rwg{uL_R<>+Rjo`i z`WA?};F}o}jyVQy?bOHj)1w{`20I_ZHo{^Om{or^HP?Bs=ckNgRz(t4Yi7~L%X=x3hS&qNL|mw=C67^m+mZCgvrWQ+$G z%$l`*mu6^yUiX2-+7k=9E)PBz zEo);zs$4rTo>>76s2A`e3#&2w+{56&fnBy$W`Us6(gKszg8Yi;ebu;_S#-JA zMQ&Fxa99juD*-fDZu^>D`|jjqe?DvUi&u$nGpbDyV|Cp*nya(ZuFX9 z0_sFW$4(M-9G~r$JkuR;=LuQmrp6bTOoWDBU%knFXQmsFF_huLcAaZ!fa$L z0Xi{@HP~LjZvnv2^T~qBkKQV07MWCy!)&BUr)6=I_7HRJxf+@n^gsCH&H3P;PEtd~chNxXoU%H82boFj#CgK%g&(btc$^>SD(L4n%<& z9V4A}=?!hJ%psM}2BvC1dj*QO0)>U_B)QT1jD=&#vCj;~><()is~+j}OM85OEGPU~ z8^9)01jyd3+-V7Iq@<`if)kRC|T=g)#wmM*fUfzLQM*vi?pvMw-B z&Oouwhv?Qk2$1NuA_lY6I0#yV_{*I2%h#C8t@#ay%x8}ZUUVF_^x^fhyyd+ggF2bd zxe#U`Zm}!RqGmfBU2T(1yomf%%{#i5o|7u_w7+5Z9+_z5|hPB6ZkErZ2 zc*_cyNg-fZ^-isLHF?a;#5>TnryC1DL ztJGkZ2hw?Qazyt+&Cw+zJ+cPgtp4h8s2av=*r5qL8lrLvHjsDq40vdAv!5TW35YL5 zKgHGojGz2QmLSD$NV_R+@4`D?ISt;`{SUe`Hg zgA0S0QgcR-*LzHGfRW9Jp|NoB=cSBA9>lHz1ROd0v`l~*Gy!9s#1aOESQsTy1z^f? z__Zwam^@(ZF6i1T0oJme66P<1x(N}qmRx;9tYUALM7VL%_V*y$4@Gp#maNupE^(tk zvsLu8$|#IZ4zs4%Y=HG%Js$)xp?vb$Xxt#%+aLjl$zzPcC^ICC4q@<;A%ZX8S}?() zlla*1fq#AT*{N!}Nx(QR%<>pX{5U(tUHQTOUP2}}%!lxSycgik0Cj5vBLl3dVr!!- z6Ima5m3K$IwO8N-H1p2xg z!4??^Uw9RI&qN=umm8F?%Gf2G2SD5uU|HPYQTckH7;@xJzSXGqEbHga&Wiz_h+hWB zUHHR=v(W5C4m`&6>0AIsJfB(~f5 z(g-X|5s2H|obds(vNbRZ?;eQm^$ORPV05{zPtnVOa_Qi{&UgCwB`Lf6tWkiEX@MB^ zbXrP22vBe17M4_LsjS8fL!ZRlSSbgUKn@|@WgvhLy9X;R-#9)y$JM9XScjIA2dqLq zjOR#I?t}>}R|kwkef5$I)rR9sGuUsxjom{JnQWmq_!54S7_FS3Z@eEf0DmxGYZIi3 z3U_H`j5D=)F_x^{$dWdDn(YJ#fE=Y`r%dw45y47Aq-KlJ3U&IPIX;A)lljWh2I_;o zgBY((>)j$q;RbhP^*A3DuSw;ws+Ft*xom~mz5+QIxBTV1(5{d?m1;1Pj`okyqB=tw zY;)lQLtDHCLk{JbqopWF3 z$r%$&`d9_)sPbIn_;d|>VlmkOW_*bO6w?Hv0c?m0QC_ns&olfvrt42ZT|LV;3-Xz1 zfPH%~*jL@80>5+NVNBND`n6mGW^N0-tDsv_ZJ}s`X}mRn-erPXPu3_FW7;}(&kP(%R8AmVnxS)|Q zegRdgUBc^)+K3i+4#X^ro-2ax8v_O0dRnHy-C{McaRbAUDafF?vleQ{4s;Lf7*8jA zP$k6T2crL#2N)A z!0B8AmP9cf`@9pvldk-xurLN3R@FmmSmZhDEqZtJ zI?Er}hqy5Z0Ct9NgSDv6pe@qR)~jc_W}&A)d{RBW&zoM@;Awze$S|vO^V2`y{N2sh z8d4dtdr&`$iNo$iFPU$PFa7uoOe=)hwx~S3D9a3#pd+d0(j(evBJT7~Kvi-T`$3*R zsKtdp@BplvC^~sq#$ZWkHomF}l@ zwU9J~r6}f879EuZnP4FrnHc=e0MQWT-w2rJ-{C~Ycqq7TjOX%W=$acGzziC|z1x0$ z3(c5^%!PsV?(_+TdwloxKCmFRkQp}@7Ma-q?OVknIW=`a}Pk$S*&#Du&Gbr?N!0lgy2dZbAy!jAdvY*~Q4(9Tehj|bne~cNQ zS~bToe;aCB?D@g3((@pGdKm=b+{|V@d1cvgc z3ox^ChGm&Y%w$ptS~W|IJs$H!mou2H3Yg;-dbv%2X9d_@CzI>f_@NNEa7G)!D82KJ zLB^eyB{Ase1qi#^35aSDu^kB*bVKM~dKDPq0|lJPXl2e0WqkH_TZYF`;$bcmlDLy= z`&5j=zIN8wo>2w^h_W#70fsUb0dV`i9x!{J?Qg=vOrK(A+7>;;5%A&D0VO%=0YwFv zFhN*lfF8m#`-Xs)m+7UY$v7};($3gTSLtFw%cUS7ja59EQKv%1Qv=nXzWM1hEPdvQ z>)oHWWX+Mu0I>M@5EjL02zPE_hHN*21e;Kd=hh69mggGS><9O$#HX`W#%1&VdWZ&3 zSo#&vsly?ew0%%{TERfq7&A))(@%x82XTlh4f2mx(afUg}jMA{6HlZGt#tXO$HXrU& zkT%C&$P@%V{(-|GROEQKrv-Y3VFV5!3p^I!J3Ueb<+NA|Uf!2UjR#?|x2ygRtC}gJ z3s}vAqREhF^I2>8oZ^h?r7)Qt6<41F$8iDj*C!v1-eCvSIe`R=tJxFrI{IN7Qpr_d z{M_G*!R*)w7uzZd+(nZi3ZISgx3YX|ScZRn;5BfNOT-FPs7lMg#r9zg_1$gYV0CuG{G<@?J{D}eOJlF{b0E|HtKWlI3NCYDj=7FbUM<}Y7y<9S26DA{8KCH8pk0(R zYp#^#!b5-k%8zcm1#PZnGXR1h+pdw^%Ge(0UtWEoC%#^n?FUGlEjY9ABa?b z;f*03o69nrx82RN9ET(Q3owj4KM@Cl3lpv@%(jAlncXuBZELa=x+G8Ru!az{8jK zRznu2dg&9v>SR5Pa}SO8L_*iM(+%U00!VJ&#kbGDq7oBh!PXA+GLK>VH%FPQ=LWqX zJNX4H?BlSr-{MwWPBF4k>G3R)ivANju1vIe2zAS#naSX0HJpM<3*<|7GkNqZ`i5H) zOo|#3l>;@i9o-?LY$$Hr|JL`w^uEC0pO-6^_fF}B2bB{U+cnmKS~|_vlp3Q==-sz& zKQkc1n~$l;L&&$JkqKCAqkOf!JTrn@yH=YIKiN7S&4x!S0AFFXILjC-I`JLs;3lB1+*WPN6Pukxh+ zw1GK>2?*PJ?*&$kh2_%30_mLx0f}~7%)h*SP%Eu?2w?lhF>jjhI)DW|?^~8B>%v=5 zH-E|WW4(5U4)rq5OhVN`V5t}FmBaSP^JBDpAq7!tfhndQO5bSM@JfK4u$17G&fp8! zCjz!6PsxZ|@@Bp4#eukU+yAP81`2Yk+2Cl20`G~%p2rVr4!u`8!Jp~*&DXoL9{suX z&7PW>E!#f~?eQgoDwYuU4yYZ|IlvNY|2QUOaP0+dApp|a!6*)%Y?p=OoE_1^_g;C5 zC*e`~MsBfDIg$OBq2T(AdSl+SdL@gXykLv7Tt+4+9Z4l*+EaneT zt}>{VhfCjo}j{xPHs_f^CvkDbRrPNqy!pozsS-7W+Pd6 zI!`{Eo&^TDUx2mAPc7ir4`QJ8?xt%$^jjQ;X-J&o4a#YB0&vvMWUTQbw`KbW@*xjx zQjAonLA5cMjK(yCXWX;>&w@?o5W4O5Ga$f+S@0i!cu7X!ewXDd% zfCLC%VC-m?33{vlAZ?|YvwWPtiB5529-n&z1UObR5JS)u z>g!H6`M^|l2aBta=&D>W-6m*LJ7X^cGGk4jJg$=F-M4p#F~uB7<*EI1UDMOJndr_g zum#c{y~ot!Gg41;izbNen>7zZu(kqi`#pToGOTt8qh}Af`*$Lk-H^;!ke#V28)_1g z28qd@;H1O!-T&3g18;>ig9S6XtPpKb8JCp0%q&YfA#kQ&3uBhIKmaBeZ|;TULj;8z&|f^%)F(sR$D>4Sm#AGBOakiQA-%hLk}R`1ctfHc zv*^NPnXMfaeM%H~xF4kI1l9qBF|1%#3gT*=XOentzrL3VE;;iyK%EyByR=gTbGne< zSL5C|c=IF(2%O^aRp?=LajzEDtY89s;Mxi^%O%cDri2;5bV&1;%(b{3wOKYKnmR}g z1i19u>=rZM$jfUIWpb6@9)a6Z_2SC)XYOV}fy>H_~sMAr6xRdhg{o_^3q)1KTc8B0cNq9GfM@^eCnfFtG&Dv8G(uHFoFOSZ zFiVj|B-mYow*A+JXc#{HyfyXC9R=m{vvQrvl$>H-Ug2>1+|hZ$Rb&_iCftSBYybUj zr`=#Q1oI4m_YzA&z#11=x#wZX!WPJ1CWT8F!<9f8_=vJX@%U{^%&_&b%1b|!1>tm8 ZZN$#PMCWxww%=1QWHk_AgfAP{{SRS;5P$#x literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 b/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..f6210055dc0c7eee9319b471e9acba841b38ea54 GIT binary patch literal 20653 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2 b/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..49018f9c4760fd0cfa18fc53ca83da623e9ce286 GIT binary patch literal 30070 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2 b/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a69a2efab0f432515427ef8d367a397558058476 GIT binary patch literal 4171 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2 b/src/blog/static/blog/fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..fb5fb994d7ac5ba36b301662e84485fe98c85ef4 GIT binary patch literal 11091 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 BxtIU| literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..db9a5bdbf9b8b02406ac2504e0c02ebc3ed9b78a GIT binary patch literal 10841 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..7a9e2e36e75466b4e44044a4b730540143a2671f GIT binary patch literal 19853 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..a9d17c0f6e3511dcae97be84ed65ccce85eb4795 GIT binary patch literal 28801 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 TZ3#Ny)Xz1P>BvObtZDxPS$2$& literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..06a53d531429ba3eea847ba9c9b10ded46653519 GIT binary patch literal 13474 zcmZWv_jjDtnI6f~sQ2DS@<>+i8r6~2d)KJ<-n&&SY2?JqA)7!bCYwTW9BRz6fyD`s zEQE86!6q0RFfrK1WH*~l&Pj5zzw95d&ol4!Hv)W~x7_!B-z{(ZWZB!THQ71`9sWdx z>ij6PM|HZ$|9$%{vC{I@KjDcHzLDYjQQf{Po=SED%k}g1a);ajIeCE9=^RXs#v{0kOwYcCQmS91XxNC*iv*HctL0;cxxS)hWEbZX70tv4C>7-k9Fe`Mvw*cHQaiB z#vrEVZpa-jzbJ;$!PP52gW6olQ{Fra)|e<$!w|EeTF+`Rc=bchgS;k8b7JZ*m@}P% zIy&uL-*@S!IWe(fZ|$^-a`(p~-#-)IDB3%3R}Oivi*D8>89aDTz-`Ag3ZeG;ySnHbmA!^n`=e ztvBENH-C=kcOYP{N)+L#@nNDcR=@{86$xHi6P7E3RER)O>h1I3wy>YL^}=Wu25!4x zfJsGZ>Vs#^80vYB7*NB;keB;W28nmf8Uh3Mwv+EZ;8SX}FyH`RUKW)J1Z?)2-C_(b zx@71bpf*rz z%=(`n{-sPZX%}bW_x|_nOVFl3ksn>C^xn;YR_)R|Z`E0tp5q{%?l4qVWJjrah&s>onrU^tocV{qm9in=dlH(<}(4Wiy$;WmDwk z??V=8=4NPpX<&I(UhX^v`%{831RN+65zitKum$O&!Sc9k43p%W;8u6(lclKM&E$&8 z{XOUbmFzNYqq#gdz`#y`5<6VOm`lJ|_H4VQ!B_}2FgiHgKQb`1j4uiHts+azA_s@G zDJWYY(@m9EAM%od6ZW%rs@RQiyO{}A3fmEr!9O01(I38cwiE)$U0^&T%N;*`40242 zsZH7Iq6v36Jb+GrSCoL&{{7Ks#+Y~B7wHIKluj>HIVUFXR*{(ybOk`KFnYWWGnK8U zBqCyIVs9ZZIW-~*Ltw(vs=~?dg4>F@9fG$?PXQYN?SuG8J;$IJ6v@%;ZTlBrGRDXCI5_4gdosut3y#fw^iXC_I(Ge=B!LG-g)b zyAPcTV2Z0o3$0RSg->dOi$4kXU&1IMusm$+PjCLnyGo{t!b<5oKS%9JP4O0u{Rk-c z=B)y)%So$NvBWXUgC|r8a1{@piGDGIfI!e5vvl^$^Wc54HQ=V8IdFil1J~~d>*|R} z8p`Z(?T5Z1_T7)qtU^<%CN|+g5GswJHG(>2LildkUoYGTRRvazG53m_oln#eZo>@eFKbV%YcQR zN#@j69vIJ^a~urd1N?FTS&LvyvODKo8mGl3`~*ZZh+zzDwu0}La}zfw9h2Sqj|)#g z7C|H=k*sv5Kem=Z?S>&%k}`rGSAOsJoU^ z8Y;RN{vo#hoOPC!Dj{f&+#;}Yj%hPq69(S2tp^7#KKTCA5Z!Khtd@g?Ofj>Gl(BXu zT@%+J@|x%(Dv^^wf#fUKxM>0Hlh9SPC7Sd`0VK#$=9jmsXXB zG9ldgQ|49=`v{vyiCB3CZvzP=R&O^2Ancu*qM2AzQe?pI-YuE5>MdK@Ka% zk?rm1jei0U1bL56F{FCuuCRG#@*q#y$yzY3F`x9cVc;49CQ|sm6~uA}m_!r2i=Y6) zKY6s5$E?$=yji{~1qZQSXLiwVK%TP6sGwLRxT}%3=XkC|eNPgKZGY*3i6H~nF#yj` ze(SR1 zDM}0k{R?#S5^ljgA%=A2juc)l8I-7Mp%pr9i>|fc)$|D9ZQ?E@L)trUu#X)EGoxxh zwoz<*BO-YX6&qG|7yT-TAAsZm+$OF*55b%p?+4Rh+4}S2FJi>H5L`cNgi;g&<|>xF z)YebG2enisj^kpfjJP9RNYjJeN2j2>u`$< zHI849C{u-J^GNdXP|TF5{h=nt#L1)nDn%>kGJt*;sA#+DH479Kp2a%W1K^=e(urR@ zd*@#@!xS{;(;6s>kN50@0sT!W;3Pn0&mt=c7zKss+q)PjAiaZ4piBnfCAyJ&o)g=m zh#l<-R`|6ytX7Cmf2&z}mwChW#~`?}xd=?<#wGSL4dL_ja@nqCfbxeIPBKAn{07X+ zop~-`k283=t96pcF+9@8mdYhAzXH~z*5Q*t=NAuxN6TPlg)cHjbZ{(7>=KXfL}W=s%cm=1#4RVvDn@*8I`EI zmC=Y0#Aro>jZBuPBC`fg}1un!s zBCtD3CAu~PrG{HwKhw2+*!WHTgGXSyc}^zop2=G#Sn=WMAk}rm)@l(+ z&u%94&qK7HT8mdDCUf8eng%6kX*rY80#-G94XaVGHb&1x`!*eU=b>Z|RzTtS%<+6} z9?GesDa(G>_*ntwD+9s~Pue$|<9Rf&uz}&@Ob0WMST2_Gch48^vXM+PEr&|Q3Vw3h zVWKZAz~l?vF;UE^&Iz^9ylU_hk^jR!)@*|rRe;RK&@&?ZGmaV+QIRWcUOv_y$LT14X9Zn3>IE7e&r z7h-UeC-OQ(3NM5hw&!swhAfKWEe0r5rNf{r1}sZKo~A)zldY!lF?s@r7DKrw9sK$l+!3rl8Am_eVt4F<6L zpP(m9wQ=A%APuW+xkkPoHO=O!@-HB)JY9RDxLMS6HWX)-fqo5ZOn``P?J*7;v=J1K z;uOa%Po`Pl?I&b{qq79JPD3i!uN2ZLS^N>Cf6?{>g^Di>su5g1+caC3 zg)xw@KgOfvFmZwI4<7_yVozG({@h1h=)+guc?6@2KVdslS6O2!0lT2SA<>0LaS4G_ zJ-crSC{(kFqLN`5BOc8vR@-miq3(m+m|;mCV1+zp0XGtCK`^L`pzagXbeeKp6jat< zE+_%CJ+xpt2FbIwZiA`2+J2gLWBK`p#_MYeH=qH|th)If=6{0v5G^s5H{MFU_!Q3+ zk!2`=3qwE}Gl#J&Pd7Uh_@?P2iBr2}HAWGK{5V)^Tf--Dt9MY3#6 zpk@$|5m(1>jLV3bzf%LYw=&CYP%&i!u(Aa%5Tz$Y4~JknZ_+OY6~y?$e3=E0?Bq;* zV6fr4U~y-Ivofam}E`O@qG@H z<935ZBFo^;9adshpu`uV*GWd$}`D>&Ry57L^Ob z<+&IDK5^5dn<~Cy`5F&sbAV-E9lu{@+e;wFRm7ow_-p?3tAS0L9VHXAtbEP-7_x#1 z)DIx^QvL)BM8jY1zmMg`H306hz=$vAfORw9`jy++7S%GYA(x-)-n4|k0;o^Ya4h`~ z)b8`x-Yh}ASn_!~R*4`TA}Q0}G{pMJIr?>b*fE}0Q1v8J;Hx}fXK{wNm%GnruW06Q zyCdi_l}j?sZyb=CuHvVf=BmD-?WjzJD2dTmqQ2b(3q!{fHCVwMV!-U_$on*mfC=7x zE-u}^6l^S3z@WMY)aZ6Gfk$nKdgm;&*wY7Wp{N!7q}Zn@*)HcncYm=U)+LC*%-Y5R z8J_y!Whh;s1m3HH=TA$kOP+&d3$0~Tc9aXCa(!SZSfo9dWDMTPEjMStnk|d~kQ>s# zo&=m+RRK8?r`#A6v$AgcXJy9OeD8OD@-!>zn^2*O#|#^f1|72!_{(=50`JKOD_w7~ z#1%oA>jM+8f>b@kL0uhH$@mq=XS0U^bccuDx+bHL&tDfMg$=A7L_HJPm~|RZc18mk0R$(;}}u#3K*n9I%(Qx&(<;p&fenQSPw@ap`gT{(02ECj$JpabdT zuVEpDKV{gaB(!rBa|+M!7dv-HB-l$WKXR_~Kl2?fiTK{G7Dw zcolovFs`Qs?TH2tU=D?l0c|4q#*pLjtRrm(F1{S&&E?pJJwYre!3K|z%2j%L zyu>t6F)9jpm1uKyv8^fWH2(CxtC@LM2s#2{UL9qE0XxhgX6RvTFgSc_fk$MJ8~HBa z#EpDJ2QOu60elvScipMy=@?huN>5KQtLV&up~!_KNOk6XbT}zxmYihRvZufp=%ZfF^o0fFz(ZA!1zr3>)$+DQl?Y5gJt)CbWk3K1 z%mRxy7`GGrPNkYMHbZ7rR&i^6JlO0E{of0!1BLcxf@`RXY=`b7V}{xMa2ZK% z`aC0gbJNWE1@G>!a=6T_-DmgM(9NxZuzDl(x^Jw{{XG2u{ z7=#i!n05NzxtmW`Lz+SBpFZi6j{npGHMG(d9JusDFm<%LSyj>hcY~nhPfASp3JEFG01tSnLM{dMG?MH={Fhx<;7? zfzGw3AOya(dG~%xLhl!76N!$z9+17872I;`yU_mUzL$8s!#IgSZ%Ljfk5<>zT_JOq ze-F158?^21+Mp|nnV>oMPCBkLr`-0`5E8O&dA{7Hz9eS8ZY;vZb)X! z2Get0#T}}^cP^B{Sl~VFp35yf%JbUyT*Qd?U@v0R0%k-E4-{plI6_x%-+m}*aYd%U zxw(MdNhSC)4^d^_Vx0iXpeQ34&!FM%523103#(muh_O)5rF+Q3Z0@t{Plka3v4pS0 z>xN_Z4RJZKFR%ch!Vq2Ie4raB;&RFg0jah`X(@}mjbK>|Nb zgJM!{YN(9Eh_?~^PD!g~fub7Mn4YbSwK@tqG6$B@4+f<3rXT(SOwUUJSuJa&5b9ed zw%9^*{0A?HKDM-Nr7t3QCBal6>-6INq2P>UPVc+V%FHlCFeu#h@>39AjD;He|Zxh%i9D>3)8r8olQW{ zUK@Vn5j{WB0ymvVwLb>!nJfUJ1Vw(D9H4~gw$Hv<`T+$H}*;Nfy zz+765;Ju8gVkLf^XjHnDqZNVS}1RMvs8B@WX_@VK00Ry(O zxjk#GWLqpP4j;TB8aH9t3$DBh*|?mM!o~mo>eIW7R6WSv)Uq+^ryCv3S$tVAS-8$y z0Pmhp2R|5;QM8H4oYc6`sIOs3?R6xy$qcyO)_kF4-u)MFmaOfU2^CNplEHZX%nX*# zsr=6H#zm+7dY>eWxP%_`O@zY~Z3JWXTlx9@N@X0ai$DIKzqwEhJJ;CdksKKpGpJ&{ zJujz>cB2oNM*wdr;9%r$SYHAL$)t^Vw%BH{ zExO9ZS*_$iZwD_Gu{?ko+kdp<@_rKuT>W0H1TB~rZ?Q}O-Q`CEX3~|%SH%YY{sZ|j z+JLE{Qb^|n-3sEez7s>tN#FF5eF&dy7HB&N@T%e&1qTqe-~XwU(k7rp#4NGUj)5BK zSqG5)6kQlq?(cA2L0{LVD25Wvw%uYYkUSV-?VEBj73Tiir|M?VlP&PBO>dU9NMyw$p495`oI;^ z-Y@HPj74Y7CRBNvm$^&|*WWlc?pr;mw~y)B)8;2(l|;+%lVAPmo?jh;T@BI2I*i%W z(F^?{p3ZW?B2?Q_+X|F(7wFRS1~47mu$0$cZv`XOGToMMO(~9bpYzbo@%$(6>;>m~ zgVyKMy`aJ%eGm6c9(P!JDr;bHD<0wTEZN@ppop8Oc2+C_qJTHZU))KQ(hp{JBsA0r(IPF~?fk-NjlL_4V&XgJ()W5q%RznMoR( zc9;=!&^6RftnDqESy*{%2EBbP&kcP3W-CwC4%u^NB$t~&Qx>4=ln$_YPeRyYtZL9b2IycM=z9}$^c|buv z?{9|0H!vkFi$U}1jG^Y7nFhI5@G?IGUG&*QpaIBYZ9iDhCTa|7=-Z>Ibb)@UA*d1qjia!WgHiRL9vkfJkSqt`3_8!jLK|1l>fKvII5mt zb(DE$$tv^7H?caBaj=XlUBB>a7%Yt628=gq0Z-8|O9;DQdSYde#p`u5G$a2AqF6ex(-0V?|(m*({LT}?je zjeXucm7}b(59TEn9;s|d^pF{@O>BVYoIu@>Fi33+NCS&h!{Eht#O782ej+sR8N1ty zA%5H!tLoD?Kv(Vt2^>y634^P&va?%K97KwWtXq~WhW`gLSP zyPY_yyB+mR_7`kc8T;EOeQGbhSr7?5`HL0H_QDG{$7cMZzB$|)fJ z%FAN7t%$BE>+ecg%$mT+zcF6sn*E^NHfH0Ql#7tCbc->|B-NSa8ZiC3edQi> z@&lBOvTYSi?Z~_e&NxQBY%Fxa0AvmVG*y32&#+1dmSqY&c1vPof``)EfApAKVI7sB zr>{L?TfshcjW>T*#i*4pn21^f7n*aoKv;M*m`RMjq=1$K%?#DC-K4!0I zt4Nj6{t4%0LYRjalv~;|Vc9oTR#T~?<)44XO!>8v)BLF?X+F;Ezux`r{zb4B7yKe~ zqKu*DojFda#jJ{0o=MehXW|Oq=Ll(=?XxW^OSOXu467t_V?CEL=>cm9Z?+qdacH%2 zEr!O_M43Fu-L*F!R+kp@joB)4d>=gssJr&NRi@0VP%}MpAPkLQw^SW_X>1!aec}9( zioRxA3iS58{Kg9@rKL*?rKN?s2n=0xxfSYgY8SU8Nd+!BcNElh@jY<=Ktu+Vpb?4{ zySA%M4Z-^Koe6Esh9=l?Q{IvxOYVr6GszY(?6_CJEUQRw2FqH@Ezi+%=N z6GH3U+_}oi$t|u5fl|)!+Rvb5e~UnsMJjL|!PlS3Nx;QG+t+W25iJUwTn-wV0jFv1*VAK-SVR0yqPu@SADjCxcBXw1BX?QxckJ#IK`&8y2;4F&G8R$voRiX8u+C(OYZ+ zDG-Jr4pM2Vjo#=!*n0aQTPS`#_zs}^h%L7i!dzEjhn3>%3I;s<`uw1p8H;I1Pm>!M z=!68(|J4xnU1aPW$3e^hw-^whkFWw~_8#cP7(2=$Jv>u46$#iv*ae4lLa{l)_EgjBKy>njtR0d z%m|)+UIu>LWWc3+xA|n3$qAVS+tj;QS~$XBtq3ZicI(8RVXFYnpWsi30)IY>&v9gR z7Iz0Qw;0BV9KC%CZ|t-fb5iNo&*XF;PUY?d?BLcAo<$JHAM;`=2f)0G!BklRi}ehQ zEQ7fE;Y+^VZJUWQ+r%@fH#xxM%T$i&`TX%AH`a1m<|vqNX@{^10B*E&UFFK0@6KVi z85QxXKbLFx(kL;rd{rBeA%l`%C3ZQ~^`|`PcV?>_rGCZH5#C_qOCJ=gi`Kjb zPyfh6yW*qg9U>|)3WLn#fM&HN7~CfC-JO}>=CYLJzBa4FJu-Pn6nK_?{_IE52@ciF z&?#+!BOpAF$1nQZ-E^U?U1=n{FR-P)J}|$13bg#yBaBo`5eU%RE2EA*LSPrqJ+`>2 z0K-#B82d1A*N=ieJO+v_bLxf4bn03f=1F-w8(wH7NHCfE$(!7#oi-EQI|JLpCWoOL zlWFcVO*gbw0?kmeQh{bsV0Rr`m^zMH)fPWJn$eCyTLd$A>g);Cs69^|*Di@r38Y-w z6M>ae_koxIBMvk5P>es2TkH#F(q4b{>~CGeWtNp+MW+>QY?!ewy8S-7UuAP66C|h& z1S~+7AiM#{J^QA0Q=u;dw^YvbDBzPbI(*{Y3@}_FKvDGh*Tib!SA6Yq(0`GnY3Y-nN_Nn zq#Ls_n1cv9pqNA%bdf8g%Jk;P$UG3_>$d+uclHmxeK*(uDLwVn$wz}bXClh@jBUuu zjoXr{dskp-h+&wzSam@2NP)Hh-7(BNC-?1@3=CogMLFv9f4$mf$!KMHEyQ!noS^N-%am}N6Q%Uyc6F9sJ-zzKgbS*ojd%@YmDhZEMhR`2I4K_Im4!}xR+ePEd!wtCRSjIjBjx@ zZv`4!M3+L%sJHVgp9@i!WH9*v--bDEV&IU!qdRnLI*2D;h5+MTpt3V}fb~4rhtJl4 zKmPssJ5v}5V1Cvnd_U1;F5Rhq-mVq)hK#}GVkPfDABME)L0zeA`9ZpHFvkK%zQXqW zPtX61Rbd%s)K*}yb!N&0TZum%s^hB|;>;t7V1SPApcD97R|pW;0E%a&0Lh|48ApJ* z0_^eW`_-fRX&@$hb2532&zG-*8HrG4R`Cekqk%`5Vzn@NyDWU$pxDfNm<$?HVO@J0 zbpAzF6+1$oUjW!T+UAQB@i3eqjDrRTCN97lY?IRSdy2CL5Utkyfh3@=gXm&wXIEl5SO-x$+=vAaWI? za?VYrc0_UV%cn<%>Y*=vz#Ipd91gS#`L)=CoK_FwO9b?DYmw>S7E~p`4&@#K=}n;P z&yW5{l^jM_-*EK*_)c~wAS#H@RKBfIbtp?DNadY?@Mh%E1{4;B*~UobMzur$F;2LAfwQOCUO9{2x# z;8i9Bz)YKj(7nFxbzs0-lyBcA3?7JwNX8%L=4U3cV%YZpy#vT~aX$+%+=BE#=y2sP zuMWPX3F4!I|F7qsz=(Sne1GhhFbe4m&$jjAT_*C8x)pkXud?+5`TK=r>+Q{80i$^Z z3@^a8Je>qe^9=-#PkixHDCJUmMZ?C>jYmh9*RH>@la0A)7OdMkC||n*hPor`%AJK6 zmDM{br+)b+hKENO8z9pY#BK`Ed8Urnx>6Xi#QVqdc4SGo2&FPoM`n;JYy~diVq#i5P$XhW}SkQ9_Dv*u?x~=oT9E?Hs zgC8(SpAA|t_W=6EjD^N8N-}-Ggkd6YvPiQbf^p@r>&&|WZJG~Gr`IZL`oP7k2_Tsj z5S7iW)!+@w3?>6JZb_|A+z1%^$?F4F2b?ho4hV z!31Bffq?<&3BGeGDPX3Nr|#J9PE8*yc7`cv6y=)?ogsEeKlCtULdL?JnBMPH1`H4D zquGlBAuABp0$}nLbe#-^b4~x+vAo~F-V4wH%Cc%}uz}eQSvLgExpc4R!1Pm}8@b41 z*dNB~y6F$pGWhM~B@k|Zcsj@OZ_oSz*7f7QU^7UDpWZ$TnzyMV!rF24G5yoeOm`4Q z>2{gSHQ?LlL`Q5xFR*awAQ#A3Tp82$+WjEU@^SAi=u+kgu)%#KEFYH9Jg)=BEBl$# zH(&0~%=Tc41wsUSsj2g@OkdaznOX~BVHLDhOlaT{LJ@dr?yPdyB&q8;CnEts&TN#AKgv!Y_fa&`0-+7sx zzXZY;UVdz7c6f>*1h{EH9pw58>{egBE0f@DRvq)`AeKAnLcaOP5t(O8rLY!get++e zp$d#33b0KdRrfK5G$0XTZf`3mETm&1Q6_~4qWkG_HO#uwPyp*MlQ9ebq< zif#sK=@c)q!NGy_@^|;t$V}19Y)>AY83>^#N>)@yx3S$cc&0H3+!|mD;eNN+ur``1 z*pGH0K)k3rsNNcuv+E3q5wE0IQinkrl3>lcX~Q@RHcp#7ZQ=*UV3TLX_)q|56Q)~a zt@WFu7G+{iE6>FDpZSH%L`GaaE*4k?2)|>HKKA9a^n`##4`l7d_L%;~-&-B6aozunjBTq(%d)U9?}=m`?Vl3^ z_LE9T$jT&-)-lDJ09aZ;yy~GXV=@k98jfB#nzPGjXXVN)6Z;^TRJ_s0;LMBxQoS?5 z{mc^}d6QQFY`8oy;7Yv=@eB>CW`tFOwXLl?U_=)K*BR`pNeNE)??SVrKR5V2J$Y&S&Mf`C4&! z;b~SpFsW*I-|1KXsLqGgDMtUo>!9YnzUc(L>DLIE~+hIgN;^ypB2y-wJ%<~n1HDrW7KZvFdhBB?ZH0Ky5pEwwptmg@8=43`V(aQ+K{Q6sbcfWB?_u11f+BzR`f$s>EDV|qXJ`}V6 zgt~;}Lv??@=dpwexdOZ=R_HeLp=8iJx2mBL*5%*GkkzpOx%nfnUZ%mUjbTS^2!kC= z_2e|u^{m!%2qTuuCjopIb5Wn3k|tWm@AS zgUoJQ8Bais|Hp|(GRI-44=gM*NDxm$A8Lql7t;!g8J5Gno_`_FT&BSZNjxD#tI}wO z+y3S;kOuaYHJBlgq z@7`lo15vZwUQBAVQ=SfnWu$T>T`>yz?CDhSS8ug&oL(~orQxGvVltipAD^^;K#Y3m z;EsHiyL8||lwz<8;TyMQf$tJTS3x`NHZSvRz!xtyjRn_7@$u|Iz&s+3oIje7 zALg6{i&+8GLw1MvTL3-8XR@1yFgYA;>!V{>R>01YZ*Jd#t~q}%cXimcqP}4ghFWfd z3CwWKHQT8W$_+Bh>Pm~5afSlCtc%@x&%FpMOKlYL0N(byrA~_D@^~)d6o|^{l{A;plqws>zt-8`5$fOnZ_@LV@& z7&6bq+A19`vx{T=1r-d+ls4qEMk8v$r(Rf6&XL7TQR~4iO{;cdgXw_w_FXVbNyQyj z$cpLrYGB>C6C_~GF5Lk&vyq)i2ZBN5sIPsT6vFrRwCg z1kRhBcKG-?7$M`!V5Wi~B?_A7Y=F=?wg%ArG)P0e+7)|;KxHKcY!Eko!{Y@)m}<<0 z6!r(fwO5)LqK(4buaD2cC}yZWK1QzqDlO9EFTc-yc}3-MKXtPpUklJu9Z10S4T$ZG z>P=g!{(YE84o-4aGgOh7V5gA_&zGkZ!{S?kpwC|ft0YKavkD59l}*#%X6aPn#$19f z8BDiy$rT#6-Tb>2+Kd?A-}ixAmY6&#VC5a$0TmqNY714l845n^S{N}k$K>!ef~G%u z3S1J$CBY7qYazQ(`yNQov=y*-I`Joh{CJsAL6?EbnJ<5dVTnwERaM(Dd4^e*^AUTz zZJJ(LPK*swxAkI}FjWa2g8-l23t|l7YIz#w^eDY6;8qGC%PHKC>0i5k@-7|L*9Zc> zdPOvNC1TOl)2T+rY1UcneSnU>z48bx9?=g{1qp;q#crK_T8*I+R};WLz@avoFo@a| zb?Ji@)pe};)|vSoQA`{lR}^0(n2uXsW}*SS5MUY?E&S`m3Hn{YRAE#1X286is%2JV zJ>p&kkm?ux^sEgW9 zV@v>b>>&K$@io2Io(N;8PJiG526)%apPBi!C@$53`3$S^X=^BeMR4sAv2{Ed(?u|y zryCq#^@Noy6v-63UK;(&qtTVM{FDQ%sHThW+p4>Z8=W!1S5!dedH2TZvi};iYjpx^ zq939&Q+LBwCu98P2SET&4%~Riy7KG^vB8s&1<0&6BL=fgRjTz4x7TP_Gg1)VDyu4H zHZl@!P<7Zv%>eTdwcAelP;Kn6Kfn@u6SGt8+AK*f$n|3s07fsieSj}Nyd_iMJ;-X? zW{)<(N4Uv|1G~|KKt^Mpvl9a&z=JT+~+LgTqomF z<}uwouO6EcZH7@Yj|cdO0uMmwbf6^x#Jbty)-=#jPBMzIZF1mCHgm(l(QKw+eq(=m z`Q-raSH}Q$LICDchNsxVd{Mzttg$IRFEp+2;0kOT^*xa3>pwN@I|fs|kjA}_Zj;rE z&+cq)G0o_F#)L6u&QQ_E*?uZ#w`Gzg5+bH86u0i*CF7GXS#m2uUb!lS7_f{ccns@Q zCPf@`eUpLQ_X<7Fbjr>K0cdgzJKPv?d-T5Sex#=r|J12tAKj)|X zO&_L0_RDS&BX*4HrYRn)G9|CQ&jm&cbI|m0X6m&<_#K#lSagE~tVR};N~0Q?wP>qr zPrUhEcdlWEwB?PfP0A(0JU@Gppnx5_&4SxI^QKG%K}A5~+*%iE&pglS$y+!59jSmD z?_GZ?asU&?jw8-c^>g!JKy5+>c_Z0$aCL~@zT(f1JYo9kx$_XGUu3!O(yfcC>R>Y6 z0wL@owV&VL6d7{k6_}A%e+C9-FP;zs%-+Dv(%`^tFjs?8C+69!0J_w{i_Kuo^drLN z^32Hg7)uD?^+3|d+!3)S zl3Qqpxf}QS8KD5PT6*#CKfM>M{E^bfwM@vKH7GBC9Sq$3Q+^qN7i{P;!0hAWbPt6+ zf!DKC4Uo9G5ik=%gAXhgQt{S=P8E&~FLGhF44Ltrr=LyffdVF!PKV8B9|Vsr*e`f( z3dSlwLCp}l0pQ08@Pf1lgA(i*MJosp%+vxhC~y__bKyOE-Wxnx_e!h3MxuWy=uR&A5HEKyBRK^Gq9Ig26cb>-ZhF;?x-%RHsm zd?XK39uVMWLb3w$8W-VI!Q85F(XZ*0-Su&|Wbe?0Ucj7`;O=x_l8%(i!A;p+!f`@`tF500#C7f#D1 z+%$WRhTSN(C6$HREWgzP7tM;`h1}PE3KTP<1y1$6{p_nCfzj^UCt*d%XuZxoB8_bb z=vGNdZv$WO9(qf?Pt^w|9Fn?YyE&^;(e&%?*+F)PFx3tLzI|S_N;C{ie|!QRTcA>0 zF4#+3O^)Hlul#&{j~_24BSYg0p)>O1ZG3F>c3q$Fn>KdgogZfQwI8dR+b^lFeu7Jf zyg@(-6K6_n(@*&guD}h|LXTLm&8I8s3Z*ipWBZ!54jCM#mXf|hW{tWMzK0u99&ezVi0%m zLZfP_RWhl3=i=_*aqy~tfAw{iN=;}vm>mpAfowRcQ-ApO=MT7!%`+iZ**pLIo8KMk zh;RG*OSfZS)Y~%+*y8~Mzmq_9+a{Qk-@I;TZMmN)rGc31`}iHY3226lLT(&mwrEJT zpbIs4+rnJ<<{0?%$9rTtI@)CqR*bWrdj;MDt~ z!Je%>{4ld>1xq}6&B+sM9;0EQD=NFTFTEUE!NdW2*lVVESj)W_aJ*S3i;Y2Bh6q-9 z{$QSTB=6K~x`>H$uT*q~fAS(G*b9rAR8Mh>#|WrPIJ{Po#`@=t?TKNIP|blLLF~9e z48BpI0?){XXp=A!RQ1BlN=OO!y?9U90Davw(-g}xV*f0F0DPB#vHbx`6B<5!(1M2! zl9m0_6=8xMiJK|b?=gx1)0Nru>FYjpk*e|466X}YQlMgwK_KLtyI2|pq1ODUcySNL z!jnHvRAF<&7+_J9iV1F3TZfrv)bBvm=;+a~UXPFdz>`na+JXUIc=iu*6;N~)Gkg`I zw=XvRtt{t8W(;FrYY6X8g$7K2k$MP5i~)2A5)A>(uTv@u(N>yEvA2Sl+(JS!u{sh z($rb5zU|5+0^&-tMi%Q~SQvmaZQx-Ow>)e2karmpfo= zK!7|*Fd zI?aj_YqYpDsbGcER;4`AROy-9ivID$gR*)!f#J0XGOB}P;KE$tUOYTN6J`2cPVjU9 z4{`cw8}MhxEmZTL!N~M0X^R!G0CQ1vj75y;FCVR>F{bq4jE+xrfy_fLJPp#&ty%1` z3<`sj7FJ=-GM{B=FFXbsP_Z9|E!b_?S;$`Sb1dYA6QA~2r;uz~39ZNhklQs1h&I=dUMwZooHG5z*9ELF-; zhIG?^CX1;D&}ZJc83|BTkkN_BVkkEVU`=PWeS5dpD0F)2w#>3?go}CV=XA3bogBv` zKOC)3l$C?5vSPY>7PIcMqG}|pdzhKagJ#P@{5ETV%Pb|pj)6#Ovyc(}pKAIS$8=nW zI(-oAuz1s-^T}oYP^Elou8iqhK@*d695U!66Kme19n36NOQ%eMufOBX)HldPEZxgK z{N3w=e3YvB^ekBh&lW_0qan< zg8cavx%dWm6CVizRG!g0&3jf6h1sT`^!7h;a@GIlBbf4c$y4X$YbPMg|7CiTFQVW~UXNGQ`ZtjfJ%cRhp0nT#j^Gs{ZWLB6zDWP+5s+zp0CjoAF^R<-fX z4#XP)y}c`2TbGPt^yzi%6f9p2tbTw;`2kumzW5}_Y=&D%ziLX)P2*>;FkR>DK!D!9 zGk!Yq-0RFQpwh$Z!=qw?X9~+AqP3AKrtyu?zx@1F%+{Y{gWNDO+r8o|6W{!FbfH{zZ@C!E3dv%a z=B7Y>EMtLg2Rgi#Y0?9!hXg|aK1+@0DprB%KPE&cb>*uRh7DZ2%SMa{dctYkyRM^y7^p3KQ$5_k?=yxT2| zr!R&Ga-EWLU15gBI8|0bck>{1jIJt!CD!f|o3W~}Hg(1}AIzQoF^DcvL1V%;Lg=xp z&!%&e6KMvpQH70t&l3{);r)DYe@rtNh!@36)#m2sVPAg1`Q*C+bPKS;%mT_qfsJCd z@dScBW&x`RNW6B8egNps5i$9U)Go%xGwU$e^jC8?xFy}iKZ1co<_uu^9$UH&H0*a# zS(vDz6Ss}YG6bvls2E>*gipwkRh9%jd+oi5SoL70;dsnP_o>VakLQL<`>x*EH?amw zkm97CmZD9;(A(23f(+AN@De*uJ$gch?xMaqUc|sp+kW_#?SP5|M>X?@lq?M9mqUQ&eXso`<*7Dy=oQmjn}NYxJqV{s)} zMjO+AW)dSF$d@?_1khLf?Ci-H9BmoNk5^f$4%|M;=<{%13^dbDaQ0zn7YKdAn^nCG zMZ>Ovq^VOehrFBz7^(@n1yBVGD+yrrTftHK*l-#iLR<2B#j4*IY6f6wr#+~FQq z)amOnYR@aL!Bnvl6H zV-0=C&|0mfR6_O1!VlP*+vO{GJ1}Z~r~s|t`FJ6FcR}5Nb zlDWq+byL|UE|+dNSYU@|g!frhg{raD!z@_X2@x9}7M*uJwi1+iwje z%2=3Lp3}K8Rm#yQ%3Gw zz>~+j&1W>quy|tI5Cn5xhFMViA14fqOQ3Y_I1@5f)Wt}ylrgK;AwVnB&h#I|hW`4t z3U`@iaCl&L?+}@?;$5*Uw1mGO{Y6ITu=%C8BX#YZ?W^Gqt)e2!CfQ^FRlfRFmGNCW zYKvHFg?x1s)C}2NW+2TW0NWkiUDPh)VklQq5@ZiD3rS5X0b4?V@%_d<*>DIGQ#XI6 zg+1uSjNw-Jd^)?|o!dS1*^A)D@*(Dn>H01h+#qu%mNx-qkmLoB2V{#yvgQ^OkYaZ2 z<=*PDdTuiWvSa}96R>=d8MtgkuL0qZltqxrw&gI`4F2-?ScB<5-#ZtB5lazZ)oSP- zhRH4q47<_c+)*2(X=!9~`rEts?2+}5c&LWvdU}EvObqi9J`|WVi!}XhVi~!LYG4z! zg_JT)O~1AG=R4o`Wjp}>WaI*|=9iAA2<4@Mk@cSa0oOK$oS1iPqk=LgX!UZLvD^Ie z53r079)|05*f?9mN>(ixs1gM(Ddn*MQ_#e8Y94a*sr&ee-HI`DX5uE(42X$J3XP3S z46kEctu6R0>jvf#aPc>Mrg@pE`@>3^_~>z6T_>!sPmFzbTX+sLv>$R9a^d#qWGK~M zVI>!bSVb`4AwJzXP<$&89uj8fo>Usa9E!R09M8m}8a@4UcV-By3M*8&0<=LE>ecR!X1W|(D!Qy+I38qeL|{7->9 z@|6N}KB7V!vOn0>kLvBecoS zslsqI)5(Bypy;aBCNZ`w235`lgfY2H#mqYZl`A}t-rix0rQK7rS~Yg%<@taOdR#Sb zRf3oh9680nFf9P{z6=J0J!D8`7!H8WN)of>+);)C@Zn2-fGLa_w7M!Tc0+hrW{)=n zh!B;tz=uqoy9+$bkO+)*&}uU>vr)$U+>Z<|7~r#mAWS=^4Nx07%Xd4N{EHHrtdaj$9<5ytKC;|Jv~X0HezB$p8QV literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..8197c399fdfead2609be6b1bf67de0817672480d GIT binary patch literal 17036 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..b9cd540ab11feeacc3f68ae1a653aa671cba1a9c GIT binary patch literal 11233 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 IKQQj_zi~!ArT_o{ literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..fa2e381c76733f7bc839aa9585b7ae3302f2bf25 GIT binary patch literal 20235 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 aLZ>IES(8weI9u!RZy$Ru-3I@UyZ-^asp?Pw literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..da3f7ecf862f1d85343159c5c873e05a1ed1ff60 GIT binary patch literal 29777 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..0b4211904303e340f549c04802a3477209f1c603 GIT binary patch literal 4096 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
",2===_t.childNodes.length),S.parseHTML=function(e,t,n){return"string"!=typeof e?[]:("boolean"==typeof t&&(n=t,t=!1),t||(y.createHTMLDocument?((r=(t=E.implementation.createHTMLDocument("")).createElement("base")).href=E.location.href,t.head.appendChild(r)):t=E),o=!n&&[],(i=N.exec(e))?[t.createElement(i[1])]:(i=xe([e],t,o),o&&o.length&&S(o).remove(),S.merge([],i.childNodes)));var r,i,o},S.fn.load=function(e,t,n){var r,i,o,a=this,s=e.indexOf(" ");return-1").append(S.parseHTML(e)).find(r):e)}).always(n&&function(e,t){a.each(function(){n.apply(this,o||[e.responseText,t,e])})}),this},S.expr.pseudos.animated=function(t){return S.grep(S.timers,function(e){return t===e.elem}).length},S.offset={setOffset:function(e,t,n){var r,i,o,a,s,u,l=S.css(e,"position"),c=S(e),f={};"static"===l&&(e.style.position="relative"),s=c.offset(),o=S.css(e,"top"),u=S.css(e,"left"),("absolute"===l||"fixed"===l)&&-1<(o+u).indexOf("auto")?(a=(r=c.position()).top,i=r.left):(a=parseFloat(o)||0,i=parseFloat(u)||0),m(t)&&(t=t.call(e,n,S.extend({},s))),null!=t.top&&(f.top=t.top-s.top+a),null!=t.left&&(f.left=t.left-s.left+i),"using"in t?t.using.call(e,f):c.css(f)}},S.fn.extend({offset:function(t){if(arguments.length)return void 0===t?this:this.each(function(e){S.offset.setOffset(this,t,e)});var e,n,r=this[0];return r?r.getClientRects().length?(e=r.getBoundingClientRect(),n=r.ownerDocument.defaultView,{top:e.top+n.pageYOffset,left:e.left+n.pageXOffset}):{top:0,left:0}:void 0},position:function(){if(this[0]){var e,t,n,r=this[0],i={top:0,left:0};if("fixed"===S.css(r,"position"))t=r.getBoundingClientRect();else{t=this.offset(),n=r.ownerDocument,e=r.offsetParent||n.documentElement;while(e&&(e===n.body||e===n.documentElement)&&"static"===S.css(e,"position"))e=e.parentNode;e&&e!==r&&1===e.nodeType&&((i=S(e).offset()).top+=S.css(e,"borderTopWidth",!0),i.left+=S.css(e,"borderLeftWidth",!0))}return{top:t.top-i.top-S.css(r,"marginTop",!0),left:t.left-i.left-S.css(r,"marginLeft",!0)}}},offsetParent:function(){return this.map(function(){var e=this.offsetParent;while(e&&"static"===S.css(e,"position"))e=e.offsetParent;return e||re})}}),S.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(t,i){var o="pageYOffset"===i;S.fn[t]=function(e){return $(this,function(e,t,n){var r;if(x(e)?r=e:9===e.nodeType&&(r=e.defaultView),void 0===n)return r?r[i]:e[t];r?r.scrollTo(o?r.pageXOffset:n,o?n:r.pageYOffset):e[t]=n},t,e,arguments.length)}}),S.each(["top","left"],function(e,n){S.cssHooks[n]=Fe(y.pixelPosition,function(e,t){if(t)return t=We(e,n),Pe.test(t)?S(e).position()[n]+"px":t})}),S.each({Height:"height",Width:"width"},function(a,s){S.each({padding:"inner"+a,content:s,"":"outer"+a},function(r,o){S.fn[o]=function(e,t){var n=arguments.length&&(r||"boolean"!=typeof e),i=r||(!0===e||!0===t?"margin":"border");return $(this,function(e,t,n){var r;return x(e)?0===o.indexOf("outer")?e["inner"+a]:e.document.documentElement["client"+a]:9===e.nodeType?(r=e.documentElement,Math.max(e.body["scroll"+a],r["scroll"+a],e.body["offset"+a],r["offset"+a],r["client"+a])):void 0===n?S.css(e,t,i):S.style(e,t,n,i)},s,n?e:void 0,n)}})}),S.each(["ajaxStart","ajaxStop","ajaxComplete","ajaxError","ajaxSuccess","ajaxSend"],function(e,t){S.fn[t]=function(e){return this.on(t,e)}}),S.fn.extend({bind:function(e,t,n){return this.on(e,null,t,n)},unbind:function(e,t){return this.off(e,null,t)},delegate:function(e,t,n,r){return this.on(t,e,n,r)},undelegate:function(e,t,n){return 1===arguments.length?this.off(e,"**"):this.off(t,e||"**",n)},hover:function(e,t){return this.mouseenter(e).mouseleave(t||e)}}),S.each("blur focus focusin focusout resize scroll click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup contextmenu".split(" "),function(e,n){S.fn[n]=function(e,t){return 0 { + console.log('MathJax渲染完成'); + // 触发自定义事件,通知其他脚本MathJax已就绪 + document.dispatchEvent(new CustomEvent('mathjaxReady')); + }).catch(error => { + console.error('MathJax渲染失败:', error); + }); + } + }, + // 输出配置 + chtml: { + scale: 1, + minScale: 0.5, + matchFontHeight: false, + displayAlign: 'center', + displayIndent: '0' + } + }; + } + + /** + * 加载MathJax库 + */ + function loadMathJax() { + console.log('检测到数学公式,开始加载MathJax...'); + + const script = document.createElement('script'); + script.src = 'https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js'; + script.async = true; + script.defer = true; + + script.onload = function() { + console.log('MathJax库加载成功'); + }; + + script.onerror = function() { + console.error('MathJax库加载失败,尝试备用CDN...'); + // 备用CDN + const fallbackScript = document.createElement('script'); + fallbackScript.src = 'https://polyfill.io/v3/polyfill.min.js?features=es6'; + fallbackScript.onload = function() { + const mathJaxScript = document.createElement('script'); + mathJaxScript.src = 'https://cdn.mathjax.org/mathjax/latest/MathJax.js?config=TeX-MML-AM_CHTML'; + mathJaxScript.async = true; + document.head.appendChild(mathJaxScript); + }; + document.head.appendChild(fallbackScript); + }; + + document.head.appendChild(script); + } + + /** + * 初始化函数 + */ + function init() { + // 等待DOM完全加载 + if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', init); + return; + } + + // 检测是否需要加载MathJax + if (hasMathFormulas()) { + // 先配置,再加载 + configureMathJax(); + loadMathJax(); + } else { + console.log('未检测到数学公式,跳过MathJax加载'); + } + } + + // 提供重新渲染的全局方法,供动态内容使用 + window.rerenderMathJax = function(element) { + if (window.MathJax && window.MathJax.typesetPromise) { + const target = element || document.body; + return window.MathJax.typesetPromise([target]); + } + return Promise.resolve(); + }; + + // 启动初始化 + init(); +})(); diff --git a/src/blog/static/blog/js/navigation.js b/src/blog/static/blog/js/navigation.js new file mode 100644 index 0000000..f7141bf --- /dev/null +++ b/src/blog/static/blog/js/navigation.js @@ -0,0 +1,55 @@ +/** + * Handles toggling the navigation menu for small screens and + * accessibility for submenu items. + */ +( function() { + var nav = document.getElementById( 'site-navigation' ), button, menu; + if ( ! nav ) { + return; + } + + button = nav.getElementsByTagName( 'button' )[0]; + menu = nav.getElementsByTagName( 'ul' )[0]; + if ( ! button ) { + return; + } + + // Hide button if menu is missing or empty. + if ( ! menu || ! menu.childNodes.length ) { + button.style.display = 'none'; + return; + } + + button.onclick = function() { + if ( -1 === menu.className.indexOf( 'nav-menu' ) ) { + menu.className = 'nav-menu'; + } + + if ( -1 !== button.className.indexOf( 'toggled-on' ) ) { + button.className = button.className.replace( ' toggled-on', '' ); + menu.className = menu.className.replace( ' toggled-on', '' ); + } else { + button.className += ' toggled-on'; + menu.className += ' toggled-on'; + } + }; +} )(); + +// Better focus for hidden submenu items for accessibility. +( function( $ ) { + $( '.main-navigation' ).find( 'a' ).on( 'focus.twentytwelve blur.twentytwelve', function() { + $( this ).parents( '.menu-item, .page_item' ).toggleClass( 'focus' ); + } ); + + if ( 'ontouchstart' in window ) { + $('body').on( 'touchstart.twentytwelve', '.menu-item-has-children > a, .page_item_has_children > a', function( e ) { + var el = $( this ).parent( 'li' ); + + if ( ! el.hasClass( 'focus' ) ) { + e.preventDefault(); + el.toggleClass( 'focus' ); + el.siblings( '.focus').removeClass( 'focus' ); + } + } ); + } +} )( jQuery ); diff --git a/src/blog/static/blog/js/nprogress.js b/src/blog/static/blog/js/nprogress.js new file mode 100644 index 0000000..d29c2aa --- /dev/null +++ b/src/blog/static/blog/js/nprogress.js @@ -0,0 +1,480 @@ +/* NProgress, (c) 2013, 2014 Rico Sta. Cruz - http://ricostacruz.com/nprogress + * @license MIT */ + +;(function(root, factory) { + + if (typeof define === 'function' && define.amd) { + define(factory); + } else if (typeof exports === 'object') { + module.exports = factory(); + } else { + root.NProgress = factory(); + } + +})(this, function() { + var NProgress = {}; + + NProgress.version = '0.2.0'; + + var Settings = NProgress.settings = { + minimum: 0.08, + easing: 'linear', + positionUsing: '', + speed: 200, + trickle: true, + trickleSpeed: 200, + showSpinner: true, + barSelector: '[role="bar"]', + spinnerSelector: '[role="spinner"]', + parent: 'body', + template: '
' + }; + + /** + * Updates configuration. + * + * NProgress.configure({ + * minimum: 0.1 + * }); + */ + NProgress.configure = function(options) { + var key, value; + for (key in options) { + value = options[key]; + if (value !== undefined && options.hasOwnProperty(key)) Settings[key] = value; + } + + return this; + }; + + /** + * Last number. + */ + + NProgress.status = null; + + /** + * Sets the progress bar status, where `n` is a number from `0.0` to `1.0`. + * + * NProgress.set(0.4); + * NProgress.set(1.0); + */ + + NProgress.set = function(n) { + var started = NProgress.isStarted(); + + n = clamp(n, Settings.minimum, 1); + NProgress.status = (n === 1 ? null : n); + + var progress = NProgress.render(!started), + bar = progress.querySelector(Settings.barSelector), + speed = Settings.speed, + ease = Settings.easing; + + progress.offsetWidth; /* Repaint */ + + queue(function(next) { + // Set positionUsing if it hasn't already been set + if (Settings.positionUsing === '') Settings.positionUsing = NProgress.getPositioningCSS(); + + // Add transition + css(bar, barPositionCSS(n, speed, ease)); + + if (n === 1) { + // Fade out + css(progress, { + transition: 'none', + opacity: 1 + }); + progress.offsetWidth; /* Repaint */ + + setTimeout(function() { + css(progress, { + transition: 'all ' + speed + 'ms linear', + opacity: 0 + }); + setTimeout(function() { + NProgress.remove(); + next(); + }, speed); + }, speed); + } else { + setTimeout(next, speed); + } + }); + + return this; + }; + + NProgress.isStarted = function() { + return typeof NProgress.status === 'number'; + }; + + /** + * Shows the progress bar. + * This is the same as setting the status to 0%, except that it doesn't go backwards. + * + * NProgress.start(); + * + */ + NProgress.start = function() { + if (!NProgress.status) NProgress.set(0); + + var work = function() { + setTimeout(function() { + if (!NProgress.status) return; + NProgress.trickle(); + work(); + }, Settings.trickleSpeed); + }; + + if (Settings.trickle) work(); + + return this; + }; + + /** + * Hides the progress bar. + * This is the *sort of* the same as setting the status to 100%, with the + * difference being `done()` makes some placebo effect of some realistic motion. + * + * NProgress.done(); + * + * If `true` is passed, it will show the progress bar even if its hidden. + * + * NProgress.done(true); + */ + + NProgress.done = function(force) { + if (!force && !NProgress.status) return this; + + return NProgress.inc(0.3 + 0.5 * Math.random()).set(1); + }; + + /** + * Increments by a random amount. + */ + + NProgress.inc = function(amount) { + var n = NProgress.status; + + if (!n) { + return NProgress.start(); + } else if(n > 1) { + + } else { + if (typeof amount !== 'number') { + if (n >= 0 && n < 0.2) { amount = 0.1; } + else if (n >= 0.2 && n < 0.5) { amount = 0.04; } + else if (n >= 0.5 && n < 0.8) { amount = 0.02; } + else if (n >= 0.8 && n < 0.99) { amount = 0.005; } + else { amount = 0; } + } + + n = clamp(n + amount, 0, 0.994); + return NProgress.set(n); + } + }; + + NProgress.trickle = function() { + return NProgress.inc(); + }; + + /** + * Waits for all supplied jQuery promises and + * increases the progress as the promises resolve. + * + * @param $promise jQUery Promise + */ + (function() { + var initial = 0, current = 0; + + NProgress.promise = function($promise) { + if (!$promise || $promise.state() === "resolved") { + return this; + } + + if (current === 0) { + NProgress.start(); + } + + initial++; + current++; + + $promise.always(function() { + current--; + if (current === 0) { + initial = 0; + NProgress.done(); + } else { + NProgress.set((initial - current) / initial); + } + }); + + return this; + }; + + })(); + + /** + * (Internal) renders the progress bar markup based on the `template` + * setting. + */ + + NProgress.render = function(fromStart) { + if (NProgress.isRendered()) return document.getElementById('nprogress'); + + addClass(document.documentElement, 'nprogress-busy'); + + var progress = document.createElement('div'); + progress.id = 'nprogress'; + progress.innerHTML = Settings.template; + + var bar = progress.querySelector(Settings.barSelector), + perc = fromStart ? '-100' : toBarPerc(NProgress.status || 0), + parent = document.querySelector(Settings.parent), + spinner; + + css(bar, { + transition: 'all 0 linear', + transform: 'translate3d(' + perc + '%,0,0)' + }); + + if (!Settings.showSpinner) { + spinner = progress.querySelector(Settings.spinnerSelector); + spinner && removeElement(spinner); + } + + if (parent != document.body) { + addClass(parent, 'nprogress-custom-parent'); + } + + parent.appendChild(progress); + return progress; + }; + + /** + * Removes the element. Opposite of render(). + */ + + NProgress.remove = function() { + removeClass(document.documentElement, 'nprogress-busy'); + removeClass(document.querySelector(Settings.parent), 'nprogress-custom-parent'); + var progress = document.getElementById('nprogress'); + progress && removeElement(progress); + }; + + /** + * Checks if the progress bar is rendered. + */ + + NProgress.isRendered = function() { + return !!document.getElementById('nprogress'); + }; + + /** + * Determine which positioning CSS rule to use. + */ + + NProgress.getPositioningCSS = function() { + // Sniff on document.body.style + var bodyStyle = document.body.style; + + // Sniff prefixes + var vendorPrefix = ('WebkitTransform' in bodyStyle) ? 'Webkit' : + ('MozTransform' in bodyStyle) ? 'Moz' : + ('msTransform' in bodyStyle) ? 'ms' : + ('OTransform' in bodyStyle) ? 'O' : ''; + + if (vendorPrefix + 'Perspective' in bodyStyle) { + // Modern browsers with 3D support, e.g. Webkit, IE10 + return 'translate3d'; + } else if (vendorPrefix + 'Transform' in bodyStyle) { + // Browsers without 3D support, e.g. IE9 + return 'translate'; + } else { + // Browsers without translate() support, e.g. IE7-8 + return 'margin'; + } + }; + + /** + * Helpers + */ + + function clamp(n, min, max) { + if (n < min) return min; + if (n > max) return max; + return n; + } + + /** + * (Internal) converts a percentage (`0..1`) to a bar translateX + * percentage (`-100%..0%`). + */ + + function toBarPerc(n) { + return (-1 + n) * 100; + } + + + /** + * (Internal) returns the correct CSS for changing the bar's + * position given an n percentage, and speed and ease from Settings + */ + + function barPositionCSS(n, speed, ease) { + var barCSS; + + if (Settings.positionUsing === 'translate3d') { + barCSS = { transform: 'translate3d('+toBarPerc(n)+'%,0,0)' }; + } else if (Settings.positionUsing === 'translate') { + barCSS = { transform: 'translate('+toBarPerc(n)+'%,0)' }; + } else { + barCSS = { 'margin-left': toBarPerc(n)+'%' }; + } + + barCSS.transition = 'all '+speed+'ms '+ease; + + return barCSS; + } + + /** + * (Internal) Queues a function to be executed. + */ + + var queue = (function() { + var pending = []; + + function next() { + var fn = pending.shift(); + if (fn) { + fn(next); + } + } + + return function(fn) { + pending.push(fn); + if (pending.length == 1) next(); + }; + })(); + + /** + * (Internal) Applies css properties to an element, similar to the jQuery + * css method. + * + * While this helper does assist with vendor prefixed property names, it + * does not perform any manipulation of values prior to setting styles. + */ + + var css = (function() { + var cssPrefixes = [ 'Webkit', 'O', 'Moz', 'ms' ], + cssProps = {}; + + function camelCase(string) { + return string.replace(/^-ms-/, 'ms-').replace(/-([\da-z])/gi, function(match, letter) { + return letter.toUpperCase(); + }); + } + + function getVendorProp(name) { + var style = document.body.style; + if (name in style) return name; + + var i = cssPrefixes.length, + capName = name.charAt(0).toUpperCase() + name.slice(1), + vendorName; + while (i--) { + vendorName = cssPrefixes[i] + capName; + if (vendorName in style) return vendorName; + } + + return name; + } + + function getStyleProp(name) { + name = camelCase(name); + return cssProps[name] || (cssProps[name] = getVendorProp(name)); + } + + function applyCss(element, prop, value) { + prop = getStyleProp(prop); + element.style[prop] = value; + } + + return function(element, properties) { + var args = arguments, + prop, + value; + + if (args.length == 2) { + for (prop in properties) { + value = properties[prop]; + if (value !== undefined && properties.hasOwnProperty(prop)) applyCss(element, prop, value); + } + } else { + applyCss(element, args[1], args[2]); + } + } + })(); + + /** + * (Internal) Determines if an element or space separated list of class names contains a class name. + */ + + function hasClass(element, name) { + var list = typeof element == 'string' ? element : classList(element); + return list.indexOf(' ' + name + ' ') >= 0; + } + + /** + * (Internal) Adds a class to an element. + */ + + function addClass(element, name) { + var oldList = classList(element), + newList = oldList + name; + + if (hasClass(oldList, name)) return; + + // Trim the opening space. + element.className = newList.substring(1); + } + + /** + * (Internal) Removes a class from an element. + */ + + function removeClass(element, name) { + var oldList = classList(element), + newList; + + if (!hasClass(element, name)) return; + + // Replace the class name. + newList = oldList.replace(' ' + name + ' ', ' '); + + // Trim the opening and closing spaces. + element.className = newList.substring(1, newList.length - 1); + } + + /** + * (Internal) Gets a space separated list of the class names on the element. + * The list is wrapped with a single space on each end to facilitate finding + * matches within the list. + */ + + function classList(element) { + return (' ' + (element && element.className || '') + ' ').replace(/\s+/gi, ' '); + } + + /** + * (Internal) Removes an element from the DOM. + */ + + function removeElement(element) { + element && element.parentNode && element.parentNode.removeChild(element); + } + + return NProgress; +}); diff --git a/src/blog/static/pygments/default.css b/src/blog/static/pygments/default.css new file mode 100755 index 0000000..73e6e49 --- /dev/null +++ b/src/blog/static/pygments/default.css @@ -0,0 +1,293 @@ +.codehilite .hll { + background-color: #ffffcc +} + +.codehilite { + background: #ffffff; +} + +.codehilite .c { + color: #177500 +} + +/* Comment */ +.codehilite .err { + color: #000000 +} + +/* Error */ +.codehilite .k { + color: #A90D91 +} + +/* Keyword */ +.codehilite .l { + color: #1C01CE +} + +/* Literal */ +.codehilite .n { + color: #000000 +} + +/* Name */ +.codehilite .o { + color: #000000 +} + +/* Operator */ +.codehilite .ch { + color: #177500 +} + +/* Comment.Hashbang */ +.codehilite .cm { + color: #177500 +} + +/* Comment.Multiline */ +.codehilite .cp { + color: #633820 +} + +/* Comment.Preproc */ +.codehilite .cpf { + color: #177500 +} + +/* Comment.PreprocFile */ +.codehilite .c1 { + color: #177500 +} + +/* Comment.Single */ +.codehilite .cs { + color: #177500 +} + +/* Comment.Special */ +.codehilite .kc { + color: #A90D91 +} + +/* Keyword.Constant */ +.codehilite .kd { + color: #A90D91 +} + +/* Keyword.Declaration */ +.codehilite .kn { + color: #A90D91 +} + +/* Keyword.Namespace */ +.codehilite .kp { + color: #A90D91 +} + +/* Keyword.Pseudo */ +.codehilite .kr { + color: #A90D91 +} + +/* Keyword.Reserved */ +.codehilite .kt { + color: #A90D91 +} + +/* Keyword.Type */ +.codehilite .ld { + color: #1C01CE +} + +/* Literal.Date */ +.codehilite .m { + color: #1C01CE +} + +/* Literal.Number */ +.codehilite .s { + color: #C41A16 +} + +/* Literal.String */ +.codehilite .na { + color: #836C28 +} + +/* Name.Attribute */ +.codehilite .nb { + color: #A90D91 +} + +/* Name.Builtin */ +.codehilite .nc { + color: #3F6E75 +} + +/* Name.Class */ +.codehilite .no { + color: #000000 +} + +/* Name.Constant */ +.codehilite .nd { + color: #000000 +} + +/* Name.Decorator */ +.codehilite .ni { + color: #000000 +} + +/* Name.Entity */ +.codehilite .ne { + color: #000000 +} + +/* Name.Exception */ +.codehilite .nf { + color: #000000 +} + +/* Name.Function */ +.codehilite .nl { + color: #000000 +} + +/* Name.Label */ +.codehilite .nn { + color: #000000 +} + +/* Name.Namespace */ +.codehilite .nx { + color: #000000 +} + +/* Name.Other */ +.codehilite .py { + color: #000000 +} + +/* Name.Property */ +.codehilite .nt { + color: #000000 +} + +/* Name.Tag */ +.codehilite .nv { + color: #000000 +} + +/* Name.Variable */ +.codehilite .ow { + color: #000000 +} + +/* Operator.Word */ +.codehilite .mb { + color: #1C01CE +} + +/* Literal.Number.Bin */ +.codehilite .mf { + color: #1C01CE +} + +/* Literal.Number.Float */ +.codehilite .mh { + color: #1C01CE +} + +/* Literal.Number.Hex */ +.codehilite .mi { + color: #1C01CE +} + +/* Literal.Number.Integer */ +.codehilite .mo { + color: #1C01CE +} + +/* Literal.Number.Oct */ +.codehilite .sb { + color: #C41A16 +} + +/* Literal.String.Backtick */ +.codehilite .sc { + color: #2300CE +} + +/* Literal.String.Char */ +.codehilite .sd { + color: #C41A16 +} + +/* Literal.String.Doc */ +.codehilite .s2 { + color: #C41A16 +} + +/* Literal.String.Double */ +.codehilite .se { + color: #C41A16 +} + +/* Literal.String.Escape */ +.codehilite .sh { + color: #C41A16 +} + +/* Literal.String.Heredoc */ +.codehilite .si { + color: #C41A16 +} + +/* Literal.String.Interpol */ +.codehilite .sx { + color: #C41A16 +} + +/* Literal.String.Other */ +.codehilite .sr { + color: #C41A16 +} + +/* Literal.String.Regex */ +.codehilite .s1 { + color: #C41A16 +} + +/* Literal.String.Single */ +.codehilite .ss { + color: #C41A16 +} + +/* Literal.String.Symbol */ +.codehilite .bp { + color: #5B269A +} + +/* Name.Builtin.Pseudo */ +.codehilite .vc { + color: #000000 +} + +/* Name.Variable.Class */ +.codehilite .vg { + color: #000000 +} + +/* Name.Variable.Global */ +.codehilite .vi { + color: #000000 +} + +/* Name.Variable.Instance */ +.codehilite .il { + color: #1C01CE +} + +/* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/src/blog/templatetags/__init__.py b/src/blog/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/blog/templatetags/blog_tags.py b/src/blog/templatetags/blog_tags.py new file mode 100644 index 0000000..bede348 --- /dev/null +++ b/src/blog/templatetags/blog_tags.py @@ -0,0 +1,401 @@ +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): + html_content = CommonMarkdown.get_markdown(content) + + # 然后应用插件过滤器优化HTML + from djangoblog.plugin_manage import hooks + from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME + optimized_html = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, html_content) + + return mark_safe(optimized_html) + + +@register.simple_tag(takes_context=True) +def render_article_content(context, article, is_summary=False): + """ + 渲染文章内容,包含完整的上下文信息供插件使用 + + Args: + context: 模板上下文 + article: 文章对象 + is_summary: 是否为摘要模式(首页使用) + """ + if not article or not hasattr(article, 'body'): + return '' + + # 先转换Markdown为HTML + html_content = CommonMarkdown.get_markdown(article.body) + + # 如果是摘要模式,先截断内容再应用插件 + if is_summary: + # 截断HTML内容到合适的长度(约300字符) + from django.utils.html import strip_tags + from django.template.defaultfilters import truncatechars + + # 先去除HTML标签,截断纯文本,然后重新转换为HTML + plain_text = strip_tags(html_content) + truncated_text = truncatechars(plain_text, 300) + + # 重新转换截断后的文本为HTML(简化版,避免复杂的插件处理) + html_content = CommonMarkdown.get_markdown(truncated_text) + + # 然后应用插件过滤器,传递完整的上下文 + from djangoblog.plugin_manage import hooks + from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME + + # 获取request对象 + request = context.get('request') + + # 应用所有文章内容相关的插件 + # 注意:摘要模式下某些插件(如版权声明)可能不适用 + optimized_html = hooks.apply_filters( + ARTICLE_CONTENT_HOOK_NAME, + html_content, + article=article, + request=request, + context=context, + is_summary=is_summary # 传递摘要标志,插件可以据此调整行为 + ) + + return mark_safe(optimized_html) + + +@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/src/blog/tests.py b/src/blog/tests.py new file mode 100644 index 0000000..ee13505 --- /dev/null +++ b/src/blog/tests.py @@ -0,0 +1,232 @@ +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/src/blog/urls.py b/src/blog/urls.py new file mode 100644 index 0000000..adf2703 --- /dev/null +++ b/src/blog/urls.py @@ -0,0 +1,62 @@ +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/src/blog/views.py b/src/blog/views.py new file mode 100644 index 0000000..ace9e63 --- /dev/null +++ b/src/blog/views.py @@ -0,0 +1,375 @@ +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) + 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/src/codecov.yml b/src/codecov.yml new file mode 100644 index 0000000..2298829 --- /dev/null +++ b/src/codecov.yml @@ -0,0 +1,87 @@ +codecov: + require_ci_to_pass: yes + +coverage: + precision: 2 + round: down + range: "70...100" + + status: + project: + default: + target: auto + threshold: 1% + informational: true + patch: + default: + target: auto + threshold: 1% + informational: true + +parsers: + gcov: + branch_detection: + conditional: yes + loop: yes + method: no + macro: no + +comment: + layout: "reach,diff,flags,tree" + behavior: default + require_changes: no + +ignore: + # Django 相关 + - "*/migrations/*" + - "manage.py" + - "*/settings.py" + - "*/wsgi.py" + - "*/asgi.py" + + # 测试相关 + - "*/tests/*" + - "*/test_*.py" + - "*/*test*.py" + + # 静态文件和模板 + - "*/static/*" + - "*/templates/*" + - "*/collectedstatic/*" + + # 国际化文件 + - "*/locale/*" + - "**/*.po" + - "**/*.mo" + + # 文档和部署 + - "*/docs/*" + - "*/deploy/*" + - "README*.md" + - "LICENSE" + - "Dockerfile" + - "docker-compose*.yml" + - "*.yaml" + - "*.yml" + + # 开发环境 + - "*/venv/*" + - "*/__pycache__/*" + - "*.pyc" + - ".coverage" + - "coverage.xml" + + # 日志文件 + - "*/logs/*" + - "*.log" + + # 特定文件 + - "*/whoosh_cn_backend.py" # 搜索后端 + - "*/elasticsearch_backend.py" # 搜索后端 + - "*/MemcacheStorage.py" # 缓存存储 + - "*/robot.py" # 机器人相关 + + # 配置文件 + - "codecov.yml" + - ".coveragerc" + - "requirements*.txt" diff --git a/src/comments/__init__.py b/src/comments/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/comments/admin.py b/src/comments/admin.py new file mode 100644 index 0000000..dbde14f --- /dev/null +++ b/src/comments/admin.py @@ -0,0 +1,49 @@ +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] + raw_id_fields = ('author', 'article') + search_fields = ('body',) + + 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/src/comments/apps.py b/src/comments/apps.py new file mode 100644 index 0000000..ff01b77 --- /dev/null +++ b/src/comments/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class CommentsConfig(AppConfig): + name = 'comments' diff --git a/src/comments/forms.py b/src/comments/forms.py new file mode 100644 index 0000000..e83737d --- /dev/null +++ b/src/comments/forms.py @@ -0,0 +1,13 @@ +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/src/comments/migrations/0001_initial.py b/src/comments/migrations/0001_initial.py new file mode 100644 index 0000000..61d1e53 --- /dev/null +++ b/src/comments/migrations/0001_initial.py @@ -0,0 +1,38 @@ +# 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/src/comments/migrations/0002_alter_comment_is_enable.py b/src/comments/migrations/0002_alter_comment_is_enable.py new file mode 100644 index 0000000..17c44db --- /dev/null +++ b/src/comments/migrations/0002_alter_comment_is_enable.py @@ -0,0 +1,18 @@ +# 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/src/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py b/src/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py new file mode 100644 index 0000000..a1ca970 --- /dev/null +++ b/src/comments/migrations/0003_alter_comment_options_remove_comment_created_time_and_more.py @@ -0,0 +1,60 @@ +# 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/src/comments/migrations/__init__.py b/src/comments/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/comments/models.py b/src/comments/models.py new file mode 100644 index 0000000..7c3bbc8 --- /dev/null +++ b/src/comments/models.py @@ -0,0 +1,39 @@ +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/src/comments/templatetags/__init__.py b/src/comments/templatetags/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/comments/templatetags/comments_tags.py b/src/comments/templatetags/comments_tags.py new file mode 100644 index 0000000..fde02b4 --- /dev/null +++ b/src/comments/templatetags/comments_tags.py @@ -0,0 +1,30 @@ +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/src/comments/tests.py b/src/comments/tests.py new file mode 100644 index 0000000..2a7f55f --- /dev/null +++ b/src/comments/tests.py @@ -0,0 +1,109 @@ +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/src/comments/urls.py b/src/comments/urls.py new file mode 100644 index 0000000..7df3fab --- /dev/null +++ b/src/comments/urls.py @@ -0,0 +1,11 @@ +from django.urls import path + +from . import views + +app_name = "comments" +urlpatterns = [ + path( + 'article//postcomment', + views.CommentPostView.as_view(), + name='postcomment'), +] diff --git a/src/comments/utils.py b/src/comments/utils.py new file mode 100644 index 0000000..f01dba7 --- /dev/null +++ b/src/comments/utils.py @@ -0,0 +1,38 @@ +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/src/comments/views.py b/src/comments/views.py new file mode 100644 index 0000000..ad9b2b9 --- /dev/null +++ b/src/comments/views.py @@ -0,0 +1,63 @@ +# 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/src/deploy/docker-compose/docker-compose.es.yml b/src/deploy/docker-compose/docker-compose.es.yml new file mode 100644 index 0000000..83e35ff --- /dev/null +++ b/src/deploy/docker-compose/docker-compose.es.yml @@ -0,0 +1,48 @@ +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/src/deploy/docker-compose/docker-compose.yml b/src/deploy/docker-compose/docker-compose.yml new file mode 100644 index 0000000..9609af3 --- /dev/null +++ b/src/deploy/docker-compose/docker-compose.yml @@ -0,0 +1,60 @@ +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/src/deploy/entrypoint.sh b/src/deploy/entrypoint.sh new file mode 100644 index 0000000..2fb6491 --- /dev/null +++ b/src/deploy/entrypoint.sh @@ -0,0 +1,31 @@ +#!/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/src/deploy/k8s/configmap.yaml b/src/deploy/k8s/configmap.yaml new file mode 100644 index 0000000..835d4ad --- /dev/null +++ b/src/deploy/k8s/configmap.yaml @@ -0,0 +1,119 @@ +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/src/deploy/k8s/deployment.yaml b/src/deploy/k8s/deployment.yaml new file mode 100644 index 0000000..414fdcc --- /dev/null +++ b/src/deploy/k8s/deployment.yaml @@ -0,0 +1,274 @@ +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/src/deploy/k8s/gateway.yaml b/src/deploy/k8s/gateway.yaml new file mode 100644 index 0000000..a8de073 --- /dev/null +++ b/src/deploy/k8s/gateway.yaml @@ -0,0 +1,17 @@ +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/src/deploy/k8s/pv.yaml b/src/deploy/k8s/pv.yaml new file mode 100644 index 0000000..874b72f --- /dev/null +++ b/src/deploy/k8s/pv.yaml @@ -0,0 +1,94 @@ +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/src/deploy/k8s/pvc.yaml b/src/deploy/k8s/pvc.yaml new file mode 100644 index 0000000..ef238c5 --- /dev/null +++ b/src/deploy/k8s/pvc.yaml @@ -0,0 +1,60 @@ +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/src/deploy/k8s/service.yaml b/src/deploy/k8s/service.yaml new file mode 100644 index 0000000..4ef2931 --- /dev/null +++ b/src/deploy/k8s/service.yaml @@ -0,0 +1,80 @@ +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/src/deploy/k8s/storageclass.yaml b/src/deploy/k8s/storageclass.yaml new file mode 100644 index 0000000..5d5a14c --- /dev/null +++ b/src/deploy/k8s/storageclass.yaml @@ -0,0 +1,10 @@ +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/src/deploy/nginx.conf b/src/deploy/nginx.conf new file mode 100644 index 0000000..32161d8 --- /dev/null +++ b/src/deploy/nginx.conf @@ -0,0 +1,50 @@ +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/src/djangoblog/__init__.py b/src/djangoblog/__init__.py new file mode 100644 index 0000000..1e205f4 --- /dev/null +++ b/src/djangoblog/__init__.py @@ -0,0 +1 @@ +default_app_config = 'djangoblog.apps.DjangoblogAppConfig' diff --git a/src/djangoblog/admin_site.py b/src/djangoblog/admin_site.py new file mode 100644 index 0000000..f120405 --- /dev/null +++ b/src/djangoblog/admin_site.py @@ -0,0 +1,64 @@ +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/src/djangoblog/apps.py b/src/djangoblog/apps.py new file mode 100644 index 0000000..d29e318 --- /dev/null +++ b/src/djangoblog/apps.py @@ -0,0 +1,11 @@ +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/src/djangoblog/blog_signals.py b/src/djangoblog/blog_signals.py new file mode 100644 index 0000000..393f441 --- /dev/null +++ b/src/djangoblog/blog_signals.py @@ -0,0 +1,122 @@ +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/src/djangoblog/elasticsearch_backend.py b/src/djangoblog/elasticsearch_backend.py new file mode 100644 index 0000000..4afe498 --- /dev/null +++ b/src/djangoblog/elasticsearch_backend.py @@ -0,0 +1,183 @@ +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/src/djangoblog/feeds.py b/src/djangoblog/feeds.py new file mode 100644 index 0000000..8c4e851 --- /dev/null +++ b/src/djangoblog/feeds.py @@ -0,0 +1,40 @@ +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/src/djangoblog/logentryadmin.py b/src/djangoblog/logentryadmin.py new file mode 100644 index 0000000..2f6a535 --- /dev/null +++ b/src/djangoblog/logentryadmin.py @@ -0,0 +1,91 @@ +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/src/djangoblog/plugin_manage/base_plugin.py b/src/djangoblog/plugin_manage/base_plugin.py new file mode 100644 index 0000000..2b4be5c --- /dev/null +++ b/src/djangoblog/plugin_manage/base_plugin.py @@ -0,0 +1,41 @@ +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/src/djangoblog/plugin_manage/hook_constants.py b/src/djangoblog/plugin_manage/hook_constants.py new file mode 100644 index 0000000..6685b7c --- /dev/null +++ b/src/djangoblog/plugin_manage/hook_constants.py @@ -0,0 +1,7 @@ +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/src/djangoblog/plugin_manage/hooks.py b/src/djangoblog/plugin_manage/hooks.py new file mode 100644 index 0000000..d712540 --- /dev/null +++ b/src/djangoblog/plugin_manage/hooks.py @@ -0,0 +1,44 @@ +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/src/djangoblog/plugin_manage/loader.py b/src/djangoblog/plugin_manage/loader.py new file mode 100644 index 0000000..12e824b --- /dev/null +++ b/src/djangoblog/plugin_manage/loader.py @@ -0,0 +1,19 @@ +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/src/djangoblog/settings.py b/src/djangoblog/settings.py new file mode 100644 index 0000000..6f071ce --- /dev/null +++ b/src/djangoblog/settings.py @@ -0,0 +1,360 @@ +""" +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 'root', + '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' + +# 安全头部配置 - 防XSS和其他攻击 +SECURE_BROWSER_XSS_FILTER = True +SECURE_CONTENT_TYPE_NOSNIFF = True +SECURE_REFERRER_POLICY = 'strict-origin-when-cross-origin' + +# 内容安全策略 (CSP) - 防XSS攻击 +CSP_DEFAULT_SRC = ["'self'"] +CSP_SCRIPT_SRC = ["'self'", "'unsafe-inline'", "cdn.mathjax.org", "*.googleapis.com"] +CSP_STYLE_SRC = ["'self'", "'unsafe-inline'", "*.googleapis.com", "*.gstatic.com"] +CSP_IMG_SRC = ["'self'", "data:", "*.lylinux.net", "*.gravatar.com", "*.githubusercontent.com"] +CSP_FONT_SRC = ["'self'", "*.googleapis.com", "*.gstatic.com"] +CSP_CONNECT_SRC = ["'self'"] +CSP_FRAME_SRC = ["'none'"] +CSP_OBJECT_SRC = ["'none'"] + +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', + 'image_lazy_loading', +] + diff --git a/src/djangoblog/sitemap.py b/src/djangoblog/sitemap.py new file mode 100644 index 0000000..8b7d446 --- /dev/null +++ b/src/djangoblog/sitemap.py @@ -0,0 +1,59 @@ +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/src/djangoblog/spider_notify.py b/src/djangoblog/spider_notify.py new file mode 100644 index 0000000..7b909e9 --- /dev/null +++ b/src/djangoblog/spider_notify.py @@ -0,0 +1,21 @@ +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/src/djangoblog/tests.py b/src/djangoblog/tests.py new file mode 100644 index 0000000..01237d9 --- /dev/null +++ b/src/djangoblog/tests.py @@ -0,0 +1,32 @@ +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/src/djangoblog/urls.py b/src/djangoblog/urls.py new file mode 100644 index 0000000..4aae58a --- /dev/null +++ b/src/djangoblog/urls.py @@ -0,0 +1,64 @@ +"""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/src/djangoblog/utils.py b/src/djangoblog/utils.py new file mode 100644 index 0000000..91d2b91 --- /dev/null +++ b/src/djangoblog/utils.py @@ -0,0 +1,272 @@ +#!/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', 'span', 'div'] + +# 安全的class值白名单 - 只允许代码高亮相关的class +ALLOWED_CLASSES = [ + 'codehilite', 'highlight', 'hll', 'c', 'err', 'k', 'l', 'n', 'o', 'p', 'cm', 'cp', 'c1', 'cs', + 'gd', 'ge', 'gr', 'gh', 'gi', 'go', 'gp', 'gs', 'gu', 'gt', 'kc', 'kd', 'kn', 'kp', 'kr', 'kt', + 'ld', 'm', 'mf', 'mh', 'mi', 'mo', 'na', 'nb', 'nc', 'no', 'nd', 'ni', 'ne', 'nf', 'nl', 'nn', + 'nt', 'nv', 'ow', 'w', 'mb', 'mh', 'mi', 'mo', 'sb', 'sc', 'sd', 'se', 'sh', 'si', 'sx', 's2', + 's1', 'ss', 'bp', 'vc', 'vg', 'vi', 'il' +] + +def class_filter(tag, name, value): + """自定义class属性过滤器""" + if name == 'class': + # 只允许预定义的安全class值 + allowed_classes = [cls for cls in value.split() if cls in ALLOWED_CLASSES] + return ' '.join(allowed_classes) if allowed_classes else False + return value + +# 安全的属性白名单 +ALLOWED_ATTRIBUTES = { + 'a': ['href', 'title'], + 'abbr': ['title'], + 'acronym': ['title'], + 'span': class_filter, + 'div': class_filter, + 'pre': class_filter, + 'code': class_filter +} + +# 安全的协议白名单 - 防止javascript:等危险协议 +ALLOWED_PROTOCOLS = ['http', 'https', 'mailto'] + +def sanitize_html(html): + """ + 安全的HTML清理函数 + 使用bleach库进行白名单过滤,防止XSS攻击 + """ + return bleach.clean( + html, + tags=ALLOWED_TAGS, + attributes=ALLOWED_ATTRIBUTES, + protocols=ALLOWED_PROTOCOLS, # 限制允许的协议 + strip=True, # 移除不允许的标签而不是转义 + strip_comments=True # 移除HTML注释 + ) diff --git a/src/djangoblog/whoosh_cn_backend.py b/src/djangoblog/whoosh_cn_backend.py new file mode 100644 index 0000000..04e3f7f --- /dev/null +++ b/src/djangoblog/whoosh_cn_backend.py @@ -0,0 +1,1044 @@ +# 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/src/djangoblog/wsgi.py b/src/djangoblog/wsgi.py new file mode 100644 index 0000000..2295efd --- /dev/null +++ b/src/djangoblog/wsgi.py @@ -0,0 +1,16 @@ +""" +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/src/docs/README-en.md b/src/docs/README-en.md new file mode 100644 index 0000000..37ea069 --- /dev/null +++ b/src/docs/README-en.md @@ -0,0 +1,158 @@ +# 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/src/docs/config-en.md b/src/docs/config-en.md new file mode 100644 index 0000000..b877efb --- /dev/null +++ b/src/docs/config-en.md @@ -0,0 +1,64 @@ +# Introduction to main features settings + +## Cache: +Cache using `memcache` for default. If you don't have `memcache` environment, you can remove the `default` setting in `CACHES` and change `locmemcache` to `default`. +```python +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache', + 'LOCATION': '127.0.0.1:11211', + 'KEY_PREFIX': 'django_test' if TESTING else 'djangoblog', + 'TIMEOUT': 60 * 60 * 10 + }, + 'locmemcache': { + 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', + 'TIMEOUT': 10800, + 'LOCATION': 'unique-snowflake', + } +} +``` + +## OAuth Login: +QQ, Weibo, Google, GitHub and Facebook are now supported for OAuth login. Fetch OAuth login permissions from the corresponding open platform, and save them with `appkey`, `appsecret` and callback address in **Backend->OAuth** configuration. + +### Callback address examples: +QQ: http://your-domain-name/oauth/authorize?type=qq +Weibo: http://your-domain-name/oauth/authorize?type=weibo +type is in the type field of `oauthmanager`. + +## owntracks: +owntracks is a location tracking application. It will send your locaiton to the server by timing.Simple support owntracks features. Just install owntracks app and set api address as `your-domain-name/owntracks/logtracks`. Visit `your-domain-name/owntracks/show_dates` and you will see the date with latitude and langitude, click it and see the motion track. The map is drawn by AMap. + +## Email feature: +Same as before, Configure your own error msg recvie email information with`ADMINS = [('liangliang', 'liangliangyy@gmail.com')]` in `settings.py`. And modify: +```python +EMAIL_HOST = 'smtp.zoho.com' +EMAIL_PORT = 587 +EMAIL_HOST_USER = os.environ.get('DJANGO_EMAIL_USER') +EMAIL_HOST_PASSWORD = os.environ.get('DJANGO_EMAIL_PASSWORD') +DEFAULT_FROM_EMAIL = EMAIL_HOST_USER +SERVER_EMAIL = os.environ.get('DJANGO_EMAIL_USER') +``` +with your email account information. + +## WeChat Official Account +Simple wechat official account features integrated. Set token as `your-domain-name/robot` in wechat backend. Default token is `lylinux`, you can change it to your own in `servermanager/robot.py`. Add a new command in `Backend->Servermanager->command`, in this way, you can manage the system through wechat official account. + +## Introduction to website configuration +You can add website configuration in **Backend->BLOG->WebSiteConfiguration**. Such as: keywords, description, Google Ad, website stats code, case number, etc. +OAuth user avatar path is saved in *StaticFileSavedAddress*. Please input absolute path, code directory for default. + +## Source code highlighting +If the code block in your article didn't show hightlight, please write the code blocks as following: + +![](https://resource.lylinux.net/image/codelang.png) + +That is, you should add the corresponding language name before the code block. + +## Update +If you get errors as following while executing database migrations: +```python +django.db.migrations.exceptions.MigrationSchemaMissing: Unable to create the django_migrations table ((1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '(6) NOT NULL)' at line 1")) +``` +This problem may cause by the mysql version under 5.6, a new version( >= 5.6 ) mysql is needed. + diff --git a/src/docs/config.md b/src/docs/config.md new file mode 100644 index 0000000..24673a3 --- /dev/null +++ b/src/docs/config.md @@ -0,0 +1,58 @@ +# 主要功能配置介绍: + +## 缓存: +缓存默认使用`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/src/docs/docker-en.md b/src/docs/docker-en.md new file mode 100644 index 0000000..8d5d59e --- /dev/null +++ b/src/docs/docker-en.md @@ -0,0 +1,114 @@ +# 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/src/docs/docker.md b/src/docs/docker.md new file mode 100644 index 0000000..e7c255a --- /dev/null +++ b/src/docs/docker.md @@ -0,0 +1,114 @@ +# 使用 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/src/docs/es.md b/src/docs/es.md new file mode 100644 index 0000000..97226c5 --- /dev/null +++ b/src/docs/es.md @@ -0,0 +1,28 @@ +# 集成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/src/docs/imgs/alipay.jpg b/src/docs/imgs/alipay.jpg new file mode 100644 index 0000000000000000000000000000000000000000..424d70a2ffbb629b481e0c27d72d6076727e8041 GIT binary patch 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 literal 0 HcmV?d00001 diff --git a/src/docs/imgs/pycharm_logo.png b/src/docs/imgs/pycharm_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..7f2a4b0ea66469bd218774de8cb3027a9c18b84d GIT binary patch literal 132045 zcmZTw2|U#4|DU$qY=^9^R*2bBDb|r>=Jvqn&-a$U8~l#LJ&jyHfAI(S>t8P)K52l%`RvBw08PF#ZVCPOerZ-T{F^v^o!^bU+DE!L z0m)bXT*H)y=^3 z$a;HC%g)N@X_wZwuj+_)rQ{FZbv>!$q^EP&B_JU1(w`32c}ac6W;C{jW_0vdjuV4q zzz>^PEu>|?gD}v^9cwc;@bI*IRO3D$y|f31i|s96XOP7(w&SKQ{S5yi4mY>2tgtuT zA~$UxU0v1Ctzo0@bJaVE(b?w+3tmgHI9zpy$$FvkubGSQOUl|AFJ1?r^>Klc4;mVL z928pj$>&I)@BNS1jOl1i(j{s7iYU8qUZJ2;P$B7Y7d_vjM|)k zL7SL|13NY6W=77^zDJ)n4zm2(ACcWq&#~B{s;c@RjT0^1;@RMzw)6#u%eee5IWF1j zazJn4j4Fjq2&RqM*i4xtAHzMc$~MSrcsM?GFPS+u>zZ03v0=h%@sFyJk&GJ#Q- ze#E`{z?3pmTZD~14w0vXfU)uxZ>>U~+ai&i>}~U`r1znYr=ByTf)g6XOeyo7dFVWm zl*JMH4!4zPE^~f9N_0+iT;=VAT4xKx%iPvVtgn0<`uOoOtp^5f4gOci>_?|=j79Km zBqj+g1uj~Z>wi`0?!#}uU0}LT6Batc7sA=ypBYH2WQ7vv3adItmL9B4`2@H8>hB-nmfv5xJX{t$rH!Xc z`ZopWW1r1;cMs2PsPnglkNRHQpAh#}pLLr_nF{Ap+Ojx&YjDa6*e!1n6~f!8XK1dpL*Fft>(IsO9d^wlCNU|T8XfkdZ?g}t>MXhLAh3H4 z(^liQoi~~IG69j~pdK~hhu7v_Lx)%#=6!m{Q)^vqvcqZLRNqhI6tyIino#1vjfFi= zt!QhkI5F z44m>@M<%g%yW}O>XbtZ%hb$N1d3ovWl;eowOV1~jQqj$u@ z;Zqd+A^QeTmF`c)yXln6#V3=`7?`wLnH%6V53EB$eW_U}#6vmYQPJY9)#$6Q+4wnD zKE2UdyL7Do>SIQ$m4gnxF|aJ_+@_+4f!U$&a1Zqdf^S8XJymwrZ~YcFkdjkA!MB*h z`)ulVg7lpab;Y~m6Wv==HVmv`t%_u_TP+9t`jhyR_2z6=Fvz8{&H^1sx|2XYuPCSkD_3FH*0>PH88SE;-y%^5iPFH6axOrBt_nTOSiyh)J z@{`)1=S;-;Mh;Xcrfa;ktNfvoBZSt#t=Z{57Z$yVb4I4B^HCjG@EiN;4-#CbFs7^c zF}DWJm+R+;_mzpP3OwRs^pt}y=86`MI;!D^1Q#;PZfu&D#uf?LnVig-H+ImqR3J=s zR|*X1vM!1@;`gemo}(r&Tr!WxY3_QFtvr!ox?tc59*?iyUYeskF}k=5eLQd>;kK>% z?pW${w}nH0!;D6PXrrE;&NwAcm9Cy8^ZauBu{$dry457xA+M z*WlgzcZ-Lxuh+YNJaL3FT?}7t00szILrfB->$$-OrktMn&aj%5+$ewF62|RWFju#= z0sotCy0c;6ntHnDsLlM10AaMq1D9T3Rl_454fpQV*42u&Aus4zMJAmaR;rt`G;hOc zW)F2b(do8a(YTHMUc@~T+}!u(bFC;-21><#FreNZp&TT{Cwe#hy%F5t=dE;Y`0h@W z?vF zd&8MG#ty1(;zR^Y#|q$L{u&Nwb{R@oe9vzi?4+jAw0$AR^O#yk`DV0$@e43s-ln2> zDyjeK$dUf5*Yy&F!a#d7An81=%3r^5>G1qVy7v z1B)dAGH8(pI@?<^r}mpif2jK-xwg*h+js@1TbM3APi6+l5wbMmsjd`9$l5O+k2(6Z z!A=-NXEiyx;DYPoJM{Vcf2m0jJda5crh2nG!wlxc>-d?ceAX?{ubQ8WOZ2}vz8QBx z`BB?P*4+aO;Q?I?FXDL#Zpy|%NJx*r(XcSy@@v=rsaj(P?js9__f#ktqVybJ9qQCD zcYb7Ppkxhh>yezfL}FQHxikCq`>CH*!~Jgs97JEf$HewroL&~c-Z%z!U(d6_x|* zKY}dMso_;ow5s$WDMjNf*`dDmWKmJWWcV7~wp`={c~?z(m$nUh5t~ekJzLs zf)iQxOttOtRPT!rgaxnhb>U21R^{}Z4!cu81pk{xUI2d;JS`Oz4>s-fMM?7kjQ9>2=3ugtSY3GJ99Qw zPTdi23bD|UQ(DLia7KICYM0_}EJau_sG4Uvf@k5ylX^M>$2N0+jAV9UZfR=MJd)=Z zy;{?KmfK|#W@>6T-~H$NsUs@kWtpE@`<8~#GV+IiPRvw05TEFwuRtjEbkcK`C>AWe zuFD!L3II?vXI$6*Db25SwPxQ?yYoceX*QwTV^6R5yD;Xv8^XLI<8(c$$Xf15f2NFK zf98U_RS34k&NVFcg=Y@UBLkf8ae>v4m-=cyE*Pl73PFEvU|tEx@O?U1zA%ic+^dUQ z86D1c3WVu}s?HYos?Pjxvti8c5VHdk+|@TaUp7$lQl_k^>u|?~i@B0ze(nLy|B-}OG3QqR`@}43{T4A_>0cmuyEjhge6JJ2rnhn8qCow9NNbgpkTR4 z_)JGIZ*+EsL=_d83&V~d3Gn0gXymLnpq@9iYcDPSyji^U*A0Vxim8lm!}@#8F$(fT zfWM=i67|8r7R)oVtsU|{A=PB`)R^x9Nt3w5sgu-SamU5A9%PrMGaibsWZW z{NCU+2RWFhG&ANGv_%?nn2Ox+lHK)_ACYx6KR)bGBcGQdOy2_QnhT6!F(gXIkM`(z z7*=MBc92*%R^u{yij$)$WbLPe1)?_%dV>Gl$c!bXj@xi^X+NQTJUsg2^K830+vuWf z0=TWXNO4bRD5OJfPUn)D!`O_4!(#a??z_M{?h9h!mR-l~+KVjJ@Ku7PIy5)NP!5(( zA39wnDJsQNeVi2t&sF0SMHajBFF+u=^%R_|hX_7m=)f_8I#*cIyhFqgK*{)GV*#Z9ZP7a!$>$*;jL2mJEkb$5s ztIFDh+dvh)S?5@u^4X2?=X-!q6x+^o^{g$(do{0|>F_ar=G(3cXVU0qV;2l*!3)yD zSW(T-iT&yy`F_D|cr3cEZj<94C3U|&y_}1Y%qwc?8qS*Lc{zhJWj4q(4}Q!(-+!iR z%$j->JOz(H3L&+mn*2Q}@Q{1dtDxHhD^Z=vEtB?FH{gc`gP1Qc;wx0x1wx(r5ntPDVXrylE$ z8f1llFW;GC)Fvj`h)_Vi_raiaPdv4F;)5=hZF61Zf&|q8I1(TTJUMQw110jF6%M8k zuQ$-x8aDCr2+eKj4s8e8Nwu!F+Gv=u!%(e-!w{a&Q5hx5aON(Qui2fV&28~n-2Byn zf0j55q+8NtmAZ-ddcC)p4VtlH(G3GTHcc}snkme1F)G(kOhc7sOp5)pNF%VbF~{g4 z@1+x)v>pp+;*=G0JPo;(;A*OX*J+v3UFF`Dha_ijBFb_9YuI>czi{@8g=%B}v*uD7 z2ht}i_EALXr2^y^dTv>b0*!cOA=AUpI?do#hn{*$y<1QSJ9Qf#xF*p5=L}Zw3 zLC4A1e!bn;zr7}kGc$aWGc#wznR#NVM!1iK&>KN@0y}cL$Q$n=B8Hn84Zbwh0oU9` zIN$4i>inRYonIF1H?)dwi?!%k(&2suynbKD()b;^-*~17SVfmLM7$yXbJx^j*7nx; zZ`NyZ0fi4gw|J4C4)zS+GU(w7Zf)cnSMTJJVT&Hm>dqU{rBOk=GJHn z8;YDK4v>xxbjXqVItuYztFk4*u0Rw$rNF9uH?>|B0i}?SuRF#8Iz-Iu|=n`&Bbul4mym161t>=nXp$`t@JSo^Lr9>4%l%aON7;Y7cV-)q%-` z+*~@5yy}4t<|n7#WtROJG4N42Rp8U@vSO!+qkes8BTjEmPQ(~XF}m;87BbXlE0DvP ztsB>dv*=(9(baYb!SXNCC7e{%G+BmLT=tnYS0UD5I_U4^zD0DY!UT^t6C#-(apav5 z<6iai0Et+~052$p5PtVngB)};{topIAbX5iHncT*=L=Zks=FRuYF5pKl5Q{m-qoa7 z;K`-SIW$eQ&YGQ1t*ecUIN8zBoSE(((YjDIeeP+Y#_J6$R`>kWcR8PYUcV9`JM4${pTH z?Zn00`qZd0#J%LIXk1Yw-6b%wj%^xV&ieQxEb+?U;KOtA=&+6@ILi{}Lu5bHz+!fa zD4p-PheH8Tb?zOpYSkWhluAx-Ni=~C1*xC<5^A>Fq9Fx_$&)Zsv+>HtN| zHsE~zpCx<_do)meuQo(I^;f{jCxN4sxt@AD=(t*}Az2syNpNAec{bX|mfSK)6`5FD zM}xkO@dqz^I`_gYp(Oyv+5#UWh@=2ponc1@&hqKzH&tJ(lFgL7>~BM}N;8#$XblyO_vJx+0NslH@%RX!Mb zZH+anyde0~i82>tTB*13>h#aX#HlJ{e;ZHtuzp?Eq!^u_X_yk%2ohB*wYGkapm7Md z^%!H&Ud7|e(?RZQade(`!G)X!1NSl?v`Y1P1<{4e3avy{a6DJ{jRV~wyv*SLR+6s0 zgW!0`)L~+;|3jQp`aeFy-{&=^$Zb47*!__)D_8axwoapMYk_!dSXPXncDl(3FJcp0obkt?7YgjaZM=FarSAYN6Y$3wyLG+~-wKw# zB&Fe-LKh9p>(FAmhLH4}**g=N@nyzb_-&~`7k1lZ9bMLKFfGm4VJvUn_`3@Mr>6;J zzF^+P(n{d^MHF4?=U!5(hLn%xOdxZ?X+GJ{C zXLsfd*O%cpoH;oa&h1bthruTqY0soPZaSJyb$Sg$_h|<%6E#iy!sp4n)c84#H zI*Y0f%{P8`|3H33aqj2O{`Y%2tyw3_o5&j4J}HB4<~`UN7JlOg8V$peQn1*xRN)o(3hvm^RK*67w|;=;dE6489_l*35kOo-ilH2TN=<1WmzZRL-Ek z!z3Mwi0F$Io0N`#O+M7m{VqkJPHOYtFaE7#-{9BScbU&a_ESu_q5kBcFeYJhVgfk> z!MF|AiodXW0dxptO^u3fXg7pBhidc5ZS(>D{PLY80|t*kgq)9A{lHIpE^+AeFNx3e z6euN4r7W78xshE$Z;lu&bK%Eb1+`X(-;a($M2~N1L;iv;T$;I=dpV0fu#_%K@O(-2 z!o?1AK^}uuZeBZpR=G|}6A~>SkQPUaJPG=AqROxTB{)n6tW)vc7E8Pcv3~=&F;1KA za+Pd#a}S@<{^|a+^X7Kk0XUe(h8Zu&mr-JL&)J8Vadx^F4WdL>bF!#b>GE@S3NKlj zw1MZzf%?^&q2x~KliZ%$Fk3u^vyN0#~g2T&XZan*{fHR4hK#?e^zeYRDG$r0+1FQD_@tDYfezF^n_J# zuW%WNK-ZHNG^H5+w@$Q!)6lvF$rn~bSuuIHmsM^)T$X~7#v5yoo1N7jelW~U%rqs0 zA*j1cF;yt>&5&uG<5V(Q%_+2rlU-#jyMI@FL#KVHQfgd3ARzGPy{srya9S$5yPx!;`Bx4*=Q zbIfa}cK(XBMCk%%&wRVzTlm+v`QQI8CijZ!?qomHr`DtH@A{vx1Bp@E#nN4RS9O3M zj)c|q_;`r_^9$H(jS3M z&z_ZMj}>2Xow+mNN6Ofj3N?LRg3JFdb7|TXUBZ(%H_wjsQ5>s_Qq&^PrInJN>WhbJ z8gi;kmN1av-~L@p@Rp$d0W%XjS)#uhW9fETlQqjmIlQdK6DbhOJn&sSJHs_TQNSL< zODN*98pD|(U1Qvl&TRMLO#HZ{{HqT|xypmjTeqBBe6cj~GT$(b<|0D=3>OL-Q?DvI z=Yu1?1YLLV)EY={^%bJ^{6O1z;SmV`7OuL?GV z9+JChiZ{0>5&*5uL7+a3Zn5KA(IS_2x!7+y$*Fw_HIR~m4x|fcWfe-Z++SmoOD=$x zukY`(dP6SqCz`Y(?cgLT>al968cOLx3AxjzD4Q6^xGK4cC)Z!;POi3G^kgDk1tBM_ zfN<}heo#|Kj;8kGu3c`6(3dnfv0G%k7KPkK`%Aj3b*>&=x0Dui9NK`qtk3ql+WHMq zIdn(70wkYU{@YXQZleV^K4m|`NO8LVm8aRhR_|T2=+d)_Tt_k+R>1X1UvGEwAF@5k zu`*DgdOKOH@B|)NzVD$&cIZ9_^?mD$*mv&-9`8|N)3!EEbcUK`OZb>LPwp+d1*-%; zQ`p|BvOD$`@lLg+Qf$2y1X*m!sxDBQz_Q%ZVTAUlixzkGowUs z;r^C{#RgB*evwZ#>}d4SQlWCJAm~dKK)B~IBxJCV%R1DhO0K~aaYRw&c=+jQ@?>`F zmc#&;7lOItK z#9N653F%BDH6q?5uEJ`QdR%rvCu@m<2Q0g9sRq&9jL*V)I3GHV#gwB6Iu+U zg}%Cdcpx?4NZ z;XJdwRd;dxwDH^94mxrl1_y1+_k3yogCCWO8PXupJnGqT}^oz0Yh%!+|u%jLG;A{@yi+ zqhsm=AvLZOq@BDeM}x2Z$G+WKe_ZzN=!?H!z_tkDR>)A+tnJ&HmOc%gg0TwRE6tX^ zOlnkE+EL&wr!T({+wtRPNL+6#R zO80Ach+C8{?Vuwt-NtF-jupvGoD-05I?W<1);x!NB{qmWG>gt>t5Xi@TC?YFSm?OX zCM|mvd?^vKm8TT7&AwA9wPu-LnN3J=$BM^(u+J8&iI}?ell;%8H=kSVg-xx-5M5U& zajQ0W=u&SX*%MBvx#egVvG=Pnu{o+*FEJc|p)ghrK*@c`vzCsBKS`nF+ZKQ6?}Cs1 zc;Gq2jV%6V4-}VNH~e+M(pJyCuqERZH`3gcQPD9=NC>Tg_BwH3a7V#vgdfa0er?ws5KT zg1u%OBt3wdD8*1a|9b4+#-UB0+a$jk^DM~iiNUTs&#Dk^GJ%dyg(uRtjGq+OHCOJM zqQ^n`r15{$bvwAs%)^;{TN~Mm>_PRT@3}JTw*GeORy65MH)B2z+~JAe_iIoFA!|zb z1T>d(a({q|a|F!(Owz|xC9Vhnl$u2nn0=*~uOj~{;bU6hYPqB2?`i++?;={hsP5h! zCzLSd`+n-qHacYEBW!IE`kbibTs>NBoiipEHyk_GxBuYf{1wl_M430y%+x@u;PUtr_2^ot`phU86m%Xue%c8TJfoBSk`5@b9 z3aeOZ56}nCvvQ4zc{6BkMmQ~;>GDtR;=7{BaD6;?v$*q%(fluKw$)BvYMzS|O2jM2 zAjkpSM3io_C6Vg~lT3)J_S5`5r0V^Mv5_95zL?~C1ye?V5d{*NS&+XhO+6Mn7IfE) zApfelRcErnLK#b@8Oo`iTO{%0s)!;UnbSA6SBdj>m%=|b>`&tpfHo;04+Zdtqqd+S zK^WPfF#5rk;@;6@IlGJha~d>HSg^ej31MK+V6|$H*Ba-~Gd?1#{RJLYneJkbC-tR; zhd}EAINpEE_9Tqy+3ML4;ArsUbAQ`wL!VA1`OSmjTGw1g^{|Wuln>xK+H zn~k_p1rF==0HG`(`G0A1e`fT+Lh@RMjL(a;7P3?E#>64f1Zc!LU}1@|kx=4qpygNy z`T2IfKjiLTajm-WQ9#prxX1CWLr9zguQ&>`1diPJaTl~LV6?k?XX3JYt*%JnyEO6< z&<~WwGCs-}tR7MNaNhbw<5u;h%Z6#%eP@ev2F+o)CAIUtpy7pFJ#0Rx$o+E-OO>1j zj`2z!GQsd#aM+8moRS{~jAd7#q#*eZKGt!n=}$w&Li6(ObMr9_T*|ZGWD4d7;0CVM z+^oCU_TvTs6P_7!PT@>x-$cVnF#*`7_E%PC%*5b%MVZ)0e+?J3$xvCW%8iksT=s}z zn>@+rI`|Zc(W2X93V*V(iQ96HSZb-vP?dlhCp~e5eTrN+Fk25~HSl-xo0bE`+v}64 zH`B)Nz6X>^-~~M9Pbq5dL7tIjr|l?tG}ORwGbskTl#ZkYa@icz z-wQv5-J*G)m11}tlET}|l_EFcB{m)e4-IDqJZ9wo9fbs9|I61W>B#At!09$BLY4$x z+9Hne{v_?HF@B z;qK_Yn6szt#)O$(fN4ZZ|1wNt48g1>)ZP(i+PJ&l694;v<#e|DJTrXTBAK4& zxo*&^#o@kvf5X6tk`UG*tKXm2T`hv0cBLfLT-Kq@LU^^`>-_9pz#v}y`rEMM;w$8G z*A7xldPBz)efrK1`d8%ko6K6kYJqyYT#d|P>h*2`V5G&W|C5nWtd4E=se+pJ(NdKr zj{33;-HWquxRU&UrdaKLNhM8yf}Rn>q2g?(qAsNI^NCWi%H6JX=;ThAJvz0Dxpz(o{4-Vf-@K-kzl2m)Z|0YG2GsK9_da!G8{fhy{UmK6KG( zN6C|3;i96;^GfUW9O(7$SxsekDeAqP?(ny_F?e*;OQ)Ow+CCK(3gh(P7o*GXs`y%`^wmQn37r?lN51CD7>W-> zLz)V_>5Qgr0sWO{P0~plz98QHa#`O5gi`={{k(fuaxxmDn(UC>P#dMyEDGlxJJ{&u z|M&LNHdJ17#*eTU`&&6I*xmh+k8GW+3>SOzV|{7nqOVc3t@vMvC)6>4iM=fKEyF-K zzY@%q6_0Yy5?|yuPX3+3ixJiE#m|BMcSy)%s*Rv+?Nx0X?d*103a@T43z!lEB%;tv z1i|Sh433i7e_g?5W#cY>W>EE^z^;>_3NxL+ZYVn~9&Tk545sxF7Cdt>Pf{eYR!=Bv z@sR(Z8n?i2gR&g4ttW8=qFQKwRjER)C>#>IC63fP#0fPrV zyIrfhbg$mBKYfx;Yq4Fd!zB{3Ks~u$<49 z2jQg3n$%}tcmStauJ#o{k%xH`r15x2eBtW`cXS z^I&t_FK0gq%!|8iO7H5WlpJH4aB z+eoE^XFOX z00yDJuC$Q=cj4xMn+IXsT&~slT864*a%p;_%RrRY(LWS6tj0otmLT1=KHQPD7L~-% zgYK8-ZVW2Bo{)<0zw!v7OH^R7(jTjoDliP=nT{zxQbATwpwZkcjV1@ z^4;NMvxkyy&bmsqw8+XFsmRrtpXp%`K2tt0n_KuQol{XBIiq5AR77f1aP2F^N z^Vu?gWdu(UI>dhpEh~Xxl`s-&o31&8NH@QFe5B%A`cg*ko{_3=cK_PYv=645p#Gq} z?tZrZT#|0qdAAks!&f2JhUg?8bkzd%de0F5ge8@C+>kL6X>!g~7t<^604xk-`3eTf zAk{&#_qh=c1+wH@f3uS8!Dh#am}eQ}Xi?Z?!3Gd26fuXN#LbUsFmIN|2kD`Dldp^-+M8JdX2;|uDkF8O+dbAbouz@;B=zCH_pw|59ILc>cWA;(M6vhp`}pim)U-O(=bph1QHcVwW)4VoXekiAa;t zR$pcZytI$mlPJM`)8*c^zT>3oU9kb$nl&9w(CWDCoYHr9VY$LV(^xf?Hst76NRon7 zCKcV?IrEp>Hko6Ubn}q^I;9PfiP@RnYJ$CZPc5u zLUMz)oVtIC9Z?I=vvUy%xP0I`r7{A{A9j=_MWiues{g++&l{$UXUmp2*_wLLZ5M)G z_xy1(iPirOTytCfN<@Z`b(V@qGO!$?^V+g1ge`jJ29{CNjHwd9Oi%H z8?&Rk4y{IZB&Wfn?6vu?+YSTWy>&~5iVs6_ieVaXg_qjghh-$<$1ITjHHB5x8H)h6 z_RERsv16K7VjFQTUdK$JJCyinHJ9>BmV>#+v+~1Ush=8sTE3Ls4T527gk(_fIg$xY z{-v+cP!sOg@;dvZ{w{Dy{i29vi1{zMubJJC6gp+cx?zhDUmNrGeK@zCuh5DIQ539&))wnh-kb zevUhwY5AM1B0~j8A*h_-Hv(*%wO~3Ml2_&;eZxLf%E;T+ypm1Zr_cDHBOwI;4s0m{?h>2r2 z)}gVvRe++9(8p`A#ZC}elB^Ktu3H*kf#AUFAP?B+dOlHpowRcTLy2KlK$WuI4npj2MN(*l+|x&3pE_y7^|gy(zv^Tq=Lsgs{X&PJYXX?t8|k*w_AF8Ndcl7YZsK zJEU=}i>T2@%b463Fnp!qys;JM-5rFoF(-Of)U=R8d5r`UuxN5#A`h3a)Sn=vt<;zno zS->T-nw;~SzhOe@e@*9$=l-tCIv!`riUxsgVlhFgZDOAK%a*faA~z>GQNRErODCl< zT89FhpuQ^m4c5^$GI|Sao1IyN%gYLN((+^$Lbmw7Ll;}Q2J7Xr(Fiv!r-@$V+Egy{ zTYhJW(~rDX3MgY^p=Dnp?p`NpL1J9>K-y4)xW!WTa3x(Xyo2W||0+~|%Lz*e{YWkK>)Nsd&2 z+>`u!N1!Yw4}fDs(+;_iz#G)JUvzGJXYvX4?aj?A6`p6w2@vhQiIda?pPI(zCP!Gb)&^hp*xiC0v@Jy&LbU$6#fwPla;!fXQ+e9XC0G( z@df@XaDg!eOTR~e4`c@+v*`AxBgz4#{FHa^s(Rsi3dF|{j+E|#c%f&}@#jU)Mo#Ll z1M&0Vf3P?nm(kX?Ez zFujVgz$iV|t;)H4IP^npE3w#mJsN3X|59>9T34_YN@L(e%gtg`A8M|ka@ej8o-|}F zszUl!+0}FAbF0fDELytUtpLU4irHXU>SsR+39#Dz)C@Gj@KCZDj9C|Kxf)w^ne+nU zmFopT?uyX#C&%+Fn}9)nCpEbOKf8+;o5q5I^e^s#dPVC~pB^qykuxBTD8 zlU`|C`f^WXpiJNH(l+b_GxS$Xn%-NcG0Pd`Jj)JBgr^5qhYv?b1`{9%-5j?A~3&|a|OJKLyEciPA4zKf6 z)<+kpJF98rBS?US&k?rX*=~aOUy@FMg9Zf#iVD^aQmhCK6>Z^lq*_w22j|JY#oF1`VD^0*U!AnguwCwh(RvZRH)>UJ0tHXGiNKid^M&%SJF zUQ?;MKH5V73wKn&X}N+6^1-Zk6f|=oAqVpkvQtBDqL z9_INxsF5w(n{4W4b7f)e8KVx^W=4fDkM5u~H4bY`+=o4G-M7i*@#T}w9hJ?_`Bk&h z$d%T(d1J(hH<`k{9nLJzsgdP2B>ZGkvoQXlo?l3;>x|VIm95ZzqC!Q(8g}qtvwY?) zTG3;W!mM@MR(GV*K0sl%8Y{J?|21R?ONw23cCxP6+*CF9{Lb zGuNocA~Sv2rIrIik~KZ!)jo5=l^Yq zfMXK+v4bPWL}oiAb|QO^O*`tYJM69cIpe5R9h%}>}ZCi4X#!9i5Wd@A3# zZ1g4&3He%VQS{bo#gH%*BlnHS(jG1=9*L^4{Zt@+ASuAn1G>dbPLO_$fFdPshKG$H z)c0z0pD%2p_V4`_4zJ0dnDug}m|hJAj~=0Wuldgy7fd}o1#^Ck!qkinm5ijLV`Y~{ z{(6(XbPyYh#uVY$F>TfRB%h)zP;p*GW6$kwrU_*9*2;y^6nF&~L3Ey0r)Q&MuJ{-D zCf`trkhwdOk3)tW8;VqA13sa3MVY*oJ}=96hjcS0e^4*0lpvu|E6&bfBy-rWzEG^*T*LI?^!8mbU(noBJIvUWR^^b!~! z{1^>-W<*(hL|O>{V{~$?a3C^)>TjvQ!di7@hvq0bgeg4a7dMzTY7F+RUl;AKx|N61 zq0uQwK2CFzQ>0Bi0m=EH@ulXNETy;hap}DeTAxUv(Wgj4r1gaR|nUb)~C|+xk$IamX!&9K&;U^1(8@0PKcL}s!iK|z0@4dN+&GxiL zC7xn(&u!ERd*#6^p4rP(!R^$@?hQ{tUiC5i7_U% zfMOzE@xVfp01W~RF3w>%46t#(pn6!1EQQUY?7D1gV`I}l_G13b)Ge99QFzN(zv5UY z#pyKZ0t+>3csA)GUf)|VaNuqH50Fr9{ceny!4=K8oKB=Gg9H_777ybi($RvY4$Vzv zg)@e8ALieri_IZ2L`&KZdo)Zl`DV>pnt3Y-*rxV6ywrzrBu2A3oy31Xil-m^m%+&W z$xeZ(ouhARCi5y-Wk)N}V>iGusB?bQPp83~3lBrCq&iHoocWzQgdoyw)3PiuVRacA zOL?MM-ngc{EZ1J)iyn-x@Q@JUK#YO!^2?Z8Fzx9|Q@;VHJGn*6UWzi=?zXM#9)!(? z?+e^LZSd&T&f?r@m}d` z+ASmJ7W>W^tl%{EK@%D(wkfEe5GW^0s~1+6n&4-gptztC4XpT!HLEWuli;NSsC?HB z_z{;Rx1{*!h%^mRMeqr9S{=*$x9LSw>6N>ZcOm0{ z2ZP#qptrj6JD)43pInYo@OiA?fjGyu?hyae?7zMRiBZL$p?rj>+G^n@=t=5Ga6Q$F zz_L9*YuQ)y7nmG)O9w?1bc4N!2*s^}Xj~cfjfH`a(_iNr&2J})W{$j6tCc>sZYua9 z;8!veO(dW@V+`Cb`=||EVC$tU2Rn^@VwSRiV0GbVcz9@Yr8|T%j8Q3eT%YQj{Ia8X zQE=x_OwIuI1EX$Zx!`vcE3nZ|R-v?mY1v&LCRqtMcax#w8- z07#y^jX>8+g_LGqnA3NRq_hgg&RD{^m|8aPhe-UK7nnBORGle(BpI78_oyEWs-qKnZ;^!kAf}p20PR0W+bz zu@eZT3|~$pQ$}O*dr+x2!b8{E!9nGO1W;;4H}Conxxl1LS~@$08k>ke>*?pyh*rR2 zNV==W>#&^9&(Noz+f=Me_}a1fcsrgf37}W>XUzM-^N_P(J1K24OevQqNal~e^lL;q z2G5{z!=c^iv89(hEaNMTa0N`}c&kJdh;H14G%qMu%d>L816y)B&F@mTWU@_FERZpH zNug9tk9BvzR|{4+aNq#HI{lXDmx3!`&xSwtRKX=Ix4(EAC(Kw=JNC=CZQdk{M^EZ^i{ajBTg;oQHwg3o7_7n{6@wD-)xkHj|3EMn=KGb*>@K$T!Hp zZ$T|7xEFXP*0)e+;$yQ8rwP-j@E}l|)B=5(lJLKxkb4QQBo)+{vGKygq}_>G&2f`A zoo~`B6HGvfL3TkN>;HM0AUDl|-3tzVOw_!hrfW=L=1p8feQHk*34}kq(wE#oEXuQkJ-lT}y zUHa?!vW2`kv43&7VcNK9jicI$A8_|~WVpl&W%{Nbf?AUEl=Pou%bR+z*+yU5N09To zSeYfDf;4U3SsZkhe3Lt5eQsvuK>57B79RVFd?{Eu{Epth@5kE*&o0O;gYz;9{?+a7p&powzH%82^baaG$3*OZ+GB`WWo0lEx(eXM0r-X`Mt^~P zq2pw)>Kn8Nq=Q0%eIrPY&3Xdcc-cHP+F<`dZwl(6(6!{97*x9t+-Vt4LfTy--j{r~ zzJkxUeDdprUpcC&ko?hX8{ANUrXwdK>nW_1j=dTfv-TyaMEGC2AN~IlEwtg|s%3xN zROo+kX+_tE-shFWG>UYW3Cy2v)iQXTl?O|OaVgCFU}YO*Q=1xCF~30b9Tn;Nc~_N)VleUjeTHCSv=-9n z5D|eGWdt+~Qbfz1EHq!NhuL0Kk4`zEqruo}m>`)0@2Y(+tcRw%G7LR*G&avvxox`= z?uhvgfW_6wZxH>!`>6$-$dx#s*Lzbw%W?8@_}dl5=c8YS?-i=9xtZ1C6UjnQv2(uO zW#XHi6;+{(D80~M#@j0~wT7iFN;jua1v5uqjzCPoV|!F-OW%;t(1|sx!v0VSM}*`A zzgOdejR0zIE0x${>2Gl%+CnpKMX^WcgQrOn-13=Y{CQm2wgLD-m1P^Dv(cWw6HJmT zETS+t@-ZvZSHUEs_kq0OA8;3(f{y$8 zp!b2w#Y9NITbphr2CPPbq94_RsaHIsUsfio}MQN&@WSM$X6DRM;sqcl#p8sk}dRD6J1kpwsa7E-}Z9;reL^??8OTV_>};% zbwF>q*AQI#D=5{VSv-dRv9GQI+!PuSf{|sUccM4IuZ8K`Oj&mh zhMQ3&xX%{*ttsEH#ytsGek;h4TnSm5H#`7n9=fedXGiojUx zSoCsG0F)^*DHcj7?@5uz;YcSF3VgL0BbH-!8uCcEtWsq|?)nE@$;qT}Y_s=}p?IspaIU z82J)MmwW29*h5T=CqrcT!0#^!wPBtpP)ckHsGrWJF{6`aSiT)7WQbcx1a|4CUsACfw9rCcp4}o zNMt4-%h<#0h1c)^Rgn_;6+t~-LL;PVOo*`G6MTJOM~D71j?i##7s@~o3&;U@6H0iB zAlZ-I>BK2$81qzB$ZuHk?gJSi2PkpiePl#3 zBHsoMu8Td1?uF>f0B2q|309BLFa(Ee%m!@bPx-4!BXfMkOc-MG*ab8 zZG9pn8(->Q}Sf4`h=a=K!CKDXQpWg;<#0Ck37X!EUCq+Napp0lAE}9^-4{ zzPGFYt$JRNi)rLDppIrZc7EcP)@obfjD0Z53#2|Jeo{)+3Eot5BfeC@vu zpt%3(gx?EYR@*5w5wh%t6$ewz&)J=2zE;F(-atRob5bQjq?xnF@%-}f?t_TJbDoTY zsiY6?*Bi!XTcXj;qs&Ung;-@(!>=Sg`aEAwIldYP^A8l;#M{-D{lGWGuSM|^^h)zR z)T~&7+r|;@Nl1gg-(M>JO8!!aG~b_Wqxtq6Xd>?%+7rVBxA)>!QX@A>aw6KirS zozb`{NSGYA0V)M+1&^AQO8C5^=#EoaTBIkdaKYL?J|Fb!ux)~8fzbX}{6(La2OHtK z5Eg_VdZ7v~h@HzNK0@OXysQq4xiz%xCA<$A$Q=sCj10rEI_kYy=v3@xX3v(rP|Mfgdm2@MlZEZ9T}@Vo)JSYqo>u zU~}X=2nT%7QPYu05#8-D*Li=rVH`X5(H+5yKA2#WQ8XPMRDx!&H#WS;nJLWjO~s1@ z?nLP(<)3u37ySxB!-u-HCU#4;x5JLJW=Ih)EZwoi z@-%qNs>_kQ%l-%AvL^BLhWdsdTSs2{eg2o#!I3iHNV0~+cdMOnf!c2E!Jjq(z0?D_ zX7s%nFQ$0P1R7B=sSjMN_Bw0D@V35p;JbfBMC8MA$=k`+Px~?`*WougdB4ce$?MSH zm%tJZ?|A|?_G|JfbTOE+=h%43&f1~G`4?eWSpe`a;j<-zxlo7yrn3$A_pc{8%qzj$ zczgJFtfO>Nm*r_!#MQ6hz=YoakaZp4RKH)GR2m|wY3Mpd+x@=Z^PYV^=e+Ml9*=~JEN(%M z>Q13Yzf&!&2=|>K2e;Kwn;c9p-6k#O_#W>8w^i{uK~8yb)nyj zY^}Ac=zvZ)*TSRDgvojKFv(G{RvcWRx_BKR6HYX1gX7JvcS0v7Rbl&|s1{mwh^=~7 z($ofwDcGEn<&ovOom1+l8`qJkcB$<-{I+;#la)IOGtn2xZ z&0mSly?q9$pwj#~{5NFje3&y}uaiDlC6B5Xhy{!22%I;^(N>ZWD$ukz$$dhvpir-( zNgkcW2Iq^WFu{N3@pbv{4J!Qk8ARZWTKu=(zaYCz61)yoq6~5mrpP_GTOmK?ltD^r z9h$tcO?E8#u^qlZK7lFBDCkBsJPm^&=$IR+Nl~GPTml2QgzInG#3U-&9#F2jT%@(9 zJ+Ut6+M^Tf)D*4mZr3f7UMQ>veJ=ZXFCd=EekSB!SuQatD3^tOyapWxNu6c#kir8` ztjENgH#kc+iW{JcDmZ5yXC`>Mt_Y2$~~Adeu{Nets@<_gzds2#=#yodt7%n8Oi` zhj_f!zZ-16%H*bf&s%EZR5_GaG(*$iI2Mum3bEELY8F6JO7W>+GcFYCuE0zV=xf8} zK<{!jzpeyKj*$ol-6J~XhgTs*g7?<$%p9N*`djUl^yE~mYqoL@6Z&oT{osxx&a3UL|*E#%oNuGbGA3p_Ul(MByyMavsNCA>)4&6 zg_{-)kC@L6eFbu2u62l95y~PvX=2C3 z>2{C14tq>3YmR~Y(+(M%uPC-%h@xA3qEkwQDJqD3wpyX~$>H?yfLIiKp{d`gYcl8}m zsidSp9!;|U*;)olM7udVyR!!>z$0+`i0Uy}prDAPjKt=AfNNo@;OG4l$EYddbmTi{ zIotBus2Fo+3guPh?IYw<>=$l2J=$;;97OUQ$1QNKcilYO9ke zgs%!XVT;tFfezXGwZtPNL=`7`(r=LV8tDwdvp3`>o?c~AT~%??^e9{bf~~2l*%H?A zE0%~?64L?F&hteSaX+=B$&}N19{y8IoKMlUHTHMCv4Yhq+jYC`=5LaQ+;rI0b5yKJ z@RwlNJ_6mo#zuQXS_;*9R}OtISc8ZTqVXT-GdMwV6mKuIuidVV-fQr+Xr%7tCbVPJ zZ;S6ghd(Fl1rYmN6?`Kb#rs)Gf~h=NY^&f0!lzipRJLAO6ksPE3Wl-`p;M5qmp>`6 zG??Z)Aqrw^MySGWomL!kjH=nF^cj(r1ZrK9WpSXEa2kvcK>#?-n4 z;0ZEk$kY}EXar?_d+bOJRiL`$c|lrh{Q&WC828np;ep~JlvrF4JE=G-8wJy=S;mX6 zZ?==>kWu1D?rONZoC?G7sZ>+C8gkUiVnkkmk`tQ}VC2iA7m44o{4e2wT*Na9NV0(hJ5^y$!0 zDhf5Y|7L`Sd#xTLrc5jOHm37a`{)Lnjv@~xvhY=BNT!XX4xmPEz0(~xoCYyR5`m)rF75?OUE%l;$k_>gh@q^`Nnw>_VnYCD zDEqkkdC;DNrn$pU&}0vhUG>b6A0Z`^~2++8-cPL@)~eME+=-|1jpE6cvuaF^#a`JSDA&99QElZ%Xv$<$!v(z z#6BxIXrbC7zY}ijY8T7?e8mzbJfr&#CyPG&bJI?9inw~FT3t4k9muGkuSIK8ep@jk zvH*wiSfx*cX&A}XWahUVBGXN(igdhUYIVC99%qF3(-NChNWIBUrfTfCe~+C01$_* zc73B^vm)V&tX}`1&FXu?d1F>(n4fb=8&jyhXEo+0>6 zg&kNMW;uj(kj4ffQHP9x6=$_ap7B1{D}IW-ZwPnXoi)E469dGniA#rCc-N61CQTG1r_mFX zgOX{-9*V#*7!>W+GIukLk-MPMdswDSRxp!*5v|SQ*>ihgCx!|2;dGESLtaO=e|u3l zq_K^k5c&?-yf4H@K*Sa~98@bM4Wn(o77CBKpIYRe>FoS<&21p10_;K*rhU;NDBLL! z@DgP{&qBh-KetJr-w7~(c0h!AhZ<K&vc4z8?aB9iV@IB{~q%>Dt-FooOz z9o8J<9kFh+f`jyggeA{=ZL@sxi`7b;?6rc4&-Rg1K}GS%H6_(RaoR2ut+R&)Ya+%h zK@>T3@QC-DB!S5L@Ak%xg>3_b%;|cQ5OKPVJXeBp%6(37kWD!BJuu>ExS|*5VLnaIITf!o5|3?=+v!>8tF^a6uer1u8HTgWUVcNj^GK#&=aaa z>H!aFh!UhLeZEd-C&p(lf*9@?%%0NoVgXxOJNtm1)Yd8xK^G{JJs{~HX#a>s){HFv zxFW3que~boSjDWc1KB8qDFF}JH>Z}CouHl@u)$v+Q4b&%)^M~~RHkZBml&oC} z>^s4-U`%xL>f^+qli!Hs&wR5lBi?%rC?Wb_(1L{*JBE~=`F2ly1C7mwoU+5glbLAa zDBeNb5gb6GPeY=ohN}=r@DAA%E8vRRP>qR24}ZKYeJORHeErF?f>F_kaR>D?EYYO| zAKn<1vn(%U2y+Ee*R>_iJ7L}|j2^XWqPJ?s%diXRI9!@*T+tDac3R+ZSor$EYGBJu z06jLfxK>TR+dZstFz$T~oxCnngeG;n3chI*)vyOwqrY30p!y|Ui94JuFOxX>k|Z9< z;H&V?sqfxl@@QM@nyD!cwNOe|pFc)>PV$;nPwxK4#>O)*51fU+lD7ScbQBlgLGN3? z^dzJO=);#_p|ligaA>SYl=hrZxDx5Rn=L7hhL_bPdp-o{iS*G+N_Le0TE>`Pg!Gs6 znNT>~%?}q{Lr<$i{1DhWX-Vc<%J4YuX?59-qw7BWP_-Qd!9qst(;-Roe?&Iw|NXTZMOk5* zzxQ`3`R2O4xKc{P%=r4Ry*ES?(4POT<3n8cQ1;<|7^?V_IdM8@V2OFtUpujc19S@O zcd{8L-f(;O|7|4c__lQvzPFEc?AR5xj$N*CEzf-l^wvKNH3iK7Ui$yN9Q|uSQ<%m@ zBZGg@m~J^cd}YV_-OylzI&^OATu0=^nak1vV$2)Y?U1RqzjD*&^&3=6-eP2Ln!Xp2w&EJ?~^NsV$lB)XpS>j|EO!6HTpR$uLpn0dzI@YCf>&+l}Uv*vs)BllZ^7oQ7*!c`WR;s`F;1F5z*McDE1;1l|v!UAf z=;n2TMy4k47lFfjVeFj_j7ge&KcZ}|J+F+s7vKO5}mG2Qr=cCy&F%iR6@Lv#|x({-Ij0Bh1}x2)SSXqZ94 z&GpZMbBWSrp*)bVqJW_L ze;?+=jLH7>Aq_xm1Ai?E{0_uW{KbmP=9t%%f62<|Nfak5#k%c)=E#))^%-mti~s8# zYW(R@GvfVWe|m1$IJgE^nPmk6{hyv&HHq-HRCb+R0Ro$m$-MrHOn{oHFs=U#+`hP> z=rSbA>%vgLroYxh;YwHR@9zypX?eN)E$2pqB{UJU7BE0qsL&()y<(z(7-4tu-~0Qj z?Bv~x=FEPFyC$i8gYrVA<-c|f@rvNKe%FXff0p%M1m2#{UB4mViYp4ad{!!I@ zRW>WuzdwWdv+7Bn^^q41=r?26e>fZx8gkVPx1JX>EXP&sp4R=fK_&btGtq_r6M~1Q z^E&3n>A*#8-G9GE^heaZ!g@X+{Jrr19+vcOW+kj+BJk!;%wNP%L)0{KnE`>r9X! zfTzuH<2#pV_@J=wj^8Y*e?o`#u^jn%S~R=!*j^e6CdwhlSI7RKmRl&iCq=?DKYh@p zteZu)>KP?Pu{87w`DlJGa(U|xtWWT%9!cx2pQ%Tf;SPNWj!r$_f6MlDtLyR^7sqmb zEgiEe7@KCTP2K#P{jmqAaYtwUs_4A3^`}pHY^I>8adpo8{9E9>ZIsfU`T9!YLw?;7 zqDCzo`fYHY$^xB3@p4)d_vIlvEU;u(X=lp7B2Q02|LyI7g+4%+B{DDm=I#_zEhoRs z-LB?eS7|AjZlh7yKNr#1fxS7(ujMR;ahfpVXQw#&hRQxzqNnwb&2+hPly;{KeEyg@ zt8-@zE>wF7$Gi&pTrhgE@Ybe*#&?LIgGsTVKQ^Xdc0*lW6R?|Ha z;qD(tGM#4{y;xO5oq`XK2b(HL<;ZtLn8Vh zkruq$$B~R~^LoH=zDRDMSbXtJc*Pj|n_F==;7HIrUtq1}9P?vv z`Pi?qmjN{s&X(0#$p68uvs4S86mY)HX8ggnBeRUxlu=<}(j$!f&5$Oka3AeLQBUo{ zF+tc=L6gM>xz}{qI)y*i8KYWgK59*9b}vinu|EyhEbiS+5oaa0;uY~*n}xzBhQ7>b z`8PSn;IHv*qfi?lUXf5%SL`P9M?flwIX05%c5EbL*|Ji_A?&6yWw>D@+_S|zzqauM zR13bFaWh)QPe(FMIHx_jx9;5oI0~H2?)ds!PRzA<(Qw)JunX5SXOyq*q2Sel`*lKJ z{7De!4e%AsW?j-4Li0~JA(_aB>QtGW+I7p&35Cnn^J_6S8n@m{d%VfDoDHI54a+)u z_U*uusm4ZIm0U?ULZR9XX>qETkmq$E72MpVC<>B#WRx9VW_+e^V~EN7PcZQbjr z#lzi!1qA2HM6H#Q_6>5Ic3|tO6#nFqGZSces&mIfy=D_aR>>yUXV1$OwbsAuDEx$k zW~K?Isy$s%Lx=d=P4D`#I$Xb{e_KvAlG2Hg0fNhzIAK+s5yNzY;*b(T2w?L1ck$$O zbTj-Kh4g^G7E8iWidP{p2FOXX`UT$XOHm4suUwGe_T-e6$@{@AxYf+In6)UkdnV~# z?_)+cCBb}=w2Tb5+1c5nF3U?F9OtJ#V5$Nqz04fBmcCQVZjmJnsTA{PcJ4AQB+Ndq z!pIBwZc4kx6FPUeo-U=6lmsV(y$Pb7j4Y zJuA+0rD6s2rg^_gKin#LR@>IbjHwQ0PRjlnc&|xK{qZ+n9(NySTvTt*3ZbXiZZL#V zhcK+}J}A2~zc1jto3DzDL}PRF0{+zjD*J32?9Ivr6C0m=tHpT!V-&Am!--u(Dl*%m zEk>h#FKnx^tG-_}qPwzi@AD?cM=CB04G~dMTnQSf2NSe&kN=pSzt^H|^SCS5#{Jx6 ztvuc19>TcIP1ws#!op0iR?ouKYXy#WZzQb@vzDeRirr;!5%;?71|O%^$Vdg-569lb zVe1MmK=Cj0k|byjZb%q0e{9YNm%_H5VKE~d{mBm7NMMXv8s~vh4yC2tE{^94;&8Y}6VCYDo7KS*H|wL%_AbGtl?EYJSH*w6(R1`V z_ma}-;$|GE`eU&66})is&(docibG?NawVm8M@} zW=hu#C=O}Btgq18hCXvtl=*OSb}x40-Oe9maGopXbn983&D=Y*Ek&=)d$P*RS=e!a zPj~BtU5!JL>{7ApBT)MJ1%GfvpEu)7;Z}+lLoP%k=0;BI_H*a}B8nl~k zFwDhy4!F!#utJz{mtE?m+xwH(vPK&JD9EfpqB2mF&+$7I=N90N-z6l1MV4CRC04Ba za!e*t>nSeU1O8>1`+)4b>LaddX|Yy|_j^vh@NqnFL@VQnaa&4>MWr*m%w?PG@-Lra z(U$8B-Jk_-1}FNN3t&Rbbs2BkHd_+7Q$fRs;!p?zQ$&y&ja;;^GSXq_#=R3eE9u!i z=_dBbh0x-%BiW&Aq$pmOFg#iBUmz0fG~0Hw=+V|*T!RXom6cWRt1B0_mT@>3zfVk@ z3E(=*oBer7qQ>sR+z%dE=H>(q24}*EuGV9T{aP8uTS4)+GwRshWRi8x>v1^{8;D%f zmDFBuh9k!*JoC_n?~1uZmm~Qq!?bJoEGNxFcTQ*VCA1p?fv_*E#$C%ao7)ke+q1){ zIbrMey+^iOd3S!R|8ONLPSgz@P;U2A`{})nfEqv-p zG&bFKF`TtDu~HLx!U;P0l8${0VMpI935BZkJR&r)q255rwDxCTy;KDEzBYvbtp^dnWf!M zOTo22JA16ER2rf#bM1qj9NCi2i?d_B9X-p|UvCTb%5yGU6dw$}AFSygz0JXYe!K2m z(PW9B^RcC2^}?xQpA;OZ@IJ)EJWyvko-oIh36ry%apjP0z+f7ox~B2bMWb(&H?9Fr z!FhZfl&r#)mhG!No%HTbXZ!bXDFDUUQzuoCM~_pBFM8{*!%{7c@0@%UbE0H0^1$n} zQHnma$zXBCp3K5U)ge-Z8&Q4ED)Ya5T-XAGg5>wVn3NWTjh&=gcrzna)H)-T9Iuhv zFK$FjQ7i^%&Fj^z6@Rpw{`tXTpuD!TlZ~h!Q9d&525%3%J`<(jwGF+3=m|l9OSO@P zpg@IN!WJ51{*{-1ShE4Pv<;Hxu|ZkDa1aB~CzZMifg4?{J8U0$4^1J|xzjB=5f zq64q3Y8<+!i>3xDiV66hiDLG1ox00Y{=^rb$W6Vpl{bR03#RUgH4RYNgad};4L z6trdl!YLGo5>KbdW#@t&6}>uqg=jv8XZ0_cn3#y;JMS0c3-HpMeHY!VC%V~la&n54 zbf$KN-38&iJszIi!(6)LScX^8$?d<*x0AyCSa_zk30XSt5A>`oO&bHBcg z90M^3k7f3ZQ?c@XL0R3s@$5mj_IAy%`)Ea>@Z=6=$oYk3?rNLoac^;@gx~$~W11!U zb86m8E-o%v!*TIN!eR60!+KsSglKkisQxW-&jmH+XN$W(mT|DkKnQj(D3Ia&dXXD> z<;jf9{Ho^yAZHdTdvWuwYHF2hnbZGzz5HEccZb z+xx{z>Nq=odh9G0J%x$|ns;ZJAu)f;%`$KH-Y8DO`+eH=uJMV9J5XZu6_YA+{(*db zQ`4RcvINVp2#_Mz@$d;6o}`ZXppPTg1x1%Pe(H}F%gq1uo0Gv;OoN!V76AH9w}OB;{6`IDV~+ zN7FU3Ju7*zBbg0U3r|NDinfky(@ku%9enD?<;vT#Gt;Skb-$mxZ2P?;A|gK5P{y8P zmA1Fc!bW-&b=mZcQPDG9?wPN)DPlGV?oCw(<#+?iu~^vOvZqYLha%1nW=N_;hDna; z-ktHnmF3#z=Dkohj0`?dJ)QqkG^SaGVs z4VKd;GB}Q`$;**+90uT#h~FA{pa`;O(OmY z2pGVY8#d5+*t@!VyP9G$EP<%&xY4$T#U zW=d^2Gbpo7;uNb*wWOym?)g@EgptZ#d4c~D-OgJ2+Nw%F6T%|iXNOp4Z7$`mcWUuh z&3^_MC^$LgQ-=iTe+jkw8F0-gG5tgoks`rxmuZ_i#p}V0FE0C5R$B0d)6vgRP_k2Y zRT{U6LGhY3Rn8Zs9CW;!{y9a5ajLQ-m<5L^#?L$m^1WcIW{pc1FMhY%u_4E``#dGV zh-=wbT{`E~B_ZL809PFyo?*%jk{faY+NLHv1Ph)>&$bpjY)EO`#+R^DtjqGs5zVx( zR8^Hi_Eei`o_HN^|C-9W(ULO1ZNtLXJ6ELpLXJ;3w`r&e2`kNA<+Rm^4X56M-Gaqn zymB4Jvw0*fvEQ92HKG-LIF6Ziiqjnxt_Z9O5)%~2$JHM?asc!Ud z?pEc&;g(*`h1zxm>RVOH=+g0IC$)96&gM+7|UmX-qeUxaVyl~?rS%`tw zTne5~*ljxN7O8~37{({=t&v$waQ6-1Hk~M3aW?+&d2{H@KjGW4%AB=@S4D?I^9qWe zHNr9*6OHfL39c>U?6H^s87hhEa;%Id=x+?da&#p zPuM6sRYio;;_R=lTt2qpZvyx+_#PLRw7U(Zxl~9o?^4;P#j;38cex%L*?Jj+(MT~Z zUd~Q=LpGLxbd*PFW}1#^Qz^v)EON}a^(Y;^a4L6a_{oQTErrYJHe1jRuWZI8E1PJE zV${`uOWT{%-#Ky}Bdep3S#rUGVM12lvu?2uj|x)=fCxPU?XkB7dInfnSo%H`)51@C zAd71!dZ!w}M2`C@Li!lLUOv{~Zuv@}b)%(lY9-<7wU#99{QKVxUitYcgvmIaI9gfh zcOSr`kRDemlFSH&Ni`#kiMx4)Q$2TJ^H&c2B)JidpQe3Zo_;LZt7SX!$p_nZ#gW~FR)8pCFA8Nuf{;K0#uR8|?22+&>K*YZGOtT5?jflOL zl!U?_1(m_mKls!!7=F%LqqQXpX2!d;PVpE(U03*YE5pI8Iru`FiY!5-yu3Wmm$(gp z4}82cnxevVvU{R4hF=G0C~d~I<3v_@l5)RA*CqeSg}#~5HsPO@0sTc_Ws?V$19x|r z<|f)OAXQ`7j62?>7g^PzC&L@dhTp!aqB3Dq#!sw~(xcVgj02dmva9ZFv?}i&e{Jgk zC5SmEgQOj-*_`KkyRQ%}h#4#>;>TE>7M}*%rhurrHT@LHub)5Ve*Vg}YfRvh!zxTL zei4fmy32hApw6qhQ5g&*xFll&0z?huID@+tzLGWcc0yiW=gM>C{Rtn6w*3MJmE8!u zF6b~Tl-5i)9O&9aVj|Lur}e{i{=mq1O?dx@bZXru`PEf^jP`j5&N}apPXt8v*HBDY zP9-wU0;ffVpyMWa#YA3;g!?=NDmcEeiYKYI2?-nZStatzs;#bZdn_Tfddho0p9#48 zV^FvHw^kmhm>tiZC@tqp9@XBAD0xIlV4d*0>aD5W1q+nB>RfToeonUiZyhfbIfkT9 zUu}bo9I3cKa)5@PY3qlZbpW_bh>*E)NmCkgeyF2AhwnO4+mt}u*uP}|$Za+~SmOh= zYeVj{gbxsdz5wET{km}Kk)F9<{dx_`Vl$>xWUGJMq=)g#lU}&-8pL^agZ+fTW3n`r zeu9gL1mDxGemf17H^*F-9T}S|kL-(2PWC}jPKuV~$`)F>Iqsj-IdyIfHq+_4oV2!; zE&xOUteAUjvzK}33xkU1X^*|x?=hII9l;X!`%rR}L?mR`RY&^Zxwx`U{}!8TFZ%Q` z&DSXNk4v9hW9meMimcb{Scikzi%ZoKgKYi{l}A2nW!<~E1i%8Wnz%@xtB$7h@pJKI zSJM*%KRg@O$7JMroO#_8J-F45l&$$KNv|wTqvWUSv_jTo(RS@~!l`U+An5TfY9tpN zr8GS64MD@6>sLjku|$SKm8|-%UnKoJVq*=cm90%(VAi|t%}Jkz@IlA@hU-K}4Fpsm~@!WDJe<|TWO`7;n7 z2XAw?sr59snqKuck28mFVlYbCNe`3TwZD+nyt5tncFTi5W0E$it$!ZSfPoEx0RwX= zkkS(~o*}9Qm#!CLcP>V-U@#fG`QrH}4)>8A@wmy4uP;8A0 z`qUTH)qr%O`vuWLIcuY$?XMSFQv&_1cG9CgSYNy%Vn^-On9Nd>i$j5mgatagxuI6 zrtZD{%d;>TBPW2~M0yu3QFWQUe?8U#WVfax0~;`i6f=#bPiR zJ3BjV>enm(ej@3S@ZxwkU`BVE;$Gufl}x}Pc;)0s`hP`Raao=Y>kdJR5Wr9ym1fwv zRA2tFjL!h$H$5VRjZ54_b`PnX<3wkv7i&3G&wcRxo9|O^fCBtvF`c5ba{->nP0S|T z%JMmU{4|HxhD~5AV&N@*oWD164{h5^th-gpo4Y>tN(_^v<$Di348}uSO-*e#Bcp>0 z+KRy)xs~?TK}j{|rVFQqQ#)y}q60kJR~ags@qmhYL2dlV)w5^Ms$RXCa}?C3)oFQt zurBrr&x8unNKZO!eT0%-em-r(F>FoIv;rm_y9 zHIzdnMVqzEq)BUxZpB{G6g9ndcIJyo6PWHt*Y3rulw_kAjX4f@Keqm@hYh>z@Nj&x z_1Vfw$m4M#P|vGv34%rU0Y&s7Qh@u-q%wF{SFso z=5PBKgYDc=WL~?W0%spOJ;&5{NYYA&Lor{T(xD7x6Db z$^c5zV)hYbh$@#s2#efJQ7soCo&}Kn>b6xOD5dZvWfe&YmRXIq9&@8*9~L6&LMf}+ zshc+=NV+fuN`2LfQ(aTSbbCZq<*rO!M+<%GU$mqG*!ZlN&3NZEXf5U8EmY&LCiVeX zZAFi`7#|z^l$Kr6mZ%IoP>$binrq{3>Zeods63)U=~UY^;$cHJFQrtTP}?F0K}Bt- zZv#mts#mq?MavDCL+-csY(E`L4m z6H{lXdk_a89V`PS80*>!@gY=+GT+b<5_f!9-f$gvc~F2pA*cD(%E>&kU1_!NU^@EG z<=9tvUGG-Q24%h3(KgxwL`C6Z*96Hu+&>ECyE4wEJx?k&bpi5=e2CrIee9&#rY&U@ z;NzL)3QBeqevaP>_-xvrFMXq%W>s+tHuzSW>Mr>U%r((?jFW3 zgwpBW#Nrw!^t*nQaZbPrjJl2=id<9UaVFLrdV7D)d`uhzq z8mR)JLp5Jk#WA3ftdkE*V4&1{a@O~tL?>E;Rm$2pA-!31b%~mdxUH8iix#-#I`>P) zT44ORd*oD9_D+yucBP+zUa99Ve%!{pO>t^@GtFAG836%=QB368z$9q`F*{MWON&O%W)p$sm@ zlG5oLTyU^rqa8GS)`8Cw=2vowjOCQE$^xj zDY17dVH~Ikt&37D^VfA|4s%(a&n$q~Fo_zfqG1J8Vhdv`pFG~qxIk^7kqN0#mjj9u z+H$(XuFGDJxz!k|Djobgr;h5lE?raukMxizTap+x;g1>-vq`W&VKoBGPIS#R^ydcg<=rY&6 zRq}5Iluyr{-TDa?-HD^#5Noqw%9Pk7DO~aXb<-=(f7Te?ECbB}RB%7lIQ05l3Do1gJGCyD zf_J*4rnUz#)l&jzSF?c5si-ovhY0J-Z9MTEfsc$^}V0WHQ z_%5;L>-VcpExXGmYNkI!&5B>SJp=)P>>n>}Ay^Xryo~0KIfZZ=_luzmVOHpjQwfa`(qVEq@lO<_?g(yk!^en@or1Fq} zbVoVP&c&W0k{fdk)jp@Bq*uH{%#%af{tFt3K$1h`^!e*I9&f+Hg%|ZGcq@716NP*8 zu8)A4C_>0bIkg8Ehs3#a=Q^jkKP(K#mqoa9uprnGkZB&;mxP+(;o)1buB9$2#-J{w z!GiIdJKvr8;@c=CY*c5Jc-ZA~7SSIw)cQ{I&w|MddW-ixLEV(T#LVtz%)%J)&gT8l z?&#>OIAUIRno;6|UlVdGo#tii=98bP5g^z#qEHJdiTID_@_oQFuij#JsoTrNuK;P3 z?%Y8%d4LzedufNY2x`XKVw(5!#h)$YCn@q)?6+miUHo`gKU9Oypby3iRy_PcE7cf= z4nFohEe5>?9MZ*GG zH@wFlFGa z+xBK2>}WFx8|&8p5E&Uc2SXCc$pLOe&x@b*hpc#EE#4zJtVk}pdtI>{6NIAy_Lk4x3X1YE9 zrYrQ+M#yu$K9_C8Jd6s4GyMHxFr|?-o-=X_#-s2jzR5j){P^QU!D1;G1K&HIdkClO znM7zHq_U9zm8zG#_O;$QQ<?_F z`sQ}W=_XN+$C53=He+!4> zuac+oo;W@Rb_#^IT+dFuLv%(5!>8{94#@7#VSh(eO)dLC#`z#&gA10}+Rskq z|1fG>zIb?-IURhnIIdPc3K|FFcQ%-$)hYZ+b|LV=9NNzh2A@#?LCyUTZK4FFuTVjG zq3kG`3|J6vSb*raoMzl|{_6WuqcghgN04*U15ixBlfUVL^o**_A^C*D<%MHr-6!1Z zH60OzhDKjNUzKcx6<@u2^#%140h|ln{yZW`%uEO6z14igWFvG4%Idg-PO6tBW*E=G z3!Ra#Fs>u3i-E?5PH5yGiSF1n0mg4`BP8eS?5wJ)D*n7V5^Z(~W?Q^dQiSkAWh+s6 z(6W+6X5+vAVKWq9gQQ4U%01mFoFc%`xHB(jD0J{Dw%fJE~4;zZ2!vmpvGI z-_H0c+F!O9|0v7`neR+2L8*Jz&p@L{@NVN=J(_?~M?r0T%xS^WKl-r^L`QL7V)U}l z*o2~&NKEu_JlAenVWTt%U3mIcrSNNknsyhb^IU||ZQ0xk!NN|byC>t5a^mFl`DeYR zp+3GKK4<`g0Rm`>1R>~34I4%PS&zPa*nplG&udDFyNFD)?;EhApDzbq{VS(ZxWAp4 zhHS-(xc&Fvf7qsYe>nr$?0xcXbDV=(wGf2A@6=2+?Jat24v1`trcvSmZORwWi9X1- zyafy9intBm%|H~}wtahuEJ+9AslbY(55)r&z;7nEaw?7NIPad1Pt4Ff z0mYEOD+2a-hp{j|dedmmND^(B=tw9822}2L%KB-*^ke@UJ?~!=dzJG70|T+O5f6nC zlp+w$!v$qir%8qsE3@AypC;F#Dw zyLW#-YW(?#FlylHL*Hs-meH(8~9r)KQT)h(HrOX07 z7NgJQY0X1P9zB{G6fAf*__~!qs8BXlfwEqxdL6Xgnpod#kgU@8k)EC8>*C+PU!ZWw z!X`4H6WU<3!J=)O={VQ`?#$0s@E}nCo7v;+fE>R5^>Q#NyXu`JZV6N7M^D8cid`5k zK)t6|Cxx{}!T7s%*BEwTBq7ziu*CRRuRCWBUDbE7!g;S^jB*MLD3zcXar*S>aTb2l zelM}v!CQW}GP|sUWZ!Ne3VcvUE_sRf9PdGOBK{_M2P2!C@;P5+=P21%2X=t-XEMwC z<+k0wUkl*wPWOYa!ooPAk$A5*p-$}H_fJH%5*9iK!4D-4Pw2U>*N3&iZ0>GIpDZvW zE&&3d%L<)>B86=c0R&6AFZH0|mGo>|xxwgC>h z^plZjdfIX;1px8shWLVnw^xlij9m70{&!6P2!k+(nkm;&A`3E=xy5hFKO#K@EIuXF z%}%(iNI_oJ#h)_Ok^_#W7jnC~V3(c+x*J5!K$oCwkGiv!Vt4ok+%Z?p8X#M%|5GnRb+vDyff3;_qw0weabDoJ`Fao8w zLcrV5uC6{fVPuP8sOV zohV?{&apI)IrB=}x{VuxU+5K^c~9Z$$+m|?j&E4#Ll)*dV|R1rhwfo_Om@K&Rp_kw zt;*H+aC?RcE3{DyJl@E&9=+k3+Vi9tDvLL=E(H(uE7Dm_v-W{|>pHwEHZ4sgDSx*8 zTH2jUeG^kNExHrJsH=>^IyW7J_4y`TWcgu-El)5Kd8WmtcLbZs-t~5%X!A9$QZLp) zp`Ty(3S4L6OZf0#YR?%CC)_3XazDFNOw}_kpA+@k`+2~5(LPggV|r&5N4H=0*0@9Z_sa{~0#&)QW7r0e6B-GM+*YF6X&K3KgyRr);y0@JT@N z?TWk?T#BWbAd{a0FYXu4AAIUEd+YnB2X*h1S;LMIjaMLCPzwr!@^60i@X>tVYh>m3jYXlFQAiQt)Z)`#-ybvT+Al@r%!}h#C92eTC ztR_E>EC33a032Kie$#=fmLQ zVQA03emzl-`Wu5=h@NLV)(b%EEQ_gEeapII(MJLYKIJsOt0<8q;yR$3nS-GYId3FB zpu*cuR{@-HD%EYJbmY`fvt{{{$~70lOg~hJRdEhZMO%61M#(ClKeWMqxB7)p_3IJ? zwpMjVdLha0;ak&~hcFpfF>rovd7-6Ha-;%VCDvoE=UcyxzyH@#~a?+ia{T2#FcmBgy3fG8_ zRNROOv9_=^ekyjiJ{pyLo$S}Pp~(Od5s}-Y`OZIHDDIb)?J+>DF7$$`-L)QkJc?(V zehg0W78=Hg1ab?9yBt^}-2)1l=1t0zXmBoA@F6q+yLvxzw}TtggJTQ#&2BKGIofB) zHTI9gD76(Nd2^ntYzd&U|JAaxqVg>MU1H+#KyJZsmECK!pN9O6hE2SdTX_=cn1Rz_ zV>mNG>&0J(YG(wKI#5+Gyl+o?iUv3J#%Csbi}hddvuRL z6;~?gGOrZ2l@!Y>Ajl>K3uR^*8mbJ`9ih~6nA{V;b=5@MuqBMTmX{cEAX2n4>;8+( zlS-*=2Q7A#A5&3LJ!7S_S=+QZX^yh-q*pK@Zr0gvi(3n{DAJC+Ij?rMRb+BO^ zCYw)69WY`b@}MQKGg`Rd5r;i?A9bm-HPn5}M>dE=0A3*tLk&R$Q#{NP$+{5PjPs|S`XeLWOEgICw|O|IUFzU!i z6fR(b4t?swTe8{6J3(7@EJ;pySsN!@EzSj1Rc*9Qu1c5;G_aP0sYxuD1QwXTeXVN} zp%AFw2BT7p+txU{Bzu((wBoVycT_eQEE!l1HzI2u2ygW^Cw_ z_P(8C&V+^2fFQe6lgMv_mPD3CdBV$QhuwK!V%m1s8r6&ae!(c971lQyvXExaFT*c! z0Cssx7Me3Tt;VTUoeXzQqB4k;(DM)uYeWIMwLDKsT?}-g{PLLIf4Mwb!+ImG?9r;{ zFun@rdnM+A)m?^+sVR%WMT6z$@h?vq*mo_4A@B~SgBbA}g+86*OWX|;TxMxKyr@gN zavfpjjJ2s!;fm%>uU3Aj`3PL%sS=V7r@NrOWsM=@0FYb4$}r2&+_t>6jP1TB70_Eu zD^sF&=J-475N)M;4P))ivftN4$6SO*LVeiP~$gE{v`Sw)hj-G<5rY$DF8Bu!cM zT71Bv|^s~?pYS3{g!0X1h-2M>dEy??{ zCah4^W^rkn;Bbs&Z~syu4;pewRbou;ZT*qrjL+c}?awU9A@QvF{D6OM)>bV59D~O= zZNZJP(JXfBfIq&|m6N_msc{1#$S_mRozsER&6T5@NhJ)NI(^0$BfuLQXzvWo=X8;Z z=T_{U4%#nPqsrmq-L`s+VmCVgM+q65xaKs| z$U0=|I?wB`+@v89ZMqR>c0QXY*=+Y=e+=`7l#}^3JBP@|{1R~$pxU9SgLh!^C@*y^ z^GK^o0?g5((ZnZ+$#KdCp-Nit>o7(*CWlSO?LFgC;a#f z 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

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


AewDOjh!MMu(vjQJBsiX3ZCmAui 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 literal 0 HcmV?d00001 diff --git a/src/docs/k8s-en.md b/src/docs/k8s-en.md new file mode 100644 index 0000000..20e9527 --- /dev/null +++ b/src/docs/k8s-en.md @@ -0,0 +1,141 @@ +# 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/src/docs/k8s.md b/src/docs/k8s.md new file mode 100644 index 0000000..9da3c28 --- /dev/null +++ b/src/docs/k8s.md @@ -0,0 +1,141 @@ +# 使用 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/src/locale/en/LC_MESSAGES/django.mo b/src/locale/en/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..f63669f46b3283a84e04098a7338b55f204e7b9d GIT binary patch literal 11097 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 literal 0 HcmV?d00001 diff --git a/src/locale/en/LC_MESSAGES/django.po b/src/locale/en/LC_MESSAGES/django.po new file mode 100644 index 0000000..c80b30a --- /dev/null +++ b/src/locale/en/LC_MESSAGES/django.po @@ -0,0 +1,685 @@ +# 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 "" +"

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/src/locale/zh_Hans/LC_MESSAGES/django.mo b/src/locale/zh_Hans/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..a2d36e98a180a2d9f413841d0cfa0c5f85654f63 GIT binary patch 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/src/locale/zh_Hant/LC_MESSAGES/django.mo b/src/locale/zh_Hant/LC_MESSAGES/django.mo new file mode 100644 index 0000000000000000000000000000000000000000..fe2ea17dc2636742b9c3b8d4eddb6293ee3b7290 GIT binary patch literal 10268 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 literal 0 HcmV?d00001 diff --git a/src/locale/zh_Hant/LC_MESSAGES/django.po b/src/locale/zh_Hant/LC_MESSAGES/django.po new file mode 100644 index 0000000..a2920ce --- /dev/null +++ b/src/locale/zh_Hant/LC_MESSAGES/django.po @@ -0,0 +1,668 @@ +# 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/src/manage.py b/src/manage.py new file mode 100755 index 0000000..919ba74 --- /dev/null +++ b/src/manage.py @@ -0,0 +1,22 @@ +#!/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/src/oauth/__init__.py b/src/oauth/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/oauth/admin.py b/src/oauth/admin.py new file mode 100644 index 0000000..57eab5f --- /dev/null +++ b/src/oauth/admin.py @@ -0,0 +1,54 @@ +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/src/oauth/apps.py b/src/oauth/apps.py new file mode 100644 index 0000000..17fcea2 --- /dev/null +++ b/src/oauth/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class OauthConfig(AppConfig): + name = 'oauth' diff --git a/src/oauth/forms.py b/src/oauth/forms.py new file mode 100644 index 0000000..0e4ede3 --- /dev/null +++ b/src/oauth/forms.py @@ -0,0 +1,12 @@ +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/src/oauth/migrations/0001_initial.py b/src/oauth/migrations/0001_initial.py new file mode 100644 index 0000000..3aa3e03 --- /dev/null +++ b/src/oauth/migrations/0001_initial.py @@ -0,0 +1,57 @@ +# 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/src/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py b/src/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py new file mode 100644 index 0000000..d5cc70e --- /dev/null +++ b/src/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py @@ -0,0 +1,86 @@ +# 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/src/oauth/migrations/0003_alter_oauthuser_nickname.py b/src/oauth/migrations/0003_alter_oauthuser_nickname.py new file mode 100644 index 0000000..6af08eb --- /dev/null +++ b/src/oauth/migrations/0003_alter_oauthuser_nickname.py @@ -0,0 +1,18 @@ +# 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/src/oauth/migrations/__init__.py b/src/oauth/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/oauth/models.py b/src/oauth/models.py new file mode 100644 index 0000000..be838ed --- /dev/null +++ b/src/oauth/models.py @@ -0,0 +1,67 @@ +# 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/src/oauth/oauthmanager.py b/src/oauth/oauthmanager.py new file mode 100644 index 0000000..2e7ceef --- /dev/null +++ b/src/oauth/oauthmanager.py @@ -0,0 +1,504 @@ +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/src/oauth/templatetags/__init__.py b/src/oauth/templatetags/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/oauth/templatetags/__init__.py @@ -0,0 +1 @@ + diff --git a/src/oauth/templatetags/oauth_tags.py b/src/oauth/templatetags/oauth_tags.py new file mode 100644 index 0000000..7b687d5 --- /dev/null +++ b/src/oauth/templatetags/oauth_tags.py @@ -0,0 +1,22 @@ +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/src/oauth/tests.py b/src/oauth/tests.py new file mode 100644 index 0000000..bb23b9b --- /dev/null +++ b/src/oauth/tests.py @@ -0,0 +1,249 @@ +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/src/oauth/urls.py b/src/oauth/urls.py new file mode 100644 index 0000000..c4a12a0 --- /dev/null +++ b/src/oauth/urls.py @@ -0,0 +1,25 @@ +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/src/oauth/views.py b/src/oauth/views.py new file mode 100644 index 0000000..12e3a6e --- /dev/null +++ b/src/oauth/views.py @@ -0,0 +1,253 @@ +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/src/owntracks/__init__.py b/src/owntracks/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/owntracks/admin.py b/src/owntracks/admin.py new file mode 100644 index 0000000..655b535 --- /dev/null +++ b/src/owntracks/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin + +# Register your models here. + + +class OwnTrackLogsAdmin(admin.ModelAdmin): + pass diff --git a/src/owntracks/apps.py b/src/owntracks/apps.py new file mode 100644 index 0000000..1bc5f12 --- /dev/null +++ b/src/owntracks/apps.py @@ -0,0 +1,5 @@ +from django.apps import AppConfig + + +class OwntracksConfig(AppConfig): + name = 'owntracks' diff --git a/src/owntracks/migrations/0001_initial.py b/src/owntracks/migrations/0001_initial.py new file mode 100644 index 0000000..9eee55c --- /dev/null +++ b/src/owntracks/migrations/0001_initial.py @@ -0,0 +1,31 @@ +# 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/src/owntracks/migrations/0002_alter_owntracklog_options_and_more.py b/src/owntracks/migrations/0002_alter_owntracklog_options_and_more.py new file mode 100644 index 0000000..b4f8dec --- /dev/null +++ b/src/owntracks/migrations/0002_alter_owntracklog_options_and_more.py @@ -0,0 +1,22 @@ +# 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/src/owntracks/migrations/__init__.py b/src/owntracks/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/src/owntracks/models.py b/src/owntracks/models.py new file mode 100644 index 0000000..760942c --- /dev/null +++ b/src/owntracks/models.py @@ -0,0 +1,20 @@ +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/src/owntracks/tests.py b/src/owntracks/tests.py new file mode 100644 index 0000000..3b4b9d8 --- /dev/null +++ b/src/owntracks/tests.py @@ -0,0 +1,64 @@ +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/src/owntracks/urls.py b/src/owntracks/urls.py new file mode 100644 index 0000000..c19ada8 --- /dev/null +++ b/src/owntracks/urls.py @@ -0,0 +1,12 @@ +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/src/owntracks/views.py b/src/owntracks/views.py new file mode 100644 index 0000000..4c72bdd --- /dev/null +++ b/src/owntracks/views.py @@ -0,0 +1,127 @@ +# 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/src/plugins/__init__.py b/src/plugins/__init__.py new file mode 100644 index 0000000..e88afca --- /dev/null +++ b/src/plugins/__init__.py @@ -0,0 +1 @@ +# This file makes this a Python package diff --git a/src/plugins/article_copyright/__init__.py b/src/plugins/article_copyright/__init__.py new file mode 100644 index 0000000..e88afca --- /dev/null +++ b/src/plugins/article_copyright/__init__.py @@ -0,0 +1 @@ +# This file makes this a Python package diff --git a/src/plugins/article_copyright/plugin.py b/src/plugins/article_copyright/plugin.py new file mode 100644 index 0000000..5dba3b3 --- /dev/null +++ b/src/plugins/article_copyright/plugin.py @@ -0,0 +1,37 @@ +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 + + # 如果是摘要模式(首页),不添加版权声明 + is_summary = kwargs.get('is_summary', False) + if is_summary: + 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/src/plugins/external_links/__init__.py b/src/plugins/external_links/__init__.py new file mode 100644 index 0000000..e88afca --- /dev/null +++ b/src/plugins/external_links/__init__.py @@ -0,0 +1 @@ +# This file makes this a Python package diff --git a/src/plugins/external_links/plugin.py b/src/plugins/external_links/plugin.py new file mode 100644 index 0000000..5b2ef14 --- /dev/null +++ b/src/plugins/external_links/plugin.py @@ -0,0 +1,48 @@ +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/src/plugins/image_lazy_loading/__init__.py b/src/plugins/image_lazy_loading/__init__.py new file mode 100644 index 0000000..2d27de0 --- /dev/null +++ b/src/plugins/image_lazy_loading/__init__.py @@ -0,0 +1 @@ +# Image Lazy Loading Plugin diff --git a/src/plugins/image_lazy_loading/plugin.py b/src/plugins/image_lazy_loading/plugin.py new file mode 100644 index 0000000..b4b9e0a --- /dev/null +++ b/src/plugins/image_lazy_loading/plugin.py @@ -0,0 +1,182 @@ +import re +import hashlib +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 ImageOptimizationPlugin(BasePlugin): + PLUGIN_NAME = '图片性能优化插件' + PLUGIN_DESCRIPTION = '自动为文章中的图片添加懒加载、异步解码等性能优化属性,显著提升页面加载速度。' + PLUGIN_VERSION = '1.0.0' + PLUGIN_AUTHOR = 'liangliangyy' + + def __init__(self): + # 插件配置 + self.config = { + 'enable_lazy_loading': True, # 启用懒加载 + 'enable_async_decoding': True, # 启用异步解码 + 'add_loading_placeholder': True, # 添加加载占位符 + 'optimize_external_images': True, # 优化外部图片 + 'add_responsive_attributes': True, # 添加响应式属性 + 'skip_first_image': True, # 跳过第一张图片(LCP优化) + } + super().__init__() + + def register_hooks(self): + hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.optimize_images) + + def optimize_images(self, content, *args, **kwargs): + """ + 优化文章中的图片标签 + """ + if not content: + return content + + # 正则表达式匹配 img 标签 + img_pattern = re.compile( + r']*?)(?:\s*/)?>', + re.IGNORECASE | re.DOTALL + ) + + image_count = 0 + + def replace_img_tag(match): + nonlocal image_count + image_count += 1 + + # 获取原始属性 + original_attrs = match.group(1) + + # 解析现有属性 + attrs = self._parse_img_attributes(original_attrs) + + # 应用优化 + optimized_attrs = self._apply_optimizations(attrs, image_count) + + # 重构 img 标签 + return self._build_img_tag(optimized_attrs) + + # 替换所有 img 标签 + optimized_content = img_pattern.sub(replace_img_tag, content) + + return optimized_content + + def _parse_img_attributes(self, attr_string): + """ + 解析 img 标签的属性 + """ + attrs = {} + + # 正则表达式匹配属性 + attr_pattern = re.compile(r'(\w+)=(["\'])(.*?)\2') + + for match in attr_pattern.finditer(attr_string): + attr_name = match.group(1).lower() + attr_value = match.group(3) + attrs[attr_name] = attr_value + + return attrs + + def _apply_optimizations(self, attrs, image_index): + """ + 应用各种图片优化 + """ + # 1. 懒加载优化(跳过第一张图片以优化LCP) + if self.config['enable_lazy_loading']: + if not (self.config['skip_first_image'] and image_index == 1): + if 'loading' not in attrs: + attrs['loading'] = 'lazy' + + # 2. 异步解码 + if self.config['enable_async_decoding']: + if 'decoding' not in attrs: + attrs['decoding'] = 'async' + + # 3. 添加样式优化 + current_style = attrs.get('style', '') + + # 确保图片不会超出容器 + if 'max-width' not in current_style: + if current_style and not current_style.endswith(';'): + current_style += ';' + current_style += 'max-width:100%;height:auto;' + attrs['style'] = current_style + + # 4. 添加 alt 属性(SEO和可访问性) + if 'alt' not in attrs: + # 尝试从图片URL生成有意义的alt文本 + src = attrs.get('src', '') + if src: + # 从文件名生成alt文本 + filename = src.split('/')[-1].split('.')[0] + # 移除常见的无意义字符 + clean_name = re.sub(r'[0-9a-f]{8,}', '', filename) # 移除长hash + clean_name = re.sub(r'[_-]+', ' ', clean_name).strip() + attrs['alt'] = clean_name if clean_name else '文章图片' + else: + attrs['alt'] = '文章图片' + + # 5. 外部图片优化 + if self.config['optimize_external_images'] and 'src' in attrs: + src = attrs['src'] + parsed_url = urlparse(src) + + # 如果是外部图片,添加 referrerpolicy + if parsed_url.netloc and parsed_url.netloc != self._get_current_domain(): + attrs['referrerpolicy'] = 'no-referrer-when-downgrade' + # 为外部图片添加crossorigin属性以支持性能监控 + if 'crossorigin' not in attrs: + attrs['crossorigin'] = 'anonymous' + + # 6. 响应式图片属性(如果配置启用) + if self.config['add_responsive_attributes']: + # 添加 sizes 属性(如果没有的话) + if 'sizes' not in attrs and 'srcset' not in attrs: + attrs['sizes'] = '(max-width: 768px) 100vw, (max-width: 1024px) 50vw, 33vw' + + # 7. 添加图片唯一标识符用于性能追踪 + if 'data-img-id' not in attrs and 'src' in attrs: + img_hash = hashlib.md5(attrs['src'].encode()).hexdigest()[:8] + attrs['data-img-id'] = f'img-{img_hash}' + + # 8. 为第一张图片添加高优先级提示(LCP优化) + if image_index == 1 and self.config['skip_first_image']: + attrs['fetchpriority'] = 'high' + # 移除懒加载以确保快速加载 + if 'loading' in attrs: + del attrs['loading'] + + return attrs + + def _build_img_tag(self, attrs): + """ + 重新构建 img 标签 + """ + attr_strings = [] + + # 确保 src 属性在最前面 + if 'src' in attrs: + attr_strings.append(f'src="{attrs["src"]}"') + + # 添加其他属性 + for key, value in attrs.items(): + if key != 'src': # src 已经添加过了 + attr_strings.append(f'{key}="{value}"') + + return f'' + + def _get_current_domain(self): + """ + 获取当前网站域名 + """ + try: + from djangoblog.utils import get_current_site + return get_current_site().domain + except: + return '' + + +# 实例化插件 +plugin = ImageOptimizationPlugin() diff --git a/src/plugins/reading_time/__init__.py b/src/plugins/reading_time/__init__.py new file mode 100644 index 0000000..e88afca --- /dev/null +++ b/src/plugins/reading_time/__init__.py @@ -0,0 +1 @@ +# This file makes this a Python package diff --git a/src/plugins/reading_time/plugin.py b/src/plugins/reading_time/plugin.py new file mode 100644 index 0000000..35f9db1 --- /dev/null +++ b/src/plugins/reading_time/plugin.py @@ -0,0 +1,43 @@ +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/src/plugins/seo_optimizer/__init__.py b/src/plugins/seo_optimizer/__init__.py new file mode 100644 index 0000000..e88afca --- /dev/null +++ b/src/plugins/seo_optimizer/__init__.py @@ -0,0 +1 @@ +# This file makes this a Python package diff --git a/src/plugins/seo_optimizer/plugin.py b/src/plugins/seo_optimizer/plugin.py new file mode 100644 index 0000000..de12c15 --- /dev/null +++ b/src/plugins/seo_optimizer/plugin.py @@ -0,0 +1,147 @@ +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", + "name": blog_setting.site_name, + "description": blog_setting.site_description, + "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'' + + seo_html = f""" + {seo_data.get("title", "")} + + + {seo_data.get("meta_tags", "")} + {json_ld_script} + """ + + # 将SEO内容追加到现有的metas内容上 + return metas + seo_html + +plugin = SeoOptimizerPlugin() diff --git a/src/plugins/view_count/__init__.py b/src/plugins/view_count/__init__.py new file mode 100644 index 0000000..8804fdf --- /dev/null +++ b/src/plugins/view_count/__init__.py @@ -0,0 +1 @@ +# This file makes this a Python package \ No newline at end of file diff --git a/src/plugins/view_count/plugin.py b/src/plugins/view_count/plugin.py new file mode 100644 index 0000000..15e9d94 --- /dev/null +++ b/src/plugins/view_count/plugin.py @@ -0,0 +1,18 @@ +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/src/requirements.txt b/src/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..4b0963860353db73cc790ee3d2e46c02921cd440 GIT binary patch literal 1690 zcmZ{kL2nvS420jgQh!RSup}Wp^iuUyDN@uPawb541a=85t>}+$JD&#wpAfuP%RBwslpl((g_!Ca#6Z#81L8}0R;*UEiRktI z{7sx6pF}T&Tj?QZU7iBn6Pnkzy+wmt3cuF98$FGFasQR@+|ui~VG?x=g=}>K^rev0 z)OEv5jCR7c`W}^+!1sma?ZVH+=BKbQ%1)5Ki^;N#g&LQ+vL|z6>2Fu|ZP|*;LZ8#f zZWWTrM3G@|or~*6x#5wjp-C_5G?)LQFT{ykl0V%v8}vo%T8xQS(T9(R`leAgaU5d| zoq}f-TFmtCQc*J1dU4IMdUOWc-=m+^n@M>TCUv@sxGVUoXdUjUBF;@@u&?f-`WMyG zNRj%0xDFl}-Ab?TLO$sp?y#=Ob{X2gitk7-T#l8S=^(N-%0g@?Ofs!v0?c%!{}*~- zLcA9|j~=pvTXkZmcc_Z#U-GePRsbZojf6s3qi~I@XChb1GSVmN;{wlwHLwaG{+)w# z({0prFU(Y0xn@rah+aABO`cXPm{weamJ_+Nje6nF3Ggsn3CAR5C8uZ4=O%UN!qUfS zWWw#TO`OPnwdGetn7b-=hdagmuvwO>$R5gllyOh6`{ON= z)4~pTRX5m8xohYL?#{c{^6zo#`(= 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/src/servermanager/tests.py b/src/servermanager/tests.py new file mode 100644 index 0000000..22a6689 --- /dev/null +++ b/src/servermanager/tests.py @@ -0,0 +1,79 @@ +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/src/servermanager/urls.py b/src/servermanager/urls.py new file mode 100644 index 0000000..8d134d2 --- /dev/null +++ b/src/servermanager/urls.py @@ -0,0 +1,10 @@ +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/src/servermanager/views.py b/src/servermanager/views.py new file mode 100644 index 0000000..60f00ef --- /dev/null +++ b/src/servermanager/views.py @@ -0,0 +1 @@ +# Create your views here. diff --git a/src/templates/account/forget_password.html b/src/templates/account/forget_password.html new file mode 100644 index 0000000..3384531 --- /dev/null +++ b/src/templates/account/forget_password.html @@ -0,0 +1,30 @@ +{% 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/src/templates/account/login.html b/src/templates/account/login.html new file mode 100644 index 0000000..cff8d33 --- /dev/null +++ b/src/templates/account/login.html @@ -0,0 +1,46 @@ +{% 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/src/templates/account/registration_form.html b/src/templates/account/registration_form.html new file mode 100644 index 0000000..65e7549 --- /dev/null +++ b/src/templates/account/registration_form.html @@ -0,0 +1,29 @@ +{% extends 'share_layout/base_account.html' %} +{% load static %} +{% block content %} +
+ + + + + +

+ Sign In +

+ +
+{% endblock %} \ No newline at end of file diff --git a/src/templates/account/result.html b/src/templates/account/result.html new file mode 100644 index 0000000..23c9094 --- /dev/null +++ b/src/templates/account/result.html @@ -0,0 +1,27 @@ +{% extends 'share_layout/base.html' %} +{% load i18n %} +{% block header %} + {{ title }} +{% endblock %} +{% block content %} +
+ +
+{% endblock %} \ No newline at end of file diff --git a/src/templates/blog/article_archives.html b/src/templates/blog/article_archives.html new file mode 100644 index 0000000..959319e --- /dev/null +++ b/src/templates/blog/article_archives.html @@ -0,0 +1,60 @@ +{% 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/src/templates/blog/article_detail.html b/src/templates/blog/article_detail.html new file mode 100755 index 0000000..a74a0db --- /dev/null +++ b/src/templates/blog/article_detail.html @@ -0,0 +1,52 @@ +{% 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/src/templates/blog/article_index.html b/src/templates/blog/article_index.html new file mode 100644 index 0000000..0ee6150 --- /dev/null +++ b/src/templates/blog/article_index.html @@ -0,0 +1,42 @@ +{% 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/src/templates/blog/error_page.html b/src/templates/blog/error_page.html new file mode 100644 index 0000000..d41cfb6 --- /dev/null +++ b/src/templates/blog/error_page.html @@ -0,0 +1,45 @@ +{% 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/src/templates/blog/links_list.html b/src/templates/blog/links_list.html new file mode 100644 index 0000000..ccecbea --- /dev/null +++ b/src/templates/blog/links_list.html @@ -0,0 +1,44 @@ +{% 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/src/templates/blog/tags/article_info.html b/src/templates/blog/tags/article_info.html new file mode 100644 index 0000000..7af7617 --- /dev/null +++ b/src/templates/blog/tags/article_info.html @@ -0,0 +1,74 @@ +{% 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 %} + {% render_article_content article True %} +

Read more

+ {% else %} + + {% if article.show_toc %} + {% get_markdown_toc article.body as toc %} + {% trans 'toc' %}: + {{ toc|safe }} + +
+ {% endif %} +
+ + {% render_article_content article False %} + +
+ {% endif %} + +
+ + {% load_article_metas article user %} + +
\ No newline at end of file diff --git a/src/templates/blog/tags/article_meta_info.html b/src/templates/blog/tags/article_meta_info.html new file mode 100644 index 0000000..cb6111c --- /dev/null +++ b/src/templates/blog/tags/article_meta_info.html @@ -0,0 +1,59 @@ +{% load i18n %} +{% load blog_tags %} + + + + + diff --git a/src/templates/blog/tags/article_pagination.html b/src/templates/blog/tags/article_pagination.html new file mode 100644 index 0000000..95514ff --- /dev/null +++ b/src/templates/blog/tags/article_pagination.html @@ -0,0 +1,17 @@ +{% load i18n %} + \ No newline at end of file diff --git a/src/templates/blog/tags/article_tag_list.html b/src/templates/blog/tags/article_tag_list.html new file mode 100644 index 0000000..c8ba474 --- /dev/null +++ b/src/templates/blog/tags/article_tag_list.html @@ -0,0 +1,19 @@ +{% 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/src/templates/blog/tags/breadcrumb.html b/src/templates/blog/tags/breadcrumb.html new file mode 100644 index 0000000..67087d5 --- /dev/null +++ b/src/templates/blog/tags/breadcrumb.html @@ -0,0 +1,19 @@ + + diff --git a/src/templates/blog/tags/sidebar.html b/src/templates/blog/tags/sidebar.html new file mode 100755 index 0000000..f70544c --- /dev/null +++ b/src/templates/blog/tags/sidebar.html @@ -0,0 +1,136 @@ +{% load blog_tags %} +{% load i18n %} + diff --git a/src/templates/comments/tags/comment_item.html b/src/templates/comments/tags/comment_item.html new file mode 100644 index 0000000..ebb0388 --- /dev/null +++ b/src/templates/comments/tags/comment_item.html @@ -0,0 +1,34 @@ +{% load blog_tags %} +
  • +
    + + + +

    {{ comment_item.body|escape|comment_markdown }}

    + +
    + +
  • \ No newline at end of file diff --git a/src/templates/comments/tags/comment_item_tree.html b/src/templates/comments/tags/comment_item_tree.html new file mode 100644 index 0000000..a9decd1 --- /dev/null +++ b/src/templates/comments/tags/comment_item_tree.html @@ -0,0 +1,54 @@ +{% 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/src/templates/comments/tags/comment_list.html b/src/templates/comments/tags/comment_list.html new file mode 100644 index 0000000..4092161 --- /dev/null +++ b/src/templates/comments/tags/comment_list.html @@ -0,0 +1,45 @@ + +
    + {% 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/src/templates/comments/tags/post_comment.html b/src/templates/comments/tags/post_comment.html new file mode 100644 index 0000000..3ae5a27 --- /dev/null +++ b/src/templates/comments/tags/post_comment.html @@ -0,0 +1,33 @@ +
    + +
    +

    发表评论 + +

    +
    {% 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/src/templates/oauth/bindsuccess.html b/src/templates/oauth/bindsuccess.html new file mode 100644 index 0000000..4bee77c --- /dev/null +++ b/src/templates/oauth/bindsuccess.html @@ -0,0 +1,22 @@ +{% extends 'share_layout/base.html' %} +{% block header %} + {{ title }} +{% endblock %} +{% block content %} +
    +
    + +
    + +

    {{ content }}

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

    + 登录 +

    + +
    +{% endblock %} \ No newline at end of file diff --git a/src/templates/owntracks/show_log_dates.html b/src/templates/owntracks/show_log_dates.html new file mode 100644 index 0000000..7dbba21 --- /dev/null +++ b/src/templates/owntracks/show_log_dates.html @@ -0,0 +1,17 @@ + + + + + 记录日期 + + + +
      + {% for date in results %} +
    • + {{ date }} +
    • + {% endfor %} +
    + + \ No newline at end of file diff --git a/src/templates/owntracks/show_maps.html b/src/templates/owntracks/show_maps.html new file mode 100644 index 0000000..3aeda36 --- /dev/null +++ b/src/templates/owntracks/show_maps.html @@ -0,0 +1,135 @@ + + + + + + + 运动轨迹 + + + +
    + + + + + + + + \ No newline at end of file diff --git a/src/templates/search/indexes/blog/article_text.txt b/src/templates/search/indexes/blog/article_text.txt new file mode 100644 index 0000000..4f9ca76 --- /dev/null +++ b/src/templates/search/indexes/blog/article_text.txt @@ -0,0 +1,3 @@ +{{ object.title }} +{{ object.author.username }} +{{ object.body }} \ No newline at end of file diff --git a/src/templates/search/search.html b/src/templates/search/search.html new file mode 100644 index 0000000..1404c60 --- /dev/null +++ b/src/templates/search/search.html @@ -0,0 +1,66 @@ +{% 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/src/templates/share_layout/adsense.html b/src/templates/share_layout/adsense.html new file mode 100644 index 0000000..8f99c55 --- /dev/null +++ b/src/templates/share_layout/adsense.html @@ -0,0 +1,6 @@ + \ No newline at end of file diff --git a/src/templates/share_layout/base.html b/src/templates/share_layout/base.html new file mode 100644 index 0000000..32d5952 --- /dev/null +++ b/src/templates/share_layout/base.html @@ -0,0 +1,113 @@ +{% load static %} +{% load cache %} +{% load i18n %} +{% load compress %} + + + + + + + + + + + + {% load blog_tags %} + {% head_meta %} + {% block header %} + + {% endblock %} + + + + + + + + + + + + + + + + + + + + + + {% 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/src/templates/share_layout/base_account.html b/src/templates/share_layout/base_account.html new file mode 100644 index 0000000..c00d842 --- /dev/null +++ b/src/templates/share_layout/base_account.html @@ -0,0 +1,47 @@ + + + + {% 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/src/templates/share_layout/footer.html b/src/templates/share_layout/footer.html new file mode 100644 index 0000000..cd86a29 --- /dev/null +++ b/src/templates/share_layout/footer.html @@ -0,0 +1,56 @@ + + + diff --git a/src/templates/share_layout/nav.html b/src/templates/share_layout/nav.html new file mode 100644 index 0000000..24d4da6 --- /dev/null +++ b/src/templates/share_layout/nav.html @@ -0,0 +1,30 @@ +{% load i18n %} + + \ No newline at end of file diff --git a/src/templates/share_layout/nav_node.html b/src/templates/share_layout/nav_node.html new file mode 100644 index 0000000..c266880 --- /dev/null +++ b/src/templates/share_layout/nav_node.html @@ -0,0 +1,19 @@ + + +

    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 I_i_@% literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..4b60ed415ee1915c729c65d0d5adbe84280a9212 GIT binary patch literal 25389 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 G@xK6XDn8`^ literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2 b/src/blog/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..d2140906a1285bd433655d8a2c8333b880cc61a5 GIT binary patch literal 17652 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 literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/img/avatar.png b/src/blog/static/blog/img/avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..320756f0defe537f858c10086b44e6c011bd3161 GIT binary patch literal 1493 zcmaJ>do+}J82)`@m@yOkO}VvHh#Hpd6S)CwpZ7h_``2@x3>W$V1;UpE0DuDR zpuL+E>;5jhw1L=4<6bFXPue=$0#JI5xN=-p$~A)yx;X<7zXJd?1ptY3g^mLdg8(qe z0KoEl094Q9KXctDJzxS49k2)MY1dl0QihABIa6`tI1(g7j?$q2B>gR*+1q-sdOq~= zZHDd;ugEo zK3-TU^N5q10^t`uKCK9iOmncuy8!3D>blZu_vRi0Ft{Z8n47!YS#JtqXD;8jXH)t# z!C;@Lv5LyE0B%iPCA2mkP_v1n&*L8AZfyX5d{N*&qP#uGY&~XUOq4&bJtbt>j%Z^b z;Lvt`LBv9x0iMh9o>!~@C~?;nULXrhH#dP=ae!Pc_#xFQFw#z@Xx7`t>}EX%nWzDN z6c>>Dd*o`Lcny02oJrn^5;rNiNFEpV;u|330$XM&7=xj&O^x;Jav#J!kOj#N475rW z%vz{Xf^kM~U;-mT8;#{$Rx8v-wMr21?OqZXgXO?GvP@MT0=7pfu>G5?Q9Jys4&mt~ zN3A;k1@oeeivS;KhnhOb8vKa!vtbhx*QhIVm9C|@cV0z1OTFkbk>Xz|z$ryB%0JR% zE6~mzp0D}>%jx%iBvj2Kzgk8w`C(`Ai;lDxY1}{f)$UiJE`4%o+G#@0TkVX-R~@^4 zo58{6?J-(6o~yVC)8_V^iWk%iqE-_%%IgLL0-r5;iW4p;@PzuO_N$5wsLVRwOl7@j z_Rm~Gz<9;$Vb7k3;_kX(533MNTN37j>30Uz8$L42dyf($TuaLk^-xyX>tN!fzzd$( zs(e~<=;XSbZp4F$cAw$V7(x0T!*BM~+L_!BSy2g7R}Qzj#~-;du{|s7w#u}>*}HO_ zT|ju5xYvhhQoPQt=F{PHMT(}*o5H6t`wOzB$ya9a1lt6$P%Aw)yLetFa|O;UU7q3= z7)sc;)h6Wgj_^1z;wWN^X40~9<23r2iGpVAM}9ykd=*d9bNtl2ul;bEN$~eSah^)~AYD7l@a(M$z8>P-`Ke*LwR zUR~wPH!;V0P1~l8p_^x$Z3ts!#=W_oR+)F)8oXT3l|BFT8DWrm2BT z?>o}mlFadqe~x8KHil+1Mn`XUbQYBRF1{~$PKeo?D7-t?@ipI5sKQ*GHswT{jIn6E zu*yHDV!K|?c#ZlSb*2`>xegIjK0w7ELXgmE`Ncbr#qttVfJEY((vt zoloVIY!x-lnA!s`&E)}35ms(l#XNygBdQkak|SB^P|Aeh+Ios4`MQ-C;mc317|E`@hinEQ(Mjw1#P4 z2`?|vPZmhCPdA!+G&=Zr^a;zLb0?$#b|bq@3=uO!BQp=g$PzKOG(o;X5K9EH dCSXPehla%cx1eM)_pnp|XjHoWV>`yhzW{rieeD1M literal 0 HcmV?d00001 diff --git a/src/blog/static/blog/img/icon-sn.svg b/src/blog/static/blog/img/icon-sn.svg new file mode 100644 index 0000000..2c2da0a --- /dev/null +++ b/src/blog/static/blog/img/icon-sn.svg @@ -0,0 +1 @@ +icon-sn \ No newline at end of file diff --git a/src/blog/static/blog/js/blog.js b/src/blog/static/blog/js/blog.js new file mode 100644 index 0000000..c50dd7d --- /dev/null +++ b/src/blog/static/blog/js/blog.js @@ -0,0 +1,91 @@ +/** + * Created by liangliang on 2016/11/20. + */ + + +function do_reply(parentid) { + console.log(parentid); + $("#id_parent_comment_id").val(parentid) + $("#commentform").appendTo($("#div-comment-" + parentid)); + $("#reply-title").hide(); + $("#cancel_comment").show(); +} + +function cancel_reply() { + $("#reply-title").show(); + $("#cancel_comment").hide(); + $("#id_parent_comment_id").val('') + $("#commentform").appendTo($("#respond")); +} + +NProgress.start(); +NProgress.set(0.4); +//Increment +var interval = setInterval(function () { + NProgress.inc(); +}, 1000); +$(document).ready(function () { + NProgress.done(); + clearInterval(interval); +}); + + +/** 侧边栏回到顶部 */ +var rocket = $('#rocket'); + +$(window).on('scroll', debounce(slideTopSet, 300)); + +function debounce(func, wait) { + var timeout; + return function () { + clearTimeout(timeout); + timeout = setTimeout(func, wait); + }; +} + +function slideTopSet() { + var top = $(document).scrollTop(); + + if (top > 200) { + rocket.addClass('show'); + } else { + rocket.removeClass('show'); + } +} + +$(document).on('click', '#rocket', function (event) { + rocket.addClass('move'); + $('body, html').animate({ + scrollTop: 0 + }, 800); +}); +$(document).on('animationEnd', function () { + setTimeout(function () { + rocket.removeClass('move'); + }, 400); + +}); +$(document).on('webkitAnimationEnd', function () { + setTimeout(function () { + rocket.removeClass('move'); + }, 400); +}); + + +window.onload = function () { + var replyLinks = document.querySelectorAll(".comment-reply-link"); + for (var i = 0; i < replyLinks.length; i++) { + replyLinks[i].onclick = function () { + var pk = this.getAttribute("data-pk"); + do_reply(pk); + }; + } +}; + +// $(document).ready(function () { +// var form = $('#i18n-form'); +// var selector = $('.i18n-select'); +// selector.on('change', function () { +// form.submit(); +// }); +// }); \ No newline at end of file diff --git a/src/blog/static/blog/js/html5.js b/src/blog/static/blog/js/html5.js new file mode 100644 index 0000000..6168aac --- /dev/null +++ b/src/blog/static/blog/js/html5.js @@ -0,0 +1,8 @@ +/* + HTML5 Shiv v3.7.0 | @afarkas @jdalton @jon_neal @rem | MIT/GPL2 Licensed +*/ +(function(l,f){function m(){var a=e.elements;return"string"==typeof a?a.split(" "):a}function i(a){var b=n[a[o]];b||(b={},h++,a[o]=h,n[h]=b);return b}function p(a,b,c){b||(b=f);if(g)return b.createElement(a);c||(c=i(b));b=c.cache[a]?c.cache[a].cloneNode():r.test(a)?(c.cache[a]=c.createElem(a)).cloneNode():c.createElem(a);return b.canHaveChildren&&!s.test(a)?c.frag.appendChild(b):b}function t(a,b){if(!b.cache)b.cache={},b.createElem=a.createElement,b.createFrag=a.createDocumentFragment,b.frag=b.createFrag(); +a.createElement=function(c){return!e.shivMethods?b.createElem(c):p(c,a,b)};a.createDocumentFragment=Function("h,f","return function(){var n=f.cloneNode(),c=n.createElement;h.shivMethods&&("+m().join().replace(/[\w\-]+/g,function(a){b.createElem(a);b.frag.createElement(a);return'c("'+a+'")'})+");return n}")(e,b.frag)}function q(a){a||(a=f);var b=i(a);if(e.shivCSS&&!j&&!b.hasCSS){var c,d=a;c=d.createElement("p");d=d.getElementsByTagName("head")[0]||d.documentElement;c.innerHTML="x"; +c=d.insertBefore(c.lastChild,d.firstChild);b.hasCSS=!!c}g||t(a,b);return a}var k=l.html5||{},s=/^<|^(?:button|map|select|textarea|object|iframe|option|optgroup)$/i,r=/^(?:a|b|code|div|fieldset|h1|h2|h3|h4|h5|h6|i|label|li|ol|p|q|span|strong|style|table|tbody|td|th|tr|ul)$/i,j,o="_html5shiv",h=0,n={},g;(function(){try{var a=f.createElement("a");a.innerHTML="";j="hidden"in a;var b;if(!(b=1==a.childNodes.length)){f.createElement("a");var c=f.createDocumentFragment();b="undefined"==typeof c.cloneNode|| +"undefined"==typeof c.createDocumentFragment||"undefined"==typeof c.createElement}g=b}catch(d){g=j=!0}})();var e={elements:k.elements||"abbr article aside audio bdi canvas data datalist details dialog figcaption figure footer header hgroup main mark meter nav output progress section summary template time video",version:"3.7.0",shivCSS:!1!==k.shivCSS,supportsUnknownElements:g,shivMethods:!1!==k.shivMethods,type:"default",shivDocument:q,createElement:p,createDocumentFragment:function(a,b){a||(a=f); +if(g)return a.createDocumentFragment();for(var b=b||i(a),c=b.frag.cloneNode(),d=0,e=m(),h=e.length;d+~]|"+M+")"+M+"*"),U=new RegExp(M+"|>"),X=new RegExp(F),V=new RegExp("^"+I+"$"),G={ID:new RegExp("^#("+I+")"),CLASS:new RegExp("^\\.("+I+")"),TAG:new RegExp("^("+I+"|[*])"),ATTR:new RegExp("^"+W),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+R+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/HTML$/i,Q=/^(?:input|select|textarea|button)$/i,J=/^h\d$/i,K=/^[^{]+\{\s*\[native \w/,Z=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ee=/[+~]/,te=new RegExp("\\\\[\\da-fA-F]{1,6}"+M+"?|\\\\([^\\r\\n\\f])","g"),ne=function(e,t){var n="0x"+e.slice(1)-65536;return t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},re=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,ie=function(e,t){return t?"\0"===e?"\ufffd":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},oe=function(){T()},ae=be(function(e){return!0===e.disabled&&"fieldset"===e.nodeName.toLowerCase()},{dir:"parentNode",next:"legend"});try{H.apply(t=O.call(p.childNodes),p.childNodes),t[p.childNodes.length].nodeType}catch(e){H={apply:t.length?function(e,t){L.apply(e,O.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function se(t,e,n,r){var i,o,a,s,u,l,c,f=e&&e.ownerDocument,p=e?e.nodeType:9;if(n=n||[],"string"!=typeof t||!t||1!==p&&9!==p&&11!==p)return n;if(!r&&(T(e),e=e||C,E)){if(11!==p&&(u=Z.exec(t)))if(i=u[1]){if(9===p){if(!(a=e.getElementById(i)))return n;if(a.id===i)return n.push(a),n}else if(f&&(a=f.getElementById(i))&&y(e,a)&&a.id===i)return n.push(a),n}else{if(u[2])return H.apply(n,e.getElementsByTagName(t)),n;if((i=u[3])&&d.getElementsByClassName&&e.getElementsByClassName)return H.apply(n,e.getElementsByClassName(i)),n}if(d.qsa&&!N[t+" "]&&(!v||!v.test(t))&&(1!==p||"object"!==e.nodeName.toLowerCase())){if(c=t,f=e,1===p&&(U.test(t)||z.test(t))){(f=ee.test(t)&&ye(e.parentNode)||e)===e&&d.scope||((s=e.getAttribute("id"))?s=s.replace(re,ie):e.setAttribute("id",s=S)),o=(l=h(t)).length;while(o--)l[o]=(s?"#"+s:":scope")+" "+xe(l[o]);c=l.join(",")}try{return H.apply(n,f.querySelectorAll(c)),n}catch(e){N(t,!0)}finally{s===S&&e.removeAttribute("id")}}}return g(t.replace($,"$1"),e,n,r)}function ue(){var r=[];return function e(t,n){return r.push(t+" ")>b.cacheLength&&delete e[r.shift()],e[t+" "]=n}}function le(e){return e[S]=!0,e}function ce(e){var t=C.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function fe(e,t){var n=e.split("|"),r=n.length;while(r--)b.attrHandle[n[r]]=t}function pe(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function de(t){return function(e){return"input"===e.nodeName.toLowerCase()&&e.type===t}}function he(n){return function(e){var t=e.nodeName.toLowerCase();return("input"===t||"button"===t)&&e.type===n}}function ge(t){return function(e){return"form"in e?e.parentNode&&!1===e.disabled?"label"in e?"label"in e.parentNode?e.parentNode.disabled===t:e.disabled===t:e.isDisabled===t||e.isDisabled!==!t&&ae(e)===t:e.disabled===t:"label"in e&&e.disabled===t}}function ve(a){return le(function(o){return o=+o,le(function(e,t){var n,r=a([],e.length,o),i=r.length;while(i--)e[n=r[i]]&&(e[n]=!(t[n]=e[n]))})})}function ye(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}for(e in d=se.support={},i=se.isXML=function(e){var t=e&&e.namespaceURI,n=e&&(e.ownerDocument||e).documentElement;return!Y.test(t||n&&n.nodeName||"HTML")},T=se.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:p;return r!=C&&9===r.nodeType&&r.documentElement&&(a=(C=r).documentElement,E=!i(C),p!=C&&(n=C.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",oe,!1):n.attachEvent&&n.attachEvent("onunload",oe)),d.scope=ce(function(e){return a.appendChild(e).appendChild(C.createElement("div")),"undefined"!=typeof e.querySelectorAll&&!e.querySelectorAll(":scope fieldset div").length}),d.attributes=ce(function(e){return e.className="i",!e.getAttribute("className")}),d.getElementsByTagName=ce(function(e){return e.appendChild(C.createComment("")),!e.getElementsByTagName("*").length}),d.getElementsByClassName=K.test(C.getElementsByClassName),d.getById=ce(function(e){return a.appendChild(e).id=S,!C.getElementsByName||!C.getElementsByName(S).length}),d.getById?(b.filter.ID=function(e){var t=e.replace(te,ne);return function(e){return e.getAttribute("id")===t}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n=t.getElementById(e);return n?[n]:[]}}):(b.filter.ID=function(e){var n=e.replace(te,ne);return function(e){var t="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return t&&t.value===n}},b.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&E){var n,r,i,o=t.getElementById(e);if(o){if((n=o.getAttributeNode("id"))&&n.value===e)return[o];i=t.getElementsByName(e),r=0;while(o=i[r++])if((n=o.getAttributeNode("id"))&&n.value===e)return[o]}return[]}}),b.find.TAG=d.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):d.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},b.find.CLASS=d.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&E)return t.getElementsByClassName(e)},s=[],v=[],(d.qsa=K.test(C.querySelectorAll))&&(ce(function(e){var t;a.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&v.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||v.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll("[id~="+S+"-]").length||v.push("~="),(t=C.createElement("input")).setAttribute("name",""),e.appendChild(t),e.querySelectorAll("[name='']").length||v.push("\\["+M+"*name"+M+"*="+M+"*(?:''|\"\")"),e.querySelectorAll(":checked").length||v.push(":checked"),e.querySelectorAll("a#"+S+"+*").length||v.push(".#.+[+~]"),e.querySelectorAll("\\\f"),v.push("[\\r\\n\\f]")}),ce(function(e){e.innerHTML="";var t=C.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&v.push("name"+M+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&v.push(":enabled",":disabled"),a.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&v.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),v.push(",.*:")})),(d.matchesSelector=K.test(c=a.matches||a.webkitMatchesSelector||a.mozMatchesSelector||a.oMatchesSelector||a.msMatchesSelector))&&ce(function(e){d.disconnectedMatch=c.call(e,"*"),c.call(e,"[s!='']:x"),s.push("!=",F)}),v=v.length&&new RegExp(v.join("|")),s=s.length&&new RegExp(s.join("|")),t=K.test(a.compareDocumentPosition),y=t||K.test(a.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},j=t?function(e,t){if(e===t)return l=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n||(1&(n=(e.ownerDocument||e)==(t.ownerDocument||t)?e.compareDocumentPosition(t):1)||!d.sortDetached&&t.compareDocumentPosition(e)===n?e==C||e.ownerDocument==p&&y(p,e)?-1:t==C||t.ownerDocument==p&&y(p,t)?1:u?P(u,e)-P(u,t):0:4&n?-1:1)}:function(e,t){if(e===t)return l=!0,0;var n,r=0,i=e.parentNode,o=t.parentNode,a=[e],s=[t];if(!i||!o)return e==C?-1:t==C?1:i?-1:o?1:u?P(u,e)-P(u,t):0;if(i===o)return pe(e,t);n=e;while(n=n.parentNode)a.unshift(n);n=t;while(n=n.parentNode)s.unshift(n);while(a[r]===s[r])r++;return r?pe(a[r],s[r]):a[r]==p?-1:s[r]==p?1:0}),C},se.matches=function(e,t){return se(e,null,null,t)},se.matchesSelector=function(e,t){if(T(e),d.matchesSelector&&E&&!N[t+" "]&&(!s||!s.test(t))&&(!v||!v.test(t)))try{var n=c.call(e,t);if(n||d.disconnectedMatch||e.document&&11!==e.document.nodeType)return n}catch(e){N(t,!0)}return 0":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(te,ne),e[3]=(e[3]||e[4]||e[5]||"").replace(te,ne),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||se.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&se.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return G.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&X.test(n)&&(t=h(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(te,ne).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=m[e+" "];return t||(t=new RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&m(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(n,r,i){return function(e){var t=se.attr(e,n);return null==t?"!="===r:!r||(t+="","="===r?t===i:"!="===r?t!==i:"^="===r?i&&0===t.indexOf(i):"*="===r?i&&-1:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;function j(e,n,r){return m(n)?S.grep(e,function(e,t){return!!n.call(e,t,e)!==r}):n.nodeType?S.grep(e,function(e){return e===n!==r}):"string"!=typeof n?S.grep(e,function(e){return-1)[^>]*|#([\w-]+))$/;(S.fn.init=function(e,t,n){var r,i;if(!e)return this;if(n=n||D,"string"==typeof e){if(!(r="<"===e[0]&&">"===e[e.length-1]&&3<=e.length?[null,e,null]:q.exec(e))||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof S?t[0]:t,S.merge(this,S.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:E,!0)),N.test(r[1])&&S.isPlainObject(t))for(r in t)m(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return(i=E.getElementById(r[2]))&&(this[0]=i,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):m(e)?void 0!==n.ready?n.ready(e):e(S):S.makeArray(e,this)}).prototype=S.fn,D=S(E);var L=/^(?:parents|prev(?:Until|All))/,H={children:!0,contents:!0,next:!0,prev:!0};function O(e,t){while((e=e[t])&&1!==e.nodeType);return e}S.fn.extend({has:function(e){var t=S(e,this),n=t.length;return this.filter(function(){for(var e=0;e\x20\t\r\n\f]*)/i,he=/^$|^module$|\/(?:java|ecma)script/i;ce=E.createDocumentFragment().appendChild(E.createElement("div")),(fe=E.createElement("input")).setAttribute("type","radio"),fe.setAttribute("checked","checked"),fe.setAttribute("name","t"),ce.appendChild(fe),y.checkClone=ce.cloneNode(!0).cloneNode(!0).lastChild.checked,ce.innerHTML="",y.noCloneChecked=!!ce.cloneNode(!0).lastChild.defaultValue,ce.innerHTML="",y.option=!!ce.lastChild;var ge={thead:[1,"","
    "],col:[2,"","
    "],tr:[2,"","
    "],td:[3,"","
    "],_default:[0,"",""]};function ve(e,t){var n;return n="undefined"!=typeof e.getElementsByTagName?e.getElementsByTagName(t||"*"):"undefined"!=typeof e.querySelectorAll?e.querySelectorAll(t||"*"):[],void 0===t||t&&A(e,t)?S.merge([e],n):n}function ye(e,t){for(var n=0,r=e.length;n",""]);var me=/<|&#?\w+;/;function xe(e,t,n,r,i){for(var o,a,s,u,l,c,f=t.createDocumentFragment(),p=[],d=0,h=e.length;d\s*$/g;function je(e,t){return A(e,"table")&&A(11!==t.nodeType?t:t.firstChild,"tr")&&S(e).children("tbody")[0]||e}function De(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function qe(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function Le(e,t){var n,r,i,o,a,s;if(1===t.nodeType){if(Y.hasData(e)&&(s=Y.get(e).events))for(i in Y.remove(t,"handle events"),s)for(n=0,r=s[i].length;n").attr(n.scriptAttrs||{}).prop({charset:n.scriptCharset,src:n.url}).on("load error",i=function(e){r.remove(),i=null,e&&t("error"===e.type?404:200,e.type)}),E.head.appendChild(r[0])},abort:function(){i&&i()}}});var _t,zt=[],Ut=/(=)\?(?=&|$)|\?\?/;S.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=zt.pop()||S.expando+"_"+wt.guid++;return this[e]=!0,e}}),S.ajaxPrefilter("json jsonp",function(e,t,n){var r,i,o,a=!1!==e.jsonp&&(Ut.test(e.url)?"url":"string"==typeof e.data&&0===(e.contentType||"").indexOf("application/x-www-form-urlencoded")&&Ut.test(e.data)&&"data");if(a||"jsonp"===e.dataTypes[0])return r=e.jsonpCallback=m(e.jsonpCallback)?e.jsonpCallback():e.jsonpCallback,a?e[a]=e[a].replace(Ut,"$1"+r):!1!==e.jsonp&&(e.url+=(Tt.test(e.url)?"&":"?")+e.jsonp+"="+r),e.converters["script json"]=function(){return o||S.error(r+" was not called"),o[0]},e.dataTypes[0]="json",i=C[r],C[r]=function(){o=arguments},n.always(function(){void 0===i?S(C).removeProp(r):C[r]=i,e[r]&&(e.jsonpCallback=t.jsonpCallback,zt.push(r)),o&&m(i)&&i(o[0]),o=i=void 0}),"script"}),y.createHTMLDocument=((_t=E.implementation.createHTMLDocument("").body).innerHTML="