add first
217
.gitignore
vendored
Normal file
@ -0,0 +1,217 @@
|
|||||||
|
*.pyc
|
||||||
|
.DS_Store
|
||||||
|
.ropeproject
|
||||||
|
.idea
|
||||||
|
### Python template
|
||||||
|
# Byte-compiled / optimized / DLL files
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
|
||||||
|
# C extensions
|
||||||
|
*.so
|
||||||
|
|
||||||
|
# Distribution / packaging
|
||||||
|
.Python
|
||||||
|
build/
|
||||||
|
develop-eggs/
|
||||||
|
dist/
|
||||||
|
downloads/
|
||||||
|
eggs/
|
||||||
|
.eggs/
|
||||||
|
lib/
|
||||||
|
lib64/
|
||||||
|
parts/
|
||||||
|
sdist/
|
||||||
|
var/
|
||||||
|
wheels/
|
||||||
|
share/python-wheels/
|
||||||
|
*.egg-info/
|
||||||
|
.installed.cfg
|
||||||
|
*.egg
|
||||||
|
MANIFEST
|
||||||
|
|
||||||
|
# PyInstaller
|
||||||
|
# Usually these files are written by a python script from a template
|
||||||
|
# before PyInstaller builds the exe, so as to inject date/other infos into it.
|
||||||
|
*.manifest
|
||||||
|
*.spec
|
||||||
|
|
||||||
|
# Installer logs
|
||||||
|
pip-log.txt
|
||||||
|
pip-delete-this-directory.txt
|
||||||
|
|
||||||
|
# Unit test / coverage reports
|
||||||
|
htmlcov/
|
||||||
|
.tox/
|
||||||
|
.nox/
|
||||||
|
.coverage
|
||||||
|
.coverage.*
|
||||||
|
.cache
|
||||||
|
nosetests.xml
|
||||||
|
coverage.xml
|
||||||
|
*.cover
|
||||||
|
*.py,cover
|
||||||
|
.hypothesis/
|
||||||
|
.pytest_cache/
|
||||||
|
cover/
|
||||||
|
|
||||||
|
# Translations
|
||||||
|
*.mo
|
||||||
|
*.pot
|
||||||
|
|
||||||
|
# Django stuff:
|
||||||
|
*.log
|
||||||
|
local_settings.py
|
||||||
|
db.sqlite3
|
||||||
|
db.sqlite3-journal
|
||||||
|
|
||||||
|
# Flask stuff:
|
||||||
|
instance/
|
||||||
|
.webassets-cache
|
||||||
|
|
||||||
|
# Scrapy stuff:
|
||||||
|
.scrapy
|
||||||
|
|
||||||
|
# Sphinx documentation
|
||||||
|
docs/_build/
|
||||||
|
|
||||||
|
# PyBuilder
|
||||||
|
.pybuilder/
|
||||||
|
target/
|
||||||
|
|
||||||
|
# Jupyter Notebook
|
||||||
|
.ipynb_checkpoints
|
||||||
|
|
||||||
|
# IPython
|
||||||
|
profile_default/
|
||||||
|
ipython_config.py
|
||||||
|
|
||||||
|
# pyenv
|
||||||
|
# For a library or package, you might want to ignore these files since the code is
|
||||||
|
# intended to run in multiple environments; otherwise, check them in:
|
||||||
|
# .python-version
|
||||||
|
|
||||||
|
# pipenv
|
||||||
|
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
|
||||||
|
# However, in case of collaboration, if having platform-specific dependencies or dependencies
|
||||||
|
# having no cross-platform support, pipenv may install dependencies that don't work, or not
|
||||||
|
# install all needed dependencies.
|
||||||
|
#Pipfile.lock
|
||||||
|
|
||||||
|
# PEP 582; used by e.g. github.com/David-OConnor/pyflow
|
||||||
|
__pypackages__/
|
||||||
|
|
||||||
|
# Celery stuff
|
||||||
|
celerybeat-schedule
|
||||||
|
celerybeat.pid
|
||||||
|
|
||||||
|
# SageMath parsed files
|
||||||
|
*.sage.py
|
||||||
|
|
||||||
|
# Environments
|
||||||
|
.env
|
||||||
|
.venv
|
||||||
|
env/
|
||||||
|
venv/
|
||||||
|
ENV/
|
||||||
|
env.bak/
|
||||||
|
venv.bak/
|
||||||
|
|
||||||
|
# Spyder project settings
|
||||||
|
.spyderproject
|
||||||
|
.spyproject
|
||||||
|
|
||||||
|
# Rope project settings
|
||||||
|
.ropeproject
|
||||||
|
|
||||||
|
# mkdocs documentation
|
||||||
|
/site
|
||||||
|
|
||||||
|
# mypy
|
||||||
|
.mypy_cache/
|
||||||
|
.dmypy.json
|
||||||
|
dmypy.json
|
||||||
|
|
||||||
|
# Pyre type checker
|
||||||
|
.pyre/
|
||||||
|
|
||||||
|
# pytype static type analyzer
|
||||||
|
.pytype/
|
||||||
|
|
||||||
|
# Cython debug symbols
|
||||||
|
cython_debug/
|
||||||
|
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/**/usage.statistics.xml
|
||||||
|
.idea/**/dictionaries
|
||||||
|
.idea/**/shelf
|
||||||
|
|
||||||
|
# Generated files
|
||||||
|
.idea/**/contentModel.xml
|
||||||
|
|
||||||
|
# Sensitive or high-churn files
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
.idea/**/dbnavigator.xml
|
||||||
|
|
||||||
|
# Gradle
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# Gradle and Maven with auto-import
|
||||||
|
# When using Gradle or Maven with auto-import, you should exclude module files,
|
||||||
|
# since they will be recreated, and may cause churn. Uncomment if using
|
||||||
|
# auto-import.
|
||||||
|
# .idea/artifacts
|
||||||
|
# .idea/compiler.xml
|
||||||
|
# .idea/jarRepositories.xml
|
||||||
|
# .idea/modules.xml
|
||||||
|
# .idea/*.iml
|
||||||
|
# .idea/modules
|
||||||
|
# *.iml
|
||||||
|
# *.ipr
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-*/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
# File-based project format
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
|
||||||
|
# Editor-based Rest Client
|
||||||
|
.idea/httpRequests
|
||||||
|
|
||||||
|
# Android studio 3.1+ serialized cache file
|
||||||
|
.idea/caches/build_file_checksums.ser
|
||||||
|
|
41
app.py
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
#
|
||||||
|
# import importlib,sys
|
||||||
|
# importlib.reload(sys)
|
||||||
|
# sys.setdefaultencoding('utf8')
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
import tornado.httpserver
|
||||||
|
import tornado.ioloop
|
||||||
|
import tornado.options
|
||||||
|
import os
|
||||||
|
from tornado.options import define, options
|
||||||
|
|
||||||
|
from libs import db_session, engine # import the engine to bind
|
||||||
|
#from libs.memcache import cache
|
||||||
|
if os.getenv("ENV")=='dev':
|
||||||
|
from dev_settings import url_handlers, settings
|
||||||
|
else:
|
||||||
|
from settings import url_handlers, settings
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
define("port", default=8000, help="run on the given port", type=int)
|
||||||
|
|
||||||
|
|
||||||
|
class Application(tornado.web.Application):
|
||||||
|
def __init__(self, handlers, **settings):
|
||||||
|
tornado.web.Application.__init__(self, handlers, **settings)
|
||||||
|
# Have one global connection. or call:session
|
||||||
|
self.db = db_session
|
||||||
|
#self.cache = cache
|
||||||
|
#self.redis = redis.StrictRedis()
|
||||||
|
|
||||||
|
application = Application(url_handlers, **settings)
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
tornado.options.parse_command_line()
|
||||||
|
http_server = tornado.httpserver.HTTPServer(application)
|
||||||
|
http_server.listen(options.port)
|
||||||
|
tornado.ioloop.IOLoop.instance().start()
|
59
config.py
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
#后台自定义设置
|
||||||
|
SAVE_LOG_OPEN = 0 # 开启后台日志记录
|
||||||
|
MAX_LOGIN_TIMES = 9 # 最大登录失败次数,防止为0时不能登录,因此不包含第一次登录
|
||||||
|
LOGIN_WAIT_TIME = 60 # 登录次数达到后需要等待时间才能再次登录,单位:分钟
|
||||||
|
|
||||||
|
DATAGRID_PAGE_SIZE = 20 # 列表默认分页数
|
||||||
|
|
||||||
|
# 单独配置,会覆盖全局配置
|
||||||
|
FILE_UPLOAD_CONFIG = {
|
||||||
|
'exts': ['zip', 'rar', 'tar', 'gz', '7z', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt'],
|
||||||
|
'maxSize': 102400
|
||||||
|
}
|
||||||
|
FILE_UPLOAD_LINK_CONFIG = {
|
||||||
|
'exts': ['zip', 'rar', 'tar', 'gz', '7z', 'pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt']
|
||||||
|
}
|
||||||
|
FILE_UPLOAD_IMG_CONFIG = {
|
||||||
|
'exts': ['jpg', 'jpeg', 'gif', 'png']
|
||||||
|
}
|
||||||
|
FILE_UPLOAD_FLASH_CONFIG = {
|
||||||
|
'exts': ['swf']
|
||||||
|
}
|
||||||
|
FILE_UPLOAD_MEDIA_CONFIG = {
|
||||||
|
'exts': ['avi']
|
||||||
|
}
|
||||||
|
|
||||||
|
#'mysql_conn':'mysql+pymysql://miles:aspect@192.168.100.30/test',
|
||||||
|
|
||||||
|
# the sql database settings
|
||||||
|
DATABASE = {
|
||||||
|
'dev': {
|
||||||
|
'driven': 'mysql+pymysql',
|
||||||
|
'host': '192.168.100.30',
|
||||||
|
'user': 'miles',
|
||||||
|
'password': 'aspect',
|
||||||
|
'port': '3306',
|
||||||
|
'database': 'utf-8',
|
||||||
|
},
|
||||||
|
'prod': {
|
||||||
|
'driven': 'mysql+pymysql',
|
||||||
|
'host': '10.10.3.5',
|
||||||
|
'user': 'miles',
|
||||||
|
'password': 'aspect',
|
||||||
|
'port': '3306',
|
||||||
|
'database': 'utf-8',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: the reids database settings
|
||||||
|
REDIS = {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
# TODO: the log settings
|
||||||
|
|
||||||
|
|
||||||
|
# TODO: memcahce usage
|
19
dev_settings.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
from os import path
|
||||||
|
from urls import urls_pattern as url_handlers
|
||||||
|
|
||||||
|
DEBUG = True
|
||||||
|
|
||||||
|
# the application settings
|
||||||
|
settings = {
|
||||||
|
'debug': DEBUG,
|
||||||
|
'cookie_secret': 'test', # TODO: get the real secret
|
||||||
|
'login_url': '/admin/login',
|
||||||
|
'xsrf_cookies': True,
|
||||||
|
'static_path': path.join(path.dirname(__file__), 'static_v1'),
|
||||||
|
'template_path': path.join(path.dirname(__file__), 'templates'),
|
||||||
|
#'ui_modules': '' # TODO: the ui modules file
|
||||||
|
}
|
||||||
|
|
0
handlers/__init__.py
Normal file
23
handlers/admin/__init__.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from tornado.web import RequestHandler
|
||||||
|
from model.admin import Admin as User
|
||||||
|
|
||||||
|
|
||||||
|
class BaseHandler(RequestHandler):
|
||||||
|
@property
|
||||||
|
def db(self):
|
||||||
|
return self.application.db
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cache(self):
|
||||||
|
return self.application.cache
|
||||||
|
|
||||||
|
def get_current_user(self):
|
||||||
|
user_id = self.get_secure_cookie('admin_user_id')
|
||||||
|
if not user_id:
|
||||||
|
return None
|
||||||
|
return self.db.query(User).get(user_id)
|
||||||
|
|
||||||
|
|
10
handlers/admin/index.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
from handlers.admin import BaseHandler
|
||||||
|
|
||||||
|
class AdminIndexHandler(BaseHandler):
|
||||||
|
@tornado.web.authenticated
|
||||||
|
def get(self):
|
||||||
|
self.render('admin/index.html', user=self.current_user)
|
50
handlers/admin/login.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
from tornado.escape import json_encode
|
||||||
|
from utils import encrypt
|
||||||
|
from handlers.admin import BaseHandler
|
||||||
|
from model.admin import Admin as User
|
||||||
|
|
||||||
|
|
||||||
|
class AdminLoginHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '填写信息不完整',
|
||||||
|
'121': '该用户不存在',
|
||||||
|
'122': '密码错误'
|
||||||
|
}
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render('admin/login.html', error=None, email='')
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
username = self.get_argument('username', '')
|
||||||
|
password = self.get_argument('password', '')
|
||||||
|
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if not (username and password):
|
||||||
|
ret = {'error': 110, 'msg': self.error_message['110'], 'url': '/admin/index'}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
user = User.get_by_username(username)
|
||||||
|
if not user:
|
||||||
|
ret = {'error': 121, 'msg': self.error_message['121']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
if user.get_password() != encrypt(password):
|
||||||
|
ret = {'error': 122, 'msg': self.error_message['122']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
self.set_secure_cookie("admin_user_id", str(user.user_id), expires_days=7)
|
||||||
|
ret = {'error': 0, 'msg': '登录成功', 'url': '/admin/index'}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminLogoutHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.clear_cookie('admin_user_id')
|
||||||
|
self.redirect('/admin/login')
|
223
handlers/admin/news.py
Normal file
@ -0,0 +1,223 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tornado.escape import json_encode
|
||||||
|
from handlers.admin import BaseHandler
|
||||||
|
from model.news import News, NewsCategory
|
||||||
|
from utils import date_encode, obj2dict
|
||||||
|
|
||||||
|
|
||||||
|
class NewsListHandler(BaseHandler):
|
||||||
|
|
||||||
|
url = 'admin/news/news_list.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
|
||||||
|
class NewsListDatagridHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
page = self.get_argument('page', 1)
|
||||||
|
rows = self.get_argument('rows', 20)
|
||||||
|
|
||||||
|
title = self.get_argument('title', '')
|
||||||
|
begin = self.get_argument('begin', '')
|
||||||
|
end = self.get_argument('end', '')
|
||||||
|
|
||||||
|
query = {}
|
||||||
|
if title:
|
||||||
|
query['title'] = title
|
||||||
|
if begin:
|
||||||
|
query['begin'] = begin
|
||||||
|
if end:
|
||||||
|
query['end'] = end
|
||||||
|
|
||||||
|
offset = (int(page) - 1) * int(rows)
|
||||||
|
limit = rows
|
||||||
|
rows = News.gets(offset, limit, **query)
|
||||||
|
rows = [obj2dict(r) for r in rows]
|
||||||
|
total = News.get_count()
|
||||||
|
|
||||||
|
response = {'total': total, 'rows': rows}
|
||||||
|
return self.write(date_encode(response))
|
||||||
|
|
||||||
|
|
||||||
|
class NewsEditHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '请选择分类',
|
||||||
|
'111': '请填写标题',
|
||||||
|
'112': '请填写内容',
|
||||||
|
'113': '请填写发布者',
|
||||||
|
'114': '添加失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'admin/news/news_edit.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
news_id = self.get_argument('id', '')
|
||||||
|
news = News.get(news_id)
|
||||||
|
category = NewsCategory.gets()
|
||||||
|
self.render(self.url, info=news, categorys=category)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
|
||||||
|
news_id = int(self.get_argument('id', 0))
|
||||||
|
category_id = int(self.get_argument('category_id', 0))
|
||||||
|
title = self.get_argument('title', '')
|
||||||
|
content = self.get_argument('content', '')
|
||||||
|
create_uid = self.get_secure_cookie('admin_user_id')
|
||||||
|
status = int(self.get_argument('status', 0))
|
||||||
|
|
||||||
|
if not category_id:
|
||||||
|
response = {'code': 110, 'msg': self.error_message['110']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
if not title:
|
||||||
|
response = {'code': 111, 'msg': self.error_message['111']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
if not content:
|
||||||
|
response = {'code': 112, 'msg': self.error_message['112']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
if not create_uid:
|
||||||
|
response = {'code': 113, 'msg': self.error_message['113']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
result = News.update(news_id, category_id, title, content, create_uid, status)
|
||||||
|
if result:
|
||||||
|
response = {'code': 0, 'msg': '添加成功'}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
else:
|
||||||
|
response = {'code': 114, 'msg': self.error_message['114']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
|
||||||
|
class NewsAddHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '请选择分类',
|
||||||
|
'111': '请填写标题',
|
||||||
|
'112': '请填写内容',
|
||||||
|
'113': '请填写发布者',
|
||||||
|
'114': '添加失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'admin/news/news_add.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
|
||||||
|
category_id = int(self.get_argument('category_id', 0))
|
||||||
|
title = self.get_argument('title', '')
|
||||||
|
content = self.get_argument('content', '')
|
||||||
|
create_uid = self.get_secure_cookie('admin_user_id')
|
||||||
|
status = int(self.get_argument('status', 0))
|
||||||
|
|
||||||
|
if not category_id:
|
||||||
|
response = {'code': 110, 'msg': self.error_message['110']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
if not title:
|
||||||
|
response = {'code': 111, 'msg': self.error_message['111']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
if not content:
|
||||||
|
response = {'code': 112, 'msg': self.error_message['112']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
result = News.new(category_id, title, content, create_uid, status)
|
||||||
|
if result:
|
||||||
|
response = {'code': 0, 'msg': '添加成功'}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
else:
|
||||||
|
response = {'code': 114, 'msg': self.error_message['114']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
|
||||||
|
class NewsCategoryListHandler(BaseHandler):
|
||||||
|
|
||||||
|
url = 'admin/news/news_category_list.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
|
||||||
|
class NewsCategoryListDatagridHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
page = self.get_argument('page', 1)
|
||||||
|
rows = self.get_argument('rows', 20)
|
||||||
|
|
||||||
|
start = (int(page) - 1) * int(rows)
|
||||||
|
limit = rows
|
||||||
|
rows = NewsCategory.gets(start, limit)
|
||||||
|
rows = [obj2dict(r) for r in rows]
|
||||||
|
total = NewsCategory.get_count()
|
||||||
|
response = {'total': total, 'rows': rows}
|
||||||
|
return self.write(date_encode(response))
|
||||||
|
|
||||||
|
|
||||||
|
class NewsCategoryEditHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '请选择分类',
|
||||||
|
'111': '分类名为空',
|
||||||
|
'112': '更新失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'admin/news/news_category_edit.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
category_id = self.get_argument('id', '')
|
||||||
|
news_category = NewsCategory.get(category_id)
|
||||||
|
self.render(self.url, info=news_category)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
|
||||||
|
category_id = int(self.get_argument('id', 0))
|
||||||
|
category_name = self.get_argument('category_name', '')
|
||||||
|
|
||||||
|
if not category_id:
|
||||||
|
response = {'code': 110, 'msg': self.error_message['110']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
if not category_name:
|
||||||
|
response = {'code': 111, 'msg': self.error_message['111']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
result = NewsCategory.update(category_id, category_name)
|
||||||
|
if result:
|
||||||
|
response = {'code': 0, 'msg': '更新成功'}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
else:
|
||||||
|
response = {'code': 112, 'msg': self.error_message['112']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
|
||||||
|
class NewsCategoryAddHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '请填写分类',
|
||||||
|
'111': '分类名为空',
|
||||||
|
'112': '添加失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'admin/news/news_category_add.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
|
||||||
|
category_name = self.get_argument('category_name', '')
|
||||||
|
|
||||||
|
if not category_name:
|
||||||
|
response = {'code': 110, 'msg': self.error_message['110']}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
result = NewsCategory.new(category_name)
|
||||||
|
if result:
|
||||||
|
response = {'code': 0, 'msg': '添加成功'}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
else:
|
||||||
|
response = {'code': 112, 'msg': self.error_message['112']}
|
||||||
|
return self.write(json_encode(response))
|
40
handlers/admin/setting.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
from tornado.escape import json_encode
|
||||||
|
from handlers.admin import BaseHandler
|
||||||
|
from model.setting import Setting
|
||||||
|
|
||||||
|
|
||||||
|
class AdminSettingSiteHandler(BaseHandler):
|
||||||
|
|
||||||
|
url = 'admin/setting/site.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
#fields = self.request.arguments
|
||||||
|
fields = self.get_body_arguments('data[]')
|
||||||
|
|
||||||
|
for field in fields:
|
||||||
|
v = field.split('#')
|
||||||
|
key = v[0]
|
||||||
|
value = v[1]
|
||||||
|
Setting.update(key, value)
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'code': 0,
|
||||||
|
'msg': '更新成功'
|
||||||
|
}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminSettingPropertyGridHandler(BaseHandler):
|
||||||
|
|
||||||
|
url = 'admin/setting/site.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
response = Setting.gets()
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
377
handlers/admin/user.py
Normal file
@ -0,0 +1,377 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
from tornado.escape import json_encode
|
||||||
|
from handlers.admin import BaseHandler
|
||||||
|
from model.admin import AdminRole, Admin as User
|
||||||
|
from utils import encrypt, obj2dict, date_encode
|
||||||
|
import re
|
||||||
|
|
||||||
|
|
||||||
|
class AdminEditInfoHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '信息填写不完整',
|
||||||
|
'111': '该用户不存在',
|
||||||
|
'112': '更新失败'
|
||||||
|
}
|
||||||
|
url = 'admin/user/edit_info.html'
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url, user=self.current_user)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
realname = self.get_argument('realname', '')
|
||||||
|
email = self.get_argument('email', '')
|
||||||
|
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if not (realname and email):
|
||||||
|
ret = {'code': 110, 'msg': self.error_message['110'], 'url': self.url}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
user = User.get_by_uid(self.get_secure_cookie("admin_user_id"))
|
||||||
|
if not user:
|
||||||
|
ret = {'code': 111, 'msg': self.error_message['111']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
user.update('', email, realname)
|
||||||
|
ret = {'code': 0, 'msg': '更新成功', 'url': self.url}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminCheckUsernameHandler(BaseHandler):
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
username = self.get_argument('username', '')
|
||||||
|
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
user = User.get_by_username(username)
|
||||||
|
if not user:
|
||||||
|
ret = {'code': 0}
|
||||||
|
else:
|
||||||
|
ret = {'code': 1}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminCheckEmailHandler(BaseHandler):
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
email = self.get_argument('email', '')
|
||||||
|
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
user = User.get_by_email(email)
|
||||||
|
if not user:
|
||||||
|
ret = {'code': 0}
|
||||||
|
else:
|
||||||
|
ret = {'code': 1}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminEditPasswordHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'210': '信息填写不完整',
|
||||||
|
'211': '该用户不存在',
|
||||||
|
'212': '更新失败'
|
||||||
|
}
|
||||||
|
url = 'admin/user/edit_password.html'
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url, user=self.current_user)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
password = self.get_argument('new_password', '')
|
||||||
|
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
ret = {'code': 210, 'msg': self.error_message['210'], 'url': self.url}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
user = User.get_by_uid(self.get_secure_cookie("admin_user_id"))
|
||||||
|
if not user:
|
||||||
|
ret = {'code': 211, 'msg': self.error_message['211']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
user.update_password(password)
|
||||||
|
ret = {'code': 0, 'msg': '更新成功', 'url': self.url}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminCheckPasswordHandler(BaseHandler):
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
password = self.get_argument('password', '')
|
||||||
|
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
user = User.get_by_uid(self.get_secure_cookie("admin_user_id"))
|
||||||
|
if user.get_password() == encrypt(password):
|
||||||
|
ret = {'code': 0}
|
||||||
|
else:
|
||||||
|
ret = {'code': 1}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminMemberListHandler(BaseHandler):
|
||||||
|
|
||||||
|
url = 'admin/user/member_list.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AdminMemberListDatagridHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
page = self.get_argument('page', 1)
|
||||||
|
rows = self.get_argument('rows', 20)
|
||||||
|
|
||||||
|
start = (int(page) - 1) * int(rows)
|
||||||
|
limit = rows
|
||||||
|
user_list = User.gets(start, limit)
|
||||||
|
total = User.get_count()
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'total': total,
|
||||||
|
'rows': user_list
|
||||||
|
}
|
||||||
|
return self.write(date_encode(response))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminMemberAddHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '用户名不能为空',
|
||||||
|
'111': '密码不能为空',
|
||||||
|
'112': '邮箱不匹配',
|
||||||
|
'113': '角色不能为空',
|
||||||
|
'114': '添加失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'admin/user/member_add.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
username = self.get_argument('username', '')
|
||||||
|
password = self.get_argument('password', '')
|
||||||
|
email = self.get_argument('email', '')
|
||||||
|
realname = self.get_argument('realname', '')
|
||||||
|
role_id = self.get_argument('role_id', 0)
|
||||||
|
|
||||||
|
if not username or len(username) > 15:
|
||||||
|
ret = {'code': 110, 'msg': self.error_message['110']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
if not password:
|
||||||
|
ret = {'code': 111, 'msg': self.error_message['111']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
match = re.search(r'[\w.-]+@[\w.-]+', email)
|
||||||
|
if not match:
|
||||||
|
ret = {'code': 112, 'msg': self.error_message['112']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
if not role_id:
|
||||||
|
ret = {'code': 113, 'msg': self.error_message['111']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
result = User.new(username, email, password, realname, role_id)
|
||||||
|
if result:
|
||||||
|
ret = {'code': 0, 'msg': '添加成功'}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
else:
|
||||||
|
ret = {'code': 114, 'msg': self.error_message['114']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class AdminMemberEditHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '用户名不能为空',
|
||||||
|
'111': '密码不能为空',
|
||||||
|
'112': '邮箱不匹配',
|
||||||
|
'113': '角色不能为空',
|
||||||
|
'114': '更新失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'admin/user/member_edit.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
user_id = int(self.get_argument('id', 0))
|
||||||
|
user = User.get(user_id)
|
||||||
|
roles = AdminRole.gets()
|
||||||
|
|
||||||
|
return self.render(self.url, info=user, roles=roles)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
user_id = int(self.get_argument('id', 0))
|
||||||
|
username = self.get_argument('username', '')
|
||||||
|
password = self.get_argument('password', '')
|
||||||
|
email = self.get_argument('email', '')
|
||||||
|
realname = self.get_argument('realname', '')
|
||||||
|
role_id = int(self.get_argument('role_id', 0))
|
||||||
|
|
||||||
|
|
||||||
|
match = re.search(r'[\w.-]+@[\w.-]+', email)
|
||||||
|
if not match:
|
||||||
|
ret = {'code': 112, 'msg': self.error_message['112']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
if not role_id:
|
||||||
|
ret = {'code': 113, 'msg': self.error_message['113']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
result = User.update(user_id, username, email, password, realname, role_id)
|
||||||
|
if result:
|
||||||
|
ret = {'code': 0, 'msg': '更新成功'}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
else:
|
||||||
|
ret = {'code': 114, 'msg': self.error_message['114']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminCheckRoleHandler(BaseHandler):
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
role_name = self.get_argument('role_name', '')
|
||||||
|
|
||||||
|
self.set_header("Content-Type", "application/json")
|
||||||
|
|
||||||
|
role = AdminRole.get_by_rolename(role_name)
|
||||||
|
if not role:
|
||||||
|
ret = {'code': 0}
|
||||||
|
else:
|
||||||
|
ret = {'code': 1}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminRoleListHandler(BaseHandler):
|
||||||
|
|
||||||
|
url = 'admin/user/role_list.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AdminRoleListDatagridHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
page = self.get_argument('page', 1)
|
||||||
|
rows = self.get_argument('rows', 20)
|
||||||
|
sort = self.get_argument('sort', '')
|
||||||
|
order = self.get_argument('order', 'ASC')
|
||||||
|
|
||||||
|
start = (int(page) - 1) * int(rows)
|
||||||
|
limit = rows
|
||||||
|
role_list = AdminRole.gets(start, limit)
|
||||||
|
total = AdminRole.get_count()
|
||||||
|
|
||||||
|
response = {
|
||||||
|
'total': total,
|
||||||
|
'rows': [obj2dict(role) for role in role_list]
|
||||||
|
}
|
||||||
|
return self.write(json_encode(response))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminRoleAddHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'310': '角色名称不能为空',
|
||||||
|
'311': '添加失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'admin/user/role_add.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render(self.url)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
role_name = self.get_argument('role_name', '')
|
||||||
|
description = self.get_argument('description', '')
|
||||||
|
list_order = int(self.get_argument('list_order', 0))
|
||||||
|
status = int(self.get_argument('status', 0))
|
||||||
|
|
||||||
|
if not role_name:
|
||||||
|
ret = {'code': 310, 'msg': self.error_message['310']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
result = AdminRole.new(role_name, description, list_order, status)
|
||||||
|
if result:
|
||||||
|
ret = {'code': 0, 'msg': '添加成功'}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
else:
|
||||||
|
ret = {'code': 311, 'msg': self.error_message['311']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminRoleEditHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'310': '角色名称不能为空',
|
||||||
|
'311': '更新失败'
|
||||||
|
}
|
||||||
|
|
||||||
|
url = 'admin/user/role_edit.html'
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
role_id = int(self.get_argument('id', 0))
|
||||||
|
role = AdminRole.get(role_id)
|
||||||
|
|
||||||
|
return self.render(self.url, info=role)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
role_id = int(self.get_argument('id', 0))
|
||||||
|
role_name = self.get_argument('role_name', '')
|
||||||
|
description = self.get_argument('description', '')
|
||||||
|
list_order = int(self.get_argument('list_order', 0))
|
||||||
|
status = int(self.get_argument('status', 0))
|
||||||
|
|
||||||
|
if not role_name:
|
||||||
|
ret = {'code': 310, 'msg': self.error_message['310']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
result = AdminRole.update(role_id, role_name, description, list_order, status)
|
||||||
|
if result:
|
||||||
|
ret = {'code': 0, 'msg': '更新成功'}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
else:
|
||||||
|
ret = {'code': 311, 'msg': self.error_message['311']}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
class AdminRoleOrderHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
order_role = self.request.arguments
|
||||||
|
|
||||||
|
for key, list_order in enumerate(order_role):
|
||||||
|
role_id = list_order[:]
|
||||||
|
|
||||||
|
list_order = order_role[list_order][0]
|
||||||
|
AdminRole.update(role_id, '', '', list_order, 0)
|
||||||
|
|
||||||
|
ret = {'code': 0, 'msg': '更新成功'}
|
||||||
|
return self.write(json_encode(ret))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
22
handlers/home/__init__.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from tornado.web import RequestHandler
|
||||||
|
from model.user import User
|
||||||
|
|
||||||
|
class BaseHandler(RequestHandler):
|
||||||
|
@property
|
||||||
|
def db(self):
|
||||||
|
return self.application.db
|
||||||
|
|
||||||
|
@property
|
||||||
|
def cache(self):
|
||||||
|
return self.application.cache
|
||||||
|
|
||||||
|
def get_current_user(self):
|
||||||
|
user_id = self.get_secure_cookie('user_id')
|
||||||
|
if not user_id:
|
||||||
|
return None
|
||||||
|
return User.get(user_id)
|
||||||
|
|
||||||
|
|
29
handlers/home/help.py
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
from handlers.home import BaseHandler
|
||||||
|
|
||||||
|
|
||||||
|
class AboutUsHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ContactUsHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class JoinUsHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class OfficialNewsHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
pass
|
10
handlers/home/index.py
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
from handlers.home import BaseHandler
|
||||||
|
|
||||||
|
class IndexHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render('home/index.html', error=None, email='')
|
112
handlers/home/login.py
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import re
|
||||||
|
import tornado.web
|
||||||
|
from utils import encrypt
|
||||||
|
#import libs.captcha
|
||||||
|
from model.user import User
|
||||||
|
from handlers.home import BaseHandler
|
||||||
|
|
||||||
|
class RegisterHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'110': '填写信息不完整',
|
||||||
|
'111': '用户名最多15个字符',
|
||||||
|
'112': '用户名已经被使用',
|
||||||
|
'113': 'Email不正确',
|
||||||
|
'114': 'Email已经被使用',
|
||||||
|
'115': '注册失败,请稍后再试'
|
||||||
|
}
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render('home/register.html', error=None, username='', email='')
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
username = self.get_argument('username', '')
|
||||||
|
email = self.get_argument('email', '')
|
||||||
|
password = self.get_argument('password', '')
|
||||||
|
|
||||||
|
if not username or len(username) > 15:
|
||||||
|
self.render('home/register.html', error=111, username=username, email=email)
|
||||||
|
return
|
||||||
|
match = re.search(r'[\w.-]+@[\w.-]+', email)
|
||||||
|
if not match:
|
||||||
|
self.render('home/register.html', error=113, username=username, email=email)
|
||||||
|
return
|
||||||
|
if not password:
|
||||||
|
self.render('home/register.html', error=110, username=username, email=email)
|
||||||
|
return
|
||||||
|
|
||||||
|
user = User.get_by_username(username)
|
||||||
|
if user:
|
||||||
|
self.render('home/register.html', error=112, username=username, email=email)
|
||||||
|
return
|
||||||
|
|
||||||
|
user = User.get_by_email(email)
|
||||||
|
if user:
|
||||||
|
self.render('home/register.html', error=114, username=username, email=email)
|
||||||
|
return
|
||||||
|
|
||||||
|
#走代理获取ip方式
|
||||||
|
reg_ip = self.request.headers['X-Real-Ip']
|
||||||
|
user = User.new(username, email, password, reg_ip)
|
||||||
|
if user:
|
||||||
|
self.set_secure_cookie('user_id', str(user.user_id), expires_days=30)
|
||||||
|
self.redirect(self.get_argument('next', '/'))
|
||||||
|
else:
|
||||||
|
self.render('home/register.html', error=115)
|
||||||
|
|
||||||
|
|
||||||
|
class LoginHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'100': '信息填写不完整',
|
||||||
|
'101': '该用户不存在',
|
||||||
|
'102': '密码错误',
|
||||||
|
'103': '验证码错误'
|
||||||
|
}
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render('home/login.html', error=None, email='')
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
email = self.get_argument('email', '')
|
||||||
|
password = self.get_argument('password', '')
|
||||||
|
|
||||||
|
if not (email and password):
|
||||||
|
self.render('home/login.html', error=100, email=email)
|
||||||
|
|
||||||
|
user = User.get_by_email(email)
|
||||||
|
if not user:
|
||||||
|
self.render('home/login.html', error=101, email=email)
|
||||||
|
|
||||||
|
if user.get_password() == encrypt(password):
|
||||||
|
last_login_ip = self.request.headers['X-Real-Ip']
|
||||||
|
user.update_login_info(last_login_ip)
|
||||||
|
self.set_secure_cookie("user_id", str(user.user_id), expires_days=7)
|
||||||
|
self.redirect(self.get_argument('next', '/'))
|
||||||
|
else:
|
||||||
|
self.render('home/login.html', error=102, email=email)
|
||||||
|
#
|
||||||
|
# #获取远程ip
|
||||||
|
# remote_ip = self.request.headers['X-Real-Ip']
|
||||||
|
# challenge = self.get_argument('recaptcha_challenge_field', None)
|
||||||
|
# response = self.get_argument('recaptcha_response_field', None)
|
||||||
|
# rsp = captcha.check_google_captcha(self,remote_ip, challenge, response)
|
||||||
|
# if not rsp.is_valid:
|
||||||
|
# self.render('home/login.html', error=103, email=email)
|
||||||
|
#
|
||||||
|
# self.render('home/login.html', error=100, email=email)
|
||||||
|
|
||||||
|
|
||||||
|
class LogoutHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.clear_cookie('user_id')
|
||||||
|
self.redirect('/bye')
|
||||||
|
|
||||||
|
class ByeHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render('home/bye.html')
|
9
handlers/home/register.py
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
|
||||||
|
class RegisterHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render('home/register.html', error=None)
|
96
handlers/home/settings.py
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf8 -*-
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
from utils import encrypt
|
||||||
|
from handlers.home import BaseHandler
|
||||||
|
from model.user import User, UserInfo
|
||||||
|
|
||||||
|
|
||||||
|
class ProfileHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'130': 'update success!',
|
||||||
|
'131': 'realname empty',
|
||||||
|
'132': 'username empty',
|
||||||
|
'133': 'email empty',
|
||||||
|
}
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
|
def get(self):
|
||||||
|
user = self.current_user
|
||||||
|
user_info = UserInfo.get_info_by_uid(user.user_id)
|
||||||
|
self.render('home/settings_profile.html', error=None, user=user, user_info=user_info)
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
user = self.current_user
|
||||||
|
realname = self.get_argument('realname', '')
|
||||||
|
username = self.get_argument('username', '')
|
||||||
|
email = self.get_argument('email', '')
|
||||||
|
about_me = self.get_argument('about_me', '')
|
||||||
|
avatar_src = self.get_argument('avatar_src', '')
|
||||||
|
if not realname:
|
||||||
|
self.render('home/settings_profile.html', error=131)
|
||||||
|
return
|
||||||
|
if not username:
|
||||||
|
self.render('home/settings_profile.html', error=132)
|
||||||
|
return
|
||||||
|
if not email:
|
||||||
|
self.render('home/settings_profile.html', error=133)
|
||||||
|
return
|
||||||
|
|
||||||
|
user.update(username, email, realname, about_me, avatar_src)
|
||||||
|
self.redirect('/settings/profile')
|
||||||
|
|
||||||
|
|
||||||
|
class PasswordHandler(BaseHandler):
|
||||||
|
|
||||||
|
error_message = {
|
||||||
|
'140': '密码修改成功',
|
||||||
|
'141': '原始密码填写错误',
|
||||||
|
'142': '新密码不能为空',
|
||||||
|
'143': '校验密码不能为空',
|
||||||
|
'144': '两次密码不一致',
|
||||||
|
'145': '密码修改出错,请稍后再试'
|
||||||
|
}
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
|
def get(self):
|
||||||
|
self.render('home/settings_password.html', error=None)
|
||||||
|
|
||||||
|
@tornado.web.authenticated
|
||||||
|
def post(self):
|
||||||
|
user = self.current_user
|
||||||
|
password = self.get_argument('password', '')
|
||||||
|
new_password = self.get_argument('new_password', '')
|
||||||
|
verify_password = self.get_argument('verify_password', '')
|
||||||
|
|
||||||
|
if user.get_password() != encrypt(password):
|
||||||
|
self.render('home/settings_password.html', error=141)
|
||||||
|
return
|
||||||
|
if new_password == '':
|
||||||
|
self.render('home/settings_password.html', error=142)
|
||||||
|
return
|
||||||
|
if verify_password == '':
|
||||||
|
self.render('home/settings_password.html', error=143)
|
||||||
|
return
|
||||||
|
if new_password != verify_password:
|
||||||
|
self.render('home/settings_password.html', error=144)
|
||||||
|
return
|
||||||
|
|
||||||
|
result = user.update_password(new_password)
|
||||||
|
if not result:
|
||||||
|
self.render('home/settings_password.html', error=145)
|
||||||
|
return
|
||||||
|
self.render('home/settings_password.html', error=140)
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationsHandler(BaseHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render('home/settings_notifications.html')
|
||||||
|
|
||||||
|
def post(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
54
handlers/home/upload.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf8 -*-
|
||||||
|
|
||||||
|
# see: http://www.afewords.com/book/502e5cff3725176a91000004/catalog/16
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
import tempfile
|
||||||
|
import Image
|
||||||
|
#import time
|
||||||
|
import logging
|
||||||
|
|
||||||
|
class UploadHandler(tornado.web.RequestHandler):
|
||||||
|
|
||||||
|
def get(self):
|
||||||
|
self.render('home/upload.html')
|
||||||
|
|
||||||
|
def post(slef):
|
||||||
|
if self.request.files == {} or 'mypicutre' not in self.request.files:
|
||||||
|
""" 看是否有文件且name为picture,跟HTML代码对应 """
|
||||||
|
self.write('<script>alert("请选择图片")</script>')
|
||||||
|
return
|
||||||
|
|
||||||
|
image_type_list = ['image/gif', 'image/jpeg', 'image/pjpeg', 'image/bmp', 'image/png', 'image/x-png']
|
||||||
|
send_file = self.request.files['mypicutre'][0]
|
||||||
|
if send_file['content_type'] not in image_type_list:
|
||||||
|
self.write('<script>alert("仅支持jpg,jpeg,bmp,gif,png格式的图片!")</script>')
|
||||||
|
return
|
||||||
|
|
||||||
|
if len(send_file['body']) > 4*1024*1024:
|
||||||
|
self.write('<script>alert("请上传4M以下的图片");</script>')
|
||||||
|
return
|
||||||
|
|
||||||
|
tmp_file = tempfile.NamedTemporaryFile(delete=True)
|
||||||
|
tmp_file.write(send_file['body'])
|
||||||
|
tmp_file.seek(0)
|
||||||
|
|
||||||
|
try:
|
||||||
|
image_one = Image.open(tmp_file.name)
|
||||||
|
except IOError, error:
|
||||||
|
logging.info(error)
|
||||||
|
logging.info('+'*30 + '\n')
|
||||||
|
tmp_file.close()
|
||||||
|
self.write('<script>alert("图片不合法!")</script>')
|
||||||
|
return
|
||||||
|
|
||||||
|
image_path = "./static/picture/"
|
||||||
|
image_format = send_file['filename'].split('.').pop().lower()
|
||||||
|
tmp_name = image_path + str(int(time.time())) + image_format
|
||||||
|
image_one.save(tmp_name)
|
||||||
|
|
||||||
|
tmp_file.close()
|
||||||
|
self.write('<script>alert("文件上传成功,路径为:" + image_path[1:])</script>')
|
||||||
|
return
|
||||||
|
|
6
libs/__init__.py
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from libs.db.database import engine, Base, create_all, drop_all, db_session
|
||||||
|
|
||||||
|
__all__ = ['engine', 'Base', 'create_all', 'drop_all', 'db_session']
|
46
libs/captcha.py
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
import urllib,urllib2
|
||||||
|
import os
|
||||||
|
|
||||||
|
GOOGLE_CAPTCHA_API = 'http://www.google.com/recaptcha/api/verify'
|
||||||
|
|
||||||
|
class RecaptchaResponse:
|
||||||
|
def __init__(self, is_valid, error_code=None):
|
||||||
|
self.is_valid = is_valid
|
||||||
|
self.error_code = error_code
|
||||||
|
|
||||||
|
def check_google_captcha(request,remote_ip,recaptcha_challenge_field,recaptcha_response_field):
|
||||||
|
if not (recaptcha_challenge_field and recaptcha_response_field):
|
||||||
|
return RecaptchaResponse (is_valid = False, error_code = 'incorrect-captcha-sol')
|
||||||
|
|
||||||
|
def encode_if_necessary(s):
|
||||||
|
if isinstance(s, unicode):
|
||||||
|
return s.encode('utf-8')
|
||||||
|
return s
|
||||||
|
|
||||||
|
params = urllib.urlencode ({
|
||||||
|
'privatekey': encode_if_necessary('6Ld58vISAAAAANJmi5SeL_3JrVooeLGw2kmo7kK5'), #这里是我的私钥
|
||||||
|
'remoteip' : encode_if_necessary(remote_ip), #远程主机ip
|
||||||
|
'challenge': encode_if_necessary(recaptcha_challenge_field),
|
||||||
|
'response' : encode_if_necessary(recaptcha_response_field), #填入的数据
|
||||||
|
})
|
||||||
|
request = urllib2.Request(
|
||||||
|
url = GOOGLE_CAPTCHA_API,
|
||||||
|
data = params,
|
||||||
|
headers = {
|
||||||
|
"Content-type": "application/x-www-form-urlencoded",
|
||||||
|
"User-agent": "reCAPTCHA Python"
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
httpresp = urllib2.urlopen(request)
|
||||||
|
return_values = httpresp.read().splitlines();
|
||||||
|
httpresp.close();
|
||||||
|
return_code = return_values[0]
|
||||||
|
|
||||||
|
if (return_code == "true"):
|
||||||
|
return RecaptchaResponse(is_valid=True)
|
||||||
|
else:
|
||||||
|
return RecaptchaResponse(is_valid=False, error_code = return_values[1])
|
0
libs/db/__init__.py
Normal file
38
libs/db/database.py
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
# database.py
|
||||||
|
|
||||||
|
from sqlalchemy import create_engine
|
||||||
|
from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
|
from sqlalchemy.ext.declarative import declarative_base
|
||||||
|
from config import DATABASE
|
||||||
|
import os
|
||||||
|
|
||||||
|
"""
|
||||||
|
Problem:
|
||||||
|
1.StatementError: Can't reconnect until invalid transaction is rolled back
|
||||||
|
2.MYSQL has gone away
|
||||||
|
See: http://mofanim.wordpress.com/2013/01/02/sqlalchemy-mysql-has-gone-away/
|
||||||
|
Solution: http://docs.sqlalchemy.org/en/rel_0_7/core/pooling.html#setting-pool-recycle
|
||||||
|
"""
|
||||||
|
# engine = create_engine('mysql+pymysql://miles:aspect@192.168.100.30/test', pool_recycle=3600, encoding="utf-8", echo=True) #会乱码
|
||||||
|
env = os.getenv("ENV") or "dev"
|
||||||
|
print(f"env={env}")
|
||||||
|
mysql_conn = "{driven}://{user}:{pswd}@{host}/test".format(driven = DATABASE[env]['driven'],
|
||||||
|
user = DATABASE[env]['user'],
|
||||||
|
pswd = DATABASE[env]['password'],
|
||||||
|
host = DATABASE[env]['host'])
|
||||||
|
print(f"mysql_conn={mysql_conn}")
|
||||||
|
engine = create_engine(mysql_conn, pool_recycle = 60, connect_args = {"charset": "utf8"}, echo = True)
|
||||||
|
|
||||||
|
Base = declarative_base()
|
||||||
|
db_session = scoped_session(sessionmaker(bind = engine))
|
||||||
|
|
||||||
|
|
||||||
|
def create_all():
|
||||||
|
Base.metadata.create_all(bind = engine)
|
||||||
|
|
||||||
|
|
||||||
|
def drop_all():
|
||||||
|
Base.metadata.drop_all(bind=engine)
|
7
libs/db/torndb.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
# database.py
|
||||||
|
import torndb
|
||||||
|
|
||||||
|
db_session = torndb.Connection("localhost", "test", user="", password="")
|
8
libs/memcache.py
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
|
import memcache
|
||||||
|
|
||||||
|
#cache = memcache.Client(['127.0.0.1:11211'], debug=1)
|
||||||
|
#cache.set('foo', 'bar')
|
||||||
|
#cache.get('foo')
|
45
libs/paginator.py
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
from __future__ import division
|
||||||
|
import math
|
||||||
|
import urlparse
|
||||||
|
import urllib
|
||||||
|
|
||||||
|
import tornado.web
|
||||||
|
|
||||||
|
def update_querystring(url, **kwargs):
|
||||||
|
base_url = urlparse.urlsplit(url)
|
||||||
|
query_args = urlparse.parse_qs(base_url.query)
|
||||||
|
query_args.update(kwargs)
|
||||||
|
for arg_name, arg_value in kwargs.iteritems():
|
||||||
|
if arg_value is None:
|
||||||
|
if query_args.has_key(arg_name):
|
||||||
|
del query_args[arg_name]
|
||||||
|
|
||||||
|
query_string = urllib.urlencode(query_args, True)
|
||||||
|
return urlparse.urlunsplit((base_url.scheme, base_url.netloc,
|
||||||
|
base_url.path, query_string, base_url.fragment))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Paginator(tornado.web.UIModule):
|
||||||
|
"""Pagination links display.
|
||||||
|
{% for result in results %}
|
||||||
|
<p>{{ result }}</p>
|
||||||
|
{% end %}
|
||||||
|
{% module Paginator(page, page_size, results_count) %}
|
||||||
|
more see: http://stackoverflow.com/questions/15981257/can-tornado-handle-pagination
|
||||||
|
"""
|
||||||
|
|
||||||
|
def render(self, page, page_size, results_count):
|
||||||
|
pages = int(math.ceil(results_count / page_size)) if results_count else 0
|
||||||
|
|
||||||
|
def get_page_url(page):
|
||||||
|
# don't allow ?page=1
|
||||||
|
if page <= 1:
|
||||||
|
page = None
|
||||||
|
return update_querystring(self.request.uri, page=page)
|
||||||
|
|
||||||
|
next = page + 1 if page < pages else None
|
||||||
|
previous = page - 1 if page > 1 else None
|
||||||
|
|
||||||
|
return self.render_string('uimodules/pagination.html', page=page, pages=pages, next=next,
|
||||||
|
previous=previous, get_page_url=get_page_url)
|
11
libs/uimodules/pagination.html
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{% if pages > 1 %}
|
||||||
|
<div class="pagination pagination-centered">
|
||||||
|
<ul>
|
||||||
|
<li{% if previous %}><a href="{{ get_page_url(previous) }}">«</a>{% else %} class="disabled"><span>«</span></li>{% end %}
|
||||||
|
{% for page_num in xrange(1, pages + 1) %}{# 1-index range #}
|
||||||
|
<li{% if page_num != page %}><a href="{{ get_page_url(page_num) }}">{{ page_num }}</a>{% else %} class="active"><span>{{ page_num }}</span></li>{% end %}
|
||||||
|
{% end %}
|
||||||
|
<li{% if next %}><a href="{{ get_page_url(next) }}">»</a>{% else %} class="disabled"><span>»</span></li>{% end %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
{% end %}
|
0
model/__init__.py
Normal file
265
model/admin.py
Normal file
@ -0,0 +1,265 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from libs import Base, db_session
|
||||||
|
from utils import encrypt, obj2dict
|
||||||
|
from sqlalchemy import Column, Integer, String, DateTime, Boolean, desc, asc
|
||||||
|
from sqlalchemy import ForeignKey
|
||||||
|
from sqlalchemy.orm import relationship, backref
|
||||||
|
|
||||||
|
|
||||||
|
class Admin(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'wmh_admin'
|
||||||
|
|
||||||
|
user_id = Column(Integer, primary_key=True)
|
||||||
|
realname = Column(String(16))
|
||||||
|
username = Column(String(16))
|
||||||
|
email = Column(String(32))
|
||||||
|
password = Column(String(32))
|
||||||
|
last_login_time = Column(DateTime)
|
||||||
|
last_login_ip = Column(String(16))
|
||||||
|
login_times = Column(Integer)
|
||||||
|
update_time = Column(DateTime)
|
||||||
|
status = Column(Integer)
|
||||||
|
role_id = Column(Integer)
|
||||||
|
#user_info = relationship('UserInfo', backref='wmh_user')
|
||||||
|
|
||||||
|
def __init__(self, user_id, username, email, realname, role_id=0):
|
||||||
|
self.user_id = user_id
|
||||||
|
self.username = username
|
||||||
|
self.email = email
|
||||||
|
self.realname = realname
|
||||||
|
self.role_id = role_id
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Admin('%s')>" % (self.username)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls, username, email, password, realname, role_id):
|
||||||
|
"""
|
||||||
|
add new user
|
||||||
|
"""
|
||||||
|
user = Admin(None, username, email, realname, role_id)
|
||||||
|
user.password = encrypt(password) if password else ''
|
||||||
|
user.status = 1
|
||||||
|
#TODO optimize
|
||||||
|
user.last_login_time = '0000-00-00 00:00:00'
|
||||||
|
user.last_login_ip = ''
|
||||||
|
user.login_times = 0
|
||||||
|
user.update_time = '0000-00-00 00:00:00'
|
||||||
|
|
||||||
|
db_session.add(user)
|
||||||
|
#只有提交事务了,才可以获取(user.user_id)数据的ID值
|
||||||
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
if user.user_id:
|
||||||
|
return cls.get(user.user_id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_password(self, password):
|
||||||
|
password = encrypt(password)
|
||||||
|
current_time = datetime.now()
|
||||||
|
update_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update = {
|
||||||
|
Admin.password: password,
|
||||||
|
Admin.update_time: update_time
|
||||||
|
}
|
||||||
|
db_session.query(Admin).filter(Admin.user_id == self.user_id).update(update)
|
||||||
|
try:
|
||||||
|
return db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
def get_password(self):
|
||||||
|
return db_session.query(Admin).filter(Admin.user_id == self.user_id).first().password
|
||||||
|
|
||||||
|
def update_login_info(self, last_login_ip):
|
||||||
|
current_time = datetime.now()
|
||||||
|
last_login_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update = {
|
||||||
|
Admin.last_login_time: last_login_time,
|
||||||
|
Admin.last_login_ip: last_login_ip,
|
||||||
|
Admin.login_times: Admin.login_times + 1
|
||||||
|
}
|
||||||
|
db_session.query(Admin).filter(Admin.user_id == self.user_id).update(update)
|
||||||
|
try:
|
||||||
|
return db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
def update_email(self, email):
|
||||||
|
current_time = datetime.now()
|
||||||
|
update_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update = {
|
||||||
|
Admin.email: email,
|
||||||
|
Admin.update_time: update_time
|
||||||
|
}
|
||||||
|
db_session.query(Admin).filter(Admin.user_id == self.user_id).update(update)
|
||||||
|
try:
|
||||||
|
return db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def initialize(cls, item):
|
||||||
|
if not item:
|
||||||
|
return None
|
||||||
|
user_id = item.user_id
|
||||||
|
username = item.username
|
||||||
|
email = item.email
|
||||||
|
realname = item.realname
|
||||||
|
if not user_id:
|
||||||
|
return None
|
||||||
|
return cls(user_id, username, email, realname)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, user_id):
|
||||||
|
item = db_session.query(Admin.user_id, Admin.realname, Admin.email, Admin.username, Admin.last_login_ip,
|
||||||
|
Admin.login_times, Admin.status).filter(Admin.user_id == user_id).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_uid(cls, user_id):
|
||||||
|
item = db_session.query(Admin).filter(Admin.user_id == user_id).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_email(cls, email):
|
||||||
|
item = db_session.query(Admin).filter(Admin.email == email).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_username(cls, username):
|
||||||
|
item = db_session.query(Admin).filter(Admin.username == username).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update(cls, user_id=0, username='', email='', password='', realname='', role_id=0):
|
||||||
|
update = {}
|
||||||
|
if username:
|
||||||
|
update['username'] = username
|
||||||
|
if email:
|
||||||
|
update['email'] = email
|
||||||
|
if password:
|
||||||
|
update['password'] = password
|
||||||
|
if realname:
|
||||||
|
update['realname'] = realname
|
||||||
|
if role_id:
|
||||||
|
update['role_id'] = role_id
|
||||||
|
|
||||||
|
current_time = datetime.now()
|
||||||
|
update_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update['update_time'] = update_time
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_session.query(Admin).filter(Admin.user_id == user_id).update(update)
|
||||||
|
db_session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
raise
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def gets(cls, start=0, limit=20):
|
||||||
|
rs = db_session.query(Admin.user_id, Admin.realname, Admin.email, Admin.username, Admin.last_login_ip,
|
||||||
|
Admin.last_login_time, Admin.login_times, Admin.status).offset(start).limit(limit)
|
||||||
|
return [obj2dict(r) for r in rs.all()]
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_count(cls):
|
||||||
|
return db_session.query(Admin).count()
|
||||||
|
|
||||||
|
|
||||||
|
class AdminRole(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'wmh_admin_role'
|
||||||
|
|
||||||
|
role_id = Column(Integer, primary_key=True)
|
||||||
|
role_name = Column(String(50))
|
||||||
|
description = Column(String)
|
||||||
|
list_order = Column(Integer)
|
||||||
|
status = Column(Integer)
|
||||||
|
|
||||||
|
def __init__(self, role_id, role_name, description, list_order, status):
|
||||||
|
self.role_id = role_id
|
||||||
|
self.role_name = role_name
|
||||||
|
self.description = description
|
||||||
|
self.list_order = list_order
|
||||||
|
self.status = status
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<AdminRole('%s')>" % self.role_name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def initialize(cls, item):
|
||||||
|
if not item:
|
||||||
|
return None
|
||||||
|
role_id = item.role_id
|
||||||
|
role_name = item.role_name
|
||||||
|
description = item.description
|
||||||
|
list_order = item.list_order
|
||||||
|
status = item.status
|
||||||
|
if not role_id:
|
||||||
|
return None
|
||||||
|
return cls(role_id, role_name, description, list_order, status)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls, role_name, description, list_order, status):
|
||||||
|
"""
|
||||||
|
add new role
|
||||||
|
"""
|
||||||
|
role = AdminRole(None, role_name, description, list_order, status)
|
||||||
|
|
||||||
|
db_session.add(role)
|
||||||
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update(cls, role_id, role_name, description, list_order, status):
|
||||||
|
update = {}
|
||||||
|
if role_name:
|
||||||
|
update['role_name'] = role_name
|
||||||
|
if description:
|
||||||
|
update['description'] = description
|
||||||
|
if list_order:
|
||||||
|
update['list_order'] = list_order
|
||||||
|
if status:
|
||||||
|
update['status'] = status
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_session.query(AdminRole).filter(AdminRole.role_id == role_id).update(update)
|
||||||
|
db_session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
raise
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, role_id):
|
||||||
|
item = db_session.query(AdminRole.role_id, AdminRole.role_name, AdminRole.description, AdminRole.list_order,
|
||||||
|
AdminRole.status).filter(AdminRole.role_id == role_id).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def gets(cls, start=0, limit=20, sort='id', order='asc'):
|
||||||
|
return db_session.query(AdminRole.role_id, AdminRole.role_name, AdminRole.description, AdminRole.list_order,
|
||||||
|
AdminRole.status).offset(start).limit(limit).all()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_count(cls):
|
||||||
|
return db_session.query(AdminRole).count()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_rolename(cls, role_name):
|
||||||
|
item = db_session.query(AdminRole).filter(AdminRole.role_name == role_name).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
190
model/news.py
Normal file
@ -0,0 +1,190 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from libs import Base, db_session
|
||||||
|
from utils import obj2dict
|
||||||
|
from sqlalchemy import Column, Integer, String, DateTime, Text, ForeignKey
|
||||||
|
from sqlalchemy.orm import relationship, backref
|
||||||
|
from model.admin import Admin
|
||||||
|
|
||||||
|
|
||||||
|
class News(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'wmh_news'
|
||||||
|
|
||||||
|
news_id = Column(Integer, primary_key=True)
|
||||||
|
title = Column(String(50))
|
||||||
|
content = Column(Text)
|
||||||
|
create_time = Column(DateTime)
|
||||||
|
update_time = Column(DateTime)
|
||||||
|
status = Column(Integer)
|
||||||
|
create_uid = Column(Integer, ForeignKey('wmh_admin.user_id'))
|
||||||
|
category_id = Column(Integer, ForeignKey('wmh_news_category.category_id'))
|
||||||
|
#category = relationship('NewsCategory', backref='News')
|
||||||
|
|
||||||
|
def __init__(self, news_id, category_id, title, content, create_uid=0, status=0, create_time='', update_time=''):
|
||||||
|
self.news_id = news_id
|
||||||
|
self.category_id = category_id
|
||||||
|
self.title = title
|
||||||
|
self.content = content
|
||||||
|
if create_time:
|
||||||
|
self.create_time = create_time
|
||||||
|
if update_time:
|
||||||
|
self.update_time = update_time
|
||||||
|
if create_uid:
|
||||||
|
self.create_uid = create_uid
|
||||||
|
self.status = status
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<News('%s')>" % self.title
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def initialize(cls, item):
|
||||||
|
if not item:
|
||||||
|
return None
|
||||||
|
|
||||||
|
news_id = item.news_id
|
||||||
|
category_id = item.category_id
|
||||||
|
title = item.title
|
||||||
|
content = item.content
|
||||||
|
create_time = item.create_time
|
||||||
|
update_time = item.update_time
|
||||||
|
create_uid = item.create_uid
|
||||||
|
status = item.status
|
||||||
|
|
||||||
|
if not news_id:
|
||||||
|
return None
|
||||||
|
return cls(news_id, category_id, title, content, create_uid, status, create_time, update_time)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls, category_id, title, content, create_uid, status):
|
||||||
|
"""
|
||||||
|
add new news
|
||||||
|
"""
|
||||||
|
news = News(None, category_id, title, content, create_uid, status)
|
||||||
|
|
||||||
|
#TODO optimize
|
||||||
|
news.update_time = '0000-00-00 00:00:00'
|
||||||
|
|
||||||
|
db_session.add(news)
|
||||||
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
if news.news_id:
|
||||||
|
return cls.get(news.news_id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update(cls, news_id, category_id, title, content, create_uid, status):
|
||||||
|
|
||||||
|
update = {}
|
||||||
|
if category_id:
|
||||||
|
update['category_id'] = category_id
|
||||||
|
if title:
|
||||||
|
update['title'] = title
|
||||||
|
if content:
|
||||||
|
update['content'] = content
|
||||||
|
if create_uid:
|
||||||
|
update['create_uid'] = create_uid
|
||||||
|
if status:
|
||||||
|
update['status'] = status
|
||||||
|
|
||||||
|
current_time = datetime.now()
|
||||||
|
update_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update['update_time'] = update_time
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_session.query(News).filter(News.news_id == news_id).update(update)
|
||||||
|
db_session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
raise
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, news_id):
|
||||||
|
item = db_session.query(News.news_id, News.category_id, News.title, News.content, News.create_uid, News.status,
|
||||||
|
News.create_time, News.update_time).filter(News.news_id == news_id).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def gets(cls, offset=0, limit=20, title='', begin=0, end=0):
|
||||||
|
end = end if end else '2039-12-12'
|
||||||
|
rs = db_session.query(News.news_id, News.news_id.label('operate_id'), News.category_id, News.title,
|
||||||
|
News.create_time, News.create_uid, News.update_time, News.status,
|
||||||
|
NewsCategory.category_name, Admin.username).\
|
||||||
|
join(NewsCategory, Admin).\
|
||||||
|
filter(News.category_id == NewsCategory.category_id, News.create_uid == Admin.user_id).\
|
||||||
|
filter(News.title.like('%'+title+'%')).\
|
||||||
|
filter(News.create_time >= begin, News.create_time <= end).\
|
||||||
|
offset(offset).limit(limit)
|
||||||
|
return rs.all()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_count(cls):
|
||||||
|
return db_session.query(News).count()
|
||||||
|
|
||||||
|
|
||||||
|
class NewsCategory(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'wmh_news_category'
|
||||||
|
|
||||||
|
category_id = Column(Integer, primary_key=True)
|
||||||
|
category_name = Column(String(50))
|
||||||
|
|
||||||
|
def __init__(self, category_id, category_name):
|
||||||
|
self.category_id = category_id
|
||||||
|
self.category_name = category_name
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<NewsCategory('%s')>" % self.category_name
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls, category_name):
|
||||||
|
"""
|
||||||
|
add new news
|
||||||
|
"""
|
||||||
|
newsCategory = NewsCategory(None, category_name)
|
||||||
|
|
||||||
|
db_session.add(newsCategory)
|
||||||
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
if newsCategory.category_id:
|
||||||
|
return cls.get(newsCategory.category_id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update(cls, category_id, category_name):
|
||||||
|
|
||||||
|
update = {}
|
||||||
|
if category_name:
|
||||||
|
update['category_name'] = category_name
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_session.query(NewsCategory).filter(NewsCategory.category_id == category_id).update(update)
|
||||||
|
db_session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
raise
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, category_id):
|
||||||
|
item = db_session.query(NewsCategory.category_id, NewsCategory.category_name)\
|
||||||
|
.filter(NewsCategory.category_id == category_id).first()
|
||||||
|
return item
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def gets(cls, start=0, limit=20):
|
||||||
|
rs = db_session.query(NewsCategory.category_id, NewsCategory.category_name).offset(start).limit(limit)
|
||||||
|
return rs.all()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_count(cls):
|
||||||
|
return db_session.query(NewsCategory).count()
|
61
model/setting.py
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from libs import Base, db_session
|
||||||
|
from utils import obj2dict
|
||||||
|
from sqlalchemy import Column, String
|
||||||
|
from utils.dict_setting import setting
|
||||||
|
|
||||||
|
|
||||||
|
class Setting(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'wmh_setting'
|
||||||
|
|
||||||
|
key = Column(String(100), primary_key=True)
|
||||||
|
value = Column(String(255))
|
||||||
|
|
||||||
|
def __init__(self, key, value):
|
||||||
|
self.key = key
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<Settings('%s')>" % (self.key)
|
||||||
|
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def update(cls, key, value):
|
||||||
|
update = {}
|
||||||
|
if key and value:
|
||||||
|
update['value'] = value
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_session.query(Setting).filter(Setting.key == key).update(update)
|
||||||
|
db_session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
raise
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def gets(cls):
|
||||||
|
_dict = {}
|
||||||
|
#get current config list
|
||||||
|
fields = setting
|
||||||
|
settingFields = setting.keys()
|
||||||
|
|
||||||
|
#get data from db
|
||||||
|
data = db_session.query(Setting.key, Setting.value).filter(Setting.key.in_(settingFields))
|
||||||
|
_dict['total'] = data.count()
|
||||||
|
data = data.all()
|
||||||
|
|
||||||
|
_data_dict = dict(data)
|
||||||
|
|
||||||
|
for key in fields:
|
||||||
|
#如果数据库不存在该设置项则从默认值中获取
|
||||||
|
fields[key]['value'] = _data_dict[key] if _data_dict[key] else fields[key]['default']
|
||||||
|
fields[key]['key'] = key
|
||||||
|
|
||||||
|
_dict['rows'] = fields.values()
|
||||||
|
return _dict
|
||||||
|
|
||||||
|
|
198
model/user.py
Normal file
@ -0,0 +1,198 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
#-*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
from libs import Base, db_session
|
||||||
|
from utils import encrypt
|
||||||
|
from sqlalchemy import Column, Integer, String, DateTime, Boolean
|
||||||
|
from sqlalchemy import ForeignKey
|
||||||
|
|
||||||
|
|
||||||
|
class User(Base):
|
||||||
|
__tablename__ = 'wmh_user'
|
||||||
|
user_id = Column(Integer, primary_key=True)
|
||||||
|
realname = Column(String(16))
|
||||||
|
username = Column(String(16), unique=True)
|
||||||
|
email = Column(String(32), unique=True, index=True)
|
||||||
|
password = Column(String(32))
|
||||||
|
reg_ip = Column(String(16))
|
||||||
|
last_login_time = Column(DateTime)
|
||||||
|
last_login_ip = Column(String(16))
|
||||||
|
login_times = Column(Integer)
|
||||||
|
update_time = Column(DateTime)
|
||||||
|
|
||||||
|
def __init__(self, user_id, username, email, realname):
|
||||||
|
self.user_id = user_id
|
||||||
|
self.username = username
|
||||||
|
self.email = email
|
||||||
|
self.realname = realname
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<User('%s')>" % (self.username)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def new(cls, username, email, password, reg_ip):
|
||||||
|
"""
|
||||||
|
add new user
|
||||||
|
"""
|
||||||
|
user = User(None, username, email)
|
||||||
|
user.password = encrypt(password) if password else ''
|
||||||
|
user.reg_ip = reg_ip
|
||||||
|
db_session.add(user)
|
||||||
|
#只有提交事务了,才可以获取(user.user_id)数据的ID值
|
||||||
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
db_session.close()
|
||||||
|
|
||||||
|
if user.user_id:
|
||||||
|
return cls.get(user.user_id)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def update_password(self, password):
|
||||||
|
password = encrypt(password)
|
||||||
|
current_time = datetime.now()
|
||||||
|
update_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update = {
|
||||||
|
User.password: password,
|
||||||
|
User.update_time: update_time
|
||||||
|
}
|
||||||
|
db_session.query(User).filter(User.user_id == self.user_id).update(update)
|
||||||
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
def get_password(self):
|
||||||
|
return db_session.query(User).filter(User.user_id == self.user_id).first().password
|
||||||
|
|
||||||
|
def update_login_info(self, last_login_ip):
|
||||||
|
current_time = datetime.now()
|
||||||
|
last_login_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update = {
|
||||||
|
User.last_login_time: last_login_time,
|
||||||
|
User.last_login_ip: last_login_ip,
|
||||||
|
User.login_times: User.login_times + 1
|
||||||
|
}
|
||||||
|
db_session.query(User).filter(User.user_id == self.user_id).update(update)
|
||||||
|
try:
|
||||||
|
db_session.commit()
|
||||||
|
return True
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
def update_email(self, email):
|
||||||
|
current_time = datetime.now()
|
||||||
|
update_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update = {
|
||||||
|
User.email: email,
|
||||||
|
User.update_time: update_time
|
||||||
|
}
|
||||||
|
db_session.query(User).filter(User.user_id == self.user_id).update(update)
|
||||||
|
try:
|
||||||
|
return db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def initialize(cls, item):
|
||||||
|
if not item:
|
||||||
|
return None
|
||||||
|
user_id = item.user_id
|
||||||
|
username = item.username
|
||||||
|
email = item.email
|
||||||
|
realname = item.realname
|
||||||
|
if not user_id:
|
||||||
|
return None
|
||||||
|
return cls(user_id, username, email, realname)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, user_id):
|
||||||
|
item = db_session.query(User).filter(User.user_id == user_id).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_uid(cls, user_id):
|
||||||
|
item = db_session.query(User).filter(User.user_id == user_id).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_email(cls, email):
|
||||||
|
item = db_session.query(User).filter(User.email == email).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_by_username(cls, username):
|
||||||
|
item = db_session.query(User).filter(User.username == username).first()
|
||||||
|
return item and cls.initialize(item)
|
||||||
|
|
||||||
|
def update(self, username='', email='', realname='', about_me='', avatar_src=''):
|
||||||
|
update = {}
|
||||||
|
update_info = {}
|
||||||
|
if username:
|
||||||
|
update['username'] = username
|
||||||
|
if email:
|
||||||
|
update['email'] = email
|
||||||
|
if realname:
|
||||||
|
update['realname'] = realname
|
||||||
|
|
||||||
|
current_time = datetime.now()
|
||||||
|
update_time = current_time.strftime('%Y-%m-%d %H:%M:%S')
|
||||||
|
update['update_time'] = update_time
|
||||||
|
|
||||||
|
if about_me:
|
||||||
|
update_info['about_me'] = about_me
|
||||||
|
if avatar_src:
|
||||||
|
update_info['avatar_src'] = avatar_src
|
||||||
|
|
||||||
|
try:
|
||||||
|
db_session.query(User).filter(User.user_id == self.user_id).update(update)
|
||||||
|
#if update_info['about_me'] or update_info['avatar_src']:
|
||||||
|
# db_session.query(UserInfo).filter(UserInfo.user_id == self.user_id).update(update_info)
|
||||||
|
|
||||||
|
db_session.commit()
|
||||||
|
except:
|
||||||
|
db_session.rollback()
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
|
class UserInfo(Base):
|
||||||
|
|
||||||
|
__tablename__ = 'wmh_user_extinfo'
|
||||||
|
|
||||||
|
id = Column(Integer, primary_key=True)
|
||||||
|
user_id = Column(Integer, ForeignKey('wmh_user.user_id'))
|
||||||
|
gender = Column(Integer)
|
||||||
|
birthday = Column(DateTime)
|
||||||
|
about_me = Column(String(500))
|
||||||
|
#avatar_src = Column(String(255))
|
||||||
|
|
||||||
|
def __init__(self, user_id, gender, birthday, about_me):
|
||||||
|
self.user_id = user_id
|
||||||
|
self.gender = gender
|
||||||
|
self.birthday = birthday
|
||||||
|
self.about_me = about_me
|
||||||
|
#self.avatar_src = avatar_src
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<UserInfo(gender=%s, birthday=%s)>" % (self.gender, self.birthday)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def initialize(cls, item):
|
||||||
|
if not item:
|
||||||
|
return None
|
||||||
|
user_id = item.user_id
|
||||||
|
gender = item.gender
|
||||||
|
birthday = item.birthday
|
||||||
|
about_me = item.about_me
|
||||||
|
#avatar_src = item.avatar_src
|
||||||
|
if not user_id:
|
||||||
|
return None
|
||||||
|
return cls(user_id, gender, birthday, about_me )
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get_info_by_uid(cls, user_id):
|
||||||
|
item = db_session.query(UserInfo).filter(UserInfo.user_id == user_id).first()
|
||||||
|
return item and cls.initialize(item)
|
6
requirements.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
tornado
|
||||||
|
sqlalchemy
|
||||||
|
wtforms
|
||||||
|
#python-memcached==1.53
|
||||||
|
#MySQL-python
|
||||||
|
pymysql
|
1
robots.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Disallow: /
|
19
settings.py
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
|
||||||
|
from os import path
|
||||||
|
from urls import urls_pattern as url_handlers
|
||||||
|
|
||||||
|
DEBUG = False
|
||||||
|
|
||||||
|
# the application settings
|
||||||
|
settings = {
|
||||||
|
'debug': DEBUG,
|
||||||
|
'cookie_secret': 'test', # TODO: get the real secret
|
||||||
|
'login_url': '/admin/login',
|
||||||
|
'xsrf_cookies': True,
|
||||||
|
'static_path': path.join(path.dirname(__file__), 'static_v1'),
|
||||||
|
'template_path': path.join(path.dirname(__file__), 'templates'),
|
||||||
|
#'ui_modules': '' # TODO: the ui modules file
|
||||||
|
}
|
||||||
|
|
27
static/css/admin/bootstrap.css
vendored
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
@CHARSET "UTF-8";
|
||||||
|
body{margin:0; padding:0;background:none;font-size:13px;font-family:sans-serif;}
|
||||||
|
a{color:#08c;text-decoration:none}
|
||||||
|
a:hover,a:focus{color:#005580;text-decoration:underline}
|
||||||
|
a:focus,a:hover,a:active {outline: 0;}
|
||||||
|
.word-wrap{word-wrap: break-word;word-break: break-all;}
|
||||||
|
.messager-body{word-wrap: break-word;word-break: break-all;}
|
||||||
|
.datagrid-row-selected a{color:#fff} /* 解决列表选中部分a标签看不见问题 */
|
||||||
|
.panel input,.panel form{font-size:12px} /* 表单中字体问题 */
|
||||||
|
|
||||||
|
/* 表单样式 */
|
||||||
|
form{font-size:13px}
|
||||||
|
textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"]{background-color: #ffffff;border: 1px solid #cccccc;-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;-moz-transition: border linear 0.2s, box-shadow linear 0.2s;-o-transition: border linear 0.2s, box-shadow linear 0.2s;transition: border linear 0.2s, box-shadow linear 0.2s;}
|
||||||
|
textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{border-color: rgba(82, 168, 236, 0.8);outline: 0;outline: thin dotted \9;-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
|
||||||
|
input[type="radio"],input[type="checkbox"] {margin: 4px 0 0;margin-top: 1px \9;*margin-top: 0;line-height: normal;}
|
||||||
|
input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"] {width: auto;}
|
||||||
|
|
||||||
|
/* 首页样式 */
|
||||||
|
#toparea{background-color:#EEEEEE;background-image:linear-gradient(to bottom, #FFFFFF, #EEEEEE);background-repeat: repeat-x;border:0;padding:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF',endColorstr='#FFEEEEEE',GradientType=0)}
|
||||||
|
#topmenu{display:inline;background:none;}
|
||||||
|
#topmenu .logo{color:#777777;display:block;float:left;font-size:20px;text-decoration:none;cursor:default;font-weight: 200;padding:7px 17px 5px;text-shadow: 0 1px 0 #FFFFFF;}
|
||||||
|
#topmenu .nav{float:left;margin:0;padding:0;list-style:none}
|
||||||
|
#topmenu .nav li{float:left;margin:0;padding:0}
|
||||||
|
#topmenu .nav li a{color:#777777;padding:13px 15px;text-decoration:none;text-shadow:0 1px 0 #FFFFFF;display: block;}
|
||||||
|
#topmenu .nav li a.focus{background-color:#E5E5E5;box-shadow:0 3px 8px rgba(0, 0, 0, 0.125) inset;color:#555555;text-decoration:none;}
|
||||||
|
#topmenu .nav-right{margin:8px 5px 0;padding:0;list-style:none;float:right}
|
||||||
|
#topmenu .nav-right li{float:left;margin:0 5px;padding:0;}
|
26
static/css/admin/default.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
@CHARSET "UTF-8";
|
||||||
|
body{margin:0; padding:0;background:none;font-size:13px;font-family:sans-serif;}
|
||||||
|
a{color:#08c;text-decoration:none}
|
||||||
|
a:hover,a:focus{color:#005580;text-decoration:underline}
|
||||||
|
a:focus,a:hover,a:active {outline: 0;}
|
||||||
|
.word-wrap{word-wrap: break-word;word-break: break-all;}
|
||||||
|
.messager-body{word-wrap: break-word;word-break: break-all;}
|
||||||
|
.datagrid-row-selected a{color:#000} /* 解决列表选中部分a标签看不见问题 */
|
||||||
|
.panel input,.panel form{font-size:12px} /* 表单中字体问题 */
|
||||||
|
|
||||||
|
/* 表单样式 */
|
||||||
|
form{font-size:13px}
|
||||||
|
textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"]{background-color: #ffffff;border: 1px solid #cccccc;-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;-moz-transition: border linear 0.2s, box-shadow linear 0.2s;-o-transition: border linear 0.2s, box-shadow linear 0.2s;transition: border linear 0.2s, box-shadow linear 0.2s;}
|
||||||
|
input[type="radio"],input[type="checkbox"] {margin: 4px 0 0;margin-top: 1px \9;*margin-top: 0;line-height: normal;}
|
||||||
|
input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"] {width: auto;}
|
||||||
|
|
||||||
|
/* 首页样式 */
|
||||||
|
#toparea{background-color:#E4EEFF;background-image:linear-gradient(to bottom, #F2F5FF, #E4EEFF);background-repeat: repeat-x;border:0;padding:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFF2F5FF',endColorstr='#FFE4EEFF',GradientType=0)}
|
||||||
|
#topmenu{display:inline;background:none;}
|
||||||
|
#topmenu .logo{color:#777777;display:block;float:left;font-size:20px;text-decoration:none;cursor:default;font-weight: 200;padding:7px 17px 5px;text-shadow: 0 1px 0 #FFFFFF;}
|
||||||
|
#topmenu .nav{float:left;margin:0;padding:0;list-style:none}
|
||||||
|
#topmenu .nav li{float:left;margin:0;padding:0}
|
||||||
|
#topmenu .nav li a{color:#777777;padding:13px 15px;text-decoration:none;text-shadow:0 1px 0 #FFFFFF;display: block;}
|
||||||
|
#topmenu .nav li a.focus{background-color:#D0DCE2;box-shadow:0 3px 8px rgba(0, 0, 0, 0.125) inset;color:#555555;text-decoration:none;}
|
||||||
|
#topmenu .nav-right{margin:8px 5px 0;padding:0;list-style:none;float:right}
|
||||||
|
#topmenu .nav-right li{float:left;margin:0 5px;padding:0;}
|
27
static/css/admin/gray.css
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
@CHARSET "UTF-8";
|
||||||
|
body{margin:0; padding:0;background:none;font-size:13px;font-family:sans-serif;}
|
||||||
|
a{color:#08c;text-decoration:none}
|
||||||
|
a:hover,a:focus{color:#005580;text-decoration:underline}
|
||||||
|
a:focus,a:hover,a:active {outline: 0;}
|
||||||
|
.word-wrap{word-wrap: break-word;word-break: break-all;}
|
||||||
|
.messager-body{word-wrap: break-word;word-break: break-all;}
|
||||||
|
.datagrid-row-selected a{color:#fff} /* 解决列表选中部分a标签看不见问题 */
|
||||||
|
.panel input,.panel form{font-size:12px} /* 表单中字体问题 */
|
||||||
|
|
||||||
|
/* 表单样式 */
|
||||||
|
form{font-size:13px}
|
||||||
|
textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"]{background-color: #ffffff;border: 1px solid #cccccc;-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075);-webkit-transition: border linear 0.2s, box-shadow linear 0.2s;-moz-transition: border linear 0.2s, box-shadow linear 0.2s;-o-transition: border linear 0.2s, box-shadow linear 0.2s;transition: border linear 0.2s, box-shadow linear 0.2s;}
|
||||||
|
textarea:focus,input[type="text"]:focus,input[type="password"]:focus,input[type="datetime"]:focus,input[type="datetime-local"]:focus,input[type="date"]:focus,input[type="month"]:focus,input[type="time"]:focus,input[type="week"]:focus,input[type="number"]:focus,input[type="email"]:focus,input[type="url"]:focus,input[type="search"]:focus,input[type="tel"]:focus,input[type="color"]:focus{border-color: rgba(82, 168, 236, 0.8);outline: 0;outline: thin dotted \9;-webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);-moz-box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);box-shadow: inset 0 1px 1px rgba(0, 0, 0, 0.075), 0 0 8px rgba(82, 168, 236, 0.6);}
|
||||||
|
input[type="radio"],input[type="checkbox"] {margin: 4px 0 0;margin-top: 1px \9;*margin-top: 0;line-height: normal;}
|
||||||
|
input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"] {width: auto;}
|
||||||
|
|
||||||
|
/* 首页样式 */
|
||||||
|
#toparea{background-color:#EFEFEF;background-image:linear-gradient(to bottom, #FFFFFF, #EFEFEF);background-repeat: repeat-x;border:0;padding:0;filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#FFFFFFFF',endColorstr='#FFEFEFEF',GradientType=0)}
|
||||||
|
#topmenu{display:inline;background:none;}
|
||||||
|
#topmenu .logo{color:#777777;display:block;float:left;font-size:20px;text-decoration:none;cursor:default;font-weight: 200;padding:7px 17px 5px;text-shadow: 0 1px 0 #FFFFFF;}
|
||||||
|
#topmenu .nav{float:left;margin:0;padding:0;list-style:none}
|
||||||
|
#topmenu .nav li{float:left;margin:0;padding:0}
|
||||||
|
#topmenu .nav li a{color:#777777;padding:13px 15px;text-decoration:none;text-shadow:0 1px 0 #FFFFFF;display: block;}
|
||||||
|
#topmenu .nav li a.focus{background-color:#E5E5E5;box-shadow:0 3px 8px rgba(0, 0, 0, 0.125) inset;color:#555555;text-decoration:none;}
|
||||||
|
#topmenu .nav-right{margin:8px 5px 0;padding:0;list-style:none;float:right}
|
||||||
|
#topmenu .nav-right li{float:left;margin:0 5px;padding:0;}
|
26
static/css/admin/metro.css
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
@CHARSET "UTF-8";
|
||||||
|
body{margin:0; padding:0;background:none;font-size:13px;font-family:sans-serif;}
|
||||||
|
a{color:#08c;text-decoration:none}
|
||||||
|
a:hover,a:focus{color:#005580;text-decoration:underline}
|
||||||
|
a:focus,a:hover,a:active {outline: 0;}
|
||||||
|
.word-wrap{word-wrap: break-word;word-break: break-all;}
|
||||||
|
.messager-body{word-wrap: break-word;word-break: break-all;}
|
||||||
|
.datagrid-row-selected a{color:#000} /* 解决列表选中部分a标签看不见问题 */
|
||||||
|
.panel input,.panel form{font-size:12px} /* 表单中字体问题 */
|
||||||
|
|
||||||
|
/* 表单样式 */
|
||||||
|
form{font-size:13px}
|
||||||
|
textarea,input[type="text"],input[type="password"],input[type="datetime"],input[type="datetime-local"],input[type="date"],input[type="month"],input[type="time"],input[type="week"],input[type="number"],input[type="email"],input[type="url"],input[type="search"],input[type="tel"],input[type="color"]{background-color: #ffffff;border: 1px solid #dddddd;}
|
||||||
|
input[type="radio"],input[type="checkbox"] {margin: 4px 0 0;margin-top: 1px \9;*margin-top: 0;line-height: normal;}
|
||||||
|
input[type="file"],input[type="image"],input[type="submit"],input[type="reset"],input[type="button"],input[type="radio"],input[type="checkbox"] {width: auto;}
|
||||||
|
|
||||||
|
/* 首页样式 */
|
||||||
|
#toparea{background-color:#FFFFFF;}
|
||||||
|
#topmenu{display:inline;background:none;}
|
||||||
|
#topmenu .logo{color:#777777;display:block;float:left;font-size:20px;text-decoration:none;cursor:default;font-weight: 200;padding:7px 17px 5px;text-shadow: 0 1px 0 #FFFFFF;}
|
||||||
|
#topmenu .nav{float:left;margin:0;padding:0;list-style:none}
|
||||||
|
#topmenu .nav li{float:left;margin:0;padding:0}
|
||||||
|
#topmenu .nav li a{color:#777777;padding:13px 15px;text-decoration:none;text-shadow:0 1px 0 #FFFFFF;display: block;}
|
||||||
|
#topmenu .nav li a.focus{background-color:#E5EFF2;color:#555555;text-decoration:none;}
|
||||||
|
#topmenu .nav-right{margin:8px 5px 0;padding:0;list-style:none;float:right}
|
||||||
|
#topmenu .nav-right li{float:left;margin:0 5px;padding:0;}
|
4
static/css/bootstrap.amethyst.min.css
vendored
Normal file
4
static/css/bootstrap.min.css
vendored
Normal file
1078
static/css/icons.css
Normal file
14
static/css/plan.amethyst.min.css
vendored
Normal file
14
static/css/plan.min.css
vendored
Normal file
58
static/css/style.css
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
html,body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
height: 100%;
|
||||||
|
font-family:sans-serif;
|
||||||
|
}
|
||||||
|
#wrap {
|
||||||
|
min-height: 100%;
|
||||||
|
height: auto !important;
|
||||||
|
height: 100%;
|
||||||
|
margin: 0px auto -60px;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
#push{
|
||||||
|
height:60px;
|
||||||
|
}
|
||||||
|
footer {
|
||||||
|
height: 60px;
|
||||||
|
background-color: white;
|
||||||
|
border-top: 1px solid #ddd;
|
||||||
|
padding: 18px 0;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
.row > [class*="col-"] {
|
||||||
|
margin-bottom: 30px;
|
||||||
|
}
|
||||||
|
.navbar-container {
|
||||||
|
position: relative;
|
||||||
|
min-height: 100px;
|
||||||
|
}
|
||||||
|
.navbar-container .navbar.navbar-fixed-top,
|
||||||
|
.navbar-container .navbar.navbar-fixed-bottom {
|
||||||
|
position: absolute;
|
||||||
|
top: 50px;
|
||||||
|
z-index: 0;
|
||||||
|
}
|
||||||
|
.navbar-container .navbar.navbar-fixed-top .container,
|
||||||
|
.navbar-container .navbar.navbar-fixed-bottom .container {
|
||||||
|
max-width: 90%;
|
||||||
|
}
|
||||||
|
.btn-group {
|
||||||
|
margin-bottom: 10px
|
||||||
|
}
|
||||||
|
.form-inline select,
|
||||||
|
.form-inline input[type="text"],
|
||||||
|
.form-inline input[type="password"] {
|
||||||
|
width: 180px;
|
||||||
|
}
|
||||||
|
.input-group {
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
.pagination { margin-top :0;}
|
||||||
|
.navbar-inverse {margin: 110px 0}
|
||||||
|
|
||||||
|
.word-wrap{
|
||||||
|
word-wrap: break-word;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
137
static/data/admin_leftmenu.json
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "系统设置",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "系统设置",
|
||||||
|
"id": "9",
|
||||||
|
"url": "/admin/setting/site"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "管理员设置",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "管理员管理",
|
||||||
|
"id": "9",
|
||||||
|
"url": "/admin/user/member_list"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "角色管理",
|
||||||
|
"id": "11",
|
||||||
|
"url": "/admin/user/role_list"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "新闻管理",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "新闻管理",
|
||||||
|
"id": "34",
|
||||||
|
"url": "/admin/news/news_list"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "分类管理",
|
||||||
|
"id": "34",
|
||||||
|
"url": "/admin/news/news_category_list"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "图片管理",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "图片管理",
|
||||||
|
"id": "34",
|
||||||
|
"url": "/admin/image"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "分类管理",
|
||||||
|
"id": "34",
|
||||||
|
"url": "/admin/image_category"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "评论管理",
|
||||||
|
"id": "34",
|
||||||
|
"url": "/admin/image_comment"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "用户管理",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "用户管理",
|
||||||
|
"id": "14",
|
||||||
|
"url": "/admin/user"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "黑名单",
|
||||||
|
"id": "14",
|
||||||
|
"url": "/admin/user_black"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "关于我们",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "关于我们",
|
||||||
|
"id": "9",
|
||||||
|
"url": "/admin/about_us"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "联系我们",
|
||||||
|
"id": "9",
|
||||||
|
"url": "/admin/contact_us"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "招贤纳士",
|
||||||
|
"id": "9",
|
||||||
|
"url": "/admin/join_us"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "公司新闻",
|
||||||
|
"id": "9",
|
||||||
|
"url": "/admin/official_news"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "友情链接",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "友情管理",
|
||||||
|
"id": "9",
|
||||||
|
"url": "/admin/friend_link"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "后台管理",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "操作日志",
|
||||||
|
"id": "14",
|
||||||
|
"url": "/admin/op_log"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "我的面板",
|
||||||
|
"son": [
|
||||||
|
{
|
||||||
|
"text": "修改个人信息",
|
||||||
|
"id": "6",
|
||||||
|
"url": "/admin/user/edit_info"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"text": "修改密码",
|
||||||
|
"id": "6",
|
||||||
|
"url": "/admin/user/edit_password"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
BIN
static/img/icons/Date/date.png
Normal file
After Width: | Height: | Size: 626 B |
BIN
static/img/icons/Date/date_add.png
Normal file
After Width: | Height: | Size: 703 B |
BIN
static/img/icons/Date/date_delete.png
Normal file
After Width: | Height: | Size: 716 B |
BIN
static/img/icons/Date/date_edit.png
Normal file
After Width: | Height: | Size: 799 B |
BIN
static/img/icons/Date/date_error.png
Normal file
After Width: | Height: | Size: 753 B |
BIN
static/img/icons/Date/date_go.png
Normal file
After Width: | Height: | Size: 753 B |
BIN
static/img/icons/Date/date_link.png
Normal file
After Width: | Height: | Size: 764 B |
BIN
static/img/icons/Date/date_magnify.png
Normal file
After Width: | Height: | Size: 711 B |
BIN
static/img/icons/Date/date_next.png
Normal file
After Width: | Height: | Size: 688 B |
BIN
static/img/icons/Date/date_previous.png
Normal file
After Width: | Height: | Size: 720 B |
BIN
static/img/icons/application/application.png
Normal file
After Width: | Height: | Size: 464 B |
BIN
static/img/icons/application/application_add.png
Normal file
After Width: | Height: | Size: 619 B |
BIN
static/img/icons/application/application_cascade.png
Normal file
After Width: | Height: | Size: 524 B |
BIN
static/img/icons/application/application_delete.png
Normal file
After Width: | Height: | Size: 610 B |
BIN
static/img/icons/application/application_double.png
Normal file
After Width: | Height: | Size: 533 B |
BIN
static/img/icons/application/application_edit.png
Normal file
After Width: | Height: | Size: 703 B |
BIN
static/img/icons/application/application_error.png
Normal file
After Width: | Height: | Size: 656 B |
BIN
static/img/icons/application/application_form.png
Normal file
After Width: | Height: | Size: 467 B |
BIN
static/img/icons/application/application_form_add.png
Normal file
After Width: | Height: | Size: 592 B |
BIN
static/img/icons/application/application_form_delete.png
Normal file
After Width: | Height: | Size: 605 B |
BIN
static/img/icons/application/application_form_edit.png
Normal file
After Width: | Height: | Size: 714 B |
BIN
static/img/icons/application/application_form_magnify.png
Normal file
After Width: | Height: | Size: 612 B |
BIN
static/img/icons/application/application_get.png
Normal file
After Width: | Height: | Size: 581 B |
BIN
static/img/icons/application/application_go.png
Normal file
After Width: | Height: | Size: 634 B |
BIN
static/img/icons/application/application_home.png
Normal file
After Width: | Height: | Size: 685 B |
BIN
static/img/icons/application/application_key.png
Normal file
After Width: | Height: | Size: 670 B |
BIN
static/img/icons/application/application_lightning.png
Normal file
After Width: | Height: | Size: 656 B |
BIN
static/img/icons/application/application_link.png
Normal file
After Width: | Height: | Size: 701 B |
BIN
static/img/icons/application/application_osx.png
Normal file
After Width: | Height: | Size: 487 B |
BIN
static/img/icons/application/application_osx_terminal.png
Normal file
After Width: | Height: | Size: 525 B |
BIN
static/img/icons/application/application_put.png
Normal file
After Width: | Height: | Size: 585 B |
BIN
static/img/icons/application/application_side_boxes.png
Normal file
After Width: | Height: | Size: 478 B |
BIN
static/img/icons/application/application_side_contract.png
Normal file
After Width: | Height: | Size: 547 B |
BIN
static/img/icons/application/application_side_expand.png
Normal file
After Width: | Height: | Size: 581 B |
BIN
static/img/icons/application/application_side_list.png
Normal file
After Width: | Height: | Size: 510 B |
BIN
static/img/icons/application/application_side_tree.png
Normal file
After Width: | Height: | Size: 483 B |
BIN
static/img/icons/application/application_split.png
Normal file
After Width: | Height: | Size: 520 B |
BIN
static/img/icons/application/application_tile_horizontal.png
Normal file
After Width: | Height: | Size: 432 B |
BIN
static/img/icons/application/application_tile_vertical.png
Normal file
After Width: | Height: | Size: 492 B |
BIN
static/img/icons/application/application_view_columns.png
Normal file
After Width: | Height: | Size: 493 B |
BIN
static/img/icons/application/application_view_detail.png
Normal file
After Width: | Height: | Size: 576 B |
BIN
static/img/icons/application/application_view_gallery.png
Normal file
After Width: | Height: | Size: 555 B |
BIN
static/img/icons/application/application_view_icons.png
Normal file
After Width: | Height: | Size: 476 B |
BIN
static/img/icons/application/application_view_list.png
Normal file
After Width: | Height: | Size: 473 B |
BIN
static/img/icons/application/application_view_tile.png
Normal file
After Width: | Height: | Size: 465 B |
BIN
static/img/icons/application/application_xp.png
Normal file
After Width: | Height: | Size: 426 B |
BIN
static/img/icons/application/application_xp_terminal.png
Normal file
After Width: | Height: | Size: 507 B |
BIN
static/img/icons/arrow/accept.png
Normal file
After Width: | Height: | Size: 781 B |
BIN
static/img/icons/arrow/add.png
Normal file
After Width: | Height: | Size: 733 B |
BIN
static/img/icons/arrow/arrow_branch.png
Normal file
After Width: | Height: | Size: 582 B |
BIN
static/img/icons/arrow/arrow_divide.png
Normal file
After Width: | Height: | Size: 677 B |
BIN
static/img/icons/arrow/arrow_down.png
Normal file
After Width: | Height: | Size: 379 B |
BIN
static/img/icons/arrow/arrow_in.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
static/img/icons/arrow/arrow_inout.png
Normal file
After Width: | Height: | Size: 551 B |