diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md
deleted file mode 100644
index 2b5b7aa..0000000
--- a/.github/ISSUE_TEMPLATE.md
+++ /dev/null
@@ -1,18 +0,0 @@
-
-
-**我确定我已经查看了** (标注`[ ]`为`[x]`)
-
-- [ ] [DjangoBlog的readme](https://github.com/liangliangyy/DjangoBlog/blob/master/README.md)
-- [ ] [配置说明](https://github.com/liangliangyy/DjangoBlog/blob/master/bin/config.md)
-- [ ] [其他 Issues](https://github.com/liangliangyy/DjangoBlog/issues)
-
-----
-
-**我要申请** (标注`[ ]`为`[x]`)
-
-- [ ] BUG 反馈
-- [ ] 添加新的特性或者功能
-- [ ] 请求技术支持
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
deleted file mode 100644
index 6b76522..0000000
--- a/.github/workflows/codeql-analysis.yml
+++ /dev/null
@@ -1,47 +0,0 @@
-name: "CodeQL"
-
-on:
- push:
- branches:
- - master
- - dev
- paths-ignore:
- - '**/*.md'
- - '**/*.css'
- - '**/*.js'
- - '**/*.yml'
- - '**/*.txt'
- pull_request:
- branches:
- - master
- - dev
- paths-ignore:
- - '**/*.md'
- - '**/*.css'
- - '**/*.js'
- - '**/*.yml'
- - '**/*.txt'
- schedule:
- - cron: '30 1 * * 0'
-
-
-jobs:
- CodeQL-Build:
- runs-on: ubuntu-latest
- permissions:
- security-events: write
- actions: read
- contents: read
-
- steps:
- - name: Checkout repository
- uses: actions/checkout@v3
-
- - name: Initialize CodeQL
- uses: github/codeql-action/init@v2
-
- - name: Autobuild
- uses: github/codeql-action/autobuild@v2
-
- - name: Perform CodeQL Analysis
- uses: github/codeql-action/analyze@v2
\ No newline at end of file
diff --git a/.github/workflows/django.yml b/.github/workflows/django.yml
deleted file mode 100644
index 94baea9..0000000
--- a/.github/workflows/django.yml
+++ /dev/null
@@ -1,136 +0,0 @@
-name: Django CI
-
-on:
- push:
- branches:
- - master
- - dev
- paths-ignore:
- - '**/*.md'
- - '**/*.css'
- - '**/*.js'
- pull_request:
- branches:
- - master
- - dev
- paths-ignore:
- - '**/*.md'
- - '**/*.css'
- - '**/*.js'
-
-jobs:
- build-normal:
- runs-on: ubuntu-latest
- strategy:
- max-parallel: 4
- matrix:
- python-version: ["3.10","3.11" ]
-
- steps:
- - name: Start MySQL
- uses: samin/mysql-action@v1.3
- with:
- host port: 3306
- container port: 3306
- character set server: utf8mb4
- collation server: utf8mb4_general_ci
- mysql version: latest
- mysql root password: root
- mysql database: djangoblog
- mysql user: root
- mysql password: root
-
- - uses: actions/checkout@v3
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v4
- with:
- python-version: ${{ matrix.python-version }}
- cache: 'pip'
- - name: Install Dependencies
- run: |
- python -m pip install --upgrade pip
- pip install -r requirements.txt
- - name: Run Tests
- env:
- DJANGO_MYSQL_PASSWORD: root
- DJANGO_MYSQL_HOST: 127.0.0.1
- run: |
- python manage.py makemigrations
- python manage.py migrate
- python manage.py test
-
- build-with-es:
- runs-on: ubuntu-latest
- strategy:
- max-parallel: 4
- matrix:
- python-version: ["3.10","3.11" ]
-
- steps:
- - name: Start MySQL
- uses: samin/mysql-action@v1.3
- with:
- host port: 3306
- container port: 3306
- character set server: utf8mb4
- collation server: utf8mb4_general_ci
- mysql version: latest
- mysql root password: root
- mysql database: djangoblog
- mysql user: root
- mysql password: root
-
- - name: Configure sysctl limits
- run: |
- sudo swapoff -a
- sudo sysctl -w vm.swappiness=1
- sudo sysctl -w fs.file-max=262144
- sudo sysctl -w vm.max_map_count=262144
-
- - uses: miyataka/elasticsearch-github-actions@1
-
- with:
- stack-version: '7.12.1'
- plugins: 'https://release.infinilabs.com/analysis-ik/stable/elasticsearch-analysis-ik-7.12.1.zip'
-
-
- - uses: actions/checkout@v3
- - name: Set up Python ${{ matrix.python-version }}
- uses: actions/setup-python@v4
- with:
- python-version: ${{ matrix.python-version }}
- cache: 'pip'
- - name: Install Dependencies
- run: |
- python -m pip install --upgrade pip
- pip install -r requirements.txt
- - name: Run Tests
- env:
- DJANGO_MYSQL_PASSWORD: root
- DJANGO_MYSQL_HOST: 127.0.0.1
- DJANGO_ELASTICSEARCH_HOST: 127.0.0.1:9200
- run: |
- python manage.py makemigrations
- python manage.py migrate
- coverage run manage.py test
- coverage xml
-
- - name: Upload coverage to Codecov
- uses: codecov/codecov-action@v1
-
- docker:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v2
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
-
- - name: Build and push
- uses: docker/build-push-action@v3
- with:
- context: .
- push: false
- tags: djangoblog/djangoblog:dev
diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml
deleted file mode 100644
index a312e2f..0000000
--- a/.github/workflows/docker.yml
+++ /dev/null
@@ -1,43 +0,0 @@
-name: docker
-
-on:
- push:
- paths-ignore:
- - '**/*.md'
- - '**/*.yml'
- branches:
- - 'master'
- - 'dev'
-
-jobs:
- docker:
- runs-on: ubuntu-latest
- steps:
- - name: Set env to docker dev tag
- if: endsWith(github.ref, '/dev')
- run: |
- echo "DOCKER_TAG=test" >> $GITHUB_ENV
- - name: Set env to docker latest tag
- if: endsWith(github.ref, '/master')
- run: |
- echo "DOCKER_TAG=latest" >> $GITHUB_ENV
- - name: Checkout
- uses: actions/checkout@v3
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
-
- - name: Login to DockerHub
- uses: docker/login-action@v2
- with:
- username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_TOKEN }}
- - name: Build and push
- uses: docker/build-push-action@v3
- with:
- context: .
- push: true
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/djangoblog:${{env.DOCKER_TAG}}
-
-
diff --git a/.github/workflows/publish-release.yml b/.github/workflows/publish-release.yml
deleted file mode 100644
index 5eb0853..0000000
--- a/.github/workflows/publish-release.yml
+++ /dev/null
@@ -1,39 +0,0 @@
-name: publish release
-
-on:
- release:
- types: [ published ]
-
-jobs:
- docker:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout
- uses: actions/checkout@v3
-
- - name: Docker meta
- id: meta
- uses: docker/metadata-action@v3
- with:
- images: name/app
- - name: Set up QEMU
- uses: docker/setup-qemu-action@v2
- - name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
- - name: Login to DockerHub
- uses: docker/login-action@v2
- with:
- username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_TOKEN }}
- - name: Build and push
- uses: docker/build-push-action@v3
- with:
- context: .
- push: true
- platforms: |
- linux/amd64
- linux/arm64
- linux/arm/v7
- linux/arm/v6
- linux/386
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/djangoblog:${{ github.event.release.tag_name }}
diff --git a/accounts--dyh/__init__.py b/accounts--dyh/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/accounts--dyh/accounts质量分析.docx b/accounts--dyh/accounts质量分析.docx
deleted file mode 100644
index 50a57f2..0000000
Binary files a/accounts--dyh/accounts质量分析.docx and /dev/null differ
diff --git a/accounts--dyh/admin-new.py b/accounts--dyh/admin-new.py
deleted file mode 100644
index 29d162a..0000000
--- a/accounts--dyh/admin-new.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from django import forms
-from django.contrib.auth.admin import UserAdmin
-from django.contrib.auth.forms import UserChangeForm
-from django.contrib.auth.forms import UsernameField
-from django.utils.translation import gettext_lazy as _
-
-# Register your models here.
-from .models import BlogUser
-
-
-class BlogUserCreationForm(forms.ModelForm):
- password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
- password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
-
- class Meta:
- model = BlogUser
- fields = ('email',)
-
- def clean_password2(self):
- # Check that the two password entries match
- password1 = self.cleaned_data.get("password1")
- password2 = self.cleaned_data.get("password2")
- if password1 and password2 and password1 != password2:
- raise forms.ValidationError(_("passwords do not match"))
- return password2
-
- def save(self, commit=True):
- # Save the provided password in hashed format
- user = super().save(commit=False)
- user.set_password(self.cleaned_data["password1"])
- if commit:
- user.source = 'adminsite'
- user.save()
- return user
-
-
-class BlogUserChangeForm(UserChangeForm):
- class Meta:
- model = BlogUser
- fields = '__all__'
- field_classes = {'username': UsernameField}
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
-
-class BlogUserAdmin(UserAdmin):
- form = BlogUserChangeForm
- add_form = BlogUserCreationForm
- list_display = (
- 'id',
- 'nickname',
- 'username',
- 'email',
- 'last_login',
- 'date_joined',
- 'source')
- list_display_links = ('id', 'username')
- ordering = ('-id',)
- search_fields = ('username', 'nickname', 'email')
diff --git a/accounts--dyh/admin.py b/accounts--dyh/admin.py
deleted file mode 100644
index ae53414..0000000
--- a/accounts--dyh/admin.py
+++ /dev/null
@@ -1,103 +0,0 @@
-# 导入Django表单模块
-from django import forms
-# 导入Django默认用户管理类
-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 _
-
-# 注册模型到管理后台
-# 导入自定义用户模型
-from .models import BlogUser
-
-
-class BlogUserCreationForm(forms.ModelForm):
- """自定义用户创建表单,用于管理员后台创建用户"""
-
- # 密码字段1 - 输入密码
- password1 = forms.CharField(
- label=_('password'), # 字段标签:密码
- widget=forms.PasswordInput # 使用密码输入控件
- )
- # 密码字段2 - 确认密码
- password2 = forms.CharField(
- label=_('Enter password again'), # 字段标签:再次输入密码
- widget=forms.PasswordInput # 使用密码输入控件
- )
-
- class Meta:
- # 指定使用的模型
- model = BlogUser
- # 表单包含的字段:仅邮箱
- fields = ('email',)
-
- def clean_password2(self):
- """清理和验证密码确认字段"""
- # 从已清理数据中获取密码1
- password1 = self.cleaned_data.get("password1")
- # 从已清理数据中获取密码2
- password2 = self.cleaned_data.get("password2")
- # 检查两个密码是否存在且匹配
- if password1 and password2 and password1 != password2:
- # 如果不匹配,抛出验证错误
- raise forms.ValidationError(_("passwords do not match"))
- # 返回验证通过的密码2
- return password2
-
- def save(self, commit=True):
- """保存用户实例,处理密码哈希"""
- # 调用父类save方法但不立即提交到数据库
- user = super().save(commit=False)
- # 使用Django的密码哈希方法设置密码
- 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):
- """自定义用户管理类,配置Django管理后台的用户界面"""
-
- # 指定修改用户时使用的表单
- form = BlogUserChangeForm
- # 指定创建用户时使用的表单
- add_form = BlogUserCreationForm
- # 列表页面显示的字段
- list_display = (
- 'id', # 用户ID
- 'nickname', # 昵称
- 'username', # 用户名
- 'email', # 邮箱
- 'last_login', # 最后登录时间
- 'date_joined', # 注册时间
- 'source' # 用户来源
- )
- # 列表中可作为链接点击的字段
- list_display_links = ('id', 'username')
- # 默认排序字段:按ID降序排列
- ordering = ('-id',)
\ No newline at end of file
diff --git a/accounts--dyh/apps-new.py b/accounts--dyh/apps-new.py
deleted file mode 100644
index 9b3fc5a..0000000
--- a/accounts--dyh/apps-new.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class AccountsConfig(AppConfig):
- name = 'accounts'
diff --git a/accounts--dyh/apps.py b/accounts--dyh/apps.py
deleted file mode 100644
index 75f1ad1..0000000
--- a/accounts--dyh/apps.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# 导入Django应用配置基类
-from django.apps import AppConfig
-
-
-class AccountsConfig(AppConfig):
- """账户应用的配置类"""
-
- # 应用的名称(Python路径)
- name = 'accounts'
\ No newline at end of file
diff --git a/accounts--dyh/forms-new.py b/accounts--dyh/forms-new.py
deleted file mode 100644
index 6bd73c4..0000000
--- a/accounts--dyh/forms-new.py
+++ /dev/null
@@ -1,127 +0,0 @@
-from django import forms
-from django.contrib.auth import get_user_model, password_validation
-from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
-from django.core.exceptions import ValidationError
-from django.forms import widgets
-from django.utils.translation import gettext_lazy as _
-from . import utils
-from .models import BlogUser
-
-
-class LoginForm(AuthenticationForm):
- def __init__(self, *args, **kwargs):
- super(LoginForm, self).__init__(*args, **kwargs)
- # 更新用户名字段的标签和提示信息,明确支持邮箱登录
- self.fields['username'].label = _('用户名或邮箱')
- self.fields['username'].widget = widgets.TextInput(
- attrs={
- 'placeholder': "用户名或邮箱地址",
- "class": "form-control",
- 'autocomplete': 'username'
- })
- self.fields['password'].widget = widgets.PasswordInput(
- attrs={
- 'placeholder': "密码",
- "class": "form-control",
- 'autocomplete': 'current-password'
- })
-
-
-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/accounts--dyh/forms.py b/accounts--dyh/forms.py
deleted file mode 100644
index a7bada7..0000000
--- a/accounts--dyh/forms.py
+++ /dev/null
@@ -1,194 +0,0 @@
-# 导入Django表单模块
-from django import forms
-# 导入用户模型获取函数和密码验证工具
-from django.contrib.auth import get_user_model, password_validation
-# 导入Django内置认证表单
-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):
- """用户登录表单,继承自Django内置认证表单"""
-
- def __init__(self, *args, **kwargs):
- """初始化表单,自定义字段控件"""
- # 调用父类初始化方法
- super(LoginForm, self).__init__(*args, **kwargs)
- # 自定义用户名字段控件:文本输入框,带占位符和CSS类
- self.fields['username'].widget = widgets.TextInput(
- attrs={
- 'placeholder': "username", # 输入框占位符文本
- "class": "form-control" # CSS类名,用于样式
- }
- )
- # 自定义密码字段控件:密码输入框,带占位符和CSS类
- self.fields['password'].widget = widgets.PasswordInput(
- attrs={
- 'placeholder': "password", # 输入框占位符文本
- "class": "form-control" # CSS类名,用于样式
- }
- )
-
-
-class RegisterForm(UserCreationForm):
- """用户注册表单,继承自Django内置用户创建表单"""
-
- def __init__(self, *args, **kwargs):
- """初始化表单,自定义所有字段的控件"""
- # 调用父类初始化方法
- super(RegisterForm, self).__init__(*args, **kwargs)
- # 自定义用户名字段控件
- self.fields['username'].widget = widgets.TextInput(
- attrs={
- 'placeholder': "username", # 占位符:用户名
- "class": "form-control" # CSS类
- }
- )
- # 自定义邮箱字段控件
- self.fields['email'].widget = widgets.EmailInput(
- attrs={
- 'placeholder': "email", # 占位符:邮箱
- "class": "form-control" # CSS类
- }
- )
- # 自定义密码字段控件
- self.fields['password1'].widget = widgets.PasswordInput(
- attrs={
- 'placeholder': "password", # 占位符:密码
- "class": "form-control" # CSS类
- }
- )
- # 自定义密码确认字段控件
- self.fields['password2'].widget = widgets.PasswordInput(
- attrs={
- 'placeholder': "repeat password", # 占位符:重复密码
- "class": "form-control" # CSS类
- }
- )
-
- 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):
- """忘记密码重置表单"""
-
- # 新密码字段1
- new_password1 = forms.CharField(
- label=_("New password"), # 字段标签:新密码
- widget=forms.PasswordInput( # 使用密码输入控件
- attrs={
- "class": "form-control", # CSS类
- 'placeholder': _("New password") # 占位符:新密码
- }
- ),
- )
-
- # 新密码字段2 - 确认密码
- new_password2 = forms.CharField(
- label="确认密码", # 字段标签:确认密码(硬编码中文)
- widget=forms.PasswordInput( # 使用密码输入控件
- attrs={
- "class": "form-control", # CSS类
- 'placeholder': _("Confirm password") # 占位符:确认密码
- }
- ),
- )
-
- # 邮箱字段
- email = forms.EmailField(
- label='邮箱', # 字段标签:邮箱(硬编码中文)
- widget=forms.TextInput( # 使用文本输入控件
- attrs={
- 'class': 'form-control', # CSS类
- 'placeholder': _("Email") # 占位符:邮箱
- }
- ),
- )
-
- # 验证码字段
- code = forms.CharField(
- label=_('Code'), # 字段标签:验证码
- widget=forms.TextInput( # 使用文本输入控件
- attrs={
- 'class': 'form-control', # CSS类
- 'placeholder': _("Code") # 占位符:验证码
- }
- ),
- )
-
- def clean_new_password2(self):
- """清理和验证密码确认字段"""
- # 从原始数据中获取新密码1(不使用cleaned_data因为可能还未验证)
- password1 = self.data.get("new_password1")
- # 从原始数据中获取新密码2
- password2 = self.data.get("new_password2")
- # 检查两个密码是否存在且匹配
- if password1 and password2 and password1 != password2:
- # 如果不匹配,抛出验证错误
- raise ValidationError(_("passwords do not match"))
- # 使用Django内置密码验证器验证密码强度
- 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():
- # 安全提示:这里会暴露邮箱是否注册,可根据安全需求修改
- # 如果邮箱不存在,抛出验证错误
- 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'), # 字段标签:邮箱
- )
\ No newline at end of file
diff --git a/accounts--dyh/models-new..py b/accounts--dyh/models-new..py
deleted file mode 100644
index 3baddbb..0000000
--- a/accounts--dyh/models-new..py
+++ /dev/null
@@ -1,35 +0,0 @@
-from django.contrib.auth.models import AbstractUser
-from django.db import models
-from django.urls import reverse
-from django.utils.timezone import now
-from django.utils.translation import gettext_lazy as _
-from djangoblog.utils import get_current_site
-
-
-# Create your models here.
-
-class BlogUser(AbstractUser):
- nickname = models.CharField(_('nick name'), max_length=100, blank=True)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_modify_time = models.DateTimeField(_('last modify time'), default=now)
- source = models.CharField(_('create source'), max_length=100, blank=True)
-
- def get_absolute_url(self):
- return reverse(
- 'blog:author_detail', kwargs={
- 'author_name': self.username})
-
- def __str__(self):
- return self.email
-
- def get_full_url(self):
- site = get_current_site().domain
- url = "https://{site}{path}".format(site=site,
- path=self.get_absolute_url())
- return url
-
- class Meta:
- ordering = ['-id']
- verbose_name = _('user')
- verbose_name_plural = verbose_name
- get_latest_by = 'id'
diff --git a/accounts--dyh/models.py b/accounts--dyh/models.py
deleted file mode 100644
index 0ec9c5c..0000000
--- a/accounts--dyh/models.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# 导入Django抽象用户基类
-from django.contrib.auth.models import AbstractUser
-# 导入Django数据库模型
-from django.db import models
-# 导入URL反向解析函数
-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
-
-
-# 在这里创建模型
-
-class BlogUser(AbstractUser):
- """自定义用户模型,继承自Django抽象用户基类"""
-
- # 昵称字段,最大长度100字符,允许为空
- 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 # 默认值:当前时间
- )
- # 用户来源字段,最大长度100字符,允许为空
- source = models.CharField(
- _('create source'), # 字段显示名称:创建来源
- max_length=100, # 最大长度
- blank=True # 允许为空
- )
-
- def get_absolute_url(self):
- """获取用户的绝对URL,用于生成用户详情页链接"""
- # 使用reverse反向解析URL,传入用户名作为参数
- return reverse(
- 'blog:author_detail', # URL模式名称
- kwargs={'author_name': self.username} # URL参数:作者用户名
- )
-
- def __str__(self):
- """对象的字符串表示,返回邮箱地址"""
- return self.email
-
- def get_full_url(self):
- """获取用户的完整URL(包含域名)"""
- # 获取当前站点域名
- site = get_current_site().domain
- # 构建完整URL:https://域名 + 用户详情页路径
- url = "https://{site}{path}".format(
- site=site, # 站点域名
- path=self.get_absolute_url() # 用户详情页路径
- )
- # 返回完整URL
- return url
-
- class Meta:
- """模型的元数据配置"""
- ordering = ['-id'] # 默认排序:按ID降序排列
- verbose_name = _('user') # 单数显示名称:用户
- verbose_name_plural = verbose_name # 复数显示名称:与单数相同
- get_latest_by = 'id' # 获取最新记录的依据字段:ID
\ No newline at end of file
diff --git a/accounts--dyh/tests-new..py b/accounts--dyh/tests-new..py
deleted file mode 100644
index 6893411..0000000
--- a/accounts--dyh/tests-new..py
+++ /dev/null
@@ -1,207 +0,0 @@
-from django.test import Client, RequestFactory, TestCase
-from django.urls import reverse
-from django.utils import timezone
-from django.utils.translation import gettext_lazy as _
-
-from accounts.models import BlogUser
-from blog.models import Article, Category
-from djangoblog.utils import *
-from . import utils
-
-
-# Create your tests here.
-
-class AccountTest(TestCase):
- def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
- self.blog_user = BlogUser.objects.create_user(
- username="test",
- email="admin@admin.com",
- password="12345678"
- )
- self.new_test = "xxx123--="
-
- def test_validate_account(self):
- site = get_current_site().domain
- user = BlogUser.objects.create_superuser(
- email="liangliangyy1@gmail.com",
- username="liangliangyy1",
- password="qwer!@#$ggg")
- testuser = BlogUser.objects.get(username='liangliangyy1')
-
- loginresult = self.client.login(
- username='liangliangyy1',
- password='qwer!@#$ggg')
- self.assertEqual(loginresult, True)
- response = self.client.get('/admin/')
- self.assertEqual(response.status_code, 200)
-
- category = Category()
- category.name = "categoryaaa"
- category.creation_time = timezone.now()
- category.last_modify_time = timezone.now()
- category.save()
-
- article = Article()
- article.title = "nicetitleaaa"
- article.body = "nicecontentaaa"
- article.author = user
- article.category = category
- article.type = 'a'
- article.status = 'p'
- article.save()
-
- response = self.client.get(article.get_admin_url())
- self.assertEqual(response.status_code, 200)
-
- def test_validate_register(self):
- self.assertEquals(
- 0, len(
- BlogUser.objects.filter(
- email='user123@user.com')))
- response = self.client.post(reverse('account:register'), {
- 'username': 'user1233',
- 'email': 'user123@user.com',
- 'password1': 'password123!q@wE#R$T',
- 'password2': 'password123!q@wE#R$T',
- })
- self.assertEquals(
- 1, len(
- BlogUser.objects.filter(
- email='user123@user.com')))
- user = BlogUser.objects.filter(email='user123@user.com')[0]
- sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
- path = reverse('accounts:result')
- url = '{path}?type=validation&id={id}&sign={sign}'.format(
- path=path, id=user.id, sign=sign)
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
-
- self.client.login(username='user1233', password='password123!q@wE#R$T')
- user = BlogUser.objects.filter(email='user123@user.com')[0]
- user.is_superuser = True
- user.is_staff = True
- user.save()
- delete_sidebar_cache()
- category = Category()
- category.name = "categoryaaa"
- category.creation_time = timezone.now()
- category.last_modify_time = timezone.now()
- category.save()
-
- article = Article()
- article.category = category
- article.title = "nicetitle333"
- article.body = "nicecontentttt"
- article.author = user
-
- article.type = 'a'
- article.status = 'p'
- article.save()
-
- response = self.client.get(article.get_admin_url())
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get(reverse('account:logout'))
- self.assertIn(response.status_code, [301, 302, 200])
-
- response = self.client.get(article.get_admin_url())
- self.assertIn(response.status_code, [301, 302, 200])
-
- response = self.client.post(reverse('account:login'), {
- 'username': 'user1233',
- 'password': 'password123'
- })
- self.assertIn(response.status_code, [301, 302, 200])
-
- response = self.client.get(article.get_admin_url())
- self.assertIn(response.status_code, [301, 302, 200])
-
- def test_verify_email_code(self):
- to_email = "admin@admin.com"
- code = generate_code()
- utils.set_code(to_email, code)
- utils.send_verify_email(to_email, code)
-
- err = utils.verify("admin@admin.com", code)
- self.assertEqual(err, None)
-
- err = utils.verify("admin@123.com", code)
- self.assertEqual(type(err), str)
-
- def test_forget_password_email_code_success(self):
- resp = self.client.post(
- path=reverse("account:forget_password_code"),
- data=dict(email="admin@admin.com")
- )
-
- self.assertEqual(resp.status_code, 200)
- self.assertEqual(resp.content.decode("utf-8"), "ok")
-
- def test_forget_password_email_code_fail(self):
- resp = self.client.post(
- path=reverse("account:forget_password_code"),
- data=dict()
- )
- self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
-
- resp = self.client.post(
- path=reverse("account:forget_password_code"),
- data=dict(email="admin@com")
- )
- self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
-
- def test_forget_password_email_success(self):
- code = generate_code()
- utils.set_code(self.blog_user.email, code)
- data = dict(
- new_password1=self.new_test,
- new_password2=self.new_test,
- email=self.blog_user.email,
- code=code,
- )
- resp = self.client.post(
- path=reverse("account:forget_password"),
- data=data
- )
- self.assertEqual(resp.status_code, 302)
-
- # 验证用户密码是否修改成功
- blog_user = BlogUser.objects.filter(
- email=self.blog_user.email,
- ).first() # type: BlogUser
- self.assertNotEqual(blog_user, None)
- self.assertEqual(blog_user.check_password(data["new_password1"]), True)
-
- def test_forget_password_email_not_user(self):
- data = dict(
- new_password1=self.new_test,
- new_password2=self.new_test,
- email="123@123.com",
- code="123456",
- )
- resp = self.client.post(
- path=reverse("account:forget_password"),
- data=data
- )
-
- self.assertEqual(resp.status_code, 200)
-
-
- def test_forget_password_email_code_error(self):
- code = generate_code()
- utils.set_code(self.blog_user.email, code)
- data = dict(
- new_password1=self.new_test,
- new_password2=self.new_test,
- email=self.blog_user.email,
- code="111111",
- )
- resp = self.client.post(
- path=reverse("account:forget_password"),
- data=data
- )
-
- self.assertEqual(resp.status_code, 200)
-
diff --git a/accounts--dyh/tests.py b/accounts--dyh/tests.py
deleted file mode 100644
index 32c2b7e..0000000
--- a/accounts--dyh/tests.py
+++ /dev/null
@@ -1,294 +0,0 @@
-# 导入Django测试客户端、请求工厂、测试用例
-from django.test import Client, RequestFactory, TestCase
-# 导入URL反向解析
-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
-
-
-# 在这里创建测试
-
-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')))
- # 发送注册POST请求
- 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)))
- # 构建验证URL路径
- path = reverse('accounts:result')
- # 构建完整验证URL
- url = '{path}?type=validation&id={id}&sign={sign}'.format(
- path=path, # 路径
- id=user.id, # 用户ID
- sign=sign # 签名
- )
- # 访问验证URL
- 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)
- # 断言验证成功(返回None)
- self.assertEqual(err, None)
-
- # 验证错误验证码
- err = utils.verify("admin@123.com", code)
- # 断言验证失败(返回错误信息字符串)
- self.assertEqual(type(err), str)
-
- def test_forget_password_email_code_success(self):
- """测试成功获取忘记密码验证码"""
- # 发送获取验证码的POST请求
- resp = self.client.post(
- path=reverse("account:forget_password_code"), # URL路径
- data=dict(email="admin@admin.com") # 请求数据:邮箱
- )
-
- # 断言响应状态码为200
- self.assertEqual(resp.status_code, 200)
- # 断言响应内容为"ok"
- self.assertEqual(resp.content.decode("utf-8"), "ok")
-
- def test_forget_password_email_code_fail(self):
- """测试获取忘记密码验证码失败情况"""
- # 发送空数据的POST请求
- resp = self.client.post(
- path=reverse("account:forget_password_code"), # URL路径
- data=dict() # 空数据
- )
- # 断言返回错误信息
- self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
-
- # 发送无效邮箱的POST请求
- resp = self.client.post(
- path=reverse("account:forget_password_code"), # URL路径
- 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, # 验证码
- )
- # 发送重置密码的POST请求
- resp = self.client.post(
- path=reverse("account:forget_password"), # URL路径
- data=data # 请求数据
- )
- # 断言重定向响应(状态码302)
- self.assertEqual(resp.status_code, 302)
-
- # 验证用户密码是否修改成功
- blog_user = BlogUser.objects.filter(
- email=self.blog_user.email,
- ).first() # 类型注解: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", # 验证码
- )
- # 发送重置密码的POST请求
- resp = self.client.post(
- path=reverse("account:forget_password"), # URL路径
- data=data # 请求数据
- )
-
- # 断言返回表单页面(状态码200)
- 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", # 错误的验证码
- )
- # 发送重置密码的POST请求
- resp = self.client.post(
- path=reverse("account:forget_password"), # URL路径
- data=data # 请求数据
- )
-
- # 断言返回表单页面(状态码200)
- self.assertEqual(resp.status_code, 200)
\ No newline at end of file
diff --git a/accounts--dyh/urls-new..py b/accounts--dyh/urls-new..py
deleted file mode 100644
index 107a801..0000000
--- a/accounts--dyh/urls-new..py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django.urls import path
-from django.urls import re_path
-
-from . import views
-from .forms import LoginForm
-
-app_name = "accounts"
-
-urlpatterns = [re_path(r'^login/$',
- views.LoginView.as_view(success_url='/'),
- name='login',
- kwargs={'authentication_form': LoginForm}),
- re_path(r'^register/$',
- views.RegisterView.as_view(success_url="/"),
- name='register'),
- re_path(r'^logout/$',
- views.LogoutView.as_view(),
- name='logout'),
- path(r'account/result.html',
- views.account_result,
- name='result'),
- re_path(r'^forget_password/$',
- views.ForgetPasswordView.as_view(),
- name='forget_password'),
- re_path(r'^forget_password_code/$',
- views.ForgetPasswordEmailCode.as_view(),
- name='forget_password_code'),
- ]
diff --git a/accounts--dyh/urls.py b/accounts--dyh/urls.py
deleted file mode 100644
index 4374124..0000000
--- a/accounts--dyh/urls.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# 导入URL路径函数
-from django.urls import path
-# 导入正则URL路径函数(兼容老版本)
-from django.urls import re_path
-
-# 导入当前应用的视图
-from . import views
-# 导入登录表单
-from .forms import LoginForm
-
-# 应用命名空间,用于URL反向解析
-app_name = "accounts"
-
-# URL模式列表
-urlpatterns = [
- # 登录URL - 使用正则表达式匹配 /login/ 路径
- re_path(r'^login/$',
- # 使用LoginView视图类,登录成功后重定向到首页
- views.LoginView.as_view(success_url='/'),
- name='login', # URL名称:login
- # 传入额外参数:指定认证表单类
- kwargs={'authentication_form': LoginForm}),
-
- # 注册URL - 使用正则表达式匹配 /register/ 路径
- re_path(r'^register/$',
- # 使用RegisterView视图类,注册成功后重定向到首页
- views.RegisterView.as_view(success_url="/"),
- name='register'), # URL名称:register
-
- # 登出URL - 使用正则表达式匹配 /logout/ 路径
- re_path(r'^logout/$',
- # 使用LogoutView视图类
- views.LogoutView.as_view(),
- name='logout'), # URL名称:logout
-
- # 账户结果页面URL - 使用path匹配固定路径
- path(r'account/result.html',
- # 使用account_result函数视图
- views.account_result,
- name='result'), # URL名称:result
-
- # 忘记密码URL - 使用正则表达式匹配 /forget_password/ 路径
- re_path(r'^forget_password/$',
- # 使用ForgetPasswordView视图类
- views.ForgetPasswordView.as_view(),
- name='forget_password'), # URL名称:forget_password
-
- # 获取忘记密码验证码URL - 使用正则表达式匹配 /forget_password_code/ 路径
- re_path(r'^forget_password_code/$',
- # 使用ForgetPasswordEmailCode视图类
- views.ForgetPasswordEmailCode.as_view(),
- name='forget_password_code'), # URL名称:forget_password_code
-]
\ No newline at end of file
diff --git a/accounts--dyh/user_login_backend-new..py b/accounts--dyh/user_login_backend-new..py
deleted file mode 100644
index 8fa42e1..0000000
--- a/accounts--dyh/user_login_backend-new..py
+++ /dev/null
@@ -1,42 +0,0 @@
-from django.contrib.auth import get_user_model
-from django.contrib.auth.backends import ModelBackend
-from django.db.models import Q
-
-
-class EmailOrUsernameModelBackend(ModelBackend):
- """
- 允许使用用户名或邮箱登录
- 支持大小写不敏感的邮箱登录
- """
-
- def authenticate(self, request, username=None, password=None, **kwargs):
- if username is None or password is None:
- return None
-
- UserModel = get_user_model()
-
- # 判断输入是否为邮箱格式
- if '@' in username:
- # 邮箱登录,不区分大小写
- try:
- user = UserModel.objects.get(email__iexact=username)
- except UserModel.DoesNotExist:
- return None
- else:
- # 用户名登录
- try:
- user = UserModel.objects.get(username=username)
- except UserModel.DoesNotExist:
- return None
-
- # 验证密码并检查用户是否激活
- if user.check_password(password) and self.user_can_authenticate(user):
- return user
- return None
-
- def get_user(self, user_id):
- UserModel = get_user_model()
- try:
- return UserModel.objects.get(pk=user_id)
- except UserModel.DoesNotExist:
- return None
diff --git a/accounts--dyh/user_login_backend.py b/accounts--dyh/user_login_backend.py
deleted file mode 100644
index 7179809..0000000
--- a/accounts--dyh/user_login_backend.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# 导入获取用户模型的函数
-from django.contrib.auth import get_user_model
-# 导入模型后端认证基类
-from django.contrib.auth.backends import ModelBackend
-
-
-class EmailOrUsernameModelBackend(ModelBackend):
- """
- 自定义认证后端,允许使用用户名或邮箱登录
- 继承自Django的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:
- # 用户不存在,返回None
- return None
-
- def get_user(self, username):
- """根据用户ID获取用户对象"""
- try:
- # 根据主键(用户ID)获取用户对象
- return get_user_model().objects.get(pk=username)
- # 捕获用户不存在的异常
- except get_user_model().DoesNotExist:
- # 用户不存在,返回None
- return None
\ No newline at end of file
diff --git a/accounts--dyh/utils-new..py b/accounts--dyh/utils-new..py
deleted file mode 100644
index 4b94bdf..0000000
--- a/accounts--dyh/utils-new..py
+++ /dev/null
@@ -1,49 +0,0 @@
-import typing
-from datetime import timedelta
-
-from django.core.cache import cache
-from django.utils.translation import gettext
-from django.utils.translation import gettext_lazy as _
-
-from djangoblog.utils import send_email
-
-_code_ttl = timedelta(minutes=5)
-
-
-def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
- """发送重设密码验证码
- Args:
- to_mail: 接受邮箱
- subject: 邮件主题
- code: 验证码
- """
- html_content = _(
- "You are resetting the password, the verification code is:%(code)s, valid within 5 minutes, please keep it "
- "properly") % {'code': code}
- send_email([to_mail], subject, html_content)
-
-
-def verify(email: str, code: str) -> typing.Optional[str]:
- """验证code是否有效
- Args:
- email: 请求邮箱
- code: 验证码
- Return:
- 如果有错误就返回错误str
- Node:
- 这里的错误处理不太合理,应该采用raise抛出
- 否测调用方也需要对error进行处理
- """
- cache_code = get_code(email)
- if cache_code != code:
- return gettext("Verification code error")
-
-
-def set_code(email: str, code: str):
- """设置code"""
- cache.set(email, code, _code_ttl.seconds)
-
-
-def get_code(email: str) -> typing.Optional[str]:
- """获取code"""
- return cache.get(email)
diff --git a/accounts--dyh/utils.py b/accounts--dyh/utils.py
deleted file mode 100644
index 607b172..0000000
--- a/accounts--dyh/utils.py
+++ /dev/null
@@ -1,70 +0,0 @@
-# 导入类型提示模块
-import typing
-# 导入时间间隔类
-from datetime import timedelta
-
-# 导入Django缓存框架
-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
-
-# 验证码有效期:5分钟
-_code_ttl = timedelta(minutes=5)
-
-
-def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
- """发送验证邮件,用于密码重置等场景
-
- Args:
- to_mail: 接收邮件的邮箱地址
- subject: 邮件主题,默认为"验证邮箱"
- code: 验证码内容
- """
- # 构建邮件HTML内容,包含验证码信息
- html_content = _(
- # 翻译文本:您正在重置密码,验证码是:{code},5分钟内有效,请妥善保管
- "You are resetting the password, the verification code is:%(code)s, valid within 5 minutes, please keep it "
- "properly"
- ) % {'code': code} # 将code插入到格式化字符串中
- # 调用发送邮件函数
- send_email([to_mail], subject, html_content)
-
-
-def verify(email: str, code: str) -> typing.Optional[str]:
- """验证验证码是否有效
-
- Args:
- email: 请求验证的邮箱地址
- code: 用户输入的验证码
-
- Return:
- 如果验证失败返回错误信息字符串,验证成功返回None
-
- Note:
- 这里的错误处理不太合理,应该采用raise抛出异常
- 否则调用方也需要对error进行处理
- """
- # 从缓存中获取该邮箱对应的验证码
- cache_code = get_code(email)
- # 比较缓存中的验证码和用户输入的验证码
- if cache_code != code:
- # 如果不匹配,返回错误信息
- return gettext("Verification code error")
- # 验证成功,返回None
-
-
-def set_code(email: str, code: str):
- """将验证码设置到缓存中"""
- # 使用cache.set方法,key为邮箱,value为验证码,设置过期时间
- cache.set(email, code, _code_ttl.seconds)
-
-
-def get_code(email: str) -> typing.Optional[str]:
- """从缓存中获取验证码"""
- # 使用cache.get方法,根据邮箱获取验证码
- return cache.get(email)
\ No newline at end of file
diff --git a/accounts--dyh/views-new..py b/accounts--dyh/views-new..py
deleted file mode 100644
index ae67aec..0000000
--- a/accounts--dyh/views-new..py
+++ /dev/null
@@ -1,204 +0,0 @@
-import logging
-from django.utils.translation import gettext_lazy as _
-from django.conf import settings
-from django.contrib import auth
-from django.contrib.auth import REDIRECT_FIELD_NAME
-from django.contrib.auth import get_user_model
-from django.contrib.auth import logout
-from django.contrib.auth.forms import AuthenticationForm
-from django.contrib.auth.hashers import make_password
-from django.http import HttpResponseRedirect, HttpResponseForbidden
-from django.http.request import HttpRequest
-from django.http.response import HttpResponse
-from django.shortcuts import get_object_or_404
-from django.shortcuts import render
-from django.urls import reverse
-from django.utils.decorators import method_decorator
-from django.utils.http import url_has_allowed_host_and_scheme
-from django.views import View
-from django.views.decorators.cache import never_cache
-from django.views.decorators.csrf import csrf_protect
-from django.views.decorators.debug import sensitive_post_parameters
-from django.views.generic import FormView, RedirectView
-
-from djangoblog.utils import send_email, get_sha256, get_current_site, generate_code, delete_sidebar_cache
-from . import utils
-from .forms import RegisterForm, LoginForm, ForgetPasswordForm, ForgetPasswordCodeForm
-from .models import BlogUser
-
-logger = logging.getLogger(__name__)
-
-
-# Create your views here.
-
-class RegisterView(FormView):
- form_class = RegisterForm
- template_name = 'account/registration_form.html'
-
- @method_decorator(csrf_protect)
- def dispatch(self, *args, **kwargs):
- return super(RegisterView, self).dispatch(*args, **kwargs)
-
- def form_valid(self, form):
- if form.is_valid():
- user = form.save(False)
- user.is_active = False
- user.source = 'Register'
- user.save(True)
- site = get_current_site().domain
- sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
-
- if settings.DEBUG:
- site = '127.0.0.1:8000'
- path = reverse('account:result')
- url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
- site=site, path=path, id=user.id, sign=sign)
-
- content = """
-
请点击下面链接验证您的邮箱
-
- {url}
-
- 再次感谢您!
-
- 如果上面链接无法打开,请将此链接复制至浏览器。
- {url}
- """.format(url=url)
- send_email(
- emailto=[
- user.email,
- ],
- title='验证您的电子邮箱',
- content=content)
-
- url = reverse('accounts:result') + \
- '?type=register&id=' + str(user.id)
- return HttpResponseRedirect(url)
- else:
- return self.render_to_response({
- 'form': form
- })
-
-
-class LogoutView(RedirectView):
- url = '/login/'
-
- @method_decorator(never_cache)
- def dispatch(self, request, *args, **kwargs):
- return super(LogoutView, self).dispatch(request, *args, **kwargs)
-
- def get(self, request, *args, **kwargs):
- logout(request)
- delete_sidebar_cache()
- return super(LogoutView, self).get(request, *args, **kwargs)
-
-
-class LoginView(FormView):
- form_class = LoginForm
- template_name = 'account/login.html'
- success_url = '/'
- redirect_field_name = REDIRECT_FIELD_NAME
- login_ttl = 2626560 # 一个月的时间
-
- @method_decorator(sensitive_post_parameters('password'))
- @method_decorator(csrf_protect)
- @method_decorator(never_cache)
- def dispatch(self, request, *args, **kwargs):
-
- return super(LoginView, self).dispatch(request, *args, **kwargs)
-
- def get_context_data(self, **kwargs):
- redirect_to = self.request.GET.get(self.redirect_field_name)
- if redirect_to is None:
- redirect_to = '/'
- kwargs['redirect_to'] = redirect_to
-
- return super(LoginView, self).get_context_data(**kwargs)
-
- def form_valid(self, form):
- form = AuthenticationForm(data=self.request.POST, request=self.request)
-
- if form.is_valid():
- delete_sidebar_cache()
- logger.info(self.redirect_field_name)
-
- auth.login(self.request, form.get_user())
- if self.request.POST.get("remember"):
- self.request.session.set_expiry(self.login_ttl)
- return super(LoginView, self).form_valid(form)
- # return HttpResponseRedirect('/')
- else:
- return self.render_to_response({
- 'form': form
- })
-
- def get_success_url(self):
-
- redirect_to = self.request.POST.get(self.redirect_field_name)
- if not url_has_allowed_host_and_scheme(
- url=redirect_to, allowed_hosts=[
- self.request.get_host()]):
- redirect_to = self.success_url
- return redirect_to
-
-
-def account_result(request):
- type = request.GET.get('type')
- id = request.GET.get('id')
-
- user = get_object_or_404(get_user_model(), id=id)
- logger.info(type)
- if user.is_active:
- return HttpResponseRedirect('/')
- if type and type in ['register', 'validation']:
- if type == 'register':
- content = '''
- 恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。
- '''
- title = '注册成功'
- else:
- c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
- sign = request.GET.get('sign')
- if sign != c_sign:
- return HttpResponseForbidden()
- user.is_active = True
- user.save()
- content = '''
- 恭喜您已经成功的完成邮箱验证,您现在可以使用您的账号来登录本站。
- '''
- title = '验证成功'
- return render(request, 'account/result.html', {
- 'title': title,
- 'content': content
- })
- else:
- return HttpResponseRedirect('/')
-
-
-class ForgetPasswordView(FormView):
- form_class = ForgetPasswordForm
- template_name = 'account/forget_password.html'
-
- def form_valid(self, form):
- if form.is_valid():
- blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
- blog_user.password = make_password(form.cleaned_data["new_password2"])
- blog_user.save()
- return HttpResponseRedirect('/login/')
- else:
- return self.render_to_response({'form': form})
-
-
-class ForgetPasswordEmailCode(View):
-
- def post(self, request: HttpRequest):
- form = ForgetPasswordCodeForm(request.POST)
- if not form.is_valid():
- return HttpResponse("错误的邮箱")
- to_email = form.cleaned_data["email"]
-
- code = generate_code()
- utils.send_verify_email(to_email, code)
- utils.set_code(to_email, code)
-
- return HttpResponse("ok")
diff --git a/accounts--dyh/views.py b/accounts--dyh/views.py
deleted file mode 100644
index 35ceaf3..0000000
--- a/accounts--dyh/views.py
+++ /dev/null
@@ -1,327 +0,0 @@
-# 导入日志模块
-import logging
-# 导入延迟翻译函数
-from django.utils.translation import gettext_lazy as _
-# 导入Django设置
-from django.conf import settings
-# 导入Django认证框架
-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
-# 导入Django内置认证表单
-from django.contrib.auth.forms import AuthenticationForm
-# 导入密码哈希函数
-from django.contrib.auth.hashers import make_password
-# 导入HTTP响应重定向和禁止访问响应
-from django.http import HttpResponseRedirect, HttpResponseForbidden
-# 导入HTTP请求类型
-from django.http.request import HttpRequest
-# 导入HTTP响应类型
-from django.http.response import HttpResponse
-# 导入快捷函数:获取对象或404错误
-from django.shortcuts import get_object_or_404
-# 导入快捷函数:渲染模板
-from django.shortcuts import render
-# 导入URL反向解析
-from django.urls import reverse
-# 导入方法装饰器
-from django.utils.decorators import method_decorator
-# 导入URL安全验证函数
-from django.utils.http import url_has_allowed_host_and_scheme
-# 导入基于类的视图基类
-from django.views import View
-# 导入禁止缓存装饰器
-from django.views.decorators.cache import never_cache
-# 导入CSRF保护装饰器
-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__)
-
-
-# 在这里创建视图
-
-class RegisterView(FormView):
- """用户注册视图"""
-
- # 指定使用的表单类
- form_class = RegisterForm
- # 指定使用的模板
- template_name = 'account/registration_form.html'
-
- @method_decorator(csrf_protect) # CSRF保护装饰器
- def dispatch(self, *args, **kwargs):
- """处理请求分发"""
- # 调用父类的dispatch方法
- return super(RegisterView, self).dispatch(*args, **kwargs)
-
- def form_valid(self, form):
- """处理表单验证通过的情况"""
- # 检查表单是否有效
- if form.is_valid():
- # 保存表单数据但不提交到数据库(commit=False)
- user = form.save(False)
- # 设置用户为未激活状态(需要邮箱验证)
- user.is_active = False
- # 设置用户来源为注册页面
- user.source = 'Register'
- # 保存用户到数据库
- user.save(True)
- # 获取当前站点域名
- site = get_current_site().domain
- # 生成验证签名:对密钥+用户ID进行双重SHA256哈希
- sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
-
- # 如果是调试模式,使用本地地址
- if settings.DEBUG:
- site = '127.0.0.1:8000'
- # 获取结果页面的URL路径
- path = reverse('account:result')
- # 构建完整的验证URL
- url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
- site=site, # 站点地址
- path=path, # 结果页面路径
- id=user.id, # 用户ID
- sign=sign # 验证签名
- )
-
- # 构建邮件内容
- content = """
- 请点击下面链接验证您的邮箱
-
- {url}
-
- 再次感谢您!
-
- 如果上面链接无法打开,请将此链接复制至浏览器。
- {url}
- """.format(url=url)
- # 发送验证邮件
- send_email(
- emailto=[user.email], # 收件人邮箱
- title='验证您的电子邮箱', # 邮件标题
- content=content # 邮件内容
- )
-
- # 构建注册结果页面URL
- 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
- url = '/login/'
-
- @method_decorator(never_cache) # 禁止缓存装饰器
- def dispatch(self, request, *args, **kwargs):
- """处理请求分发"""
- # 调用父类的dispatch方法
- return super(LogoutView, self).dispatch(request, *args, **kwargs)
-
- def get(self, request, *args, **kwargs):
- """处理GET请求(登出操作)"""
- # 调用Django登出函数
- logout(request)
- # 删除侧边栏缓存
- delete_sidebar_cache()
- # 调用父类的get方法进行重定向
- return super(LogoutView, self).get(request, *args, **kwargs)
-
-
-class LoginView(FormView):
- """用户登录视图"""
-
- # 指定使用的表单类
- form_class = LoginForm
- # 指定使用的模板
- template_name = 'account/login.html'
- # 登录成功后的重定向URL
- success_url = '/'
- # 重定向字段名
- redirect_field_name = REDIRECT_FIELD_NAME
- # 登录会话保持时间:一个月(秒数)
- login_ttl = 2626560
-
- # 方法装饰器:保护敏感参数、CSRF保护、禁止缓存
- @method_decorator(sensitive_post_parameters('password'))
- @method_decorator(csrf_protect)
- @method_decorator(never_cache)
- def dispatch(self, request, *args, **kwargs):
- """处理请求分发"""
- # 调用父类的dispatch方法
- return super(LoginView, self).dispatch(request, *args, **kwargs)
-
- def get_context_data(self, **kwargs):
- """获取模板上下文数据"""
- # 从GET参数中获取重定向URL
- redirect_to = self.request.GET.get(self.redirect_field_name)
- # 如果重定向URL为空,设置为首页
- if redirect_to is None:
- redirect_to = '/'
- # 将重定向URL添加到上下文数据中
- kwargs['redirect_to'] = redirect_to
- # 调用父类方法获取基础上下文数据
- return super(LoginView, self).get_context_data(**kwargs)
-
- def form_valid(self, form):
- """处理表单验证通过的情况"""
- # 创建认证表单实例(使用POST数据)
- 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)
- # 调用父类的form_valid方法(会处理重定向)
- return super(LoginView, self).form_valid(form)
- else:
- # 表单无效,重新渲染表单并显示错误
- return self.render_to_response({'form': form})
-
- def get_success_url(self):
- """获取登录成功后的重定向URL"""
- # 从POST数据中获取重定向URL
- redirect_to = self.request.POST.get(self.redirect_field_name)
- # 验证重定向URL是否安全(同源策略)
- if not url_has_allowed_host_and_scheme(
- url=redirect_to,
- allowed_hosts=[self.request.get_host()]):
- # 如果不安全,使用默认的成功URL
- redirect_to = self.success_url
- # 返回重定向URL
- return redirect_to
-
-
-def account_result(request):
- """账户操作结果页面视图函数"""
- # 从GET参数获取操作类型
- type = request.GET.get('type')
- # 从GET参数获取用户ID
- id = request.GET.get('id')
-
- # 根据ID获取用户对象,如果不存在返回404
- 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)))
- # 从GET参数获取签名
- 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):
- """获取忘记密码验证码的API视图"""
-
- def post(self, request: HttpRequest):
- """处理POST请求"""
- # 创建表单实例并验证数据
- 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")
\ No newline at end of file
diff --git a/accounts/__init__.py b/accounts/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/accounts/admin.py b/accounts/admin.py
deleted file mode 100644
index 32e483c..0000000
--- a/accounts/admin.py
+++ /dev/null
@@ -1,59 +0,0 @@
-from django import forms
-from django.contrib.auth.admin import UserAdmin
-from django.contrib.auth.forms import UserChangeForm
-from django.contrib.auth.forms import UsernameField
-from django.utils.translation import gettext_lazy as _
-
-# Register your models here.
-from .models import BlogUser
-
-
-class BlogUserCreationForm(forms.ModelForm):
- password1 = forms.CharField(label=_('password'), widget=forms.PasswordInput)
- password2 = forms.CharField(label=_('Enter password again'), widget=forms.PasswordInput)
-
- class Meta:
- model = BlogUser
- fields = ('email',)
-
- def clean_password2(self):
- # Check that the two password entries match
- password1 = self.cleaned_data.get("password1")
- password2 = self.cleaned_data.get("password2")
- if password1 and password2 and password1 != password2:
- raise forms.ValidationError(_("passwords do not match"))
- return password2
-
- def save(self, commit=True):
- # Save the provided password in hashed format
- user = super().save(commit=False)
- user.set_password(self.cleaned_data["password1"])
- if commit:
- user.source = 'adminsite'
- user.save()
- return user
-
-
-class BlogUserChangeForm(UserChangeForm):
- class Meta:
- model = BlogUser
- fields = '__all__'
- field_classes = {'username': UsernameField}
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
-
-
-class BlogUserAdmin(UserAdmin):
- form = BlogUserChangeForm
- add_form = BlogUserCreationForm
- list_display = (
- 'id',
- 'nickname',
- 'username',
- 'email',
- 'last_login',
- 'date_joined',
- 'source')
- list_display_links = ('id', 'username')
- ordering = ('-id',)
diff --git a/accounts/apps.py b/accounts/apps.py
deleted file mode 100644
index 9b3fc5a..0000000
--- a/accounts/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class AccountsConfig(AppConfig):
- name = 'accounts'
diff --git a/accounts/forms.py b/accounts/forms.py
deleted file mode 100644
index fce4137..0000000
--- a/accounts/forms.py
+++ /dev/null
@@ -1,117 +0,0 @@
-from django import forms
-from django.contrib.auth import get_user_model, password_validation
-from django.contrib.auth.forms import AuthenticationForm, UserCreationForm
-from django.core.exceptions import ValidationError
-from django.forms import widgets
-from django.utils.translation import gettext_lazy as _
-from . import utils
-from .models import BlogUser
-
-
-class LoginForm(AuthenticationForm):
- def __init__(self, *args, **kwargs):
- super(LoginForm, self).__init__(*args, **kwargs)
- self.fields['username'].widget = widgets.TextInput(
- attrs={'placeholder': "username", "class": "form-control"})
- self.fields['password'].widget = widgets.PasswordInput(
- attrs={'placeholder': "password", "class": "form-control"})
-
-
-class RegisterForm(UserCreationForm):
- def __init__(self, *args, **kwargs):
- super(RegisterForm, self).__init__(*args, **kwargs)
-
- self.fields['username'].widget = widgets.TextInput(
- attrs={'placeholder': "username", "class": "form-control"})
- self.fields['email'].widget = widgets.EmailInput(
- attrs={'placeholder': "email", "class": "form-control"})
- self.fields['password1'].widget = widgets.PasswordInput(
- attrs={'placeholder': "password", "class": "form-control"})
- self.fields['password2'].widget = widgets.PasswordInput(
- attrs={'placeholder': "repeat password", "class": "form-control"})
-
- def clean_email(self):
- email = self.cleaned_data['email']
- if get_user_model().objects.filter(email=email).exists():
- raise ValidationError(_("email already exists"))
- return email
-
- class Meta:
- model = get_user_model()
- fields = ("username", "email")
-
-
-class ForgetPasswordForm(forms.Form):
- new_password1 = forms.CharField(
- label=_("New password"),
- widget=forms.PasswordInput(
- attrs={
- "class": "form-control",
- 'placeholder': _("New password")
- }
- ),
- )
-
- new_password2 = forms.CharField(
- label="确认密码",
- widget=forms.PasswordInput(
- attrs={
- "class": "form-control",
- 'placeholder': _("Confirm password")
- }
- ),
- )
-
- email = forms.EmailField(
- label='邮箱',
- widget=forms.TextInput(
- attrs={
- 'class': 'form-control',
- 'placeholder': _("Email")
- }
- ),
- )
-
- code = forms.CharField(
- label=_('Code'),
- widget=forms.TextInput(
- attrs={
- 'class': 'form-control',
- 'placeholder': _("Code")
- }
- ),
- )
-
- def clean_new_password2(self):
- password1 = self.data.get("new_password1")
- password2 = self.data.get("new_password2")
- if password1 and password2 and password1 != password2:
- raise ValidationError(_("passwords do not match"))
- password_validation.validate_password(password2)
-
- return password2
-
- def clean_email(self):
- user_email = self.cleaned_data.get("email")
- if not BlogUser.objects.filter(
- email=user_email
- ).exists():
- # todo 这里的报错提示可以判断一个邮箱是不是注册过,如果不想暴露可以修改
- raise ValidationError(_("email does not exist"))
- return user_email
-
- def clean_code(self):
- code = self.cleaned_data.get("code")
- error = utils.verify(
- email=self.cleaned_data.get("email"),
- code=code,
- )
- if error:
- raise ValidationError(error)
- return code
-
-
-class ForgetPasswordCodeForm(forms.Form):
- email = forms.EmailField(
- label=_('Email'),
- )
diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py
deleted file mode 100644
index d2fbcab..0000000
--- a/accounts/migrations/0001_initial.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Generated by Django 4.1.7 on 2023-03-02 07:14
-
-import django.contrib.auth.models
-import django.contrib.auth.validators
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ('auth', '0012_alter_user_first_name_max_length'),
- ]
-
- operations = [
- migrations.CreateModel(
- name='BlogUser',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('password', models.CharField(max_length=128, verbose_name='password')),
- ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
- ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
- ('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
- ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')),
- ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
- ('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
- ('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
- ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
- ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
- ('nickname', models.CharField(blank=True, max_length=100, verbose_name='昵称')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('source', models.CharField(blank=True, max_length=100, verbose_name='创建来源')),
- ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')),
- ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')),
- ],
- options={
- 'verbose_name': '用户',
- 'verbose_name_plural': '用户',
- 'ordering': ['-id'],
- 'get_latest_by': 'id',
- },
- managers=[
- ('objects', django.contrib.auth.models.UserManager()),
- ],
- ),
- ]
diff --git a/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py b/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
deleted file mode 100644
index 1a9f509..0000000
--- a/accounts/migrations/0002_alter_bloguser_options_remove_bloguser_created_time_and_more.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Generated by Django 4.2.5 on 2023-09-06 13:13
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('accounts', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='bloguser',
- options={'get_latest_by': 'id', 'ordering': ['-id'], 'verbose_name': 'user', 'verbose_name_plural': 'user'},
- ),
- migrations.RemoveField(
- model_name='bloguser',
- name='created_time',
- ),
- migrations.RemoveField(
- model_name='bloguser',
- name='last_mod_time',
- ),
- migrations.AddField(
- model_name='bloguser',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
- ),
- migrations.AddField(
- model_name='bloguser',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
- ),
- migrations.AlterField(
- model_name='bloguser',
- name='nickname',
- field=models.CharField(blank=True, max_length=100, verbose_name='nick name'),
- ),
- migrations.AlterField(
- model_name='bloguser',
- name='source',
- field=models.CharField(blank=True, max_length=100, verbose_name='create source'),
- ),
- ]
diff --git a/accounts/migrations/__init__.py b/accounts/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/accounts/models.py b/accounts/models.py
deleted file mode 100644
index 3baddbb..0000000
--- a/accounts/models.py
+++ /dev/null
@@ -1,35 +0,0 @@
-from django.contrib.auth.models import AbstractUser
-from django.db import models
-from django.urls import reverse
-from django.utils.timezone import now
-from django.utils.translation import gettext_lazy as _
-from djangoblog.utils import get_current_site
-
-
-# Create your models here.
-
-class BlogUser(AbstractUser):
- nickname = models.CharField(_('nick name'), max_length=100, blank=True)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_modify_time = models.DateTimeField(_('last modify time'), default=now)
- source = models.CharField(_('create source'), max_length=100, blank=True)
-
- def get_absolute_url(self):
- return reverse(
- 'blog:author_detail', kwargs={
- 'author_name': self.username})
-
- def __str__(self):
- return self.email
-
- def get_full_url(self):
- site = get_current_site().domain
- url = "https://{site}{path}".format(site=site,
- path=self.get_absolute_url())
- return url
-
- class Meta:
- ordering = ['-id']
- verbose_name = _('user')
- verbose_name_plural = verbose_name
- get_latest_by = 'id'
diff --git a/accounts/templatetags/__init__.py b/accounts/templatetags/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/accounts/tests.py b/accounts/tests.py
deleted file mode 100644
index 6893411..0000000
--- a/accounts/tests.py
+++ /dev/null
@@ -1,207 +0,0 @@
-from django.test import Client, RequestFactory, TestCase
-from django.urls import reverse
-from django.utils import timezone
-from django.utils.translation import gettext_lazy as _
-
-from accounts.models import BlogUser
-from blog.models import Article, Category
-from djangoblog.utils import *
-from . import utils
-
-
-# Create your tests here.
-
-class AccountTest(TestCase):
- def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
- self.blog_user = BlogUser.objects.create_user(
- username="test",
- email="admin@admin.com",
- password="12345678"
- )
- self.new_test = "xxx123--="
-
- def test_validate_account(self):
- site = get_current_site().domain
- user = BlogUser.objects.create_superuser(
- email="liangliangyy1@gmail.com",
- username="liangliangyy1",
- password="qwer!@#$ggg")
- testuser = BlogUser.objects.get(username='liangliangyy1')
-
- loginresult = self.client.login(
- username='liangliangyy1',
- password='qwer!@#$ggg')
- self.assertEqual(loginresult, True)
- response = self.client.get('/admin/')
- self.assertEqual(response.status_code, 200)
-
- category = Category()
- category.name = "categoryaaa"
- category.creation_time = timezone.now()
- category.last_modify_time = timezone.now()
- category.save()
-
- article = Article()
- article.title = "nicetitleaaa"
- article.body = "nicecontentaaa"
- article.author = user
- article.category = category
- article.type = 'a'
- article.status = 'p'
- article.save()
-
- response = self.client.get(article.get_admin_url())
- self.assertEqual(response.status_code, 200)
-
- def test_validate_register(self):
- self.assertEquals(
- 0, len(
- BlogUser.objects.filter(
- email='user123@user.com')))
- response = self.client.post(reverse('account:register'), {
- 'username': 'user1233',
- 'email': 'user123@user.com',
- 'password1': 'password123!q@wE#R$T',
- 'password2': 'password123!q@wE#R$T',
- })
- self.assertEquals(
- 1, len(
- BlogUser.objects.filter(
- email='user123@user.com')))
- user = BlogUser.objects.filter(email='user123@user.com')[0]
- sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
- path = reverse('accounts:result')
- url = '{path}?type=validation&id={id}&sign={sign}'.format(
- path=path, id=user.id, sign=sign)
- response = self.client.get(url)
- self.assertEqual(response.status_code, 200)
-
- self.client.login(username='user1233', password='password123!q@wE#R$T')
- user = BlogUser.objects.filter(email='user123@user.com')[0]
- user.is_superuser = True
- user.is_staff = True
- user.save()
- delete_sidebar_cache()
- category = Category()
- category.name = "categoryaaa"
- category.creation_time = timezone.now()
- category.last_modify_time = timezone.now()
- category.save()
-
- article = Article()
- article.category = category
- article.title = "nicetitle333"
- article.body = "nicecontentttt"
- article.author = user
-
- article.type = 'a'
- article.status = 'p'
- article.save()
-
- response = self.client.get(article.get_admin_url())
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get(reverse('account:logout'))
- self.assertIn(response.status_code, [301, 302, 200])
-
- response = self.client.get(article.get_admin_url())
- self.assertIn(response.status_code, [301, 302, 200])
-
- response = self.client.post(reverse('account:login'), {
- 'username': 'user1233',
- 'password': 'password123'
- })
- self.assertIn(response.status_code, [301, 302, 200])
-
- response = self.client.get(article.get_admin_url())
- self.assertIn(response.status_code, [301, 302, 200])
-
- def test_verify_email_code(self):
- to_email = "admin@admin.com"
- code = generate_code()
- utils.set_code(to_email, code)
- utils.send_verify_email(to_email, code)
-
- err = utils.verify("admin@admin.com", code)
- self.assertEqual(err, None)
-
- err = utils.verify("admin@123.com", code)
- self.assertEqual(type(err), str)
-
- def test_forget_password_email_code_success(self):
- resp = self.client.post(
- path=reverse("account:forget_password_code"),
- data=dict(email="admin@admin.com")
- )
-
- self.assertEqual(resp.status_code, 200)
- self.assertEqual(resp.content.decode("utf-8"), "ok")
-
- def test_forget_password_email_code_fail(self):
- resp = self.client.post(
- path=reverse("account:forget_password_code"),
- data=dict()
- )
- self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
-
- resp = self.client.post(
- path=reverse("account:forget_password_code"),
- data=dict(email="admin@com")
- )
- self.assertEqual(resp.content.decode("utf-8"), "错误的邮箱")
-
- def test_forget_password_email_success(self):
- code = generate_code()
- utils.set_code(self.blog_user.email, code)
- data = dict(
- new_password1=self.new_test,
- new_password2=self.new_test,
- email=self.blog_user.email,
- code=code,
- )
- resp = self.client.post(
- path=reverse("account:forget_password"),
- data=data
- )
- self.assertEqual(resp.status_code, 302)
-
- # 验证用户密码是否修改成功
- blog_user = BlogUser.objects.filter(
- email=self.blog_user.email,
- ).first() # type: BlogUser
- self.assertNotEqual(blog_user, None)
- self.assertEqual(blog_user.check_password(data["new_password1"]), True)
-
- def test_forget_password_email_not_user(self):
- data = dict(
- new_password1=self.new_test,
- new_password2=self.new_test,
- email="123@123.com",
- code="123456",
- )
- resp = self.client.post(
- path=reverse("account:forget_password"),
- data=data
- )
-
- self.assertEqual(resp.status_code, 200)
-
-
- def test_forget_password_email_code_error(self):
- code = generate_code()
- utils.set_code(self.blog_user.email, code)
- data = dict(
- new_password1=self.new_test,
- new_password2=self.new_test,
- email=self.blog_user.email,
- code="111111",
- )
- resp = self.client.post(
- path=reverse("account:forget_password"),
- data=data
- )
-
- self.assertEqual(resp.status_code, 200)
-
diff --git a/accounts/urls.py b/accounts/urls.py
deleted file mode 100644
index 107a801..0000000
--- a/accounts/urls.py
+++ /dev/null
@@ -1,28 +0,0 @@
-from django.urls import path
-from django.urls import re_path
-
-from . import views
-from .forms import LoginForm
-
-app_name = "accounts"
-
-urlpatterns = [re_path(r'^login/$',
- views.LoginView.as_view(success_url='/'),
- name='login',
- kwargs={'authentication_form': LoginForm}),
- re_path(r'^register/$',
- views.RegisterView.as_view(success_url="/"),
- name='register'),
- re_path(r'^logout/$',
- views.LogoutView.as_view(),
- name='logout'),
- path(r'account/result.html',
- views.account_result,
- name='result'),
- re_path(r'^forget_password/$',
- views.ForgetPasswordView.as_view(),
- name='forget_password'),
- re_path(r'^forget_password_code/$',
- views.ForgetPasswordEmailCode.as_view(),
- name='forget_password_code'),
- ]
diff --git a/accounts/user_login_backend.py b/accounts/user_login_backend.py
deleted file mode 100644
index 73cdca1..0000000
--- a/accounts/user_login_backend.py
+++ /dev/null
@@ -1,26 +0,0 @@
-from django.contrib.auth import get_user_model
-from django.contrib.auth.backends import ModelBackend
-
-
-class EmailOrUsernameModelBackend(ModelBackend):
- """
- 允许使用用户名或邮箱登录
- """
-
- def authenticate(self, request, username=None, password=None, **kwargs):
- if '@' in username:
- kwargs = {'email': username}
- else:
- kwargs = {'username': username}
- try:
- user = get_user_model().objects.get(**kwargs)
- if user.check_password(password):
- return user
- except get_user_model().DoesNotExist:
- return None
-
- def get_user(self, username):
- try:
- return get_user_model().objects.get(pk=username)
- except get_user_model().DoesNotExist:
- return None
diff --git a/accounts/utils.py b/accounts/utils.py
deleted file mode 100644
index 4b94bdf..0000000
--- a/accounts/utils.py
+++ /dev/null
@@ -1,49 +0,0 @@
-import typing
-from datetime import timedelta
-
-from django.core.cache import cache
-from django.utils.translation import gettext
-from django.utils.translation import gettext_lazy as _
-
-from djangoblog.utils import send_email
-
-_code_ttl = timedelta(minutes=5)
-
-
-def send_verify_email(to_mail: str, code: str, subject: str = _("Verify Email")):
- """发送重设密码验证码
- Args:
- to_mail: 接受邮箱
- subject: 邮件主题
- code: 验证码
- """
- html_content = _(
- "You are resetting the password, the verification code is:%(code)s, valid within 5 minutes, please keep it "
- "properly") % {'code': code}
- send_email([to_mail], subject, html_content)
-
-
-def verify(email: str, code: str) -> typing.Optional[str]:
- """验证code是否有效
- Args:
- email: 请求邮箱
- code: 验证码
- Return:
- 如果有错误就返回错误str
- Node:
- 这里的错误处理不太合理,应该采用raise抛出
- 否测调用方也需要对error进行处理
- """
- cache_code = get_code(email)
- if cache_code != code:
- return gettext("Verification code error")
-
-
-def set_code(email: str, code: str):
- """设置code"""
- cache.set(email, code, _code_ttl.seconds)
-
-
-def get_code(email: str) -> typing.Optional[str]:
- """获取code"""
- return cache.get(email)
diff --git a/accounts/views.py b/accounts/views.py
deleted file mode 100644
index ae67aec..0000000
--- a/accounts/views.py
+++ /dev/null
@@ -1,204 +0,0 @@
-import logging
-from django.utils.translation import gettext_lazy as _
-from django.conf import settings
-from django.contrib import auth
-from django.contrib.auth import REDIRECT_FIELD_NAME
-from django.contrib.auth import get_user_model
-from django.contrib.auth import logout
-from django.contrib.auth.forms import AuthenticationForm
-from django.contrib.auth.hashers import make_password
-from django.http import HttpResponseRedirect, HttpResponseForbidden
-from django.http.request import HttpRequest
-from django.http.response import HttpResponse
-from django.shortcuts import get_object_or_404
-from django.shortcuts import render
-from django.urls import reverse
-from django.utils.decorators import method_decorator
-from django.utils.http import url_has_allowed_host_and_scheme
-from django.views import View
-from django.views.decorators.cache import never_cache
-from django.views.decorators.csrf import csrf_protect
-from django.views.decorators.debug import sensitive_post_parameters
-from django.views.generic import FormView, RedirectView
-
-from djangoblog.utils import send_email, get_sha256, get_current_site, generate_code, delete_sidebar_cache
-from . import utils
-from .forms import RegisterForm, LoginForm, ForgetPasswordForm, ForgetPasswordCodeForm
-from .models import BlogUser
-
-logger = logging.getLogger(__name__)
-
-
-# Create your views here.
-
-class RegisterView(FormView):
- form_class = RegisterForm
- template_name = 'account/registration_form.html'
-
- @method_decorator(csrf_protect)
- def dispatch(self, *args, **kwargs):
- return super(RegisterView, self).dispatch(*args, **kwargs)
-
- def form_valid(self, form):
- if form.is_valid():
- user = form.save(False)
- user.is_active = False
- user.source = 'Register'
- user.save(True)
- site = get_current_site().domain
- sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
-
- if settings.DEBUG:
- site = '127.0.0.1:8000'
- path = reverse('account:result')
- url = "http://{site}{path}?type=validation&id={id}&sign={sign}".format(
- site=site, path=path, id=user.id, sign=sign)
-
- content = """
- 请点击下面链接验证您的邮箱
-
- {url}
-
- 再次感谢您!
-
- 如果上面链接无法打开,请将此链接复制至浏览器。
- {url}
- """.format(url=url)
- send_email(
- emailto=[
- user.email,
- ],
- title='验证您的电子邮箱',
- content=content)
-
- url = reverse('accounts:result') + \
- '?type=register&id=' + str(user.id)
- return HttpResponseRedirect(url)
- else:
- return self.render_to_response({
- 'form': form
- })
-
-
-class LogoutView(RedirectView):
- url = '/login/'
-
- @method_decorator(never_cache)
- def dispatch(self, request, *args, **kwargs):
- return super(LogoutView, self).dispatch(request, *args, **kwargs)
-
- def get(self, request, *args, **kwargs):
- logout(request)
- delete_sidebar_cache()
- return super(LogoutView, self).get(request, *args, **kwargs)
-
-
-class LoginView(FormView):
- form_class = LoginForm
- template_name = 'account/login.html'
- success_url = '/'
- redirect_field_name = REDIRECT_FIELD_NAME
- login_ttl = 2626560 # 一个月的时间
-
- @method_decorator(sensitive_post_parameters('password'))
- @method_decorator(csrf_protect)
- @method_decorator(never_cache)
- def dispatch(self, request, *args, **kwargs):
-
- return super(LoginView, self).dispatch(request, *args, **kwargs)
-
- def get_context_data(self, **kwargs):
- redirect_to = self.request.GET.get(self.redirect_field_name)
- if redirect_to is None:
- redirect_to = '/'
- kwargs['redirect_to'] = redirect_to
-
- return super(LoginView, self).get_context_data(**kwargs)
-
- def form_valid(self, form):
- form = AuthenticationForm(data=self.request.POST, request=self.request)
-
- if form.is_valid():
- delete_sidebar_cache()
- logger.info(self.redirect_field_name)
-
- auth.login(self.request, form.get_user())
- if self.request.POST.get("remember"):
- self.request.session.set_expiry(self.login_ttl)
- return super(LoginView, self).form_valid(form)
- # return HttpResponseRedirect('/')
- else:
- return self.render_to_response({
- 'form': form
- })
-
- def get_success_url(self):
-
- redirect_to = self.request.POST.get(self.redirect_field_name)
- if not url_has_allowed_host_and_scheme(
- url=redirect_to, allowed_hosts=[
- self.request.get_host()]):
- redirect_to = self.success_url
- return redirect_to
-
-
-def account_result(request):
- type = request.GET.get('type')
- id = request.GET.get('id')
-
- user = get_object_or_404(get_user_model(), id=id)
- logger.info(type)
- if user.is_active:
- return HttpResponseRedirect('/')
- if type and type in ['register', 'validation']:
- if type == 'register':
- content = '''
- 恭喜您注册成功,一封验证邮件已经发送到您的邮箱,请验证您的邮箱后登录本站。
- '''
- title = '注册成功'
- else:
- c_sign = get_sha256(get_sha256(settings.SECRET_KEY + str(user.id)))
- sign = request.GET.get('sign')
- if sign != c_sign:
- return HttpResponseForbidden()
- user.is_active = True
- user.save()
- content = '''
- 恭喜您已经成功的完成邮箱验证,您现在可以使用您的账号来登录本站。
- '''
- title = '验证成功'
- return render(request, 'account/result.html', {
- 'title': title,
- 'content': content
- })
- else:
- return HttpResponseRedirect('/')
-
-
-class ForgetPasswordView(FormView):
- form_class = ForgetPasswordForm
- template_name = 'account/forget_password.html'
-
- def form_valid(self, form):
- if form.is_valid():
- blog_user = BlogUser.objects.filter(email=form.cleaned_data.get("email")).get()
- blog_user.password = make_password(form.cleaned_data["new_password2"])
- blog_user.save()
- return HttpResponseRedirect('/login/')
- else:
- return self.render_to_response({'form': form})
-
-
-class ForgetPasswordEmailCode(View):
-
- def post(self, request: HttpRequest):
- form = ForgetPasswordCodeForm(request.POST)
- if not form.is_valid():
- return HttpResponse("错误的邮箱")
- to_email = form.cleaned_data["email"]
-
- code = generate_code()
- utils.send_verify_email(to_email, code)
- utils.set_code(to_email, code)
-
- return HttpResponse("ok")
diff --git a/blog-lsh/__init__.py b/blog-lsh/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/blog-lsh/__pycache__/__init__.cpython-312.pyc b/blog-lsh/__pycache__/__init__.cpython-312.pyc
deleted file mode 100644
index c02cb87..0000000
Binary files a/blog-lsh/__pycache__/__init__.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/__pycache__/admin.cpython-312.pyc b/blog-lsh/__pycache__/admin.cpython-312.pyc
deleted file mode 100644
index 38e685e..0000000
Binary files a/blog-lsh/__pycache__/admin.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/__pycache__/apps.cpython-312.pyc b/blog-lsh/__pycache__/apps.cpython-312.pyc
deleted file mode 100644
index 95b88b4..0000000
Binary files a/blog-lsh/__pycache__/apps.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/__pycache__/documents.cpython-312.pyc b/blog-lsh/__pycache__/documents.cpython-312.pyc
deleted file mode 100644
index 5681bee..0000000
Binary files a/blog-lsh/__pycache__/documents.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/__pycache__/middleware.cpython-312.pyc b/blog-lsh/__pycache__/middleware.cpython-312.pyc
deleted file mode 100644
index 36078af..0000000
Binary files a/blog-lsh/__pycache__/middleware.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/__pycache__/models.cpython-312.pyc b/blog-lsh/__pycache__/models.cpython-312.pyc
deleted file mode 100644
index a7c3108..0000000
Binary files a/blog-lsh/__pycache__/models.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/__pycache__/urls.cpython-312.pyc b/blog-lsh/__pycache__/urls.cpython-312.pyc
deleted file mode 100644
index f97d159..0000000
Binary files a/blog-lsh/__pycache__/urls.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/__pycache__/views.cpython-312.pyc b/blog-lsh/__pycache__/views.cpython-312.pyc
deleted file mode 100644
index c95b610..0000000
Binary files a/blog-lsh/__pycache__/views.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/admin.py b/blog-lsh/admin.py
deleted file mode 100644
index 0f61314..0000000
--- a/blog-lsh/admin.py
+++ /dev/null
@@ -1,133 +0,0 @@
-"""
-LJX: Django后台管理配置模块
-负责blog应用中各模型在Django admin后台的显示和操作配置
-包括文章、分类、标签、友情链接、侧边栏等模型的后台管理界面设置
-"""
-from django import forms
-from django.contrib import admin
-from django.contrib.auth import get_user_model
-from django.urls import reverse
-from django.utils.html import format_html
-from django.utils.translation import gettext_lazy as _
-
-# Register your models here.
-from .models import Article
-
-
-class ArticleForm(forms.ModelForm):
- """LJX: 文章表单类,用于后台文章编辑"""
- # body = forms.CharField(widget=AdminPagedownWidget())
-
- class Meta:
- model = Article
- fields = '__all__'
-
-
-def makr_article_publish(modeladmin, request, queryset):
- """LJX: 批量发布文章的管理动作"""
- queryset.update(status='p')
-
-
-def draft_article(modeladmin, request, queryset):
- """LJX: 批量将文章设为草稿的管理动作"""
- queryset.update(status='d')
-
-
-def close_article_commentstatus(modeladmin, request, queryset):
- """LJX: 批量关闭文章评论的管理动作"""
- queryset.update(comment_status='c')
-
-
-def open_article_commentstatus(modeladmin, request, queryset):
- """LJX: 批量开启文章评论的管理动作"""
- queryset.update(comment_status='o')
-
-
-# LJX: 设置管理动作的描述信息
-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):
- """LJX: 文章模型的后台管理配置"""
- list_per_page = 20 # LJX: 每页显示20条记录
- search_fields = ('body', 'title') # LJX: 可搜索的字段
- form = ArticleForm # LJX: 使用自定义表单
- list_display = (
- 'id',
- 'title',
- 'author',
- 'link_to_category', # LJX: 自定义字段显示分类链接
- 'creation_time',
- 'views',
- 'status',
- 'type',
- 'article_order')
- list_display_links = ('id', 'title') # LJX: 可点击的字段
- list_filter = ('status', 'type', 'category') # LJX: 右侧过滤器
- filter_horizontal = ('tags',) # LJX: 水平选择器用于多对多字段
- exclude = ('creation_time', 'last_modify_time') # LJX: 排除的字段
- view_on_site = True # LJX: 显示"在站点查看"按钮
- actions = [ # LJX: 可用的批量动作
- makr_article_publish,
- draft_article,
- close_article_commentstatus,
- open_article_commentstatus]
-
- def link_to_category(self, obj):
- """LJX: 自定义方法,显示分类名称并链接到分类编辑页面"""
- 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):
- """LJX: 重写获取表单方法,限制作者只能选择超级用户"""
- 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):
- """LJX: 重写保存模型方法"""
- super(ArticlelAdmin, self).save_model(request, obj, form, change)
-
- def get_view_on_site_url(self, obj=None):
- """LJX: 获取在站点查看的URL"""
- if obj:
- url = obj.get_full_url() # LJX: 使用文章的完整URL
- return url
- else:
- from djangoblog.utils import get_current_site
- site = get_current_site().domain
- return site
-
-
-class TagAdmin(admin.ModelAdmin):
- """LJX: 标签模型的后台管理配置"""
- exclude = ('slug', 'last_mod_time', 'creation_time') # LJX: 排除自动生成的字段
-
-
-class CategoryAdmin(admin.ModelAdmin):
- """LJX: 分类模型的后台管理配置"""
- list_display = ('name', 'parent_category', 'index') # LJX: 列表显示字段
- exclude = ('slug', 'last_mod_time', 'creation_time') # LJX: 排除自动生成的字段
-
-
-class LinksAdmin(admin.ModelAdmin):
- """LJX: 友情链接模型的后台管理配置"""
- exclude = ('last_mod_time', 'creation_time') # LJX: 排除时间字段
-
-
-class SideBarAdmin(admin.ModelAdmin):
- """LJX: 侧边栏模型的后台管理配置"""
- list_display = ('name', 'content', 'is_enable', 'sequence') # LJX: 列表显示字段
- exclude = ('last_mod_time', 'creation_time') # LJX: 排除时间字段
-
-
-class BlogSettingsAdmin(admin.ModelAdmin):
- """LJX: 博客设置模型的后台管理配置"""
- pass
\ No newline at end of file
diff --git a/blog-lsh/apps.py b/blog-lsh/apps.py
deleted file mode 100644
index b26e48a..0000000
--- a/blog-lsh/apps.py
+++ /dev/null
@@ -1,10 +0,0 @@
-"""
-LJX: Blog应用配置模块
-定义blog应用的配置信息,包括应用名称等基础设置
-"""
-from django.apps import AppConfig
-
-
-class BlogConfig(AppConfig):
- """LJX: Blog应用配置类"""
- name = 'blog' # LJX: 应用名称
\ No newline at end of file
diff --git a/blog-lsh/context_processors.py b/blog-lsh/context_processors.py
deleted file mode 100644
index 0af9401..0000000
--- a/blog-lsh/context_processors.py
+++ /dev/null
@@ -1,49 +0,0 @@
-"""
-LJX: 模板上下文处理器模块
-为所有模板提供全局的上下文变量,包括SEO信息、导航数据、网站设置等
-这些变量在所有模板中都可以直接使用
-"""
-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):
- """LJX: SEO上下文处理器,为模板提供SEO相关变量"""
- key = 'seo_processor'
- value = cache.get(key)
- if value:
- return value # LJX: 如果缓存中存在,直接返回
- else:
- logger.info('set processor cache.')
- setting = get_blog_setting() # LJX: 获取博客设置
- value = {
- 'SITE_NAME': setting.site_name, # LJX: 网站名称
- 'SHOW_GOOGLE_ADSENSE': setting.show_google_adsense, # LJX: 是否显示Google广告
- 'GOOGLE_ADSENSE_CODES': setting.google_adsense_codes, # LJX: 广告代码
- 'SITE_SEO_DESCRIPTION': setting.site_seo_description, # LJX: SEO描述
- 'SITE_DESCRIPTION': setting.site_description, # LJX: 网站描述
- 'SITE_KEYWORDS': setting.site_keywords, # LJX: 网站关键词
- 'SITE_BASE_URL': requests.scheme + '://' + requests.get_host() + '/', # LJX: 网站基础URL
- 'ARTICLE_SUB_LENGTH': setting.article_sub_length, # LJX: 文章摘要长度
- 'nav_category_list': Category.objects.all(), # LJX: 导航分类列表
- 'nav_pages': Article.objects.filter( # LJX: 导航页面
- type='p',
- status='p'),
- 'OPEN_SITE_COMMENT': setting.open_site_comment, # LJX: 是否开启评论
- 'BEIAN_CODE': setting.beian_code, # LJX: 备案号
- 'ANALYTICS_CODE': setting.analytics_code, # LJX: 统计代码
- "BEIAN_CODE_GONGAN": setting.gongan_beiancode, # LJX: 公安备案号
- "SHOW_GONGAN_CODE": setting.show_gongan_code, # LJX: 是否显示公安备案
- "CURRENT_YEAR": timezone.now().year, # LJX: 当前年份
- "GLOBAL_HEADER": setting.global_header, # LJX: 全局头部
- "GLOBAL_FOOTER": setting.global_footer, # LJX: 全局尾部
- "COMMENT_NEED_REVIEW": setting.comment_need_review, # LJX: 评论是否需要审核
- }
- cache.set(key, value, 60 * 60 * 10) # LJX: 缓存10小时
- return value
\ No newline at end of file
diff --git a/blog-lsh/documents.py b/blog-lsh/documents.py
deleted file mode 100644
index 2d56e39..0000000
--- a/blog-lsh/documents.py
+++ /dev/null
@@ -1,243 +0,0 @@
-"""
-LJX: Elasticsearch文档定义模块
-定义Elasticsearch索引的文档结构和数据模型
-用于博客文章的全文搜索和性能监控数据的存储
-"""
-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
-
-# LJX: 检查是否启用Elasticsearch
-ELASTICSEARCH_ENABLED = hasattr(settings, 'ELASTICSEARCH_DSL')
-
-if ELASTICSEARCH_ENABLED:
- # LJX: 创建Elasticsearch连接
- 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') # LJX: 检查geoip管道是否存在
- except elasticsearch.exceptions.NotFoundError:
- # LJX: 创建geoip处理管道,用于IP地址地理位置解析
- c.put_pipeline('geoip', body='''{
- "description" : "Add geoip info",
- "processors" : [
- {
- "geoip" : {
- "field" : "ip"
- }
- }
- ]
- }''')
-
-
-class GeoIp(InnerDoc):
- """LJX: IP地理位置信息内嵌文档"""
- continent_name = Keyword() # LJX: 大洲名称
- country_iso_code = Keyword() # LJX: 国家ISO代码
- country_name = Keyword() # LJX: 国家名称
- location = GeoPoint() # LJX: 地理位置坐标
-
-
-class UserAgentBrowser(InnerDoc):
- """LJX: 用户代理浏览器信息"""
- Family = Keyword() # LJX: 浏览器家族
- Version = Keyword() # LJX: 浏览器版本
-
-
-class UserAgentOS(UserAgentBrowser):
- """LJX: 用户代理操作系统信息"""
- pass
-
-
-class UserAgentDevice(InnerDoc):
- """LJX: 用户代理设备信息"""
- Family = Keyword() # LJX: 设备家族
- Brand = Keyword() # LJX: 设备品牌
- Model = Keyword() # LJX: 设备型号
-
-
-class UserAgent(InnerDoc):
- """LJX: 完整的用户代理信息"""
- browser = Object(UserAgentBrowser, required=False) # LJX: 浏览器信息
- os = Object(UserAgentOS, required=False) # LJX: 操作系统信息
- device = Object(UserAgentDevice, required=False) # LJX: 设备信息
- string = Text() # LJX: 原始用户代理字符串
- is_bot = Boolean() # LJX: 是否是爬虫
-
-
-class ElapsedTimeDocument(Document):
- """LJX: 性能监控耗时文档,记录页面加载时间等性能数据"""
- url = Keyword() # LJX: 请求URL
- time_taken = Long() # LJX: 耗时(毫秒)
- log_datetime = Date() # LJX: 日志时间
- ip = Keyword() # LJX: IP地址
- geoip = Object(GeoIp, required=False) # LJX: 地理位置信息
- useragent = Object(UserAgent, required=False) # LJX: 用户代理信息
-
- class Index:
- """LJX: 索引配置"""
- name = 'performance' # LJX: 索引名称
- settings = {
- "number_of_shards": 1, # LJX: 分片数量
- "number_of_replicas": 0 # LJX: 副本数量
- }
-
- class Meta:
- doc_type = 'ElapsedTime' # LJX: 文档类型
-
-
-class ElaspedTimeDocumentManager:
- """LJX: 性能监控文档管理器"""
- @staticmethod
- def build_index():
- """LJX: 构建性能监控索引"""
- from elasticsearch import Elasticsearch
- client = Elasticsearch(settings.ELASTICSEARCH_DSL['default']['hosts'])
- res = client.indices.exists(index="performance")
- if not res:
- ElapsedTimeDocument.init() # LJX: 初始化索引
-
- @staticmethod
- def delete_index():
- """LJX: 删除性能监控索引"""
- 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):
- """LJX: 创建性能监控记录"""
- ElaspedTimeDocumentManager.build_index()
- # LJX: 构建用户代理信息对象
- 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
-
- # LJX: 创建文档并保存,使用geoip管道处理IP地址
- doc = ElapsedTimeDocument(
- meta={
- 'id': int(
- round(
- time.time() *
- 1000)) # LJX: 使用时间戳作为文档ID
- },
- url=url,
- time_taken=time_taken,
- log_datetime=log_datetime,
- useragent=ua, ip=ip)
- doc.save(pipeline="geoip")
-
-
-class ArticleDocument(Document):
- """LJX: 博客文章搜索文档,用于全文搜索"""
- body = Text(analyzer='ik_max_word', search_analyzer='ik_smart') # LJX: 文章内容,使用IK分词器
- title = Text(analyzer='ik_max_word', search_analyzer='ik_smart') # LJX: 文章标题
- author = Object(properties={ # LJX: 作者信息
- 'nickname': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
- 'id': Integer()
- })
- category = Object(properties={ # LJX: 分类信息
- 'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
- 'id': Integer()
- })
- tags = Object(properties={ # LJX: 标签信息
- 'name': Text(analyzer='ik_max_word', search_analyzer='ik_smart'),
- 'id': Integer()
- })
-
- # LJX: 文章元数据字段
- pub_time = Date() # LJX: 发布时间
- status = Text() # LJX: 状态
- comment_status = Text() # LJX: 评论状态
- type = Text() # LJX: 类型
- views = Integer() # LJX: 浏览量
- article_order = Integer() # LJX: 文章排序
-
- class Index:
- """LJX: 文章索引配置"""
- name = 'blog' # LJX: 索引名称
- settings = {
- "number_of_shards": 1,
- "number_of_replicas": 0
- }
-
- class Meta:
- doc_type = 'Article' # LJX: 文档类型
-
-
-class ArticleDocumentManager():
- """LJX: 文章文档管理器,处理文章搜索索引的创建和更新"""
-
- def __init__(self):
- self.create_index() # LJX: 初始化时创建索引
-
- def create_index(self):
- """LJX: 创建文章索引"""
- ArticleDocument.init()
-
- def delete_index(self):
- """LJX: 删除文章索引"""
- 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):
- """LJX: 将文章对象转换为搜索文档对象"""
- return [
- ArticleDocument(
- meta={
- 'id': article.id}, # LJX: 使用文章ID作为文档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()], # LJX: 转换标签列表
- 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):
- """LJX: 重建搜索索引"""
- ArticleDocument.init()
- articles = articles if articles else Article.objects.all() # LJX: 如果没有指定文章,则使用所有文章
- docs = self.convert_to_doc(articles)
- for doc in docs:
- doc.save() # LJX: 保存所有文档到索引
-
- def update_docs(self, docs):
- """LJX: 更新搜索文档"""
- for doc in docs:
- doc.save()
\ No newline at end of file
diff --git a/blog-lsh/forms.py b/blog-lsh/forms.py
deleted file mode 100644
index 82e6929..0000000
--- a/blog-lsh/forms.py
+++ /dev/null
@@ -1,25 +0,0 @@
-"""
-LJX: 表单定义模块
-定义博客搜索相关的表单类和验证逻辑
-"""
-import logging
-
-from django import forms
-from haystack.forms import SearchForm
-
-logger = logging.getLogger(__name__)
-
-
-class BlogSearchForm(SearchForm):
- """LJX: 博客搜索表单,继承自Haystack的SearchForm"""
- querydata = forms.CharField(required=True) # LJX: 搜索查询字段,必须填写
-
- def search(self):
- """LJX: 执行搜索操作"""
- datas = super(BlogSearchForm, self).search() # LJX: 调用父类搜索方法
- if not self.is_valid(): # LJX: 表单验证
- return self.no_query_found() # LJX: 如果没有查询条件,返回空结果
-
- if self.cleaned_data['querydata']:
- logger.info(self.cleaned_data['querydata']) # LJX: 记录搜索关键词
- return datas
\ No newline at end of file
diff --git a/blog-lsh/management/__init__.py b/blog-lsh/management/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/blog-lsh/management/commands/__init__.py b/blog-lsh/management/commands/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/blog-lsh/management/commands/build_index.py b/blog-lsh/management/commands/build_index.py
deleted file mode 100644
index 3c4acd7..0000000
--- a/blog-lsh/management/commands/build_index.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from django.core.management.base import BaseCommand
-
-from blog.documents import ElapsedTimeDocument, ArticleDocumentManager, ElaspedTimeDocumentManager, \
- ELASTICSEARCH_ENABLED
-
-
-# TODO 参数化
-class Command(BaseCommand):
- help = 'build search index'
-
- def handle(self, *args, **options):
- if ELASTICSEARCH_ENABLED:
- ElaspedTimeDocumentManager.build_index()
- manager = ElapsedTimeDocument()
- manager.init()
- manager = ArticleDocumentManager()
- manager.delete_index()
- manager.rebuild()
diff --git a/blog-lsh/management/commands/build_search_words.py b/blog-lsh/management/commands/build_search_words.py
deleted file mode 100644
index cfe7e0d..0000000
--- a/blog-lsh/management/commands/build_search_words.py
+++ /dev/null
@@ -1,13 +0,0 @@
-from django.core.management.base import BaseCommand
-
-from blog.models import Tag, Category
-
-
-# TODO 参数化
-class Command(BaseCommand):
- help = 'build search words'
-
- def handle(self, *args, **options):
- datas = set([t.name for t in Tag.objects.all()] +
- [t.name for t in Category.objects.all()])
- print('\n'.join(datas))
diff --git a/blog-lsh/management/commands/clear_cache.py b/blog-lsh/management/commands/clear_cache.py
deleted file mode 100644
index 0d66172..0000000
--- a/blog-lsh/management/commands/clear_cache.py
+++ /dev/null
@@ -1,11 +0,0 @@
-from django.core.management.base import BaseCommand
-
-from djangoblog.utils import cache
-
-
-class Command(BaseCommand):
- help = 'clear the whole cache'
-
- def handle(self, *args, **options):
- cache.clear()
- self.stdout.write(self.style.SUCCESS('Cleared cache\n'))
diff --git a/blog-lsh/management/commands/create_testdata.py b/blog-lsh/management/commands/create_testdata.py
deleted file mode 100644
index 675d2ba..0000000
--- a/blog-lsh/management/commands/create_testdata.py
+++ /dev/null
@@ -1,40 +0,0 @@
-from django.contrib.auth import get_user_model
-from django.contrib.auth.hashers import make_password
-from django.core.management.base import BaseCommand
-
-from blog.models import Article, Tag, Category
-
-
-class Command(BaseCommand):
- help = 'create test datas'
-
- def handle(self, *args, **options):
- user = get_user_model().objects.get_or_create(
- email='test@test.com', username='测试用户', password=make_password('test!q@w#eTYU'))[0]
-
- pcategory = Category.objects.get_or_create(
- name='我是父类目', parent_category=None)[0]
-
- category = Category.objects.get_or_create(
- name='子类目', parent_category=pcategory)[0]
-
- category.save()
- basetag = Tag()
- basetag.name = "标签"
- basetag.save()
- for i in range(1, 20):
- article = Article.objects.get_or_create(
- category=category,
- title='nice title ' + str(i),
- body='nice content ' + str(i),
- author=user)[0]
- tag = Tag()
- tag.name = "标签" + str(i)
- tag.save()
- article.tags.add(tag)
- article.tags.add(basetag)
- article.save()
-
- from djangoblog.utils import cache
- cache.clear()
- self.stdout.write(self.style.SUCCESS('created test datas \n'))
diff --git a/blog-lsh/management/commands/ping_baidu.py b/blog-lsh/management/commands/ping_baidu.py
deleted file mode 100644
index 2c7fbdd..0000000
--- a/blog-lsh/management/commands/ping_baidu.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from django.core.management.base import BaseCommand
-
-from djangoblog.spider_notify import SpiderNotify
-from djangoblog.utils import get_current_site
-from blog.models import Article, Tag, Category
-
-site = get_current_site().domain
-
-
-class Command(BaseCommand):
- help = 'notify baidu url'
-
- def add_arguments(self, parser):
- parser.add_argument(
- 'data_type',
- type=str,
- choices=[
- 'all',
- 'article',
- 'tag',
- 'category'],
- help='article : all article,tag : all tag,category: all category,all: All of these')
-
- def get_full_url(self, path):
- url = "https://{site}{path}".format(site=site, path=path)
- return url
-
- def handle(self, *args, **options):
- type = options['data_type']
- self.stdout.write('start get %s' % type)
-
- urls = []
- if type == 'article' or type == 'all':
- for article in Article.objects.filter(status='p'):
- urls.append(article.get_full_url())
- if type == 'tag' or type == 'all':
- for tag in Tag.objects.all():
- url = tag.get_absolute_url()
- urls.append(self.get_full_url(url))
- if type == 'category' or type == 'all':
- for category in Category.objects.all():
- url = category.get_absolute_url()
- urls.append(self.get_full_url(url))
-
- self.stdout.write(
- self.style.SUCCESS(
- 'start notify %d urls' %
- len(urls)))
- SpiderNotify.baidu_notify(urls)
- self.stdout.write(self.style.SUCCESS('finish notify'))
diff --git a/blog-lsh/management/commands/sync_user_avatar.py b/blog-lsh/management/commands/sync_user_avatar.py
deleted file mode 100644
index d0f4612..0000000
--- a/blog-lsh/management/commands/sync_user_avatar.py
+++ /dev/null
@@ -1,47 +0,0 @@
-import requests
-from django.core.management.base import BaseCommand
-from django.templatetags.static import static
-
-from djangoblog.utils import save_user_avatar
-from oauth.models import OAuthUser
-from oauth.oauthmanager import get_manager_by_type
-
-
-class Command(BaseCommand):
- help = 'sync user avatar'
-
- def test_picture(self, url):
- try:
- if requests.get(url, timeout=2).status_code == 200:
- return True
- except:
- pass
-
- def handle(self, *args, **options):
- static_url = static("../")
- users = OAuthUser.objects.all()
- self.stdout.write(f'开始同步{len(users)}个用户头像')
- for u in users:
- self.stdout.write(f'开始同步:{u.nickname}')
- url = u.picture
- if url:
- if url.startswith(static_url):
- if self.test_picture(url):
- continue
- else:
- if u.metadata:
- manage = get_manager_by_type(u.type)
- url = manage.get_picture(u.metadata)
- url = save_user_avatar(url)
- else:
- url = static('blog/img/avatar.png')
- else:
- url = save_user_avatar(url)
- else:
- url = static('blog/img/avatar.png')
- if url:
- self.stdout.write(
- f'结束同步:{u.nickname}.url:{url}')
- u.picture = url
- u.save()
- self.stdout.write('结束同步')
diff --git a/blog-lsh/middleware.py b/blog-lsh/middleware.py
deleted file mode 100644
index af8172d..0000000
--- a/blog-lsh/middleware.py
+++ /dev/null
@@ -1,51 +0,0 @@
-"""
-LJX: 中间件模块
-定义自定义中间件,用于处理请求和响应的额外逻辑
-包括性能监控、用户访问统计等功能
-"""
-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):
- """LJX: 在线中间件,用于监控页面渲染时间和用户访问信息"""
- def __init__(self, get_response=None):
- self.get_response = get_response
- super().__init__()
-
- def __call__(self, request):
- ''' LJX: 页面渲染时间监控 '''
- start_time = time.time() # LJX: 记录开始时间
- response = self.get_response(request) # LJX: 获取响应
- http_user_agent = request.META.get('HTTP_USER_AGENT', '') # LJX: 获取用户代理
- ip, _ = get_client_ip(request) # LJX: 获取客户端IP
- user_agent = parse(http_user_agent) # LJX: 解析用户代理信息
-
- if not response.streaming: # LJX: 如果不是流式响应
- try:
- cast_time = time.time() - start_time # LJX: 计算渲染耗时
- if ELASTICSEARCH_ENABLED:
- time_taken = round((cast_time) * 1000, 2) # LJX: 转换为毫秒
- url = request.path # LJX: 请求路径
- from django.utils import timezone
- # LJX: 创建性能监控记录
- ElaspedTimeDocumentManager.create(
- url=url,
- time_taken=time_taken,
- log_datetime=timezone.now(),
- useragent=user_agent,
- ip=ip)
- # LJX: 在响应内容中替换加载时间占位符
- response.content = response.content.replace(
- b'', str.encode(str(cast_time)[:5]))
- except Exception as e:
- logger.error("Error OnlineMiddleware: %s" % e) # LJX: 记录错误日志
-
- return response
\ No newline at end of file
diff --git a/blog-lsh/migrations/0001_initial.py b/blog-lsh/migrations/0001_initial.py
deleted file mode 100644
index 3d391b6..0000000
--- a/blog-lsh/migrations/0001_initial.py
+++ /dev/null
@@ -1,137 +0,0 @@
-# Generated by Django 4.1.7 on 2023-03-02 07:14
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-import mdeditor.fields
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ]
-
- operations = [
- migrations.CreateModel(
- name='BlogSettings',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('sitename', models.CharField(default='', max_length=200, verbose_name='网站名称')),
- ('site_description', models.TextField(default='', max_length=1000, verbose_name='网站描述')),
- ('site_seo_description', models.TextField(default='', max_length=1000, verbose_name='网站SEO描述')),
- ('site_keywords', models.TextField(default='', max_length=1000, verbose_name='网站关键字')),
- ('article_sub_length', models.IntegerField(default=300, verbose_name='文章摘要长度')),
- ('sidebar_article_count', models.IntegerField(default=10, verbose_name='侧边栏文章数目')),
- ('sidebar_comment_count', models.IntegerField(default=5, verbose_name='侧边栏评论数目')),
- ('article_comment_count', models.IntegerField(default=5, verbose_name='文章页面默认显示评论数目')),
- ('show_google_adsense', models.BooleanField(default=False, verbose_name='是否显示谷歌广告')),
- ('google_adsense_codes', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='广告内容')),
- ('open_site_comment', models.BooleanField(default=True, verbose_name='是否打开网站评论功能')),
- ('beiancode', models.CharField(blank=True, default='', max_length=2000, null=True, verbose_name='备案号')),
- ('analyticscode', models.TextField(default='', max_length=1000, verbose_name='网站统计代码')),
- ('show_gongan_code', models.BooleanField(default=False, verbose_name='是否显示公安备案号')),
- ('gongan_beiancode', models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='公安备案号')),
- ],
- options={
- 'verbose_name': '网站配置',
- 'verbose_name_plural': '网站配置',
- },
- ),
- migrations.CreateModel(
- name='Links',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=30, unique=True, verbose_name='链接名称')),
- ('link', models.URLField(verbose_name='链接地址')),
- ('sequence', models.IntegerField(unique=True, verbose_name='排序')),
- ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
- ('show_type', models.CharField(choices=[('i', '首页'), ('l', '列表页'), ('p', '文章页面'), ('a', '全站'), ('s', '友情链接页面')], default='i', max_length=1, verbose_name='显示类型')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ],
- options={
- 'verbose_name': '友情链接',
- 'verbose_name_plural': '友情链接',
- 'ordering': ['sequence'],
- },
- ),
- migrations.CreateModel(
- name='SideBar',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('name', models.CharField(max_length=100, verbose_name='标题')),
- ('content', models.TextField(verbose_name='内容')),
- ('sequence', models.IntegerField(unique=True, verbose_name='排序')),
- ('is_enable', models.BooleanField(default=True, verbose_name='是否启用')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ],
- options={
- 'verbose_name': '侧边栏',
- 'verbose_name_plural': '侧边栏',
- 'ordering': ['sequence'],
- },
- ),
- migrations.CreateModel(
- name='Tag',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('name', models.CharField(max_length=30, unique=True, verbose_name='标签名')),
- ('slug', models.SlugField(blank=True, default='no-slug', max_length=60)),
- ],
- options={
- 'verbose_name': '标签',
- 'verbose_name_plural': '标签',
- 'ordering': ['name'],
- },
- ),
- migrations.CreateModel(
- name='Category',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('name', models.CharField(max_length=30, unique=True, verbose_name='分类名')),
- ('slug', models.SlugField(blank=True, default='no-slug', max_length=60)),
- ('index', models.IntegerField(default=0, verbose_name='权重排序-越大越靠前')),
- ('parent_category', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='父级分类')),
- ],
- options={
- 'verbose_name': '分类',
- 'verbose_name_plural': '分类',
- 'ordering': ['-index'],
- },
- ),
- migrations.CreateModel(
- name='Article',
- fields=[
- ('id', models.AutoField(primary_key=True, serialize=False)),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('title', models.CharField(max_length=200, unique=True, verbose_name='标题')),
- ('body', mdeditor.fields.MDTextField(verbose_name='正文')),
- ('pub_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='发布时间')),
- ('status', models.CharField(choices=[('d', '草稿'), ('p', '发表')], default='p', max_length=1, verbose_name='文章状态')),
- ('comment_status', models.CharField(choices=[('o', '打开'), ('c', '关闭')], default='o', max_length=1, verbose_name='评论状态')),
- ('type', models.CharField(choices=[('a', '文章'), ('p', '页面')], default='a', max_length=1, verbose_name='类型')),
- ('views', models.PositiveIntegerField(default=0, verbose_name='浏览量')),
- ('article_order', models.IntegerField(default=0, verbose_name='排序,数字越大越靠前')),
- ('show_toc', models.BooleanField(default=False, verbose_name='是否显示toc目录')),
- ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='作者')),
- ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='分类')),
- ('tags', models.ManyToManyField(blank=True, to='blog.tag', verbose_name='标签集合')),
- ],
- options={
- 'verbose_name': '文章',
- 'verbose_name_plural': '文章',
- 'ordering': ['-article_order', '-pub_time'],
- 'get_latest_by': 'id',
- },
- ),
- ]
diff --git a/blog-lsh/migrations/0002_blogsettings_global_footer_and_more.py b/blog-lsh/migrations/0002_blogsettings_global_footer_and_more.py
deleted file mode 100644
index adbaa36..0000000
--- a/blog-lsh/migrations/0002_blogsettings_global_footer_and_more.py
+++ /dev/null
@@ -1,23 +0,0 @@
-# Generated by Django 4.1.7 on 2023-03-29 06:08
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('blog', '0001_initial'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='blogsettings',
- name='global_footer',
- field=models.TextField(blank=True, default='', null=True, verbose_name='公共尾部'),
- ),
- migrations.AddField(
- model_name='blogsettings',
- name='global_header',
- field=models.TextField(blank=True, default='', null=True, verbose_name='公共头部'),
- ),
- ]
diff --git a/blog-lsh/migrations/0003_blogsettings_comment_need_review.py b/blog-lsh/migrations/0003_blogsettings_comment_need_review.py
deleted file mode 100644
index e9f5502..0000000
--- a/blog-lsh/migrations/0003_blogsettings_comment_need_review.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 4.2.1 on 2023-05-09 07:45
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ('blog', '0002_blogsettings_global_footer_and_more'),
- ]
-
- operations = [
- migrations.AddField(
- model_name='blogsettings',
- name='comment_need_review',
- field=models.BooleanField(default=False, verbose_name='评论是否需要审核'),
- ),
- ]
diff --git a/blog-lsh/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py b/blog-lsh/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
deleted file mode 100644
index ceb1398..0000000
--- a/blog-lsh/migrations/0004_rename_analyticscode_blogsettings_analytics_code_and_more.py
+++ /dev/null
@@ -1,27 +0,0 @@
-# Generated by Django 4.2.1 on 2023-05-09 07:51
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
- dependencies = [
- ('blog', '0003_blogsettings_comment_need_review'),
- ]
-
- operations = [
- migrations.RenameField(
- model_name='blogsettings',
- old_name='analyticscode',
- new_name='analytics_code',
- ),
- migrations.RenameField(
- model_name='blogsettings',
- old_name='beiancode',
- new_name='beian_code',
- ),
- migrations.RenameField(
- model_name='blogsettings',
- old_name='sitename',
- new_name='site_name',
- ),
- ]
diff --git a/blog-lsh/migrations/0005_alter_article_options_alter_category_options_and_more.py b/blog-lsh/migrations/0005_alter_article_options_alter_category_options_and_more.py
deleted file mode 100644
index d08e853..0000000
--- a/blog-lsh/migrations/0005_alter_article_options_alter_category_options_and_more.py
+++ /dev/null
@@ -1,300 +0,0 @@
-# Generated by Django 4.2.5 on 2023-09-06 13:13
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-import mdeditor.fields
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('blog', '0004_rename_analyticscode_blogsettings_analytics_code_and_more'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='article',
- options={'get_latest_by': 'id', 'ordering': ['-article_order', '-pub_time'], 'verbose_name': 'article', 'verbose_name_plural': 'article'},
- ),
- migrations.AlterModelOptions(
- name='category',
- options={'ordering': ['-index'], 'verbose_name': 'category', 'verbose_name_plural': 'category'},
- ),
- migrations.AlterModelOptions(
- name='links',
- options={'ordering': ['sequence'], 'verbose_name': 'link', 'verbose_name_plural': 'link'},
- ),
- migrations.AlterModelOptions(
- name='sidebar',
- options={'ordering': ['sequence'], 'verbose_name': 'sidebar', 'verbose_name_plural': 'sidebar'},
- ),
- migrations.AlterModelOptions(
- name='tag',
- options={'ordering': ['name'], 'verbose_name': 'tag', 'verbose_name_plural': 'tag'},
- ),
- migrations.RemoveField(
- model_name='article',
- name='created_time',
- ),
- migrations.RemoveField(
- model_name='article',
- name='last_mod_time',
- ),
- migrations.RemoveField(
- model_name='category',
- name='created_time',
- ),
- migrations.RemoveField(
- model_name='category',
- name='last_mod_time',
- ),
- migrations.RemoveField(
- model_name='links',
- name='created_time',
- ),
- migrations.RemoveField(
- model_name='sidebar',
- name='created_time',
- ),
- migrations.RemoveField(
- model_name='tag',
- name='created_time',
- ),
- migrations.RemoveField(
- model_name='tag',
- name='last_mod_time',
- ),
- migrations.AddField(
- model_name='article',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
- ),
- migrations.AddField(
- model_name='article',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
- ),
- migrations.AddField(
- model_name='category',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
- ),
- migrations.AddField(
- model_name='category',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
- ),
- migrations.AddField(
- model_name='links',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
- ),
- migrations.AddField(
- model_name='sidebar',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
- ),
- migrations.AddField(
- model_name='tag',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
- ),
- migrations.AddField(
- model_name='tag',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
- ),
- migrations.AlterField(
- model_name='article',
- name='article_order',
- field=models.IntegerField(default=0, verbose_name='order'),
- ),
- migrations.AlterField(
- model_name='article',
- name='author',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'),
- ),
- migrations.AlterField(
- model_name='article',
- name='body',
- field=mdeditor.fields.MDTextField(verbose_name='body'),
- ),
- migrations.AlterField(
- model_name='article',
- name='category',
- field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='category'),
- ),
- migrations.AlterField(
- model_name='article',
- name='comment_status',
- field=models.CharField(choices=[('o', 'Open'), ('c', 'Close')], default='o', max_length=1, verbose_name='comment status'),
- ),
- migrations.AlterField(
- model_name='article',
- name='pub_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='publish time'),
- ),
- migrations.AlterField(
- model_name='article',
- name='show_toc',
- field=models.BooleanField(default=False, verbose_name='show toc'),
- ),
- migrations.AlterField(
- model_name='article',
- name='status',
- field=models.CharField(choices=[('d', 'Draft'), ('p', 'Published')], default='p', max_length=1, verbose_name='status'),
- ),
- migrations.AlterField(
- model_name='article',
- name='tags',
- field=models.ManyToManyField(blank=True, to='blog.tag', verbose_name='tag'),
- ),
- migrations.AlterField(
- model_name='article',
- name='title',
- field=models.CharField(max_length=200, unique=True, verbose_name='title'),
- ),
- migrations.AlterField(
- model_name='article',
- name='type',
- field=models.CharField(choices=[('a', 'Article'), ('p', 'Page')], default='a', max_length=1, verbose_name='type'),
- ),
- migrations.AlterField(
- model_name='article',
- name='views',
- field=models.PositiveIntegerField(default=0, verbose_name='views'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='article_comment_count',
- field=models.IntegerField(default=5, verbose_name='article comment count'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='article_sub_length',
- field=models.IntegerField(default=300, verbose_name='article sub length'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='google_adsense_codes',
- field=models.TextField(blank=True, default='', max_length=2000, null=True, verbose_name='adsense code'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='open_site_comment',
- field=models.BooleanField(default=True, verbose_name='open site comment'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='show_google_adsense',
- field=models.BooleanField(default=False, verbose_name='show adsense'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='sidebar_article_count',
- field=models.IntegerField(default=10, verbose_name='sidebar article count'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='sidebar_comment_count',
- field=models.IntegerField(default=5, verbose_name='sidebar comment count'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='site_description',
- field=models.TextField(default='', max_length=1000, verbose_name='site description'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='site_keywords',
- field=models.TextField(default='', max_length=1000, verbose_name='site keywords'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='site_name',
- field=models.CharField(default='', max_length=200, verbose_name='site name'),
- ),
- migrations.AlterField(
- model_name='blogsettings',
- name='site_seo_description',
- field=models.TextField(default='', max_length=1000, verbose_name='site seo description'),
- ),
- migrations.AlterField(
- model_name='category',
- name='index',
- field=models.IntegerField(default=0, verbose_name='index'),
- ),
- migrations.AlterField(
- model_name='category',
- name='name',
- field=models.CharField(max_length=30, unique=True, verbose_name='category name'),
- ),
- migrations.AlterField(
- model_name='category',
- name='parent_category',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='blog.category', verbose_name='parent category'),
- ),
- migrations.AlterField(
- model_name='links',
- name='is_enable',
- field=models.BooleanField(default=True, verbose_name='is show'),
- ),
- migrations.AlterField(
- model_name='links',
- name='last_mod_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
- ),
- migrations.AlterField(
- model_name='links',
- name='link',
- field=models.URLField(verbose_name='link'),
- ),
- migrations.AlterField(
- model_name='links',
- name='name',
- field=models.CharField(max_length=30, unique=True, verbose_name='link name'),
- ),
- migrations.AlterField(
- model_name='links',
- name='sequence',
- field=models.IntegerField(unique=True, verbose_name='order'),
- ),
- migrations.AlterField(
- model_name='links',
- name='show_type',
- field=models.CharField(choices=[('i', 'index'), ('l', 'list'), ('p', 'post'), ('a', 'all'), ('s', 'slide')], default='i', max_length=1, verbose_name='show type'),
- ),
- migrations.AlterField(
- model_name='sidebar',
- name='content',
- field=models.TextField(verbose_name='content'),
- ),
- migrations.AlterField(
- model_name='sidebar',
- name='is_enable',
- field=models.BooleanField(default=True, verbose_name='is enable'),
- ),
- migrations.AlterField(
- model_name='sidebar',
- name='last_mod_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='modify time'),
- ),
- migrations.AlterField(
- model_name='sidebar',
- name='name',
- field=models.CharField(max_length=100, verbose_name='title'),
- ),
- migrations.AlterField(
- model_name='sidebar',
- name='sequence',
- field=models.IntegerField(unique=True, verbose_name='order'),
- ),
- migrations.AlterField(
- model_name='tag',
- name='name',
- field=models.CharField(max_length=30, unique=True, verbose_name='tag name'),
- ),
- ]
diff --git a/blog-lsh/migrations/0006_alter_blogsettings_options.py b/blog-lsh/migrations/0006_alter_blogsettings_options.py
deleted file mode 100644
index e36feb4..0000000
--- a/blog-lsh/migrations/0006_alter_blogsettings_options.py
+++ /dev/null
@@ -1,17 +0,0 @@
-# Generated by Django 4.2.7 on 2024-01-26 02:41
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('blog', '0005_alter_article_options_alter_category_options_and_more'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='blogsettings',
- options={'verbose_name': 'Website configuration', 'verbose_name_plural': 'Website configuration'},
- ),
- ]
diff --git a/blog-lsh/migrations/__init__.py b/blog-lsh/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/blog-lsh/migrations/__pycache__/0001_initial.cpython-312.pyc b/blog-lsh/migrations/__pycache__/0001_initial.cpython-312.pyc
deleted file mode 100644
index 8a9660b..0000000
Binary files a/blog-lsh/migrations/__pycache__/0001_initial.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/migrations/__pycache__/0002_blogsettings_global_footer_and_more.cpython-312.pyc b/blog-lsh/migrations/__pycache__/0002_blogsettings_global_footer_and_more.cpython-312.pyc
deleted file mode 100644
index b0f3a9e..0000000
Binary files a/blog-lsh/migrations/__pycache__/0002_blogsettings_global_footer_and_more.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/migrations/__pycache__/0003_blogsettings_comment_need_review.cpython-312.pyc b/blog-lsh/migrations/__pycache__/0003_blogsettings_comment_need_review.cpython-312.pyc
deleted file mode 100644
index 844b4a3..0000000
Binary files a/blog-lsh/migrations/__pycache__/0003_blogsettings_comment_need_review.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/migrations/__pycache__/0004_rename_analyticscode_blogsettings_analytics_code_and_more.cpython-312.pyc b/blog-lsh/migrations/__pycache__/0004_rename_analyticscode_blogsettings_analytics_code_and_more.cpython-312.pyc
deleted file mode 100644
index 33a5e45..0000000
Binary files a/blog-lsh/migrations/__pycache__/0004_rename_analyticscode_blogsettings_analytics_code_and_more.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/migrations/__pycache__/0005_alter_article_options_alter_category_options_and_more.cpython-312.pyc b/blog-lsh/migrations/__pycache__/0005_alter_article_options_alter_category_options_and_more.cpython-312.pyc
deleted file mode 100644
index 28359fc..0000000
Binary files a/blog-lsh/migrations/__pycache__/0005_alter_article_options_alter_category_options_and_more.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/migrations/__pycache__/0006_alter_blogsettings_options.cpython-312.pyc b/blog-lsh/migrations/__pycache__/0006_alter_blogsettings_options.cpython-312.pyc
deleted file mode 100644
index 4710bc6..0000000
Binary files a/blog-lsh/migrations/__pycache__/0006_alter_blogsettings_options.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/migrations/__pycache__/__init__.cpython-312.pyc b/blog-lsh/migrations/__pycache__/__init__.cpython-312.pyc
deleted file mode 100644
index 4fe1f59..0000000
Binary files a/blog-lsh/migrations/__pycache__/__init__.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/models.py b/blog-lsh/models.py
deleted file mode 100644
index 74c8535..0000000
--- a/blog-lsh/models.py
+++ /dev/null
@@ -1,397 +0,0 @@
-"""
-LJX: 数据模型定义模块
-定义博客系统的核心数据模型,包括文章、分类、标签、友情链接等
-使用Django的ORM进行数据库映射和操作
-"""
-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):
- """LJX: 链接显示类型选择枚举"""
- I = ('i', _('index')) # LJX: 首页显示
- L = ('l', _('list')) # LJX: 列表页显示
- P = ('p', _('post')) # LJX: 文章页显示
- A = ('a', _('all')) # LJX: 所有页面显示
- S = ('s', _('slide')) # LJX: 幻灯片显示
-
-
-class BaseModel(models.Model):
- """LJX: 基础模型类,提供公共字段和方法"""
- id = models.AutoField(primary_key=True) # LJX: 自增主键
- creation_time = models.DateTimeField(_('creation time'), default=now) # LJX: 创建时间
- last_modify_time = models.DateTimeField(_('modify time'), default=now) # LJX: 最后修改时间
-
- def save(self, *args, **kwargs):
- """LJX: 重写保存方法,添加自动处理逻辑"""
- is_update_views = isinstance(
- self,
- Article) and 'update_fields' in kwargs and kwargs['update_fields'] == ['views'] # LJX: 检查是否是更新浏览量
- if is_update_views:
- Article.objects.filter(pk=self.pk).update(views=self.views) # LJX: 直接更新浏览量,避免递归
- else:
- if 'slug' in self.__dict__: # LJX: 如果有slug字段,自动生成
- slug = getattr(
- self, 'title') if 'title' in self.__dict__ else getattr(
- self, 'name') # LJX: 根据title或name生成slug
- setattr(self, 'slug', slugify(slug)) # LJX: 使用uuslug生成友好的URL
- super().save(*args, **kwargs) # LJX: 调用父类保存方法
-
- def get_full_url(self):
- """LJX: 获取完整URL"""
- site = get_current_site().domain # LJX: 获取当前站点域名
- url = "https://{site}{path}".format(site=site,
- path=self.get_absolute_url()) # LJX: 拼接完整URL
- return url
-
- class Meta:
- abstract = True # LJX: 抽象基类,不会创建数据库表
-
- @abstractmethod
- def get_absolute_url(self):
- """LJX: 抽象方法,子类必须实现获取绝对URL的方法"""
- pass
-
-
-class Article(BaseModel):
- """LJX: 文章模型,博客系统的核心数据模型"""
- STATUS_CHOICES = ( # LJX: 文章状态选择
- ('d', _('Draft')), # LJX: 草稿
- ('p', _('Published')), # LJX: 已发布
- )
- COMMENT_STATUS = ( # LJX: 评论状态选择
- ('o', _('Open')), # LJX: 开启评论
- ('c', _('Close')), # LJX: 关闭评论
- )
- TYPE = ( # LJX: 文章类型选择
- ('a', _('Article')), # LJX: 普通文章
- ('p', _('Page')), # LJX: 页面
- )
-
- # LJX: 文章核心字段
- title = models.CharField(_('title'), max_length=200, unique=True) # LJX: 标题,唯一
- body = MDTextField(_('body')) # LJX: 内容,使用Markdown编辑器
- pub_time = models.DateTimeField( # LJX: 发布时间
- _('publish time'), blank=False, null=False, default=now)
- status = models.CharField( # LJX: 状态
- _('status'),
- max_length=1,
- choices=STATUS_CHOICES,
- default='p') # LJX: 默认已发布
- comment_status = models.CharField( # LJX: 评论状态
- _('comment status'),
- max_length=1,
- choices=COMMENT_STATUS,
- default='o') # LJX: 默认开启评论
- type = models.CharField(_('type'), max_length=1, choices=TYPE, default='a') # LJX: 类型,默认普通文章
- views = models.PositiveIntegerField(_('views'), default=0) # LJX: 浏览量
- author = models.ForeignKey( # LJX: 作者,外键关联用户模型
- settings.AUTH_USER_MODEL,
- verbose_name=_('author'),
- blank=False,
- null=False,
- on_delete=models.CASCADE) # LJX: 级联删除
- article_order = models.IntegerField( # LJX: 文章排序
- _('order'), blank=False, null=False, default=0)
- show_toc = models.BooleanField(_('show toc'), blank=False, null=False, default=False) # LJX: 是否显示目录
- category = models.ForeignKey( # LJX: 分类,外键关联分类模型
- 'Category',
- verbose_name=_('category'),
- on_delete=models.CASCADE,
- blank=False,
- null=False)
- tags = models.ManyToManyField('Tag', verbose_name=_('tag'), blank=True) # LJX: 标签,多对多关系
-
- def body_to_string(self):
- """LJX: 将文章内容转换为字符串"""
- return self.body
-
- def __str__(self):
- """LJX: 字符串表示,返回文章标题"""
- return self.title
-
- class Meta:
- ordering = ['-article_order', '-pub_time'] # LJX: 默认按排序和发布时间降序
- verbose_name = _('article') # LJX: 单数名称
- verbose_name_plural = verbose_name # LJX: 复数名称
- get_latest_by = 'id' # LJX: 最新记录按ID
-
- def get_absolute_url(self):
- """LJX: 获取文章绝对URL,用于生成文章详情页链接"""
- return reverse('blog:detailbyid', kwargs={
- 'article_id': self.id,
- 'year': self.creation_time.year, # LJX: 包含年月日用于SEO友好的URL
- 'month': self.creation_time.month,
- 'day': self.creation_time.day
- })
-
- @cache_decorator(60 * 60 * 10) # LJX: 缓存10小时
- def get_category_tree(self):
- """LJX: 获取分类树,返回分类的层级结构"""
- tree = self.category.get_category_tree() # LJX: 调用分类的获取分类树方法
- names = list(map(lambda c: (c.name, c.get_absolute_url()), tree)) # LJX: 转换为名称和URL的元组列表
- return names
-
- def save(self, *args, **kwargs):
- """LJX: 重写保存方法"""
- super().save(*args, **kwargs)
-
- def viewed(self):
- """LJX: 增加文章浏览量"""
- self.views += 1
- self.save(update_fields=['views']) # LJX: 只更新views字段
-
- def comment_list(self):
- """LJX: 获取文章评论列表,使用缓存提高性能"""
- 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 # LJX: 如果缓存中存在,直接返回
- else:
- comments = self.comment_set.filter(is_enable=True).order_by('-id') # LJX: 获取已启用的评论,按ID降序
- cache.set(cache_key, comments, 60 * 100) # LJX: 缓存100分钟
- logger.info('set article comments:{id}'.format(id=self.id))
- return comments
-
- def get_admin_url(self):
- """LJX: 获取文章在Admin后台的URL"""
- info = (self._meta.app_label, self._meta.model_name)
- return reverse('admin:%s_%s_change' % info, args=(self.pk,))
-
- @cache_decorator(expiration=60 * 100) # LJX: 缓存100分钟
- def next_article(self):
- """LJX: 获取下一篇文章"""
- return Article.objects.filter(
- id__gt=self.id, status='p').order_by('id').first() # LJX: ID大于当前文章的第一篇已发布文章
-
- @cache_decorator(expiration=60 * 100) # LJX: 缓存100分钟
- def prev_article(self):
- """LJX: 获取上一篇文章"""
- return Article.objects.filter(id__lt=self.id, status='p').first() # LJX: ID小于当前文章的第一篇已发布文章
-
- def get_first_image_url(self):
- """LJX: 从文章内容中提取第一张图片的URL"""
- match = re.search(r'!\[.*?\]\((.+?)\)', self.body) # LJX: 使用正则匹配Markdown图片语法
- if match:
- return match.group(1) # LJX: 返回图片URL
- return ""
-
-class Category(BaseModel):
- """LJX: 文章分类模型,支持多级分类结构"""
- name = models.CharField(_('category name'), max_length=30, unique=True) # LJX: 分类名称,唯一
- parent_category = models.ForeignKey( # LJX: 父级分类,支持分类层级
- 'self',
- verbose_name=_('parent category'),
- blank=True,
- null=True,
- on_delete=models.CASCADE) # LJX: 自关联外键
- slug = models.SlugField(default='no-slug', max_length=60, blank=True) # LJX: URL友好名称
- index = models.IntegerField(default=0, verbose_name=_('index')) # LJX: 排序索引
-
- class Meta:
- ordering = ['-index'] # LJX: 按索引降序排列
- verbose_name = _('category') # LJX: 单数名称
- verbose_name_plural = verbose_name # LJX: 复数名称
-
- def get_absolute_url(self):
- """LJX: 获取分类绝对URL"""
- return reverse(
- 'blog:category_detail', kwargs={
- 'category_name': self.slug}) # LJX: 使用slug作为URL参数
-
- def __str__(self):
- """LJX: 字符串表示,返回分类名称"""
- return self.name
-
- @cache_decorator(60 * 60 * 10) # LJX: 缓存10小时
- def get_category_tree(self):
- """LJX: 递归获得分类目录的父级,返回从当前分类到根分类的路径"""
- categorys = [] # LJX: 存储分类路径
-
- def parse(category):
- """LJX: 递归解析分类父级"""
- categorys.append(category)
- if category.parent_category: # LJX: 如果存在父分类,继续递归
- parse(category.parent_category)
-
- parse(self) # LJX: 从当前分类开始解析
- return categorys
-
- @cache_decorator(60 * 60 * 10) # LJX: 缓存10小时
- def get_sub_categorys(self):
- """LJX: 获得当前分类目录所有子集,包括所有下级分类"""
- categorys = [] # LJX: 存储所有子分类
- all_categorys = Category.objects.all() # LJX: 获取所有分类
-
- def parse(category):
- """LJX: 递归解析子分类"""
- if category not in categorys:
- categorys.append(category) # LJX: 添加当前分类
- childs = all_categorys.filter(parent_category=category) # LJX: 查找直接子分类
- for child in childs:
- if category not in categorys:
- categorys.append(child) # LJX: 添加子分类
- parse(child) # LJX: 递归解析子分类的子分类
-
- parse(self) # LJX: 从当前分类开始解析
- return categorys
-
-
-class Tag(BaseModel):
- """LJX: 文章标签模型,用于文章分类和检索"""
- name = models.CharField(_('tag name'), max_length=30, unique=True) # LJX: 标签名称,唯一
- slug = models.SlugField(default='no-slug', max_length=60, blank=True) # LJX: URL友好名称
-
- def __str__(self):
- """LJX: 字符串表示,返回标签名称"""
- return self.name
-
- def get_absolute_url(self):
- """LJX: 获取标签绝对URL"""
- return reverse('blog:tag_detail', kwargs={'tag_name': self.slug}) # LJX: 使用slug作为URL参数
-
- @cache_decorator(60 * 60 * 10) # LJX: 缓存10小时
- def get_article_count(self):
- """LJX: 获取使用该标签的文章数量"""
- return Article.objects.filter(tags__name=self.name).distinct().count() # LJX: 去重计数
-
- class Meta:
- ordering = ['name'] # LJX: 按名称排序
- verbose_name = _('tag') # LJX: 单数名称
- verbose_name_plural = verbose_name # LJX: 复数名称
-
-
-class Links(models.Model):
- """LJX: 友情链接模型,管理网站的外部链接"""
-
- name = models.CharField(_('link name'), max_length=30, unique=True) # LJX: 链接名称,唯一
- link = models.URLField(_('link')) # LJX: 链接地址
- sequence = models.IntegerField(_('order'), unique=True) # LJX: 显示顺序,唯一
- is_enable = models.BooleanField( # LJX: 是否启用显示
- _('is show'), default=True, blank=False, null=False)
- show_type = models.CharField( # LJX: 显示类型
- _('show type'),
- max_length=1,
- choices=LinkShowType.choices,
- default=LinkShowType.I) # LJX: 默认在首页显示
- creation_time = models.DateTimeField(_('creation time'), default=now) # LJX: 创建时间
- last_mod_time = models.DateTimeField(_('modify time'), default=now) # LJX: 最后修改时间
-
- class Meta:
- ordering = ['sequence'] # LJX: 按顺序排序
- verbose_name = _('link') # LJX: 单数名称
- verbose_name_plural = verbose_name # LJX: 复数名称
-
- def __str__(self):
- """LJX: 字符串表示,返回链接名称"""
- return self.name
-
-
-class SideBar(models.Model):
- """LJX: 侧边栏模型,可以展示一些HTML内容"""
- name = models.CharField(_('title'), max_length=100) # LJX: 侧边栏标题
- content = models.TextField(_('content')) # LJX: 侧边栏内容,支持HTML
- sequence = models.IntegerField(_('order'), unique=True) # LJX: 显示顺序,唯一
- is_enable = models.BooleanField(_('is enable'), default=True) # LJX: 是否启用
- creation_time = models.DateTimeField(_('creation time'), default=now) # LJX: 创建时间
- last_mod_time = models.DateTimeField(_('modify time'), default=now) # LJX: 最后修改时间
-
- class Meta:
- ordering = ['sequence'] # LJX: 按顺序排序
- verbose_name = _('sidebar') # LJX: 单数名称
- verbose_name_plural = verbose_name # LJX: 复数名称
-
- def __str__(self):
- """LJX: 字符串表示,返回侧边栏名称"""
- return self.name
-
-
-class BlogSettings(models.Model):
- """LJX: 博客设置模型,存储博客的全局配置信息"""
- site_name = models.CharField( # LJX: 网站名称
- _('site name'),
- max_length=200,
- null=False,
- blank=False,
- default='')
- site_description = models.TextField( # LJX: 网站描述
- _('site description'),
- max_length=1000,
- null=False,
- blank=False,
- default='')
- site_seo_description = models.TextField( # LJX: 网站SEO描述
- _('site seo description'), max_length=1000, null=False, blank=False, default='')
- site_keywords = models.TextField( # LJX: 网站关键词
- _('site keywords'),
- max_length=1000,
- null=False,
- blank=False,
- default='')
- article_sub_length = models.IntegerField(_('article sub length'), default=300) # LJX: 文章摘要长度
- sidebar_article_count = models.IntegerField(_('sidebar article count'), default=10) # LJX: 侧边栏文章数量
- sidebar_comment_count = models.IntegerField(_('sidebar comment count'), default=5) # LJX: 侧边栏评论数量
- article_comment_count = models.IntegerField(_('article comment count'), default=5) # LJX: 文章评论显示数量
- show_google_adsense = models.BooleanField(_('show adsense'), default=False) # LJX: 是否显示Google广告
- google_adsense_codes = models.TextField( # LJX: Google广告代码
- _('adsense code'), max_length=2000, null=True, blank=True, default='')
- open_site_comment = models.BooleanField(_('open site comment'), default=True) # LJX: 是否开启全站评论
- global_header = models.TextField("公共头部", null=True, blank=True, default='') # LJX: 全局头部HTML
- global_footer = models.TextField("公共尾部", null=True, blank=True, default='') # LJX: 全局尾部HTML
- beian_code = models.CharField( # LJX: 备案号
- '备案号',
- max_length=2000,
- null=True,
- blank=True,
- default='')
- analytics_code = models.TextField( # LJX: 网站统计代码
- "网站统计代码",
- max_length=1000,
- null=False,
- blank=False,
- default='')
- show_gongan_code = models.BooleanField( # LJX: 是否显示公安备案号
- '是否显示公安备案号', default=False, null=False)
- gongan_beiancode = models.TextField( # LJX: 公安备案号
- '公安备案号',
- max_length=2000,
- null=True,
- blank=True,
- default='')
- comment_need_review = models.BooleanField( # LJX: 评论是否需要审核
- '评论是否需要审核', default=False, null=False)
-
- class Meta:
- verbose_name = _('Website configuration') # LJX: 单数名称
- verbose_name_plural = verbose_name # LJX: 复数名称
-
- def __str__(self):
- """LJX: 字符串表示,返回网站名称"""
- return self.site_name
-
- def clean(self):
- """LJX: 数据清洗验证,确保只能有一个配置实例"""
- if BlogSettings.objects.exclude(id=self.id).count():
- raise ValidationError(_('There can only be one configuration')) # LJX: 只能有一个配置
-
- def save(self, *args, **kwargs):
- """LJX: 重写保存方法,保存后清除缓存"""
- super().save(*args, **kwargs)
- from djangoblog.utils import cache
- cache.clear() # LJX: 清除缓存,使配置立即生效
\ No newline at end of file
diff --git a/blog-lsh/search_indexes.py b/blog-lsh/search_indexes.py
deleted file mode 100644
index 09af7eb..0000000
--- a/blog-lsh/search_indexes.py
+++ /dev/null
@@ -1,20 +0,0 @@
-"""
-LJX: Haystack搜索索引配置模块
-定义Django Haystack的搜索索引配置,用于全文搜索功能
-"""
-from haystack import indexes
-
-from blog.models import Article
-
-
-class ArticleIndex(indexes.SearchIndex, indexes.Indexable):
- """LJX: 文章搜索索引类,定义文章的搜索字段和索引行为"""
- text = indexes.CharField(document=True, use_template=True) # LJX: 主搜索字段,使用模板定义
-
- def get_model(self):
- """LJX: 返回要索引的模型类"""
- return Article
-
- def index_queryset(self, using=None):
- """LJX: 返回要索引的查询集,只索引已发布的文章"""
- return self.get_model().objects.filter(status='p') # LJX: 只索引已发布状态的文章
\ No newline at end of file
diff --git a/blog-lsh/static/account/css/account.css b/blog-lsh/static/account/css/account.css
deleted file mode 100644
index 7d4cec7..0000000
--- a/blog-lsh/static/account/css/account.css
+++ /dev/null
@@ -1,9 +0,0 @@
-.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/blog-lsh/static/account/js/account.js b/blog-lsh/static/account/js/account.js
deleted file mode 100644
index f1a8771..0000000
--- a/blog-lsh/static/account/js/account.js
+++ /dev/null
@@ -1,47 +0,0 @@
-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/blog-lsh/static/assets/css/bootstrap.min.css b/blog-lsh/static/assets/css/bootstrap.min.css
deleted file mode 100644
index ed3905e..0000000
--- a/blog-lsh/static/assets/css/bootstrap.min.css
+++ /dev/null
@@ -1,6 +0,0 @@
-/*!
- * 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/blog-lsh/static/assets/css/docs.min.css b/blog-lsh/static/assets/css/docs.min.css
deleted file mode 100644
index 3945197..0000000
--- a/blog-lsh/static/assets/css/docs.min.css
+++ /dev/null
@@ -1,11 +0,0 @@
-/*!
- * 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/blog-lsh/static/assets/css/ie10-viewport-bug-workaround.css b/blog-lsh/static/assets/css/ie10-viewport-bug-workaround.css
deleted file mode 100644
index 4b9518e..0000000
--- a/blog-lsh/static/assets/css/ie10-viewport-bug-workaround.css
+++ /dev/null
@@ -1,13 +0,0 @@
-/*!
- * 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/blog-lsh/static/assets/css/signin.css b/blog-lsh/static/assets/css/signin.css
deleted file mode 100644
index 121fb0d..0000000
--- a/blog-lsh/static/assets/css/signin.css
+++ /dev/null
@@ -1,58 +0,0 @@
-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/blog-lsh/static/assets/css/todc-bootstrap.min.css b/blog-lsh/static/assets/css/todc-bootstrap.min.css
deleted file mode 100644
index 66c9cb2..0000000
--- a/blog-lsh/static/assets/css/todc-bootstrap.min.css
+++ /dev/null
@@ -1,6 +0,0 @@
-/*!
- * 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/blog-lsh/static/assets/img/checkmark.png b/blog-lsh/static/assets/img/checkmark.png
deleted file mode 100644
index 4bd0eb3..0000000
Binary files a/blog-lsh/static/assets/img/checkmark.png and /dev/null differ
diff --git a/blog-lsh/static/assets/js/ie-emulation-modes-warning.js b/blog-lsh/static/assets/js/ie-emulation-modes-warning.js
deleted file mode 100644
index 3f97ba5..0000000
--- a/blog-lsh/static/assets/js/ie-emulation-modes-warning.js
+++ /dev/null
@@ -1,51 +0,0 @@
-// 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/blog-lsh/static/assets/js/ie10-viewport-bug-workaround.js b/blog-lsh/static/assets/js/ie10-viewport-bug-workaround.js
deleted file mode 100644
index 479a6eb..0000000
--- a/blog-lsh/static/assets/js/ie10-viewport-bug-workaround.js
+++ /dev/null
@@ -1,23 +0,0 @@
-/*!
- * 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/blog-lsh/static/blog/css/ie.css b/blog-lsh/static/blog/css/ie.css
deleted file mode 100644
index 706f510..0000000
--- a/blog-lsh/static/blog/css/ie.css
+++ /dev/null
@@ -1,273 +0,0 @@
-/*
-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/blog-lsh/static/blog/css/nprogress.css b/blog-lsh/static/blog/css/nprogress.css
deleted file mode 100644
index 90c7b6c..0000000
--- a/blog-lsh/static/blog/css/nprogress.css
+++ /dev/null
@@ -1,74 +0,0 @@
-/* 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/blog-lsh/static/blog/css/oauth_style.css b/blog-lsh/static/blog/css/oauth_style.css
deleted file mode 100644
index 8af78af..0000000
--- a/blog-lsh/static/blog/css/oauth_style.css
+++ /dev/null
@@ -1,305 +0,0 @@
-
-.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/blog-lsh/static/blog/css/style.css b/blog-lsh/static/blog/css/style.css
deleted file mode 100644
index d43f7f3..0000000
--- a/blog-lsh/static/blog/css/style.css
+++ /dev/null
@@ -1,2504 +0,0 @@
-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,iVBORw0KGgoAAAANSUhEUgAAABoAAAB8CAYAAAB356CJAAAACXBIWXMAAAsTAAALEwEAmpwYAAAKTWlDQ1BQaG90b3Nob3AgSUNDIHByb2ZpbGUAAHjanVN3WJP3Fj7f92UPVkLY8LGXbIEAIiOsCMgQWaIQkgBhhBASQMWFiApWFBURnEhVxILVCkidiOKgKLhnQYqIWotVXDjuH9yntX167+3t+9f7vOec5/zOec8PgBESJpHmomoAOVKFPDrYH49PSMTJvYACFUjgBCAQ5svCZwXFAADwA3l4fnSwP/wBr28AAgBw1S4kEsfh/4O6UCZXACCRAOAiEucLAZBSAMguVMgUAMgYALBTs2QKAJQAAGx5fEIiAKoNAOz0ST4FANipk9wXANiiHKkIAI0BAJkoRyQCQLsAYFWBUiwCwMIAoKxAIi4EwK4BgFm2MkcCgL0FAHaOWJAPQGAAgJlCLMwAIDgCAEMeE80DIEwDoDDSv+CpX3CFuEgBAMDLlc2XS9IzFLiV0Bp38vDg4iHiwmyxQmEXKRBmCeQinJebIxNI5wNMzgwAABr50cH+OD+Q5+bk4eZm52zv9MWi/mvwbyI+IfHf/ryMAgQAEE7P79pf5eXWA3DHAbB1v2upWwDaVgBo3/ldM9sJoFoK0Hr5i3k4/EAenqFQyDwdHAoLC+0lYqG9MOOLPv8z4W/gi372/EAe/tt68ABxmkCZrcCjg/1xYW52rlKO58sEQjFu9+cj/seFf/2OKdHiNLFcLBWK8ViJuFAiTcd5uVKRRCHJleIS6X8y8R+W/QmTdw0ArIZPwE62B7XLbMB+7gECiw5Y0nYAQH7zLYwaC5EAEGc0Mnn3AACTv/mPQCsBAM2XpOMAALzoGFyolBdMxggAAESggSqwQQcMwRSswA6cwR28wBcCYQZEQAwkwDwQQgbkgBwKoRiWQRlUwDrYBLWwAxqgEZrhELTBMTgN5+ASXIHrcBcGYBiewhi8hgkEQcgIE2EhOogRYo7YIs4IF5mOBCJhSDSSgKQg6YgUUSLFyHKkAqlCapFdSCPyLXIUOY1cQPqQ28ggMor8irxHMZSBslED1AJ1QLmoHxqKxqBz0XQ0D12AlqJr0Rq0Hj2AtqKn0UvodXQAfYqOY4DRMQ5mjNlhXIyHRWCJWBomxxZj5Vg1Vo81Yx1YN3YVG8CeYe8IJAKLgBPsCF6EEMJsgpCQR1hMWEOoJewjtBK6CFcJg4Qxwicik6hPtCV6EvnEeGI6sZBYRqwm7iEeIZ4lXicOE1+TSCQOyZLkTgohJZAySQtJa0jbSC2kU6Q+0hBpnEwm65Btyd7kCLKArCCXkbeQD5BPkvvJw+S3FDrFiOJMCaIkUqSUEko1ZT/lBKWfMkKZoKpRzame1AiqiDqfWkltoHZQL1OHqRM0dZolzZsWQ8ukLaPV0JppZ2n3aC/pdLoJ3YMeRZfQl9Jr6Afp5+mD9HcMDYYNg8dIYigZaxl7GacYtxkvmUymBdOXmchUMNcyG5lnmA+Yb1VYKvYqfBWRyhKVOpVWlX6V56pUVXNVP9V5qgtUq1UPq15WfaZGVbNQ46kJ1Bar1akdVbupNq7OUndSj1DPUV+jvl/9gvpjDbKGhUaghkijVGO3xhmNIRbGMmXxWELWclYD6yxrmE1iW7L57Ex2Bfsbdi97TFNDc6pmrGaRZp3mcc0BDsax4PA52ZxKziHODc57LQMtPy2x1mqtZq1+rTfaetq+2mLtcu0W7eva73VwnUCdLJ31Om0693UJuja6UbqFutt1z+o+02PreekJ9cr1Dund0Uf1bfSj9Rfq79bv0R83MDQINpAZbDE4Y/DMkGPoa5hpuNHwhOGoEctoupHEaKPRSaMnuCbuh2fjNXgXPmasbxxirDTeZdxrPGFiaTLbpMSkxeS+Kc2Ua5pmutG003TMzMgs3KzYrMnsjjnVnGueYb7ZvNv8jYWlRZzFSos2i8eW2pZ8ywWWTZb3rJhWPlZ5VvVW16xJ1lzrLOtt1ldsUBtXmwybOpvLtqitm63Edptt3xTiFI8p0in1U27aMez87ArsmuwG7Tn2YfYl9m32zx3MHBId1jt0O3xydHXMdmxwvOuk4TTDqcSpw+lXZxtnoXOd8zUXpkuQyxKXdpcXU22niqdun3rLleUa7rrStdP1o5u7m9yt2W3U3cw9xX2r+00umxvJXcM970H08PdY4nHM452nm6fC85DnL152Xlle+70eT7OcJp7WMG3I28Rb4L3Le2A6Pj1l+s7pAz7GPgKfep+Hvqa+It89viN+1n6Zfgf8nvs7+sv9j/i/4XnyFvFOBWABwQHlAb2BGoGzA2sDHwSZBKUHNQWNBbsGLww+FUIMCQ1ZH3KTb8AX8hv5YzPcZyya0RXKCJ0VWhv6MMwmTB7WEY6GzwjfEH5vpvlM6cy2CIjgR2yIuB9pGZkX+X0UKSoyqi7qUbRTdHF09yzWrORZ+2e9jvGPqYy5O9tqtnJ2Z6xqbFJsY+ybuIC4qriBeIf4RfGXEnQTJAntieTE2MQ9ieNzAudsmjOc5JpUlnRjruXcorkX5unOy553PFk1WZB8OIWYEpeyP+WDIEJQLxhP5aduTR0T8oSbhU9FvqKNolGxt7hKPJLmnVaV9jjdO31D+miGT0Z1xjMJT1IreZEZkrkj801WRNberM/ZcdktOZSclJyjUg1plrQr1zC3KLdPZisrkw3keeZtyhuTh8r35CP5c/PbFWyFTNGjtFKuUA4WTC+oK3hbGFt4uEi9SFrUM99m/ur5IwuCFny9kLBQuLCz2Lh4WfHgIr9FuxYji1MXdy4xXVK6ZHhp8NJ9y2jLspb9UOJYUlXyannc8o5Sg9KlpUMrglc0lamUycturvRauWMVYZVkVe9ql9VbVn8qF5VfrHCsqK74sEa45uJXTl/VfPV5bdra3kq3yu3rSOuk626s91m/r0q9akHV0IbwDa0b8Y3lG19tSt50oXpq9Y7NtM3KzQM1YTXtW8y2rNvyoTaj9nqdf13LVv2tq7e+2Sba1r/dd3vzDoMdFTve75TsvLUreFdrvUV99W7S7oLdjxpiG7q/5n7duEd3T8Wej3ulewf2Re/ranRvbNyvv7+yCW1SNo0eSDpw5ZuAb9qb7Zp3tXBaKg7CQeXBJ9+mfHvjUOihzsPcw83fmX+39QjrSHkr0jq/dawto22gPaG97+iMo50dXh1Hvrf/fu8x42N1xzWPV56gnSg98fnkgpPjp2Snnp1OPz3Umdx590z8mWtdUV29Z0PPnj8XdO5Mt1/3yfPe549d8Lxw9CL3Ytslt0utPa49R35w/eFIr1tv62X3y+1XPK509E3rO9Hv03/6asDVc9f41y5dn3m978bsG7duJt0cuCW69fh29u0XdwruTNxdeo94r/y+2v3qB/oP6n+0/rFlwG3g+GDAYM/DWQ/vDgmHnv6U/9OH4dJHzEfVI0YjjY+dHx8bDRq98mTOk+GnsqcTz8p+Vv9563Or59/94vtLz1j82PAL+YvPv655qfNy76uprzrHI8cfvM55PfGm/K3O233vuO+638e9H5ko/ED+UPPR+mPHp9BP9z7nfP78L/eE8/sl0p8zAAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1MAAA6mAAADqYAAAXb5JfxUYAAAbdSURBVHja5NlbbBRVGAfw5VID+LAK8cEoxqTgmw8kPPhwipTGxJTDUAVBQBMNKtZboiDE2ES8pFEjGhNkkCrin3JbZo4YCqloUOoKJCDIRWyRAgW6R3dobU2bJtj6+eCMTqczs2d3Zh6Mm3xpdvc7++vMnHNmzvlSRJQqJgA8B8AC8EQx7YoBxgD4CAC54i0Ao2KDAIwCsNGDOPF6nNBLAYgTiyNDAKYDGCwA/Q7gtpIhAKMBHC+AOPF5FGiBIuLEXaVCR4uEzKIhAHcViRCAP4OuVRi0pgSIACwvFurw/ohhGJTP56m7u5vy+TwZhuEHHVKGANzmh3R3d48IH2wQwPWq0CIv5ByJN/L5vN9RzVKF3vQ29kOcULlOQZAZ8YjWq0JHI1wjAvClKnTJr+sq9joCcEoV6itxDDmRU4UoYvT8f6GeiFCXKpSLCJ1XhU5GhI6oQs0RoT2qUENESFeFlkeEXlCFZkeEqlWhWyNCtxSE7GdsPSL0AYAxgRCACQB2xzAzEAABYMIIyEYOxIQ4sR/AOC+UiRlxYvM/EID5CSFO1DjQoYShFmfFMJgwdC0FYHzCCAEYck5dZ8LQWQdCwpAe19xWKCocqAzA1YSQiwBGuwfs2yHJpwDcEBJHQtqu9s4MU0KSHy+wBF0c1NsATPabVL/ye6IBML4AVAbgik/bvUGz9zyf5HrFTY9VPm0XBkFlAH7xrN5uVYQmAuh3P0Q6M3fQje81V/LWIne+1gY9oPglTwLQai+Wby8SugnAj/Y2W7nqqnyUz2cagDb7P24DoAXshI2Nsl9XZXdXb/etintjMBswVrJxQ0H3rMG4oYEAaOA/e+rqAqC6uKHyAKg8VsjGDnqQg7Hve9tQrQeqTQpKuybOfgDpRCDParAhkZKBC5pmQ9MShWysvtg2RSOZTKYu0WqLYRhjTdMUQghqbGxMrtpimuYuIQQJIWj79u3JVFsMw3jHQYQQfhuC0asthmFUCiGG3JAQgjZv3hxftaW5uXmMEOJnLyKEoK1bt8ZXbTEMY5kfIoSgHTt2xFdtEUK0BkE7d+6Mp9piGMY9QYgQgkzTjKfaYprmJvcPn/vhOHV8+D511j5EuUWzqXPZEmpd9x59/102WrVFCPGrG7myopZkzUyS2ox/Ijf3bjq/8mkvpl5tMQzjDvfRdKx7l+TcmZR7bAH1nThGf167Rn0njlHn0gcoV1NJrWvXlFZtMQzjaTfU+eQSknMqqP+n0+R+9Z05RXJOBXUsW1xatcUwjAY3lLu/iuScCvJ7SW0GXVlUXVq1xTTN/cOghfcGH5E2w++I1Kot3vFzceP6vy++5xrlli6gXM1MOvOxXlq1RQiR946by6tXkpw7vNfJmko698qL1NzUVFq1RQgx4DdIL2z7lDqfephyD2l05dlH6ELjRj9EvdoSNiMozA7qtQlVSAjx34H6IkJdqlBXROi86oBtjwgdUYUOR4T2qEJmREhXnVTrI0IvqEJLIg7YalWoXAUKqSwXrrZIzsZIzvSfT5woCTr2zdckOftAchZcbZGcTZCc7ZacUfu+vQWhTCYzAjq9vZEkZyQ5E5KzkdUWGzlgJ9GFjetLgtrerXcgkpztl5yN80IZVwJdWvVMQcizqiAAdPHZR90QSc7+rbZIzuZ7vqTcfZXUdvp0KOR9/j78bQvlaiq9EEnOahzokM+X1P7FnlBoy5Ytw69P4yd+CEnOWlKSs9GSs0G/hI41bxQ1WNtffj4IupaSnI0P+JJyD1bT8aNHlbr24ZYWys2rCoKGnFPXGYS1N+1S6nFnPtaDEJKcnXUgBCVdfrHWF9q2bdswqGPZ4jBId6DZIUnUnm0J7Qgnd5lhCEnOKhyoTHJ2NSjx0qurQifTCytqw5CLkrPR7gH7dkhy6HaZ5OzbkLarvTPDlJDkRQWg+UG9TXI22W9S/conWUrOrisAjbVPkbft3qDZe55P8qsqmx6SsxU+bRcGQWWSs19ciX9Izm5WhG6UnPW52vY4M3fQje81V3JR1RbJ2Vr32Cl0h50kOWuVnHVIzm4vErpJcvaj5MySnKlVWyRnw7bHLF1L9WbTWm823dabTZP9V7N0bUQ7yVnp1RZL16p69k0eshHqzaapZ9/kIUvX4q22WLqW7cpMJzfUlZlOlq5l44YGrQ3VwyBrQzVZujYYNzRg6Rr1tkz8G2qZSJaukaVrA7GfOkvX6LemqdSbTdNvTVMdKPZTV2fpGl3dNIt6s2m6ummWA9XFDZXbP0zdn93pIGTpWnncUMrStYMugOz3qSSgWg9UmxSUtnSt30b67feJQClL1xpsqMH5LClomg1NSxpKWbpW736v0v6vAQCo4CbBrd8RBQAAAABJRU5ErkJggg==") 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;*/
-}
\ No newline at end of file
diff --git a/blog-lsh/static/blog/fonts/fonts.css b/blog-lsh/static/blog/fonts/fonts.css
deleted file mode 100644
index c1a29cf..0000000
--- a/blog-lsh/static/blog/fonts/fonts.css
+++ /dev/null
@@ -1,378 +0,0 @@
-/* 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/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2
deleted file mode 100644
index 2c47cc5..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUehpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2
deleted file mode 100644
index 601706a..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OUuhp.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2
deleted file mode 100644
index 119f1d7..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OVuhpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2
deleted file mode 100644
index d56688f..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OX-hpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2
deleted file mode 100644
index e1f546c..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXOhpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2
deleted file mode 100644
index 0f17e3d..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXehpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2
deleted file mode 100644
index 50d8183..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UN_r8OXuhpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2
deleted file mode 100644
index b935198..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUehpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUuhp.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUuhp.woff2
deleted file mode 100644
index d77bb4c..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOUuhp.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2
deleted file mode 100644
index e293ffc..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOVuhpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2
deleted file mode 100644
index 46fd61b..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOX-hpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2
deleted file mode 100644
index 88a1616..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXOhpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2
deleted file mode 100644
index 2100b6b..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXehpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2 b/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2
deleted file mode 100644
index d54c7c0..0000000
Binary files a/blog-lsh/static/blog/fonts/mem5YaGs126MiZpBA-UNirkOXuhpOqc.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2 b/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2
deleted file mode 100644
index 683014d..0000000
Binary files a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Udc1UAw.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2 b/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2
deleted file mode 100644
index 72eb246..0000000
Binary files a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Vdc1UAw.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2 b/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2
deleted file mode 100644
index 6da5562..0000000
Binary files a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Wdc1UAw.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2 b/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2
deleted file mode 100644
index 2f22c67..0000000
Binary files a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Xdc1UAw.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2 b/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2
deleted file mode 100644
index 28c6c76..0000000
Binary files a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0Zdc0.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2 b/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2
deleted file mode 100644
index fdeb9a4..0000000
Binary files a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0adc1UAw.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2 b/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2
deleted file mode 100644
index 2a48105..0000000
Binary files a/blog-lsh/static/blog/fonts/mem6YaGs126MiZpBA-UFUK0ddc1UAw.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2 b/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2
deleted file mode 100644
index 1ddef14..0000000
Binary files a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFUZ0bbck.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2 b/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2
deleted file mode 100644
index 1d5e847..0000000
Binary files a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFVZ0b.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFVp0bbck.woff2 b/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFVp0bbck.woff2
deleted file mode 100644
index 0e22822..0000000
Binary files a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFVp0bbck.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 b/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2
deleted file mode 100644
index f621005..0000000
Binary files a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFW50bbck.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2 b/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2
deleted file mode 100644
index 49018f9..0000000
Binary files a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWJ0bbck.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2 b/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2
deleted file mode 100644
index a69a2ef..0000000
Binary files a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWZ0bbck.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2 b/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2
deleted file mode 100644
index fb5fb99..0000000
Binary files a/blog-lsh/static/blog/fonts/mem8YaGs126MiZpBA-UFWp0bbck.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2
deleted file mode 100644
index db9a5bd..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hkIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2
deleted file mode 100644
index 7a9e2e3..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hlIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2
deleted file mode 100644
index a9d17c0..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hmIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2
deleted file mode 100644
index b76038f..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hnIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2
deleted file mode 100644
index 06a53d5..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hoIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2
deleted file mode 100644
index 94dc4e4..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hrIqM.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2
deleted file mode 100644
index 8197c39..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKWyV9hvIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2
deleted file mode 100644
index b9cd540..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhkIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2
deleted file mode 100644
index fa2e381..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhlIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2
deleted file mode 100644
index da3f7ec..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhmIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2
deleted file mode 100644
index 0b42119..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhnIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2
deleted file mode 100644
index 36bdef1..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhoIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2
deleted file mode 100644
index 4b60ed4..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhrIqM.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2 b/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2
deleted file mode 100644
index d214090..0000000
Binary files a/blog-lsh/static/blog/fonts/memnYaGs126MiZpBA-UFUKXGUdhvIqOjjg.woff2 and /dev/null differ
diff --git a/blog-lsh/static/blog/img/avatar.png b/blog-lsh/static/blog/img/avatar.png
deleted file mode 100644
index 320756f..0000000
Binary files a/blog-lsh/static/blog/img/avatar.png and /dev/null differ
diff --git a/blog-lsh/static/blog/img/icon-sn.svg b/blog-lsh/static/blog/img/icon-sn.svg
deleted file mode 100644
index 2c2da0a..0000000
--- a/blog-lsh/static/blog/img/icon-sn.svg
+++ /dev/null
@@ -1 +0,0 @@
-icon-sn
\ No newline at end of file
diff --git a/blog-lsh/static/blog/js/blog.js b/blog-lsh/static/blog/js/blog.js
deleted file mode 100644
index c50dd7d..0000000
--- a/blog-lsh/static/blog/js/blog.js
+++ /dev/null
@@ -1,91 +0,0 @@
-/**
- * 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/blog-lsh/static/blog/js/html5.js b/blog-lsh/static/blog/js/html5.js
deleted file mode 100644
index 6168aac..0000000
--- a/blog-lsh/static/blog/js/html5.js
+++ /dev/null
@@ -1,8 +0,0 @@
-/*
- 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="",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 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/blog-lsh/static/blog/js/nprogress.js b/blog-lsh/static/blog/js/nprogress.js
deleted file mode 100644
index d29c2aa..0000000
--- a/blog-lsh/static/blog/js/nprogress.js
+++ /dev/null
@@ -1,480 +0,0 @@
-/* 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/blog-lsh/static/mathjax/js/mathjax-config.js b/blog-lsh/static/mathjax/js/mathjax-config.js
deleted file mode 100644
index 158ba65..0000000
--- a/blog-lsh/static/mathjax/js/mathjax-config.js
+++ /dev/null
@@ -1,21 +0,0 @@
-$(function () {
- MathJax.Hub.Config({
- showProcessingMessages: false, //关闭js加载过程信息
- messageStyle: "none", //不显示信息
- extensions: ["tex2jax.js"], jax: ["input/TeX", "output/HTML-CSS"], displayAlign: "left", tex2jax: {
- inlineMath: [["$", "$"]], //行内公式选择$
- displayMath: [["$$", "$$"]], //段内公式选择$$
- skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code', 'a'], //避开某些标签
- }, "HTML-CSS": {
- availableFonts: ["STIX", "TeX"], //可选字体
- showMathMenu: false //关闭右击菜单显示
- }
- });
- // 识别范围 => 文章内容、评论内容标签
- const contentId = document.getElementById("content");
- const commentId = document.getElementById("comments");
- MathJax.Hub.Queue(["Typeset", MathJax.Hub, contentId, commentId]);
-})
-
-
-
diff --git a/blog-lsh/static/pygments/default.css b/blog-lsh/static/pygments/default.css
deleted file mode 100644
index 73e6e49..0000000
--- a/blog-lsh/static/pygments/default.css
+++ /dev/null
@@ -1,293 +0,0 @@
-.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/blog-lsh/templatetags/__init__.py b/blog-lsh/templatetags/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/blog-lsh/templatetags/__pycache__/__init__.cpython-312.pyc b/blog-lsh/templatetags/__pycache__/__init__.cpython-312.pyc
deleted file mode 100644
index 8e5af91..0000000
Binary files a/blog-lsh/templatetags/__pycache__/__init__.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/templatetags/__pycache__/blog_tags.cpython-312.pyc b/blog-lsh/templatetags/__pycache__/blog_tags.cpython-312.pyc
deleted file mode 100644
index e47902d..0000000
Binary files a/blog-lsh/templatetags/__pycache__/blog_tags.cpython-312.pyc and /dev/null differ
diff --git a/blog-lsh/templatetags/blog_tags.py b/blog-lsh/templatetags/blog_tags.py
deleted file mode 100644
index d6cd5d5..0000000
--- a/blog-lsh/templatetags/blog_tags.py
+++ /dev/null
@@ -1,344 +0,0 @@
-import hashlib
-import logging
-import random
-import urllib
-
-from django import template
-from django.conf import settings
-from django.db.models import Q
-from django.shortcuts import get_object_or_404
-from django.template.defaultfilters import stringfilter
-from django.templatetags.static import static
-from django.urls import reverse
-from django.utils.safestring import mark_safe
-
-from blog.models import Article, Category, Tag, Links, SideBar, LinkShowType
-from comments.models import Comment
-from djangoblog.utils import CommonMarkdown, sanitize_html
-from djangoblog.utils import cache
-from djangoblog.utils import get_current_site
-from oauth.models import OAuthUser
-from djangoblog.plugin_manage import hooks
-
-logger = logging.getLogger(__name__)
-
-register = template.Library()
-
-
-@register.simple_tag(takes_context=True)
-def head_meta(context):
- return mark_safe(hooks.apply_filters('head_meta', '', context))
-
-
-@register.simple_tag
-def timeformat(data):
- try:
- return data.strftime(settings.TIME_FORMAT)
- except Exception as e:
- logger.error(e)
- return ""
-
-
-@register.simple_tag
-def datetimeformat(data):
- try:
- return data.strftime(settings.DATE_TIME_FORMAT)
- except Exception as e:
- logger.error(e)
- return ""
-
-
-@register.filter()
-@stringfilter
-def custom_markdown(content):
- return mark_safe(CommonMarkdown.get_markdown(content))
-
-
-@register.simple_tag
-def get_markdown_toc(content):
- from djangoblog.utils import CommonMarkdown
- body, toc = CommonMarkdown.get_markdown_with_toc(content)
- return mark_safe(toc)
-
-
-@register.filter()
-@stringfilter
-def comment_markdown(content):
- content = CommonMarkdown.get_markdown(content)
- return mark_safe(sanitize_html(content))
-
-
-@register.filter(is_safe=True)
-@stringfilter
-def truncatechars_content(content):
- """
- 获得文章内容的摘要
- :param content:
- :return:
- """
- from django.template.defaultfilters import truncatechars_html
- from djangoblog.utils import get_blog_setting
- blogsetting = get_blog_setting()
- return truncatechars_html(content, blogsetting.article_sub_length)
-
-
-@register.filter(is_safe=True)
-@stringfilter
-def truncate(content):
- from django.utils.html import strip_tags
-
- return strip_tags(content)[:150]
-
-
-@register.inclusion_tag('blog/tags/breadcrumb.html')
-def load_breadcrumb(article):
- """
- 获得文章面包屑
- :param article:
- :return:
- """
- names = article.get_category_tree()
- from djangoblog.utils import get_blog_setting
- blogsetting = get_blog_setting()
- site = get_current_site().domain
- names.append((blogsetting.site_name, '/'))
- names = names[::-1]
-
- return {
- 'names': names,
- 'title': article.title,
- 'count': len(names) + 1
- }
-
-
-@register.inclusion_tag('blog/tags/article_tag_list.html')
-def load_articletags(article):
- """
- 文章标签
- :param article:
- :return:
- """
- tags = article.tags.all()
- tags_list = []
- for tag in tags:
- url = tag.get_absolute_url()
- count = tag.get_article_count()
- tags_list.append((
- url, count, tag, random.choice(settings.BOOTSTRAP_COLOR_TYPES)
- ))
- return {
- 'article_tags_list': tags_list
- }
-
-
-@register.inclusion_tag('blog/tags/sidebar.html')
-def load_sidebar(user, linktype):
- """
- 加载侧边栏
- :return:
- """
- value = cache.get("sidebar" + linktype)
- if value:
- value['user'] = user
- return value
- else:
- logger.info('load sidebar')
- from djangoblog.utils import get_blog_setting
- blogsetting = get_blog_setting()
- recent_articles = Article.objects.filter(
- status='p')[:blogsetting.sidebar_article_count]
- sidebar_categorys = Category.objects.all()
- extra_sidebars = SideBar.objects.filter(
- is_enable=True).order_by('sequence')
- most_read_articles = Article.objects.filter(status='p').order_by(
- '-views')[:blogsetting.sidebar_article_count]
- dates = Article.objects.datetimes('creation_time', 'month', order='DESC')
- links = Links.objects.filter(is_enable=True).filter(
- Q(show_type=str(linktype)) | Q(show_type=LinkShowType.A))
- commment_list = Comment.objects.filter(is_enable=True).order_by(
- '-id')[:blogsetting.sidebar_comment_count]
- # 标签云 计算字体大小
- # 根据总数计算出平均值 大小为 (数目/平均值)*步长
- increment = 5
- tags = Tag.objects.all()
- sidebar_tags = None
- if tags and len(tags) > 0:
- s = [t for t in [(t, t.get_article_count()) for t in tags] if t[1]]
- count = sum([t[1] for t in s])
- dd = 1 if (count == 0 or not len(tags)) else count / len(tags)
- import random
- sidebar_tags = list(
- map(lambda x: (x[0], x[1], (x[1] / dd) * increment + 10), s))
- random.shuffle(sidebar_tags)
-
- value = {
- 'recent_articles': recent_articles,
- 'sidebar_categorys': sidebar_categorys,
- 'most_read_articles': most_read_articles,
- 'article_dates': dates,
- 'sidebar_comments': commment_list,
- 'sidabar_links': links,
- 'show_google_adsense': blogsetting.show_google_adsense,
- 'google_adsense_codes': blogsetting.google_adsense_codes,
- 'open_site_comment': blogsetting.open_site_comment,
- 'show_gongan_code': blogsetting.show_gongan_code,
- 'sidebar_tags': sidebar_tags,
- 'extra_sidebars': extra_sidebars
- }
- cache.set("sidebar" + linktype, value, 60 * 60 * 60 * 3)
- logger.info('set sidebar cache.key:{key}'.format(key="sidebar" + linktype))
- value['user'] = user
- return value
-
-
-@register.inclusion_tag('blog/tags/article_meta_info.html')
-def load_article_metas(article, user):
- """
- 获得文章meta信息
- :param article:
- :return:
- """
- return {
- 'article': article,
- 'user': user
- }
-
-
-@register.inclusion_tag('blog/tags/article_pagination.html')
-def load_pagination_info(page_obj, page_type, tag_name):
- previous_url = ''
- next_url = ''
- if page_type == '':
- if page_obj.has_next():
- next_number = page_obj.next_page_number()
- next_url = reverse('blog:index_page', kwargs={'page': next_number})
- if page_obj.has_previous():
- previous_number = page_obj.previous_page_number()
- previous_url = reverse(
- 'blog:index_page', kwargs={
- 'page': previous_number})
- if page_type == '分类标签归档':
- tag = get_object_or_404(Tag, name=tag_name)
- if page_obj.has_next():
- next_number = page_obj.next_page_number()
- next_url = reverse(
- 'blog:tag_detail_page',
- kwargs={
- 'page': next_number,
- 'tag_name': tag.slug})
- if page_obj.has_previous():
- previous_number = page_obj.previous_page_number()
- previous_url = reverse(
- 'blog:tag_detail_page',
- kwargs={
- 'page': previous_number,
- 'tag_name': tag.slug})
- if page_type == '作者文章归档':
- if page_obj.has_next():
- next_number = page_obj.next_page_number()
- next_url = reverse(
- 'blog:author_detail_page',
- kwargs={
- 'page': next_number,
- 'author_name': tag_name})
- if page_obj.has_previous():
- previous_number = page_obj.previous_page_number()
- previous_url = reverse(
- 'blog:author_detail_page',
- kwargs={
- 'page': previous_number,
- 'author_name': tag_name})
-
- if page_type == '分类目录归档':
- category = get_object_or_404(Category, name=tag_name)
- if page_obj.has_next():
- next_number = page_obj.next_page_number()
- next_url = reverse(
- 'blog:category_detail_page',
- kwargs={
- 'page': next_number,
- 'category_name': category.slug})
- if page_obj.has_previous():
- previous_number = page_obj.previous_page_number()
- previous_url = reverse(
- 'blog:category_detail_page',
- kwargs={
- 'page': previous_number,
- 'category_name': category.slug})
-
- return {
- 'previous_url': previous_url,
- 'next_url': next_url,
- 'page_obj': page_obj
- }
-
-
-@register.inclusion_tag('blog/tags/article_info.html')
-def load_article_detail(article, isindex, user):
- """
- 加载文章详情
- :param article:
- :param isindex:是否列表页,若是列表页只显示摘要
- :return:
- """
- from djangoblog.utils import get_blog_setting
- blogsetting = get_blog_setting()
-
- return {
- 'article': article,
- 'isindex': isindex,
- 'user': user,
- 'open_site_comment': blogsetting.open_site_comment,
- }
-
-
-# return only the URL of the gravatar
-# TEMPLATE USE: {{ email|gravatar_url:150 }}
-@register.filter
-def gravatar_url(email, size=40):
- """获得gravatar头像"""
- cachekey = 'gravatat/' + email
- url = cache.get(cachekey)
- if url:
- return url
- else:
- usermodels = OAuthUser.objects.filter(email=email)
- if usermodels:
- o = list(filter(lambda x: x.picture is not None, usermodels))
- if o:
- return o[0].picture
- email = email.encode('utf-8')
-
- default = static('blog/img/avatar.png')
-
- url = "https://www.gravatar.com/avatar/%s?%s" % (hashlib.md5(
- email.lower()).hexdigest(), urllib.parse.urlencode({'d': default, 's': str(size)}))
- cache.set(cachekey, url, 60 * 60 * 10)
- logger.info('set gravatar cache.key:{key}'.format(key=cachekey))
- return url
-
-
-@register.filter
-def gravatar(email, size=40):
- """获得gravatar头像"""
- url = gravatar_url(email, size)
- return mark_safe(
- ' ' %
- (url, size, size))
-
-
-@register.simple_tag
-def query(qs, **kwargs):
- """ template tag which allows queryset filtering. Usage:
- {% query books author=author as mybooks %}
- {% for book in mybooks %}
- ...
- {% endfor %}
- """
- return qs.filter(**kwargs)
-
-
-@register.filter
-def addstr(arg1, arg2):
- """concatenate arg1 & arg2"""
- return str(arg1) + str(arg2)
diff --git a/blog-lsh/tests.py b/blog-lsh/tests.py
deleted file mode 100644
index d415fdb..0000000
--- a/blog-lsh/tests.py
+++ /dev/null
@@ -1,284 +0,0 @@
-"""
-LJX: 测试模块
-包含blog应用的单元测试和集成测试,确保代码质量和功能正确性
-"""
-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):
- """LJX: 文章相关功能测试类"""
- def setUp(self):
- """LJX: 测试初始化设置,创建测试客户端和工厂"""
- self.client = Client() # LJX: Django测试客户端
- self.factory = RequestFactory() # LJX: 请求工厂,用于创建请求对象
-
- def test_validate_article(self):
- """LJX: 测试文章验证和相关功能"""
- site = get_current_site().domain
- # LJX: 创建测试用户
- 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()
-
- # LJX: 测试用户详情页访问
- response = self.client.get(user.get_absolute_url())
- self.assertEqual(response.status_code, 200)
-
- # LJX: 测试后台页面访问
- response = self.client.get('/admin/servermanager/emailsendlog/')
- response = self.client.get('admin/admin/logentry/')
-
- # LJX: 创建测试侧边栏
- s = SideBar()
- s.sequence = 1
- s.name = 'test'
- s.content = 'test content'
- s.is_enable = True
- s.save()
-
- # LJX: 创建测试分类
- category = Category()
- category.name = "category"
- category.creation_time = timezone.now()
- category.last_mod_time = timezone.now()
- category.save()
-
- # LJX: 创建测试标签
- tag = Tag()
- tag.name = "nicetag"
- tag.save()
-
- # LJX: 创建测试文章
- article = Article()
- article.title = "nicetitle"
- article.body = "nicecontent"
- article.author = user
- article.category = category
- article.type = 'a'
- article.status = 'p'
- article.save()
-
- # LJX: 测试标签关联
- self.assertEqual(0, article.tags.count())
- article.tags.add(tag)
- article.save()
- self.assertEqual(1, article.tags.count())
-
- # LJX: 创建多篇文章用于测试分页和搜索
- 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()
-
- # LJX: 测试Elasticsearch搜索功能
- from blog.documents import ELASTICSEARCH_ENABLED
- if ELASTICSEARCH_ENABLED:
- call_command("build_index") # LJX: 构建搜索索引
- response = self.client.get('/search', {'q': 'nicetitle'})
- self.assertEqual(response.status_code, 200)
-
- # LJX: 测试文章详情页访问
- response = self.client.get(article.get_absolute_url())
- self.assertEqual(response.status_code, 200)
-
- # LJX: 测试搜索引擎通知
- from djangoblog.spider_notify import SpiderNotify
- SpiderNotify.notify(article.get_absolute_url())
-
- # LJX: 测试标签页访问
- response = self.client.get(tag.get_absolute_url())
- self.assertEqual(response.status_code, 200)
-
- # LJX: 测试分类页访问
- response = self.client.get(category.get_absolute_url())
- self.assertEqual(response.status_code, 200)
-
- # LJX: 测试搜索功能
- response = self.client.get('/search', {'q': 'django'})
- self.assertEqual(response.status_code, 200)
-
- # LJX: 测试模板标签
- s = load_articletags(article)
- self.assertIsNotNone(s)
-
- # LJX: 用户登录测试
- self.client.login(username='liangliangyy', password='liangliangyy')
-
- # LJX: 测试归档页面
- response = self.client.get(reverse('blog:archives'))
- self.assertEqual(response.status_code, 200)
-
- # LJX: 测试分页功能
- 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)
-
- # LJX: 测试搜索表单
- f = BlogSearchForm()
- f.search()
-
- # LJX: 测试百度通知功能
- from djangoblog.spider_notify import SpiderNotify
- SpiderNotify.baidu_notify([article.get_full_url()])
-
- # LJX: 测试Gravatar相关功能
- from blog.templatetags.blog_tags import gravatar_url, gravatar
- u = gravatar_url('liangliangyy@gmail.com')
- u = gravatar('liangliangyy@gmail.com')
-
- # LJX: 测试友情链接功能
- 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)
-
- # LJX: 测试Feed和Sitemap
- response = self.client.get('/feed/')
- self.assertEqual(response.status_code, 200)
-
- response = self.client.get('/sitemap.xml')
- self.assertEqual(response.status_code, 200)
-
- # LJX: 测试后台删除操作
- 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):
- """LJX: 检查分页功能是否正常工作"""
- 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']: # LJX: 测试上一页链接
- response = self.client.get(s['previous_url'])
- self.assertEqual(response.status_code, 200)
- if s['next_url']: # LJX: 测试下一页链接
- response = self.client.get(s['next_url'])
- self.assertEqual(response.status_code, 200)
-
- def test_image(self):
- """LJX: 测试图片上传和处理功能"""
- import requests
- # LJX: 下载测试图片
- 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)
-
- # LJX: 测试未授权上传
- rsp = self.client.post('/upload')
- self.assertEqual(rsp.status_code, 403)
-
- # LJX: 测试授权上传
- 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) # LJX: 清理测试文件
-
- # LJX: 测试工具函数
- 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):
- """LJX: 测试错误页面处理"""
- rsp = self.client.get('/eee')
- self.assertEqual(rsp.status_code, 404)
-
- def test_commands(self):
- """LJX: 测试自定义管理命令"""
- 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()
-
- # LJX: 测试OAuth配置
- 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()
-
- # LJX: 测试各种管理命令
- 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")
\ No newline at end of file
diff --git a/blog-lsh/urls.py b/blog-lsh/urls.py
deleted file mode 100644
index 95aa89d..0000000
--- a/blog-lsh/urls.py
+++ /dev/null
@@ -1,80 +0,0 @@
-"""
-LJX: URL路由配置模块
-定义blog应用的所有URL路由规则,将URL映射到相应的视图函数或类
-使用Django的path函数定义RESTful风格的URL
-"""
-from django.urls import path
-from django.views.decorators.cache import cache_page
-
-from . import views
-
-app_name = "blog" # LJX: 应用命名空间,用于URL反向解析
-urlpatterns = [
- # LJX: 首页路由 - 显示文章列表
- path(
- r'',
- views.IndexView.as_view(), # LJX: 使用类视图处理首页
- name='index'), # LJX: URL名称,用于反向解析
- # LJX: 首页分页路由 - 显示指定页码的文章列表
- path(
- r'page//', # LJX: 页码参数,整数类型
- views.IndexView.as_view(),
- name='index_page'),
- # LJX: 文章详情页路由 - 使用年月日和文章ID构建SEO友好的URL
- path(
- r'article////.html', # LJX: 包含年月日的URL结构
- views.ArticleDetailView.as_view(),
- name='detailbyid'), # LJX: 通过ID获取文章详情
- # LJX: 分类详情页路由 - 显示指定分类的文章列表
- path(
- r'category/.html', # LJX: 使用slug作为分类标识
- views.CategoryDetailView.as_view(),
- name='category_detail'),
- # LJX: 分类分页路由 - 显示指定分类的指定页码
- path(
- r'category//.html',
- views.CategoryDetailView.as_view(),
- name='category_detail_page'),
- # LJX: 作者详情页路由 - 显示指定作者的文章列表
- path(
- r'author/.html', # LJX: 作者用户名作为参数
- views.AuthorDetailView.as_view(),
- name='author_detail'),
- # LJX: 作者分页路由 - 显示指定作者的指定页码
- path(
- r'author//.html',
- views.AuthorDetailView.as_view(),
- name='author_detail_page'),
- # LJX: 标签详情页路由 - 显示指定标签的文章列表
- path(
- r'tag/.html', # LJX: 使用slug作为标签标识
- views.TagDetailView.as_view(),
- name='tag_detail'),
- # LJX: 标签分页路由 - 显示指定标签的指定页码
- path(
- r'tag//.html',
- views.TagDetailView.as_view(),
- name='tag_detail_page'),
- # LJX: 文章归档页路由 - 显示所有文章的归档列表,使用缓存提高性能
- path(
- 'archives.html',
- cache_page( # LJX: 页面缓存,60*60秒=1小时
- 60 * 60)(
- views.ArchivesView.as_view()),
- name='archives'),
- # LJX: 友情链接页路由 - 显示所有友情链接
- path(
- 'links.html',
- views.LinkListView.as_view(),
- name='links'),
- # LJX: 文件上传路由 - 处理图片等文件的上传
- path(
- r'upload',
- views.fileupload, # LJX: 使用函数视图处理文件上传
- name='upload'),
- # LJX: 缓存清理路由 - 用于手动清理系统缓存
- path(
- r'clean',
- views.clean_cache_view,
- name='clean'),
-]
\ No newline at end of file
diff --git a/blog-lsh/views.py b/blog-lsh/views.py
deleted file mode 100644
index 327cba5..0000000
--- a/blog-lsh/views.py
+++ /dev/null
@@ -1,398 +0,0 @@
-"""
-LJX: 视图处理模块
-定义blog应用的所有视图函数和类,处理HTTP请求并返回响应
-包含文章列表、详情、分类、标签、搜索等核心功能
-"""
-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):
- """LJX: 文章列表基类视图,提供文章列表的通用功能"""
- # LJX: template_name属性用于指定使用哪个模板进行渲染
- template_name = 'blog/article_index.html'
-
- # LJX: context_object_name属性用于给上下文变量取名(在模板中使用该名字)
- context_object_name = 'article_list'
-
- # LJX: 页面类型,分类目录或标签列表等
- page_type = ''
- paginate_by = settings.PAGINATE_BY # LJX: 每页显示文章数量
- page_kwarg = 'page' # LJX: URL中页码参数的名称
- link_type = LinkShowType.L # LJX: 友情链接显示类型
-
- def get_view_cache_key(self):
- """LJX: 获取视图缓存键"""
- return self.request.get['pages']
-
- @property
- def page_number(self):
- """LJX: 获取当前页码属性"""
- page_kwarg = self.page_kwarg
- page = self.kwargs.get( # LJX: 从URL参数获取页码
- page_kwarg) or self.request.GET.get(page_kwarg) or 1 # LJX: 从GET参数获取或默认第一页
- return page
-
- def get_queryset_cache_key(self):
- """LJX: 子类重写.获得queryset的缓存key"""
- raise NotImplementedError()
-
- def get_queryset_data(self):
- """LJX: 子类重写.获取queryset的数据"""
- raise NotImplementedError()
-
- def get_queryset_from_cache(self, cache_key):
- '''LJX: 缓存页面数据,提高性能'''
- value = cache.get(cache_key)
- if value:
- logger.info('get view cache.key:{key}'.format(key=cache_key))
- return value # LJX: 如果缓存中存在,直接返回
- else:
- article_list = self.get_queryset_data() # LJX: 从数据库获取数据
- cache.set(cache_key, article_list) # LJX: 设置缓存
- logger.info('set view cache.key:{key}'.format(key=cache_key))
- return article_list
-
- def get_queryset(self):
- '''LJX: 重写默认,从缓存获取数据'''
- key = self.get_queryset_cache_key()
- value = self.get_queryset_from_cache(key)
- return value
-
- def get_context_data(self, **kwargs):
- """LJX: 添加上下文数据"""
- kwargs['linktype'] = self.link_type # LJX: 添加链接类型到上下文
- return super(ArticleListView, self).get_context_data(**kwargs)
-
-
-class IndexView(ArticleListView):
- '''LJX: 首页视图,显示最新的文章列表'''
- # LJX: 友情链接类型 - 在首页显示
- link_type = LinkShowType.I
-
- def get_queryset_data(self):
- """LJX: 获取首页文章数据 - 只获取已发布的普通文章"""
- article_list = Article.objects.filter(type='a', status='p') # LJX: 过滤普通类型且已发布的文章
- return article_list
-
- def get_queryset_cache_key(self):
- """LJX: 生成首页缓存键,包含页码信息"""
- cache_key = 'index_{page}'.format(page=self.page_number)
- return cache_key
-
-
-class ArticleDetailView(DetailView):
- '''LJX: 文章详情页面视图,显示单篇文章的完整内容'''
- template_name = 'blog/article_detail.html' # LJX: 详情页模板
- model = Article # LJX: 关联的模型
- pk_url_kwarg = 'article_id' # LJX: URL中主键参数的名称
- context_object_name = "article" # LJX: 模板中使用的变量名
-
- def get_context_data(self, **kwargs):
- """LJX: 添加上下文数据,包括评论表单、评论列表等"""
- comment_form = CommentForm() # LJX: 评论表单实例
-
- # LJX: 获取文章评论列表
- article_comments = self.object.comment_list()
- parent_comments = article_comments.filter(parent_comment=None) # LJX: 顶级评论(无父评论)
- blog_setting = get_blog_setting() # LJX: 获取博客设置
-
- # LJX: 评论分页处理
- paginator = Paginator(parent_comments, blog_setting.article_comment_count)
- page = self.request.GET.get('comment_page', '1') # LJX: 获取评论页码
- if not page.isnumeric():
- page = 1 # LJX: 如果不是数字,默认第一页
- else:
- page = int(page)
- if page < 1:
- page = 1 # LJX: 页码不能小于1
- if page > paginator.num_pages:
- page = paginator.num_pages # LJX: 页码不能大于总页数
-
- p_comments = paginator.page(page) # LJX: 获取指定页的评论
- next_page = p_comments.next_page_number() if p_comments.has_next() else None # LJX: 下一页页码
- prev_page = p_comments.previous_page_number() if p_comments.has_previous() else None # LJX: 上一页页码
-
- # LJX: 添加上下一页评论的URL
- 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'
-
- # LJX: 添加各种上下文数据
- 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 # LJX: 下一篇文章
- kwargs['prev_article'] = self.object.prev_article # LJX: 上一篇文章
-
- context = super(ArticleDetailView, self).get_context_data(**kwargs)
- article = self.object
-
- # LJX: Action Hook, 通知插件"文章详情已获取"
- hooks.run_action('after_article_body_get', article=article, request=self.request)
- # LJX: Filter Hook, 允许插件修改文章正文
- article.body = hooks.apply_filters(ARTICLE_CONTENT_HOOK_NAME, article.body, article=article,
- request=self.request)
-
- return context
-
-
-class CategoryDetailView(ArticleListView):
- '''LJX: 分类目录列表视图,显示指定分类下的所有文章'''
- page_type = "分类目录归档" # LJX: 页面类型描述
-
- def get_queryset_data(self):
- """LJX: 获取分类下的文章数据,包括子分类的文章"""
- slug = self.kwargs['category_name'] # LJX: 从URL获取分类slug
- category = get_object_or_404(Category, slug=slug) # LJX: 获取分类对象,不存在返回404
-
- categoryname = category.name
- self.categoryname = categoryname
- categorynames = list(
- map(lambda c: c.name, category.get_sub_categorys())) # LJX: 获取所有子分类名称
- # LJX: 获取分类及其子分类下的所有已发布文章
- article_list = Article.objects.filter(
- category__name__in=categorynames, status='p')
- return article_list
-
- def get_queryset_cache_key(self):
- """LJX: 生成分类页缓存键"""
- 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):
- """LJX: 添加上下文数据"""
- categoryname = self.categoryname
- try:
- categoryname = categoryname.split('/')[-1] # LJX: 处理分类路径,取最后一部分
- except BaseException:
- pass
- kwargs['page_type'] = CategoryDetailView.page_type
- kwargs['tag_name'] = categoryname # LJX: 实际上这里是分类名,变量名保持兼容
- return super(CategoryDetailView, self).get_context_data(**kwargs)
-
-
-class AuthorDetailView(ArticleListView):
- '''LJX: 作者详情页视图,显示指定作者的所有文章'''
- page_type = '作者文章归档' # LJX: 页面类型描述
-
- def get_queryset_cache_key(self):
- """LJX: 生成作者页缓存键"""
- from uuslug import slugify
- author_name = slugify(self.kwargs['author_name']) # LJX: 对作者名进行slugify处理
- cache_key = 'author_{author_name}_{page}'.format(
- author_name=author_name, page=self.page_number)
- return cache_key
-
- def get_queryset_data(self):
- """LJX: 获取作者的文章数据"""
- author_name = self.kwargs['author_name']
- article_list = Article.objects.filter(
- author__username=author_name, type='a', status='p') # LJX: 过滤指定作者的已发布普通文章
- return article_list
-
- def get_context_data(self, **kwargs):
- """LJX: 添加上下文数据"""
- author_name = self.kwargs['author_name']
- kwargs['page_type'] = AuthorDetailView.page_type
- kwargs['tag_name'] = author_name # LJX: 变量名保持兼容
- return super(AuthorDetailView, self).get_context_data(**kwargs)
-
-
-class TagDetailView(ArticleListView):
- '''LJX: 标签列表页面视图,显示指定标签的所有文章'''
- page_type = '分类标签归档' # LJX: 页面类型描述
-
- def get_queryset_data(self):
- """LJX: 获取标签下的文章数据"""
- slug = self.kwargs['tag_name']
- tag = get_object_or_404(Tag, slug=slug) # LJX: 获取标签对象
- tag_name = tag.name
- self.name = tag_name
- article_list = Article.objects.filter(
- tags__name=tag_name, type='a', status='p') # LJX: 多对多关系查询
- return article_list
-
- def get_queryset_cache_key(self):
- """LJX: 生成标签页缓存键"""
- 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):
- """LJX: 添加上下文数据"""
- 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):
- '''LJX: 文章归档页面视图,显示所有文章的按时间归档'''
- page_type = '文章归档' # LJX: 页面类型描述
- paginate_by = None # LJX: 不分页,显示所有文章
- page_kwarg = None # LJX: 无页码参数
- template_name = 'blog/article_archives.html' # LJX: 归档页专用模板
-
- def get_queryset_data(self):
- """LJX: 获取所有已发布文章"""
- return Article.objects.filter(status='p').all()
-
- def get_queryset_cache_key(self):
- """LJX: 生成归档页缓存键"""
- cache_key = 'archives'
- return cache_key
-
-
-class LinkListView(ListView):
- """LJX: 友情链接列表视图,显示所有启用的友情链接"""
- model = Links # LJX: 关联的模型
- template_name = 'blog/links_list.html' # LJX: 模板名称
-
- def get_queryset(self):
- """LJX: 获取已启用的友情链接"""
- return Links.objects.filter(is_enable=True)
-
-
-class EsSearchView(SearchView):
- """LJX: Elasticsearch搜索视图,处理全文搜索请求"""
- def get_context(self):
- """LJX: 获取搜索上下文数据"""
- paginator, page = self.build_page() # LJX: 构建分页
- context = {
- "query": self.query, # LJX: 搜索关键词
- "form": self.form, # LJX: 搜索表单
- "page": page, # LJX: 当前页
- "paginator": paginator, # LJX: 分页器
- "suggestion": None, # LJX: 搜索建议
- }
- # LJX: 如果有拼写建议,添加到上下文
- 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()) # LJX: 添加额外上下文
-
- return context
-
-
-@csrf_exempt # LJX: 免除CSRF保护,用于文件上传
-def fileupload(request):
- """
- LJX: 文件上传视图函数
- 该方法需自己写调用端来上传图片,该方法仅提供图床功能
- """
- if request.method == 'POST':
- sign = request.GET.get('sign', None) # LJX: 获取签名参数
- if not sign:
- return HttpResponseForbidden() # LJX: 无签名,拒绝访问
- if not sign == get_sha256(get_sha256(settings.SECRET_KEY)):
- return HttpResponseForbidden() # LJX: 签名验证失败,拒绝访问
-
- response = []
- for filename in request.FILES: # LJX: 遍历所有上传的文件
- timestr = timezone.now().strftime('%Y/%m/%d') # LJX: 按日期创建目录结构
- imgextensions = ['jpg', 'png', 'jpeg', 'bmp'] # LJX: 图片文件扩展名
- fname = u''.join(str(filename))
- isimage = len([i for i in imgextensions if fname.find(i) >= 0]) > 0 # LJX: 判断是否为图片文件
- # LJX: 构建保存路径
- 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) # LJX: 创建目录
- # LJX: 生成唯一文件名
- 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") # LJX: 路径安全检查
- # LJX: 保存文件
- with open(savepath, 'wb+') as wfile:
- for chunk in request.FILES[filename].chunks():
- wfile.write(chunk)
- # LJX: 如果是图片,进行压缩优化
- if isimage:
- from PIL import Image
- image = Image.open(savepath)
- image.save(savepath, quality=20, optimize=True) # LJX: 质量压缩到20%,优化存储
- url = static(savepath) # LJX: 生成静态文件URL
- response.append(url)
- return HttpResponse(response) # LJX: 返回文件URL列表
-
- else:
- return HttpResponse("only for post") # LJX: 只支持POST请求
-
-
-def page_not_found_view(
- request,
- exception,
- template_name='blog/error_page.html'):
- """LJX: 404页面未找到错误处理视图"""
- if exception:
- logger.error(exception) # LJX: 记录错误日志
- 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'}, # LJX: 错误信息
- status=404) # LJX: 返回404状态码
-
-
-def server_error_view(request, template_name='blog/error_page.html'):
- """LJX: 500服务器错误处理视图"""
- return render(request,
- template_name,
- {'message': _('Sorry, the server is busy, please click the home page to see other?'),
- 'statuscode': '500'},
- status=500) # LJX: 返回500状态码
-
-
-def permission_denied_view(
- request,
- exception,
- template_name='blog/error_page.html'):
- """LJX: 403权限拒绝错误处理视图"""
- 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) # LJX: 返回403状态码
-
-
-def clean_cache_view(request):
- """LJX: 清理缓存视图,用于手动清理系统缓存"""
- cache.clear() # LJX: 清除所有缓存
- return HttpResponse('ok') # LJX: 返回成功响应
\ No newline at end of file
diff --git a/deploy/docker-compose/docker-compose.es.yml b/deploy/docker-compose/docker-compose.es.yml
deleted file mode 100644
index 83e35ff..0000000
--- a/deploy/docker-compose/docker-compose.es.yml
+++ /dev/null
@@ -1,48 +0,0 @@
-version: '3'
-
-services:
- es:
- image: liangliangyy/elasticsearch-analysis-ik:8.6.1
- container_name: es
- restart: always
- environment:
- - discovery.type=single-node
- - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
- ports:
- - 9200:9200
- volumes:
- - ./bin/datas/es/:/usr/share/elasticsearch/data/
-
- kibana:
- image: kibana:8.6.1
- restart: always
- container_name: kibana
- ports:
- - 5601:5601
- environment:
- - ELASTICSEARCH_HOSTS=http://es:9200
-
- djangoblog:
- build: .
- restart: always
- command: bash -c 'sh /code/djangoblog/bin/docker_start.sh'
- ports:
- - "8000:8000"
- volumes:
- - ./collectedstatic:/code/djangoblog/collectedstatic
- - ./uploads:/code/djangoblog/uploads
- environment:
- - DJANGO_MYSQL_DATABASE=djangoblog
- - DJANGO_MYSQL_USER=root
- - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E
- - DJANGO_MYSQL_HOST=db
- - DJANGO_MYSQL_PORT=3306
- - DJANGO_MEMCACHED_LOCATION=memcached:11211
- - DJANGO_ELASTICSEARCH_HOST=es:9200
- links:
- - db
- - memcached
- depends_on:
- - db
- container_name: djangoblog
-
diff --git a/deploy/docker-compose/docker-compose.yml b/deploy/docker-compose/docker-compose.yml
deleted file mode 100644
index 9609af3..0000000
--- a/deploy/docker-compose/docker-compose.yml
+++ /dev/null
@@ -1,60 +0,0 @@
-version: '3'
-
-services:
- db:
- image: mysql:latest
- restart: always
- environment:
- - MYSQL_DATABASE=djangoblog
- - MYSQL_ROOT_PASSWORD=DjAnGoBlOg!2!Q@W#E
- ports:
- - 3306:3306
- volumes:
- - ./bin/datas/mysql/:/var/lib/mysql
- depends_on:
- - redis
- container_name: db
-
- djangoblog:
- build:
- context: ../../
- restart: always
- command: bash -c 'sh /code/djangoblog/bin/docker_start.sh'
- ports:
- - "8000:8000"
- volumes:
- - ./collectedstatic:/code/djangoblog/collectedstatic
- - ./logs:/code/djangoblog/logs
- - ./uploads:/code/djangoblog/uploads
- environment:
- - DJANGO_MYSQL_DATABASE=djangoblog
- - DJANGO_MYSQL_USER=root
- - DJANGO_MYSQL_PASSWORD=DjAnGoBlOg!2!Q@W#E
- - DJANGO_MYSQL_HOST=db
- - DJANGO_MYSQL_PORT=3306
- - DJANGO_REDIS_URL=redis:6379
- links:
- - db
- - redis
- depends_on:
- - db
- container_name: djangoblog
- nginx:
- restart: always
- image: nginx:latest
- ports:
- - "80:80"
- - "443:443"
- volumes:
- - ./bin/nginx.conf:/etc/nginx/nginx.conf
- - ./collectedstatic:/code/djangoblog/collectedstatic
- links:
- - djangoblog:djangoblog
- container_name: nginx
-
- redis:
- restart: always
- image: redis:latest
- container_name: redis
- ports:
- - "6379:6379"
diff --git a/deploy/entrypoint.sh b/deploy/entrypoint.sh
deleted file mode 100644
index 2fb6491..0000000
--- a/deploy/entrypoint.sh
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env bash
-NAME="djangoblog"
-DJANGODIR=/code/djangoblog
-USER=root
-GROUP=root
-NUM_WORKERS=1
-DJANGO_WSGI_MODULE=djangoblog.wsgi
-
-
-echo "Starting $NAME as `whoami`"
-
-cd $DJANGODIR
-
-export PYTHONPATH=$DJANGODIR:$PYTHONPATH
-
-python manage.py makemigrations && \
- python manage.py migrate && \
- python manage.py collectstatic --noinput && \
- python manage.py compress --force && \
- python manage.py build_index && \
- python manage.py compilemessages || exit 1
-
-exec gunicorn ${DJANGO_WSGI_MODULE}:application \
---name $NAME \
---workers $NUM_WORKERS \
---user=$USER --group=$GROUP \
---bind 0.0.0.0:8000 \
---log-level=debug \
---log-file=- \
---worker-class gevent \
---threads 4
diff --git a/deploy/k8s/configmap.yaml b/deploy/k8s/configmap.yaml
deleted file mode 100644
index 835d4ad..0000000
--- a/deploy/k8s/configmap.yaml
+++ /dev/null
@@ -1,119 +0,0 @@
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: web-nginx-config
- namespace: djangoblog
-data:
- nginx.conf: |
- user nginx;
- worker_processes auto;
- error_log /var/log/nginx/error.log notice;
- pid /var/run/nginx.pid;
-
- events {
- worker_connections 1024;
- multi_accept on;
- use epoll;
- }
-
- http {
- include /etc/nginx/mime.types;
- default_type application/octet-stream;
-
- log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- '$status $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for"';
-
- access_log /var/log/nginx/access.log main;
-
- sendfile on;
- keepalive_timeout 65;
- gzip on;
- gzip_disable "msie6";
-
- gzip_vary on;
- gzip_proxied any;
- gzip_comp_level 8;
- gzip_buffers 16 8k;
- gzip_http_version 1.1;
- gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
-
- # Include server configurations
- include /etc/nginx/conf.d/*.conf;
- }
- djangoblog.conf: |
- server {
- server_name lylinux.net;
- root /code/djangoblog/collectedstatic/;
- listen 80;
- keepalive_timeout 70;
- location /static/ {
- expires max;
- alias /code/djangoblog/collectedstatic/;
- }
-
- location ~* (robots\.txt|ads\.txt|favicon\.ico|favion\.ico|crossdomain\.xml|google93fd32dbd906620a\.html|BingSiteAuth\.xml|baidu_verify_Ijeny6KrmS\.html)$ {
- root /resource/djangopub;
- expires 1d;
- access_log off;
- error_log off;
- }
-
- location / {
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $http_host;
- proxy_set_header X-NginX-Proxy true;
- proxy_redirect off;
- if (!-f $request_filename) {
- proxy_pass http://djangoblog:8000;
- break;
- }
- }
- }
- server {
- server_name www.lylinux.net;
- listen 80;
- return 301 https://lylinux.net$request_uri;
- }
- resource.lylinux.net.conf: |
- server {
- index index.html index.htm;
- server_name resource.lylinux.net;
- root /resource/;
-
- location /djangoblog/ {
- alias /code/djangoblog/collectedstatic/;
- }
-
- access_log off;
- error_log off;
- include lylinux/resource.conf;
- }
- lylinux.resource.conf: |
- expires max;
- access_log off;
- log_not_found off;
- add_header Pragma public;
- add_header Cache-Control "public";
- add_header "Access-Control-Allow-Origin" "*";
-
----
-apiVersion: v1
-kind: ConfigMap
-metadata:
- name: djangoblog-env
- namespace: djangoblog
-data:
- DJANGO_MYSQL_DATABASE: djangoblog
- DJANGO_MYSQL_USER: db_user
- DJANGO_MYSQL_PASSWORD: db_password
- DJANGO_MYSQL_HOST: db_host
- DJANGO_MYSQL_PORT: db_port
- DJANGO_REDIS_URL: "redis:6379"
- DJANGO_DEBUG: "False"
- MYSQL_ROOT_PASSWORD: db_password
- MYSQL_DATABASE: djangoblog
- MYSQL_PASSWORD: db_password
- DJANGO_SECRET_KEY: xxxxxxxxxxxxxxxxxxxxxxxxxxxxx
-
diff --git a/deploy/k8s/deployment.yaml b/deploy/k8s/deployment.yaml
deleted file mode 100644
index 414fdcc..0000000
--- a/deploy/k8s/deployment.yaml
+++ /dev/null
@@ -1,274 +0,0 @@
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: djangoblog
- namespace: djangoblog
- labels:
- app: djangoblog
-spec:
- replicas: 3
- selector:
- matchLabels:
- app: djangoblog
- template:
- metadata:
- labels:
- app: djangoblog
- spec:
- containers:
- - name: djangoblog
- image: liangliangyy/djangoblog:latest
- imagePullPolicy: Always
- ports:
- - containerPort: 8000
- envFrom:
- - configMapRef:
- name: djangoblog-env
- readinessProbe:
- httpGet:
- path: /
- port: 8000
- initialDelaySeconds: 10
- periodSeconds: 30
- livenessProbe:
- httpGet:
- path: /
- port: 8000
- initialDelaySeconds: 10
- periodSeconds: 30
- resources:
- requests:
- cpu: 10m
- memory: 100Mi
- limits:
- cpu: "2"
- memory: 2Gi
- volumeMounts:
- - name: djangoblog
- mountPath: /code/djangoblog/collectedstatic
- - name: resource
- mountPath: /resource
- volumes:
- - name: djangoblog
- persistentVolumeClaim:
- claimName: djangoblog-pvc
- - name: resource
- persistentVolumeClaim:
- claimName: resource-pvc
-
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: redis
- namespace: djangoblog
- labels:
- app: redis
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: redis
- template:
- metadata:
- labels:
- app: redis
- spec:
- containers:
- - name: redis
- image: redis:latest
- imagePullPolicy: IfNotPresent
- ports:
- - containerPort: 6379
- resources:
- requests:
- cpu: 10m
- memory: 100Mi
- limits:
- cpu: 200m
- memory: 2Gi
-
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: db
- namespace: djangoblog
- labels:
- app: db
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: db
- template:
- metadata:
- labels:
- app: db
- spec:
- containers:
- - name: db
- image: mysql:latest
- imagePullPolicy: IfNotPresent
- ports:
- - containerPort: 3306
- envFrom:
- - configMapRef:
- name: djangoblog-env
- readinessProbe:
- exec:
- command:
- - mysqladmin
- - ping
- - "-h"
- - "127.0.0.1"
- - "-u"
- - "root"
- - "-p$MYSQL_ROOT_PASSWORD"
- initialDelaySeconds: 10
- periodSeconds: 10
- livenessProbe:
- exec:
- command:
- - mysqladmin
- - ping
- - "-h"
- - "127.0.0.1"
- - "-u"
- - "root"
- - "-p$MYSQL_ROOT_PASSWORD"
- initialDelaySeconds: 10
- periodSeconds: 10
- resources:
- requests:
- cpu: 10m
- memory: 100Mi
- limits:
- cpu: "2"
- memory: 2Gi
- volumeMounts:
- - name: db-data
- mountPath: /var/lib/mysql
- volumes:
- - name: db-data
- persistentVolumeClaim:
- claimName: db-pvc
-
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: nginx
- namespace: djangoblog
- labels:
- app: nginx
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: nginx
- template:
- metadata:
- labels:
- app: nginx
- spec:
- containers:
- - name: nginx
- image: nginx:latest
- imagePullPolicy: IfNotPresent
- ports:
- - containerPort: 80
- resources:
- requests:
- cpu: 10m
- memory: 100Mi
- limits:
- cpu: "2"
- memory: 2Gi
- volumeMounts:
- - name: nginx-config
- mountPath: /etc/nginx/nginx.conf
- subPath: nginx.conf
- - name: nginx-config
- mountPath: /etc/nginx/conf.d/default.conf
- subPath: djangoblog.conf
- - name: nginx-config
- mountPath: /etc/nginx/conf.d/resource.lylinux.net.conf
- subPath: resource.lylinux.net.conf
- - name: nginx-config
- mountPath: /etc/nginx/lylinux/resource.conf
- subPath: lylinux.resource.conf
- - name: djangoblog-pvc
- mountPath: /code/djangoblog/collectedstatic
- - name: resource-pvc
- mountPath: /resource
- volumes:
- - name: nginx-config
- configMap:
- name: web-nginx-config
- - name: djangoblog-pvc
- persistentVolumeClaim:
- claimName: djangoblog-pvc
- - name: resource-pvc
- persistentVolumeClaim:
- claimName: resource-pvc
-
----
-apiVersion: apps/v1
-kind: Deployment
-metadata:
- name: elasticsearch
- namespace: djangoblog
- labels:
- app: elasticsearch
-spec:
- replicas: 1
- selector:
- matchLabels:
- app: elasticsearch
- template:
- metadata:
- labels:
- app: elasticsearch
- spec:
- containers:
- - name: elasticsearch
- image: liangliangyy/elasticsearch-analysis-ik:8.6.1
- imagePullPolicy: IfNotPresent
- env:
- - name: discovery.type
- value: single-node
- - name: ES_JAVA_OPTS
- value: "-Xms256m -Xmx256m"
- - name: xpack.security.enabled
- value: "false"
- - name: xpack.monitoring.templates.enabled
- value: "false"
- ports:
- - containerPort: 9200
- resources:
- requests:
- cpu: 10m
- memory: 100Mi
- limits:
- cpu: "2"
- memory: 2Gi
- readinessProbe:
- httpGet:
- path: /
- port: 9200
- initialDelaySeconds: 15
- periodSeconds: 30
- livenessProbe:
- httpGet:
- path: /
- port: 9200
- initialDelaySeconds: 15
- periodSeconds: 30
- volumeMounts:
- - name: elasticsearch-data
- mountPath: /usr/share/elasticsearch/data/
- volumes:
- - name: elasticsearch-data
- persistentVolumeClaim:
- claimName: elasticsearch-pvc
diff --git a/deploy/k8s/gateway.yaml b/deploy/k8s/gateway.yaml
deleted file mode 100644
index a8de073..0000000
--- a/deploy/k8s/gateway.yaml
+++ /dev/null
@@ -1,17 +0,0 @@
-apiVersion: networking.k8s.io/v1
-kind: Ingress
-metadata:
- name: nginx
- namespace: djangoblog
-spec:
- ingressClassName: nginx
- rules:
- - http:
- paths:
- - path: /
- pathType: Prefix
- backend:
- service:
- name: nginx
- port:
- number: 80
\ No newline at end of file
diff --git a/deploy/k8s/pv.yaml b/deploy/k8s/pv.yaml
deleted file mode 100644
index 874b72f..0000000
--- a/deploy/k8s/pv.yaml
+++ /dev/null
@@ -1,94 +0,0 @@
-apiVersion: v1
-kind: PersistentVolume
-metadata:
- name: local-pv-db
-spec:
- capacity:
- storage: 10Gi
- volumeMode: Filesystem
- accessModes:
- - ReadWriteOnce
- persistentVolumeReclaimPolicy: Retain
- storageClassName: local-storage
- local:
- path: /mnt/local-storage-db
- nodeAffinity:
- required:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/hostname
- operator: In
- values:
- - master
----
-apiVersion: v1
-kind: PersistentVolume
-metadata:
- name: local-pv-djangoblog
-spec:
- capacity:
- storage: 5Gi
- volumeMode: Filesystem
- accessModes:
- - ReadWriteOnce
- persistentVolumeReclaimPolicy: Retain
- storageClassName: local-storage
- local:
- path: /mnt/local-storage-djangoblog
- nodeAffinity:
- required:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/hostname
- operator: In
- values:
- - master
-
-
----
-apiVersion: v1
-kind: PersistentVolume
-metadata:
- name: local-pv-resource
-spec:
- capacity:
- storage: 5Gi
- volumeMode: Filesystem
- accessModes:
- - ReadWriteOnce
- persistentVolumeReclaimPolicy: Retain
- storageClassName: local-storage
- local:
- path: /mnt/resource/
- nodeAffinity:
- required:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/hostname
- operator: In
- values:
- - master
-
----
-apiVersion: v1
-kind: PersistentVolume
-metadata:
- name: local-pv-elasticsearch
-spec:
- capacity:
- storage: 5Gi
- volumeMode: Filesystem
- accessModes:
- - ReadWriteOnce
- persistentVolumeReclaimPolicy: Retain
- storageClassName: local-storage
- local:
- path: /mnt/local-storage-elasticsearch
- nodeAffinity:
- required:
- nodeSelectorTerms:
- - matchExpressions:
- - key: kubernetes.io/hostname
- operator: In
- values:
- - master
\ No newline at end of file
diff --git a/deploy/k8s/pvc.yaml b/deploy/k8s/pvc.yaml
deleted file mode 100644
index ef238c5..0000000
--- a/deploy/k8s/pvc.yaml
+++ /dev/null
@@ -1,60 +0,0 @@
-apiVersion: v1
-kind: PersistentVolumeClaim
-metadata:
- name: db-pvc
- namespace: djangoblog
-spec:
- storageClassName: local-storage
- volumeName: local-pv-db
- accessModes:
- - ReadWriteOnce
- resources:
- requests:
- storage: 10Gi
-
-
----
-apiVersion: v1
-kind: PersistentVolumeClaim
-metadata:
- name: djangoblog-pvc
- namespace: djangoblog
-spec:
- volumeName: local-pv-djangoblog
- storageClassName: local-storage
- accessModes:
- - ReadWriteOnce
- resources:
- requests:
- storage: 5Gi
-
----
-apiVersion: v1
-kind: PersistentVolumeClaim
-metadata:
- name: resource-pvc
- namespace: djangoblog
-spec:
- volumeName: local-pv-resource
- storageClassName: local-storage
- accessModes:
- - ReadWriteOnce
- resources:
- requests:
- storage: 5Gi
-
----
-apiVersion: v1
-kind: PersistentVolumeClaim
-metadata:
- name: elasticsearch-pvc
- namespace: djangoblog
-spec:
- volumeName: local-pv-elasticsearch
- storageClassName: local-storage
- accessModes:
- - ReadWriteOnce
- resources:
- requests:
- storage: 5Gi
-
\ No newline at end of file
diff --git a/deploy/k8s/service.yaml b/deploy/k8s/service.yaml
deleted file mode 100644
index 4ef2931..0000000
--- a/deploy/k8s/service.yaml
+++ /dev/null
@@ -1,80 +0,0 @@
-apiVersion: v1
-kind: Service
-metadata:
- name: djangoblog
- namespace: djangoblog
- labels:
- app: djangoblog
-spec:
- selector:
- app: djangoblog
- ports:
- - protocol: TCP
- port: 8000
- targetPort: 8000
- type: ClusterIP
----
-apiVersion: v1
-kind: Service
-metadata:
- name: nginx
- namespace: djangoblog
- labels:
- app: nginx
-spec:
- selector:
- app: nginx
- ports:
- - protocol: TCP
- port: 80
- targetPort: 80
- type: ClusterIP
----
-apiVersion: v1
-kind: Service
-metadata:
- name: redis
- namespace: djangoblog
- labels:
- app: redis
-spec:
- selector:
- app: redis
- ports:
- - protocol: TCP
- port: 6379
- targetPort: 6379
- type: ClusterIP
----
-apiVersion: v1
-kind: Service
-metadata:
- name: db
- namespace: djangoblog
- labels:
- app: db
-spec:
- selector:
- app: db
- ports:
- - protocol: TCP
- port: 3306
- targetPort: 3306
- type: ClusterIP
----
-apiVersion: v1
-kind: Service
-metadata:
- name: elasticsearch
- namespace: djangoblog
- labels:
- app: elasticsearch
-spec:
- selector:
- app: elasticsearch
- ports:
- - protocol: TCP
- port: 9200
- targetPort: 9200
- type: ClusterIP
-
diff --git a/deploy/k8s/storageclass.yaml b/deploy/k8s/storageclass.yaml
deleted file mode 100644
index 5d5a14c..0000000
--- a/deploy/k8s/storageclass.yaml
+++ /dev/null
@@ -1,10 +0,0 @@
-apiVersion: storage.k8s.io/v1
-kind: StorageClass
-metadata:
- name: local-storage
- annotations:
- storageclass.kubernetes.io/is-default-class: "true"
-provisioner: kubernetes.io/no-provisioner
-volumeBindingMode: Immediate
-
-
diff --git a/deploy/nginx.conf b/deploy/nginx.conf
deleted file mode 100644
index 32161d8..0000000
--- a/deploy/nginx.conf
+++ /dev/null
@@ -1,50 +0,0 @@
-user nginx;
-worker_processes auto;
-
-error_log /var/log/nginx/error.log notice;
-pid /var/run/nginx.pid;
-
-
-events {
- worker_connections 1024;
-}
-
-
-http {
- include /etc/nginx/mime.types;
- default_type application/octet-stream;
-
- log_format main '$remote_addr - $remote_user [$time_local] "$request" '
- '$status $body_bytes_sent "$http_referer" '
- '"$http_user_agent" "$http_x_forwarded_for"';
-
- access_log /var/log/nginx/access.log main;
-
- sendfile on;
- #tcp_nopush on;
-
- keepalive_timeout 65;
-
- #gzip on;
-
- server {
- root /code/djangoblog/collectedstatic/;
- listen 80;
- keepalive_timeout 70;
- location /static/ {
- expires max;
- alias /code/djangoblog/collectedstatic/;
- }
- location / {
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- proxy_set_header Host $http_host;
- proxy_set_header X-NginX-Proxy true;
- proxy_redirect off;
- if (!-f $request_filename) {
- proxy_pass http://djangoblog:8000;
- break;
- }
- }
- }
-}
diff --git a/locale/en/LC_MESSAGES/django.mo b/locale/en/LC_MESSAGES/django.mo
deleted file mode 100644
index f63669f..0000000
Binary files a/locale/en/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/locale/en/LC_MESSAGES/django.po b/locale/en/LC_MESSAGES/django.po
deleted file mode 100644
index c80b30a..0000000
--- a/locale/en/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,685 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR , YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-09-13 16:02+0800\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=2; plural=(n != 1);\n"
-
-#: .\accounts\admin.py:12
-msgid "password"
-msgstr "password"
-
-#: .\accounts\admin.py:13
-msgid "Enter password again"
-msgstr "Enter password again"
-
-#: .\accounts\admin.py:24 .\accounts\forms.py:89
-msgid "passwords do not match"
-msgstr "passwords do not match"
-
-#: .\accounts\forms.py:36
-msgid "email already exists"
-msgstr "email already exists"
-
-#: .\accounts\forms.py:46 .\accounts\forms.py:50
-msgid "New password"
-msgstr "New password"
-
-#: .\accounts\forms.py:60
-msgid "Confirm password"
-msgstr "Confirm password"
-
-#: .\accounts\forms.py:70 .\accounts\forms.py:116
-msgid "Email"
-msgstr "Email"
-
-#: .\accounts\forms.py:76 .\accounts\forms.py:80
-msgid "Code"
-msgstr "Code"
-
-#: .\accounts\forms.py:100 .\accounts\tests.py:194
-msgid "email does not exist"
-msgstr "email does not exist"
-
-#: .\accounts\models.py:12 .\oauth\models.py:17
-msgid "nick name"
-msgstr "nick name"
-
-#: .\accounts\models.py:13 .\blog\models.py:29 .\blog\models.py:266
-#: .\blog\models.py:284 .\comments\models.py:13 .\oauth\models.py:23
-#: .\oauth\models.py:53
-msgid "creation time"
-msgstr "creation time"
-
-#: .\accounts\models.py:14 .\comments\models.py:14 .\oauth\models.py:24
-#: .\oauth\models.py:54
-msgid "last modify time"
-msgstr "last modify time"
-
-#: .\accounts\models.py:15
-msgid "create source"
-msgstr "create source"
-
-#: .\accounts\models.py:33 .\djangoblog\logentryadmin.py:81
-msgid "user"
-msgstr "user"
-
-#: .\accounts\tests.py:216 .\accounts\utils.py:39
-msgid "Verification code error"
-msgstr "Verification code error"
-
-#: .\accounts\utils.py:13
-msgid "Verify Email"
-msgstr "Verify Email"
-
-#: .\accounts\utils.py:21
-#, python-format
-msgid ""
-"You are resetting the password, the verification code is:%(code)s, valid "
-"within 5 minutes, please keep it properly"
-msgstr ""
-"You are resetting the password, the verification code is:%(code)s, valid "
-"within 5 minutes, please keep it properly"
-
-#: .\blog\admin.py:13 .\blog\models.py:92 .\comments\models.py:17
-#: .\oauth\models.py:12
-msgid "author"
-msgstr "author"
-
-#: .\blog\admin.py:53
-msgid "Publish selected articles"
-msgstr "Publish selected articles"
-
-#: .\blog\admin.py:54
-msgid "Draft selected articles"
-msgstr "Draft selected articles"
-
-#: .\blog\admin.py:55
-msgid "Close article comments"
-msgstr "Close article comments"
-
-#: .\blog\admin.py:56
-msgid "Open article comments"
-msgstr "Open article comments"
-
-#: .\blog\admin.py:89 .\blog\models.py:101 .\blog\models.py:183
-#: .\templates\blog\tags\sidebar.html:40
-msgid "category"
-msgstr "category"
-
-#: .\blog\models.py:20 .\blog\models.py:179 .\templates\share_layout\nav.html:8
-msgid "index"
-msgstr "index"
-
-#: .\blog\models.py:21
-msgid "list"
-msgstr "list"
-
-#: .\blog\models.py:22
-msgid "post"
-msgstr "post"
-
-#: .\blog\models.py:23
-msgid "all"
-msgstr "all"
-
-#: .\blog\models.py:24
-msgid "slide"
-msgstr "slide"
-
-#: .\blog\models.py:30 .\blog\models.py:267 .\blog\models.py:285
-msgid "modify time"
-msgstr "modify time"
-
-#: .\blog\models.py:63
-msgid "Draft"
-msgstr "Draft"
-
-#: .\blog\models.py:64
-msgid "Published"
-msgstr "Published"
-
-#: .\blog\models.py:67
-msgid "Open"
-msgstr "Open"
-
-#: .\blog\models.py:68
-msgid "Close"
-msgstr "Close"
-
-#: .\blog\models.py:71 .\comments\admin.py:47
-msgid "Article"
-msgstr "Article"
-
-#: .\blog\models.py:72
-msgid "Page"
-msgstr "Page"
-
-#: .\blog\models.py:74 .\blog\models.py:280
-msgid "title"
-msgstr "title"
-
-#: .\blog\models.py:75
-msgid "body"
-msgstr "body"
-
-#: .\blog\models.py:77
-msgid "publish time"
-msgstr "publish time"
-
-#: .\blog\models.py:79
-msgid "status"
-msgstr "status"
-
-#: .\blog\models.py:84
-msgid "comment status"
-msgstr "comment status"
-
-#: .\blog\models.py:88 .\oauth\models.py:43
-msgid "type"
-msgstr "type"
-
-#: .\blog\models.py:89
-msgid "views"
-msgstr "views"
-
-#: .\blog\models.py:97 .\blog\models.py:258 .\blog\models.py:282
-msgid "order"
-msgstr "order"
-
-#: .\blog\models.py:98
-msgid "show toc"
-msgstr "show toc"
-
-#: .\blog\models.py:105 .\blog\models.py:249
-msgid "tag"
-msgstr "tag"
-
-#: .\blog\models.py:115 .\comments\models.py:21
-msgid "article"
-msgstr "article"
-
-#: .\blog\models.py:171
-msgid "category name"
-msgstr "category name"
-
-#: .\blog\models.py:174
-msgid "parent category"
-msgstr "parent category"
-
-#: .\blog\models.py:234
-msgid "tag name"
-msgstr "tag name"
-
-#: .\blog\models.py:256
-msgid "link name"
-msgstr "link name"
-
-#: .\blog\models.py:257 .\blog\models.py:271
-msgid "link"
-msgstr "link"
-
-#: .\blog\models.py:260
-msgid "is show"
-msgstr "is show"
-
-#: .\blog\models.py:262
-msgid "show type"
-msgstr "show type"
-
-#: .\blog\models.py:281
-msgid "content"
-msgstr "content"
-
-#: .\blog\models.py:283 .\oauth\models.py:52
-msgid "is enable"
-msgstr "is enable"
-
-#: .\blog\models.py:289
-msgid "sidebar"
-msgstr "sidebar"
-
-#: .\blog\models.py:299
-msgid "site name"
-msgstr "site name"
-
-#: .\blog\models.py:305
-msgid "site description"
-msgstr "site description"
-
-#: .\blog\models.py:311
-msgid "site seo description"
-msgstr "site seo description"
-
-#: .\blog\models.py:313
-msgid "site keywords"
-msgstr "site keywords"
-
-#: .\blog\models.py:318
-msgid "article sub length"
-msgstr "article sub length"
-
-#: .\blog\models.py:319
-msgid "sidebar article count"
-msgstr "sidebar article count"
-
-#: .\blog\models.py:320
-msgid "sidebar comment count"
-msgstr "sidebar comment count"
-
-#: .\blog\models.py:321
-msgid "article comment count"
-msgstr "article comment count"
-
-#: .\blog\models.py:322
-msgid "show adsense"
-msgstr "show adsense"
-
-#: .\blog\models.py:324
-msgid "adsense code"
-msgstr "adsense code"
-
-#: .\blog\models.py:325
-msgid "open site comment"
-msgstr "open site comment"
-
-#: .\blog\models.py:352
-msgid "Website configuration"
-msgstr "Website configuration"
-
-#: .\blog\models.py:360
-msgid "There can only be one configuration"
-msgstr "There can only be one configuration"
-
-#: .\blog\views.py:348
-msgid ""
-"Sorry, the page you requested is not found, please click the home page to "
-"see other?"
-msgstr ""
-"Sorry, the page you requested is not found, please click the home page to "
-"see other?"
-
-#: .\blog\views.py:356
-msgid "Sorry, the server is busy, please click the home page to see other?"
-msgstr "Sorry, the server is busy, please click the home page to see other?"
-
-#: .\blog\views.py:369
-msgid "Sorry, you do not have permission to access this page?"
-msgstr "Sorry, you do not have permission to access this page?"
-
-#: .\comments\admin.py:15
-msgid "Disable comments"
-msgstr "Disable comments"
-
-#: .\comments\admin.py:16
-msgid "Enable comments"
-msgstr "Enable comments"
-
-#: .\comments\admin.py:46
-msgid "User"
-msgstr "User"
-
-#: .\comments\models.py:25
-msgid "parent comment"
-msgstr "parent comment"
-
-#: .\comments\models.py:29
-msgid "enable"
-msgstr "enable"
-
-#: .\comments\models.py:34 .\templates\blog\tags\article_info.html:30
-msgid "comment"
-msgstr "comment"
-
-#: .\comments\utils.py:13
-msgid "Thanks for your comment"
-msgstr "Thanks for your comment"
-
-#: .\comments\utils.py:15
-#, python-format
-msgid ""
-"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/locale/zh_Hans/LC_MESSAGES/django.mo b/locale/zh_Hans/LC_MESSAGES/django.mo
deleted file mode 100644
index a2d36e9..0000000
Binary files a/locale/zh_Hans/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/locale/zh_Hans/LC_MESSAGES/django.po b/locale/zh_Hans/LC_MESSAGES/django.po
deleted file mode 100644
index 200b7e6..0000000
--- a/locale/zh_Hans/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,667 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR , YEAR.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-09-13 16:02+0800\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#: .\accounts\admin.py:12
-msgid "password"
-msgstr "密码"
-
-#: .\accounts\admin.py:13
-msgid "Enter password again"
-msgstr "再次输入密码"
-
-#: .\accounts\admin.py:24 .\accounts\forms.py:89
-msgid "passwords do not match"
-msgstr "密码不匹配"
-
-#: .\accounts\forms.py:36
-msgid "email already exists"
-msgstr "邮箱已存在"
-
-#: .\accounts\forms.py:46 .\accounts\forms.py:50
-msgid "New password"
-msgstr "新密码"
-
-#: .\accounts\forms.py:60
-msgid "Confirm password"
-msgstr "确认密码"
-
-#: .\accounts\forms.py:70 .\accounts\forms.py:116
-msgid "Email"
-msgstr "邮箱"
-
-#: .\accounts\forms.py:76 .\accounts\forms.py:80
-msgid "Code"
-msgstr "验证码"
-
-#: .\accounts\forms.py:100 .\accounts\tests.py:194
-msgid "email does not exist"
-msgstr "邮箱不存在"
-
-#: .\accounts\models.py:12 .\oauth\models.py:17
-msgid "nick name"
-msgstr "昵称"
-
-#: .\accounts\models.py:13 .\blog\models.py:29 .\blog\models.py:266
-#: .\blog\models.py:284 .\comments\models.py:13 .\oauth\models.py:23
-#: .\oauth\models.py:53
-msgid "creation time"
-msgstr "创建时间"
-
-#: .\accounts\models.py:14 .\comments\models.py:14 .\oauth\models.py:24
-#: .\oauth\models.py:54
-msgid "last modify time"
-msgstr "最后修改时间"
-
-#: .\accounts\models.py:15
-msgid "create source"
-msgstr "来源"
-
-#: .\accounts\models.py:33 .\djangoblog\logentryadmin.py:81
-msgid "user"
-msgstr "用户"
-
-#: .\accounts\tests.py:216 .\accounts\utils.py:39
-msgid "Verification code error"
-msgstr "验证码错误"
-
-#: .\accounts\utils.py:13
-msgid "Verify Email"
-msgstr "验证邮箱"
-
-#: .\accounts\utils.py:21
-#, python-format
-msgid ""
-"You are resetting the password, the verification code is:%(code)s, valid "
-"within 5 minutes, please keep it properly"
-msgstr "您正在重置密码,验证码为:%(code)s,5分钟内有效 请妥善保管."
-
-#: .\blog\admin.py:13 .\blog\models.py:92 .\comments\models.py:17
-#: .\oauth\models.py:12
-msgid "author"
-msgstr "作者"
-
-#: .\blog\admin.py:53
-msgid "Publish selected articles"
-msgstr "发布选中的文章"
-
-#: .\blog\admin.py:54
-msgid "Draft selected articles"
-msgstr "选中文章设为草稿"
-
-#: .\blog\admin.py:55
-msgid "Close article comments"
-msgstr "关闭文章评论"
-
-#: .\blog\admin.py:56
-msgid "Open article comments"
-msgstr "打开文章评论"
-
-#: .\blog\admin.py:89 .\blog\models.py:101 .\blog\models.py:183
-#: .\templates\blog\tags\sidebar.html:40
-msgid "category"
-msgstr "分类目录"
-
-#: .\blog\models.py:20 .\blog\models.py:179 .\templates\share_layout\nav.html:8
-msgid "index"
-msgstr "首页"
-
-#: .\blog\models.py:21
-msgid "list"
-msgstr "列表"
-
-#: .\blog\models.py:22
-msgid "post"
-msgstr "文章"
-
-#: .\blog\models.py:23
-msgid "all"
-msgstr "所有"
-
-#: .\blog\models.py:24
-msgid "slide"
-msgstr "侧边栏"
-
-#: .\blog\models.py:30 .\blog\models.py:267 .\blog\models.py:285
-msgid "modify time"
-msgstr "修改时间"
-
-#: .\blog\models.py:63
-msgid "Draft"
-msgstr "草稿"
-
-#: .\blog\models.py:64
-msgid "Published"
-msgstr "发布"
-
-#: .\blog\models.py:67
-msgid "Open"
-msgstr "打开"
-
-#: .\blog\models.py:68
-msgid "Close"
-msgstr "关闭"
-
-#: .\blog\models.py:71 .\comments\admin.py:47
-msgid "Article"
-msgstr "文章"
-
-#: .\blog\models.py:72
-msgid "Page"
-msgstr "页面"
-
-#: .\blog\models.py:74 .\blog\models.py:280
-msgid "title"
-msgstr "标题"
-
-#: .\blog\models.py:75
-msgid "body"
-msgstr "内容"
-
-#: .\blog\models.py:77
-msgid "publish time"
-msgstr "发布时间"
-
-#: .\blog\models.py:79
-msgid "status"
-msgstr "状态"
-
-#: .\blog\models.py:84
-msgid "comment status"
-msgstr "评论状态"
-
-#: .\blog\models.py:88 .\oauth\models.py:43
-msgid "type"
-msgstr "类型"
-
-#: .\blog\models.py:89
-msgid "views"
-msgstr "阅读量"
-
-#: .\blog\models.py:97 .\blog\models.py:258 .\blog\models.py:282
-msgid "order"
-msgstr "排序"
-
-#: .\blog\models.py:98
-msgid "show toc"
-msgstr "显示目录"
-
-#: .\blog\models.py:105 .\blog\models.py:249
-msgid "tag"
-msgstr "标签"
-
-#: .\blog\models.py:115 .\comments\models.py:21
-msgid "article"
-msgstr "文章"
-
-#: .\blog\models.py:171
-msgid "category name"
-msgstr "分类名"
-
-#: .\blog\models.py:174
-msgid "parent category"
-msgstr "上级分类"
-
-#: .\blog\models.py:234
-msgid "tag name"
-msgstr "标签名"
-
-#: .\blog\models.py:256
-msgid "link name"
-msgstr "链接名"
-
-#: .\blog\models.py:257 .\blog\models.py:271
-msgid "link"
-msgstr "链接"
-
-#: .\blog\models.py:260
-msgid "is show"
-msgstr "是否显示"
-
-#: .\blog\models.py:262
-msgid "show type"
-msgstr "显示类型"
-
-#: .\blog\models.py:281
-msgid "content"
-msgstr "内容"
-
-#: .\blog\models.py:283 .\oauth\models.py:52
-msgid "is enable"
-msgstr "是否启用"
-
-#: .\blog\models.py:289
-msgid "sidebar"
-msgstr "侧边栏"
-
-#: .\blog\models.py:299
-msgid "site name"
-msgstr "站点名称"
-
-#: .\blog\models.py:305
-msgid "site description"
-msgstr "站点描述"
-
-#: .\blog\models.py:311
-msgid "site seo description"
-msgstr "站点SEO描述"
-
-#: .\blog\models.py:313
-msgid "site keywords"
-msgstr "关键字"
-
-#: .\blog\models.py:318
-msgid "article sub length"
-msgstr "文章摘要长度"
-
-#: .\blog\models.py:319
-msgid "sidebar article count"
-msgstr "侧边栏文章数目"
-
-#: .\blog\models.py:320
-msgid "sidebar comment count"
-msgstr "侧边栏评论数目"
-
-#: .\blog\models.py:321
-msgid "article comment count"
-msgstr "文章页面默认显示评论数目"
-
-#: .\blog\models.py:322
-msgid "show adsense"
-msgstr "是否显示广告"
-
-#: .\blog\models.py:324
-msgid "adsense code"
-msgstr "广告内容"
-
-#: .\blog\models.py:325
-msgid "open site comment"
-msgstr "公共头部"
-
-#: .\blog\models.py:352
-msgid "Website configuration"
-msgstr "网站配置"
-
-#: .\blog\models.py:360
-msgid "There can only be one configuration"
-msgstr "只能有一个配置"
-
-#: .\blog\views.py:348
-msgid ""
-"Sorry, the page you requested is not found, please click the home page to "
-"see other?"
-msgstr "抱歉,你所访问的页面找不到,请点击首页看看别的?"
-
-#: .\blog\views.py:356
-msgid "Sorry, the server is busy, please click the home page to see other?"
-msgstr "抱歉,服务出错了,请点击首页看看别的?"
-
-#: .\blog\views.py:369
-msgid "Sorry, you do not have permission to access this page?"
-msgstr "抱歉,你没用权限访问此页面。"
-
-#: .\comments\admin.py:15
-msgid "Disable comments"
-msgstr "禁用评论"
-
-#: .\comments\admin.py:16
-msgid "Enable comments"
-msgstr "启用评论"
-
-#: .\comments\admin.py:46
-msgid "User"
-msgstr "用户"
-
-#: .\comments\models.py:25
-msgid "parent comment"
-msgstr "上级评论"
-
-#: .\comments\models.py:29
-msgid "enable"
-msgstr "启用"
-
-#: .\comments\models.py:34 .\templates\blog\tags\article_info.html:30
-msgid "comment"
-msgstr "评论"
-
-#: .\comments\utils.py:13
-msgid "Thanks for your comment"
-msgstr "感谢你的评论"
-
-#: .\comments\utils.py:15
-#, python-format
-msgid ""
-"Thank you very much for your comments on this site
\n"
-" You can visit %(article_title)s \n"
-" to review your comments,\n"
-" Thank you again!\n"
-" \n"
-" If the link above cannot be opened, please copy this "
-"link to your browser.\n"
-" %(article_url)s"
-msgstr ""
-"非常感谢您对此网站的评论
\n"
-" 您可以访问%(article_title)s \n"
-"查看您的评论,\n"
-"再次感谢您!\n"
-" \n"
-" 如果上面的链接打不开,请复制此链接链接到您的浏览器。\n"
-"%(article_url)s"
-
-#: .\comments\utils.py:26
-#, python-format
-msgid ""
-"Your comment on "
-"%(article_title)s has \n"
-" received a reply. %(comment_body)s\n"
-" \n"
-" go check it out!\n"
-" \n"
-" If the link above cannot be opened, please copy this "
-"link to your browser.\n"
-" %(article_url)s\n"
-" "
-msgstr ""
-"您对 %(article_title)s "
-"的评论有\n"
-" 收到回复。 %(comment_body)s\n"
-" \n"
-"快去看看吧!\n"
-" \n"
-" 如果上面的链接打不开,请复制此链接链接到您的浏览器。\n"
-" %(article_url)s\n"
-" "
-
-#: .\djangoblog\logentryadmin.py:63
-msgid "object"
-msgstr "对象"
-
-#: .\djangoblog\settings.py:140
-msgid "English"
-msgstr "英文"
-
-#: .\djangoblog\settings.py:141
-msgid "Simplified Chinese"
-msgstr "简体中文"
-
-#: .\djangoblog\settings.py:142
-msgid "Traditional Chinese"
-msgstr "繁体中文"
-
-#: .\oauth\models.py:30
-msgid "oauth user"
-msgstr "第三方用户"
-
-#: .\oauth\models.py:37
-msgid "weibo"
-msgstr "微博"
-
-#: .\oauth\models.py:38
-msgid "google"
-msgstr "谷歌"
-
-#: .\oauth\models.py:48
-msgid "callback url"
-msgstr "回调地址"
-
-#: .\oauth\models.py:59
-msgid "already exists"
-msgstr "已经存在"
-
-#: .\oauth\views.py:154
-#, python-format
-msgid ""
-"\n"
-" Congratulations, you have successfully bound your email address. You "
-"can use\n"
-" %(oauthuser_type)s to directly log in to this website without a "
-"password.
\n"
-" You are welcome to continue to follow this site, the address is\n"
-" %(site)s \n"
-" Thank you again!\n"
-" \n"
-" If the link above cannot be opened, please copy this link to your "
-"browser.\n"
-" %(site)s\n"
-" "
-msgstr ""
-"\n"
-" 恭喜你已经绑定成功 你可以使用\n"
-" %(oauthuser_type)s 来免密登录本站
\n"
-" 欢迎继续关注本站, 地址是\n"
-" %(site)s \n"
-" 再次感谢你\n"
-" \n"
-" 如果上面链接无法打开,请复制此链接到你的浏览器 \n"
-" %(site)s\n"
-" "
-
-#: .\oauth\views.py:165
-msgid "Congratulations on your successful binding!"
-msgstr "恭喜你绑定成功"
-
-#: .\oauth\views.py:217
-#, python-format
-msgid ""
-"\n"
-" Please click the link below to bind your email
\n"
-"\n"
-" %(url)s \n"
-"\n"
-" Thank you again!\n"
-" \n"
-" If the link above cannot be opened, please copy this link "
-"to your browser.\n"
-" \n"
-" %(url)s\n"
-" "
-msgstr ""
-"\n"
-" 请点击下面的链接绑定您的邮箱
\n"
-"\n"
-" %(url)s \n"
-"\n"
-"再次感谢您!\n"
-" \n"
-"如果上面的链接打不开,请复制此链接到您的浏览器。\n"
-"%(url)s\n"
-" "
-
-#: .\oauth\views.py:228 .\oauth\views.py:240
-msgid "Bind your email"
-msgstr "绑定邮箱"
-
-#: .\oauth\views.py:242
-msgid ""
-"Congratulations, the binding is just one step away. Please log in to your "
-"email to check the email to complete the binding. Thank you."
-msgstr "恭喜您,还差一步就绑定成功了,请登录您的邮箱查看邮件完成绑定,谢谢。"
-
-#: .\oauth\views.py:245
-msgid "Binding successful"
-msgstr "绑定成功"
-
-#: .\oauth\views.py:247
-#, python-format
-msgid ""
-"Congratulations, you have successfully bound your email address. You can use "
-"%(oauthuser_type)s to directly log in to this website without a password. "
-"You are welcome to continue to follow this site."
-msgstr ""
-"恭喜您绑定成功,您以后可以使用%(oauthuser_type)s来直接免密码登录本站啦,感谢"
-"您对本站对关注。"
-
-#: .\templates\account\forget_password.html:7
-msgid "forget the password"
-msgstr "忘记密码"
-
-#: .\templates\account\forget_password.html:18
-msgid "get verification code"
-msgstr "获取验证码"
-
-#: .\templates\account\forget_password.html:19
-msgid "submit"
-msgstr "提交"
-
-#: .\templates\account\login.html:36
-msgid "Create Account"
-msgstr "创建账号"
-
-#: .\templates\account\login.html:42
-#| 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/locale/zh_Hant/LC_MESSAGES/django.mo b/locale/zh_Hant/LC_MESSAGES/django.mo
deleted file mode 100644
index fe2ea17..0000000
Binary files a/locale/zh_Hant/LC_MESSAGES/django.mo and /dev/null differ
diff --git a/locale/zh_Hant/LC_MESSAGES/django.po b/locale/zh_Hant/LC_MESSAGES/django.po
deleted file mode 100644
index a2920ce..0000000
--- a/locale/zh_Hant/LC_MESSAGES/django.po
+++ /dev/null
@@ -1,668 +0,0 @@
-# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR , YEAR.
-#
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\n"
-"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2023-09-13 16:02+0800\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
-"Last-Translator: FULL NAME \n"
-"Language-Team: LANGUAGE \n"
-"Language: \n"
-"MIME-Version: 1.0\n"
-"Content-Type: text/plain; charset=UTF-8\n"
-"Content-Transfer-Encoding: 8bit\n"
-"Plural-Forms: nplurals=1; plural=0;\n"
-
-#: .\accounts\admin.py:12
-msgid "password"
-msgstr "密碼"
-
-#: .\accounts\admin.py:13
-msgid "Enter password again"
-msgstr "再次輸入密碼"
-
-#: .\accounts\admin.py:24 .\accounts\forms.py:89
-msgid "passwords do not match"
-msgstr "密碼不匹配"
-
-#: .\accounts\forms.py:36
-msgid "email already exists"
-msgstr "郵箱已存在"
-
-#: .\accounts\forms.py:46 .\accounts\forms.py:50
-msgid "New password"
-msgstr "新密碼"
-
-#: .\accounts\forms.py:60
-msgid "Confirm password"
-msgstr "確認密碼"
-
-#: .\accounts\forms.py:70 .\accounts\forms.py:116
-msgid "Email"
-msgstr "郵箱"
-
-#: .\accounts\forms.py:76 .\accounts\forms.py:80
-msgid "Code"
-msgstr "驗證碼"
-
-#: .\accounts\forms.py:100 .\accounts\tests.py:194
-msgid "email does not exist"
-msgstr "郵箱不存在"
-
-#: .\accounts\models.py:12 .\oauth\models.py:17
-msgid "nick name"
-msgstr "昵稱"
-
-#: .\accounts\models.py:13 .\blog\models.py:29 .\blog\models.py:266
-#: .\blog\models.py:284 .\comments\models.py:13 .\oauth\models.py:23
-#: .\oauth\models.py:53
-msgid "creation time"
-msgstr "創建時間"
-
-#: .\accounts\models.py:14 .\comments\models.py:14 .\oauth\models.py:24
-#: .\oauth\models.py:54
-msgid "last modify time"
-msgstr "最後修改時間"
-
-#: .\accounts\models.py:15
-msgid "create source"
-msgstr "來源"
-
-#: .\accounts\models.py:33 .\djangoblog\logentryadmin.py:81
-msgid "user"
-msgstr "用戶"
-
-#: .\accounts\tests.py:216 .\accounts\utils.py:39
-msgid "Verification code error"
-msgstr "驗證碼錯誤"
-
-#: .\accounts\utils.py:13
-msgid "Verify Email"
-msgstr "驗證郵箱"
-
-#: .\accounts\utils.py:21
-#, python-format
-msgid ""
-"You are resetting the password, the verification code is:%(code)s, valid "
-"within 5 minutes, please keep it properly"
-msgstr "您正在重置密碼,驗證碼為:%(code)s,5分鐘內有效 請妥善保管."
-
-#: .\blog\admin.py:13 .\blog\models.py:92 .\comments\models.py:17
-#: .\oauth\models.py:12
-msgid "author"
-msgstr "作者"
-
-#: .\blog\admin.py:53
-msgid "Publish selected articles"
-msgstr "發布選中的文章"
-
-#: .\blog\admin.py:54
-msgid "Draft selected articles"
-msgstr "選中文章設為草稿"
-
-#: .\blog\admin.py:55
-msgid "Close article comments"
-msgstr "關閉文章評論"
-
-#: .\blog\admin.py:56
-msgid "Open article comments"
-msgstr "打開文章評論"
-
-#: .\blog\admin.py:89 .\blog\models.py:101 .\blog\models.py:183
-#: .\templates\blog\tags\sidebar.html:40
-msgid "category"
-msgstr "分類目錄"
-
-#: .\blog\models.py:20 .\blog\models.py:179 .\templates\share_layout\nav.html:8
-msgid "index"
-msgstr "首頁"
-
-#: .\blog\models.py:21
-msgid "list"
-msgstr "列表"
-
-#: .\blog\models.py:22
-msgid "post"
-msgstr "文章"
-
-#: .\blog\models.py:23
-msgid "all"
-msgstr "所有"
-
-#: .\blog\models.py:24
-msgid "slide"
-msgstr "側邊欄"
-
-#: .\blog\models.py:30 .\blog\models.py:267 .\blog\models.py:285
-msgid "modify time"
-msgstr "修改時間"
-
-#: .\blog\models.py:63
-msgid "Draft"
-msgstr "草稿"
-
-#: .\blog\models.py:64
-msgid "Published"
-msgstr "發布"
-
-#: .\blog\models.py:67
-msgid "Open"
-msgstr "打開"
-
-#: .\blog\models.py:68
-msgid "Close"
-msgstr "關閉"
-
-#: .\blog\models.py:71 .\comments\admin.py:47
-msgid "Article"
-msgstr "文章"
-
-#: .\blog\models.py:72
-msgid "Page"
-msgstr "頁面"
-
-#: .\blog\models.py:74 .\blog\models.py:280
-msgid "title"
-msgstr "標題"
-
-#: .\blog\models.py:75
-msgid "body"
-msgstr "內容"
-
-#: .\blog\models.py:77
-msgid "publish time"
-msgstr "發布時間"
-
-#: .\blog\models.py:79
-msgid "status"
-msgstr "狀態"
-
-#: .\blog\models.py:84
-msgid "comment status"
-msgstr "評論狀態"
-
-#: .\blog\models.py:88 .\oauth\models.py:43
-msgid "type"
-msgstr "類型"
-
-#: .\blog\models.py:89
-msgid "views"
-msgstr "閱讀量"
-
-#: .\blog\models.py:97 .\blog\models.py:258 .\blog\models.py:282
-msgid "order"
-msgstr "排序"
-
-#: .\blog\models.py:98
-msgid "show toc"
-msgstr "顯示目錄"
-
-#: .\blog\models.py:105 .\blog\models.py:249
-msgid "tag"
-msgstr "標簽"
-
-#: .\blog\models.py:115 .\comments\models.py:21
-msgid "article"
-msgstr "文章"
-
-#: .\blog\models.py:171
-msgid "category name"
-msgstr "分類名"
-
-#: .\blog\models.py:174
-msgid "parent category"
-msgstr "上級分類"
-
-#: .\blog\models.py:234
-msgid "tag name"
-msgstr "標簽名"
-
-#: .\blog\models.py:256
-msgid "link name"
-msgstr "鏈接名"
-
-#: .\blog\models.py:257 .\blog\models.py:271
-msgid "link"
-msgstr "鏈接"
-
-#: .\blog\models.py:260
-msgid "is show"
-msgstr "是否顯示"
-
-#: .\blog\models.py:262
-msgid "show type"
-msgstr "顯示類型"
-
-#: .\blog\models.py:281
-msgid "content"
-msgstr "內容"
-
-#: .\blog\models.py:283 .\oauth\models.py:52
-msgid "is enable"
-msgstr "是否啟用"
-
-#: .\blog\models.py:289
-msgid "sidebar"
-msgstr "側邊欄"
-
-#: .\blog\models.py:299
-msgid "site name"
-msgstr "站點名稱"
-
-#: .\blog\models.py:305
-msgid "site description"
-msgstr "站點描述"
-
-#: .\blog\models.py:311
-msgid "site seo description"
-msgstr "站點SEO描述"
-
-#: .\blog\models.py:313
-msgid "site keywords"
-msgstr "關鍵字"
-
-#: .\blog\models.py:318
-msgid "article sub length"
-msgstr "文章摘要長度"
-
-#: .\blog\models.py:319
-msgid "sidebar article count"
-msgstr "側邊欄文章數目"
-
-#: .\blog\models.py:320
-msgid "sidebar comment count"
-msgstr "側邊欄評論數目"
-
-#: .\blog\models.py:321
-msgid "article comment count"
-msgstr "文章頁面默認顯示評論數目"
-
-#: .\blog\models.py:322
-msgid "show adsense"
-msgstr "是否顯示廣告"
-
-#: .\blog\models.py:324
-msgid "adsense code"
-msgstr "廣告內容"
-
-#: .\blog\models.py:325
-msgid "open site comment"
-msgstr "公共頭部"
-
-#: .\blog\models.py:352
-msgid "Website configuration"
-msgstr "網站配置"
-
-#: .\blog\models.py:360
-msgid "There can only be one configuration"
-msgstr "只能有一個配置"
-
-#: .\blog\views.py:348
-msgid ""
-"Sorry, the page you requested is not found, please click the home page to "
-"see other?"
-msgstr "抱歉,你所訪問的頁面找不到,請點擊首頁看看別的?"
-
-#: .\blog\views.py:356
-msgid "Sorry, the server is busy, please click the home page to see other?"
-msgstr "抱歉,服務出錯了,請點擊首頁看看別的?"
-
-#: .\blog\views.py:369
-msgid "Sorry, you do not have permission to access this page?"
-msgstr "抱歉,你沒用權限訪問此頁面。"
-
-#: .\comments\admin.py:15
-msgid "Disable comments"
-msgstr "禁用評論"
-
-#: .\comments\admin.py:16
-msgid "Enable comments"
-msgstr "啟用評論"
-
-#: .\comments\admin.py:46
-msgid "User"
-msgstr "用戶"
-
-#: .\comments\models.py:25
-msgid "parent comment"
-msgstr "上級評論"
-
-#: .\comments\models.py:29
-msgid "enable"
-msgstr "啟用"
-
-#: .\comments\models.py:34 .\templates\blog\tags\article_info.html:30
-msgid "comment"
-msgstr "評論"
-
-#: .\comments\utils.py:13
-msgid "Thanks for your comment"
-msgstr "感謝你的評論"
-
-#: .\comments\utils.py:15
-#, python-format
-msgid ""
-"Thank you very much for your comments on this site
\n"
-" You can visit %(article_title)s \n"
-" to review your comments,\n"
-" Thank you again!\n"
-" \n"
-" If the link above cannot be opened, please copy this "
-"link to your browser.\n"
-" %(article_url)s"
-msgstr ""
-"非常感謝您對此網站的評論
\n"
-" 您可以訪問%(article_title)s \n"
-"查看您的評論,\n"
-"再次感謝您!\n"
-" \n"
-" 如果上面的鏈接打不開,請復製此鏈接鏈接到您的瀏覽器。\n"
-"%(article_url)s"
-
-#: .\comments\utils.py:26
-#, python-format
-msgid ""
-"Your comment on "
-"%(article_title)s has \n"
-" received a reply. %(comment_body)s\n"
-" \n"
-" go check it out!\n"
-" \n"
-" If the link above cannot be opened, please copy this "
-"link to your browser.\n"
-" %(article_url)s\n"
-" "
-msgstr ""
-"您對 %(article_title)s "
-"的評論有\n"
-" 收到回復。 %(comment_body)s\n"
-" \n"
-"快去看看吧!\n"
-" \n"
-" 如果上面的鏈接打不開,請復製此鏈接鏈接到您的瀏覽器。\n"
-" %(article_url)s\n"
-" "
-
-#: .\djangoblog\logentryadmin.py:63
-msgid "object"
-msgstr "對象"
-
-#: .\djangoblog\settings.py:140
-msgid "English"
-msgstr "英文"
-
-#: .\djangoblog\settings.py:141
-msgid "Simplified Chinese"
-msgstr "簡體中文"
-
-#: .\djangoblog\settings.py:142
-msgid "Traditional Chinese"
-msgstr "繁體中文"
-
-#: .\oauth\models.py:30
-msgid "oauth user"
-msgstr "第三方用戶"
-
-#: .\oauth\models.py:37
-msgid "weibo"
-msgstr "微博"
-
-#: .\oauth\models.py:38
-msgid "google"
-msgstr "谷歌"
-
-#: .\oauth\models.py:48
-msgid "callback url"
-msgstr "回調地址"
-
-#: .\oauth\models.py:59
-msgid "already exists"
-msgstr "已經存在"
-
-#: .\oauth\views.py:154
-#, python-format
-msgid ""
-"\n"
-" Congratulations, you have successfully bound your email address. You "
-"can use\n"
-" %(oauthuser_type)s to directly log in to this website without a "
-"password.
\n"
-" You are welcome to continue to follow this site, the address is\n"
-" %(site)s \n"
-" Thank you again!\n"
-" \n"
-" If the link above cannot be opened, please copy this link to your "
-"browser.\n"
-" %(site)s\n"
-" "
-msgstr ""
-"\n"
-" 恭喜你已經綁定成功 你可以使用\n"
-" %(oauthuser_type)s 來免密登錄本站
\n"
-" 歡迎繼續關註本站, 地址是\n"
-" %(site)s \n"
-" 再次感謝你\n"
-" \n"
-" 如果上面鏈接無法打開,請復製此鏈接到你的瀏覽器 \n"
-" %(site)s\n"
-" "
-
-#: .\oauth\views.py:165
-msgid "Congratulations on your successful binding!"
-msgstr "恭喜你綁定成功"
-
-#: .\oauth\views.py:217
-#, python-format
-msgid ""
-"\n"
-" Please click the link below to bind your email
\n"
-"\n"
-" %(url)s \n"
-"\n"
-" Thank you again!\n"
-" \n"
-" If the link above cannot be opened, please copy this link "
-"to your browser.\n"
-" \n"
-" %(url)s\n"
-" "
-msgstr ""
-"\n"
-" 請點擊下面的鏈接綁定您的郵箱
\n"
-"\n"
-" %(url)s \n"
-"\n"
-"再次感謝您!\n"
-" \n"
-"如果上面的鏈接打不開,請復製此鏈接到您的瀏覽器。\n"
-"%(url)s\n"
-" "
-
-#: .\oauth\views.py:228 .\oauth\views.py:240
-msgid "Bind your email"
-msgstr "綁定郵箱"
-
-#: .\oauth\views.py:242
-msgid ""
-"Congratulations, the binding is just one step away. Please log in to your "
-"email to check the email to complete the binding. Thank you."
-msgstr "恭喜您,還差一步就綁定成功了,請登錄您的郵箱查看郵件完成綁定,謝謝。"
-
-#: .\oauth\views.py:245
-msgid "Binding successful"
-msgstr "綁定成功"
-
-#: .\oauth\views.py:247
-#, python-format
-msgid ""
-"Congratulations, you have successfully bound your email address. You can use "
-"%(oauthuser_type)s to directly log in to this website without a password. "
-"You are welcome to continue to follow this site."
-msgstr ""
-"恭喜您綁定成功,您以後可以使用%(oauthuser_type)s來直接免密碼登錄本站啦,感謝"
-"您對本站對關註。"
-
-#: .\templates\account\forget_password.html:7
-msgid "forget the password"
-msgstr "忘記密碼"
-
-#: .\templates\account\forget_password.html:18
-msgid "get verification code"
-msgstr "獲取驗證碼"
-
-#: .\templates\account\forget_password.html:19
-msgid "submit"
-msgstr "提交"
-
-#: .\templates\account\login.html:36
-msgid "Create Account"
-msgstr "創建賬號"
-
-#: .\templates\account\login.html:42
-#, fuzzy
-#| msgid "forget the password"
-msgid "Forget Password"
-msgstr "忘記密碼"
-
-#: .\templates\account\result.html:18 .\templates\blog\tags\sidebar.html:126
-msgid "login"
-msgstr "登錄"
-
-#: .\templates\account\result.html:22
-msgid "back to the homepage"
-msgstr "返回首頁吧"
-
-#: .\templates\blog\article_archives.html:7
-#: .\templates\blog\article_archives.html:24
-msgid "article archive"
-msgstr "文章歸檔"
-
-#: .\templates\blog\article_archives.html:32
-msgid "year"
-msgstr "年"
-
-#: .\templates\blog\article_archives.html:36
-msgid "month"
-msgstr "月"
-
-#: .\templates\blog\tags\article_info.html:12
-msgid "pin to top"
-msgstr "置頂"
-
-#: .\templates\blog\tags\article_info.html:28
-msgid "comments"
-msgstr "評論"
-
-#: .\templates\blog\tags\article_info.html:58
-msgid "toc"
-msgstr "目錄"
-
-#: .\templates\blog\tags\article_meta_info.html:6
-msgid "posted in"
-msgstr "發布於"
-
-#: .\templates\blog\tags\article_meta_info.html:14
-msgid "and tagged"
-msgstr "並標記為"
-
-#: .\templates\blog\tags\article_meta_info.html:25
-msgid "by "
-msgstr "由"
-
-#: .\templates\blog\tags\article_meta_info.html:29
-#, python-format
-msgid ""
-"\n"
-" title=\"View all articles published by "
-"%(article.author.username)s\"\n"
-" "
-msgstr ""
-"\n"
-" title=\"查看所有由 %(article.author.username)s\"發布的文章\n"
-" "
-
-#: .\templates\blog\tags\article_meta_info.html:44
-msgid "on"
-msgstr "在"
-
-#: .\templates\blog\tags\article_meta_info.html:54
-msgid "edit"
-msgstr "編輯"
-
-#: .\templates\blog\tags\article_pagination.html:4
-msgid "article navigation"
-msgstr "文章導航"
-
-#: .\templates\blog\tags\article_pagination.html:9
-msgid "earlier articles"
-msgstr "早期文章"
-
-#: .\templates\blog\tags\article_pagination.html:12
-msgid "newer articles"
-msgstr "較新文章"
-
-#: .\templates\blog\tags\article_tag_list.html:5
-msgid "tags"
-msgstr "標簽"
-
-#: .\templates\blog\tags\sidebar.html:7
-msgid "search"
-msgstr "搜索"
-
-#: .\templates\blog\tags\sidebar.html:50
-msgid "recent comments"
-msgstr "近期評論"
-
-#: .\templates\blog\tags\sidebar.html:57
-msgid "published on"
-msgstr "發表於"
-
-#: .\templates\blog\tags\sidebar.html:65
-msgid "recent articles"
-msgstr "近期文章"
-
-#: .\templates\blog\tags\sidebar.html:77
-msgid "bookmark"
-msgstr "書簽"
-
-#: .\templates\blog\tags\sidebar.html:96
-msgid "Tag Cloud"
-msgstr "標簽雲"
-
-#: .\templates\blog\tags\sidebar.html:107
-msgid "Welcome to star or fork the source code of this site"
-msgstr "歡迎您STAR或者FORK本站源代碼"
-
-#: .\templates\blog\tags\sidebar.html:118
-msgid "Function"
-msgstr "功能"
-
-#: .\templates\blog\tags\sidebar.html:120
-msgid "management site"
-msgstr "管理站點"
-
-#: .\templates\blog\tags\sidebar.html:122
-msgid "logout"
-msgstr "登出"
-
-#: .\templates\blog\tags\sidebar.html:129
-msgid "Track record"
-msgstr "運動軌跡記錄"
-
-#: .\templates\blog\tags\sidebar.html:135
-msgid "Click me to return to the top"
-msgstr "點我返回頂部"
-
-#: .\templates\oauth\oauth_applications.html:5
-#| msgid "login"
-msgid "quick login"
-msgstr "快捷登錄"
-
-#: .\templates\share_layout\nav.html:26
-msgid "Article archive"
-msgstr "文章歸檔"
diff --git a/oauth/__init__.py b/oauth/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/oauth/admin.py b/oauth/admin.py
deleted file mode 100644
index 57eab5f..0000000
--- a/oauth/admin.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import logging
-
-from django.contrib import admin
-# Register your models here.
-from django.urls import reverse
-from django.utils.html import format_html
-
-logger = logging.getLogger(__name__)
-
-
-class OAuthUserAdmin(admin.ModelAdmin):
- search_fields = ('nickname', 'email')
- list_per_page = 20
- list_display = (
- 'id',
- 'nickname',
- 'link_to_usermodel',
- 'show_user_image',
- 'type',
- 'email',
- )
- list_display_links = ('id', 'nickname')
- list_filter = ('author', 'type',)
- readonly_fields = []
-
- def get_readonly_fields(self, request, obj=None):
- return list(self.readonly_fields) + \
- [field.name for field in obj._meta.fields] + \
- [field.name for field in obj._meta.many_to_many]
-
- def has_add_permission(self, request):
- return False
-
- def link_to_usermodel(self, obj):
- if obj.author:
- info = (obj.author._meta.app_label, obj.author._meta.model_name)
- link = reverse('admin:%s_%s_change' % info, args=(obj.author.id,))
- return format_html(
- u'%s ' %
- (link, obj.author.nickname if obj.author.nickname else obj.author.email))
-
- def show_user_image(self, obj):
- img = obj.picture
- return format_html(
- u' ' %
- (img))
-
- link_to_usermodel.short_description = '用户'
- show_user_image.short_description = '用户头像'
-
-
-class OAuthConfigAdmin(admin.ModelAdmin):
- list_display = ('type', 'appkey', 'appsecret', 'is_enable')
- list_filter = ('type',)
diff --git a/oauth/apps.py b/oauth/apps.py
deleted file mode 100644
index 17fcea2..0000000
--- a/oauth/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class OauthConfig(AppConfig):
- name = 'oauth'
diff --git a/oauth/forms.py b/oauth/forms.py
deleted file mode 100644
index 0e4ede3..0000000
--- a/oauth/forms.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.contrib.auth.forms import forms
-from django.forms import widgets
-
-
-class RequireEmailForm(forms.Form):
- email = forms.EmailField(label='电子邮箱', required=True)
- oauthid = forms.IntegerField(widget=forms.HiddenInput, required=False)
-
- def __init__(self, *args, **kwargs):
- super(RequireEmailForm, self).__init__(*args, **kwargs)
- self.fields['email'].widget = widgets.EmailInput(
- attrs={'placeholder': "email", "class": "form-control"})
diff --git a/oauth/migrations/0001_initial.py b/oauth/migrations/0001_initial.py
deleted file mode 100644
index 3aa3e03..0000000
--- a/oauth/migrations/0001_initial.py
+++ /dev/null
@@ -1,57 +0,0 @@
-# Generated by Django 4.1.7 on 2023-03-07 09:53
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ]
-
- operations = [
- migrations.CreateModel(
- name='OAuthConfig',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('type', models.CharField(choices=[('weibo', '微博'), ('google', '谷歌'), ('github', 'GitHub'), ('facebook', 'FaceBook'), ('qq', 'QQ')], default='a', max_length=10, verbose_name='类型')),
- ('appkey', models.CharField(max_length=200, verbose_name='AppKey')),
- ('appsecret', models.CharField(max_length=200, verbose_name='AppSecret')),
- ('callback_url', models.CharField(default='http://www.baidu.com', max_length=200, verbose_name='回调地址')),
- ('is_enable', models.BooleanField(default=True, verbose_name='是否显示')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ],
- options={
- 'verbose_name': 'oauth配置',
- 'verbose_name_plural': 'oauth配置',
- 'ordering': ['-created_time'],
- },
- ),
- migrations.CreateModel(
- name='OAuthUser',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('openid', models.CharField(max_length=50)),
- ('nickname', models.CharField(max_length=50, verbose_name='昵称')),
- ('token', models.CharField(blank=True, max_length=150, null=True)),
- ('picture', models.CharField(blank=True, max_length=350, null=True)),
- ('type', models.CharField(max_length=50)),
- ('email', models.CharField(blank=True, max_length=50, null=True)),
- ('metadata', models.TextField(blank=True, null=True)),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='修改时间')),
- ('author', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='用户')),
- ],
- options={
- 'verbose_name': 'oauth用户',
- 'verbose_name_plural': 'oauth用户',
- 'ordering': ['-created_time'],
- },
- ),
- ]
diff --git a/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py b/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py
deleted file mode 100644
index d5cc70e..0000000
--- a/oauth/migrations/0002_alter_oauthconfig_options_alter_oauthuser_options_and_more.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Generated by Django 4.2.5 on 2023-09-06 13:13
-
-from django.conf import settings
-from django.db import migrations, models
-import django.db.models.deletion
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- migrations.swappable_dependency(settings.AUTH_USER_MODEL),
- ('oauth', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='oauthconfig',
- options={'ordering': ['-creation_time'], 'verbose_name': 'oauth配置', 'verbose_name_plural': 'oauth配置'},
- ),
- migrations.AlterModelOptions(
- name='oauthuser',
- options={'ordering': ['-creation_time'], 'verbose_name': 'oauth user', 'verbose_name_plural': 'oauth user'},
- ),
- migrations.RemoveField(
- model_name='oauthconfig',
- name='created_time',
- ),
- migrations.RemoveField(
- model_name='oauthconfig',
- name='last_mod_time',
- ),
- migrations.RemoveField(
- model_name='oauthuser',
- name='created_time',
- ),
- migrations.RemoveField(
- model_name='oauthuser',
- name='last_mod_time',
- ),
- migrations.AddField(
- model_name='oauthconfig',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
- ),
- migrations.AddField(
- model_name='oauthconfig',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
- ),
- migrations.AddField(
- model_name='oauthuser',
- name='creation_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='creation time'),
- ),
- migrations.AddField(
- model_name='oauthuser',
- name='last_modify_time',
- field=models.DateTimeField(default=django.utils.timezone.now, verbose_name='last modify time'),
- ),
- migrations.AlterField(
- model_name='oauthconfig',
- name='callback_url',
- field=models.CharField(default='', max_length=200, verbose_name='callback url'),
- ),
- migrations.AlterField(
- model_name='oauthconfig',
- name='is_enable',
- field=models.BooleanField(default=True, verbose_name='is enable'),
- ),
- migrations.AlterField(
- model_name='oauthconfig',
- name='type',
- field=models.CharField(choices=[('weibo', 'weibo'), ('google', 'google'), ('github', 'GitHub'), ('facebook', 'FaceBook'), ('qq', 'QQ')], default='a', max_length=10, verbose_name='type'),
- ),
- migrations.AlterField(
- model_name='oauthuser',
- name='author',
- field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL, verbose_name='author'),
- ),
- migrations.AlterField(
- model_name='oauthuser',
- name='nickname',
- field=models.CharField(max_length=50, verbose_name='nickname'),
- ),
- ]
diff --git a/oauth/migrations/0003_alter_oauthuser_nickname.py b/oauth/migrations/0003_alter_oauthuser_nickname.py
deleted file mode 100644
index 6af08eb..0000000
--- a/oauth/migrations/0003_alter_oauthuser_nickname.py
+++ /dev/null
@@ -1,18 +0,0 @@
-# Generated by Django 4.2.7 on 2024-01-26 02:41
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('oauth', '0002_alter_oauthconfig_options_alter_oauthuser_options_and_more'),
- ]
-
- operations = [
- migrations.AlterField(
- model_name='oauthuser',
- name='nickname',
- field=models.CharField(max_length=50, verbose_name='nick name'),
- ),
- ]
diff --git a/oauth/migrations/__init__.py b/oauth/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/oauth/models.py b/oauth/models.py
deleted file mode 100644
index be838ed..0000000
--- a/oauth/models.py
+++ /dev/null
@@ -1,67 +0,0 @@
-# Create your models here.
-from django.conf import settings
-from django.core.exceptions import ValidationError
-from django.db import models
-from django.utils.timezone import now
-from django.utils.translation import gettext_lazy as _
-
-
-class OAuthUser(models.Model):
- author = models.ForeignKey(
- settings.AUTH_USER_MODEL,
- verbose_name=_('author'),
- blank=True,
- null=True,
- on_delete=models.CASCADE)
- openid = models.CharField(max_length=50)
- nickname = models.CharField(max_length=50, verbose_name=_('nick name'))
- token = models.CharField(max_length=150, null=True, blank=True)
- picture = models.CharField(max_length=350, blank=True, null=True)
- type = models.CharField(blank=False, null=False, max_length=50)
- email = models.CharField(max_length=50, null=True, blank=True)
- metadata = models.TextField(null=True, blank=True)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_modify_time = models.DateTimeField(_('last modify time'), default=now)
-
- def __str__(self):
- return self.nickname
-
- class Meta:
- verbose_name = _('oauth user')
- verbose_name_plural = verbose_name
- ordering = ['-creation_time']
-
-
-class OAuthConfig(models.Model):
- TYPE = (
- ('weibo', _('weibo')),
- ('google', _('google')),
- ('github', 'GitHub'),
- ('facebook', 'FaceBook'),
- ('qq', 'QQ'),
- )
- type = models.CharField(_('type'), max_length=10, choices=TYPE, default='a')
- appkey = models.CharField(max_length=200, verbose_name='AppKey')
- appsecret = models.CharField(max_length=200, verbose_name='AppSecret')
- callback_url = models.CharField(
- max_length=200,
- verbose_name=_('callback url'),
- blank=False,
- default='')
- is_enable = models.BooleanField(
- _('is enable'), default=True, blank=False, null=False)
- creation_time = models.DateTimeField(_('creation time'), default=now)
- last_modify_time = models.DateTimeField(_('last modify time'), default=now)
-
- def clean(self):
- if OAuthConfig.objects.filter(
- type=self.type).exclude(id=self.id).count():
- raise ValidationError(_(self.type + _('already exists')))
-
- def __str__(self):
- return self.type
-
- class Meta:
- verbose_name = 'oauth配置'
- verbose_name_plural = verbose_name
- ordering = ['-creation_time']
diff --git a/oauth/oauthmanager.py b/oauth/oauthmanager.py
deleted file mode 100644
index 2e7ceef..0000000
--- a/oauth/oauthmanager.py
+++ /dev/null
@@ -1,504 +0,0 @@
-import json
-import logging
-import os
-import urllib.parse
-from abc import ABCMeta, abstractmethod
-
-import requests
-
-from djangoblog.utils import cache_decorator
-from oauth.models import OAuthUser, OAuthConfig
-
-logger = logging.getLogger(__name__)
-
-
-class OAuthAccessTokenException(Exception):
- '''
- oauth授权失败异常
- '''
-
-
-class BaseOauthManager(metaclass=ABCMeta):
- """获取用户授权"""
- AUTH_URL = None
- """获取token"""
- TOKEN_URL = None
- """获取用户信息"""
- API_URL = None
- '''icon图标名'''
- ICON_NAME = None
-
- def __init__(self, access_token=None, openid=None):
- self.access_token = access_token
- self.openid = openid
-
- @property
- def is_access_token_set(self):
- return self.access_token is not None
-
- @property
- def is_authorized(self):
- return self.is_access_token_set and self.access_token is not None and self.openid is not None
-
- @abstractmethod
- def get_authorization_url(self, nexturl='/'):
- pass
-
- @abstractmethod
- def get_access_token_by_code(self, code):
- pass
-
- @abstractmethod
- def get_oauth_userinfo(self):
- pass
-
- @abstractmethod
- def get_picture(self, metadata):
- pass
-
- def do_get(self, url, params, headers=None):
- rsp = requests.get(url=url, params=params, headers=headers)
- logger.info(rsp.text)
- return rsp.text
-
- def do_post(self, url, params, headers=None):
- rsp = requests.post(url, params, headers=headers)
- logger.info(rsp.text)
- return rsp.text
-
- def get_config(self):
- value = OAuthConfig.objects.filter(type=self.ICON_NAME)
- return value[0] if value else None
-
-
-class WBOauthManager(BaseOauthManager):
- AUTH_URL = 'https://api.weibo.com/oauth2/authorize'
- TOKEN_URL = 'https://api.weibo.com/oauth2/access_token'
- API_URL = 'https://api.weibo.com/2/users/show.json'
- ICON_NAME = 'weibo'
-
- def __init__(self, access_token=None, openid=None):
- config = self.get_config()
- self.client_id = config.appkey if config else ''
- self.client_secret = config.appsecret if config else ''
- self.callback_url = config.callback_url if config else ''
- super(
- WBOauthManager,
- self).__init__(
- access_token=access_token,
- openid=openid)
-
- def get_authorization_url(self, nexturl='/'):
- params = {
- 'client_id': self.client_id,
- 'response_type': 'code',
- 'redirect_uri': self.callback_url + '&next_url=' + nexturl
- }
- url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
- return url
-
- def get_access_token_by_code(self, code):
-
- params = {
- 'client_id': self.client_id,
- 'client_secret': self.client_secret,
- 'grant_type': 'authorization_code',
- 'code': code,
- 'redirect_uri': self.callback_url
- }
- rsp = self.do_post(self.TOKEN_URL, params)
-
- obj = json.loads(rsp)
- if 'access_token' in obj:
- self.access_token = str(obj['access_token'])
- self.openid = str(obj['uid'])
- return self.get_oauth_userinfo()
- else:
- raise OAuthAccessTokenException(rsp)
-
- def get_oauth_userinfo(self):
- if not self.is_authorized:
- return None
- params = {
- 'uid': self.openid,
- 'access_token': self.access_token
- }
- rsp = self.do_get(self.API_URL, params)
- try:
- datas = json.loads(rsp)
- user = OAuthUser()
- user.metadata = rsp
- user.picture = datas['avatar_large']
- user.nickname = datas['screen_name']
- user.openid = datas['id']
- user.type = 'weibo'
- user.token = self.access_token
- if 'email' in datas and datas['email']:
- user.email = datas['email']
- return user
- except Exception as e:
- logger.error(e)
- logger.error('weibo oauth error.rsp:' + rsp)
- return None
-
- def get_picture(self, metadata):
- datas = json.loads(metadata)
- return datas['avatar_large']
-
-
-class ProxyManagerMixin:
- def __init__(self, *args, **kwargs):
- if os.environ.get("HTTP_PROXY"):
- self.proxies = {
- "http": os.environ.get("HTTP_PROXY"),
- "https": os.environ.get("HTTP_PROXY")
- }
- else:
- self.proxies = None
-
- def do_get(self, url, params, headers=None):
- rsp = requests.get(url=url, params=params, headers=headers, proxies=self.proxies)
- logger.info(rsp.text)
- return rsp.text
-
- def do_post(self, url, params, headers=None):
- rsp = requests.post(url, params, headers=headers, proxies=self.proxies)
- logger.info(rsp.text)
- return rsp.text
-
-
-class GoogleOauthManager(ProxyManagerMixin, BaseOauthManager):
- AUTH_URL = 'https://accounts.google.com/o/oauth2/v2/auth'
- TOKEN_URL = 'https://www.googleapis.com/oauth2/v4/token'
- API_URL = 'https://www.googleapis.com/oauth2/v3/userinfo'
- ICON_NAME = 'google'
-
- def __init__(self, access_token=None, openid=None):
- config = self.get_config()
- self.client_id = config.appkey if config else ''
- self.client_secret = config.appsecret if config else ''
- self.callback_url = config.callback_url if config else ''
- super(
- GoogleOauthManager,
- self).__init__(
- access_token=access_token,
- openid=openid)
-
- def get_authorization_url(self, nexturl='/'):
- params = {
- 'client_id': self.client_id,
- 'response_type': 'code',
- 'redirect_uri': self.callback_url,
- 'scope': 'openid email',
- }
- url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
- return url
-
- def get_access_token_by_code(self, code):
- params = {
- 'client_id': self.client_id,
- 'client_secret': self.client_secret,
- 'grant_type': 'authorization_code',
- 'code': code,
-
- 'redirect_uri': self.callback_url
- }
- rsp = self.do_post(self.TOKEN_URL, params)
-
- obj = json.loads(rsp)
-
- if 'access_token' in obj:
- self.access_token = str(obj['access_token'])
- self.openid = str(obj['id_token'])
- logger.info(self.ICON_NAME + ' oauth ' + rsp)
- return self.access_token
- else:
- raise OAuthAccessTokenException(rsp)
-
- def get_oauth_userinfo(self):
- if not self.is_authorized:
- return None
- params = {
- 'access_token': self.access_token
- }
- rsp = self.do_get(self.API_URL, params)
- try:
-
- datas = json.loads(rsp)
- user = OAuthUser()
- user.metadata = rsp
- user.picture = datas['picture']
- user.nickname = datas['name']
- user.openid = datas['sub']
- user.token = self.access_token
- user.type = 'google'
- if datas['email']:
- user.email = datas['email']
- return user
- except Exception as e:
- logger.error(e)
- logger.error('google oauth error.rsp:' + rsp)
- return None
-
- def get_picture(self, metadata):
- datas = json.loads(metadata)
- return datas['picture']
-
-
-class GitHubOauthManager(ProxyManagerMixin, BaseOauthManager):
- AUTH_URL = 'https://github.com/login/oauth/authorize'
- TOKEN_URL = 'https://github.com/login/oauth/access_token'
- API_URL = 'https://api.github.com/user'
- ICON_NAME = 'github'
-
- def __init__(self, access_token=None, openid=None):
- config = self.get_config()
- self.client_id = config.appkey if config else ''
- self.client_secret = config.appsecret if config else ''
- self.callback_url = config.callback_url if config else ''
- super(
- GitHubOauthManager,
- self).__init__(
- access_token=access_token,
- openid=openid)
-
- def get_authorization_url(self, next_url='/'):
- params = {
- 'client_id': self.client_id,
- 'response_type': 'code',
- 'redirect_uri': f'{self.callback_url}&next_url={next_url}',
- 'scope': 'user'
- }
- url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
- return url
-
- def get_access_token_by_code(self, code):
- params = {
- 'client_id': self.client_id,
- 'client_secret': self.client_secret,
- 'grant_type': 'authorization_code',
- 'code': code,
-
- 'redirect_uri': self.callback_url
- }
- rsp = self.do_post(self.TOKEN_URL, params)
-
- from urllib import parse
- r = parse.parse_qs(rsp)
- if 'access_token' in r:
- self.access_token = (r['access_token'][0])
- return self.access_token
- else:
- raise OAuthAccessTokenException(rsp)
-
- def get_oauth_userinfo(self):
-
- rsp = self.do_get(self.API_URL, params={}, headers={
- "Authorization": "token " + self.access_token
- })
- try:
- datas = json.loads(rsp)
- user = OAuthUser()
- user.picture = datas['avatar_url']
- user.nickname = datas['name']
- user.openid = datas['id']
- user.type = 'github'
- user.token = self.access_token
- user.metadata = rsp
- if 'email' in datas and datas['email']:
- user.email = datas['email']
- return user
- except Exception as e:
- logger.error(e)
- logger.error('github oauth error.rsp:' + rsp)
- return None
-
- def get_picture(self, metadata):
- datas = json.loads(metadata)
- return datas['avatar_url']
-
-
-class FaceBookOauthManager(ProxyManagerMixin, BaseOauthManager):
- AUTH_URL = 'https://www.facebook.com/v16.0/dialog/oauth'
- TOKEN_URL = 'https://graph.facebook.com/v16.0/oauth/access_token'
- API_URL = 'https://graph.facebook.com/me'
- ICON_NAME = 'facebook'
-
- def __init__(self, access_token=None, openid=None):
- config = self.get_config()
- self.client_id = config.appkey if config else ''
- self.client_secret = config.appsecret if config else ''
- self.callback_url = config.callback_url if config else ''
- super(
- FaceBookOauthManager,
- self).__init__(
- access_token=access_token,
- openid=openid)
-
- def get_authorization_url(self, next_url='/'):
- params = {
- 'client_id': self.client_id,
- 'response_type': 'code',
- 'redirect_uri': self.callback_url,
- 'scope': 'email,public_profile'
- }
- url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
- return url
-
- def get_access_token_by_code(self, code):
- params = {
- 'client_id': self.client_id,
- 'client_secret': self.client_secret,
- # 'grant_type': 'authorization_code',
- 'code': code,
-
- 'redirect_uri': self.callback_url
- }
- rsp = self.do_post(self.TOKEN_URL, params)
-
- obj = json.loads(rsp)
- if 'access_token' in obj:
- token = str(obj['access_token'])
- self.access_token = token
- return self.access_token
- else:
- raise OAuthAccessTokenException(rsp)
-
- def get_oauth_userinfo(self):
- params = {
- 'access_token': self.access_token,
- 'fields': 'id,name,picture,email'
- }
- try:
- rsp = self.do_get(self.API_URL, params)
- datas = json.loads(rsp)
- user = OAuthUser()
- user.nickname = datas['name']
- user.openid = datas['id']
- user.type = 'facebook'
- user.token = self.access_token
- user.metadata = rsp
- if 'email' in datas and datas['email']:
- user.email = datas['email']
- if 'picture' in datas and datas['picture'] and datas['picture']['data'] and datas['picture']['data']['url']:
- user.picture = str(datas['picture']['data']['url'])
- return user
- except Exception as e:
- logger.error(e)
- return None
-
- def get_picture(self, metadata):
- datas = json.loads(metadata)
- return str(datas['picture']['data']['url'])
-
-
-class QQOauthManager(BaseOauthManager):
- AUTH_URL = 'https://graph.qq.com/oauth2.0/authorize'
- TOKEN_URL = 'https://graph.qq.com/oauth2.0/token'
- API_URL = 'https://graph.qq.com/user/get_user_info'
- OPEN_ID_URL = 'https://graph.qq.com/oauth2.0/me'
- ICON_NAME = 'qq'
-
- def __init__(self, access_token=None, openid=None):
- config = self.get_config()
- self.client_id = config.appkey if config else ''
- self.client_secret = config.appsecret if config else ''
- self.callback_url = config.callback_url if config else ''
- super(
- QQOauthManager,
- self).__init__(
- access_token=access_token,
- openid=openid)
-
- def get_authorization_url(self, next_url='/'):
- params = {
- 'response_type': 'code',
- 'client_id': self.client_id,
- 'redirect_uri': self.callback_url + '&next_url=' + next_url,
- }
- url = self.AUTH_URL + "?" + urllib.parse.urlencode(params)
- return url
-
- def get_access_token_by_code(self, code):
- params = {
- 'grant_type': 'authorization_code',
- 'client_id': self.client_id,
- 'client_secret': self.client_secret,
- 'code': code,
- 'redirect_uri': self.callback_url
- }
- rsp = self.do_get(self.TOKEN_URL, params)
- if rsp:
- d = urllib.parse.parse_qs(rsp)
- if 'access_token' in d:
- token = d['access_token']
- self.access_token = token[0]
- return token
- else:
- raise OAuthAccessTokenException(rsp)
-
- def get_open_id(self):
- if self.is_access_token_set:
- params = {
- 'access_token': self.access_token
- }
- rsp = self.do_get(self.OPEN_ID_URL, params)
- if rsp:
- rsp = rsp.replace(
- 'callback(', '').replace(
- ')', '').replace(
- ';', '')
- obj = json.loads(rsp)
- openid = str(obj['openid'])
- self.openid = openid
- return openid
-
- def get_oauth_userinfo(self):
- openid = self.get_open_id()
- if openid:
- params = {
- 'access_token': self.access_token,
- 'oauth_consumer_key': self.client_id,
- 'openid': self.openid
- }
- rsp = self.do_get(self.API_URL, params)
- logger.info(rsp)
- obj = json.loads(rsp)
- user = OAuthUser()
- user.nickname = obj['nickname']
- user.openid = openid
- user.type = 'qq'
- user.token = self.access_token
- user.metadata = rsp
- if 'email' in obj:
- user.email = obj['email']
- if 'figureurl' in obj:
- user.picture = str(obj['figureurl'])
- return user
-
- def get_picture(self, metadata):
- datas = json.loads(metadata)
- return str(datas['figureurl'])
-
-
-@cache_decorator(expiration=100 * 60)
-def get_oauth_apps():
- configs = OAuthConfig.objects.filter(is_enable=True).all()
- if not configs:
- return []
- configtypes = [x.type for x in configs]
- applications = BaseOauthManager.__subclasses__()
- apps = [x() for x in applications if x().ICON_NAME.lower() in configtypes]
- return apps
-
-
-def get_manager_by_type(type):
- applications = get_oauth_apps()
- if applications:
- finds = list(
- filter(
- lambda x: x.ICON_NAME.lower() == type.lower(),
- applications))
- if finds:
- return finds[0]
- return None
diff --git a/oauth/templatetags/__init__.py b/oauth/templatetags/__init__.py
deleted file mode 100644
index 8b13789..0000000
--- a/oauth/templatetags/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/oauth/templatetags/oauth_tags.py b/oauth/templatetags/oauth_tags.py
deleted file mode 100644
index 7b687d5..0000000
--- a/oauth/templatetags/oauth_tags.py
+++ /dev/null
@@ -1,22 +0,0 @@
-from django import template
-from django.urls import reverse
-
-from oauth.oauthmanager import get_oauth_apps
-
-register = template.Library()
-
-
-@register.inclusion_tag('oauth/oauth_applications.html')
-def load_oauth_applications(request):
- applications = get_oauth_apps()
- if applications:
- baseurl = reverse('oauth:oauthlogin')
- path = request.get_full_path()
-
- apps = list(map(lambda x: (x.ICON_NAME, '{baseurl}?type={type}&next_url={next}'.format(
- baseurl=baseurl, type=x.ICON_NAME, next=path)), applications))
- else:
- apps = []
- return {
- 'apps': apps
- }
diff --git a/oauth/tests.py b/oauth/tests.py
deleted file mode 100644
index bb23b9b..0000000
--- a/oauth/tests.py
+++ /dev/null
@@ -1,249 +0,0 @@
-import json
-from unittest.mock import patch
-
-from django.conf import settings
-from django.contrib import auth
-from django.test import Client, RequestFactory, TestCase
-from django.urls import reverse
-
-from djangoblog.utils import get_sha256
-from oauth.models import OAuthConfig
-from oauth.oauthmanager import BaseOauthManager
-
-
-# Create your tests here.
-class OAuthConfigTest(TestCase):
- def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
-
- def test_oauth_login_test(self):
- c = OAuthConfig()
- c.type = 'weibo'
- c.appkey = 'appkey'
- c.appsecret = 'appsecret'
- c.save()
-
- response = self.client.get('/oauth/oauthlogin?type=weibo')
- self.assertEqual(response.status_code, 302)
- self.assertTrue("api.weibo.com" in response.url)
-
- response = self.client.get('/oauth/authorize?type=weibo&code=code')
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, '/')
-
-
-class OauthLoginTest(TestCase):
- def setUp(self) -> None:
- self.client = Client()
- self.factory = RequestFactory()
- self.apps = self.init_apps()
-
- def init_apps(self):
- applications = [p() for p in BaseOauthManager.__subclasses__()]
- for application in applications:
- c = OAuthConfig()
- c.type = application.ICON_NAME.lower()
- c.appkey = 'appkey'
- c.appsecret = 'appsecret'
- c.save()
- return applications
-
- def get_app_by_type(self, type):
- for app in self.apps:
- if app.ICON_NAME.lower() == type:
- return app
-
- @patch("oauth.oauthmanager.WBOauthManager.do_post")
- @patch("oauth.oauthmanager.WBOauthManager.do_get")
- def test_weibo_login(self, mock_do_get, mock_do_post):
- weibo_app = self.get_app_by_type('weibo')
- assert weibo_app
- url = weibo_app.get_authorization_url()
- mock_do_post.return_value = json.dumps({"access_token": "access_token",
- "uid": "uid"
- })
- mock_do_get.return_value = json.dumps({
- "avatar_large": "avatar_large",
- "screen_name": "screen_name",
- "id": "id",
- "email": "email",
- })
- userinfo = weibo_app.get_access_token_by_code('code')
- self.assertEqual(userinfo.token, 'access_token')
- self.assertEqual(userinfo.openid, 'id')
-
- @patch("oauth.oauthmanager.GoogleOauthManager.do_post")
- @patch("oauth.oauthmanager.GoogleOauthManager.do_get")
- def test_google_login(self, mock_do_get, mock_do_post):
- google_app = self.get_app_by_type('google')
- assert google_app
- url = google_app.get_authorization_url()
- mock_do_post.return_value = json.dumps({
- "access_token": "access_token",
- "id_token": "id_token",
- })
- mock_do_get.return_value = json.dumps({
- "picture": "picture",
- "name": "name",
- "sub": "sub",
- "email": "email",
- })
- token = google_app.get_access_token_by_code('code')
- userinfo = google_app.get_oauth_userinfo()
- self.assertEqual(userinfo.token, 'access_token')
- self.assertEqual(userinfo.openid, 'sub')
-
- @patch("oauth.oauthmanager.GitHubOauthManager.do_post")
- @patch("oauth.oauthmanager.GitHubOauthManager.do_get")
- def test_github_login(self, mock_do_get, mock_do_post):
- github_app = self.get_app_by_type('github')
- assert github_app
- url = github_app.get_authorization_url()
- self.assertTrue("github.com" in url)
- self.assertTrue("client_id" in url)
- mock_do_post.return_value = "access_token=gho_16C7e42F292c6912E7710c838347Ae178B4a&scope=repo%2Cgist&token_type=bearer"
- mock_do_get.return_value = json.dumps({
- "avatar_url": "avatar_url",
- "name": "name",
- "id": "id",
- "email": "email",
- })
- token = github_app.get_access_token_by_code('code')
- userinfo = github_app.get_oauth_userinfo()
- self.assertEqual(userinfo.token, 'gho_16C7e42F292c6912E7710c838347Ae178B4a')
- self.assertEqual(userinfo.openid, 'id')
-
- @patch("oauth.oauthmanager.FaceBookOauthManager.do_post")
- @patch("oauth.oauthmanager.FaceBookOauthManager.do_get")
- def test_facebook_login(self, mock_do_get, mock_do_post):
- facebook_app = self.get_app_by_type('facebook')
- assert facebook_app
- url = facebook_app.get_authorization_url()
- self.assertTrue("facebook.com" in url)
- mock_do_post.return_value = json.dumps({
- "access_token": "access_token",
- })
- mock_do_get.return_value = json.dumps({
- "name": "name",
- "id": "id",
- "email": "email",
- "picture": {
- "data": {
- "url": "url"
- }
- }
- })
- token = facebook_app.get_access_token_by_code('code')
- userinfo = facebook_app.get_oauth_userinfo()
- self.assertEqual(userinfo.token, 'access_token')
-
- @patch("oauth.oauthmanager.QQOauthManager.do_get", side_effect=[
- 'access_token=access_token&expires_in=3600',
- 'callback({"client_id":"appid","openid":"openid"} );',
- json.dumps({
- "nickname": "nickname",
- "email": "email",
- "figureurl": "figureurl",
- "openid": "openid",
- })
- ])
- def test_qq_login(self, mock_do_get):
- qq_app = self.get_app_by_type('qq')
- assert qq_app
- url = qq_app.get_authorization_url()
- self.assertTrue("qq.com" in url)
- token = qq_app.get_access_token_by_code('code')
- userinfo = qq_app.get_oauth_userinfo()
- self.assertEqual(userinfo.token, 'access_token')
-
- @patch("oauth.oauthmanager.WBOauthManager.do_post")
- @patch("oauth.oauthmanager.WBOauthManager.do_get")
- def test_weibo_authoriz_login_with_email(self, mock_do_get, mock_do_post):
-
- mock_do_post.return_value = json.dumps({"access_token": "access_token",
- "uid": "uid"
- })
- mock_user_info = {
- "avatar_large": "avatar_large",
- "screen_name": "screen_name1",
- "id": "id",
- "email": "email",
- }
- mock_do_get.return_value = json.dumps(mock_user_info)
-
- response = self.client.get('/oauth/oauthlogin?type=weibo')
- self.assertEqual(response.status_code, 302)
- self.assertTrue("api.weibo.com" in response.url)
-
- response = self.client.get('/oauth/authorize?type=weibo&code=code')
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, '/')
-
- user = auth.get_user(self.client)
- assert user.is_authenticated
- self.assertTrue(user.is_authenticated)
- self.assertEqual(user.username, mock_user_info['screen_name'])
- self.assertEqual(user.email, mock_user_info['email'])
- self.client.logout()
-
- response = self.client.get('/oauth/authorize?type=weibo&code=code')
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, '/')
-
- user = auth.get_user(self.client)
- assert user.is_authenticated
- self.assertTrue(user.is_authenticated)
- self.assertEqual(user.username, mock_user_info['screen_name'])
- self.assertEqual(user.email, mock_user_info['email'])
-
- @patch("oauth.oauthmanager.WBOauthManager.do_post")
- @patch("oauth.oauthmanager.WBOauthManager.do_get")
- def test_weibo_authoriz_login_without_email(self, mock_do_get, mock_do_post):
-
- mock_do_post.return_value = json.dumps({"access_token": "access_token",
- "uid": "uid"
- })
- mock_user_info = {
- "avatar_large": "avatar_large",
- "screen_name": "screen_name1",
- "id": "id",
- }
- mock_do_get.return_value = json.dumps(mock_user_info)
-
- response = self.client.get('/oauth/oauthlogin?type=weibo')
- self.assertEqual(response.status_code, 302)
- self.assertTrue("api.weibo.com" in response.url)
-
- response = self.client.get('/oauth/authorize?type=weibo&code=code')
-
- self.assertEqual(response.status_code, 302)
-
- oauth_user_id = int(response.url.split('/')[-1].split('.')[0])
- self.assertEqual(response.url, f'/oauth/requireemail/{oauth_user_id}.html')
-
- response = self.client.post(response.url, {'email': 'test@gmail.com', 'oauthid': oauth_user_id})
-
- self.assertEqual(response.status_code, 302)
- sign = get_sha256(settings.SECRET_KEY +
- str(oauth_user_id) + settings.SECRET_KEY)
-
- url = reverse('oauth:bindsuccess', kwargs={
- 'oauthid': oauth_user_id,
- })
- self.assertEqual(response.url, f'{url}?type=email')
-
- path = reverse('oauth:email_confirm', kwargs={
- 'id': oauth_user_id,
- 'sign': sign
- })
- response = self.client.get(path)
- self.assertEqual(response.status_code, 302)
- self.assertEqual(response.url, f'/oauth/bindsuccess/{oauth_user_id}.html?type=success')
- user = auth.get_user(self.client)
- from oauth.models import OAuthUser
- oauth_user = OAuthUser.objects.get(author=user)
- self.assertTrue(user.is_authenticated)
- self.assertEqual(user.username, mock_user_info['screen_name'])
- self.assertEqual(user.email, 'test@gmail.com')
- self.assertEqual(oauth_user.pk, oauth_user_id)
diff --git a/oauth/urls.py b/oauth/urls.py
deleted file mode 100644
index c4a12a0..0000000
--- a/oauth/urls.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from django.urls import path
-
-from . import views
-
-app_name = "oauth"
-urlpatterns = [
- path(
- r'oauth/authorize',
- views.authorize),
- path(
- r'oauth/requireemail/.html',
- views.RequireEmailView.as_view(),
- name='require_email'),
- path(
- r'oauth/emailconfirm//.html',
- views.emailconfirm,
- name='email_confirm'),
- path(
- r'oauth/bindsuccess/.html',
- views.bindsuccess,
- name='bindsuccess'),
- path(
- r'oauth/oauthlogin',
- views.oauthlogin,
- name='oauthlogin')]
diff --git a/oauth/views.py b/oauth/views.py
deleted file mode 100644
index 12e3a6e..0000000
--- a/oauth/views.py
+++ /dev/null
@@ -1,253 +0,0 @@
-import logging
-# Create your views here.
-from urllib.parse import urlparse
-
-from django.conf import settings
-from django.contrib.auth import get_user_model
-from django.contrib.auth import login
-from django.core.exceptions import ObjectDoesNotExist
-from django.db import transaction
-from django.http import HttpResponseForbidden
-from django.http import HttpResponseRedirect
-from django.shortcuts import get_object_or_404
-from django.shortcuts import render
-from django.urls import reverse
-from django.utils import timezone
-from django.utils.translation import gettext_lazy as _
-from django.views.generic import FormView
-
-from djangoblog.blog_signals import oauth_user_login_signal
-from djangoblog.utils import get_current_site
-from djangoblog.utils import send_email, get_sha256
-from oauth.forms import RequireEmailForm
-from .models import OAuthUser
-from .oauthmanager import get_manager_by_type, OAuthAccessTokenException
-
-logger = logging.getLogger(__name__)
-
-
-def get_redirecturl(request):
- nexturl = request.GET.get('next_url', None)
- if not nexturl or nexturl == '/login/' or nexturl == '/login':
- nexturl = '/'
- return nexturl
- p = urlparse(nexturl)
- if p.netloc:
- site = get_current_site().domain
- if not p.netloc.replace('www.', '') == site.replace('www.', ''):
- logger.info('非法url:' + nexturl)
- return "/"
- return nexturl
-
-
-def oauthlogin(request):
- type = request.GET.get('type', None)
- if not type:
- return HttpResponseRedirect('/')
- manager = get_manager_by_type(type)
- if not manager:
- return HttpResponseRedirect('/')
- nexturl = get_redirecturl(request)
- authorizeurl = manager.get_authorization_url(nexturl)
- return HttpResponseRedirect(authorizeurl)
-
-
-def authorize(request):
- type = request.GET.get('type', None)
- if not type:
- return HttpResponseRedirect('/')
- manager = get_manager_by_type(type)
- if not manager:
- return HttpResponseRedirect('/')
- code = request.GET.get('code', None)
- try:
- rsp = manager.get_access_token_by_code(code)
- except OAuthAccessTokenException as e:
- logger.warning("OAuthAccessTokenException:" + str(e))
- return HttpResponseRedirect('/')
- except Exception as e:
- logger.error(e)
- rsp = None
- nexturl = get_redirecturl(request)
- if not rsp:
- return HttpResponseRedirect(manager.get_authorization_url(nexturl))
- user = manager.get_oauth_userinfo()
- if user:
- if not user.nickname or not user.nickname.strip():
- user.nickname = "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S')
- try:
- temp = OAuthUser.objects.get(type=type, openid=user.openid)
- temp.picture = user.picture
- temp.metadata = user.metadata
- temp.nickname = user.nickname
- user = temp
- except ObjectDoesNotExist:
- pass
- # facebook的token过长
- if type == 'facebook':
- user.token = ''
- if user.email:
- with transaction.atomic():
- author = None
- try:
- author = get_user_model().objects.get(id=user.author_id)
- except ObjectDoesNotExist:
- pass
- if not author:
- result = get_user_model().objects.get_or_create(email=user.email)
- author = result[0]
- if result[1]:
- try:
- get_user_model().objects.get(username=user.nickname)
- except ObjectDoesNotExist:
- author.username = user.nickname
- else:
- author.username = "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S')
- author.source = 'authorize'
- author.save()
-
- user.author = author
- user.save()
-
- oauth_user_login_signal.send(
- sender=authorize.__class__, id=user.id)
- login(request, author)
- return HttpResponseRedirect(nexturl)
- else:
- user.save()
- url = reverse('oauth:require_email', kwargs={
- 'oauthid': user.id
- })
-
- return HttpResponseRedirect(url)
- else:
- return HttpResponseRedirect(nexturl)
-
-
-def emailconfirm(request, id, sign):
- if not sign:
- return HttpResponseForbidden()
- if not get_sha256(settings.SECRET_KEY +
- str(id) +
- settings.SECRET_KEY).upper() == sign.upper():
- return HttpResponseForbidden()
- oauthuser = get_object_or_404(OAuthUser, pk=id)
- with transaction.atomic():
- if oauthuser.author:
- author = get_user_model().objects.get(pk=oauthuser.author_id)
- else:
- result = get_user_model().objects.get_or_create(email=oauthuser.email)
- author = result[0]
- if result[1]:
- author.source = 'emailconfirm'
- author.username = oauthuser.nickname.strip() if oauthuser.nickname.strip(
- ) else "djangoblog" + timezone.now().strftime('%y%m%d%I%M%S')
- author.save()
- oauthuser.author = author
- oauthuser.save()
- oauth_user_login_signal.send(
- sender=emailconfirm.__class__,
- id=oauthuser.id)
- login(request, author)
-
- site = 'http://' + get_current_site().domain
- content = _('''
- Congratulations, you have successfully bound your email address. You can use
- %(oauthuser_type)s to directly log in to this website without a password.
- You are welcome to continue to follow this site, the address is
- %(site)s
- Thank you again!
-
- If the link above cannot be opened, please copy this link to your browser.
- %(site)s
- ''') % {'oauthuser_type': oauthuser.type, 'site': site}
-
- send_email(emailto=[oauthuser.email, ], title=_('Congratulations on your successful binding!'), content=content)
- url = reverse('oauth:bindsuccess', kwargs={
- 'oauthid': id
- })
- url = url + '?type=success'
- return HttpResponseRedirect(url)
-
-
-class RequireEmailView(FormView):
- form_class = RequireEmailForm
- template_name = 'oauth/require_email.html'
-
- def get(self, request, *args, **kwargs):
- oauthid = self.kwargs['oauthid']
- oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
- if oauthuser.email:
- pass
- # return HttpResponseRedirect('/')
-
- return super(RequireEmailView, self).get(request, *args, **kwargs)
-
- def get_initial(self):
- oauthid = self.kwargs['oauthid']
- return {
- 'email': '',
- 'oauthid': oauthid
- }
-
- def get_context_data(self, **kwargs):
- oauthid = self.kwargs['oauthid']
- oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
- if oauthuser.picture:
- kwargs['picture'] = oauthuser.picture
- return super(RequireEmailView, self).get_context_data(**kwargs)
-
- def form_valid(self, form):
- email = form.cleaned_data['email']
- oauthid = form.cleaned_data['oauthid']
- oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
- oauthuser.email = email
- oauthuser.save()
- sign = get_sha256(settings.SECRET_KEY +
- str(oauthuser.id) + settings.SECRET_KEY)
- site = get_current_site().domain
- if settings.DEBUG:
- site = '127.0.0.1:8000'
- path = reverse('oauth:email_confirm', kwargs={
- 'id': oauthid,
- 'sign': sign
- })
- url = "http://{site}{path}".format(site=site, path=path)
-
- content = _("""
- Please click the link below to bind your email
-
- %(url)s
-
- Thank you again!
-
- If the link above cannot be opened, please copy this link to your browser.
-
- %(url)s
- """) % {'url': url}
- send_email(emailto=[email, ], title=_('Bind your email'), content=content)
- url = reverse('oauth:bindsuccess', kwargs={
- 'oauthid': oauthid
- })
- url = url + '?type=email'
- return HttpResponseRedirect(url)
-
-
-def bindsuccess(request, oauthid):
- type = request.GET.get('type', None)
- oauthuser = get_object_or_404(OAuthUser, pk=oauthid)
- if type == 'email':
- title = _('Bind your email')
- content = _(
- 'Congratulations, the binding is just one step away. '
- 'Please log in to your email to check the email to complete the binding. Thank you.')
- else:
- title = _('Binding successful')
- content = _(
- "Congratulations, you have successfully bound your email address. You can use %(oauthuser_type)s"
- " to directly log in to this website without a password. You are welcome to continue to follow this site." % {
- 'oauthuser_type': oauthuser.type})
- return render(request, 'oauth/bindsuccess.html', {
- 'title': title,
- 'content': content
- })
diff --git a/owntracks/__init__.py b/owntracks/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/owntracks/admin.py b/owntracks/admin.py
deleted file mode 100644
index 655b535..0000000
--- a/owntracks/admin.py
+++ /dev/null
@@ -1,7 +0,0 @@
-from django.contrib import admin
-
-# Register your models here.
-
-
-class OwnTrackLogsAdmin(admin.ModelAdmin):
- pass
diff --git a/owntracks/apps.py b/owntracks/apps.py
deleted file mode 100644
index 1bc5f12..0000000
--- a/owntracks/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class OwntracksConfig(AppConfig):
- name = 'owntracks'
diff --git a/owntracks/migrations/0001_initial.py b/owntracks/migrations/0001_initial.py
deleted file mode 100644
index 9eee55c..0000000
--- a/owntracks/migrations/0001_initial.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Generated by Django 4.1.7 on 2023-03-02 07:14
-
-from django.db import migrations, models
-import django.utils.timezone
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='OwnTrackLog',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('tid', models.CharField(max_length=100, verbose_name='用户')),
- ('lat', models.FloatField(verbose_name='纬度')),
- ('lon', models.FloatField(verbose_name='经度')),
- ('created_time', models.DateTimeField(default=django.utils.timezone.now, verbose_name='创建时间')),
- ],
- options={
- 'verbose_name': 'OwnTrackLogs',
- 'verbose_name_plural': 'OwnTrackLogs',
- 'ordering': ['created_time'],
- 'get_latest_by': 'created_time',
- },
- ),
- ]
diff --git a/owntracks/migrations/0002_alter_owntracklog_options_and_more.py b/owntracks/migrations/0002_alter_owntracklog_options_and_more.py
deleted file mode 100644
index b4f8dec..0000000
--- a/owntracks/migrations/0002_alter_owntracklog_options_and_more.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# Generated by Django 4.2.5 on 2023-09-06 13:19
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('owntracks', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='owntracklog',
- options={'get_latest_by': 'creation_time', 'ordering': ['creation_time'], 'verbose_name': 'OwnTrackLogs', 'verbose_name_plural': 'OwnTrackLogs'},
- ),
- migrations.RenameField(
- model_name='owntracklog',
- old_name='created_time',
- new_name='creation_time',
- ),
- ]
diff --git a/owntracks/migrations/__init__.py b/owntracks/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/owntracks/models.py b/owntracks/models.py
deleted file mode 100644
index 760942c..0000000
--- a/owntracks/models.py
+++ /dev/null
@@ -1,20 +0,0 @@
-from django.db import models
-from django.utils.timezone import now
-
-
-# Create your models here.
-
-class OwnTrackLog(models.Model):
- tid = models.CharField(max_length=100, null=False, verbose_name='用户')
- lat = models.FloatField(verbose_name='纬度')
- lon = models.FloatField(verbose_name='经度')
- creation_time = models.DateTimeField('创建时间', default=now)
-
- def __str__(self):
- return self.tid
-
- class Meta:
- ordering = ['creation_time']
- verbose_name = "OwnTrackLogs"
- verbose_name_plural = verbose_name
- get_latest_by = 'creation_time'
diff --git a/owntracks/tests.py b/owntracks/tests.py
deleted file mode 100644
index 3b4b9d8..0000000
--- a/owntracks/tests.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import json
-
-from django.test import Client, RequestFactory, TestCase
-
-from accounts.models import BlogUser
-from .models import OwnTrackLog
-
-
-# Create your tests here.
-
-class OwnTrackLogTest(TestCase):
- def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
-
- def test_own_track_log(self):
- o = {
- 'tid': 12,
- 'lat': 123.123,
- 'lon': 134.341
- }
-
- self.client.post(
- '/owntracks/logtracks',
- json.dumps(o),
- content_type='application/json')
- length = len(OwnTrackLog.objects.all())
- self.assertEqual(length, 1)
-
- o = {
- 'tid': 12,
- 'lat': 123.123
- }
-
- self.client.post(
- '/owntracks/logtracks',
- json.dumps(o),
- content_type='application/json')
- length = len(OwnTrackLog.objects.all())
- self.assertEqual(length, 1)
-
- rsp = self.client.get('/owntracks/show_maps')
- self.assertEqual(rsp.status_code, 302)
-
- user = BlogUser.objects.create_superuser(
- email="liangliangyy1@gmail.com",
- username="liangliangyy1",
- password="liangliangyy1")
-
- self.client.login(username='liangliangyy1', password='liangliangyy1')
- s = OwnTrackLog()
- s.tid = 12
- s.lon = 123.234
- s.lat = 34.234
- s.save()
-
- rsp = self.client.get('/owntracks/show_dates')
- self.assertEqual(rsp.status_code, 200)
- rsp = self.client.get('/owntracks/show_maps')
- self.assertEqual(rsp.status_code, 200)
- rsp = self.client.get('/owntracks/get_datas')
- self.assertEqual(rsp.status_code, 200)
- rsp = self.client.get('/owntracks/get_datas?date=2018-02-26')
- self.assertEqual(rsp.status_code, 200)
diff --git a/owntracks/urls.py b/owntracks/urls.py
deleted file mode 100644
index c19ada8..0000000
--- a/owntracks/urls.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from django.urls import path
-
-from . import views
-
-app_name = "owntracks"
-
-urlpatterns = [
- path('owntracks/logtracks', views.manage_owntrack_log, name='logtracks'),
- path('owntracks/show_maps', views.show_maps, name='show_maps'),
- path('owntracks/get_datas', views.get_datas, name='get_datas'),
- path('owntracks/show_dates', views.show_log_dates, name='show_dates')
-]
diff --git a/owntracks/views.py b/owntracks/views.py
deleted file mode 100644
index 4c72bdd..0000000
--- a/owntracks/views.py
+++ /dev/null
@@ -1,127 +0,0 @@
-# Create your views here.
-import datetime
-import itertools
-import json
-import logging
-from datetime import timezone
-from itertools import groupby
-
-import django
-import requests
-from django.contrib.auth.decorators import login_required
-from django.http import HttpResponse
-from django.http import JsonResponse
-from django.shortcuts import render
-from django.views.decorators.csrf import csrf_exempt
-
-from .models import OwnTrackLog
-
-logger = logging.getLogger(__name__)
-
-
-@csrf_exempt
-def manage_owntrack_log(request):
- try:
- s = json.loads(request.read().decode('utf-8'))
- tid = s['tid']
- lat = s['lat']
- lon = s['lon']
-
- logger.info(
- 'tid:{tid}.lat:{lat}.lon:{lon}'.format(
- tid=tid, lat=lat, lon=lon))
- if tid and lat and lon:
- m = OwnTrackLog()
- m.tid = tid
- m.lat = lat
- m.lon = lon
- m.save()
- return HttpResponse('ok')
- else:
- return HttpResponse('data error')
- except Exception as e:
- logger.error(e)
- return HttpResponse('error')
-
-
-@login_required
-def show_maps(request):
- if request.user.is_superuser:
- defaultdate = str(datetime.datetime.now(timezone.utc).date())
- date = request.GET.get('date', defaultdate)
- context = {
- 'date': date
- }
- return render(request, 'owntracks/show_maps.html', context)
- else:
- from django.http import HttpResponseForbidden
- return HttpResponseForbidden()
-
-
-@login_required
-def show_log_dates(request):
- dates = OwnTrackLog.objects.values_list('creation_time', flat=True)
- results = list(sorted(set(map(lambda x: x.strftime('%Y-%m-%d'), dates))))
-
- context = {
- 'results': results
- }
- return render(request, 'owntracks/show_log_dates.html', context)
-
-
-def convert_to_amap(locations):
- convert_result = []
- it = iter(locations)
-
- item = list(itertools.islice(it, 30))
- while item:
- datas = ';'.join(
- set(map(lambda x: str(x.lon) + ',' + str(x.lat), item)))
-
- key = '8440a376dfc9743d8924bf0ad141f28e'
- api = 'http://restapi.amap.com/v3/assistant/coordinate/convert'
- query = {
- 'key': key,
- 'locations': datas,
- 'coordsys': 'gps'
- }
- rsp = requests.get(url=api, params=query)
- result = json.loads(rsp.text)
- if "locations" in result:
- convert_result.append(result['locations'])
- item = list(itertools.islice(it, 30))
-
- return ";".join(convert_result)
-
-
-@login_required
-def get_datas(request):
- now = django.utils.timezone.now().replace(tzinfo=timezone.utc)
- querydate = django.utils.timezone.datetime(
- now.year, now.month, now.day, 0, 0, 0)
- if request.GET.get('date', None):
- date = list(map(lambda x: int(x), request.GET.get('date').split('-')))
- querydate = django.utils.timezone.datetime(
- date[0], date[1], date[2], 0, 0, 0)
- nextdate = querydate + datetime.timedelta(days=1)
- models = OwnTrackLog.objects.filter(
- creation_time__range=(querydate, nextdate))
- result = list()
- if models and len(models):
- for tid, item in groupby(
- sorted(models, key=lambda k: k.tid), key=lambda k: k.tid):
-
- d = dict()
- d["name"] = tid
- paths = list()
- # 使用高德转换后的经纬度
- # locations = convert_to_amap(
- # sorted(item, key=lambda x: x.creation_time))
- # for i in locations.split(';'):
- # paths.append(i.split(','))
- # 使用GPS原始经纬度
- for location in sorted(item, key=lambda x: x.creation_time):
- paths.append([str(location.lon), str(location.lat)])
- d["path"] = paths
- result.append(d)
- return JsonResponse(result, safe=False)
diff --git a/plugins/__init__.py b/plugins/__init__.py
deleted file mode 100644
index e88afca..0000000
--- a/plugins/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# This file makes this a Python package
diff --git a/plugins/article_copyright/__init__.py b/plugins/article_copyright/__init__.py
deleted file mode 100644
index e88afca..0000000
--- a/plugins/article_copyright/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# This file makes this a Python package
diff --git a/plugins/article_copyright/plugin.py b/plugins/article_copyright/plugin.py
deleted file mode 100644
index 317fed2..0000000
--- a/plugins/article_copyright/plugin.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from djangoblog.plugin_manage.base_plugin import BasePlugin
-from djangoblog.plugin_manage import hooks
-from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME
-
-
-class ArticleCopyrightPlugin(BasePlugin):
- PLUGIN_NAME = '文章结尾版权声明'
- PLUGIN_DESCRIPTION = '一个在文章正文末尾添加版权声明的插件。'
- PLUGIN_VERSION = '0.2.0'
- PLUGIN_AUTHOR = 'liangliangyy'
-
- # 2. 实现 register_hooks 方法,专门用于注册钩子
- def register_hooks(self):
- # 在这里将插件的方法注册到指定的钩子上
- hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.add_copyright_to_content)
-
- def add_copyright_to_content(self, content, *args, **kwargs):
- """
- 这个方法会被注册到 'the_content' 过滤器钩子上。
- 它接收原始内容,并返回添加了版权信息的新内容。
- """
- article = kwargs.get('article')
- if not article:
- return content
-
- copyright_info = f"\n本文由 {article.author.username} 原创,转载请注明出处。
"
- return content + copyright_info
-
-
-# 3. 实例化插件。
-# 这会自动调用 BasePlugin.__init__,然后 BasePlugin.__init__ 会调用我们上面定义的 register_hooks 方法。
-plugin = ArticleCopyrightPlugin()
diff --git a/plugins/external_links/__init__.py b/plugins/external_links/__init__.py
deleted file mode 100644
index e88afca..0000000
--- a/plugins/external_links/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# This file makes this a Python package
diff --git a/plugins/external_links/plugin.py b/plugins/external_links/plugin.py
deleted file mode 100644
index 5b2ef14..0000000
--- a/plugins/external_links/plugin.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import re
-from urllib.parse import urlparse
-from djangoblog.plugin_manage.base_plugin import BasePlugin
-from djangoblog.plugin_manage import hooks
-from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME
-
-
-class ExternalLinksPlugin(BasePlugin):
- PLUGIN_NAME = '外部链接处理器'
- PLUGIN_DESCRIPTION = '自动为文章中的外部链接添加 target="_blank" 和 rel="noopener noreferrer" 属性。'
- PLUGIN_VERSION = '0.1.0'
- PLUGIN_AUTHOR = 'liangliangyy'
-
- def register_hooks(self):
- hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.process_external_links)
-
- def process_external_links(self, content, *args, **kwargs):
- from djangoblog.utils import get_current_site
- site_domain = get_current_site().domain
-
- # 正则表达式查找所有 标签
- link_pattern = re.compile(r'( ]*?\s+)?href=")([^"]*)(".*?/a>)', re.IGNORECASE)
-
- def replacer(match):
- # match.group(1) 是 ...
- href = match.group(2)
-
- # 如果链接已经有 target 属性,则不处理
- if 'target=' in match.group(0).lower():
- return match.group(0)
-
- # 解析链接
- parsed_url = urlparse(href)
-
- # 如果链接是外部的 (有域名且域名不等于当前网站域名)
- if parsed_url.netloc and parsed_url.netloc != site_domain:
- # 添加 target 和 rel 属性
- return f'{match.group(1)}{href}" target="_blank" rel="noopener noreferrer"{match.group(3)}'
-
- # 否则返回原样
- return match.group(0)
-
- return link_pattern.sub(replacer, content)
-
-
-plugin = ExternalLinksPlugin()
diff --git a/plugins/reading_time/__init__.py b/plugins/reading_time/__init__.py
deleted file mode 100644
index e88afca..0000000
--- a/plugins/reading_time/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# This file makes this a Python package
diff --git a/plugins/reading_time/plugin.py b/plugins/reading_time/plugin.py
deleted file mode 100644
index 35f9db1..0000000
--- a/plugins/reading_time/plugin.py
+++ /dev/null
@@ -1,43 +0,0 @@
-import math
-import re
-from djangoblog.plugin_manage.base_plugin import BasePlugin
-from djangoblog.plugin_manage import hooks
-from djangoblog.plugin_manage.hook_constants import ARTICLE_CONTENT_HOOK_NAME
-
-
-class ReadingTimePlugin(BasePlugin):
- PLUGIN_NAME = '阅读时间预测'
- PLUGIN_DESCRIPTION = '估算文章阅读时间并显示在文章开头。'
- PLUGIN_VERSION = '0.1.0'
- PLUGIN_AUTHOR = 'liangliangyy'
-
- def register_hooks(self):
- hooks.register(ARTICLE_CONTENT_HOOK_NAME, self.add_reading_time)
-
- def add_reading_time(self, content, *args, **kwargs):
- """
- 计算阅读时间并添加到内容开头。
- """
- # 移除HTML标签和空白字符,以获得纯文本
- clean_content = re.sub(r'<[^>]*>', '', content)
- clean_content = clean_content.strip()
-
- # 中文和英文单词混合计数的一个简单方法
- # 匹配中文字符或连续的非中文字符(视为单词)
- words = re.findall(r'[\u4e00-\u9fa5]|\w+', clean_content)
- word_count = len(words)
-
- # 按平均每分钟200字的速度计算
- reading_speed = 200
- reading_minutes = math.ceil(word_count / reading_speed)
-
- # 如果阅读时间少于1分钟,则显示为1分钟
- if reading_minutes < 1:
- reading_minutes = 1
-
- reading_time_html = f'预计阅读时间:{reading_minutes} 分钟
'
-
- return reading_time_html + content
-
-
-plugin = ReadingTimePlugin()
\ No newline at end of file
diff --git a/plugins/seo_optimizer/__init__.py b/plugins/seo_optimizer/__init__.py
deleted file mode 100644
index e88afca..0000000
--- a/plugins/seo_optimizer/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# This file makes this a Python package
diff --git a/plugins/seo_optimizer/plugin.py b/plugins/seo_optimizer/plugin.py
deleted file mode 100644
index b5b19a3..0000000
--- a/plugins/seo_optimizer/plugin.py
+++ /dev/null
@@ -1,142 +0,0 @@
-import json
-from django.utils.html import strip_tags
-from django.template.defaultfilters import truncatewords
-from djangoblog.plugin_manage.base_plugin import BasePlugin
-from djangoblog.plugin_manage import hooks
-from blog.models import Article, Category, Tag
-from djangoblog.utils import get_blog_setting
-
-
-class SeoOptimizerPlugin(BasePlugin):
- PLUGIN_NAME = 'SEO 优化器'
- PLUGIN_DESCRIPTION = '为文章、页面等提供 SEO 优化,动态生成 meta 标签和 JSON-LD 结构化数据。'
- PLUGIN_VERSION = '0.2.0'
- PLUGIN_AUTHOR = 'liuangliangyy'
-
- def register_hooks(self):
- hooks.register('head_meta', self.dispatch_seo_generation)
-
- def _get_article_seo_data(self, context, request, blog_setting):
- article = context.get('article')
- if not isinstance(article, Article):
- return None
-
- description = strip_tags(article.body)[:150]
- keywords = ",".join([tag.name for tag in article.tags.all()]) or blog_setting.site_keywords
-
- meta_tags = f'''
-
-
-
-
-
-
-
-
- '''
- for tag in article.tags.all():
- meta_tags += f' '
- meta_tags += f' '
-
- structured_data = {
- "@context": "https://schema.org",
- "@type": "Article",
- "mainEntityOfPage": {"@type": "WebPage", "@id": request.build_absolute_uri()},
- "headline": article.title,
- "description": description,
- "image": request.build_absolute_uri(article.get_first_image_url()),
- "datePublished": article.pub_time.isoformat(),
- "dateModified": article.last_modify_time.isoformat(),
- "author": {"@type": "Person", "name": article.author.username},
- "publisher": {"@type": "Organization", "name": blog_setting.site_name}
- }
- if not structured_data.get("image"):
- del structured_data["image"]
-
- return {
- "title": f"{article.title} | {blog_setting.site_name}",
- "description": description,
- "keywords": keywords,
- "meta_tags": meta_tags,
- "json_ld": structured_data
- }
-
- def _get_category_seo_data(self, context, request, blog_setting):
- category_name = context.get('tag_name')
- if not category_name:
- return None
-
- category = Category.objects.filter(name=category_name).first()
- if not category:
- return None
-
- title = f"{category.name} | {blog_setting.site_name}"
- description = strip_tags(category.name) or blog_setting.site_description
- keywords = category.name
-
- # BreadcrumbList structured data for category page
- breadcrumb_items = [{"@type": "ListItem", "position": 1, "name": "首页", "item": request.build_absolute_uri('/')}]
- breadcrumb_items.append({"@type": "ListItem", "position": 2, "name": category.name, "item": request.build_absolute_uri()})
-
- structured_data = {
- "@context": "https://schema.org",
- "@type": "BreadcrumbList",
- "itemListElement": breadcrumb_items
- }
-
- return {
- "title": title,
- "description": description,
- "keywords": keywords,
- "meta_tags": "",
- "json_ld": structured_data
- }
-
- def _get_default_seo_data(self, context, request, blog_setting):
- # Homepage and other default pages
- structured_data = {
- "@context": "https://schema.org",
- "@type": "WebSite",
- "url": request.build_absolute_uri('/'),
- "potentialAction": {
- "@type": "SearchAction",
- "target": f"{request.build_absolute_uri('/search/')}?q={{search_term_string}}",
- "query-input": "required name=search_term_string"
- }
- }
- return {
- "title": f"{blog_setting.site_name} | {blog_setting.site_description}",
- "description": blog_setting.site_description,
- "keywords": blog_setting.site_keywords,
- "meta_tags": "",
- "json_ld": structured_data
- }
-
- def dispatch_seo_generation(self, metas, context):
- request = context.get('request')
- if not request:
- return metas
-
- view_name = request.resolver_match.view_name
- blog_setting = get_blog_setting()
-
- seo_data = None
- if view_name == 'blog:detailbyid':
- seo_data = self._get_article_seo_data(context, request, blog_setting)
- elif view_name == 'blog:category_detail':
- seo_data = self._get_category_seo_data(context, request, blog_setting)
-
- if not seo_data:
- seo_data = self._get_default_seo_data(context, request, blog_setting)
-
- json_ld_script = f''
-
- return f"""
- {seo_data.get("title", "")}
-
-
- {seo_data.get("meta_tags", "")}
- {json_ld_script}
- """
-
-plugin = SeoOptimizerPlugin()
diff --git a/plugins/view_count/__init__.py b/plugins/view_count/__init__.py
deleted file mode 100644
index 8804fdf..0000000
--- a/plugins/view_count/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-# This file makes this a Python package
\ No newline at end of file
diff --git a/plugins/view_count/plugin.py b/plugins/view_count/plugin.py
deleted file mode 100644
index 15e9d94..0000000
--- a/plugins/view_count/plugin.py
+++ /dev/null
@@ -1,18 +0,0 @@
-from djangoblog.plugin_manage.base_plugin import BasePlugin
-from djangoblog.plugin_manage import hooks
-
-
-class ViewCountPlugin(BasePlugin):
- PLUGIN_NAME = '文章浏览次数统计'
- PLUGIN_DESCRIPTION = '统计文章的浏览次数'
- PLUGIN_VERSION = '0.1.0'
- PLUGIN_AUTHOR = 'liangliangyy'
-
- def register_hooks(self):
- hooks.register('after_article_body_get', self.record_view)
-
- def record_view(self, article, *args, **kwargs):
- article.viewed()
-
-
-plugin = ViewCountPlugin()
\ No newline at end of file
diff --git a/servermanager/MemcacheStorage.py b/servermanager/MemcacheStorage.py
deleted file mode 100644
index 38a7990..0000000
--- a/servermanager/MemcacheStorage.py
+++ /dev/null
@@ -1,32 +0,0 @@
-from werobot.session import SessionStorage
-from werobot.utils import json_loads, json_dumps
-
-from djangoblog.utils import cache
-
-
-class MemcacheStorage(SessionStorage):
- def __init__(self, prefix='ws_'):
- self.prefix = prefix
- self.cache = cache
-
- @property
- def is_available(self):
- value = "1"
- self.set('checkavaliable', value=value)
- return value == self.get('checkavaliable')
-
- def key_name(self, s):
- return '{prefix}{s}'.format(prefix=self.prefix, s=s)
-
- def get(self, id):
- id = self.key_name(id)
- session_json = self.cache.get(id) or '{}'
- return json_loads(session_json)
-
- def set(self, id, value):
- id = self.key_name(id)
- self.cache.set(id, json_dumps(value))
-
- def delete(self, id):
- id = self.key_name(id)
- self.cache.delete(id)
diff --git a/servermanager/__init__.py b/servermanager/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/servermanager/admin.py b/servermanager/admin.py
deleted file mode 100644
index f26f4f6..0000000
--- a/servermanager/admin.py
+++ /dev/null
@@ -1,19 +0,0 @@
-from django.contrib import admin
-# Register your models here.
-
-
-class CommandsAdmin(admin.ModelAdmin):
- list_display = ('title', 'command', 'describe')
-
-
-class EmailSendLogAdmin(admin.ModelAdmin):
- list_display = ('title', 'emailto', 'send_result', 'creation_time')
- readonly_fields = (
- 'title',
- 'emailto',
- 'send_result',
- 'creation_time',
- 'content')
-
- def has_add_permission(self, request):
- return False
diff --git a/servermanager/api/__init__.py b/servermanager/api/__init__.py
deleted file mode 100644
index 8b13789..0000000
--- a/servermanager/api/__init__.py
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/servermanager/api/blogapi.py b/servermanager/api/blogapi.py
deleted file mode 100644
index 8a4d6ac..0000000
--- a/servermanager/api/blogapi.py
+++ /dev/null
@@ -1,27 +0,0 @@
-from haystack.query import SearchQuerySet
-
-from blog.models import Article, Category
-
-
-class BlogApi:
- def __init__(self):
- self.searchqueryset = SearchQuerySet()
- self.searchqueryset.auto_query('')
- self.__max_takecount__ = 8
-
- def search_articles(self, query):
- sqs = self.searchqueryset.auto_query(query)
- sqs = sqs.load_all()
- return sqs[:self.__max_takecount__]
-
- def get_category_lists(self):
- return Category.objects.all()
-
- def get_category_articles(self, categoryname):
- articles = Article.objects.filter(category__name=categoryname)
- if articles:
- return articles[:self.__max_takecount__]
- return None
-
- def get_recent_articles(self):
- return Article.objects.all()[:self.__max_takecount__]
diff --git a/servermanager/api/commonapi.py b/servermanager/api/commonapi.py
deleted file mode 100644
index 83ad9ff..0000000
--- a/servermanager/api/commonapi.py
+++ /dev/null
@@ -1,64 +0,0 @@
-import logging
-import os
-
-import openai
-
-from servermanager.models import commands
-
-logger = logging.getLogger(__name__)
-
-openai.api_key = os.environ.get('OPENAI_API_KEY')
-if os.environ.get('HTTP_PROXY'):
- openai.proxy = os.environ.get('HTTP_PROXY')
-
-
-class ChatGPT:
-
- @staticmethod
- def chat(prompt):
- try:
- completion = openai.ChatCompletion.create(model="gpt-3.5-turbo",
- messages=[{"role": "user", "content": prompt}])
- return completion.choices[0].message.content
- except Exception as e:
- logger.error(e)
- return "服务器出错了"
-
-
-class CommandHandler:
- def __init__(self):
- self.commands = commands.objects.all()
-
- def run(self, title):
- """
- 运行命令
- :param title: 命令
- :return: 返回命令执行结果
- """
- cmd = list(
- filter(
- lambda x: x.title.upper() == title.upper(),
- self.commands))
- if cmd:
- return self.__run_command__(cmd[0].command)
- else:
- return "未找到相关命令,请输入hepme获得帮助。"
-
- def __run_command__(self, cmd):
- try:
- res = os.popen(cmd).read()
- return res
- except BaseException:
- return '命令执行出错!'
-
- def get_help(self):
- rsp = ''
- for cmd in self.commands:
- rsp += '{c}:{d}\n'.format(c=cmd.title, d=cmd.describe)
- return rsp
-
-
-if __name__ == '__main__':
- chatbot = ChatGPT()
- prompt = "写一篇1000字关于AI的论文"
- print(chatbot.chat(prompt))
diff --git a/servermanager/apps.py b/servermanager/apps.py
deleted file mode 100644
index 03cc38d..0000000
--- a/servermanager/apps.py
+++ /dev/null
@@ -1,5 +0,0 @@
-from django.apps import AppConfig
-
-
-class ServermanagerConfig(AppConfig):
- name = 'servermanager'
diff --git a/servermanager/migrations/0001_initial.py b/servermanager/migrations/0001_initial.py
deleted file mode 100644
index bbdbf77..0000000
--- a/servermanager/migrations/0001_initial.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Generated by Django 4.1.7 on 2023-03-02 07:14
-
-from django.db import migrations, models
-
-
-class Migration(migrations.Migration):
-
- initial = True
-
- dependencies = [
- ]
-
- operations = [
- migrations.CreateModel(
- name='commands',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('title', models.CharField(max_length=300, verbose_name='命令标题')),
- ('command', models.CharField(max_length=2000, verbose_name='命令')),
- ('describe', models.CharField(max_length=300, verbose_name='命令描述')),
- ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
- ('last_mod_time', models.DateTimeField(auto_now=True, verbose_name='修改时间')),
- ],
- options={
- 'verbose_name': '命令',
- 'verbose_name_plural': '命令',
- },
- ),
- migrations.CreateModel(
- name='EmailSendLog',
- fields=[
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
- ('emailto', models.CharField(max_length=300, verbose_name='收件人')),
- ('title', models.CharField(max_length=2000, verbose_name='邮件标题')),
- ('content', models.TextField(verbose_name='邮件内容')),
- ('send_result', models.BooleanField(default=False, verbose_name='结果')),
- ('created_time', models.DateTimeField(auto_now_add=True, verbose_name='创建时间')),
- ],
- options={
- 'verbose_name': '邮件发送log',
- 'verbose_name_plural': '邮件发送log',
- 'ordering': ['-created_time'],
- },
- ),
- ]
diff --git a/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py b/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py
deleted file mode 100644
index 4858857..0000000
--- a/servermanager/migrations/0002_alter_emailsendlog_options_and_more.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Generated by Django 4.2.5 on 2023-09-06 13:19
-
-from django.db import migrations
-
-
-class Migration(migrations.Migration):
-
- dependencies = [
- ('servermanager', '0001_initial'),
- ]
-
- operations = [
- migrations.AlterModelOptions(
- name='emailsendlog',
- options={'ordering': ['-creation_time'], 'verbose_name': '邮件发送log', 'verbose_name_plural': '邮件发送log'},
- ),
- migrations.RenameField(
- model_name='commands',
- old_name='created_time',
- new_name='creation_time',
- ),
- migrations.RenameField(
- model_name='commands',
- old_name='last_mod_time',
- new_name='last_modify_time',
- ),
- migrations.RenameField(
- model_name='emailsendlog',
- old_name='created_time',
- new_name='creation_time',
- ),
- ]
diff --git a/servermanager/migrations/__init__.py b/servermanager/migrations/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/servermanager/models.py b/servermanager/models.py
deleted file mode 100644
index 4326c65..0000000
--- a/servermanager/models.py
+++ /dev/null
@@ -1,33 +0,0 @@
-from django.db import models
-
-
-# Create your models here.
-class commands(models.Model):
- title = models.CharField('命令标题', max_length=300)
- command = models.CharField('命令', max_length=2000)
- describe = models.CharField('命令描述', max_length=300)
- creation_time = models.DateTimeField('创建时间', auto_now_add=True)
- last_modify_time = models.DateTimeField('修改时间', auto_now=True)
-
- def __str__(self):
- return self.title
-
- class Meta:
- verbose_name = '命令'
- verbose_name_plural = verbose_name
-
-
-class EmailSendLog(models.Model):
- emailto = models.CharField('收件人', max_length=300)
- title = models.CharField('邮件标题', max_length=2000)
- content = models.TextField('邮件内容')
- send_result = models.BooleanField('结果', default=False)
- creation_time = models.DateTimeField('创建时间', auto_now_add=True)
-
- def __str__(self):
- return self.title
-
- class Meta:
- verbose_name = '邮件发送log'
- verbose_name_plural = verbose_name
- ordering = ['-creation_time']
diff --git a/servermanager/robot.py b/servermanager/robot.py
deleted file mode 100644
index 7b45736..0000000
--- a/servermanager/robot.py
+++ /dev/null
@@ -1,187 +0,0 @@
-import logging
-import os
-import re
-
-import jsonpickle
-from django.conf import settings
-from werobot import WeRoBot
-from werobot.replies import ArticlesReply, Article
-from werobot.session.filestorage import FileStorage
-
-from djangoblog.utils import get_sha256
-from servermanager.api.blogapi import BlogApi
-from servermanager.api.commonapi import ChatGPT, CommandHandler
-from .MemcacheStorage import MemcacheStorage
-
-robot = WeRoBot(token=os.environ.get('DJANGO_WEROBOT_TOKEN')
- or 'lylinux', enable_session=True)
-memstorage = MemcacheStorage()
-if memstorage.is_available:
- robot.config['SESSION_STORAGE'] = memstorage
-else:
- if os.path.exists(os.path.join(settings.BASE_DIR, 'werobot_session')):
- os.remove(os.path.join(settings.BASE_DIR, 'werobot_session'))
- robot.config['SESSION_STORAGE'] = FileStorage(filename='werobot_session')
-
-blogapi = BlogApi()
-cmd_handler = CommandHandler()
-logger = logging.getLogger(__name__)
-
-
-def convert_to_article_reply(articles, message):
- reply = ArticlesReply(message=message)
- from blog.templatetags.blog_tags import truncatechars_content
- for post in articles:
- imgs = re.findall(r'(?:http\:|https\:)?\/\/.*\.(?:png|jpg)', post.body)
- imgurl = ''
- if imgs:
- imgurl = imgs[0]
- article = Article(
- title=post.title,
- description=truncatechars_content(post.body),
- img=imgurl,
- url=post.get_full_url()
- )
- reply.add_article(article)
- return reply
-
-
-@robot.filter(re.compile(r"^\?.*"))
-def search(message, session):
- s = message.content
- searchstr = str(s).replace('?', '')
- result = blogapi.search_articles(searchstr)
- if result:
- articles = list(map(lambda x: x.object, result))
- reply = convert_to_article_reply(articles, message)
- return reply
- else:
- return '没有找到相关文章。'
-
-
-@robot.filter(re.compile(r'^category\s*$', re.I))
-def category(message, session):
- categorys = blogapi.get_category_lists()
- content = ','.join(map(lambda x: x.name, categorys))
- return '所有文章分类目录:' + content
-
-
-@robot.filter(re.compile(r'^recent\s*$', re.I))
-def recents(message, session):
- articles = blogapi.get_recent_articles()
- if articles:
- reply = convert_to_article_reply(articles, message)
- return reply
- else:
- return "暂时还没有文章"
-
-
-@robot.filter(re.compile('^help$', re.I))
-def help(message, session):
- return '''欢迎关注!
- 默认会与图灵机器人聊天~~
- 你可以通过下面这些命令来获得信息
- ?关键字搜索文章.
- 如?python.
- category获得文章分类目录及文章数.
- category-***获得该分类目录文章
- 如category-python
- recent获得最新文章
- help获得帮助.
- weather:获得天气
- 如weather:西安
- idcard:获得身份证信息
- 如idcard:61048119xxxxxxxxxx
- music:音乐搜索
- 如music:阴天快乐
- PS:以上标点符号都不支持中文标点~~
- '''
-
-
-@robot.filter(re.compile(r'^weather\:.*$', re.I))
-def weather(message, session):
- return "建设中..."
-
-
-@robot.filter(re.compile(r'^idcard\:.*$', re.I))
-def idcard(message, session):
- return "建设中..."
-
-
-@robot.handler
-def echo(message, session):
- handler = MessageHandler(message, session)
- return handler.handler()
-
-
-class MessageHandler:
- def __init__(self, message, session):
- userid = message.source
- self.message = message
- self.session = session
- self.userid = userid
- try:
- info = session[userid]
- self.userinfo = jsonpickle.decode(info)
- except Exception as e:
- userinfo = WxUserInfo()
- self.userinfo = userinfo
-
- @property
- def is_admin(self):
- return self.userinfo.isAdmin
-
- @property
- def is_password_set(self):
- return self.userinfo.isPasswordSet
-
- def save_session(self):
- info = jsonpickle.encode(self.userinfo)
- self.session[self.userid] = info
-
- def handler(self):
- info = self.message.content
-
- if self.userinfo.isAdmin and info.upper() == 'EXIT':
- self.userinfo = WxUserInfo()
- self.save_session()
- return "退出成功"
- if info.upper() == 'ADMIN':
- self.userinfo.isAdmin = True
- self.save_session()
- return "输入管理员密码"
- if self.userinfo.isAdmin and not self.userinfo.isPasswordSet:
- passwd = settings.WXADMIN
- if settings.TESTING:
- passwd = '123'
- if passwd.upper() == get_sha256(get_sha256(info)).upper():
- self.userinfo.isPasswordSet = True
- self.save_session()
- return "验证通过,请输入命令或者要执行的命令代码:输入helpme获得帮助"
- else:
- if self.userinfo.Count >= 3:
- self.userinfo = WxUserInfo()
- self.save_session()
- return "超过验证次数"
- self.userinfo.Count += 1
- self.save_session()
- return "验证失败,请重新输入管理员密码:"
- if self.userinfo.isAdmin and self.userinfo.isPasswordSet:
- if self.userinfo.Command != '' and info.upper() == 'Y':
- return cmd_handler.run(self.userinfo.Command)
- else:
- if info.upper() == 'HELPME':
- return cmd_handler.get_help()
- self.userinfo.Command = info
- self.save_session()
- return "确认执行: " + info + " 命令?"
-
- return ChatGPT.chat(info)
-
-
-class WxUserInfo():
- def __init__(self):
- self.isAdmin = False
- self.isPasswordSet = False
- self.Count = 0
- self.Command = ''
diff --git a/servermanager/tests.py b/servermanager/tests.py
deleted file mode 100644
index 22a6689..0000000
--- a/servermanager/tests.py
+++ /dev/null
@@ -1,79 +0,0 @@
-from django.test import Client, RequestFactory, TestCase
-from django.utils import timezone
-from werobot.messages.messages import TextMessage
-
-from accounts.models import BlogUser
-from blog.models import Category, Article
-from servermanager.api.commonapi import ChatGPT
-from .models import commands
-from .robot import MessageHandler, CommandHandler
-from .robot import search, category, recents
-
-
-# Create your tests here.
-class ServerManagerTest(TestCase):
- def setUp(self):
- self.client = Client()
- self.factory = RequestFactory()
-
- def test_chat_gpt(self):
- content = ChatGPT.chat("你好")
- self.assertIsNotNone(content)
-
- def test_validate_comment(self):
- user = BlogUser.objects.create_superuser(
- email="liangliangyy1@gmail.com",
- username="liangliangyy1",
- password="liangliangyy1")
-
- self.client.login(username='liangliangyy1', password='liangliangyy1')
-
- c = Category()
- c.name = "categoryccc"
- c.save()
-
- article = Article()
- article.title = "nicetitleccc"
- article.body = "nicecontentccc"
- article.author = user
- article.category = c
- article.type = 'a'
- article.status = 'p'
- article.save()
- s = TextMessage([])
- s.content = "nice"
- rsp = search(s, None)
- rsp = category(None, None)
- self.assertIsNotNone(rsp)
- rsp = recents(None, None)
- self.assertTrue(rsp != '暂时还没有文章')
-
- cmd = commands()
- cmd.title = "test"
- cmd.command = "ls"
- cmd.describe = "test"
- cmd.save()
-
- cmdhandler = CommandHandler()
- rsp = cmdhandler.run('test')
- self.assertIsNotNone(rsp)
- s.source = 'u'
- s.content = 'test'
- msghandler = MessageHandler(s, {})
-
- # msghandler.userinfo.isPasswordSet = True
- # msghandler.userinfo.isAdmin = True
- msghandler.handler()
- s.content = 'y'
- msghandler.handler()
- s.content = 'idcard:12321233'
- msghandler.handler()
- s.content = 'weather:上海'
- msghandler.handler()
- s.content = 'admin'
- msghandler.handler()
- s.content = '123'
- msghandler.handler()
-
- s.content = 'exit'
- msghandler.handler()
diff --git a/servermanager/urls.py b/servermanager/urls.py
deleted file mode 100644
index 8d134d2..0000000
--- a/servermanager/urls.py
+++ /dev/null
@@ -1,10 +0,0 @@
-from django.urls import path
-from werobot.contrib.django import make_view
-
-from .robot import robot
-
-app_name = "servermanager"
-urlpatterns = [
- path(r'robot', make_view(robot)),
-
-]
diff --git a/servermanager/views.py b/servermanager/views.py
deleted file mode 100644
index 60f00ef..0000000
--- a/servermanager/views.py
+++ /dev/null
@@ -1 +0,0 @@
-# Create your views here.
diff --git a/t.py b/t.py
deleted file mode 100644
index e69de29..0000000
diff --git a/templates/account/forget_password.html b/templates/account/forget_password.html
deleted file mode 100644
index 3384531..0000000
--- a/templates/account/forget_password.html
+++ /dev/null
@@ -1,30 +0,0 @@
-{% extends 'share_layout/base_account.html' %}
-{% load i18n %}
-{% load static %}
-{% block content %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/templates/account/login.html b/templates/account/login.html
deleted file mode 100644
index cff8d33..0000000
--- a/templates/account/login.html
+++ /dev/null
@@ -1,46 +0,0 @@
-{% extends 'share_layout/base_account.html' %}
-{% load static %}
-{% load i18n %}
-{% block content %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/templates/account/registration_form.html b/templates/account/registration_form.html
deleted file mode 100644
index 65e7549..0000000
--- a/templates/account/registration_form.html
+++ /dev/null
@@ -1,29 +0,0 @@
-{% extends 'share_layout/base_account.html' %}
-{% load static %}
-{% block content %}
-
-
-
Create Your Account
-
-
-
-
-
-
-
- Sign In
-
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/templates/account/result.html b/templates/account/result.html
deleted file mode 100644
index 23c9094..0000000
--- a/templates/account/result.html
+++ /dev/null
@@ -1,27 +0,0 @@
-{% extends 'share_layout/base.html' %}
-{% load i18n %}
-{% block header %}
- {{ title }}
-{% endblock %}
-{% block content %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/templates/blog/article_archives.html b/templates/blog/article_archives.html
deleted file mode 100644
index 959319e..0000000
--- a/templates/blog/article_archives.html
+++ /dev/null
@@ -1,60 +0,0 @@
-{% extends 'share_layout/base.html' %}
-{% load blog_tags %}
-{% load cache %}
-{% load i18n %}
-{% block header %}
-
- {% trans 'article archive' %} | {{ SITE_DESCRIPTION }}
-
-
-
-
-
-
-
-
-
-{% endblock %}
-{% block content %}
-
-
-
-
-
-
-
- {% 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/templates/blog/article_detail.html b/templates/blog/article_detail.html
deleted file mode 100755
index a74a0db..0000000
--- a/templates/blog/article_detail.html
+++ /dev/null
@@ -1,52 +0,0 @@
-{% extends 'share_layout/base.html' %}
-{% load blog_tags %}
-
-{% block header %}
-{% endblock %}
-{% block content %}
-
-
- {% load_article_detail article False user %}
-
- {% if article.type == 'a' %}
-
- 文章导航
- {% if next_article %}
-
- ← {{ next_article.title }}
- {% endif %}
- {% if prev_article %}
- {{ prev_article.title }} →
- {% endif %}
-
- {% 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 %}
-
- {% endif %}
- {% endif %}
-
-
-{% endblock %}
-
-{% block sidebar %}
- {% load_sidebar user "p" %}
-{% endblock %}
\ No newline at end of file
diff --git a/templates/blog/article_index.html b/templates/blog/article_index.html
deleted file mode 100644
index 0ee6150..0000000
--- a/templates/blog/article_index.html
+++ /dev/null
@@ -1,42 +0,0 @@
-{% extends 'share_layout/base.html' %}
-{% load blog_tags %}
-{% load cache %}
-{% block header %}
- {% if tag_name %}
- {{ page_type }}:{{ tag_name }} | {{ SITE_DESCRIPTION }}
- {% comment %} {% endcomment %}
- {% else %}
- {{ SITE_NAME }} | {{ SITE_DESCRIPTION }}
- {% endif %}
-
-
-
-
-
-
-
-{% endblock %}
-{% block content %}
-
-
- {% if page_type and tag_name %}
-
- {% 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/templates/blog/error_page.html b/templates/blog/error_page.html
deleted file mode 100644
index d41cfb6..0000000
--- a/templates/blog/error_page.html
+++ /dev/null
@@ -1,45 +0,0 @@
-{% extends 'share_layout/base.html' %}
-{% load blog_tags %}
-{% load cache %}
-{% block header %}
- {% if tag_name %}
- {% if statuscode == '404' %}
- 404 NotFound
- {% elif statuscode == '403' %}
- Permission Denied
- {% elif statuscode == '500' %}
- 500 Error
- {% else %}
-
- {% endif %}
- {% comment %} {% endcomment %}
- {% else %}
- {{ SITE_NAME }} | {{ SITE_DESCRIPTION }}
- {% endif %}
-
-
-
-
-
-
-
-{% endblock %}
-{% block content %}
-
-
-{% endblock %}
-
-
-{% block sidebar %}
- {% load_sidebar user 'i' %}
-{% endblock %}
-
-
diff --git a/templates/blog/links_list.html b/templates/blog/links_list.html
deleted file mode 100644
index ccecbea..0000000
--- a/templates/blog/links_list.html
+++ /dev/null
@@ -1,44 +0,0 @@
-{% extends 'share_layout/base.html' %}
-{% load blog_tags %}
-{% load cache %}
-{% block header %}
-
- 友情链接 | {{ SITE_DESCRIPTION }}
-
-
-
-
-
-
-
-
-
-{% endblock %}
-{% block content %}
-
-
-{% endblock %}
-
-
-{% block sidebar %}
- {% load_sidebar user 'i' %}
-{% endblock %}
-
-
diff --git a/templates/blog/tags/article_info.html b/templates/blog/tags/article_info.html
deleted file mode 100644
index 3deec44..0000000
--- a/templates/blog/tags/article_info.html
+++ /dev/null
@@ -1,74 +0,0 @@
-{% load blog_tags %}
-{% load cache %}
-{% load i18n %}
-
-
-
-
- {% if isindex %}
- {{ article.body|custom_markdown|escape|truncatechars_content }}
-
Read more
- {% else %}
-
- {% if article.show_toc %}
- {% get_markdown_toc article.body as toc %}
-
{% trans 'toc' %}:
- {{ toc|safe }}
-
-
- {% endif %}
-
-
- {{ article.body|custom_markdown|escape }}
-
-
- {% endif %}
-
-
-
- {% load_article_metas article user %}
-
-
\ No newline at end of file
diff --git a/templates/blog/tags/article_meta_info.html b/templates/blog/tags/article_meta_info.html
deleted file mode 100644
index cb6111c..0000000
--- a/templates/blog/tags/article_meta_info.html
+++ /dev/null
@@ -1,59 +0,0 @@
-{% load i18n %}
-{% load blog_tags %}
-
-
-
-
-
diff --git a/templates/blog/tags/article_pagination.html b/templates/blog/tags/article_pagination.html
deleted file mode 100644
index 95514ff..0000000
--- a/templates/blog/tags/article_pagination.html
+++ /dev/null
@@ -1,17 +0,0 @@
-{% load i18n %}
-
-
- {% trans 'article navigation' %}
-
- {% if page_obj.has_next and next_url%}
-
- {% endif %}
- {% if page_obj.has_previous and previous_url %}
-
- {% endif %}
-
\ No newline at end of file
diff --git a/templates/blog/tags/article_tag_list.html b/templates/blog/tags/article_tag_list.html
deleted file mode 100644
index c8ba474..0000000
--- a/templates/blog/tags/article_tag_list.html
+++ /dev/null
@@ -1,19 +0,0 @@
-{% load i18n %}
-{% if article_tags_list %}
-
-{% endif %}
diff --git a/templates/blog/tags/breadcrumb.html b/templates/blog/tags/breadcrumb.html
deleted file mode 100644
index 67087d5..0000000
--- a/templates/blog/tags/breadcrumb.html
+++ /dev/null
@@ -1,19 +0,0 @@
-
-
- {% for name,url in names %}
-
-
- {{ name }}
-
-
-
- {% endfor %}
-
- {{ title }}
-
-
-
-
-
diff --git a/templates/blog/tags/sidebar.html b/templates/blog/tags/sidebar.html
deleted file mode 100755
index f70544c..0000000
--- a/templates/blog/tags/sidebar.html
+++ /dev/null
@@ -1,136 +0,0 @@
-{% load blog_tags %}
-{% load i18n %}
-
diff --git a/templates/comments/tags/comment_item.html b/templates/comments/tags/comment_item.html
deleted file mode 100644
index ebb0388..0000000
--- a/templates/comments/tags/comment_item.html
+++ /dev/null
@@ -1,34 +0,0 @@
-{% load blog_tags %}
-
\ No newline at end of file
diff --git a/templates/comments/tags/comment_item_tree.html b/templates/comments/tags/comment_item_tree.html
deleted file mode 100644
index a9decd1..0000000
--- a/templates/comments/tags/comment_item_tree.html
+++ /dev/null
@@ -1,54 +0,0 @@
-{% load blog_tags %}
-
-{% 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/templates/comments/tags/comment_list.html b/templates/comments/tags/comment_list.html
deleted file mode 100644
index 4092161..0000000
--- a/templates/comments/tags/comment_list.html
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
\ No newline at end of file
diff --git a/templates/comments/tags/post_comment.html b/templates/comments/tags/post_comment.html
deleted file mode 100644
index 3ae5a27..0000000
--- a/templates/comments/tags/post_comment.html
+++ /dev/null
@@ -1,33 +0,0 @@
-
-
-
diff --git a/templates/oauth/bindsuccess.html b/templates/oauth/bindsuccess.html
deleted file mode 100644
index 4bee77c..0000000
--- a/templates/oauth/bindsuccess.html
+++ /dev/null
@@ -1,22 +0,0 @@
-{% extends 'share_layout/base.html' %}
-{% block header %}
- {{ title }}
-{% endblock %}
-{% block content %}
-
-{% endblock %}
\ No newline at end of file
diff --git a/templates/oauth/oauth_applications.html b/templates/oauth/oauth_applications.html
deleted file mode 100644
index a841ad2..0000000
--- a/templates/oauth/oauth_applications.html
+++ /dev/null
@@ -1,13 +0,0 @@
-{% load i18n %}
-
diff --git a/templates/oauth/require_email.html b/templates/oauth/require_email.html
deleted file mode 100644
index 3adef12..0000000
--- a/templates/oauth/require_email.html
+++ /dev/null
@@ -1,46 +0,0 @@
-{% extends 'share_layout/base_account.html' %}
-
-{% load static %}
-{% block content %}
-
-
-
绑定您的邮箱账号
-
-
- {% if picture %}
-
- {% else %}
-
- {% endif %}
-
-
-
-
- 登录
-
-
-
-{% endblock %}
\ No newline at end of file
diff --git a/templates/owntracks/show_log_dates.html b/templates/owntracks/show_log_dates.html
deleted file mode 100644
index 7dbba21..0000000
--- a/templates/owntracks/show_log_dates.html
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
- 记录日期
-
-
-
-
- {% for date in results %}
-
- {{ date }}
-
- {% endfor %}
-
-
-
\ No newline at end of file
diff --git a/templates/owntracks/show_maps.html b/templates/owntracks/show_maps.html
deleted file mode 100644
index 3aeda36..0000000
--- a/templates/owntracks/show_maps.html
+++ /dev/null
@@ -1,135 +0,0 @@
-
-
-
-
-
-
- 运动轨迹
-
-
-
-
-
-
-
-
-
-
-
-
- {# {% 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 %} - -
--