├── .gitattributes ├── .gitignore ├── LICENSE ├── Pipfile ├── Pipfile.lock ├── README.md ├── account ├── __init__.py ├── admin.py ├── apps.py ├── forms.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py ├── urls.py └── views.py ├── logtest ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tasks.py ├── tests.py ├── urls.py ├── utils.py └── views.py ├── manage.py ├── servermanager ├── __init__.py ├── admin.py ├── apps.py ├── migrations │ └── __init__.py ├── models.py ├── tests.py └── views.py ├── static ├── css │ ├── base.css │ ├── bootstrap-table.min.css │ ├── bootstrap.min.css │ └── chart.css ├── favicon.ico ├── js │ ├── bootstrap-table.min.js │ ├── bootstrap.min.js │ ├── bootstrap.min.js.map │ ├── china.js │ ├── echarts.min.js │ ├── extensions │ │ ├── accent-neutralise │ │ │ ├── bootstrap-table-accent-neutralise.js │ │ │ └── bootstrap-table-accent-neutralise.min.js │ │ ├── addrbar │ │ │ ├── bootstrap-table-addrbar.js │ │ │ └── bootstrap-table-addrbar.min.js │ │ ├── auto-refresh │ │ │ ├── bootstrap-table-auto-refresh.js │ │ │ └── bootstrap-table-auto-refresh.min.js │ │ ├── cookie │ │ │ ├── bootstrap-table-cookie.js │ │ │ └── bootstrap-table-cookie.min.js │ │ ├── copy-rows │ │ │ ├── bootstrap-table-copy-rows.js │ │ │ └── bootstrap-table-copy-rows.min.js │ │ ├── defer-url │ │ │ ├── bootstrap-table-defer-url.js │ │ │ └── bootstrap-table-defer-url.min.js │ │ ├── editable │ │ │ ├── bootstrap-table-editable.js │ │ │ └── bootstrap-table-editable.min.js │ │ ├── export │ │ │ ├── bootstrap-table-export.js │ │ │ └── bootstrap-table-export.min.js │ │ ├── filter-control │ │ │ ├── bootstrap-table-filter-control.css │ │ │ ├── bootstrap-table-filter-control.js │ │ │ ├── bootstrap-table-filter-control.min.css │ │ │ └── bootstrap-table-filter-control.min.js │ │ ├── fixed-columns │ │ │ ├── bootstrap-table-fixed-columns.css │ │ │ ├── bootstrap-table-fixed-columns.js │ │ │ ├── bootstrap-table-fixed-columns.min.css │ │ │ └── bootstrap-table-fixed-columns.min.js │ │ ├── group-by-v2 │ │ │ ├── bootstrap-table-group-by.css │ │ │ ├── bootstrap-table-group-by.js │ │ │ ├── bootstrap-table-group-by.min.css │ │ │ └── bootstrap-table-group-by.min.js │ │ ├── group-by │ │ │ ├── bootstrap-table-group-by.css │ │ │ ├── bootstrap-table-group-by.js │ │ │ ├── bootstrap-table-group-by.min.css │ │ │ └── bootstrap-table-group-by.min.js │ │ ├── i18n-enhance │ │ │ ├── bootstrap-table-i18n-enhance.js │ │ │ └── bootstrap-table-i18n-enhance.min.js │ │ ├── key-events │ │ │ ├── bootstrap-table-key-events.js │ │ │ └── bootstrap-table-key-events.min.js │ │ ├── mobile │ │ │ ├── bootstrap-table-mobile.js │ │ │ └── bootstrap-table-mobile.min.js │ │ ├── multi-column-toggle │ │ │ ├── bootstrap-table-multi-toggle.js │ │ │ └── bootstrap-table-multi-toggle.min.js │ │ ├── multiple-search │ │ │ ├── bootstrap-table-multiple-search.js │ │ │ └── bootstrap-table-multiple-search.min.js │ │ ├── multiple-selection-row │ │ │ ├── bootstrap-table-multiple-selection-row.css │ │ │ ├── bootstrap-table-multiple-selection-row.js │ │ │ ├── bootstrap-table-multiple-selection-row.min.css │ │ │ └── bootstrap-table-multiple-selection-row.min.js │ │ ├── multiple-sort │ │ │ ├── bootstrap-table-multiple-sort.js │ │ │ └── bootstrap-table-multiple-sort.min.js │ │ ├── natural-sorting │ │ │ ├── bootstrap-table-natural-sorting.js │ │ │ └── bootstrap-table-natural-sorting.min.js │ │ ├── page-jump-to │ │ │ ├── bootstrap-table-page-jump-to.css │ │ │ ├── bootstrap-table-page-jump-to.js │ │ │ ├── bootstrap-table-page-jump-to.min.css │ │ │ └── bootstrap-table-page-jump-to.min.js │ │ ├── pipeline │ │ │ ├── bootstrap-table-pipeline.js │ │ │ └── bootstrap-table-pipeline.min.js │ │ ├── print │ │ │ ├── bootstrap-table-print.js │ │ │ └── bootstrap-table-print.min.js │ │ ├── reorder-columns │ │ │ ├── bootstrap-table-reorder-columns.js │ │ │ └── bootstrap-table-reorder-columns.min.js │ │ ├── reorder-rows │ │ │ ├── bootstrap-table-reorder-rows.css │ │ │ ├── bootstrap-table-reorder-rows.js │ │ │ ├── bootstrap-table-reorder-rows.min.css │ │ │ └── bootstrap-table-reorder-rows.min.js │ │ ├── resizable │ │ │ ├── bootstrap-table-resizable.js │ │ │ └── bootstrap-table-resizable.min.js │ │ ├── select2-filter │ │ │ ├── bootstrap-table-select2-filter.js │ │ │ └── bootstrap-table-select2-filter.min.js │ │ ├── sticky-header │ │ │ ├── bootstrap-table-sticky-header.css │ │ │ ├── bootstrap-table-sticky-header.js │ │ │ ├── bootstrap-table-sticky-header.min.css │ │ │ └── bootstrap-table-sticky-header.min.js │ │ ├── toolbar │ │ │ ├── bootstrap-table-toolbar.js │ │ │ └── bootstrap-table-toolbar.min.js │ │ ├── tree-column │ │ │ ├── bootstrap-table-tree-column.css │ │ │ ├── bootstrap-table-tree-column.js │ │ │ ├── bootstrap-table-tree-column.min.css │ │ │ └── bootstrap-table-tree-column.min.js │ │ └── treegrid │ │ │ ├── bootstrap-table-treegrid.js │ │ │ └── bootstrap-table-treegrid.min.js │ ├── jquery.min.js │ ├── locale │ │ ├── bootstrap-table-zh-CN.js │ │ └── bootstrap-table-zh-CN.min.js │ ├── popper.min.js │ ├── tableExport.min.js │ └── themes │ │ ├── bulma │ │ ├── bootstrap-table-bulma.css │ │ ├── bootstrap-table-bulma.js │ │ ├── bootstrap-table-bulma.min.css │ │ └── bootstrap-table-bulma.min.js │ │ ├── foundation │ │ ├── bootstrap-table-foundation.css │ │ ├── bootstrap-table-foundation.js │ │ ├── bootstrap-table-foundation.min.css │ │ └── bootstrap-table-foundation.min.js │ │ ├── materialize │ │ ├── bootstrap-table-materialize.css │ │ ├── bootstrap-table-materialize.js │ │ ├── bootstrap-table-materialize.min.css │ │ └── bootstrap-table-materialize.min.js │ │ └── semantic │ │ ├── bootstrap-table-semantic.css │ │ ├── bootstrap-table-semantic.js │ │ ├── bootstrap-table-semantic.min.css │ │ └── bootstrap-table-semantic.min.js └── open-iconic │ ├── .gitignore │ ├── FONT-LICENSE │ ├── ICON-LICENSE │ ├── README.md │ ├── bower.json │ ├── font │ ├── css │ │ ├── open-iconic-bootstrap.css │ │ ├── open-iconic-bootstrap.less │ │ ├── open-iconic-bootstrap.min.css │ │ ├── open-iconic-bootstrap.scss │ │ ├── open-iconic-bootstrap.styl │ │ ├── open-iconic-foundation.css │ │ ├── open-iconic-foundation.less │ │ ├── open-iconic-foundation.min.css │ │ ├── open-iconic-foundation.scss │ │ ├── open-iconic-foundation.styl │ │ ├── open-iconic.css │ │ ├── open-iconic.less │ │ ├── open-iconic.min.css │ │ ├── open-iconic.scss │ │ └── open-iconic.styl │ └── fonts │ │ ├── open-iconic.eot │ │ ├── open-iconic.otf │ │ ├── open-iconic.svg │ │ ├── open-iconic.ttf │ │ └── open-iconic.woff │ └── package.json ├── templates ├── account │ ├── base.html │ ├── login.html │ └── register.html └── logtest │ ├── base.html │ ├── err_log_table.html │ ├── http_status_line.html │ ├── http_status_pie.html │ ├── index.html │ ├── inner_diff_ip.html │ ├── inner_ip.html │ ├── outer_diff_ip.html │ ├── outer_ip.html │ ├── recent_log_table.html │ ├── temp.html │ ├── today_access.html │ ├── ua_browser_bot.html │ ├── ua_browser_detail.html │ └── ua_os.html └── weblog ├── __init__.py ├── admin_site.py ├── log_entry_admin.py ├── log_signals.py ├── settings.py ├── urls.py ├── utils.py └── wsgi.py /.gitattributes: -------------------------------------------------------------------------------- 1 | *.js linguist-language=Python 2 | *.css linguist-language=Python 3 | *.html linguist-language=Python 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Zhang Wang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Pipfile: -------------------------------------------------------------------------------- 1 | [[source]] 2 | name = "pypi" 3 | url = "https://pypi.org/simple" 4 | verify_ssl = true 5 | 6 | [dev-packages] 7 | 8 | [packages] 9 | django = "*" 10 | pymongo = "*" 11 | user-agents = "*" 12 | apscheduler = "*" 13 | django-apscheduler = "*" 14 | 15 | [requires] 16 | python_version = "3.6" 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 相关截图 2 | 3 | ![log](https://cos.rmboot.com/web-log/log.png) 4 | 5 | ![index](https://cos.rmboot.com/web-log/index.png) 6 | 7 | ![access](https://cos.rmboot.com/web-log/access.png) 8 | 9 | ![inner_ip](https://cos.rmboot.com/web-log/inner_ip.png) 10 | 11 | ![http_status](https://cos.rmboot.com/web-log/http_status.png) 12 | 13 | ![mail](https://cos.rmboot.com/web-log/mail.png) 14 | 15 | ![block](https://cos.rmboot.com/web-log/block.png) 16 | -------------------------------------------------------------------------------- /account/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/account/__init__.py -------------------------------------------------------------------------------- /account/admin.py: -------------------------------------------------------------------------------- 1 | from django import forms 2 | from django.contrib.auth.admin import UserAdmin 3 | from django.contrib.auth.forms import ReadOnlyPasswordHashField 4 | # Register your models here. 5 | from .models import WebLogUser 6 | 7 | 8 | class WebLogUserCreationForm(forms.ModelForm): 9 | password1 = forms.CharField(label='密码', widget=forms.PasswordInput) 10 | password2 = forms.CharField(label='再次输入密码', widget=forms.PasswordInput) 11 | 12 | class Meta: 13 | model = WebLogUser 14 | fields = ('email',) 15 | 16 | def clean_password2(self): 17 | # Check that the two password entries match 18 | password1 = self.cleaned_data.get("password1") 19 | password2 = self.cleaned_data.get("password2") 20 | if password1 and password2 and password1 != password2: 21 | raise forms.ValidationError("两次密码不一致") 22 | return password2 23 | 24 | def save(self, commit=True): 25 | # Save the provided password in hashed format 26 | user = super().save(commit=False) 27 | user.set_password(self.cleaned_data["password1"]) 28 | if commit: 29 | user.save() 30 | return user 31 | 32 | 33 | class WebLogUserChangeForm(forms.ModelForm): 34 | password = ReadOnlyPasswordHashField 35 | email = forms.EmailField(label="Email", widget=forms.EmailInput) 36 | 37 | class Meta: 38 | model = WebLogUser 39 | fields = ('email', 'password', 'is_active') 40 | 41 | def clean_password(self): 42 | # Regardless of what the user provides, return the initial value. 43 | # This is done here, rather than on the field, because the 44 | # field does not have access to the initial value 45 | return self.initial["password"] 46 | 47 | 48 | class WebLogUserAdmin(UserAdmin): 49 | form = WebLogUserChangeForm 50 | add_form = WebLogUserCreationForm 51 | list_display = ('id', 'nickname', 'username', 'email', 'last_login', 'date_joined') 52 | list_display_links = ('id', 'username') 53 | ordering = ('-id',) 54 | -------------------------------------------------------------------------------- /account/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class AccountConfig(AppConfig): 5 | name = 'account' 6 | -------------------------------------------------------------------------------- /account/forms.py: -------------------------------------------------------------------------------- 1 | from django.contrib.auth.forms import AuthenticationForm, UserCreationForm 2 | from django.forms import widgets 3 | from django.conf import settings 4 | from django.contrib.auth import get_user_model 5 | 6 | 7 | class LoginForm(AuthenticationForm): 8 | def __init__(self, *args, **kwargs): 9 | super(LoginForm, self).__init__(*args, **kwargs) 10 | self.fields['username'].widget = widgets.TextInput(attrs={'placeholder': "username", "class": "form-control"}) 11 | self.fields['password'].widget = widgets.PasswordInput( 12 | attrs={'placeholder': "password", "class": "form-control"}) 13 | 14 | 15 | class RegisterForm(UserCreationForm): 16 | def __init__(self, *args, **kwargs): 17 | super(RegisterForm, self).__init__(*args, **kwargs) 18 | 19 | self.fields['username'].widget = widgets.TextInput(attrs={'placeholder': "username", "class": "form-control"}) 20 | self.fields['email'].widget = widgets.EmailInput(attrs={'placeholder': "email", "class": "form-control"}) 21 | self.fields['password1'].widget = widgets.PasswordInput( 22 | attrs={'placeholder': "password", "class": "form-control"}) 23 | self.fields['password2'].widget = widgets.PasswordInput( 24 | attrs={'placeholder': "repeat password", "class": "form-control"}) 25 | 26 | class Meta: 27 | model = get_user_model() 28 | fields = ("username", "email") 29 | -------------------------------------------------------------------------------- /account/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/account/migrations/__init__.py -------------------------------------------------------------------------------- /account/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import AbstractUser 3 | from django.utils.timezone import now 4 | 5 | 6 | # Create your models here. 7 | 8 | class WebLogUser(AbstractUser): 9 | nickname = models.CharField('昵称', max_length=100, blank=True) 10 | created_time = models.DateTimeField('创建时间', default=now) 11 | last_mod_time = models.DateTimeField('修改时间', default=now) 12 | 13 | def __str__(self): 14 | return self.email 15 | 16 | class Meta: 17 | ordering = ['-id'] 18 | verbose_name = "用户" 19 | verbose_name_plural = verbose_name 20 | get_latest_by = 'id' 21 | -------------------------------------------------------------------------------- /account/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /account/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from django.contrib.auth import views as auth_view 3 | 4 | from . import views 5 | from .forms import LoginForm 6 | 7 | app_name = "account" 8 | 9 | urlpatterns = [ 10 | url(r'^login/$', views.LoginView.as_view(success_url='/'), name='login', kwargs={'authentication_form': LoginForm}), 11 | url(r'^register/$', views.RegisterView.as_view(success_url="/"), name='register'), 12 | url(r'^logout/$', views.LogoutView.as_view(), name='logout') 13 | ] -------------------------------------------------------------------------------- /account/views.py: -------------------------------------------------------------------------------- 1 | import logging 2 | from .forms import RegisterForm, LoginForm 3 | from django.contrib.auth import logout 4 | from django.views.generic import FormView, RedirectView 5 | from django.http import HttpResponseRedirect 6 | from django.urls import reverse 7 | from django.contrib.auth.forms import AuthenticationForm 8 | from django.contrib.auth import REDIRECT_FIELD_NAME 9 | from django.views.decorators.csrf import csrf_protect 10 | from django.contrib import auth 11 | from django.views.decorators.cache import never_cache 12 | from django.utils.decorators import method_decorator 13 | from django.views.decorators.debug import sensitive_post_parameters 14 | from django.utils.http import is_safe_url 15 | 16 | logger = logging.getLogger(__name__) 17 | 18 | 19 | # Create your views here. 20 | 21 | class RegisterView(FormView): 22 | form_class = RegisterForm 23 | template_name = 'account/register.html' 24 | 25 | def form_valid(self, form): 26 | user = form.save(False) 27 | user.save(True) 28 | url = reverse('logtest:index') 29 | return HttpResponseRedirect(url) 30 | 31 | 32 | class LogoutView(RedirectView): 33 | url = '/login/' 34 | 35 | @method_decorator(never_cache) 36 | def dispatch(self, request, *args, **kwargs): 37 | return super(LogoutView, self).dispatch(request, *args, **kwargs) 38 | 39 | def get(self, request, *args, **kwargs): 40 | logout(request) 41 | return super(LogoutView, self).get(request, *args, **kwargs) 42 | 43 | 44 | class LoginView(FormView): 45 | form_class = LoginForm 46 | template_name = 'account/login.html' 47 | success_url = '/' 48 | redirect_field_name = REDIRECT_FIELD_NAME 49 | 50 | @method_decorator(sensitive_post_parameters('password')) 51 | @method_decorator(csrf_protect) 52 | @method_decorator(never_cache) 53 | def dispatch(self, request, *args, **kwargs): 54 | return super(LoginView, self).dispatch(request, *args, **kwargs) 55 | 56 | def get_context_data(self, **kwargs): 57 | redirect_to = self.request.GET.get(self.redirect_field_name) 58 | if redirect_to is None: 59 | redirect_to = '/' 60 | kwargs['redirect_to'] = redirect_to 61 | 62 | return super(LoginView, self).get_context_data(**kwargs) 63 | 64 | def form_valid(self, form): 65 | form = AuthenticationForm(data=self.request.POST, request=self.request) 66 | 67 | if form.is_valid(): 68 | # from weblog.utils import cache 69 | # if cache and cache is not None: 70 | # cache.clear() 71 | logger.info(self.redirect_field_name) 72 | # redirect_to = self.request.GET.get(self.redirect_field_name) 73 | auth.login(self.request, form.get_user()) 74 | return super(LoginView, self).form_valid(form) 75 | # return HttpResponseRedirect('/') 76 | else: 77 | return self.render_to_response({ 78 | 'form': form 79 | }) 80 | 81 | def get_success_url(self): 82 | 83 | redirect_to = self.request.POST.get(self.redirect_field_name) 84 | if not is_safe_url(url=redirect_to, allowed_hosts=[self.request.get_host()]): 85 | redirect_to = self.success_url 86 | return redirect_to 87 | -------------------------------------------------------------------------------- /logtest/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/logtest/__init__.py -------------------------------------------------------------------------------- /logtest/admin.py: -------------------------------------------------------------------------------- 1 | from django_apscheduler.admin import DjangoJobAdmin, DjangoJobExecutionAdmin 2 | -------------------------------------------------------------------------------- /logtest/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class LogtestConfig(AppConfig): 5 | name = 'logtest' 6 | -------------------------------------------------------------------------------- /logtest/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/logtest/migrations/__init__.py -------------------------------------------------------------------------------- /logtest/models.py: -------------------------------------------------------------------------------- 1 | from django_apscheduler.admin import DjangoJob, DjangoJobExecution 2 | -------------------------------------------------------------------------------- /logtest/tests.py: -------------------------------------------------------------------------------- 1 | import json 2 | import time 3 | import random 4 | import pymongo 5 | from urllib import request 6 | from user_agents import parse 7 | 8 | client = pymongo.MongoClient("mongodb://localhost:27017/") 9 | db = client.logtest 10 | 11 | # ip to detail 12 | """ 13 | def ip_to_db(ip): 14 | url = 'http://ip.taobao.com/service/getIpInfo.php?ip=' + ip 15 | headers = [{'User-Agent': 'Opera/9.80 (Windows NT 6.1; U; en) Presto/2.8.131 Version/11.11'}, 16 | {'User-Agent': 'Mozilla/5.0 (Windows NT 6.1; rv,2.0.1) Gecko/20100101 Firefox/4.0.1'}, 17 | {'User-Agent': ' Mozilla/5.0 (Macintosh; Intel Mac OS X 10.6; rv,2.0.1) Gecko/20100101 Firefox/4.0.1'}, 18 | {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) \ 19 | AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36'}, 20 | {'User-Agent': 'Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_6_8; en-us) \ 21 | AppleWebKit/534.50 (KHTML, like Gecko) Version/5.1 Safari/534.50'}] 22 | rand_index = random.randint(0, len(headers) - 1) 23 | req = request.Request(url=url, headers=headers[rand_index]) 24 | data = request.urlopen(req).read().decode('utf-8') 25 | jsondata = json.loads(data) 26 | if jsondata['code'] == 0: 27 | return jsondata['data'] 28 | 29 | 30 | count = 1 31 | ip = db.ip_copy.find({}, {'_id': 0}) 32 | for i in ip: 33 | if i['isp'] == '': 34 | time.sleep(random.randint(3, 4)) 35 | data = ip_to_db(i['name']) 36 | for k in list(data.keys()): 37 | if k != 'country' and k != 'region' and k != 'city' and k != 'country_id' and k != 'isp' and k != 'ip': 38 | data.pop(k) 39 | db.ip_copy.update_one({'name': data['ip']}, {'$set': data}) 40 | print(count, data['country']) 41 | count += 1 42 | """ 43 | # httpstatus to pie show 44 | """ 45 | http_status = db.httpstatus.find({}, {'_id': 0}) 46 | http_status_all, http_status_name = [], [] 47 | http_status_dict = {'1': ' 提示信息 - 表示请求已被成功接收,继续处理', 48 | '2': ' 成功 - 表示请求已被成功接收,理解,接受', 49 | '3': ' 重定向 - 要完成请求必须进行更进一步的处理', 50 | '4': ' 客户端错误 - 请求有语法错误或请求无法实现', 51 | '5': ' 服务器端错误 - 服务器未能实现合法的请求', 52 | 'e': ' 自定义危险请求 - 不符合解析规则', } 53 | for i in http_status: 54 | if i['name'][0] in http_status_dict: 55 | i['name'] = i['name']+http_status_dict[i['name'][0]] 56 | http_status_all.append(i) 57 | http_status_name.append(i['name']) 58 | print(http_status_all) 59 | print(http_status_name) 60 | """ 61 | # internal ip 62 | """ 63 | ip = db.ip.aggregate([ 64 | {'$match': {'country_id': 'CN'}}, 65 | {'$group': {'_id': '$region', 'count': {'$sum': 1}}}, 66 | {'$project': {'name': '$_id', 'value': '$count', '_id': 0}}, 67 | {'$sort': {'value': -1}} 68 | ]) 69 | data = [] 70 | for i in ip: 71 | data.append(i) 72 | all_data = [{'name': '江苏'}, {'name': '北京'}, {'name': '上海'}, {'name': '重庆'}, {'name': '河北'}, {'name': '河南'}, 73 | {'name': '云南'}, {'name': '辽宁'}, {'name': '黑龙江'}, {'name': '湖南'}, {'name': '安徽'}, {'name': '山东'}, 74 | {'name': '新疆'}, {'name': '江苏'}, {'name': '浙江'}, {'name': '江西'}, {'name': '湖北'}, {'name': '广西'}, 75 | {'name': '甘肃'}, {'name': '山西'}, {'name': '内蒙古'}, {'name': '陕西'}, {'name': '吉林'}, {'name': '福建'}, 76 | {'name': '贵州'}, {'name': '广东'}, {'name': '青海'}, {'name': '西藏'}, {'name': '四川'}, {'name': '宁夏'}, 77 | {'name': '海南'}, {'name': '台湾'}, {'name': '香港'}, {'name': '澳门'}] 78 | for a in all_data: 79 | a['value'] = 0 80 | for i in all_data: 81 | for j in data: 82 | if j['name'] == i['name']: 83 | i['value'] = j['value'] 84 | all_data_sort = sorted(all_data, key=lambda x: x['value'], reverse=True) 85 | print(all_data_sort) 86 | """ 87 | # useragent to mongodb 88 | """ 89 | agent = db.log.aggregate([ 90 | {'$group': {'_id': '$user_agent', 'count': {'$sum': 1}}}, 91 | {'$project': {'name': '$_id', 'value': '$count', '_id': 0}}, 92 | {'$sort': {'value': -1}} 93 | ]) 94 | num = 0 95 | for i in agent: 96 | num += 1 97 | user_agent = parse(i['name']) 98 | db.user_agent.insert_one({'name': i['name'], 'value': i['value'], 99 | 'browser': user_agent.browser.family, 100 | 'browser_version': user_agent.browser.version_string, 101 | 'os': user_agent.os.family, 102 | 'os_version': user_agent.os.version_string}) 103 | print(num) 104 | """ 105 | 106 | -------------------------------------------------------------------------------- /logtest/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | 3 | from . import views 4 | 5 | app_name = 'logtest' 6 | urlpatterns = [ 7 | path('', views.IndexView.as_view(), name='index'), 8 | path('today_access', views.TodayAccessView.as_view(), name='today_access'), 9 | path('recent_log_table', views.RecentLogView.as_view(), name='recent_log_table'), 10 | path('err_log_table', views.ErrLogView.as_view(), name='err_log_table'), 11 | path('http_status_pie', views.HttpStatusPieView.as_view(), name='http_status_pie'), 12 | path('http_status_line', views.HttpStatusLineView.as_view(), name='http_status_line'), 13 | path('inner_ip', views.InnerIPView.as_view(), name='inner_ip'), 14 | path('inner_diff_ip', views.InnerDiffIPView.as_view(), name='inner_diff_ip'), 15 | path('outer_ip', views.OuterIPView.as_view(), name='outer_ip'), 16 | path('outer_diff_ip', views.OuterDiffIPView.as_view(), name='outer_diff_ip'), 17 | path('ua_browser_bot', views.UABrowserBotView.as_view(), name='ua_browser_bot'), 18 | path('ua_browser_detail', views.UABrowserDetailView.as_view(), name='ua_browser_detail'), 19 | path('ua_os', views.UAOsView.as_view(), name='ua_os'), 20 | ] 21 | -------------------------------------------------------------------------------- /logtest/utils.py: -------------------------------------------------------------------------------- 1 | from weblog.utils import send_email 2 | 3 | import logging 4 | 5 | logger = logging.getLogger(__name__) 6 | 7 | 8 | def send_comment_email(subject, comment, log_info): 9 | try: 10 | site = 'your site domain' 11 | subject = subject 12 | url = "https://{site}".format(site=site) 13 | html_content = '网址:%s
%s
%s' % (url, comment, log_info) 14 | tomail = 'your email addr' 15 | send_email([tomail], subject, html_content) 16 | except Exception as e: 17 | logger.error(e) 18 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'web_log.settings') 9 | try: 10 | from django.core.management import execute_from_command_line 11 | except ImportError as exc: 12 | raise ImportError( 13 | "Couldn't import Django. Are you sure it's installed and " 14 | "available on your PYTHONPATH environment variable? Did you " 15 | "forget to activate a virtual environment?" 16 | ) from exc 17 | execute_from_command_line(sys.argv) 18 | 19 | 20 | if __name__ == '__main__': 21 | main() 22 | -------------------------------------------------------------------------------- /servermanager/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/servermanager/__init__.py -------------------------------------------------------------------------------- /servermanager/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from servermanager.models import BlockIPLog 3 | import os 4 | 5 | 6 | # Register your models here. 7 | class EmailSendLogAdmin(admin.ModelAdmin): 8 | list_display = ('title', 'emailto', 'send_result', 'created_time') 9 | readonly_fields = ('title', 'emailto', 'send_result', 'created_time', 'content') 10 | 11 | def has_add_permission(self, request): 12 | return False 13 | 14 | 15 | class BlockIPLogAdmin(admin.ModelAdmin): 16 | list_display = ('ip_addr', 'send_result', 'created_time') 17 | readonly_fields = ('ip_addr', 'send_result', 'created_time') 18 | actions = ['delete_model'] 19 | 20 | def get_actions(self, request): 21 | actions = super(BlockIPLogAdmin, self).get_actions(request) 22 | del actions['delete_selected'] 23 | return actions 24 | 25 | def delete_model(self, request, obj): 26 | for i in obj: 27 | os.system('sudo iptables -D INPUT -s %s -j DROP' % i.ip_addr) 28 | print('解禁:' + i.ip_addr) 29 | i.delete() 30 | delete_model.short_description = '解除禁止访问' 31 | 32 | def has_add_permission(self, request): 33 | return False 34 | -------------------------------------------------------------------------------- /servermanager/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ServermanagerConfig(AppConfig): 5 | name = 'servermanager' 6 | -------------------------------------------------------------------------------- /servermanager/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/servermanager/migrations/__init__.py -------------------------------------------------------------------------------- /servermanager/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | class EmailSendLog(models.Model): 5 | emailto = models.CharField('收件人', max_length=300) 6 | title = models.CharField('邮件标题', max_length=2000) 7 | content = models.TextField('邮件内容') 8 | send_result = models.BooleanField('结果', default=False) 9 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 10 | 11 | def __str__(self): 12 | return self.title 13 | 14 | class Meta: 15 | verbose_name = '预警邮件日志' 16 | verbose_name_plural = verbose_name 17 | ordering = ['-created_time'] 18 | 19 | 20 | class BlockIPLog(models.Model): 21 | ip_addr = models.CharField('IP', max_length=20,unique=True) 22 | send_result = models.BooleanField('结果', default=False) 23 | created_time = models.DateTimeField('创建时间', auto_now_add=True) 24 | 25 | def __str__(self): 26 | return self.ip_addr 27 | 28 | # def delete(self): 29 | # os.system('sudo iptables -D INPUT -s %s -j DROP' % self.ip_addr) 30 | # print(self.ip_addr) 31 | # super(BlockIPLog, self).delete() 32 | 33 | class Meta: 34 | verbose_name = '禁止IP列表' 35 | verbose_name_plural = verbose_name 36 | ordering = ['-created_time'] 37 | -------------------------------------------------------------------------------- /servermanager/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /servermanager/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | 3 | # Create your views here. 4 | -------------------------------------------------------------------------------- /static/css/base.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - Simple Sidebar (https://startbootstrap.com/template-overviews/simple-sidebar) 3 | * Copyright 2013-2019 Start Bootstrap 4 | * Licensed under MIT (https://github.com/BlackrockDigital/startbootstrap-simple-sidebar/blob/master/LICENSE) 5 | */ 6 | body { 7 | overflow-x: hidden; 8 | } 9 | 10 | #sidebar-wrapper { 11 | min-height: 100vh; 12 | margin-left: -15rem; 13 | -webkit-transition: margin .25s ease-out; 14 | -moz-transition: margin .25s ease-out; 15 | -o-transition: margin .25s ease-out; 16 | transition: margin .25s ease-out; 17 | } 18 | 19 | #sidebar-wrapper .sidebar-heading { 20 | padding: 0.875rem 1.25rem; 21 | font-size: 1.2rem; 22 | } 23 | 24 | #sidebar-wrapper .list-group { 25 | width: 17rem; 26 | } 27 | 28 | #page-content-wrapper { 29 | min-width: 100vw; 30 | } 31 | 32 | #wrapper.toggled #sidebar-wrapper { 33 | margin-left: 0; 34 | } 35 | 36 | @media (min-width: 768px) { 37 | #sidebar-wrapper { 38 | margin-left: 0; 39 | } 40 | 41 | #page-content-wrapper { 42 | min-width: 0; 43 | width: 100%; 44 | } 45 | 46 | #wrapper.toggled #sidebar-wrapper { 47 | margin-left: -17rem; 48 | } 49 | } 50 | .footer { 51 | padding: 15px 20px; 52 | } -------------------------------------------------------------------------------- /static/css/chart.css: -------------------------------------------------------------------------------- 1 | #container{ 2 | height: 600px; 3 | } -------------------------------------------------------------------------------- /static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/static/favicon.ico -------------------------------------------------------------------------------- /static/js/extensions/addrbar/bootstrap-table-addrbar.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableAddrbar={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d=c.length)break;g=c[e++]}else{if(e=c.next(),e.done)break;g=e.value}var h=g,i=f(h,2),j=i[0],k=i[1],l=j+'='+k;if(b.match(j+'=([^&]*)')){var m=new RegExp('('+j+'=)([^&]*)','gi');b=b.replace(m,l)}else{var n=b.match('[?]')?'&':'?';b=b+n+l}}return location.hash&&(b+=location.hash),b}g.BootstrapTable=function(f){function j(){return a(this,j),b(this,(j.__proto__||Object.getPrototypeOf(j)).apply(this,arguments))}return c(j,f),d(j,[{key:'init',value:function(){var a=this;if(this.options.addrbar){this.addrbarInit=!0;var b=this.options.addrPrefix||'';this.options.pageSize=this.options.pageSize||(h(b+'limit')?parseInt(h(b+'limit')):g.BootstrapTable.DEFAULTS.pageSize),this.options.pageNumber=this.options.pageNumber||(h(b+'page')?parseInt(h(b+'page')):g.BootstrapTable.DEFAULTS.pageNumber),this.options.sortOrder=this.options.sortOrder||(h(b+'order')?h(b+'order'):g.BootstrapTable.DEFAULTS.sortOrder),this.options.sortName=this.options.sortName||(h(b+'sort')?h(b+'sort'):'id'),this.options.searchText=this.options.searchText||(h(b+'search')?h(b+'search'):g.BootstrapTable.DEFAULTS.searchText);var c=this.options.onLoadSuccess;this.options.onLoadSuccess=function(d){if(a.addrbarInit)a.addrbarInit=!1;else{var e={};e[b+'page']=a.options.pageNumber,e[b+'limit']=a.options.pageSize,e[b+'order']=a.options.sortOrder,e[b+'sort']=a.options.sortName,e[b+'search']=a.options.searchText,window.history.pushState({},'',i(e))}c&&c.call(a,d)}}e(j.prototype.__proto__||Object.getPrototypeOf(j.prototype),'init',this).call(this)}}]),j}(g.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/auto-refresh/bootstrap-table-auto-refresh.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableAutoRefresh={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d.btn-group'),j=h.find('.auto-refresh');j.length||(j=f('\n \n ').appendTo(h),j.on('click',f.proxy(this.toggleAutoRefresh,this)))}}},{key:'toggleAutoRefresh',value:function(){var a=this;this.options.autoRefresh&&(this.options.autoRefreshStatus?(clearInterval(this.options.autoRefreshFunction),this.$toolbar.find('>.btn-group').find('.auto-refresh').removeClass('active')):(this.options.autoRefreshFunction=setInterval(function(){a.refresh({silent:a.options.autoRefreshSilent})},1e3*this.options.autoRefreshInterval),this.$toolbar.find('>.btn-group').find('.auto-refresh').addClass('active')),this.options.autoRefreshStatus=!this.options.autoRefreshStatus)}}]),i}(f.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/copy-rows/bootstrap-table-copy-rows.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.bootstrapTableCopyRows = mod.exports; 12 | } 13 | })(this, function () { 14 | 'use strict'; 15 | 16 | /** 17 | * @author Homer Glascock 18 | * @version: v1.0.0 19 | */ 20 | 21 | !function ($) { 22 | "use strict"; 23 | 24 | var calculateObjectValue = $.fn.bootstrapTable.utils.calculateObjectValue, 25 | sprintf = $.fn.bootstrapTable.utils.sprintf; 26 | 27 | var copytext = function copytext(text) { 28 | var textField = document.createElement('textarea'); 29 | $(textField).html(text); 30 | document.body.appendChild(textField); 31 | textField.select(); 32 | 33 | try { 34 | document.execCommand('copy'); 35 | } catch (e) { 36 | console.log("Oops, unable to copy"); 37 | } 38 | $(textField).remove(); 39 | }; 40 | 41 | $.extend($.fn.bootstrapTable.defaults, { 42 | copyBtn: false, 43 | copyWHiddenBtn: false, 44 | copyDelemeter: ", " 45 | }); 46 | 47 | $.fn.bootstrapTable.methods.push('copyColumnsToClipboard', 'copyColumnsToClipboardWithHidden'); 48 | 49 | var BootstrapTable = $.fn.bootstrapTable.Constructor, 50 | _initToolbar = BootstrapTable.prototype.initToolbar; 51 | 52 | BootstrapTable.prototype.initToolbar = function () { 53 | 54 | _initToolbar.apply(this, Array.prototype.slice.apply(arguments)); 55 | 56 | var that = this, 57 | $btnGroup = this.$toolbar.find('>.btn-group'); 58 | 59 | if (this.options.clickToSelect || this.options.singleSelect) { 60 | 61 | if (this.options.copyBtn) { 62 | var copybtn = ""; 63 | $btnGroup.append(copybtn); 64 | $btnGroup.find('#copyBtn').click(function () { 65 | that.copyColumnsToClipboard(); 66 | }); 67 | } 68 | 69 | if (this.options.copyWHiddenBtn) { 70 | var copyhiddenbtn = ""; 71 | $btnGroup.append(copyhiddenbtn); 72 | $btnGroup.find('#copyWHiddenBtn').click(function () { 73 | that.copyColumnsToClipboardWithHidden(); 74 | }); 75 | } 76 | } 77 | }; 78 | 79 | BootstrapTable.prototype.copyColumnsToClipboard = function () { 80 | var that = this, 81 | ret = "", 82 | delimet = this.options.copyDelemeter; 83 | 84 | $.each(that.getSelections(), function (index, row) { 85 | $.each(that.options.columns[0], function (indy, column) { 86 | if (column.field !== "state" && column.field !== "RowNumber" && column.visible) { 87 | if (row[column.field] !== null) { 88 | ret += calculateObjectValue(column, that.header.formatters[indy], [row[column.field], row, index], row[column.field]); 89 | } 90 | ret += delimet; 91 | } 92 | }); 93 | 94 | ret += "\r\n"; 95 | }); 96 | 97 | copytext(ret); 98 | }; 99 | 100 | BootstrapTable.prototype.copyColumnsToClipboardWithHidden = function () { 101 | var that = this, 102 | ret = "", 103 | delimet = this.options.copyDelemeter; 104 | 105 | $.each(that.getSelections(), function (index, row) { 106 | $.each(that.options.columns[0], function (indy, column) { 107 | if (column.field != "state" && column.field !== "RowNumber") { 108 | if (row[column.field] !== null) { 109 | ret += calculateObjectValue(column, that.header.formatters[indy], [row[column.field], row, index], row[column.field]); 110 | } 111 | ret += delimet; 112 | } 113 | }); 114 | 115 | ret += "\r\n"; 116 | }); 117 | 118 | copytext(ret); 119 | }; 120 | }(jQuery); 121 | }); -------------------------------------------------------------------------------- /static/js/extensions/copy-rows/bootstrap-table-copy-rows.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableCopyRows={exports:{}}.exports}})(this,function(){'use strict';!function(a){var b=a.fn.bootstrapTable.utils.calculateObjectValue,c=a.fn.bootstrapTable.utils.sprintf,d=function(b){var c=document.createElement('textarea');a(c).html(b),document.body.appendChild(c),c.select();try{document.execCommand('copy')}catch(a){console.log('Oops, unable to copy')}a(c).remove()};a.extend(a.fn.bootstrapTable.defaults,{copyBtn:!1,copyWHiddenBtn:!1,copyDelemeter:', '}),a.fn.bootstrapTable.methods.push('copyColumnsToClipboard','copyColumnsToClipboardWithHidden');var e=a.fn.bootstrapTable.Constructor,f=e.prototype.initToolbar;e.prototype.initToolbar=function(){f.apply(this,Array.prototype.slice.apply(arguments));var a=this,b=this.$toolbar.find('>.btn-group');if(this.options.clickToSelect||this.options.singleSelect){if(this.options.copyBtn){b.append(''),b.find('#copyBtn').click(function(){a.copyColumnsToClipboard()})}if(this.options.copyWHiddenBtn){b.append(''),b.find('#copyWHiddenBtn').click(function(){a.copyColumnsToClipboardWithHidden()})}}},e.prototype.copyColumnsToClipboard=function(){var c=this,e='',f=this.options.copyDelemeter;a.each(c.getSelections(),function(d,g){a.each(c.options.columns[0],function(a,h){'state'!==h.field&&'RowNumber'!==h.field&&h.visible&&(null!==g[h.field]&&(e+=b(h,c.header.formatters[a],[g[h.field],g,d],g[h.field])),e+=f)}),e+='\r\n'}),d(e)},e.prototype.copyColumnsToClipboardWithHidden=function(){var c=this,e='',f=this.options.copyDelemeter;a.each(c.getSelections(),function(d,g){a.each(c.options.columns[0],function(a,h){'state'!=h.field&&'RowNumber'!==h.field&&(null!==g[h.field]&&(e+=b(h,c.header.formatters[a],[g[h.field],g,d],g[h.field])),e+=f)}),e+='\r\n'}),d(e)}}(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/defer-url/bootstrap-table-defer-url.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.bootstrapTableDeferUrl = mod.exports; 12 | } 13 | })(this, function () { 14 | 'use strict'; 15 | 16 | /** 17 | * When using server-side processing, the default mode of operation for 18 | * bootstrap-table is to simply throw away any data that currently exists in the 19 | * table and make a request to the server to get the first page of data to 20 | * display. This is fine for an empty table, but if you already have the first 21 | * page of data displayed in the plain HTML, it is a waste of resources. As 22 | * such, you can use data-defer-url instead of data-url to allow you to instruct 23 | * bootstrap-table to not make that initial request, rather it will use the data 24 | * already on the page. 25 | * 26 | * @author: Ruben Suarez 27 | * @webSite: http://rubensa.eu.org 28 | * @version: v1.0.0 29 | */ 30 | 31 | (function ($) { 32 | 'use strict'; 33 | 34 | $.extend($.fn.bootstrapTable.defaults, { 35 | deferUrl: undefined 36 | }); 37 | 38 | var BootstrapTable = $.fn.bootstrapTable.Constructor, 39 | _init = BootstrapTable.prototype.init; 40 | 41 | BootstrapTable.prototype.init = function () { 42 | _init.apply(this, Array.prototype.slice.apply(arguments)); 43 | 44 | if (this.options.deferUrl) { 45 | this.options.url = this.options.deferUrl; 46 | } 47 | }; 48 | })(jQuery); 49 | }); -------------------------------------------------------------------------------- /static/js/extensions/defer-url/bootstrap-table-defer-url.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableDeferUrl={exports:{}}.exports}})(this,function(){'use strict';(function(a){a.extend(a.fn.bootstrapTable.defaults,{deferUrl:void 0});var b=a.fn.bootstrapTable.Constructor,c=b.prototype.init;b.prototype.init=function(){c.apply(this,Array.prototype.slice.apply(arguments)),this.options.deferUrl&&(this.options.url=this.options.deferUrl)}})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/editable/bootstrap-table-editable.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableEditable={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d':l}}})}},{key:'initBody',value:function(a){var b=this;e(i.prototype.__proto__||Object.getPrototypeOf(i.prototype),'initBody',this).call(this,a),this.options.editable&&(f.each(this.columns,function(a,c){if(c.editable){var d=b.getData(),e=b.$body.find('a[data-name="'+c.field+'"]');e.each(function(a,b){var e=f(b),h=e.closest('tr'),i=h.data('index'),j=d[i],k=g.calculateObjectValue(c,c.editable,[i,j,e],{});e.editable(k)}),e.off('save').on('save',function(a,d){var e=a.currentTarget,g=d.submitValue,h=f(e),i=b.getData(),j=h.parents('tr[data-index]').data('index'),k=i[j],l=k[c.field];h.data('value',g),k[c.field]=g,b.trigger('editable-save',c.field,k,l,h),b.resetFooter()}),e.off('shown').on('shown',function(a,d){var e=a.currentTarget,g=f(e),h=b.getData(),i=g.parents('tr[data-index]').data('index'),j=h[i];b.trigger('editable-shown',c.field,j,g,d)}),e.off('hidden').on('hidden',function(a,d){var e=a.currentTarget,g=f(e),h=b.getData(),i=g.parents('tr[data-index]').data('index'),j=h[i];b.trigger('editable-hidden',c.field,j,g,d)})}}),this.trigger('editable-init'))}}]),i}(f.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/filter-control/bootstrap-table-filter-control.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @author: Dennis Hernández 3 | * @webSite: http://djhvscf.github.io/Blog 4 | * @version: v2.1.1 5 | */ 6 | 7 | .no-filter-control { 8 | height: 34px; 9 | } 10 | 11 | .filter-control { 12 | margin: 0 2px 2px 2px; 13 | } -------------------------------------------------------------------------------- /static/js/extensions/filter-control/bootstrap-table-filter-control.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | .no-filter-control{height:34px}.filter-control{margin:0 2px 2px 2px} -------------------------------------------------------------------------------- /static/js/extensions/fixed-columns/bootstrap-table-fixed-columns.css: -------------------------------------------------------------------------------- 1 | .fixed-table-header-columns, 2 | .fixed-table-body-columns { 3 | position: absolute; 4 | background-color: #fff; 5 | box-sizing: border-box; 6 | overflow: hidden; 7 | z-index: 1; 8 | } 9 | 10 | .fixed-table-header-columns { 11 | z-index: 2; 12 | } 13 | 14 | .fixed-table-header-columns .table, 15 | .fixed-table-body-columns .table { 16 | border-right: 1px solid #ddd; 17 | } 18 | 19 | .fixed-table-header-columns .table.table-no-bordered, 20 | .fixed-table-body-columns .table.table-no-bordered { 21 | border-right: 1px solid transparent; 22 | } 23 | 24 | .fixed-table-body-columns table { 25 | position: absolute; 26 | animation: none; 27 | } 28 | -------------------------------------------------------------------------------- /static/js/extensions/fixed-columns/bootstrap-table-fixed-columns.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | .fixed-table-header-columns,.fixed-table-body-columns{position:absolute;background-color:#fff;box-sizing:border-box;overflow:hidden;z-index:1}.fixed-table-header-columns{z-index:2}.fixed-table-header-columns .table,.fixed-table-body-columns .table{border-right:1px solid #ddd}.fixed-table-header-columns .table.table-no-bordered,.fixed-table-body-columns .table.table-no-bordered{border-right:1px solid transparent}.fixed-table-body-columns table{position:absolute;animation:none} -------------------------------------------------------------------------------- /static/js/extensions/fixed-columns/bootstrap-table-fixed-columns.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableFixedColumns={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d'),this.$fixedHeader.append(this.$tableHeader.find('>table').clone(!0)),this.$tableHeader.after(this.$fixedHeader);var g=this.getFixedColumnsWidth();this.$fixedHeader.css({top:0,width:g,height:this.$tableHeader.outerHeight(!0)}),this.initFixedColumnsBody(),this.$fixedBody.css({top:this.$tableHeader.outerHeight(!0),width:g,height:this.$tableBody.outerHeight(!0)-1}),this.initFixedColumnsEvents()}}},{key:'initBody',value:function(){for(var a,b=arguments.length,c=Array(b),d=0;d'),this.$fixedBody.append(this.$tableBody.find('>table').clone(!0)),this.$tableBody.after(this.$fixedBody)}},{key:'getFixedColumnsWidth',value:function(){for(var a=this.getVisibleFields(),b=0,c=0;c tr[data-index]').off('hover').hover(function(b){var c=f(b.currentTarget).data('index');a.$fixedBody.find('tr[data-index="'+c+'"]').css('background-color',f(b.currentTarget).css('background-color'))},function(b){var c=f(b.currentTarget).data('index'),d=a.$fixedBody.find('tr[data-index="'+c+'"]');d.attr('style',d.attr('style').replace(/background-color:.*;/,''))}),this.$fixedBody.find('tr[data-index]').off('hover').hover(function(b){var c=f(b.currentTarget).data('index');a.$body.find('tr[data-index="'+c+'"]').css('background-color',f(b.currentTarget).css('background-color'))},function(b){var c=f(b.currentTarget).data('index'),d=a.$body.find('> tr[data-index="'+c+'"]');d.attr('style',d.attr('style').replace(/background-color:.*;/,''))})}}]),h}(f.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/group-by-v2/bootstrap-table-group-by.css: -------------------------------------------------------------------------------- 1 | .bootstrap-table .table > tbody > tr.groupBy { 2 | cursor: pointer; 3 | } 4 | 5 | .bootstrap-table .table > tbody > tr.groupBy.expanded { 6 | 7 | } 8 | 9 | .bootstrap-table .table > tbody > tr.hidden + tr.detail-view { 10 | display: none; 11 | } 12 | -------------------------------------------------------------------------------- /static/js/extensions/group-by-v2/bootstrap-table-group-by.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | .bootstrap-table .table>tbody>tr.groupBy{cursor:pointer}.bootstrap-table .table>tbody>tr.hidden+tr.detail-view{display:none} -------------------------------------------------------------------------------- /static/js/extensions/group-by-v2/bootstrap-table-group-by.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableGroupBy={exports:{}}.exports}})(this,function(){'use strict';(function(a){var b,c,d=function(a){var b=arguments,c=!0,d=1;return a=a.replace(/%s/g,function(){var a=b[d++];return'undefined'==typeof a?(c=!1,''):a}),c?a:''},e=function(a,b){var c={};return a.forEach(function(a){var d=b(a);c[d]=c[d]||[],c[d].push(a)}),c};a.extend(a.fn.bootstrapTable.defaults,{groupBy:!1,groupByField:'',groupByFormatter:void 0});var f=a.fn.bootstrapTable.Constructor,g=f.prototype.initSort,h=f.prototype.initBody,i=f.prototype.updateSelected;f.prototype.initSort=function(){g.apply(this,Array.prototype.slice.apply(arguments));var d=this;if(c=[],this.options.groupBy&&''!==this.options.groupByField){this.options.sortName!=this.options.groupByField&&this.data.sort(function(c,a){return c[d.options.groupByField].localeCompare(a[d.options.groupByField])});var d=this,b=e(d.data,function(a){return[a[d.options.groupByField]]}),f=0;a.each(b,function(a,b){c.push({id:f,name:a,data:b}),b.forEach(function(a){a._data||(a._data={}),a._data['parent-index']=f}),f++})}},f.prototype.initBody=function(){if(b=!0,h.apply(this,Array.prototype.slice.apply(arguments)),this.options.groupBy&&''!==this.options.groupByField){var e=this,f=!1,g=0;this.columns.forEach(function(a){a.checkbox?f=!0:a.visible&&(g+=1)}),this.options.detailView&&!this.options.cardView&&(g+=1),c.forEach(function(b){var c=[];c.push(d('',b.id)),e.options.detailView&&!e.options.cardView&&c.push(''),f&&c.push('','','');var h=b.name;'function'==typeof e.options.groupByFormatter&&(h=e.options.groupByFormatter(b.name,b.id,b.data)),c.push('',h,''),c.push(''),e.$body.find('tr[data-parent-index='+b.id+']:first').before(a(c.join('')))}),this.$selectGroup=[],this.$body.find('[name="btSelectGroup"]').each(function(){var b=a(this);e.$selectGroup.push({group:b,item:e.$selectItem.filter(function(){return a(this).closest('tr').data('parent-index')===b.closest('tr').data('group-index')})})}),this.$container.off('click','.groupBy').on('click','.groupBy',function(){a(this).toggleClass('expanded'),e.$body.find('tr[data-parent-index='+a(this).closest('tr').data('group-index')+']').toggleClass('hidden')}),this.$container.off('click','[name="btSelectGroup"]').on('click','[name="btSelectGroup"]',function(b){b.stopImmediatePropagation();var c=a(this),d=c.prop('checked');e[d?'checkGroup':'uncheckGroup'](a(this).closest('tr').data('group-index'))})}b=!1,this.updateSelected()},f.prototype.updateSelected=function(){b||(i.apply(this,Array.prototype.slice.apply(arguments)),this.options.groupBy&&''!==this.options.groupByField&&this.$selectGroup.forEach(function(a){var b=a.item.filter(':enabled').length===a.item.filter(':enabled').filter(':checked').length;a.group.prop('checked',b)}))},f.prototype.getGroupSelections=function(b){var c=this;return a.grep(this.data,function(a){return a[c.header.stateField]&&a._data['parent-index']===b})},f.prototype.checkGroup=function(a){this.checkGroup_(a,!0)},f.prototype.uncheckGroup=function(a){this.checkGroup_(a,!1)},f.prototype.checkGroup_=function(b,c){var d;c||(d=this.getGroupSelections(b)),this.$selectItem.filter(function filter(){return a(this).closest('tr').data('parent-index')===b}).prop('checked',c),this.updateRows(),this.updateSelected(),c&&(d=this.getGroupSelections(b)),this.trigger(c?'check-all':'uncheck-all',d)}})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/group-by/bootstrap-table-group-by.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableGroupBy={exports:{}}.exports}})(this,function(){'use strict';!function(a){var b,c='data-tt-parent-id',d={},e=void 0,f=function(b,c){for(var d=b.$body.find('tr').not('[data-tt-parent-id]'),e=0;e (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableI18nEnhance={exports:{}}.exports}})(this,function(){'use strict';!function(a){var b=a.fn.bootstrapTable.Constructor;b.prototype.changeTitle=function(b){a.each(this.options.columns,function(c,d){a.each(d,function(a,c){c.field&&(c.title=b[c.field])})}),this.initHeader(),this.initBody(),this.initToolbar()},b.prototype.changeLocale=function(a){this.options.locale=a,this.initLocale(),this.initPagination(),this.initBody(),this.initToolbar()},a.fn.bootstrapTable.methods.push('changeTitle'),a.fn.bootstrapTable.methods.push('changeLocale')}(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/key-events/bootstrap-table-key-events.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.bootstrapTableKeyEvents = mod.exports; 12 | } 13 | })(this, function () { 14 | 'use strict'; 15 | 16 | /** 17 | * @author: Dennis Hernández 18 | * @webSite: http://djhvscf.github.io/Blog 19 | * @version: v1.0.0 20 | * 21 | * @update zhixin wen 22 | */ 23 | 24 | !function ($) { 25 | 26 | 'use strict'; 27 | 28 | $.extend($.fn.bootstrapTable.defaults, { 29 | keyEvents: false 30 | }); 31 | 32 | var BootstrapTable = $.fn.bootstrapTable.Constructor, 33 | _init = BootstrapTable.prototype.init; 34 | 35 | BootstrapTable.prototype.init = function () { 36 | _init.apply(this, Array.prototype.slice.apply(arguments)); 37 | this.initKeyEvents(); 38 | }; 39 | 40 | BootstrapTable.prototype.initKeyEvents = function () { 41 | if (this.options.keyEvents) { 42 | var that = this; 43 | 44 | $(document).off('keydown').on('keydown', function (e) { 45 | var $search = that.$toolbar.find('.search input'), 46 | $refresh = that.$toolbar.find('button[name="refresh"]'), 47 | $toggle = that.$toolbar.find('button[name="toggle"]'), 48 | $paginationSwitch = that.$toolbar.find('button[name="paginationSwitch"]'); 49 | 50 | if (document.activeElement === $search.get(0) || !$.contains(document.activeElement, that.$toolbar.get(0))) { 51 | return true; 52 | } 53 | 54 | switch (e.keyCode) { 55 | case 83: 56 | //s 57 | if (!that.options.search) { 58 | return; 59 | } 60 | $search.focus(); 61 | return false; 62 | case 82: 63 | //r 64 | if (!that.options.showRefresh) { 65 | return; 66 | } 67 | $refresh.click(); 68 | return false; 69 | case 84: 70 | //t 71 | if (!that.options.showToggle) { 72 | return; 73 | } 74 | $toggle.click(); 75 | return false; 76 | case 80: 77 | //p 78 | if (!that.options.showPaginationSwitch) { 79 | return; 80 | } 81 | $paginationSwitch.click(); 82 | return false; 83 | case 37: 84 | // left 85 | if (!that.options.pagination) { 86 | return; 87 | } 88 | that.prevPage(); 89 | return false; 90 | case 39: 91 | // right 92 | if (!that.options.pagination) { 93 | return; 94 | } 95 | that.nextPage(); 96 | return; 97 | } 98 | }); 99 | } 100 | }; 101 | }(jQuery); 102 | }); -------------------------------------------------------------------------------- /static/js/extensions/key-events/bootstrap-table-key-events.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableKeyEvents={exports:{}}.exports}})(this,function(){'use strict';!function(a){a.extend(a.fn.bootstrapTable.defaults,{keyEvents:!1});var b=a.fn.bootstrapTable.Constructor,c=b.prototype.init;b.prototype.init=function(){c.apply(this,Array.prototype.slice.apply(arguments)),this.initKeyEvents()},b.prototype.initKeyEvents=function(){if(this.options.keyEvents){var b=this;a(document).off('keydown').on('keydown',function(c){var d=b.$toolbar.find('.search input'),e=b.$toolbar.find('button[name="refresh"]'),f=b.$toolbar.find('button[name="toggle"]'),g=b.$toolbar.find('button[name="paginationSwitch"]');if(document.activeElement===d.get(0)||!a.contains(document.activeElement,b.$toolbar.get(0)))return!0;switch(c.keyCode){case 83:return b.options.search?(d.focus(),!1):void 0;case 82:return b.options.showRefresh?(e.click(),!1):void 0;case 84:return b.options.showToggle?(f.click(),!1):void 0;case 80:return b.options.showPaginationSwitch?(g.click(),!1):void 0;case 37:return b.options.pagination?(b.prevPage(),!1):void 0;case 39:return b.options.pagination?void b.nextPage():void 0;}})}}}(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/mobile/bootstrap-table-mobile.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableMobile={exports:{}}.exports}})(this,function(){'use strict';!function(a){var b=function(b,c){0a.options.minWidth&&d>a.options.minHeight&&f(a):b<=a.options.minWidth?e(a):b>a.options.minWidth&&f(a),c(a)},e=function(a){g(a,!1),b(a,!1)},f=function(a){g(a,!0),b(a,!0)},g=function(a,b){a.options.cardView=b,a.toggleView()},h=function(a,b){var c;return function(){var d=this,e=arguments;clearTimeout(c),c=setTimeout(function later(){c=null,a.apply(d,e)},b)}};a.extend(a.fn.bootstrapTable.defaults,{mobileResponsive:!1,minWidth:562,minHeight:void 0,heightThreshold:100,checkOnInit:!0,columnsHidden:[]});var i=a.fn.bootstrapTable.Constructor,j=i.prototype.init;i.prototype.init=function(){if((j.apply(this,Array.prototype.slice.apply(arguments)),!!this.options.mobileResponsive)&&this.options.minWidth){100>this.options.minWidth&&this.options.resizable&&(console.log('The minWidth when the resizable extension is active should be greater or equal than 100'),this.options.minWidth=100);var b=this,c={width:a(window).width(),height:a(window).height()};if(a(window).on('resize orientationchange',h(function(){var e=a(this).height(),f=a(this).width();(Math.abs(c.height-e)>b.options.heightThreshold||c.width!=f)&&(d(b,f,e),c={width:f,height:e})},200)),this.options.checkOnInit){var e=a(window).height(),f=a(window).width();d(this,f,e),c={width:f,height:e}}}}}(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/multi-column-toggle/bootstrap-table-multi-toggle.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.bootstrapTableMultiToggle = mod.exports; 12 | } 13 | })(this, function () { 14 | 'use strict'; 15 | 16 | /** 17 | * @author Homer Glascock 18 | * @version: v1.0.0 19 | */ 20 | 21 | !function ($) { 22 | "use strict"; 23 | 24 | var sprintf = $.fn.bootstrapTable.utils.sprintf; 25 | 26 | var reInit = function reInit(self) { 27 | self.initHeader(); 28 | self.initSearch(); 29 | self.initPagination(); 30 | self.initBody(); 31 | }; 32 | 33 | $.extend($.fn.bootstrapTable.defaults, { 34 | showToggleBtn: false, 35 | multiToggleDefaults: [] //column names go here 36 | }); 37 | 38 | $.fn.bootstrapTable.methods.push('hideAllColumns', 'showAllColumns'); 39 | 40 | var BootstrapTable = $.fn.bootstrapTable.Constructor, 41 | _initToolbar = BootstrapTable.prototype.initToolbar; 42 | 43 | BootstrapTable.prototype.initToolbar = function () { 44 | 45 | _initToolbar.apply(this, Array.prototype.slice.apply(arguments)); 46 | 47 | var that = this, 48 | $btnGroup = this.$toolbar.find('>.btn-group'); 49 | 50 | if (typeof this.options.multiToggleDefaults === 'string') { 51 | this.options.multiToggleDefaults = JSON.parse(this.options.multiToggleDefaults); 52 | } 53 | 54 | if (this.options.showToggleBtn && this.options.showColumns) { 55 | var showbtn = "", 56 | hidebtn = ""; 57 | 58 | $btnGroup.append(showbtn + hidebtn); 59 | 60 | $btnGroup.find('#showAllBtn').click(function () { 61 | that.showAllColumns(); 62 | $btnGroup.find('#hideAllBtn').toggleClass('hidden'); 63 | $btnGroup.find('#showAllBtn').toggleClass('hidden'); 64 | }); 65 | $btnGroup.find('#hideAllBtn').click(function () { 66 | that.hideAllColumns(); 67 | $btnGroup.find('#hideAllBtn').toggleClass('hidden'); 68 | $btnGroup.find('#showAllBtn').toggleClass('hidden'); 69 | }); 70 | } 71 | }; 72 | 73 | BootstrapTable.prototype.hideAllColumns = function () { 74 | var that = this, 75 | defaults = that.options.multiToggleDefaults; 76 | 77 | $.each(this.columns, function (index, column) { 78 | //if its one of the defaults dont touch it 79 | if (defaults.indexOf(column.field) == -1 && column.switchable) { 80 | column.visible = false; 81 | var $items = that.$toolbar.find('.keep-open input').prop('disabled', false); 82 | $items.filter(sprintf('[value="%s"]', index)).prop('checked', false); 83 | } 84 | }); 85 | 86 | reInit(that); 87 | }; 88 | 89 | BootstrapTable.prototype.showAllColumns = function () { 90 | var that = this; 91 | $.each(this.columns, function (index, column) { 92 | if (column.switchable) { 93 | column.visible = true; 94 | } 95 | 96 | var $items = that.$toolbar.find('.keep-open input').prop('disabled', false); 97 | $items.filter(sprintf('[value="%s"]', index)).prop('checked', true); 98 | }); 99 | 100 | reInit(that); 101 | 102 | that.toggleColumn(0, that.columns[0].visible, false); 103 | }; 104 | }(jQuery); 105 | }); -------------------------------------------------------------------------------- /static/js/extensions/multi-column-toggle/bootstrap-table-multi-toggle.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableMultiToggle={exports:{}}.exports}})(this,function(){'use strict';!function(a){var b=a.fn.bootstrapTable.utils.sprintf,c=function(a){a.initHeader(),a.initSearch(),a.initPagination(),a.initBody()};a.extend(a.fn.bootstrapTable.defaults,{showToggleBtn:!1,multiToggleDefaults:[]}),a.fn.bootstrapTable.methods.push('hideAllColumns','showAllColumns');var d=a.fn.bootstrapTable.Constructor,e=d.prototype.initToolbar;d.prototype.initToolbar=function(){e.apply(this,Array.prototype.slice.apply(arguments));var a=this,b=this.$toolbar.find('>.btn-group');if('string'==typeof this.options.multiToggleDefaults&&(this.options.multiToggleDefaults=JSON.parse(this.options.multiToggleDefaults)),this.options.showToggleBtn&&this.options.showColumns){b.append(''+''),b.find('#showAllBtn').click(function(){a.showAllColumns(),b.find('#hideAllBtn').toggleClass('hidden'),b.find('#showAllBtn').toggleClass('hidden')}),b.find('#hideAllBtn').click(function(){a.hideAllColumns(),b.find('#hideAllBtn').toggleClass('hidden'),b.find('#showAllBtn').toggleClass('hidden')})}},d.prototype.hideAllColumns=function(){var d=this,e=d.options.multiToggleDefaults;a.each(this.columns,function(a,c){if(-1==e.indexOf(c.field)&&c.switchable){c.visible=!1;var f=d.$toolbar.find('.keep-open input').prop('disabled',!1);f.filter(b('[value="%s"]',a)).prop('checked',!1)}}),c(d)},d.prototype.showAllColumns=function(){var d=this;a.each(this.columns,function(a,c){c.switchable&&(c.visible=!0);var e=d.$toolbar.find('.keep-open input').prop('disabled',!1);e.filter(b('[value="%s"]',a)).prop('checked',!0)}),c(d),d.toggleColumn(0,d.columns[0].visible,!1)}}(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/multiple-search/bootstrap-table-multiple-search.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.bootstrapTableMultipleSearch = mod.exports; 12 | } 13 | })(this, function () { 14 | 'use strict'; 15 | 16 | /** 17 | * @author: Dennis Hernández 18 | * @webSite: http://djhvscf.github.io/Blog 19 | * @version: v1.0.0 20 | */ 21 | 22 | !function ($) { 23 | 24 | 'use strict'; 25 | 26 | $.extend($.fn.bootstrapTable.defaults, { 27 | multipleSearch: false, 28 | delimeter: " " 29 | }); 30 | 31 | var BootstrapTable = $.fn.bootstrapTable.Constructor, 32 | _initSearch = BootstrapTable.prototype.initSearch; 33 | 34 | BootstrapTable.prototype.initSearch = function () { 35 | if (this.options.multipleSearch) { 36 | if (this.searchText === undefined) { 37 | return; 38 | } 39 | var strArray = this.searchText.split(this.options.delimeter), 40 | that = this, 41 | f = $.isEmptyObject(this.filterColumns) ? null : this.filterColumns, 42 | dataFiltered = []; 43 | 44 | if (strArray.length === 1) { 45 | _initSearch.apply(this, Array.prototype.slice.apply(arguments)); 46 | } else { 47 | for (var i = 0; i < strArray.length; i++) { 48 | var str = strArray[i].trim(); 49 | dataFiltered = str ? $.grep(dataFiltered.length === 0 ? this.options.data : dataFiltered, function (item, i) { 50 | for (var key in item) { 51 | key = $.isNumeric(key) ? parseInt(key, 10) : key; 52 | var value = item[key], 53 | column = that.columns[that.fieldsColumnsIndex[key]], 54 | j = $.inArray(key, that.header.fields); 55 | 56 | // Fix #142: search use formated data 57 | if (column && column.searchFormatter) { 58 | value = $.fn.bootstrapTable.utils.calculateObjectValue(column, that.header.formatters[j], [value, item, i], value); 59 | } 60 | 61 | var index = $.inArray(key, that.header.fields); 62 | if (index !== -1 && that.header.searchables[index] && (typeof value === 'string' || typeof value === 'number')) { 63 | if (that.options.strictSearch) { 64 | if ((value + '').toLowerCase() === str) { 65 | return true; 66 | } 67 | } else { 68 | if ((value + '').toLowerCase().indexOf(str) !== -1) { 69 | return true; 70 | } 71 | } 72 | } 73 | } 74 | return false; 75 | }) : this.data; 76 | } 77 | 78 | this.data = dataFiltered; 79 | } 80 | } else { 81 | _initSearch.apply(this, Array.prototype.slice.apply(arguments)); 82 | } 83 | }; 84 | }(jQuery); 85 | }); -------------------------------------------------------------------------------- /static/js/extensions/multiple-search/bootstrap-table-multiple-search.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableMultipleSearch={exports:{}}.exports}})(this,function(){'use strict';!function(a){a.extend(a.fn.bootstrapTable.defaults,{multipleSearch:!1,delimeter:' '});var b=a.fn.bootstrapTable.Constructor,c=b.prototype.initSearch;b.prototype.initSearch=function(){if(this.options.multipleSearch){if(this.searchText===void 0)return;var b=this.searchText.split(this.options.delimeter),d=this,e=a.isEmptyObject(this.filterColumns)?null:this.filterColumns,f=[];if(1===b.length)c.apply(this,Array.prototype.slice.apply(arguments));else{for(var g,h=0;h (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | .multiple-select-row-selected{background:lightBlue}.table tbody tr:hover td,.table tbody tr:hover th{background-color:transparent}.table-striped tbody tr:nth-child(odd):hover td{background-color:#f9f9f9}.fixed-table-container tbody .selected td{background:lightBlue} -------------------------------------------------------------------------------- /static/js/extensions/multiple-selection-row/bootstrap-table-multiple-selection-row.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.bootstrapTableMultipleSelectionRow={exports:{}}.exports}})(this,function(){"use strict";!function(a){document.onselectstart=function(){return!1};var b=function(b){return b=a(b),b.is("table")?b:b.parents().find(".table")},c=function(b){return b=a(b),b.parent().parent()},d=function(a){var c=b(a.currentTarget);window.event.ctrlKey&&f(a.currentTarget,c,!1,!1),0===window.event.button&&(!window.event.ctrlKey&&!window.event.shiftKey&&(h(c),f(a.currentTarget,c,!1,!1)),window.event.shiftKey&&g([c.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow.rowIndex,a.currentTarget.rowIndex],c))},e=function(a){var d=b(a.currentTarget);h(d),f(c(a.currentTarget),d,!1,!1)},f=function(b,c,d,e){d?(b=a(b),c.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow=void 0,b.removeClass(c.bootstrapTable("getOptions").multipleSelectRowCssClass),c.bootstrapTable("uncheck",b.data("index"))):(c.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow=b,b=a(b),e?(b.addClass(c.bootstrapTable("getOptions").multipleSelectRowCssClass),c.bootstrapTable("check",b.data("index"))):b.hasClass(c.bootstrapTable("getOptions").multipleSelectRowCssClass)?(b.removeClass(c.bootstrapTable("getOptions").multipleSelectRowCssClass),c.bootstrapTable("uncheck",b.data("index"))):(b.addClass(c.bootstrapTable("getOptions").multipleSelectRowCssClass),c.bootstrapTable("check",b.data("index"))))},g=function(a,b){a.sort(function(c,a){return c-a});for(var c=a[0];c<=a[1];c++)f(b.bootstrapTable("getOptions").multipleSelectRowRows[c-1],b,!1,!0)},h=function(a){for(var b=0;b 24 | * @update Duane May 25 | */ 26 | 27 | function alphanum(a, b) { 28 | function chunkify(t) { 29 | var tz = [], 30 | x = 0, 31 | y = -1, 32 | n = 0, 33 | i, 34 | j; 35 | 36 | while (i = (j = t.charAt(x++)).charCodeAt(0)) { 37 | var m = i === 46 || i >= 48 && i <= 57; 38 | if (m !== n) { 39 | tz[++y] = ""; 40 | n = m; 41 | } 42 | tz[y] += j; 43 | } 44 | return tz; 45 | } 46 | 47 | function stringfy(v) { 48 | if (typeof v === "number") { 49 | v = "" + v; 50 | } 51 | if (!v) { 52 | v = ""; 53 | } 54 | return v; 55 | } 56 | 57 | var aa = chunkify(stringfy(a)); 58 | var bb = chunkify(stringfy(b)); 59 | 60 | for (x = 0; aa[x] && bb[x]; x++) { 61 | if (aa[x] !== bb[x]) { 62 | var c = Number(aa[x]), 63 | d = Number(bb[x]); 64 | 65 | if (c == aa[x] && d == bb[x]) { 66 | return c - d; 67 | } else { 68 | return aa[x] > bb[x] ? 1 : -1; 69 | } 70 | } 71 | } 72 | return aa.length - bb.length; 73 | } 74 | 75 | function numericOnly(a, b) { 76 | function stripNonNumber(s) { 77 | s = s.replace(new RegExp(/[^0-9]/g), ""); 78 | return parseInt(s, 10); 79 | } 80 | 81 | return stripNonNumber(a) - stripNonNumber(b); 82 | } 83 | }); -------------------------------------------------------------------------------- /static/js/extensions/natural-sorting/bootstrap-table-natural-sorting.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.bootstrapTableNaturalSorting={exports:{}}.exports}})(this,function(){"use strict"}); -------------------------------------------------------------------------------- /static/js/extensions/page-jump-to/bootstrap-table-page-jump-to.css: -------------------------------------------------------------------------------- 1 | .jumpto input { 2 | height: 31px; 3 | width: 50px; 4 | margin-left: 5px; 5 | margin-right: 5px; 6 | text-align: center; 7 | display: inline-block; 8 | } -------------------------------------------------------------------------------- /static/js/extensions/page-jump-to/bootstrap-table-page-jump-to.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.bootstrapTablePageJumpTo = mod.exports; 12 | } 13 | })(this, function () { 14 | 'use strict'; 15 | 16 | /** 17 | * @author Jay 18 | */ 19 | 20 | (function ($) { 21 | 'use strict'; 22 | 23 | var sprintf = $.fn.bootstrapTable.utils.sprintf; 24 | 25 | $.extend($.fn.bootstrapTable.defaults, { 26 | showJumpto: false, 27 | exportOptions: {} 28 | }); 29 | 30 | $.extend($.fn.bootstrapTable.locales, { 31 | formatJumpto: function formatJumpto() { 32 | return 'GO'; 33 | } 34 | }); 35 | $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales); 36 | 37 | var BootstrapTable = $.fn.bootstrapTable.Constructor, 38 | _initPagination = BootstrapTable.prototype.initPagination; 39 | 40 | BootstrapTable.prototype.initPagination = function () { 41 | _initPagination.apply(this, Array.prototype.slice.apply(arguments)); 42 | 43 | if (this.options.showJumpto) { 44 | var that = this, 45 | $pageGroup = this.$pagination.find('ul.pagination'), 46 | $jumpto = $pageGroup.find('li.jumpto'); 47 | 48 | if (!$jumpto.length) { 49 | $jumpto = $(['
  • ', '', '', '
  • '].join('')).appendTo($pageGroup); 50 | 51 | $jumpto.find('button').click(function () { 52 | that.selectPage(parseInt($jumpto.find('input').val())); 53 | }); 54 | } 55 | } 56 | }; 57 | })(jQuery); 58 | }); -------------------------------------------------------------------------------- /static/js/extensions/page-jump-to/bootstrap-table-page-jump-to.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | .jumpto input{height:31px;width:50px;margin-left:5px;margin-right:5px;text-align:center;display:inline-block} -------------------------------------------------------------------------------- /static/js/extensions/page-jump-to/bootstrap-table-page-jump-to.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTablePageJumpTo={exports:{}}.exports}})(this,function(){'use strict';(function(a){var b=a.fn.bootstrapTable.utils.sprintf;a.extend(a.fn.bootstrapTable.defaults,{showJumpto:!1,exportOptions:{}}),a.extend(a.fn.bootstrapTable.locales,{formatJumpto:function(){return'GO'}}),a.extend(a.fn.bootstrapTable.defaults,a.fn.bootstrapTable.locales);var c=a.fn.bootstrapTable.Constructor,d=c.prototype.initPagination;c.prototype.initPagination=function(){if(d.apply(this,Array.prototype.slice.apply(arguments)),this.options.showJumpto){var c=this,e=this.$pagination.find('ul.pagination'),f=e.find('li.jumpto');f.length||(f=a(['
  • ','','','
  • '].join('')).appendTo(e),f.find('button').click(function(){c.selectPage(parseInt(f.find('input').val()))}))}}})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/pipeline/bootstrap-table-pipeline.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTablePipeline={exports:{}}.exports}})(this,function(){'use strict';(function(a){var b=a.fn.bootstrapTable.utils;a.extend(a.fn.bootstrapTable.defaults,{usePipeline:!1,pipelineSize:1e3,onCachedDataHit:function(){return!1},onCachedDataReset:function(){return!1}}),a.extend(a.fn.bootstrapTable.Constructor.EVENTS,{"cached-data-hit.bs.table":'onCachedDataHit',"cached-data-reset.bs.table":'onCachedDataReset'});var c=a.fn.bootstrapTable.Constructor,d=c.prototype.init,e=c.prototype.initServer,f=c.prototype.onSearch,g=c.prototype.onSort,h=c.prototype.onPageListChange;c.prototype.init=function(){this.initPipeline(),d.apply(this,Array.prototype.slice.apply(arguments))},c.prototype.initPipeline=function(){this.cacheRequestJSON={},this.cacheWindows=[],this.currWindow=0,this.resetCache=!0},c.prototype.onSearch=function(){this.options.usePipeline&&(this.resetCache=!0),f.apply(this,Array.prototype.slice.apply(arguments))},c.prototype.onSort=function(){this.options.usePipeline&&(this.resetCache=!0),g.apply(this,Array.prototype.slice.apply(arguments))},c.prototype.onPageListChange=function(b){var c=a(b.currentTarget),d=parseInt(c.text());this.options.pipelineSize=this.calculatePipelineSize(this.options.pipelineSize,d),this.resetCache=!0,h.apply(this,Array.prototype.slice.apply(arguments))},c.prototype.calculatePipelineSize=function(a,b){return 0==b?0:Math.ceil(a/b)*b},c.prototype.setCacheWindows=function(){this.cacheWindows=[];for(var a,c=this.options.totalRows/this.options.pipelineSize,d=0;d<=c;d++)a=d*this.options.pipelineSize,this.cacheWindows[d]={lower:a,upper:a+this.options.pipelineSize-1}},c.prototype.setCurrWindow=function(a){this.currWindow=0;for(var b=0;bk.upper?(j=!0,this.setCurrWindow(h.offset),h.drawOffset=h.offset,h.offset=this.cacheWindows[this.currWindow].lower):j=!1}if(this.resetCache&&(j=!0,this.resetCache=!1),this.options.usePipeline&&j&&(h.drawLimit=h.limit,h.limit=this.options.pipelineSize),!j){var l=this.drawFromCache(h.offset,h.limit);return this.load(l),this.trigger('load-success',l),void this.trigger('cached-data-hit',l)}if(a.isEmptyObject(this.filterColumnsPartial)||(h.filter=JSON.stringify(this.filterColumnsPartial,null)),f=b.calculateObjectValue(this.options,this.options.queryParams,[h],f),a.extend(f,d||{}),!1!==f){c||this.$tableLoading.show();var m=this;i=a.extend({},b.calculateObjectValue(null,this.options.ajaxOptions),{type:this.options.method,url:e||this.options.url,data:'application/json'===this.options.contentType&&'post'===this.options.method?JSON.stringify(f):f,cache:this.options.cache,contentType:this.options.contentType,dataType:this.options.dataType,success:function(d){d=b.calculateObjectValue(m.options,m.options.responseHandler,[d],d),m.options.usePipeline&&(m.cacheRequestJSON=a.extend(!0,{},d),m.options.totalRows=d[m.options.totalField],m.setCacheWindows(),m.setCurrWindow(h.drawOffset),d=m.drawFromCache(h.drawOffset,h.drawLimit),m.trigger('cached-data-reset',d)),m.load(d),m.trigger('load-success',d),c||m.$tableLoading.hide()},error:function(a){var b=[];'server'===m.options.sidePagination&&(b={},b[m.options.totalField]=0,b[m.options.dataField]=[]),m.load(b),m.trigger('load-error',a.status,a),c||m.$tableLoading.hide()}}),this.options.ajax?b.calculateObjectValue(this,this.options.ajax,[i],null):(this._xhr&&4!==this._xhr.readyState&&this._xhr.abort(),this._xhr=a.ajax(i))}}},a.fn.bootstrapTable.methods.push()})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/print/bootstrap-table-print.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTablePrint={exports:{}}.exports}})(this,function(){'use strict';(function(a){function b(a){return'Print Table

    Printed on: '+new Date+'

    '+a+'
    '}var c=a.fn.bootstrapTable.utils.sprintf;a.extend(a.fn.bootstrapTable.defaults,{showPrint:!1,printAsFilteredAndSortedOnUI:!0,printSortColumn:void 0,printSortOrder:'asc',printPageBuilder:function(a){return b(a)}}),a.extend(a.fn.bootstrapTable.COLUMN_DEFAULTS,{printFilter:void 0,printIgnore:!1,printFormatter:void 0}),a.extend(a.fn.bootstrapTable.defaults.icons,{print:'glyphicon-print icon-share'});var d=a.fn.bootstrapTable.Constructor,e=d.prototype.initToolbar;d.prototype.initToolbar=function(){if(this.showToolbar=this.showToolbar||this.options.showPrint,e.apply(this,Array.prototype.slice.apply(arguments)),this.options.showPrint){var b=this,d=this.$toolbar.find('>.btn-group'),f=d.find('button.bs-print');f.length||(f=a([''].join('')).appendTo(d),f.click(function(){function a(a,b,c){var d=a[c.field];return'function'==typeof c.printFormatter?c.printFormatter.apply(c,[d,a,b]):'undefined'==typeof d?'-':d}function d(b,d){for(var e,f=[''],g=0;g');for(var k=0;k%s',e[k].title));f.push('')}f.push('');for(var h=0;h');for(var e,i=0;i',a(b[h],h,e[l]),'')}f.push('')}return f.push('
    '),f.join('')}function e(a,c,b){if(!c)return a;var d='asc'!=b;return d=-(+d||-1),a.sort(function(e,a){return d*e[c].localeCompare(a[c])})}function f(a,b){for(var c=0;c (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableReorderColumns={exports:{}}.exports}})(this,function(){'use strict';!function(a){var b=function(){Array.prototype.filter||(Array.prototype.filter=function(a){if(void 0===this||null===this)throw new TypeError;var b=Object(this),c=b.length>>>0;if('function'!=typeof a)throw new TypeError;for(var d=[],e=2<=arguments.length?arguments[1]:void 0,f=0;f (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | .reorder_rows_onDragClass td{background-color:#eee;-webkit-box-shadow:11px 5px 12px 2px #333,0 1px 0 #ccc inset,0 -1px 0 #ccc inset;-webkit-box-shadow:6px 3px 5px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset;-moz-box-shadow:6px 4px 5px 1px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset;-box-shadow:6px 4px 5px 1px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset}.reorder_rows_onDragClass td:last-child{-webkit-box-shadow:8px 7px 12px 0 #333,0 1px 0 #ccc inset,0 -1px 0 #ccc inset;-webkit-box-shadow:1px 8px 6px -4px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset;-moz-box-shadow:0 9px 4px -4px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset,-1px 0 0 #ccc inset;-box-shadow:0 9px 4px -4px #555,0 1px 0 #ccc inset,0 -1px 0 #ccc inset,-1px 0 0 #ccc inset} -------------------------------------------------------------------------------- /static/js/extensions/reorder-rows/bootstrap-table-reorder-rows.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableReorderRows={exports:{}}.exports}})(this,function(){'use strict';(function(a){var b=function(a,b){return{id:'customId_'+b}};a.extend(a.fn.bootstrapTable.defaults,{reorderableRows:!1,onDragStyle:null,onDropStyle:null,onDragClass:'reorder_rows_onDragClass',dragHandle:null,useRowAttrFunc:!1,onReorderRowsDrag:function(){return!1},onReorderRowsDrop:function(){return!1},onReorderRow:function(){return!1}}),a.extend(a.fn.bootstrapTable.Constructor.EVENTS,{"reorder-row.bs.table":'onReorderRow'});var c=a.fn.bootstrapTable.Constructor,d=c.prototype.init,e=c.prototype.initSearch;c.prototype.init=function(){if(!this.options.reorderableRows)return void d.apply(this,Array.prototype.slice.apply(arguments));var a=this;this.options.useRowAttrFunc&&(this.options.rowAttributes=b);var c=this.options.onPostBody;this.options.onPostBody=function(){setTimeout(function(){a.makeRowsReorderable(),c.apply()},1)},d.apply(this,Array.prototype.slice.apply(arguments))},c.prototype.initSearch=function(){e.apply(this,Array.prototype.slice.apply(arguments));!this.options.reorderableRows},c.prototype.makeRowsReorderable=function(){if(!this.options.cardView){var a=this;this.$el.tableDnD({onDragStyle:a.options.onDragStyle,onDropStyle:a.options.onDropStyle,onDragClass:a.options.onDragClass,onDrop:a.onDrop,onDragStart:a.options.onReorderRowsDrag,dragHandle:a.options.dragHandle})}},c.prototype.onDrop=function(b,c){for(var d=a(b),e=d.data('bootstrap.table'),f=d.data('bootstrap.table').options,g=null,h=[],j=0;j (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if("function"==typeof define&&define.amd)define([],b);else if("undefined"!=typeof exports)b();else{b(),a.bootstrapTableResizable={exports:{}}.exports}})(this,function(){"use strict";(function(a){var b=function(a){!a.options.resizable||a.options.cardView||e(a)||a.$el.resizableColumns()},c=function(a){d(a),b(a)},d=function(a){e(a)&&a.$el.data("resizableColumns").destroy()},e=function(a){return a.$el.data("resizableColumns")!==void 0};a.extend(a.fn.bootstrapTable.defaults,{resizable:!1});var f=a.fn.bootstrapTable.Constructor,g=f.prototype.initBody,h=f.prototype.toggleView,i=f.prototype.resetView;f.prototype.initBody=function(){var a=this;g.apply(this,Array.prototype.slice.apply(arguments)),a.$el.off("column-switch.bs.table, page-change.bs.table").on("column-switch.bs.table, page-change.bs.table",function(){c(a)})},f.prototype.toggleView=function(){h.apply(this,Array.prototype.slice.apply(arguments)),this.options.resizable&&this.options.cardView&&d(this)},f.prototype.resetView=function(){var a=this;i.apply(this,Array.prototype.slice.apply(arguments)),this.options.resizable&&setTimeout(function(){b(a)},100)}})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/sticky-header/bootstrap-table-sticky-header.css: -------------------------------------------------------------------------------- 1 | /** 2 | * @author vincent loh 3 | * @update zhixin wen 4 | */ 5 | 6 | .fix-sticky { 7 | position: fixed !important; 8 | overflow: hidden; 9 | z-index: 100; 10 | } 11 | 12 | .fix-sticky table thead { 13 | background: #fff; 14 | } 15 | 16 | .fix-sticky table thead.thead-light { 17 | background: #e9ecef; 18 | } 19 | 20 | .fix-sticky table thead.thead-light { 21 | background: #212529; 22 | } 23 | -------------------------------------------------------------------------------- /static/js/extensions/sticky-header/bootstrap-table-sticky-header.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | .fix-sticky{position:fixed!important;overflow:hidden;z-index:100}.fix-sticky table thead{background:#fff}.fix-sticky table thead.thead-light{background:#e9ecef}.fix-sticky table thead.thead-light{background:#212529} -------------------------------------------------------------------------------- /static/js/extensions/sticky-header/bootstrap-table-sticky-header.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableStickyHeader={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d'),this.$el.before('
    '),this.$el.after('
    '),this.$header.addClass('sticky-header'),this.$stickyContainer=this.$tableBody.find('.sticky-header-container'),this.$stickyBegin=this.$tableBody.find('.sticky_anchor_begin'),this.$stickyEnd=this.$tableBody.find('.sticky_anchor_end'),this.$stickyHeader=this.$header.clone(!0,!0),f(window).on('resize.sticky-header-table',function(){return b.renderStickyHeader()}),f(window).on('scroll.sticky-header-table',function(){return b.renderStickyHeader()}),this.$tableBody.off('scroll').on('scroll',function(){return b.matchPositionX()}))}},{key:'renderStickyHeader',value:function(){var a=this,b=f(window).scrollTop(),c=this.$stickyBegin.offset().top-this.options.stickyHeaderOffsetY,d=this.$stickyEnd.offset().top-this.options.stickyHeaderOffsetY-this.$header.height();b>c&&b<=d?(this.$stickyHeader.find('tr:eq(0)').find('th').each(function(b,c){f(c).css('min-width',a.$header.find('tr:eq(0)').find('th').eq(b).css('width'))}),this.$stickyContainer.removeClass(h).addClass('fix-sticky fixed-table-container'),this.$stickyContainer.css('top',this.options.stickyHeaderOffsetY+'px'),this.$stickyTable=f(''),this.$stickyTable.addClass(this.options.classes),this.$stickyContainer.html(this.$stickyTable.append(this.$stickyHeader)),this.matchPositionX()):this.$stickyContainer.removeClass('fix-sticky').addClass(h)}},{key:'matchPositionX',value:function(){this.$stickyContainer.scrollLeft(this.$tableBody.scrollLeft())}}]),i}(f.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/tree-column/bootstrap-table-tree-column.css: -------------------------------------------------------------------------------- 1 | .table:not(.table-condensed)>tbody>tr>td.treenode{padding-top:0;padding-bottom:0;border-bottom:solid #fff 1px}.table:not(.table-condensed)>tbody>tr:last-child>td.treenode{border-bottom:none}.treenode .text{float:left;display:block;padding-top:6px;padding-bottom:6px}.treenode .vertical,.treenode .vertical.last{float:left;display:block;width:1px;border-left:dashed silver 1px;height:38px;margin-left:8px}.treenode .vertical.last{height:15px}.treenode .space,.treenode .node{float:left;display:block;width:15px;height:5px;margin-top:15px}.treenode .node{border-top:dashed silver 1px} -------------------------------------------------------------------------------- /static/js/extensions/tree-column/bootstrap-table-tree-column.min.css: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | .table:not(.table-condensed)>tbody>tr>td.treenode{padding-top:0;padding-bottom:0;border-bottom:solid #fff 1px}.table:not(.table-condensed)>tbody>tr:last-child>td.treenode{border-bottom:0}.treenode .text{float:left;display:block;padding-top:6px;padding-bottom:6px}.treenode .vertical,.treenode .vertical.last{float:left;display:block;width:1px;border-left:dashed silver 1px;height:38px;margin-left:8px}.treenode .vertical.last{height:15px}.treenode .space,.treenode .node{float:left;display:block;width:15px;height:5px;margin-top:15px}.treenode .node{border-top:dashed silver 1px} -------------------------------------------------------------------------------- /static/js/extensions/tree-column/bootstrap-table-tree-column.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableTreeColumn={exports:{}}.exports}})(this,function(){'use strict';!function(a){a.extend(a.fn.bootstrapTable.defaults,{treeShowField:null,idField:'id',parentIdField:'pid',treeVerticalcls:'vertical',treeVerticalLastcls:'vertical last',treeSpacecls:'space',treeNodecls:'node',treeCellcls:'treenode',treeTextcls:'text',onTreeFormatter:function(a){for(var b=this,c=b.options,d=a._level||0,e=a._parent&&a._parent._level||0,f=[],g=0;g'),f.push('');for(var g=e;g'):f.push(''),f.push('');return f.join('')},onGetNodes:function(b,c){var d=this,e=[];return a.each(c,function(a,c){b[d.options.idField]===c[d.options.parentIdField]&&e.push(c)}),e},onCheckLeaf:function(a){return void 0===a.isLeaf?!a._nodes||!a._nodes.length:a.isLeaf},onCheckRoot:function(a){var b=this;return!a[b.options.parentIdField]}});var b=a.fn.bootstrapTable.Constructor,c=b.prototype.initRow,d=b.prototype.initHeader;b.prototype.initHeader=function(){var b=this;d.apply(b,Array.prototype.slice.apply(arguments));var c=b.options.treeShowField;c&&a.each(this.header.fields,function(a,d){if(c===d){b.treeEnable=!0;var e=b.header.formatters[a],f=[b.options.treeCellcls];return b.header.classes[a]&&f.push(b.header.classes[a].split('"')[1]||''),b.header.classes[a]=' class="'+f.join(' ')+'"',b.header.formatters[a]=function(a,c){var d=[b.options.onTreeFormatter.apply(b,[c])];return d.push(''),e?d.push(e.apply(this,Array.prototype.slice.apply(arguments))):d.push(a),d.push(''),d.join('')},!1}})};var e=function(b,d,f,g){var h=this,j=h.options.onGetNodes.apply(h,[b,f]);b._nodes=j,g.append(c.apply(h,[b,d,f,g]));for(var k,l=j.length-1,m=0;m<=l;m++)k=j[m],k._level=b._level+1,k._parent=b,m===l&&(k._last=1),e.apply(h,[k,a.inArray(k,f),f,g])};b.prototype.initRow=function(a,b,d,f){var g=this;return g.treeEnable?!!g.options.onCheckRoot.apply(g,[a,d])&&(void 0===a._level&&(a._level=0),e.apply(g,[a,b,d,f]),!0):c.apply(g,Array.prototype.slice.apply(arguments))}}(jQuery)}); -------------------------------------------------------------------------------- /static/js/extensions/treegrid/bootstrap-table-treegrid.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.bootstrapTableTreegrid = mod.exports; 12 | } 13 | })(this, function () { 14 | 'use strict'; 15 | 16 | /** 17 | * @author: YL 18 | * @version: v1.0.0 19 | */ 20 | !function ($) { 21 | 'use strict'; 22 | 23 | $.extend($.fn.bootstrapTable.defaults, { 24 | treeShowField: null, 25 | idField: 'id', 26 | parentIdField: 'pid', 27 | rootParentId: null, 28 | onGetNodes: function onGetNodes(row, data) { 29 | var that = this; 30 | var nodes = []; 31 | $.each(data, function (i, item) { 32 | if (row[that.options.idField] === item[that.options.parentIdField]) { 33 | nodes.push(item); 34 | } 35 | }); 36 | return nodes; 37 | }, 38 | onCheckRoot: function onCheckRoot(row, data) { 39 | var that = this; 40 | return that.options.rootParentId === row[that.options.parentIdField] || !row[that.options.parentIdField]; 41 | } 42 | }); 43 | 44 | var BootstrapTable = $.fn.bootstrapTable.Constructor, 45 | _init = BootstrapTable.prototype.init, 46 | _initRow = BootstrapTable.prototype.initRow, 47 | _initHeader = BootstrapTable.prototype.initHeader, 48 | _rowStyle = null; 49 | 50 | BootstrapTable.prototype.init = function () { 51 | _rowStyle = this.options.rowStyle; 52 | _init.apply(this, Array.prototype.slice.apply(arguments)); 53 | }; 54 | 55 | // td 56 | BootstrapTable.prototype.initHeader = function () { 57 | var that = this; 58 | _initHeader.apply(that, Array.prototype.slice.apply(arguments)); 59 | var treeShowField = that.options.treeShowField; 60 | if (treeShowField) { 61 | $.each(this.header.fields, function (i, field) { 62 | if (treeShowField === field) { 63 | that.treeEnable = true; 64 | return false; 65 | } 66 | }); 67 | } 68 | }; 69 | 70 | var initTr = function initTr(item, idx, data, parentDom) { 71 | var that = this; 72 | var nodes = that.options.onGetNodes.apply(that, [item, data]); 73 | item._nodes = nodes; 74 | parentDom.append(_initRow.apply(that, [item, idx, data, parentDom])); 75 | 76 | // init sub node 77 | var len = nodes.length - 1; 78 | for (var i = 0; i <= len; i++) { 79 | var node = nodes[i]; 80 | node._level = item._level + 1; 81 | node._parent = item; 82 | if (i === len) node._last = 1; 83 | // jquery.treegrid.js 84 | that.options.rowStyle = function (item, idx) { 85 | var res = _rowStyle.apply(that, Array.prototype.slice.apply(arguments)); 86 | var id = item[that.options.idField] ? item[that.options.idField] : 0; 87 | var pid = item[that.options.parentIdField] ? item[that.options.parentIdField] : 0; 88 | res.classes = [res.classes || '', 'treegrid-' + id, 'treegrid-parent-' + pid].join(' '); 89 | return res; 90 | }; 91 | initTr.apply(that, [node, $.inArray(node, data), data, parentDom]); 92 | } 93 | }; 94 | 95 | // tr 96 | BootstrapTable.prototype.initRow = function (item, idx, data, parentDom) { 97 | var that = this; 98 | if (that.treeEnable) { 99 | // init root node 100 | if (that.options.onCheckRoot.apply(that, [item, data])) { 101 | if (item._level === undefined) { 102 | item._level = 0; 103 | } 104 | // jquery.treegrid.js 105 | that.options.rowStyle = function (item, idx) { 106 | var res = _rowStyle.apply(that, Array.prototype.slice.apply(arguments)); 107 | var x = item[that.options.idField] ? item[that.options.idField] : 0; 108 | res.classes = [res.classes || '', 'treegrid-' + x].join(' '); 109 | return res; 110 | }; 111 | initTr.apply(that, [item, idx, data, parentDom]); 112 | return true; 113 | } 114 | return false; 115 | } 116 | return _initRow.apply(that, Array.prototype.slice.apply(arguments)); 117 | }; 118 | }(jQuery); 119 | }); -------------------------------------------------------------------------------- /static/js/extensions/treegrid/bootstrap-table-treegrid.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableTreegrid={exports:{}}.exports}})(this,function(){'use strict';!function(a){a.extend(a.fn.bootstrapTable.defaults,{treeShowField:null,idField:'id',parentIdField:'pid',rootParentId:null,onGetNodes:function(b,c){var d=this,e=[];return a.each(c,function(a,c){b[d.options.idField]===c[d.options.parentIdField]&&e.push(c)}),e},onCheckRoot:function(a){var b=this;return b.options.rootParentId===a[b.options.parentIdField]||!a[b.options.parentIdField]}});var b=a.fn.bootstrapTable.Constructor,c=b.prototype.init,d=b.prototype.initRow,e=b.prototype.initHeader,f=null;b.prototype.init=function(){f=this.options.rowStyle,c.apply(this,Array.prototype.slice.apply(arguments))},b.prototype.initHeader=function(){var b=this;e.apply(b,Array.prototype.slice.apply(arguments));var c=b.options.treeShowField;c&&a.each(this.header.fields,function(a,d){if(c===d)return b.treeEnable=!0,!1})};var g=function(b,c,e,h){var j=this,k=j.options.onGetNodes.apply(j,[b,e]);b._nodes=k,h.append(d.apply(j,[b,c,e,h]));for(var l,m=k.length-1,n=0;n<=m;n++)l=k[n],l._level=b._level+1,l._parent=b,n===m&&(l._last=1),j.options.rowStyle=function(a){var b=f.apply(j,Array.prototype.slice.apply(arguments)),c=a[j.options.idField]?a[j.options.idField]:0,d=a[j.options.parentIdField]?a[j.options.parentIdField]:0;return b.classes=[b.classes||'','treegrid-'+c,'treegrid-parent-'+d].join(' '),b},g.apply(j,[l,a.inArray(l,e),e,h])};b.prototype.initRow=function(a,b,c,e){var h=this;return h.treeEnable?!!h.options.onCheckRoot.apply(h,[a,c])&&(void 0===a._level&&(a._level=0),h.options.rowStyle=function(a){var b=f.apply(h,Array.prototype.slice.apply(arguments)),c=a[h.options.idField]?a[h.options.idField]:0;return b.classes=[b.classes||'','treegrid-'+c].join(' '),b},g.apply(h,[a,b,c,e]),!0):d.apply(h,Array.prototype.slice.apply(arguments))}}(jQuery)}); -------------------------------------------------------------------------------- /static/js/locale/bootstrap-table-zh-CN.js: -------------------------------------------------------------------------------- 1 | (function (global, factory) { 2 | if (typeof define === "function" && define.amd) { 3 | define([], factory); 4 | } else if (typeof exports !== "undefined") { 5 | factory(); 6 | } else { 7 | var mod = { 8 | exports: {} 9 | }; 10 | factory(); 11 | global.bootstrapTableZhCN = mod.exports; 12 | } 13 | })(this, function () { 14 | 'use strict'; 15 | 16 | /** 17 | * Bootstrap Table Chinese translation 18 | * Author: Zhixin Wen 19 | */ 20 | (function ($) { 21 | $.fn.bootstrapTable.locales['zh-CN'] = { 22 | formatLoadingMessage: function formatLoadingMessage() { 23 | return '正在努力地加载数据中,请稍候'; 24 | }, 25 | formatRecordsPerPage: function formatRecordsPerPage(pageNumber) { 26 | return '\u6BCF\u9875\u663E\u793A ' + pageNumber + ' \u6761\u8BB0\u5F55'; 27 | }, 28 | formatShowingRows: function formatShowingRows(pageFrom, pageTo, totalRows) { 29 | return '\u663E\u793A\u7B2C ' + pageFrom + ' \u5230\u7B2C ' + pageTo + ' \u6761\u8BB0\u5F55\uFF0C\u603B\u5171 ' + totalRows + ' \u6761\u8BB0\u5F55'; 30 | }, 31 | formatDetailPagination: function formatDetailPagination(totalRows) { 32 | return '\u603B\u5171 ' + totalRows + ' \u6761\u8BB0\u5F55'; 33 | }, 34 | formatSearch: function formatSearch() { 35 | return '搜索'; 36 | }, 37 | formatNoMatches: function formatNoMatches() { 38 | return '没有找到匹配的记录'; 39 | }, 40 | formatPaginationSwitch: function formatPaginationSwitch() { 41 | return '隐藏/显示分页'; 42 | }, 43 | formatRefresh: function formatRefresh() { 44 | return '刷新'; 45 | }, 46 | formatToggle: function formatToggle() { 47 | return '切换'; 48 | }, 49 | formatColumns: function formatColumns() { 50 | return '列'; 51 | }, 52 | formatFullscreen: function formatFullscreen() { 53 | return '全屏'; 54 | }, 55 | formatAllRows: function formatAllRows() { 56 | return '所有'; 57 | }, 58 | formatAutoRefresh: function formatAutoRefresh() { 59 | return '自动刷新'; 60 | }, 61 | formatExport: function formatExport() { 62 | return '导出数据'; 63 | }, 64 | formatClearFilters: function formatClearFilters() { 65 | return '清空过滤'; 66 | }, 67 | formatJumpto: function formatJumpto() { 68 | return '跳转'; 69 | }, 70 | formatAdvancedSearch: function formatAdvancedSearch() { 71 | return '高级搜索'; 72 | }, 73 | formatAdvancedCloseButton: function formatAdvancedCloseButton() { 74 | return '关闭'; 75 | } 76 | }; 77 | 78 | $.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales['zh-CN']); 79 | })(jQuery); 80 | }); -------------------------------------------------------------------------------- /static/js/locale/bootstrap-table-zh-CN.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableZhCN={exports:{}}.exports}})(this,function(){'use strict';(function(a){a.fn.bootstrapTable.locales['zh-CN']={formatLoadingMessage:function(){return'\u6B63\u5728\u52AA\u529B\u5730\u52A0\u8F7D\u6570\u636E\u4E2D\uFF0C\u8BF7\u7A0D\u5019'},formatRecordsPerPage:function(a){return'\u6BCF\u9875\u663E\u793A '+a+' \u6761\u8BB0\u5F55'},formatShowingRows:function(a,b,c){return'\u663E\u793A\u7B2C '+a+' \u5230\u7B2C '+b+' \u6761\u8BB0\u5F55\uFF0C\u603B\u5171 '+c+' \u6761\u8BB0\u5F55'},formatDetailPagination:function(a){return'\u603B\u5171 '+a+' \u6761\u8BB0\u5F55'},formatSearch:function(){return'\u641C\u7D22'},formatNoMatches:function(){return'\u6CA1\u6709\u627E\u5230\u5339\u914D\u7684\u8BB0\u5F55'},formatPaginationSwitch:function(){return'\u9690\u85CF/\u663E\u793A\u5206\u9875'},formatRefresh:function(){return'\u5237\u65B0'},formatToggle:function(){return'\u5207\u6362'},formatColumns:function(){return'\u5217'},formatFullscreen:function(){return'\u5168\u5C4F'},formatAllRows:function(){return'\u6240\u6709'},formatAutoRefresh:function(){return'\u81EA\u52A8\u5237\u65B0'},formatExport:function(){return'\u5BFC\u51FA\u6570\u636E'},formatClearFilters:function(){return'\u6E05\u7A7A\u8FC7\u6EE4'},formatJumpto:function(){return'\u8DF3\u8F6C'},formatAdvancedSearch:function(){return'\u9AD8\u7EA7\u641C\u7D22'},formatAdvancedCloseButton:function(){return'\u5173\u95ED'}},a.extend(a.fn.bootstrapTable.defaults,a.fn.bootstrapTable.locales['zh-CN'])})(jQuery)}); -------------------------------------------------------------------------------- /static/js/themes/bulma/bootstrap-table-bulma.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableBulma={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d'],this.constants.html.toobarDropdowItem='',this.constants.html.pageDropdown=[''],this.constants.html.pageDropdownItem='%s',this.constants.html.dropdownCaret='',this.constants.html.pagination=['
      ','
    '],this.constants.html.paginationItem='
  • %s
  • '}},{key:'initToolbar',value:function(){e(h.prototype.__proto__||Object.getPrototypeOf(h.prototype),'initToolbar',this).call(this),this.options.showColumns&&this._initDropdown()}},{key:'initPagination',value:function(){e(h.prototype.__proto__||Object.getPrototypeOf(h.prototype),'initPagination',this).call(this),this.options.pagination&&!this.options.onlyInfoPagination&&this._initDropdown()}},{key:'_initDropdown',value:function(){var a=this.$container.find('.dropdown:not(.is-hoverable)');a.off('click').on('click',function(a){a.stopPropagation(),f(a.currentTarget).toggleClass('is-active')}),f(document).off('click.bs.dropdown.bulma').on('click.bs.dropdown.bulma',function(){a.removeClass('is-active')})}}]),h}(f.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/js/themes/foundation/bootstrap-table-foundation.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableFoundation={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d'],this.constants.html.toobarDropdowItem='
  • ',this.constants.html.pageDropdown=[''],this.constants.html.pageDropdownItem='',this.constants.html.dropdownCaret='',this.constants.html.pagination=['
      ','
    '],this.constants.html.paginationItem='
  • %s
  • '}},{key:'initToolbar',value:function(){if(e(h.prototype.__proto__||Object.getPrototypeOf(h.prototype),'initToolbar',this).call(this),this.options.showColumns){this.$toolbar.find('.keep-open').attr('data-toggle','toolbar-dropdown');var a=this.$toolbar.find('.dropdown-pane').attr('data-position','bottom').attr('data-alignment','right');new window.Foundation.Dropdown(a),this._initDropdown()}}},{key:'initPagination',value:function(){if(e(h.prototype.__proto__||Object.getPrototypeOf(h.prototype),'initPagination',this).call(this),this.options.pagination&&!this.options.onlyInfoPagination){this.$pagination.find('.dropdown-toggle').attr('data-toggle','page-list-dropdown');var a=this.$pagination.find('.dropdown-pane').attr('data-position','top').attr('data-alignment','left');new window.Foundation.Dropdown(a)}}},{key:'_initDropdown',value:function(){var a=this.$container.find('.dropdown-container');a.off('click').on('click',function(b){b.stopPropagation(),a.find('.dropdown-pane').foundation('toggle')}),f(document).off('click.bs.dropdown.foundation').on('click.bs.dropdown.foundation',function(){a.find('.dropdown-pane').foundation('close')})}}]),h}(f.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/js/themes/materialize/bootstrap-table-materialize.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableMaterialize={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d',''],this.constants.html.toobarDropdowItem='
  • ',this.constants.html.pageDropdown=[''],this.constants.html.pageDropdownItem='
  • %s
  • ',this.constants.html.dropdownCaret='arrow_drop_down',this.constants.html.pagination=['
      ','
    '],this.constants.html.paginationItem='
  • %s
  • ',this.constants.html.icon='%s'}},{key:'initToolbar',value:function(){e(g.prototype.__proto__||Object.getPrototypeOf(g.prototype),'initToolbar',this).call(this),this.options.showColumns&&this.$toolbar.find('.dropdown-toggle').attr('data-target','toolbar-dropdown').dropdown({alignment:'right',constrainWidth:!1,closeOnClick:!1})}},{key:'initPagination',value:function(){e(g.prototype.__proto__||Object.getPrototypeOf(g.prototype),'initPagination',this).call(this),this.pagination&&!this.options.onlyInfoPagination&&this.$pagination.find('.dropdown-toggle').attr('data-target','page-list-dropdown').dropdown()}}]),g}(f.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/js/themes/semantic/bootstrap-table-semantic.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * bootstrap-table - An extended Bootstrap table with radio, checkbox, sort, pagination, and other added features. (supports twitter bootstrap v2 and v3). 3 | * 4 | * @version v1.14.2 5 | * @homepage https://bootstrap-table.com 6 | * @author wenzhixin (http://wenzhixin.net.cn/) 7 | * @license MIT 8 | */ 9 | 10 | (function(a,b){if('function'==typeof define&&define.amd)define([],b);else if('undefined'!=typeof exports)b();else{b(),a.bootstrapTableSemantic={exports:{}}.exports}})(this,function(){'use strict';function a(a,b){if(!(a instanceof b))throw new TypeError('Cannot call a class as a function')}function b(a,b){if(!a)throw new ReferenceError('this hasn\'t been initialised - super() hasn\'t been called');return b&&('object'==typeof b||'function'==typeof b)?b:a}function c(a,b){if('function'!=typeof b&&null!==b)throw new TypeError('Super expression must either be null or a function, not '+typeof b);a.prototype=Object.create(b&&b.prototype,{constructor:{value:a,enumerable:!1,writable:!0,configurable:!0}}),b&&(Object.setPrototypeOf?Object.setPrototypeOf(a,b):a.__proto__=b)}var d=function(){function a(a,b){for(var c,d=0;d',''],this.constants.html.toobarDropdowItem='',this.constants.html.pageDropdown=[''],this.constants.html.pageDropdownItem='%s',this.constants.html.dropdownCaret='',this.constants.html.pagination=[''],this.constants.html.paginationItem='%s'}},{key:'initToolbar',value:function(){e(g.prototype.__proto__||Object.getPrototypeOf(g.prototype),'initToolbar',this).call(this),this.options.showColumns&&this.$toolbar.find('.button.dropdown').dropdown()}},{key:'initPagination',value:function(){e(g.prototype.__proto__||Object.getPrototypeOf(g.prototype),'initPagination',this).call(this),this.options.pagination&&!this.options.onlyInfoPagination&&this.$pagination.find('.dropdown').dropdown()}}]),g}(f.BootstrapTable)})(jQuery)}); -------------------------------------------------------------------------------- /static/open-iconic/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /static/open-iconic/FONT-LICENSE: -------------------------------------------------------------------------------- 1 | SIL OPEN FONT LICENSE Version 1.1 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | PREAMBLE 6 | The goals of the Open Font License (OFL) are to stimulate worldwide 7 | development of collaborative font projects, to support the font creation 8 | efforts of academic and linguistic communities, and to provide a free and 9 | open framework in which fonts may be shared and improved in partnership 10 | with others. 11 | 12 | The OFL allows the licensed fonts to be used, studied, modified and 13 | redistributed freely as long as they are not sold by themselves. The 14 | fonts, including any derivative works, can be bundled, embedded, 15 | redistributed and/or sold with any software provided that any reserved 16 | names are not used by derivative works. The fonts and derivatives, 17 | however, cannot be released under any other type of license. The 18 | requirement for fonts to remain under this license does not apply 19 | to any document created using the fonts or their derivatives. 20 | 21 | DEFINITIONS 22 | "Font Software" refers to the set of files released by the Copyright 23 | Holder(s) under this license and clearly marked as such. This may 24 | include source files, build scripts and documentation. 25 | 26 | "Reserved Font Name" refers to any names specified as such after the 27 | copyright statement(s). 28 | 29 | "Original Version" refers to the collection of Font Software components as 30 | distributed by the Copyright Holder(s). 31 | 32 | "Modified Version" refers to any derivative made by adding to, deleting, 33 | or substituting -- in part or in whole -- any of the components of the 34 | Original Version, by changing formats or by porting the Font Software to a 35 | new environment. 36 | 37 | "Author" refers to any designer, engineer, programmer, technical 38 | writer or other person who contributed to the Font Software. 39 | 40 | PERMISSION & CONDITIONS 41 | Permission is hereby granted, free of charge, to any person obtaining 42 | a copy of the Font Software, to use, study, copy, merge, embed, modify, 43 | redistribute, and sell modified and unmodified copies of the Font 44 | Software, subject to the following conditions: 45 | 46 | 1) Neither the Font Software nor any of its individual components, 47 | in Original or Modified Versions, may be sold by itself. 48 | 49 | 2) Original or Modified Versions of the Font Software may be bundled, 50 | redistributed and/or sold with any software, provided that each copy 51 | contains the above copyright notice and this license. These can be 52 | included either as stand-alone text files, human-readable headers or 53 | in the appropriate machine-readable metadata fields within text or 54 | binary files as long as those fields can be easily viewed by the user. 55 | 56 | 3) No Modified Version of the Font Software may use the Reserved Font 57 | Name(s) unless explicit written permission is granted by the corresponding 58 | Copyright Holder. This restriction only applies to the primary font name as 59 | presented to the users. 60 | 61 | 4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font 62 | Software shall not be used to promote, endorse or advertise any 63 | Modified Version, except to acknowledge the contribution(s) of the 64 | Copyright Holder(s) and the Author(s) or with their explicit written 65 | permission. 66 | 67 | 5) The Font Software, modified or unmodified, in part or in whole, 68 | must be distributed entirely under this license, and must not be 69 | distributed under any other license. The requirement for fonts to 70 | remain under this license does not apply to any document created 71 | using the Font Software. 72 | 73 | TERMINATION 74 | This license becomes null and void if any of the above conditions are 75 | not met. 76 | 77 | DISCLAIMER 78 | THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 79 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF 80 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT 81 | OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE 82 | COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 83 | INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL 84 | DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 85 | FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM 86 | OTHER DEALINGS IN THE FONT SOFTWARE. 87 | -------------------------------------------------------------------------------- /static/open-iconic/ICON-LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Waybury 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. -------------------------------------------------------------------------------- /static/open-iconic/README.md: -------------------------------------------------------------------------------- 1 | [Open Iconic v1.1.1](http://useiconic.com/open) 2 | =========== 3 | 4 | ### Open Iconic is the open source sibling of [Iconic](http://useiconic.com). It is a hyper-legible collection of 223 icons with a tiny footprint—ready to use with Bootstrap and Foundation. [View the collection](http://useiconic.com/open#icons) 5 | 6 | 7 | 8 | ## What's in Open Iconic? 9 | 10 | * 223 icons designed to be legible down to 8 pixels 11 | * Super-light SVG files - 61.8 for the entire set 12 | * SVG sprite—the modern replacement for icon fonts 13 | * Webfont (EOT, OTF, SVG, TTF, WOFF), PNG and WebP formats 14 | * Webfont stylesheets (including versions for Bootstrap and Foundation) in CSS, LESS, SCSS and Stylus formats 15 | * PNG and WebP raster images in 8px, 16px, 24px, 32px, 48px and 64px. 16 | 17 | 18 | ## Getting Started 19 | 20 | #### For code samples and everything else you need to get started with Open Iconic, check out our [Icons](http://useiconic.com/open#icons) and [Reference](http://useiconic.com/open#reference) sections. 21 | 22 | ### General Usage 23 | 24 | #### Using Open Iconic's SVGs 25 | 26 | We like SVGs and we think they're the way to display icons on the web. Since Open Iconic are just basic SVGs, we suggest you display them like you would any other image (don't forget the `alt` attribute). 27 | 28 | ``` 29 | icon name 30 | ``` 31 | 32 | #### Using Open Iconic's SVG Sprite 33 | 34 | Open Iconic also comes in a SVG sprite which allows you to display all the icons in the set with a single request. It's like an icon font, without being a hack. 35 | 36 | Adding an icon from an SVG sprite is a little different than what you're used to, but it's still a piece of cake. *Tip: To make your icons easily style able, we suggest adding a general class to the* `` *tag and a unique class name for each different icon in the* `` *tag.* 37 | 38 | ``` 39 | 40 | 41 | 42 | ``` 43 | 44 | Sizing icons only needs basic CSS. All the icons are in a square format, so just set the `` tag with equal width and height dimensions. 45 | 46 | ``` 47 | .icon { 48 | width: 16px; 49 | height: 16px; 50 | } 51 | ``` 52 | 53 | Coloring icons is even easier. All you need to do is set the `fill` rule on the `` tag. 54 | 55 | ``` 56 | .icon-account-login { 57 | fill: #f00; 58 | } 59 | ``` 60 | 61 | To learn more about SVG Sprites, read [Chris Coyier's guide](http://css-tricks.com/svg-sprites-use-better-icon-fonts/). 62 | 63 | #### Using Open Iconic's Icon Font... 64 | 65 | 66 | ##### …with Bootstrap 67 | 68 | You can find our Bootstrap stylesheets in `font/css/open-iconic-bootstrap.{css, less, scss, styl}` 69 | 70 | 71 | ``` 72 | 73 | ``` 74 | 75 | 76 | ``` 77 | 78 | ``` 79 | 80 | ##### …with Foundation 81 | 82 | You can find our Foundation stylesheets in `font/css/open-iconic-foundation.{css, less, scss, styl}` 83 | 84 | ``` 85 | 86 | ``` 87 | 88 | 89 | ``` 90 | 91 | ``` 92 | 93 | ##### …on its own 94 | 95 | You can find our default stylesheets in `font/css/open-iconic.{css, less, scss, styl}` 96 | 97 | ``` 98 | 99 | ``` 100 | 101 | ``` 102 | 103 | ``` 104 | 105 | 106 | ## License 107 | 108 | ### Icons 109 | 110 | All code (including SVG markup) is under the [MIT License](http://opensource.org/licenses/MIT). 111 | 112 | ### Fonts 113 | 114 | All fonts are under the [SIL Licensed](http://scripts.sil.org/cms/scripts/page.php?item_id=OFL_web). 115 | -------------------------------------------------------------------------------- /static/open-iconic/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-iconic", 3 | "description": "An open source icon set in SVG, webfont and raster formats", 4 | "version": "1.1.1", 5 | "license": [ 6 | "MIT", 7 | "OFL-1.1" 8 | ], 9 | "homepage": "https://useiconic.com/open", 10 | "repository": { 11 | "type": "git", 12 | "url": "git://github.com/iconic/open-iconic.git" 13 | }, 14 | "main": [ 15 | "./sprite/open-iconic.min.svg" 16 | ], 17 | "ignore": [ 18 | "*.json", 19 | "*.md" 20 | ] 21 | } 22 | -------------------------------------------------------------------------------- /static/open-iconic/font/fonts/open-iconic.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/static/open-iconic/font/fonts/open-iconic.eot -------------------------------------------------------------------------------- /static/open-iconic/font/fonts/open-iconic.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/static/open-iconic/font/fonts/open-iconic.otf -------------------------------------------------------------------------------- /static/open-iconic/font/fonts/open-iconic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/static/open-iconic/font/fonts/open-iconic.ttf -------------------------------------------------------------------------------- /static/open-iconic/font/fonts/open-iconic.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/static/open-iconic/font/fonts/open-iconic.woff -------------------------------------------------------------------------------- /static/open-iconic/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-iconic", 3 | "description": "An open source icon set with marks in SVG, sprite, webfont and raster format", 4 | "version": "1.1.1", 5 | "keywords": ["icon", "iconic", "open-iconic", "svg", "sprite", "font", "png", "webp"], 6 | "homepage": "http://useiconic.com/open-iconic/", 7 | "author": { 8 | "name": "Iconic", 9 | "email": "yourfriends@useiconic.com", 10 | "web": "http://useiconic.com/" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/iconic/open-iconic.git" 15 | }, 16 | "contributors": [ 17 | { 18 | "name": "P.J. Onori", 19 | "web": "http://twitter.com/somerandomdude" 20 | }, 21 | { 22 | "name": "Dave Johnson", 23 | "web": "http://twitter.com/protodave" 24 | } 25 | ], 26 | "licenses": [ 27 | { 28 | "type": "MIT License", 29 | "url": "http://opensource.org/licenses/mit-license.html" 30 | }, 31 | { 32 | "type": "SIL OFL 1.1", 33 | "url": "http://scripts.sil.org/OFL" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /templates/account/base.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Web日志分析可视化系统 5 | {% load static %} 6 | 7 | 8 | 9 | 10 |
    11 | {% block content %}{% endblock %} 12 |
    13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /templates/account/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'account/base.html' %} 2 | {% load static %} 3 | {% block content %} 4 |
    5 |
    6 |
    7 |
    8 | 9 |
    10 |
    11 |
    12 |
    13 |
    14 |

    Web日志分析可视化系统

    15 |
    16 | 17 | {% csrf_token %} 18 | {{ form.non_field_errors }} 19 | {% for field in form %} 20 |
    21 | {{ field }} 22 | {{ field.errors }} 23 |
    24 | {% endfor %} 25 |
    26 |
    27 | 28 | 29 |
    30 |
    31 | 32 | 33 |
    34 |
    35 | 注册 36 |
    37 |
    38 |
    39 |
    40 |
    41 |
    42 |
    43 |
    44 |
    45 | {% endblock %} -------------------------------------------------------------------------------- /templates/account/register.html: -------------------------------------------------------------------------------- 1 | {% extends 'account/base.html' %} 2 | {% load static %} 3 | {% block content %} 4 |
    5 |
    6 |
    7 |
    8 | 9 |
    10 |
    11 |
    12 |
    13 |
    14 |

    注册

    15 |
    16 | 17 | {% csrf_token %} 18 | {{ form.non_field_errors }} 19 | {% for field in form %} 20 |
    21 | {{ field }} 22 | {{ field.errors }} 23 |
    24 | {% endfor %} 25 | 26 | 27 |
    28 |
    29 | 登录 30 |
    31 |
    32 |
    33 |
    34 |
    35 |
    36 |
    37 |
    38 |
    39 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/err_log_table.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 | {% load static %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
    14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 |
    IP地址访问时间日志记录
    22 | 42 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/http_status_line.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |

    网站访问状态统计

    8 |
    9 |
    10 |
    11 |
    12 |
    13 | 74 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/http_status_pie.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |

    网站访问状态统计

    8 |
    9 |
    10 |
    11 |
    12 |
    13 | 75 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 |
    13 |
    14 |
    15 |
    16 |
    17 |
    18 |

    今日IP访问量前六名

    19 |
    20 |
    21 |
    22 |
    23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | {% for i in log.remote_addr_data %} 32 | 33 | 34 | 35 | 36 | {% endfor %} 37 | 38 |
    IP访问量
    {{ i.remote_addr }}{{ i.count }}
    39 |
    40 |
    41 |
    42 |
    43 |
    44 |
    45 |
    46 |
    47 |

    今日链接访问量前六名

    48 |
    49 |
    50 |
    51 |
    52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | {% for i in log.url_data %} 61 | 62 | 63 | 64 | 65 | {% endfor %} 66 | 67 |
    链接访问量
    {{ i.url }}{{ i.count }}
    68 |
    69 |
    70 |
    71 |
    72 | 125 | {% endblock %} 126 | -------------------------------------------------------------------------------- /templates/logtest/outer_diff_ip.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 | 73 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/outer_ip.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 | {#

    国内不同IP访问排名

    #} 8 |
    9 |
    10 |
    11 |
    12 |
    13 | 74 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/recent_log_table.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 | {% load static %} 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | {##} 20 | 21 | 22 | 23 | {##} 24 | 25 | 26 |
    IP地址访问时间方式链接状态字节数来源user_agent
    27 | 47 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/temp.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 | 65 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/today_access.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 | 100 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/ua_browser_bot.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 | 74 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/ua_browser_detail.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 | 74 | {% endblock %} -------------------------------------------------------------------------------- /templates/logtest/ua_os.html: -------------------------------------------------------------------------------- 1 | {% extends 'logtest/base.html' %} 2 | {% block content %} 3 |
    4 |
    5 |
    6 |
    7 |
    8 |
    9 |
    10 |
    11 |
    12 | 74 | {% endblock %} -------------------------------------------------------------------------------- /weblog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rmboot/web-log/3a12b39d6147756af479043fc5bdbfc6781f4502/weblog/__init__.py -------------------------------------------------------------------------------- /weblog/admin_site.py: -------------------------------------------------------------------------------- 1 | from django.contrib.admin import AdminSite 2 | # 模型 3 | from django.contrib.admin.models import LogEntry 4 | from django.contrib.sites.models import Site 5 | from servermanager.models import EmailSendLog, BlockIPLog 6 | from account.models import WebLogUser 7 | from logtest.models import DjangoJob, DjangoJobExecution 8 | # 模型管理 9 | from weblog.log_entry_admin import LogEntryAdmin 10 | from django.contrib.sites.admin import SiteAdmin 11 | from account.admin import WebLogUserAdmin 12 | from logtest.admin import DjangoJobAdmin, DjangoJobExecutionAdmin 13 | from servermanager.admin import EmailSendLogAdmin, BlockIPLogAdmin 14 | 15 | 16 | class WebLogAdminSite(AdminSite): 17 | site_header = '日志分析可视化系统' 18 | site_title = '管理后台' 19 | 20 | def __init__(self, name='admin'): 21 | super().__init__(name) 22 | 23 | def has_permission(self, request): 24 | return request.user.is_superuser 25 | 26 | 27 | admin_site = WebLogAdminSite(name='admin') 28 | admin_site.register(Site, SiteAdmin) # 站点名称管理 29 | admin_site.register(WebLogUser, WebLogUserAdmin) # 站点用户管理 30 | admin_site.register(DjangoJob, DjangoJobAdmin) # 日志处理任务管理 31 | admin_site.register(DjangoJobExecution, DjangoJobExecutionAdmin) # 日志处理任务调度管理 32 | admin_site.register(EmailSendLog, EmailSendLogAdmin) # 预警邮件日志管理 33 | admin_site.register(BlockIPLog, BlockIPLogAdmin) # 禁止IP日志管理 34 | admin_site.register(LogEntry, LogEntryAdmin) # 站点日志管理 35 | -------------------------------------------------------------------------------- /weblog/log_entry_admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib.contenttypes.models import ContentType 2 | from django.urls import reverse, NoReverseMatch 3 | from django.contrib.admin.models import LogEntry, ADDITION, CHANGE, DELETION 4 | from django.contrib.auth import get_user_model 5 | from django.contrib.contenttypes.models import ContentType 6 | from django.utils.encoding import force_text 7 | from django.utils.html import escape 8 | from django.utils.translation import pgettext_lazy, ugettext_lazy as _ 9 | from django.utils.safestring import mark_safe 10 | from django.contrib import admin 11 | 12 | action_names = { 13 | ADDITION: pgettext_lazy('logentry_admin:action_type', 'Addition'), 14 | DELETION: pgettext_lazy('logentry_admin:action_type', 'Deletion'), 15 | CHANGE: pgettext_lazy('logentry_admin:action_type', 'Change'), 16 | } 17 | 18 | 19 | class LogEntryAdmin(admin.ModelAdmin): 20 | date_hierarchy = 'action_time' 21 | 22 | readonly_fields = ([f.name for f in LogEntry._meta.fields] + 23 | ['object_link', 'action_description', 'user_link', 24 | 'get_change_message']) 25 | 26 | fieldsets = ( 27 | (_('Metadata'), { 28 | 'fields': ( 29 | 'action_time', 30 | 'user_link', 31 | 'action_description', 32 | 'object_link', 33 | ) 34 | }), 35 | (_('Details'), { 36 | 'fields': ( 37 | 'get_change_message', 38 | 'content_type', 39 | 'object_id', 40 | 'object_repr', 41 | ) 42 | }), 43 | ) 44 | 45 | list_filter = [ 46 | 'content_type' 47 | ] 48 | 49 | search_fields = [ 50 | 'object_repr', 51 | 'change_message' 52 | ] 53 | 54 | list_display_links = [ 55 | 'action_time', 56 | 'get_change_message', 57 | ] 58 | list_display = [ 59 | 'action_time', 60 | 'user_link', 61 | 'content_type', 62 | 'object_link', 63 | 'action_description', 64 | 'get_change_message', 65 | ] 66 | 67 | def has_add_permission(self, request): 68 | return False 69 | 70 | def has_change_permission(self, request, obj=None): 71 | return ( 72 | request.user.is_superuser or 73 | request.user.has_perm('admin.change_logentry') 74 | ) and request.method != 'POST' 75 | 76 | def has_delete_permission(self, request, obj=None): 77 | return False 78 | 79 | def object_link(self, obj): 80 | object_link = escape(obj.object_repr) 81 | content_type = obj.content_type 82 | 83 | if obj.action_flag != DELETION and content_type is not None: 84 | # try returning an actual link instead of object repr string 85 | try: 86 | url = reverse( 87 | 'admin:{}_{}_change'.format(content_type.app_label, 88 | content_type.model), 89 | args=[obj.object_id] 90 | ) 91 | object_link = '{}'.format(url, object_link) 92 | except NoReverseMatch: 93 | pass 94 | return mark_safe(object_link) 95 | 96 | object_link.admin_order_field = 'object_repr' 97 | object_link.short_description = _('object') 98 | 99 | def user_link(self, obj): 100 | content_type = ContentType.objects.get_for_model(type(obj.user)) 101 | user_link = escape(force_text(obj.user)) 102 | try: 103 | # try returning an actual link instead of object repr string 104 | url = reverse( 105 | 'admin:{}_{}_change'.format(content_type.app_label, 106 | content_type.model), 107 | args=[obj.user.pk] 108 | ) 109 | user_link = '{}'.format(url, user_link) 110 | except NoReverseMatch: 111 | pass 112 | return mark_safe(user_link) 113 | 114 | user_link.admin_order_field = 'user' 115 | user_link.short_description = _('user') 116 | 117 | def get_queryset(self, request): 118 | queryset = super(LogEntryAdmin, self).get_queryset(request) 119 | return queryset.prefetch_related('content_type') 120 | 121 | def get_actions(self, request): 122 | actions = super(LogEntryAdmin, self).get_actions(request) 123 | if 'delete_selected' in actions: 124 | del actions['delete_selected'] 125 | return actions 126 | 127 | def action_description(self, obj): 128 | return action_names[obj.action_flag] 129 | 130 | action_description.short_description = _('action') 131 | 132 | def get_change_message(self, obj): 133 | return obj.get_change_message() 134 | 135 | get_change_message.short_description = _('change message') 136 | -------------------------------------------------------------------------------- /weblog/log_signals.py: -------------------------------------------------------------------------------- 1 | import django.dispatch 2 | from django.dispatch import receiver 3 | from django.conf import settings 4 | from django.core.mail import EmailMultiAlternatives 5 | import os 6 | import logging 7 | 8 | logger = logging.getLogger(__name__) 9 | 10 | send_email_signal = django.dispatch.Signal(providing_args=['emailto', 'title', 'content']) 11 | block_ip = django.dispatch.Signal(providing_args=['ip_addr']) 12 | unblock_ip = django.dispatch.Signal(providing_args=['ip_addr']) 13 | 14 | 15 | @receiver(send_email_signal) 16 | def send_email_signal_handler(sender, **kwargs): 17 | emailto = kwargs['emailto'] 18 | title = kwargs['title'] 19 | content = kwargs['content'] 20 | 21 | msg = EmailMultiAlternatives(title, content, from_email=settings.DEFAULT_FROM_EMAIL, to=emailto) 22 | msg.content_subtype = "html" 23 | 24 | from servermanager.models import EmailSendLog 25 | log = EmailSendLog() 26 | log.title = title 27 | log.content = content 28 | log.emailto = ','.join(emailto) 29 | 30 | try: 31 | result = msg.send() 32 | print(result) 33 | log.send_result = result > 0 34 | except Exception as e: 35 | logger.error(e) 36 | log.send_result = False 37 | log.save() 38 | 39 | 40 | @receiver(block_ip) 41 | def block_ip_handler(sender, **kwargs): 42 | ip_addr = kwargs['ip_addr'] 43 | 44 | res = os.system('sudo iptables -I INPUT -s %s -j DROP' % ip_addr) 45 | 46 | from servermanager.models import BlockIPLog 47 | if BlockIPLog.objects.filter(ip_addr=ip_addr): 48 | print(ip_addr+' 该IP已经禁止,请勿重复添加') 49 | return 50 | block_log = BlockIPLog() 51 | block_log.ip_addr = ip_addr 52 | try: 53 | result = res 54 | block_log.send_result = result == 0 55 | except Exception as e: 56 | logger.error(e) 57 | block_log.send_result = False 58 | block_log.save() 59 | 60 | 61 | @receiver(unblock_ip) 62 | def unblock_ip_handler(sender, **kwargs): 63 | ip_addr = kwargs['ip_addr'] 64 | 65 | res = os.system('sudo iptables -D INPUT -s %s -j DROP' % ip_addr) 66 | 67 | from servermanager.models import BlockIPLog 68 | try: 69 | if res == 0: 70 | BlockIPLog.objects.filter(ip_addr=ip_addr).delete() 71 | else: 72 | raise Exception("解除IP禁止失败") 73 | except Exception as e: 74 | logger.error(e) 75 | -------------------------------------------------------------------------------- /weblog/urls.py: -------------------------------------------------------------------------------- 1 | """weblog URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/2.2/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Import the include() function: from django.urls import include, path 14 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 15 | """ 16 | from django.conf.urls import url 17 | from weblog.admin_site import admin_site 18 | from django.urls import include 19 | 20 | urlpatterns = [ 21 | url('admin/', admin_site.urls), 22 | url('', include('account.urls', namespace='account')), 23 | url('', include('logtest.urls', namespace='logtest')), 24 | ] 25 | -------------------------------------------------------------------------------- /weblog/utils.py: -------------------------------------------------------------------------------- 1 | import logging 2 | 3 | logger = logging.getLogger(__name__) 4 | 5 | 6 | def send_email(emailto, title, content): 7 | from weblog.log_signals import send_email_signal 8 | send_email_signal.send(send_email.__class__, emailto=emailto, title=title, content=content) 9 | 10 | 11 | def block_ip(ip_addr): 12 | from weblog.log_signals import block_ip 13 | block_ip.send(block_ip.__class__, ip_addr=ip_addr) 14 | 15 | 16 | def unblock_ip(ip_addr): 17 | from weblog.log_signals import unblock_ip 18 | unblock_ip.send(unblock_ip.__class__, ip_addr=ip_addr) 19 | -------------------------------------------------------------------------------- /weblog/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for weblog project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/2.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'weblog.settings') 15 | 16 | application = get_wsgi_application() 17 | --------------------------------------------------------------------------------