Merge branch 'master' of http://git.zjvtit.net/21653B118/demo
This commit is contained in:
commit
df7006915c
|
@ -0,0 +1,8 @@
|
|||
# 默认忽略的文件
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# 数据源本地存储已忽略文件
|
||||
/../../../../../../../:\Users\GLL\Desktop\demo\blog\.idea/dataSources/
|
||||
/dataSources.local.xml
|
||||
# 基于编辑器的 HTTP 客户端请求
|
||||
/httpRequests/
|
|
@ -0,0 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="PYTHON_MODULE" version="4">
|
||||
<component name="FacetManager">
|
||||
<facet type="django" name="Django">
|
||||
<configuration>
|
||||
<option name="rootFolder" value="$MODULE_DIR$" />
|
||||
<option name="settingsModule" value="blog/settings.py" />
|
||||
<option name="manageScript" value="$MODULE_DIR$/manage.py" />
|
||||
<option name="environment" value="<map/>" />
|
||||
<option name="doNotUseTestRunner" value="false" />
|
||||
<option name="trackFilePattern" value="migrations" />
|
||||
</configuration>
|
||||
</facet>
|
||||
</component>
|
||||
<component name="NewModuleRootManager">
|
||||
<content url="file://$MODULE_DIR$" />
|
||||
<orderEntry type="jdk" jdkName="Python 3.8 (blog) (2)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="PyDocumentationSettings">
|
||||
<option name="format" value="PLAIN" />
|
||||
<option name="myDocStringFormat" value="Plain" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
<option name="TEMPLATE_CONFIGURATION" value="Django" />
|
||||
</component>
|
||||
</module>
|
|
@ -0,0 +1,6 @@
|
|||
<component name="InspectionProjectProfileManager">
|
||||
<settings>
|
||||
<option name="USE_PROJECT_PROFILE" value="false" />
|
||||
<version value="1.0" />
|
||||
</settings>
|
||||
</component>
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.8 (blog) (2)" project-jdk-type="Python SDK" />
|
||||
</project>
|
|
@ -0,0 +1,8 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectModuleManager">
|
||||
<modules>
|
||||
<module fileurl="file://$PROJECT_DIR$/.idea/blog.iml" filepath="$PROJECT_DIR$/.idea/blog.iml" />
|
||||
</modules>
|
||||
</component>
|
||||
</project>
|
|
@ -0,0 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
ASGI config for blog project.
|
||||
|
||||
It exposes the ASGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/howto/deployment/asgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.asgi import get_asgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog.settings')
|
||||
|
||||
application = get_asgi_application()
|
|
@ -0,0 +1,204 @@
|
|||
"""
|
||||
Django settings for blog project.
|
||||
|
||||
Generated by 'django-admin startproject' using Django 3.1.1.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/topics/settings/
|
||||
|
||||
For the full list of settings and their values, see
|
||||
https://docs.djangoproject.com/en/3.1/ref/settings/
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import time
|
||||
import os
|
||||
import logging
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
# Quick-start development settings - unsuitable for production
|
||||
# See https://docs.djangoproject.com/en/3.1/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'n7y-z2a*%3tvn*o65i)0=8d#6bt9skwe)#8z=*s*0v68%egw77'
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
#子应用的注册
|
||||
'users.apps.UsersConfig',
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'blog.urls'
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [os.path.join(BASE_DIR,'templates')],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'blog.wsgi.application'
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.mysql',#数据库引擎
|
||||
'HOST':'127.0.0.1',#数据库主机
|
||||
'PORT':'3306',# 数据库端口
|
||||
'USER':'gjj',
|
||||
'PASSWORD':'123456',
|
||||
'NAME': 'gjj',
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/3.1/ref/settings/#auth-password-validators
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
},
|
||||
]
|
||||
|
||||
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/3.1/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'zh-hans'
|
||||
|
||||
TIME_ZONE = 'Asia/Shanghai'
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
USE_L10N = True
|
||||
|
||||
USE_TZ = True
|
||||
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/3.1/howto/static-files/
|
||||
|
||||
STATIC_URL = '/static/'
|
||||
#设置静态资源路径
|
||||
STATICFILES_DIRS=[
|
||||
os.path.join(BASE_DIR,"static"),
|
||||
|
||||
]
|
||||
|
||||
# 配置用户上传文件
|
||||
MEDIA_URL = '/media/'
|
||||
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
|
||||
|
||||
CACHES = {
|
||||
"default": { # 默认
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": "redis://127.0.0.1:6379/0",
|
||||
"OPTIONS": {
|
||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
}
|
||||
},
|
||||
"session": { # session
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": "redis://127.0.0.1:6379/1",
|
||||
"OPTIONS": {
|
||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
}
|
||||
},
|
||||
}
|
||||
SESSION_ENGINE = "django.contrib.sessions.backends.cache"
|
||||
SESSION_CACHE_ALIAS = "session"
|
||||
|
||||
|
||||
#日志
|
||||
cur_path = os.path.dirname(os.path.realpath(__file__)) # log_path是存放日志的路径
|
||||
log_path = os.path.join(os.path.dirname(cur_path), 'logs')
|
||||
if not os.path.exists(log_path): os.mkdir(log_path) # 如果不存在这个logs文件夹,就自动创建一个
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
'disable_existing_loggers': False, # 是否禁用已经存在的日志器
|
||||
'formatters': { # 日志信息显示的格式
|
||||
'verbose': {
|
||||
'format': '%(levelname)s %(asctime)s %(module)s %(lineno)d %(message)s'
|
||||
},
|
||||
'simple': {
|
||||
'format': '%(levelname)s %(module)s %(lineno)d %(message)s'
|
||||
},
|
||||
},
|
||||
'filters': { # 对日志进行过滤
|
||||
'require_debug_true': { # django在debug模式下才输出日志
|
||||
'()': 'django.utils.log.RequireDebugTrue',
|
||||
},
|
||||
},
|
||||
'handlers': { # 日志处理方法
|
||||
'console': { # 向终端中输出日志
|
||||
'level': 'INFO',
|
||||
'filters': ['require_debug_true'],
|
||||
'class': 'logging.StreamHandler',
|
||||
'formatter': 'simple'
|
||||
},
|
||||
'file': { # 向文件中输出日志
|
||||
'level': 'INFO',
|
||||
'class': 'logging.handlers.RotatingFileHandler',
|
||||
'filename': os.path.join(BASE_DIR, 'logs/blog.log'), # 日志文件的位置
|
||||
'maxBytes': 300 * 1024 * 1024,
|
||||
'backupCount': 10,
|
||||
'formatter': 'verbose'
|
||||
},
|
||||
},
|
||||
'loggers': { # 日志器
|
||||
'django': { # 定义了一个名为django的日志器
|
||||
'handlers': ['console', 'file'], # 可以同时向终端与文件中输出日志
|
||||
'propagate': True, # 是否继续传递日志信息
|
||||
'level': 'INFO', # 日志器接收的最低日志级别
|
||||
},
|
||||
}
|
||||
}
|
||||
AUTH_USER_MODEL = 'users.User'
|
|
@ -0,0 +1,44 @@
|
|||
"""blog URL Configuration
|
||||
|
||||
The `urlpatterns` list routes URLs to views. For more information please see:
|
||||
https://docs.djangoproject.com/en/3.1/topics/http/urls/
|
||||
Examples:
|
||||
Function views
|
||||
1. Add an import: from my_app import views
|
||||
2. Add a URL to urlpatterns: path('', views.home, name='home')
|
||||
Class-based views
|
||||
1. Add an import: from other_app.views import Home
|
||||
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
|
||||
Including another URLconf
|
||||
1. Import the include() function: from django.urls import include, path
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.http import HttpResponse
|
||||
from django.conf import settings
|
||||
from django.conf.urls.static import static
|
||||
from django.urls import path,include
|
||||
|
||||
|
||||
#导入系统logging
|
||||
# import logging
|
||||
# #创建获取日志器
|
||||
# logger=logging.getLogger('django')
|
||||
|
||||
|
||||
# def log(request):
|
||||
# logger.info('info')
|
||||
# return HttpResponse('test')
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('', include(('users.urls', 'users'), namespace='users')),
|
||||
path('', include(('home.urls', 'home'), namespace='home')),
|
||||
# include 参数1要设置为元组(urlconf_module, app_name)
|
||||
# namespace 设置命名空间
|
||||
]
|
||||
# path('',log),
|
||||
# ]+static(settings.STATIC_URL,document_root=settings.STATIC_ROOT)
|
||||
# urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
||||
# admin.site.site_header = "轻编程的管理后台"
|
||||
# admin.site.index_title = "轻编程管理后台"
|
||||
# admin.site.site_title = "轻编程管理员登陆了"
|
|
@ -0,0 +1,16 @@
|
|||
"""
|
||||
WSGI config for blog project.
|
||||
|
||||
It exposes the WSGI callable as a module-level variable named ``application``.
|
||||
|
||||
For more information on this file, see
|
||||
https://docs.djangoproject.com/en/3.1/howto/deployment/wsgi/
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog.settings')
|
||||
|
||||
application = get_wsgi_application()
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,3 @@
|
|||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
|
@ -0,0 +1,5 @@
|
|||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class HomeConfig(AppConfig):
|
||||
name = 'home'
|
|
@ -0,0 +1,3 @@
|
|||
from django.db import models
|
||||
|
||||
# Create your models here.
|
|
@ -0,0 +1,3 @@
|
|||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
|
@ -0,0 +1,7 @@
|
|||
from django.urls import path
|
||||
from home.views import IndexView
|
||||
urlpatterns = [
|
||||
path('', IndexView.as_view(),name='index'),
|
||||
|
||||
|
||||
]
|
|
@ -0,0 +1,9 @@
|
|||
from django.shortcuts import render
|
||||
from django.views import View
|
||||
class IndexView(View):
|
||||
"""首页广告"""
|
||||
|
||||
def get(self, request):
|
||||
"""提供首页广告界面"""
|
||||
return render(request, 'index.html')
|
||||
# Create your views here.
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,220 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
# refer to `https://bitbucket.org/akorn/wheezy.captcha`
|
||||
|
||||
import random
|
||||
import string
|
||||
import os.path
|
||||
from io import BytesIO
|
||||
|
||||
from PIL import Image
|
||||
from PIL import ImageFilter
|
||||
from PIL.ImageDraw import Draw
|
||||
from PIL.ImageFont import truetype
|
||||
|
||||
|
||||
class Bezier:
|
||||
def __init__(self):
|
||||
self.tsequence = tuple([t / 20.0 for t in range(21)])
|
||||
self.beziers = {}
|
||||
|
||||
def pascal_row(self, n):
|
||||
""" Returns n-th row of Pascal's triangle
|
||||
"""
|
||||
result = [1]
|
||||
x, numerator = 1, n
|
||||
for denominator in range(1, n // 2 + 1):
|
||||
x *= numerator
|
||||
x /= denominator
|
||||
result.append(x)
|
||||
numerator -= 1
|
||||
if n & 1 == 0:
|
||||
result.extend(reversed(result[:-1]))
|
||||
else:
|
||||
result.extend(reversed(result))
|
||||
return result
|
||||
|
||||
def make_bezier(self, n):
|
||||
""" Bezier curves:
|
||||
http://en.wikipedia.org/wiki/B%C3%A9zier_curve#Generalization
|
||||
"""
|
||||
try:
|
||||
return self.beziers[n]
|
||||
except KeyError:
|
||||
combinations = self.pascal_row(n - 1)
|
||||
result = []
|
||||
for t in self.tsequence:
|
||||
tpowers = (t ** i for i in range(n))
|
||||
upowers = ((1 - t) ** i for i in range(n - 1, -1, -1))
|
||||
coefs = [c * a * b for c, a, b in zip(combinations,
|
||||
tpowers, upowers)]
|
||||
result.append(coefs)
|
||||
self.beziers[n] = result
|
||||
return result
|
||||
|
||||
|
||||
class Captcha(object):
|
||||
def __init__(self):
|
||||
self._bezier = Bezier()
|
||||
self._dir = os.path.dirname(__file__)
|
||||
# self._captcha_path = os.path.join(self._dir, '..', 'static', 'captcha')
|
||||
|
||||
@staticmethod
|
||||
def instance():
|
||||
if not hasattr(Captcha, "_instance"):
|
||||
Captcha._instance = Captcha()
|
||||
return Captcha._instance
|
||||
|
||||
def initialize(self, width=200, height=75, color=None, text=None, fonts=None):
|
||||
# self.image = Image.new('RGB', (width, height), (255, 255, 255))
|
||||
self._text = text if text else random.sample(string.ascii_uppercase + string.ascii_uppercase + '3456789', 4)
|
||||
self.fonts = fonts if fonts else \
|
||||
[os.path.join(self._dir, 'fonts', font) for font in ['Arial.ttf', 'Georgia.ttf', 'actionj.ttf']]
|
||||
self.width = width
|
||||
self.height = height
|
||||
self._color = color if color else self.random_color(0, 200, random.randint(220, 255))
|
||||
|
||||
@staticmethod
|
||||
def random_color(start, end, opacity=None):
|
||||
red = random.randint(start, end)
|
||||
green = random.randint(start, end)
|
||||
blue = random.randint(start, end)
|
||||
if opacity is None:
|
||||
return red, green, blue
|
||||
return red, green, blue, opacity
|
||||
|
||||
# draw image
|
||||
|
||||
def background(self, image):
|
||||
Draw(image).rectangle([(0, 0), image.size], fill=self.random_color(238, 255))
|
||||
return image
|
||||
|
||||
@staticmethod
|
||||
def smooth(image):
|
||||
return image.filter(ImageFilter.SMOOTH)
|
||||
|
||||
def curve(self, image, width=4, number=6, color=None):
|
||||
dx, height = image.size
|
||||
dx /= number
|
||||
path = [(dx * i, random.randint(0, height))
|
||||
for i in range(1, number)]
|
||||
bcoefs = self._bezier.make_bezier(number - 1)
|
||||
points = []
|
||||
for coefs in bcoefs:
|
||||
points.append(tuple(sum([coef * p for coef, p in zip(coefs, ps)])
|
||||
for ps in zip(*path)))
|
||||
Draw(image).line(points, fill=color if color else self._color, width=width)
|
||||
return image
|
||||
|
||||
def noise(self, image, number=50, level=2, color=None):
|
||||
width, height = image.size
|
||||
dx = width / 10
|
||||
width -= dx
|
||||
dy = height / 10
|
||||
height -= dy
|
||||
draw = Draw(image)
|
||||
for i in range(number):
|
||||
x = int(random.uniform(dx, width))
|
||||
y = int(random.uniform(dy, height))
|
||||
draw.line(((x, y), (x + level, y)), fill=color if color else self._color, width=level)
|
||||
return image
|
||||
|
||||
def text(self, image, fonts, font_sizes=None, drawings=None, squeeze_factor=0.75, color=None):
|
||||
color = color if color else self._color
|
||||
fonts = tuple([truetype(name, size)
|
||||
for name in fonts
|
||||
for size in font_sizes or (65, 70, 75)])
|
||||
draw = Draw(image)
|
||||
char_images = []
|
||||
for c in self._text:
|
||||
font = random.choice(fonts)
|
||||
c_width, c_height = draw.textsize(c, font=font)
|
||||
char_image = Image.new('RGB', (c_width, c_height), (0, 0, 0))
|
||||
char_draw = Draw(char_image)
|
||||
char_draw.text((0, 0), c, font=font, fill=color)
|
||||
char_image = char_image.crop(char_image.getbbox())
|
||||
for drawing in drawings:
|
||||
d = getattr(self, drawing)
|
||||
char_image = d(char_image)
|
||||
char_images.append(char_image)
|
||||
width, height = image.size
|
||||
offset = int((width - sum(int(i.size[0] * squeeze_factor)
|
||||
for i in char_images[:-1]) -
|
||||
char_images[-1].size[0]) / 2)
|
||||
for char_image in char_images:
|
||||
c_width, c_height = char_image.size
|
||||
mask = char_image.convert('L').point(lambda i: i * 1.97)
|
||||
image.paste(char_image,
|
||||
(offset, int((height - c_height) / 2)),
|
||||
mask)
|
||||
offset += int(c_width * squeeze_factor)
|
||||
return image
|
||||
|
||||
# draw text
|
||||
@staticmethod
|
||||
def warp(image, dx_factor=0.27, dy_factor=0.21):
|
||||
width, height = image.size
|
||||
dx = width * dx_factor
|
||||
dy = height * dy_factor
|
||||
x1 = int(random.uniform(-dx, dx))
|
||||
y1 = int(random.uniform(-dy, dy))
|
||||
x2 = int(random.uniform(-dx, dx))
|
||||
y2 = int(random.uniform(-dy, dy))
|
||||
image2 = Image.new('RGB',
|
||||
(width + abs(x1) + abs(x2),
|
||||
height + abs(y1) + abs(y2)))
|
||||
image2.paste(image, (abs(x1), abs(y1)))
|
||||
width2, height2 = image2.size
|
||||
return image2.transform(
|
||||
(width, height), Image.QUAD,
|
||||
(x1, y1,
|
||||
-x1, height2 - y2,
|
||||
width2 + x2, height2 + y2,
|
||||
width2 - x2, -y1))
|
||||
|
||||
@staticmethod
|
||||
def offset(image, dx_factor=0.1, dy_factor=0.2):
|
||||
width, height = image.size
|
||||
dx = int(random.random() * width * dx_factor)
|
||||
dy = int(random.random() * height * dy_factor)
|
||||
image2 = Image.new('RGB', (width + dx, height + dy))
|
||||
image2.paste(image, (dx, dy))
|
||||
return image2
|
||||
|
||||
@staticmethod
|
||||
def rotate(image, angle=25):
|
||||
return image.rotate(
|
||||
random.uniform(-angle, angle), Image.BILINEAR, expand=1)
|
||||
|
||||
def captcha(self, path=None, fmt='JPEG'):
|
||||
"""Create a captcha.
|
||||
|
||||
Args:
|
||||
path: save path, default None.
|
||||
fmt: image format, PNG / JPEG.
|
||||
Returns:
|
||||
A tuple, (text, StringIO.value).
|
||||
For example:
|
||||
('JGW9', '\x89PNG\r\n\x1a\n\x00\x00\x00\r...')
|
||||
|
||||
"""
|
||||
image = Image.new('RGB', (self.width, self.height), (255, 255, 255))
|
||||
image = self.background(image)
|
||||
image = self.text(image, self.fonts, drawings=['warp', 'rotate', 'offset'])
|
||||
image = self.curve(image)
|
||||
image = self.noise(image)
|
||||
image = self.smooth(image)
|
||||
text = "".join(self._text)
|
||||
out = BytesIO()
|
||||
image.save(out, format=fmt)
|
||||
return text, out.getvalue()
|
||||
|
||||
def generate_captcha(self):
|
||||
self.initialize()
|
||||
return self.captcha("")
|
||||
|
||||
captcha = Captcha.instance()
|
||||
|
||||
if __name__ == '__main__':
|
||||
print(captcha.generate_captcha())
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,801 @@
|
|||
# -*- coding: UTF-8 -*-
|
||||
# Copyright (c) 2014 The CCP project authors. All Rights Reserved.
|
||||
#
|
||||
# Use of this source code is governed by a Beijing Speedtong Information Technology Co.,Ltd license
|
||||
# that can be found in the LICENSE file in the root of the web site.
|
||||
#
|
||||
# http://www.yuntongxun.com
|
||||
#
|
||||
# An additional intellectual property rights grant can be found
|
||||
# in the file PATENTS. All contributing project authors may
|
||||
# be found in the AUTHORS file in the root of the source tree.
|
||||
|
||||
from hashlib import md5
|
||||
import base64
|
||||
import datetime
|
||||
from urllib import request as urllib2
|
||||
import json
|
||||
from .xmltojson import xmltojson
|
||||
|
||||
|
||||
class REST:
|
||||
AccountSid = ''
|
||||
AccountToken = ''
|
||||
AppId = ''
|
||||
SubAccountSid = ''
|
||||
SubAccountToken = ''
|
||||
ServerIP = ''
|
||||
ServerPort = ''
|
||||
SoftVersion = ''
|
||||
Iflog = False # 是否打印日志
|
||||
Batch = '' # 时间戳
|
||||
BodyType = 'xml' # 包体格式,可填值:json 、xml
|
||||
|
||||
# 初始化
|
||||
# @param serverIP 必选参数 服务器地址
|
||||
# @param serverPort 必选参数 服务器端口
|
||||
# @param softVersion 必选参数 REST版本号
|
||||
def __init__(self, ServerIP, ServerPort, SoftVersion):
|
||||
|
||||
self.ServerIP = ServerIP
|
||||
self.ServerPort = ServerPort
|
||||
self.SoftVersion = SoftVersion
|
||||
|
||||
# 设置主帐号
|
||||
# @param AccountSid 必选参数 主帐号
|
||||
# @param AccountToken 必选参数 主帐号Token
|
||||
|
||||
def setAccount(self, AccountSid, AccountToken):
|
||||
self.AccountSid = AccountSid
|
||||
self.AccountToken = AccountToken
|
||||
|
||||
# 设置子帐号
|
||||
#
|
||||
# @param SubAccountSid 必选参数 子帐号
|
||||
# @param SubAccountToken 必选参数 子帐号Token
|
||||
|
||||
def setSubAccount(self, SubAccountSid, SubAccountToken):
|
||||
self.SubAccountSid = SubAccountSid
|
||||
self.SubAccountToken = SubAccountToken
|
||||
|
||||
# 设置应用ID
|
||||
#
|
||||
# @param AppId 必选参数 应用ID
|
||||
|
||||
def setAppId(self, AppId):
|
||||
self.AppId = AppId
|
||||
|
||||
def log(self, url, body, data):
|
||||
print('这是请求的URL:')
|
||||
print(url)
|
||||
print('这是请求包体:')
|
||||
print(body)
|
||||
print('这是响应包体:')
|
||||
print(data)
|
||||
print('********************************')
|
||||
|
||||
# 创建子账号
|
||||
# @param friendlyName 必选参数 子帐号名称
|
||||
def CreateSubAccount(self, friendlyName):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/SubAccounts?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
req.add_header("Authorization", auth)
|
||||
# xml格式
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?><SubAccount><appId>%s</appId>\
|
||||
<friendlyName>%s</friendlyName>\
|
||||
</SubAccount>\
|
||||
''' % (self.AppId, friendlyName)
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
body = '''{"friendlyName": "%s", "appId": "%s"}''' % (friendlyName, self.AppId)
|
||||
data = ''
|
||||
req.data = body.encode()
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 获取子帐号
|
||||
# @param startNo 可选参数 开始的序号,默认从0开始
|
||||
# @param offset 可选参数 一次查询的最大条数,最小是1条,最大是100条
|
||||
def getSubAccounts(self, startNo, offset):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/GetSubAccounts?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
# auth = base64.encodestring(src).strip()
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
req.add_header("Authorization", auth)
|
||||
# xml格式
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?><SubAccount><appId>%s</appId>\
|
||||
<startNo>%s</startNo><offset>%s</offset>\
|
||||
</SubAccount>\
|
||||
''' % (self.AppId, startNo, offset)
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
body = '''{"appId": "%s", "startNo": "%s", "offset": "%s"}''' % (self.AppId, startNo, offset)
|
||||
data = ''
|
||||
req.data = body.encode()
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 子帐号信息查询
|
||||
# @param friendlyName 必选参数 子帐号名称
|
||||
|
||||
def querySubAccount(self, friendlyName):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/QuerySubAccountByName?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
# auth = base64.encodestring(src).strip()
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
|
||||
req.add_header("Authorization", auth)
|
||||
|
||||
# 创建包体
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?><SubAccount><appId>%s</appId>\
|
||||
<friendlyName>%s</friendlyName>\
|
||||
</SubAccount>\
|
||||
''' % (self.AppId, friendlyName)
|
||||
if self.BodyType == 'json':
|
||||
body = '''{"friendlyName": "%s", "appId": "%s"}''' % (friendlyName, self.AppId)
|
||||
data = ''
|
||||
req.data = body.encode()
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 发送模板短信
|
||||
# @param to 必选参数 短信接收彿手机号码集合,用英文逗号分开
|
||||
# @param datas 可选参数 内容数据
|
||||
# @param tempId 必选参数 模板Id
|
||||
def sendTemplateSMS(self, to, datas, tempId):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/SMS/TemplateSMS?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
# auth = base64.encodestring(src).strip()
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
req.add_header("Authorization", auth)
|
||||
# 创建包体
|
||||
b = ''
|
||||
for a in datas:
|
||||
b += '<data>%s</data>' % (a)
|
||||
|
||||
body = '<?xml version="1.0" encoding="utf-8"?><SubAccount><datas>' + b + '</datas><to>%s</to><templateId>%s</templateId><appId>%s</appId>\
|
||||
</SubAccount>\
|
||||
' % (to, tempId, self.AppId)
|
||||
if self.BodyType == 'json':
|
||||
# if this model is Json ..then do next code
|
||||
b = '['
|
||||
for a in datas:
|
||||
b += '"%s",' % (a)
|
||||
b += ']'
|
||||
body = '''{"to": "%s", "datas": %s, "templateId": "%s", "appId": "%s"}''' % (to, b, tempId, self.AppId)
|
||||
req.data = body.encode()
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 外呼通知
|
||||
# @param to 必选参数 被叫号码
|
||||
# @param mediaName 可选参数 语音文件名称,格式 wav。与mediaTxt不能同时为空。当不为空时mediaTxt属性失效。
|
||||
# @param mediaTxt 可选参数 文本内容
|
||||
# @param displayNum 可选参数 显示的主叫号码
|
||||
# @param playTimes 可选参数 循环播放次数,1-3次,默认播放1次。
|
||||
# @param respUrl 可选参数 外呼通知状态通知回调地址,云通讯平台将向该Url地址发送呼叫结果通知。
|
||||
# @param userData 可选参数 用户私有数据
|
||||
# @param maxCallTime 可选参数 最大通话时长
|
||||
# @param speed 可选参数 发音速度
|
||||
# @param volume 可选参数 音量
|
||||
# @param pitch 可选参数 音调
|
||||
# @param bgsound 可选参数 背景音编号
|
||||
|
||||
def landingCall(self, to, mediaName, mediaTxt, displayNum, playTimes, respUrl, userData, maxCallTime, speed, volume,
|
||||
pitch, bgsound):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/Calls/LandingCalls?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
# auth = base64.encodestring(src).strip()
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
req.add_header("Authorization", auth)
|
||||
|
||||
# 创建包体
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?><LandingCall>\
|
||||
<to>%s</to><mediaName>%s</mediaName><mediaTxt>%s</mediaTxt><appId>%s</appId><displayNum>%s</displayNum>\
|
||||
<playTimes>%s</playTimes><respUrl>%s</respUrl><userData>%s</userData><maxCallTime>%s</maxCallTime><speed>%s</speed>
|
||||
<volume>%s</volume><pitch>%s</pitch><bgsound>%s</bgsound></LandingCall>\
|
||||
''' % (
|
||||
to, mediaName, mediaTxt, self.AppId, displayNum, playTimes, respUrl, userData, maxCallTime, speed, volume,
|
||||
pitch, bgsound)
|
||||
if self.BodyType == 'json':
|
||||
body = '''{"to": "%s", "mediaName": "%s","mediaTxt": "%s","appId": "%s","displayNum": "%s","playTimes": "%s","respUrl": "%s","userData": "%s","maxCallTime": "%s","speed": "%s","volume": "%s","pitch": "%s","bgsound": "%s"}''' % (
|
||||
to, mediaName, mediaTxt, self.AppId, displayNum, playTimes, respUrl, userData, maxCallTime, speed, volume,
|
||||
pitch, bgsound)
|
||||
req.data = body.encode()
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 语音验证码
|
||||
# @param verifyCode 必选参数 验证码内容,为数字和英文字母,不区分大小写,长度4-8位
|
||||
# @param playTimes 可选参数 播放次数,1-3次
|
||||
# @param to 必选参数 接收号码
|
||||
# @param displayNum 可选参数 显示的主叫号码
|
||||
# @param respUrl 可选参数 语音验证码状态通知回调地址,云通讯平台将向该Url地址发送呼叫结果通知
|
||||
# @param lang 可选参数 语言类型
|
||||
# @param userData 可选参数 第三方私有数据
|
||||
|
||||
def voiceVerify(self, verifyCode, playTimes, to, displayNum, respUrl, lang, userData):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/Calls/VoiceVerify?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
# auth = base64.encodestring(src).strip()
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
|
||||
req.add_header("Authorization", auth)
|
||||
|
||||
# 创建包体
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?><VoiceVerify>\
|
||||
<appId>%s</appId><verifyCode>%s</verifyCode><playTimes>%s</playTimes><to>%s</to><respUrl>%s</respUrl>\
|
||||
<displayNum>%s</displayNum><lang>%s</lang><userData>%s</userData></VoiceVerify>\
|
||||
''' % (self.AppId, verifyCode, playTimes, to, respUrl, displayNum, lang, userData)
|
||||
if self.BodyType == 'json':
|
||||
# if this model is Json ..then do next code
|
||||
body = '''{"appId": "%s", "verifyCode": "%s","playTimes": "%s","to": "%s","respUrl": "%s","displayNum": "%s","lang": "%s","userData": "%s"}''' % (
|
||||
self.AppId, verifyCode, playTimes, to, respUrl, displayNum, lang, userData)
|
||||
req.data = body.encode()
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# IVR外呼
|
||||
# @param number 必选参数 待呼叫号码,为Dial节点的属性
|
||||
# @param userdata 可选参数 用户数据,在<startservice>通知中返回,只允许填写数字字符,为Dial节点的属性
|
||||
# @param record 可选参数 是否录音,可填项为true和false,默认值为false不录音,为Dial节点的属性
|
||||
|
||||
def ivrDial(self, number, userdata, record):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch;
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/ivr/dial?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
req.add_header("Accept", "application/xml")
|
||||
req.add_header("Content-Type", "application/xml;charset=utf-8")
|
||||
req.add_header("Authorization", auth)
|
||||
|
||||
# 创建包体
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?>
|
||||
<Request>
|
||||
<Appid>%s</Appid>
|
||||
<Dial number="%s" userdata="%s" record="%s"></Dial>
|
||||
</Request>
|
||||
''' % (self.AppId, number, userdata, record)
|
||||
req.data = body.encode()
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 话单下载
|
||||
# @param date 必选参数 day 代表前一天的数据(从00:00 – 23:59),目前只支持按天查询
|
||||
# @param keywords 可选参数 客户的查询条件,由客户自行定义并提供给云通讯平台。默认不填忽略此参数
|
||||
def billRecords(self, date, keywords):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/BillRecords?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
req.add_header("Authorization", auth)
|
||||
|
||||
# 创建包体
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?><BillRecords>\
|
||||
<appId>%s</appId><date>%s</date><keywords>%s</keywords>\
|
||||
</BillRecords>\
|
||||
''' % (self.AppId, date, keywords)
|
||||
if self.BodyType == 'json':
|
||||
# if this model is Json ..then do next code
|
||||
body = '''{"appId": "%s", "date": "%s","keywords": "%s"}''' % (self.AppId, date, keywords)
|
||||
req.data = body.encode()
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 主帐号信息查询
|
||||
|
||||
def queryAccountInfo(self):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/AccountInfo?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
body = ''
|
||||
req.add_header("Authorization", auth)
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 短信模板查询
|
||||
# @param templateId 必选参数 模板Id,不带此参数查询全部可用模板
|
||||
|
||||
def QuerySMSTemplate(self, templateId):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/SMS/QuerySMSTemplate?sig=" + sig
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
|
||||
req.add_header("Authorization", auth)
|
||||
|
||||
# 创建包体
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?><Request>\
|
||||
<appId>%s</appId><templateId>%s</templateId></Request>
|
||||
''' % (self.AppId, templateId)
|
||||
if self.BodyType == 'json':
|
||||
# if this model is Json ..then do next code
|
||||
body = '''{"appId": "%s", "templateId": "%s"}''' % (self.AppId, templateId)
|
||||
req.data = body.encode()
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main2(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 呼叫结果查询
|
||||
# @param callsid 必选参数 呼叫ID
|
||||
|
||||
def CallResult(self, callSid):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/CallResult?sig=" + sig + "&callsid=" + callSid
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
body = ''
|
||||
req.add_header("Authorization", auth)
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 呼叫状态查询
|
||||
# @param callid 必选参数 一个由32个字符组成的电话唯一标识符
|
||||
# @param action 可选参数 查询结果通知的回调url地址
|
||||
def QueryCallState(self, callid, action):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/ivr/call?sig=" + sig + "&callid=" + callid
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
self.setHttpHeader(req)
|
||||
req.add_header("Authorization", auth)
|
||||
|
||||
# 创建包体
|
||||
body = '''<?xml version="1.0" encoding="utf-8"?><Request>\
|
||||
<Appid>%s</Appid><QueryCallState callid="%s" action="%s"/>\
|
||||
</Request>\
|
||||
''' % (self.AppId, callid, action)
|
||||
if self.BodyType == 'json':
|
||||
# if this model is Json ..then do next code
|
||||
body = '''{"Appid":"%s","QueryCallState":{"callid":"%s","action":"%s"}}''' % (self.AppId, callid, action)
|
||||
req.data = body.encode()
|
||||
data = ''
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 语音文件上传
|
||||
# @param filename 必选参数 文件名
|
||||
# @param body 必选参数 二进制串
|
||||
def MediaFileUpload(self, filename, body):
|
||||
|
||||
self.accAuth()
|
||||
nowdate = datetime.datetime.now()
|
||||
self.Batch = nowdate.strftime("%Y%m%d%H%M%S")
|
||||
# 生成sig
|
||||
signature = self.AccountSid + self.AccountToken + self.Batch
|
||||
sig = md5(signature.encode()).hexdigest().upper()
|
||||
# 拼接URL
|
||||
url = "https://" + self.ServerIP + ":" + self.ServerPort + "/" + self.SoftVersion + "/Accounts/" + self.AccountSid + "/Calls/MediaFileUpload?sig=" + sig + "&appid=" + self.AppId + "&filename=" + filename
|
||||
# 生成auth
|
||||
src = self.AccountSid + ":" + self.Batch
|
||||
auth = base64.encodebytes(src.encode()).decode().strip()
|
||||
req = urllib2.Request(url)
|
||||
req.add_header("Authorization", auth)
|
||||
if self.BodyType == 'json':
|
||||
req.add_header("Accept", "application/json")
|
||||
req.add_header("Content-Type", "application/octet-stream")
|
||||
|
||||
else:
|
||||
req.add_header("Accept", "application/xml")
|
||||
req.add_header("Content-Type", "application/octet-stream")
|
||||
|
||||
# 创建包体
|
||||
req.data = body.encode()
|
||||
|
||||
try:
|
||||
res = urllib2.urlopen(req)
|
||||
data = res.read()
|
||||
|
||||
res.close()
|
||||
|
||||
if self.BodyType == 'json':
|
||||
# json格式
|
||||
locations = json.loads(data)
|
||||
else:
|
||||
# xml格式
|
||||
xtj = xmltojson()
|
||||
locations = xtj.main(data)
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return locations
|
||||
except Exception as error:
|
||||
if self.Iflog:
|
||||
self.log(url, body, data)
|
||||
return {'172001': '网络错误'}
|
||||
|
||||
# 子帐号鉴权
|
||||
def subAuth(self):
|
||||
if (self.ServerIP == ""):
|
||||
print('172004')
|
||||
print('IP为空')
|
||||
|
||||
if (int(self.ServerPort) <= 0):
|
||||
print('172005')
|
||||
print('端口错误(小于等于0)')
|
||||
|
||||
if (self.SoftVersion == ""):
|
||||
print('172013')
|
||||
print('版本号为空')
|
||||
|
||||
if (self.SubAccountSid == ""):
|
||||
print('172008')
|
||||
print('子帐号为空')
|
||||
|
||||
if (self.SubAccountToken == ""):
|
||||
print('172009')
|
||||
print('子帐号令牌为空')
|
||||
|
||||
if (self.AppId == ""):
|
||||
print('172012')
|
||||
print('应用ID为空')
|
||||
|
||||
# 主帐号鉴权
|
||||
def accAuth(self):
|
||||
if (self.ServerIP == ""):
|
||||
print('172004')
|
||||
print('IP为空')
|
||||
|
||||
if (int(self.ServerPort) <= 0):
|
||||
print('172005')
|
||||
print('端口错误(小于等于0)')
|
||||
|
||||
if (self.SoftVersion == ""):
|
||||
print('172013')
|
||||
print('版本号为空')
|
||||
|
||||
if (self.AccountSid == ""):
|
||||
print('172006')
|
||||
print('主帐号为空')
|
||||
|
||||
if (self.AccountToken == ""):
|
||||
print('172007')
|
||||
print('主帐号令牌为空')
|
||||
|
||||
if (self.AppId == ""):
|
||||
print('172012')
|
||||
print('应用ID为空')
|
||||
|
||||
# 设置包头
|
||||
def setHttpHeader(self, req):
|
||||
if self.BodyType == 'json':
|
||||
req.add_header("Accept", "application/json")
|
||||
req.add_header("Content-Type", "application/json;charset=utf-8")
|
||||
|
||||
else:
|
||||
req.add_header("Accept", "application/xml")
|
||||
req.add_header("Content-Type", "application/xml;charset=utf-8")
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,75 @@
|
|||
# -*- coding:utf-8 -*-
|
||||
|
||||
from libs.yuntongxun.CCPRestSDK import REST
|
||||
|
||||
# 说明:主账号,登陆云通讯网站后,可在"控制台-应用"中看到开发者主账号ACCOUNT SID
|
||||
_accountSid = '8a216da87e0595ef017e064eefcc00cd'
|
||||
|
||||
# 说明:主账号Token,登陆云通讯网站后,可在控制台-应用中看到开发者主账号AUTH TOKEN
|
||||
_accountToken = '38795c32c26040dfb4b5c23c655dd1d5'
|
||||
|
||||
# 请使用管理控制台首页的APPID或自己创建应用的APPID
|
||||
_appId = '8a216da87e0595ef017e064ef0d300d4'
|
||||
|
||||
# 说明:请求地址,生产环境配置成app.cloopen.com
|
||||
_serverIP = 'sandboxapp.cloopen.com'
|
||||
|
||||
# 说明:请求端口 ,生产环境为8883
|
||||
_serverPort = "8883"
|
||||
|
||||
# 说明:REST API版本号保持不变
|
||||
_softVersion = '2013-12-26'
|
||||
|
||||
# 云通讯官方提供的发送短信代码实例
|
||||
# # 发送模板短信
|
||||
# # @param to 手机号码
|
||||
# # @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
|
||||
# # @param $tempId 模板Id
|
||||
#
|
||||
# def sendTemplateSMS(to, datas, tempId):
|
||||
# # 初始化REST SDK
|
||||
# rest = REST(serverIP, serverPort, softVersion)
|
||||
# rest.setAccount(accountSid, accountToken)
|
||||
# rest.setAppId(appId)
|
||||
#
|
||||
# result = rest.sendTemplateSMS(to, datas, tempId)
|
||||
# for k, v in result.iteritems():
|
||||
#
|
||||
# if k == 'templateSMS':
|
||||
# for k, s in v.iteritems():
|
||||
# print '%s:%s' % (k, s)
|
||||
# else:
|
||||
# print '%s:%s' % (k, v)
|
||||
|
||||
|
||||
class CCP(object):
|
||||
"""发送短信的辅助类"""
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
# 判断是否存在类属性_instance,_instance是类CCP的唯一对象,即单例
|
||||
if not hasattr(CCP, "_instance"):
|
||||
cls._instance = super(CCP, cls).__new__(cls, *args, **kwargs)
|
||||
cls._instance.rest = REST(_serverIP, _serverPort, _softVersion)
|
||||
cls._instance.rest.setAccount(_accountSid, _accountToken)
|
||||
cls._instance.rest.setAppId(_appId)
|
||||
return cls._instance
|
||||
|
||||
def send_template_sms(self, to, datas, temp_id):
|
||||
"""发送模板短信"""
|
||||
# @param to 手机号码
|
||||
# @param datas 内容数据 格式为数组 例如:{'12','34'},如不需替换请填 ''
|
||||
# @param temp_id 模板Id
|
||||
result = self.rest.sendTemplateSMS(to, datas, temp_id)
|
||||
# 如果云通讯发送短信成功,返回的字典数据result中statuCode字段的值为"000000"
|
||||
if result.get("statusCode") == "000000":
|
||||
# 返回0 表示发送短信成功
|
||||
return 0
|
||||
else:
|
||||
# 返回-1 表示发送失败
|
||||
return -1
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
ccp = CCP()
|
||||
# 注意: 测试的短信模板编号为1
|
||||
ccp.send_template_sms('18368421420', ['1234', 5], 1)
|
|
@ -0,0 +1,170 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# python xml.etree.ElementTree
|
||||
|
||||
import os
|
||||
import xml.etree.ElementTree as ET
|
||||
from xml.dom import minidom
|
||||
|
||||
|
||||
class xmltojson:
|
||||
# global var
|
||||
# show log
|
||||
SHOW_LOG = True
|
||||
# XML file
|
||||
XML_PATH = None
|
||||
a = {}
|
||||
m = []
|
||||
|
||||
def get_root(self, path):
|
||||
'''parse the XML file,and get the tree of the XML file
|
||||
finally,return the root element of the tree.
|
||||
if the XML file dose not exist,then print the information'''
|
||||
# if os.path.exists(path):
|
||||
# if SHOW_LOG:
|
||||
# print('start to parse the file : [{}]'.format(path))
|
||||
tree = ET.fromstring(path)
|
||||
return tree
|
||||
# else:
|
||||
# print('the path [{}] dose not exist!'.format(path))
|
||||
|
||||
def get_element_tag(self, element):
|
||||
'''return the element tag if the element is not None.'''
|
||||
if element is not None:
|
||||
|
||||
return element.tag
|
||||
else:
|
||||
print('the element is None!')
|
||||
|
||||
def get_element_attrib(self, element):
|
||||
'''return the element attrib if the element is not None.'''
|
||||
if element is not None:
|
||||
|
||||
return element.attrib
|
||||
else:
|
||||
print('the element is None!')
|
||||
|
||||
def get_element_text(self, element):
|
||||
'''return the text of the element.'''
|
||||
if element is not None:
|
||||
return element.text
|
||||
else:
|
||||
print('the element is None!')
|
||||
|
||||
def get_element_children(self, element):
|
||||
'''return the element children if the element is not None.'''
|
||||
if element is not None:
|
||||
|
||||
return [c for c in element]
|
||||
else:
|
||||
print('the element is None!')
|
||||
|
||||
def get_elements_tag(self, elements):
|
||||
'''return the list of tags of element's tag'''
|
||||
if elements is not None:
|
||||
tags = []
|
||||
for e in elements:
|
||||
tags.append(e.tag)
|
||||
return tags
|
||||
else:
|
||||
print('the elements is None!')
|
||||
|
||||
def get_elements_attrib(self, elements):
|
||||
'''return the list of attribs of element's attrib'''
|
||||
if elements is not None:
|
||||
attribs = []
|
||||
for a in elements:
|
||||
attribs.append(a.attrib)
|
||||
return attribs
|
||||
else:
|
||||
print('the elements is None!')
|
||||
|
||||
def get_elements_text(self, elements):
|
||||
'''return the dict of element'''
|
||||
if elements is not None:
|
||||
text = []
|
||||
for t in elements:
|
||||
text.append(t.text)
|
||||
return dict(zip(self.get_elements_tag(elements), text))
|
||||
else:
|
||||
print('the elements is None!')
|
||||
|
||||
def main(self, xml):
|
||||
# root
|
||||
root = self.get_root(xml)
|
||||
|
||||
# children
|
||||
children = self.get_element_children(root)
|
||||
|
||||
children_tags = self.get_elements_tag(children)
|
||||
|
||||
children_attribs = self.get_elements_attrib(children)
|
||||
|
||||
i = 0
|
||||
|
||||
# 获取二级元素的每一个子节点的名称和值
|
||||
for c in children:
|
||||
p = 0
|
||||
c_children = self.get_element_children(c)
|
||||
dict_text = self.get_elements_text(c_children)
|
||||
if dict_text:
|
||||
# print (children_tags[i])
|
||||
if children_tags[i] == 'TemplateSMS':
|
||||
self.a['templateSMS'] = dict_text
|
||||
else:
|
||||
if children_tags[i] == 'SubAccount':
|
||||
k = 0
|
||||
|
||||
for x in children:
|
||||
if children_tags[k] == 'totalCount':
|
||||
self.m.append(dict_text)
|
||||
self.a['SubAccount'] = self.m
|
||||
p = 1
|
||||
k = k + 1
|
||||
if p == 0:
|
||||
self.a[children_tags[i]] = dict_text
|
||||
else:
|
||||
self.a[children_tags[i]] = dict_text
|
||||
|
||||
|
||||
else:
|
||||
self.a[children_tags[i]] = c.text
|
||||
i = i + 1
|
||||
return self.a
|
||||
|
||||
def main2(self, xml):
|
||||
# root
|
||||
root = self.get_root(xml)
|
||||
|
||||
# children
|
||||
children = self.get_element_children(root)
|
||||
|
||||
children_tags = self.get_elements_tag(children)
|
||||
|
||||
children_attribs = self.get_elements_attrib(children)
|
||||
|
||||
i = 0
|
||||
|
||||
# 获取二级元素的每一个子节点的名称和值
|
||||
for c in children:
|
||||
p = 0
|
||||
c_children = self.get_element_children(c)
|
||||
dict_text = self.get_elements_text(c_children)
|
||||
if dict_text:
|
||||
if children_tags[i] == 'TemplateSMS':
|
||||
k = 0
|
||||
|
||||
for x in children:
|
||||
if children_tags[k] == 'totalCount':
|
||||
self.m.append(dict_text)
|
||||
self.a['TemplateSMS'] = self.m
|
||||
p = 1
|
||||
k = k + 1
|
||||
if p == 0:
|
||||
self.a[children_tags[i]] = dict_text
|
||||
else:
|
||||
self.a[children_tags[i]] = dict_text
|
||||
|
||||
else:
|
||||
self.a[children_tags[i]] = c.text
|
||||
i = i + 1
|
||||
return self.a
|
|
@ -0,0 +1,25 @@
|
|||
[2021-12-26 21:36:17,745] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:36:19,428] [basehttp.py:154] [basehttp:log_message] [ERROR]- "GET / HTTP/1.1" 500 59
|
||||
[2021-12-26 21:36:51,530] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:36:52,672] [basehttp.py:154] [basehttp:log_message] [ERROR]- "GET / HTTP/1.1" 500 59
|
||||
[2021-12-26 21:40:49,355] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:40:50,384] [basehttp.py:154] [basehttp:log_message] [INFO]- "GET / HTTP/1.1" 200 4
|
||||
[2021-12-26 21:46:43,823] [autoreload.py:203] [autoreload:trigger_reload] [INFO]- C:\Users\GLL\Desktop\web\demo\blog\blog\urls.py changed, reloading.
|
||||
[2021-12-26 21:46:44,482] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:47:27,951] [autoreload.py:203] [autoreload:trigger_reload] [INFO]- C:\Users\GLL\Desktop\web\demo\blog\blog\urls.py changed, reloading.
|
||||
[2021-12-26 21:47:28,662] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:47:36,891] [autoreload.py:203] [autoreload:trigger_reload] [INFO]- C:\Users\GLL\Desktop\web\demo\blog\blog\urls.py changed, reloading.
|
||||
[2021-12-26 21:47:37,733] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:47:54,492] [autoreload.py:203] [autoreload:trigger_reload] [INFO]- C:\Users\GLL\Desktop\web\demo\blog\blog\urls.py changed, reloading.
|
||||
[2021-12-26 21:47:55,567] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:48:49,300] [autoreload.py:203] [autoreload:trigger_reload] [INFO]- C:\Users\GLL\Desktop\web\demo\blog\blog\urls.py changed, reloading.
|
||||
[2021-12-26 21:48:50,357] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:49:10,297] [autoreload.py:203] [autoreload:trigger_reload] [INFO]- C:\Users\GLL\Desktop\web\demo\blog\blog\urls.py changed, reloading.
|
||||
[2021-12-26 21:49:11,140] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:51:24,541] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-26 21:51:28,039] [urls.py:26] [urls:log] [INFO]- info
|
||||
[2021-12-26 21:51:28,042] [basehttp.py:154] [basehttp:log_message] [INFO]- "GET / HTTP/1.1" 200 4
|
||||
[2021-12-26 21:51:29,213] [urls.py:26] [urls:log] [INFO]- info
|
||||
[2021-12-26 21:51:29,215] [basehttp.py:154] [basehttp:log_message] [INFO]- "GET / HTTP/1.1" 200 4
|
||||
[2021-12-26 21:51:30,845] [urls.py:26] [urls:log] [INFO]- info
|
||||
[2021-12-26 21:51:30,847] [basehttp.py:154] [basehttp:log_message] [INFO]- "GET / HTTP/1.1" 200 4
|
|
@ -0,0 +1,9 @@
|
|||
[2021-12-27 13:30:43,521] [autoreload.py:578] [autoreload:run_with_reloader] [INFO]- Watching for file changes with StatReloader
|
||||
[2021-12-27 13:30:47,133] [urls.py:29] [urls:log] [INFO]- info
|
||||
[2021-12-27 13:30:47,143] [basehttp.py:154] [basehttp:log_message] [INFO]- "GET / HTTP/1.1" 200 4
|
||||
[2021-12-27 13:30:47,283] [log.py:222] [log:log_response] [WARNING]- Not Found: /favicon.ico
|
||||
[2021-12-27 13:30:47,283] [basehttp.py:154] [basehttp:log_message] [WARNING]- "GET /favicon.ico HTTP/1.1" 404 2323
|
||||
[2021-12-27 13:30:52,156] [urls.py:29] [urls:log] [INFO]- info
|
||||
[2021-12-27 13:30:52,157] [basehttp.py:154] [basehttp:log_message] [INFO]- "GET / HTTP/1.1" 200 4
|
||||
[2021-12-27 13:30:53,023] [urls.py:29] [urls:log] [INFO]- info
|
||||
[2021-12-27 13:30:53,025] [basehttp.py:154] [basehttp:log_message] [INFO]- "GET / HTTP/1.1" 200 4
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,22 @@
|
|||
#!/usr/bin/env python
|
||||
"""Django's command-line utility for administrative tasks."""
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main():
|
||||
"""Run administrative tasks."""
|
||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog.settings')
|
||||
try:
|
||||
from django.core.management import execute_from_command_line
|
||||
except ImportError as exc:
|
||||
raise ImportError(
|
||||
"Couldn't import Django. Are you sure it's installed and "
|
||||
"available on your PYTHONPATH environment variable? Did you "
|
||||
"forget to activate a virtual environment?"
|
||||
) from exc
|
||||
execute_from_command_line(sys.argv)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
|
@ -0,0 +1,50 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- 网站主语言 -->
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<!-- 网站采用的字符编码 -->
|
||||
<meta charset="utf-8">
|
||||
<!-- 网站标题 -->
|
||||
<title>404</title>
|
||||
<!-- 引入bootstrap的css文件 -->
|
||||
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<!-- 定义导航栏 -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<!-- 导航栏商标 -->
|
||||
<div>
|
||||
<a class="navbar-brand" href="../templates/index.html">个人博客</a>
|
||||
</div>
|
||||
</div>
|
||||
<!--登录/个人中心-->
|
||||
<div class="navbar-collapse">
|
||||
</div>
|
||||
</nav>
|
||||
<!--content-->
|
||||
<div class="container" style="height: 600px;margin-top: 20px">
|
||||
<img src="img/404.png" alt="">
|
||||
<br>
|
||||
<div style="text-align: center;">
|
||||
<a href="/" class="primaryAction btn btn-primary">返回首页</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="py-3 bg-dark" id="footer">
|
||||
<div class="container">
|
||||
<h5 class="m-0 text-center text-white">Copyright @ qiruihua</h5>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
|
||||
</html>
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,331 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html {
|
||||
font-family: sans-serif;
|
||||
line-height: 1.15;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
-ms-text-size-adjust: 100%;
|
||||
-ms-overflow-style: scrollbar;
|
||||
-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
@-ms-viewport {
|
||||
width: device-width;
|
||||
}
|
||||
|
||||
article, aside, figcaption, figure, footer, header, hgroup, main, nav, section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
line-height: 1.5;
|
||||
color: #212529;
|
||||
text-align: left;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
[tabindex="-1"]:focus {
|
||||
outline: 0 !important;
|
||||
}
|
||||
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
abbr[title],
|
||||
abbr[data-original-title] {
|
||||
text-decoration: underline;
|
||||
-webkit-text-decoration: underline dotted;
|
||||
text-decoration: underline dotted;
|
||||
cursor: help;
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
address {
|
||||
margin-bottom: 1rem;
|
||||
font-style: normal;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
ol,
|
||||
ul,
|
||||
dl {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
ol ol,
|
||||
ul ul,
|
||||
ol ul,
|
||||
ul ol {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
dt {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-bottom: .5rem;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
blockquote {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
b,
|
||||
strong {
|
||||
font-weight: bolder;
|
||||
}
|
||||
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
sub {
|
||||
bottom: -.25em;
|
||||
}
|
||||
|
||||
sup {
|
||||
top: -.5em;
|
||||
}
|
||||
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
-webkit-text-decoration-skip: objects;
|
||||
}
|
||||
|
||||
a:hover {
|
||||
color: #0056b3;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]) {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):hover, a:not([href]):not([tabindex]):focus {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
a:not([href]):not([tabindex]):focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
pre,
|
||||
code,
|
||||
kbd,
|
||||
samp {
|
||||
font-family: SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
margin-top: 0;
|
||||
margin-bottom: 1rem;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
}
|
||||
|
||||
figure {
|
||||
margin: 0 0 1rem;
|
||||
}
|
||||
|
||||
img {
|
||||
vertical-align: middle;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
svg {
|
||||
overflow: hidden;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
caption {
|
||||
padding-top: 0.75rem;
|
||||
padding-bottom: 0.75rem;
|
||||
color: #6c757d;
|
||||
text-align: left;
|
||||
caption-side: bottom;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
label {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
button {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline: 1px dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
}
|
||||
|
||||
input,
|
||||
button,
|
||||
select,
|
||||
optgroup,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
button,
|
||||
input {
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
button,
|
||||
html [type="button"],
|
||||
[type="reset"],
|
||||
[type="submit"] {
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
button::-moz-focus-inner,
|
||||
[type="button"]::-moz-focus-inner,
|
||||
[type="reset"]::-moz-focus-inner,
|
||||
[type="submit"]::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border-style: none;
|
||||
}
|
||||
|
||||
input[type="radio"],
|
||||
input[type="checkbox"] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
input[type="date"],
|
||||
input[type="time"],
|
||||
input[type="datetime-local"],
|
||||
input[type="month"] {
|
||||
-webkit-appearance: listbox;
|
||||
}
|
||||
|
||||
textarea {
|
||||
overflow: auto;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
fieldset {
|
||||
min-width: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
legend {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
padding: 0;
|
||||
margin-bottom: .5rem;
|
||||
font-size: 1.5rem;
|
||||
line-height: inherit;
|
||||
color: inherit;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
progress {
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
[type="number"]::-webkit-inner-spin-button,
|
||||
[type="number"]::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
[type="search"] {
|
||||
outline-offset: -2px;
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
[type="search"]::-webkit-search-cancel-button,
|
||||
[type="search"]::-webkit-search-decoration {
|
||||
-webkit-appearance: none;
|
||||
}
|
||||
|
||||
::-webkit-file-upload-button {
|
||||
font: inherit;
|
||||
-webkit-appearance: button;
|
||||
}
|
||||
|
||||
output {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
summary {
|
||||
display: list-item;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[hidden] {
|
||||
display: none !important;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-reboot.css.map */
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,8 @@
|
|||
/*!
|
||||
* Bootstrap Reboot v4.1.3 (https://getbootstrap.com/)
|
||||
* Copyright 2011-2018 The Bootstrap Authors
|
||||
* Copyright 2011-2018 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* Forked from Normalize.css, licensed MIT (https://github.com/necolas/normalize.css/blob/master/LICENSE.md)
|
||||
*/*,::after,::before{box-sizing:border-box}html{font-family:sans-serif;line-height:1.15;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%;-ms-overflow-style:scrollbar;-webkit-tap-highlight-color:transparent}@-ms-viewport{width:device-width}article,aside,figcaption,figure,footer,header,hgroup,main,nav,section{display:block}body{margin:0;font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";font-size:1rem;font-weight:400;line-height:1.5;color:#212529;text-align:left;background-color:#fff}[tabindex="-1"]:focus{outline:0!important}hr{box-sizing:content-box;height:0;overflow:visible}h1,h2,h3,h4,h5,h6{margin-top:0;margin-bottom:.5rem}p{margin-top:0;margin-bottom:1rem}abbr[data-original-title],abbr[title]{text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted;cursor:help;border-bottom:0}address{margin-bottom:1rem;font-style:normal;line-height:inherit}dl,ol,ul{margin-top:0;margin-bottom:1rem}ol ol,ol ul,ul ol,ul ul{margin-bottom:0}dt{font-weight:700}dd{margin-bottom:.5rem;margin-left:0}blockquote{margin:0 0 1rem}dfn{font-style:italic}b,strong{font-weight:bolder}small{font-size:80%}sub,sup{position:relative;font-size:75%;line-height:0;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}a{color:#007bff;text-decoration:none;background-color:transparent;-webkit-text-decoration-skip:objects}a:hover{color:#0056b3;text-decoration:underline}a:not([href]):not([tabindex]){color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus,a:not([href]):not([tabindex]):hover{color:inherit;text-decoration:none}a:not([href]):not([tabindex]):focus{outline:0}code,kbd,pre,samp{font-family:SFMono-Regular,Menlo,Monaco,Consolas,"Liberation Mono","Courier New",monospace;font-size:1em}pre{margin-top:0;margin-bottom:1rem;overflow:auto;-ms-overflow-style:scrollbar}figure{margin:0 0 1rem}img{vertical-align:middle;border-style:none}svg{overflow:hidden;vertical-align:middle}table{border-collapse:collapse}caption{padding-top:.75rem;padding-bottom:.75rem;color:#6c757d;text-align:left;caption-side:bottom}th{text-align:inherit}label{display:inline-block;margin-bottom:.5rem}button{border-radius:0}button:focus{outline:1px dotted;outline:5px auto -webkit-focus-ring-color}button,input,optgroup,select,textarea{margin:0;font-family:inherit;font-size:inherit;line-height:inherit}button,input{overflow:visible}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{padding:0;border-style:none}input[type=checkbox],input[type=radio]{box-sizing:border-box;padding:0}input[type=date],input[type=datetime-local],input[type=month],input[type=time]{-webkit-appearance:listbox}textarea{overflow:auto;resize:vertical}fieldset{min-width:0;padding:0;margin:0;border:0}legend{display:block;width:100%;max-width:100%;padding:0;margin-bottom:.5rem;font-size:1.5rem;line-height:inherit;color:inherit;white-space:normal}progress{vertical-align:baseline}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{outline-offset:-2px;-webkit-appearance:none}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{font:inherit;-webkit-appearance:button}output{display:inline-block}summary{display:list-item;cursor:pointer}template{display:none}[hidden]{display:none!important}
|
||||
/*# sourceMappingURL=bootstrap-reboot.min.css.map */
|
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,101 @@
|
|||
<!DOCTYPE html>
|
||||
<!-- 网站主语言 -->
|
||||
<html lang="zh-cn">
|
||||
|
||||
<head>
|
||||
<!-- 网站采用的字符编码 -->
|
||||
<meta charset="utf-8">
|
||||
<!-- 网站标题 -->
|
||||
<title> 用户信息 </title>
|
||||
<!-- 引入bootstrap的css文件 -->
|
||||
<link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
|
||||
<!-- 引入vuejs -->
|
||||
<script type="text/javascript" src="js/vue-2.5.16.js"></script>
|
||||
<script type="text/javascript" src="js/axios-0.18.0.min.js"></script>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app">
|
||||
|
||||
<!-- 定义导航栏 -->
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
|
||||
<div class="container">
|
||||
<!-- 导航栏商标 -->
|
||||
<div>
|
||||
<a class="navbar-brand" href="../templates/index.html">个人博客</a>
|
||||
</div>
|
||||
</div>
|
||||
<!--登录/个人中心-->
|
||||
<div class="navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<!-- 如果用户已经登录,则显示用户名下拉框 -->
|
||||
<li class="nav-item dropdown" v-if="is_login">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" @click="show_menu_click">admin</a>
|
||||
<div class="dropdown-menu" aria-labelledby="navbarDropdown" style="display: block" v-show="show_menu">
|
||||
<a class="dropdown-item" href="write_blog.html">写文章</a>
|
||||
<a class="dropdown-item" href='./center.html'>个人信息</a>
|
||||
<a class="dropdown-item" href='#'>退出登录</a>
|
||||
</div>
|
||||
</li>
|
||||
<!-- 如果用户未登录,则显示登录按钮 -->
|
||||
<li class="nav-item" v-else>
|
||||
<a class="nav-link" href="../templates/login.html">登录</a>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<!--content-->
|
||||
<div class="container" style="margin-bottom: 20px">
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<br>
|
||||
<form method="post" enctype="multipart/form-data">
|
||||
<!-- username -->
|
||||
<div class="form-group col-md-4">
|
||||
<label for="username">用户名</label>
|
||||
<input type="text" class="form-control" id="username" name="username" value="admin" >
|
||||
</div>
|
||||
<!--<br><h5 class="col-md-4">暂无头像</h5><br>-->
|
||||
<br> <div class="col-md-4">头像</div>
|
||||
<img src="img/mei.png" style="max-width: 20%;" class="col-md-4"><br>
|
||||
<!-- avatar -->
|
||||
<div class="form-group col-md-4">
|
||||
<label for="avatar">上传头像</label>
|
||||
<input type="file" class="form-control-file" name="avatar" id="avatar">
|
||||
</div>
|
||||
|
||||
<!-- phone -->
|
||||
<div class="form-group col-md-4">
|
||||
<label for="phone">电话</label>
|
||||
<input type="text" class="form-control" disabled="disabled" id="phone" name="phone" value="">
|
||||
</div>
|
||||
<!-- desc -->
|
||||
<div class="form-group col-md-4">
|
||||
<label for="desc">简介</label>
|
||||
<!-- 文本区域 -->
|
||||
<textarea type="text" class="form-control" id="desc" name="desc" rows="12" ></textarea>
|
||||
</div>
|
||||
<!-- 提交按钮 -->
|
||||
<button type="submit" class="btn btn-primary" style="margin-left: 12px" >修改</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="py-3 bg-dark" id="footer">
|
||||
<div class="container">
|
||||
<h5 class="m-0 text-center text-white">Copyright @ qiruihua</h5>
|
||||
</div>
|
||||
</footer>
|
||||
</div>
|
||||
<!-- 引入js -->
|
||||
<script type="text/javascript" src="js/host.js"></script>
|
||||
<script type="text/javascript" src="js/common.js"></script>
|
||||
<script type="text/javascript" src="js/center.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -0,0 +1,61 @@
|
|||
/* global CKEDITOR */
|
||||
;(function() {
|
||||
var el = document.getElementById('ckeditor-init-script');
|
||||
if (el && !window.CKEDITOR_BASEPATH) {
|
||||
window.CKEDITOR_BASEPATH = el.getAttribute('data-ckeditor-basepath');
|
||||
}
|
||||
|
||||
// Polyfill from https://developer.mozilla.org/en/docs/Web/API/Element/matches
|
||||
if (!Element.prototype.matches) {
|
||||
Element.prototype.matches =
|
||||
Element.prototype.matchesSelector ||
|
||||
Element.prototype.mozMatchesSelector ||
|
||||
Element.prototype.msMatchesSelector ||
|
||||
Element.prototype.oMatchesSelector ||
|
||||
Element.prototype.webkitMatchesSelector ||
|
||||
function(s) {
|
||||
var matches = (this.document || this.ownerDocument).querySelectorAll(s),
|
||||
i = matches.length;
|
||||
while (--i >= 0 && matches.item(i) !== this) {}
|
||||
return i > -1;
|
||||
};
|
||||
}
|
||||
|
||||
function runInitialisers() {
|
||||
initialiseCKEditor();
|
||||
initialiseCKEditorInInlinedForms();
|
||||
}
|
||||
|
||||
if (document.readyState != 'loading') {
|
||||
runInitialisers();
|
||||
} else {
|
||||
document.addEventListener('DOMContentLoaded', runInitialisers);
|
||||
}
|
||||
|
||||
function initialiseCKEditor() {
|
||||
var textareas = Array.prototype.slice.call(document.querySelectorAll('textarea[data-type=ckeditortype]'));
|
||||
for (var i=0; i<textareas.length; ++i) {
|
||||
var t = textareas[i];
|
||||
if (t.getAttribute('data-processed') == '0' && t.id.indexOf('__prefix__') == -1) {
|
||||
t.setAttribute('data-processed', '1');
|
||||
var ext = JSON.parse(t.getAttribute('data-external-plugin-resources'));
|
||||
for (var j=0; j<ext.length; ++j) {
|
||||
CKEDITOR.plugins.addExternal(ext[j][0], ext[j][1], ext[j][2]);
|
||||
}
|
||||
CKEDITOR.replace(t.id, JSON.parse(t.getAttribute('data-config')));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function initialiseCKEditorInInlinedForms() {
|
||||
document.body.addEventListener('click', function(e) {
|
||||
if (e.target && (
|
||||
e.target.matches('.add-row a') ||
|
||||
e.target.matches('.grp-add-handler')
|
||||
)) {
|
||||
initialiseCKEditor();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}());
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,39 @@
|
|||
CKEditor 4
|
||||
==========
|
||||
|
||||
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
|
||||
http://ckeditor.com - See LICENSE.md for license information.
|
||||
|
||||
CKEditor is a text editor to be used inside web pages. It's not a replacement
|
||||
for desktop text editors like Word or OpenOffice, but a component to be used as
|
||||
part of web applications and websites.
|
||||
|
||||
## Documentation
|
||||
|
||||
The full editor documentation is available online at the following address:
|
||||
http://docs.ckeditor.com
|
||||
|
||||
## Installation
|
||||
|
||||
Installing CKEditor is an easy task. Just follow these simple steps:
|
||||
|
||||
1. **Download** the latest version from the CKEditor website:
|
||||
http://ckeditor.com. You should have already completed this step, but be
|
||||
sure you have the very latest version.
|
||||
2. **Extract** (decompress) the downloaded file into the root of your website.
|
||||
|
||||
**Note:** CKEditor is by default installed in the `ckeditor` folder. You can
|
||||
place the files in whichever you want though.
|
||||
|
||||
## Checking Your Installation
|
||||
|
||||
The editor comes with a few sample pages that can be used to verify that
|
||||
installation proceeded properly. Take a look at the `samples` directory.
|
||||
|
||||
To test your installation, just call the following page at your website:
|
||||
|
||||
http://<your site>/<CKEditor installation path>/samples/index.html
|
||||
|
||||
For example:
|
||||
|
||||
http://www.example.com/ckeditor/samples/index.html
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
|
||||
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
(function(a){if("undefined"==typeof a)throw Error("jQuery should be loaded before CKEditor jQuery adapter.");if("undefined"==typeof CKEDITOR)throw Error("CKEditor should be loaded before CKEditor jQuery adapter.");CKEDITOR.config.jqueryOverrideVal="undefined"==typeof CKEDITOR.config.jqueryOverrideVal?!0:CKEDITOR.config.jqueryOverrideVal;a.extend(a.fn,{ckeditorGet:function(){var a=this.eq(0).data("ckeditorInstance");if(!a)throw"CKEditor is not initialized yet, use ckeditor() with a callback.";return a},
|
||||
ckeditor:function(g,d){if(!CKEDITOR.env.isCompatible)throw Error("The environment is incompatible.");if(!a.isFunction(g)){var m=d;d=g;g=m}var k=[];d=d||{};this.each(function(){var b=a(this),c=b.data("ckeditorInstance"),f=b.data("_ckeditorInstanceLock"),h=this,l=new a.Deferred;k.push(l.promise());if(c&&!f)g&&g.apply(c,[this]),l.resolve();else if(f)c.once("instanceReady",function(){setTimeout(function(){c.element?(c.element.$==h&&g&&g.apply(c,[h]),l.resolve()):setTimeout(arguments.callee,100)},0)},
|
||||
null,null,9999);else{if(d.autoUpdateElement||"undefined"==typeof d.autoUpdateElement&&CKEDITOR.config.autoUpdateElement)d.autoUpdateElementJquery=!0;d.autoUpdateElement=!1;b.data("_ckeditorInstanceLock",!0);c=a(this).is("textarea")?CKEDITOR.replace(h,d):CKEDITOR.inline(h,d);b.data("ckeditorInstance",c);c.on("instanceReady",function(d){var e=d.editor;setTimeout(function(){if(e.element){d.removeListener();e.on("dataReady",function(){b.trigger("dataReady.ckeditor",[e])});e.on("setData",function(a){b.trigger("setData.ckeditor",
|
||||
[e,a.data])});e.on("getData",function(a){b.trigger("getData.ckeditor",[e,a.data])},999);e.on("destroy",function(){b.trigger("destroy.ckeditor",[e])});e.on("save",function(){a(h.form).submit();return!1},null,null,20);if(e.config.autoUpdateElementJquery&&b.is("textarea")&&a(h.form).length){var c=function(){b.ckeditor(function(){e.updateElement()})};a(h.form).submit(c);a(h.form).bind("form-pre-serialize",c);b.bind("destroy.ckeditor",function(){a(h.form).unbind("submit",c);a(h.form).unbind("form-pre-serialize",
|
||||
c)})}e.on("destroy",function(){b.removeData("ckeditorInstance")});b.removeData("_ckeditorInstanceLock");b.trigger("instanceReady.ckeditor",[e]);g&&g.apply(e,[h]);l.resolve()}else setTimeout(arguments.callee,100)},0)},null,null,9999)}});var f=new a.Deferred;this.promise=f.promise();a.when.apply(this,k).then(function(){f.resolve()});this.editor=this.eq(0).data("ckeditorInstance");return this}});CKEDITOR.config.jqueryOverrideVal&&(a.fn.val=CKEDITOR.tools.override(a.fn.val,function(g){return function(d){if(arguments.length){var m=
|
||||
this,k=[],f=this.each(function(){var b=a(this),c=b.data("ckeditorInstance");if(b.is("textarea")&&c){var f=new a.Deferred;c.setData(d,function(){f.resolve()});k.push(f.promise());return!0}return g.call(b,d)});if(k.length){var b=new a.Deferred;a.when.apply(this,k).done(function(){b.resolveWith(m)});return b.promise()}return f}var f=a(this).eq(0),c=f.data("ckeditorInstance");return f.is("textarea")&&c?c.getData():g.call(f)}}))})(window.jQuery);
|
|
@ -0,0 +1,189 @@
|
|||
/**
|
||||
* @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see LICENSE.md or http://ckeditor.com/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* This file was added automatically by CKEditor builder.
|
||||
* You may re-use it at any time to build CKEditor again.
|
||||
*
|
||||
* If you would like to build CKEditor online again
|
||||
* (for example to upgrade), visit one the following links:
|
||||
*
|
||||
* (1) http://ckeditor.com/builder
|
||||
* Visit online builder to build CKEditor from scratch.
|
||||
*
|
||||
* (2) http://ckeditor.com/builder/d92ab52ffca329d7b5e1b78ef77a81f9
|
||||
* Visit online builder to build CKEditor, starting with the same setup as before.
|
||||
*
|
||||
* (3) http://ckeditor.com/builder/download/d92ab52ffca329d7b5e1b78ef77a81f9
|
||||
* Straight download link to the latest version of CKEditor (Optimized) with the same setup as before.
|
||||
*
|
||||
* NOTE:
|
||||
* This file is not used by CKEditor, you may remove it.
|
||||
* Changing this file will not change your CKEditor configuration.
|
||||
*/
|
||||
|
||||
var CKBUILDER_CONFIG = {
|
||||
skin: 'moono-lisa',
|
||||
preset: 'full',
|
||||
ignore: [
|
||||
'.DS_Store',
|
||||
'.bender',
|
||||
'.editorconfig',
|
||||
'.gitattributes',
|
||||
'.gitignore',
|
||||
'.idea',
|
||||
'.jscsrc',
|
||||
'.jshintignore',
|
||||
'.jshintrc',
|
||||
'.mailmap',
|
||||
'.travis.yml',
|
||||
'bender-err.log',
|
||||
'bender-out.log',
|
||||
'bender.ci.js',
|
||||
'bender.js',
|
||||
'dev',
|
||||
'gruntfile.js',
|
||||
'less',
|
||||
'node_modules',
|
||||
'package.json',
|
||||
'tests'
|
||||
],
|
||||
plugins : {
|
||||
'a11yhelp' : 1,
|
||||
'about' : 1,
|
||||
'basicstyles' : 1,
|
||||
'bidi' : 1,
|
||||
'blockquote' : 1,
|
||||
'clipboard' : 1,
|
||||
'colorbutton' : 1,
|
||||
'colordialog' : 1,
|
||||
'contextmenu' : 1,
|
||||
'copyformatting' : 1,
|
||||
'dialogadvtab' : 1,
|
||||
'div' : 1,
|
||||
'elementspath' : 1,
|
||||
'enterkey' : 1,
|
||||
'entities' : 1,
|
||||
'filebrowser' : 1,
|
||||
'find' : 1,
|
||||
'flash' : 1,
|
||||
'floatingspace' : 1,
|
||||
'font' : 1,
|
||||
'format' : 1,
|
||||
'forms' : 1,
|
||||
'horizontalrule' : 1,
|
||||
'htmlwriter' : 1,
|
||||
'iframe' : 1,
|
||||
'image' : 1,
|
||||
'indentblock' : 1,
|
||||
'indentlist' : 1,
|
||||
'justify' : 1,
|
||||
'language' : 1,
|
||||
'link' : 1,
|
||||
'list' : 1,
|
||||
'liststyle' : 1,
|
||||
'magicline' : 1,
|
||||
'maximize' : 1,
|
||||
'newpage' : 1,
|
||||
'pagebreak' : 1,
|
||||
'pastefromword' : 1,
|
||||
'pastetext' : 1,
|
||||
'preview' : 1,
|
||||
'print' : 1,
|
||||
'removeformat' : 1,
|
||||
'resize' : 1,
|
||||
'save' : 1,
|
||||
'scayt' : 1,
|
||||
'selectall' : 1,
|
||||
'showblocks' : 1,
|
||||
'showborders' : 1,
|
||||
'smiley' : 1,
|
||||
'sourcearea' : 1,
|
||||
'specialchar' : 1,
|
||||
'stylescombo' : 1,
|
||||
'tab' : 1,
|
||||
'table' : 1,
|
||||
'tableselection' : 1,
|
||||
'tabletools' : 1,
|
||||
'templates' : 1,
|
||||
'toolbar' : 1,
|
||||
'undo' : 1,
|
||||
'uploadimage' : 1,
|
||||
'wsc' : 1,
|
||||
'wysiwygarea' : 1
|
||||
},
|
||||
languages : {
|
||||
'af' : 1,
|
||||
'ar' : 1,
|
||||
'az' : 1,
|
||||
'bg' : 1,
|
||||
'bn' : 1,
|
||||
'bs' : 1,
|
||||
'ca' : 1,
|
||||
'cs' : 1,
|
||||
'cy' : 1,
|
||||
'da' : 1,
|
||||
'de' : 1,
|
||||
'de-ch' : 1,
|
||||
'el' : 1,
|
||||
'en' : 1,
|
||||
'en-au' : 1,
|
||||
'en-ca' : 1,
|
||||
'en-gb' : 1,
|
||||
'eo' : 1,
|
||||
'es' : 1,
|
||||
'es-mx' : 1,
|
||||
'et' : 1,
|
||||
'eu' : 1,
|
||||
'fa' : 1,
|
||||
'fi' : 1,
|
||||
'fo' : 1,
|
||||
'fr' : 1,
|
||||
'fr-ca' : 1,
|
||||
'gl' : 1,
|
||||
'gu' : 1,
|
||||
'he' : 1,
|
||||
'hi' : 1,
|
||||
'hr' : 1,
|
||||
'hu' : 1,
|
||||
'id' : 1,
|
||||
'is' : 1,
|
||||
'it' : 1,
|
||||
'ja' : 1,
|
||||
'ka' : 1,
|
||||
'km' : 1,
|
||||
'ko' : 1,
|
||||
'ku' : 1,
|
||||
'lt' : 1,
|
||||
'lv' : 1,
|
||||
'mk' : 1,
|
||||
'mn' : 1,
|
||||
'ms' : 1,
|
||||
'nb' : 1,
|
||||
'nl' : 1,
|
||||
'no' : 1,
|
||||
'oc' : 1,
|
||||
'pl' : 1,
|
||||
'pt' : 1,
|
||||
'pt-br' : 1,
|
||||
'ro' : 1,
|
||||
'ru' : 1,
|
||||
'si' : 1,
|
||||
'sk' : 1,
|
||||
'sl' : 1,
|
||||
'sq' : 1,
|
||||
'sr' : 1,
|
||||
'sr-latn' : 1,
|
||||
'sv' : 1,
|
||||
'th' : 1,
|
||||
'tr' : 1,
|
||||
'tt' : 1,
|
||||
'ug' : 1,
|
||||
'uk' : 1,
|
||||
'vi' : 1,
|
||||
'zh' : 1,
|
||||
'zh-cn' : 1
|
||||
}
|
||||
};
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,10 @@
|
|||
/**
|
||||
* @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
|
||||
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
|
||||
CKEDITOR.editorConfig = function( config ) {
|
||||
// Define changes to default configuration here. For example:
|
||||
// config.language = 'fr';
|
||||
// config.uiColor = '#AADC6E';
|
||||
};
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
|
||||
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
|
||||
*/
|
||||
|
||||
body
|
||||
{
|
||||
/* Font */
|
||||
font-family: sans-serif, Arial, Verdana, "Trebuchet MS";
|
||||
font-size: 12px;
|
||||
|
||||
/* Text color */
|
||||
color: #333;
|
||||
|
||||
/* Remove the background color to make it transparent */
|
||||
background-color: #fff;
|
||||
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.cke_editable
|
||||
{
|
||||
font-size: 13px;
|
||||
line-height: 1.6;
|
||||
|
||||
/* Fix for missing scrollbars with RTL texts. (#10488) */
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
blockquote
|
||||
{
|
||||
font-style: italic;
|
||||
font-family: Georgia, Times, "Times New Roman", serif;
|
||||
padding: 2px 0;
|
||||
border-style: solid;
|
||||
border-color: #ccc;
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.cke_contents_ltr blockquote
|
||||
{
|
||||
padding-left: 20px;
|
||||
padding-right: 8px;
|
||||
border-left-width: 5px;
|
||||
}
|
||||
|
||||
.cke_contents_rtl blockquote
|
||||
{
|
||||
padding-left: 8px;
|
||||
padding-right: 20px;
|
||||
border-right-width: 5px;
|
||||
}
|
||||
|
||||
a
|
||||
{
|
||||
color: #0782C1;
|
||||
}
|
||||
|
||||
ol,ul,dl
|
||||
{
|
||||
/* IE7: reset rtl list margin. (#7334) */
|
||||
*margin-right: 0px;
|
||||
/* preserved spaces for list items with text direction other than the list. (#6249,#8049)*/
|
||||
padding: 0 40px;
|
||||
}
|
||||
|
||||
h1,h2,h3,h4,h5,h6
|
||||
{
|
||||
font-weight: normal;
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
hr
|
||||
{
|
||||
border: 0px;
|
||||
border-top: 1px solid #ccc;
|
||||
}
|
||||
|
||||
img.right
|
||||
{
|
||||
border: 1px solid #ccc;
|
||||
float: right;
|
||||
margin-left: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
img.left
|
||||
{
|
||||
border: 1px solid #ccc;
|
||||
float: left;
|
||||
margin-right: 15px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
pre
|
||||
{
|
||||
white-space: pre-wrap; /* CSS 2.1 */
|
||||
word-wrap: break-word; /* IE7 */
|
||||
-moz-tab-size: 4;
|
||||
tab-size: 4;
|
||||
}
|
||||
|
||||
.marker
|
||||
{
|
||||
background-color: Yellow;
|
||||
}
|
||||
|
||||
span[lang]
|
||||
{
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
figure
|
||||
{
|
||||
text-align: center;
|
||||
outline: solid 1px #ccc;
|
||||
background: rgba(0,0,0,0.05);
|
||||
padding: 10px;
|
||||
margin: 10px 20px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
figure > figcaption
|
||||
{
|
||||
text-align: center;
|
||||
display: block; /* For IE8 */
|
||||
}
|
||||
|
||||
a > img {
|
||||
padding: 1px;
|
||||
margin: 1px;
|
||||
border: none;
|
||||
outline: 1px solid #0782C1;
|
||||
}
|
||||
|
||||
/* Widget Styles */
|
||||
.code-featured
|
||||
{
|
||||
border: 5px solid red;
|
||||
}
|
||||
|
||||
.math-featured
|
||||
{
|
||||
padding: 20px;
|
||||
box-shadow: 0 0 2px rgba(200, 0, 0, 1);
|
||||
background-color: rgba(255, 0, 0, 0.05);
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.image-clean
|
||||
{
|
||||
border: 0;
|
||||
background: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.image-clean > figcaption
|
||||
{
|
||||
font-size: .9em;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.image-grayscale
|
||||
{
|
||||
background-color: white;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.image-grayscale img, img.image-grayscale
|
||||
{
|
||||
filter: grayscale(100%);
|
||||
}
|
||||
|
||||
.embed-240p
|
||||
{
|
||||
max-width: 426px;
|
||||
max-height: 240px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
.embed-360p
|
||||
{
|
||||
max-width: 640px;
|
||||
max-height: 360px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
.embed-480p
|
||||
{
|
||||
max-width: 854px;
|
||||
max-height: 480px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
.embed-720p
|
||||
{
|
||||
max-width: 1280px;
|
||||
max-height: 720px;
|
||||
margin:0 auto;
|
||||
}
|
||||
|
||||
.embed-1080p
|
||||
{
|
||||
max-width: 1920px;
|
||||
max-height: 1080px;
|
||||
margin:0 auto;
|
||||
}
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue